Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
223 lines
6.8 KiB
Go
223 lines
6.8 KiB
Go
package runtimeprep
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
type countingCommandCatalog struct {
|
|
calls int
|
|
capabilities []CommandCapability
|
|
}
|
|
|
|
func (catalog *countingCommandCatalog) Capabilities(_ context.Context, _ CommandContext) []CommandCapability {
|
|
catalog.calls++
|
|
return append([]CommandCapability(nil), catalog.capabilities...)
|
|
}
|
|
|
|
func TestRenderSkillBundleUsesOneHostCommandSnapshot(t *testing.T) {
|
|
capabilities := testCommandCapabilities()
|
|
filtered := make([]CommandCapability, 0, len(capabilities))
|
|
for _, capability := range capabilities {
|
|
if capability.ID == "issue-manager.issue.task.create-batch" ||
|
|
strings.HasPrefix(capability.ID, "computer.") {
|
|
continue
|
|
}
|
|
if strings.HasPrefix(capability.ID, "browser.") {
|
|
capability.Visibility = "integration"
|
|
}
|
|
filtered = append(filtered, capability)
|
|
}
|
|
catalog := &countingCommandCatalog{capabilities: filtered}
|
|
preparer := NewDefaultPreparer(t.TempDir())
|
|
preparer.CommandCatalog = catalog
|
|
preparer.CLICommand = "tutti-dev"
|
|
|
|
bundle, err := preparer.RenderSkillBundle(t.Context(), PrepareInput{
|
|
WorkspaceID: "workspace-1",
|
|
AgentSessionID: "session-1",
|
|
AgentTargetID: "local:codex",
|
|
Provider: "codex",
|
|
BrowserUse: true,
|
|
ComputerUse: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if catalog.calls != 1 {
|
|
t.Fatalf("catalog calls = %d, want 1", catalog.calls)
|
|
}
|
|
|
|
issue := bundleSkillContent(t, bundle, "tutti/issue-manager")
|
|
if !strings.Contains(issue, "Host has no usable batch-create capability") ||
|
|
!strings.Contains(issue, "tutti-dev issue task create") {
|
|
t.Fatalf("issue skill did not select the advertised single-create flow: %s", issue)
|
|
}
|
|
for _, forbidden := range []string{
|
|
"issue task create-batch",
|
|
"--room-id",
|
|
} {
|
|
if strings.Contains(issue, forbidden) {
|
|
t.Fatalf("issue skill leaked unsupported syntax %q: %s", forbidden, issue)
|
|
}
|
|
}
|
|
for _, id := range []string{"tutti/browser-use", "tutti/computer-use"} {
|
|
if bundleHasSkill(bundle, id) {
|
|
t.Fatalf("bundle materialized unavailable skill %q", id)
|
|
}
|
|
}
|
|
|
|
guide := bundleSkillFileContent(t, bundle, "tutti/tutti-cli", commandGuideReferencePath)
|
|
if strings.Contains(guide, "browser navigate") ||
|
|
strings.Contains(guide, "computer screenshot") ||
|
|
strings.Contains(guide, "--room-id") {
|
|
t.Fatalf("guide leaked commands outside the agent-facing snapshot: %s", guide)
|
|
}
|
|
if !strings.Contains(guide, "tutti-dev issue update --issue-id <issue-id> --json") {
|
|
t.Fatalf("guide missing advertised command: %s", guide)
|
|
}
|
|
}
|
|
|
|
func TestRenderSkillBundleOmitsTaskCenterSkillAndPromptWithoutIssueFamily(t *testing.T) {
|
|
capabilities := testCommandCapabilities()
|
|
filtered := make([]CommandCapability, 0, len(capabilities))
|
|
for _, capability := range capabilities {
|
|
if strings.HasPrefix(capability.ID, "issue-manager.") {
|
|
continue
|
|
}
|
|
filtered = append(filtered, capability)
|
|
}
|
|
preparer := NewDefaultPreparer(t.TempDir())
|
|
preparer.CommandCatalog = &countingCommandCatalog{capabilities: filtered}
|
|
preparer.CLICommand = "tutti-dev"
|
|
|
|
bundle, err := preparer.RenderSkillBundle(t.Context(), PrepareInput{
|
|
WorkspaceID: "workspace-1",
|
|
AgentSessionID: "session-1",
|
|
AgentTargetID: "local:codex",
|
|
Provider: "codex",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bundleHasSkill(bundle, "tutti/issue-manager") {
|
|
t.Fatal("task-center skill was materialized without an issue command family")
|
|
}
|
|
|
|
contents := make([]string, 0, len(bundle.Skills)+1)
|
|
for _, skill := range bundle.Skills {
|
|
contents = append(contents, skill.Content)
|
|
for _, file := range skill.Files {
|
|
contents = append(contents, file.Content)
|
|
}
|
|
}
|
|
if bundle.RecommendedSystemPrompt != nil {
|
|
contents = append(contents, bundle.RecommendedSystemPrompt.Content)
|
|
}
|
|
for _, content := range contents {
|
|
for _, forbidden := range []string{"issue-manager", "workspace-issue", "Issue Manager", "Task Manager", "issue task"} {
|
|
if strings.Contains(content, forbidden) {
|
|
t.Fatalf("task-center prompt leaked %q without an issue command family: %s", forbidden, content)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestRenderSkillBundleOmitsConnectorPolicyWhenCLICommandIsNotRegistered(t *testing.T) {
|
|
capabilities := testCommandCapabilities()
|
|
filtered := make([]CommandCapability, 0, len(capabilities))
|
|
for _, capability := range capabilities {
|
|
if capability.ID != "connector.available" {
|
|
filtered = append(filtered, capability)
|
|
}
|
|
}
|
|
preparer := NewDefaultPreparer(t.TempDir())
|
|
preparer.CommandCatalog = staticCommandCatalog(filtered)
|
|
preparer.CLICommand = "tutti-dev"
|
|
|
|
bundle, err := preparer.RenderSkillBundle(t.Context(), PrepareInput{
|
|
WorkspaceID: "workspace-1",
|
|
AgentSessionID: "session-1",
|
|
AgentTargetID: "local:codex",
|
|
Provider: "codex",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if bundle.RecommendedSystemPrompt == nil {
|
|
t.Fatal("missing recommended system prompt")
|
|
}
|
|
for _, forbidden := range []string{
|
|
"connector available",
|
|
"injected `connector` MCP server",
|
|
"connector-specific command",
|
|
} {
|
|
if strings.Contains(bundle.RecommendedSystemPrompt.Content, forbidden) {
|
|
t.Fatalf("recommended system prompt leaked disabled connector policy %q: %s", forbidden, bundle.RecommendedSystemPrompt.Content)
|
|
}
|
|
}
|
|
guide := bundleSkillFileContent(t, bundle, "tutti/tutti-cli", commandGuideReferencePath)
|
|
if strings.Contains(guide, "connector available") {
|
|
t.Fatalf("command guide leaked disabled connector CLI: %s", guide)
|
|
}
|
|
}
|
|
|
|
func TestRuntimeTemplateFailsOnUnknownCommandOrInput(t *testing.T) {
|
|
resolver, err := newCommandResolver("tutti", []CommandCapability{{
|
|
ID: "known",
|
|
Path: []string{"known"},
|
|
InputSchema: map[string]any{"properties": map[string]any{"value": map[string]any{"type": "string"}}},
|
|
}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
input := PrepareInput{commandCapabilities: resolver}
|
|
for name, template := range map[string]string{
|
|
"unknown command": `{{command "missing"}}`,
|
|
"unknown input": `{{command "known" (args "missing" "value")}}`,
|
|
} {
|
|
t.Run(name, func(t *testing.T) {
|
|
if _, err := renderRuntimeTemplate(name, template, input, nil); err == nil {
|
|
t.Fatal("renderRuntimeTemplate() error = nil")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func bundleSkillContent(t *testing.T, bundle SkillBundle, id string) string {
|
|
t.Helper()
|
|
for _, skill := range bundle.Skills {
|
|
if skill.SkillID == id {
|
|
return skill.Content
|
|
}
|
|
}
|
|
t.Fatalf("skill %q not found", id)
|
|
return ""
|
|
}
|
|
|
|
func bundleSkillFileContent(t *testing.T, bundle SkillBundle, id string, path string) string {
|
|
t.Helper()
|
|
for _, skill := range bundle.Skills {
|
|
if skill.SkillID != id {
|
|
continue
|
|
}
|
|
for _, file := range skill.Files {
|
|
if file.Path == path {
|
|
return file.Content
|
|
}
|
|
}
|
|
}
|
|
t.Fatalf("skill file %q/%q not found", id, path)
|
|
return ""
|
|
}
|
|
|
|
func bundleHasSkill(bundle SkillBundle, id string) bool {
|
|
for _, skill := range bundle.Skills {
|
|
if skill.SkillID == id {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|