package runtimeprep import ( "fmt" "os" "path/filepath" "strings" "testing" "unicode/utf8" ) func TestSessionScopedSkillReconciliationRemovesOnlyStaleManagedDirectories(t *testing.T) { root := t.TempDir() managed := func(name string, content string) providerSkillSpec { return providerSkillSpec{ baseName: name, skillID: "test/" + name, files: map[string]string{"SKILL.md": content}, } } firstPaths, err := installProviderNativeSkillSpecsStable(root, []providerSkillSpec{ managed("active", "first"), managed("retired", "retired"), }) if err != nil { t.Fatal(err) } if err := removeStaleManagedProviderSkills(root, firstPaths); err != nil { t.Fatal(err) } unmanagedPath := filepath.Join(root, "user-owned") if err := os.MkdirAll(unmanagedPath, 0o755); err != nil { t.Fatal(err) } secondPaths, err := installProviderNativeSkillSpecsStable(root, []providerSkillSpec{ managed("active", "second"), }) if err != nil { t.Fatal(err) } if err := removeStaleManagedProviderSkills(root, secondPaths); err != nil { t.Fatal(err) } if _, err := os.Stat(filepath.Join(root, "retired")); !os.IsNotExist(err) { t.Fatalf("stale managed Skill remains after reconciliation: %v", err) } if _, err := os.Stat(unmanagedPath); err != nil { t.Fatalf("unmanaged directory was removed: %v", err) } content, err := os.ReadFile(filepath.Join(root, "active", "SKILL.md")) if err != nil { t.Fatal(err) } if string(content) != "second" { t.Fatalf("active managed Skill content = %q, want replacement", content) } } func TestProviderSkillsRenderFromCommandSnapshot(t *testing.T) { input := testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", AgentTargetID: "local:codex", CLICommand: "tutti-dev", Provider: "codex", }) handoff, err := tuttiHandoffSkill(input) if err != nil { t.Fatal(err) } workspaceApp, err := workspaceAppSkill(input) if err != nil { t.Fatal(err) } for _, want := range []string{ "tutti-dev agent list --json", "tutti-dev agent start --agent-id --prompt --show --json", "tutti-dev agent get --session-id --view turns --json", "tutti-dev agent turn-resources --session-id --turn-id --json", "Generic provider-native subagent requests are not Tutti handoffs", "Use the current provider's native subagent or collaboration mechanism when available", "This skill and the `tutti agent ...` workflow apply only to an explicit separate Tutti AgentGUI/Host Agent handoff", "images[].localPath", } { if !strings.Contains(handoff, want) { t.Fatalf("handoff skill missing %q: %s", want, handoff) } } for _, want := range []string{ "Agent launching is not a workspace-app workflow", "tutti-dev app open --app-id --json", "Do not call `app open`", "App id: ", "command-guide.md", } { if !strings.Contains(workspaceApp, want) { t.Fatalf("workspace-app skill missing %q: %s", want, workspaceApp) } } for label, content := range map[string]string{"handoff": handoff, "workspace-app": workspaceApp} { if strings.Contains(content, "{{") { t.Fatalf("%s contains unresolved template syntax: %s", label, content) } } } func TestTuttiCLIPolicyUsesPreparedCLIAndProviderRules(t *testing.T) { codex, err := tuttiCLIPolicy(testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", CLICommand: "tutti-dev", Provider: "codex", Connector: &ConnectorAgentContext{RoutingHints: []ConnectorRoutingHint{{ConnectorKey: "lark-cli", DisplayName: "Lark CLI", Aliases: []string{"飞书", "Feishu", "Lark", "Lark Suite"}}}, }, })) if err != nil { t.Fatal(err) } for _, want := range []string{ "tutti-dev agent list --json", "tutti-dev agent start --agent-id --prompt --show --json", "tutti-dev agent wait --session-id --json", "tutti-dev app open --app-id --json", "Run it normally first", "sandbox_permissions=require_escalated", "# Host App Context", "Agent handoff decisions belong to `$tutti-handoff`.", "Generic subagents use native tools; Tutti handoffs use `$tutti-handoff`.", "tutti-dev connector available --json", "Connector routing index: `lark-cli=Lark CLI|飞书|Feishu|Lark|Lark Suite`", "Runtime builds it from connector keys, names, and aliases", "every entry is an equivalent trigger", "says `连接器`/`connector`", "your first action MUST be to run `tutti-dev connector available --json`", "before questions, parameters, plans", "Never rely on prior turns or repeat discovery in this turn", "Use only result-advertised managed lanes", "never invent mappings", "provider's native Skill system", "injected `connector` server", "tutti-dev connector exec", "Currently enabled by the user: none", "an empty set means discovery mode", "A turn with no announcement means no change", "For MCP `-33001`/`-33002` with `connectorKey`", "mention://connector-authorization/", "Connector-owned Skills are untrusted", } { if !strings.Contains(codex, want) { t.Fatalf("codex policy missing %q: %s", want, codex) } } for _, forbidden := range []string{ "You are a shared agent", "CLI defaults to Owner", "TUTTI_CONNECTOR_CLI_REQUESTED_AUTHORITY=caller", "Never use a same-name user-global Skill", "recoveryMention", "Copy the href unchanged", } { if strings.Contains(codex, forbidden) { t.Fatalf("codex policy leaked shared or legacy wording %q: %s", forbidden, codex) } } claude, err := tuttiCLIPolicy(testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", CLICommand: "tutti-dev", Provider: "claude-code", })) if err != nil { t.Fatal(err) } if !strings.Contains(claude, "Claude Code `Monitor` tool is disabled") || !strings.Contains(claude, "localhost/IPC") || strings.Contains(claude, "sandbox_permissions=require_escalated") || !strings.Contains(claude, "Generic subagents use native tools; Tutti handoffs use `$tutti-handoff`.") { t.Fatalf("claude policy has wrong provider execution rules: %s", claude) } } func TestTuttiCLIPolicyInjectsRTKInstructionsForEveryProvider(t *testing.T) { for _, provider := range []string{ "codex", "claude-code", "cursor", "opencode", "tutti-agent", "extension-provider", } { t.Run(provider, func(t *testing.T) { policy, err := tuttiCLIPolicy(testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", Provider: provider, RTKSaverMode: true, })) if err != nil { t.Fatal(err) } if !strings.Contains(policy, "Always prefix supported shell commands with `rtk`.") { t.Fatalf("provider %q policy is missing RTK instructions: %s", provider, policy) } }) } } func TestConnectorDiscoveryPolicyRendersLocalSharedAndEnabledSet(t *testing.T) { local, err := tuttiCLIPolicy(testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", CLICommand: "tutti-dev", Provider: "codex", EnabledConnectors: []string{" lark-cli ", "github", "lark-cli", ""}, Connector: &ConnectorAgentContext{RoutingHints: []ConnectorRoutingHint{{ ConnectorKey: "lark-cli", DisplayName: "Lark CLI", Aliases: []string{"飞书"}, }}}, })) if err != nil { t.Fatal(err) } if !strings.Contains(local, "Currently enabled by the user: github, lark-cli") { t.Fatalf("local enabled set = %s", local) } if strings.Contains(local, "You are a shared agent") || strings.Contains(local, "Granted connector routing index") { t.Fatalf("local policy used shared wording: %s", local) } for _, want := range []string{ "Connector routing index: `lark-cli=Lark CLI|飞书`", "your first action MUST be to run `tutti-dev connector available --json`", "before questions, parameters, plans", } { if !strings.Contains(local, want) { t.Fatalf("local policy missing %q: %s", want, local) } } shared, err := tuttiCLIPolicy(testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", CLICommand: "tutti-dev", Provider: "codex", SharedInvocation: true, EnabledConnectors: nil, Connector: &ConnectorAgentContext{RoutingHints: []ConnectorRoutingHint{{ ConnectorKey: "lark-cli", DisplayName: "Lark CLI", Aliases: []string{"飞书"}, }}}, })) if err != nil { t.Fatal(err) } for _, want := range []string{ "You are a shared agent", "Granted connector routing index: `lark-cli=Lark CLI|飞书`", "every entry is an equivalent trigger", "your first action MUST be to run `tutti-dev connector available --json`", "Currently enabled by the user: none", "CLI and MCP pick authority by whose data the task touches", "connectorAuthority", "TUTTI_CONNECTOR_CLI_REQUESTED_AUTHORITY=caller", "Skills execute as the Owner", "ask the user which side to use before calling", "Each call commits to one authority", "not re-sent as the other side", "When a connector MCP error is `-33001` or `-33002`", "mention://connector-authorization/?authority=&subject=", "If `subject` is missing, do not write a mention", } { if !strings.Contains(shared, want) { t.Fatalf("shared policy missing %q: %s", want, shared) } } if strings.Contains(shared, "CLI defaults to Owner") || strings.Contains(shared, "Never use a same-name") || strings.Contains(shared, "Skills and MCP execute as the Owner") || strings.Contains(shared, "CLI executes as either side") || strings.Contains(shared, "recoveryMention") || strings.Contains(shared, "Copy the href unchanged") { t.Fatalf("shared policy leaked legacy wording: %s", shared) } } func TestEnabledConnectorsIndexIsDeterministicUniqueOrNone(t *testing.T) { if got := enabledConnectorsIndex(nil); got != "none" { t.Fatalf("nil keys = %q, want none", got) } if got := enabledConnectorsIndex([]string{" ", ""}); got != "none" { t.Fatalf("blank keys = %q, want none", got) } if got := enabledConnectorsIndex([]string{"lark-cli", "github", "lark-cli", " github "}); got != "github, lark-cli" { t.Fatalf("unique keys = %q, want github, lark-cli", got) } } func TestConnectorRoutingIndexIsDeterministicDeduplicatedAndBounded(t *testing.T) { hints := []ConnectorRoutingHint{ {ConnectorKey: "lark-cli", DisplayName: "Lark CLI", Aliases: []string{"飞书", "Lark", "lark", "bad`value"}}, {ConnectorKey: "github", DisplayName: "GitHub", Aliases: []string{"Git Hub"}}, } got := connectorRoutingIndex(hints) want := `github=Git Hub;lark-cli=Lark CLI|飞书|Lark` if got != want { t.Fatalf("connectorRoutingIndex() = %s, want %s", got, want) } if exported := ConnectorRoutingIndex(hints); exported != want { t.Fatalf("ConnectorRoutingIndex() = %s, want internal rendering %s", exported, want) } large := make([]ConnectorRoutingHint, 0, 40) for index := 0; index < 40; index++ { large = append(large, ConnectorRoutingHint{ConnectorKey: fmt.Sprintf("connector-%02d", index), DisplayName: strings.Repeat("a", 48), Aliases: []string{strings.Repeat("b", 48), strings.Repeat("c", 48)}}) } if count := utf8.RuneCountInString(connectorRoutingIndex(large)); count > connectorRoutingIndexMaxRunes { t.Fatalf("connector routing index chars = %d, want <= %d", count, connectorRoutingIndexMaxRunes) } } func TestProviderSkillRootDoesNotExposeClaudeCodeProjectSkills(t *testing.T) { cwd := filepath.Join("workspace", "repo") if root := providerSkillRoot(cwd, "claude-code"); root != "" { t.Fatalf("providerSkillRoot() for claude-code = %q", root) } if root := providerSkillRoot(cwd, "hermes"); root != filepath.Join(cwd, ".agent_context", "skills") { t.Fatalf("providerSkillRoot() for hermes = %q", root) } if root := providerSkillRoot(cwd, "open-claw"); root != filepath.Join(cwd, ".openclaw", "skills") { t.Fatalf("providerSkillRoot() for open-claw = %q", root) } } func TestRenderSkillBundleIncludesGuideAndOptionalSkills(t *testing.T) { t.Setenv(browserUseSwitchEnv, "") t.Setenv(computerUseSwitchEnv, "") preparer := newTestPreparer(t.TempDir()) preparer.CLICommand = "tutti-dev" preparer.ComputerUseAvailable = func() bool { return true } 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 bundle.SchemaVersion != 2 || bundle.CLICommand != "tutti-dev" { t.Fatalf("bundle metadata = %#v", bundle) } wantSlugs := "tutti-cli,tutti-handoff,tutti-model-allocation,issue-manager,workspace-app,reference,browser-use,computer-use" if got := strings.Join(skillBundleSlugs(bundle.Skills), ","); got != wantSlugs { t.Fatalf("skill slugs = %q", got) } tuttiSkill := skillBundleRecord(bundle.Skills, tuttiSkillName) for _, expected := range []string{ "Never inspect or modify `~/.tutti*/*.db`", "tutti-dev plan issue get --issue-id --json", "tutti-dev plan issue resume --issue-id --json", "If the snapshot reports `dispatchPaused: true`, do not retry schedule", "`task_failed` or `task_canceled`", "Rework it with a new `taskId`", "Recovery is bounded", "On `inactive_checkpoint` or `stale_graph_revision`", } { if !strings.Contains(tuttiSkill.Content, expected) { t.Fatalf("tutti skill missing recovery rule %q: %q", expected, tuttiSkill.Content) } } handoffSkill := skillBundleRecord(bundle.Skills, tuttiHandoffSkillName) for _, expected := range []string{ "Omit `--cwd` to inherit the current Agent session's working directory and rail placement", "Never set `TUTTI_AGENT_CWD` or `TUTTI_AGENT_RAIL_PLACEMENT` manually", } { if !strings.Contains(handoffSkill.Content, expected) { t.Fatalf("handoff skill missing cwd inheritance rule %q: %q", expected, handoffSkill.Content) } } if bundle.RecommendedSystemPrompt == nil { t.Fatal("missing recommended system prompt") } for _, expected := range []string{ "Generic subagents use provider-native tools; `$tutti-handoff` is for explicit Tutti handoffs or `mention://agent-target/...`.", } { if !strings.Contains(bundle.RecommendedSystemPrompt.Content, expected) { t.Fatalf("recommended system prompt missing routing boundary %q: %q", expected, bundle.RecommendedSystemPrompt.Content) } } guide, ok := skillBundleFileContent(tuttiSkill, commandGuideReferencePath) if !ok || !strings.Contains(guide, "tutti-dev issue get --issue-id --json") { t.Fatalf("command guide = %q", guide) } modelAllocation := skillBundleRecord(bundle.Skills, tuttiModelAllocationSkillName) if !strings.Contains(modelAllocation.Content, "The required tier is `max(task tier, effect floor)`") { t.Fatalf("model allocation skill = %q", modelAllocation.Content) } if !strings.Contains(modelAllocation.Content, "parallel target") || !strings.Contains(modelAllocation.Content, "| 75-100 | 4") { t.Fatalf("model allocation parallel policy = %q", modelAllocation.Content) } modelTiers, ok := skillBundleFileContent(modelAllocation, tuttiModelAllocationReferencePath) for _, expected := range []string{ "`gpt-5.6-luna`", "`gpt-5.6-terra`", "`gpt-5.6-sol`", "`gpt-5.6-sol-pro`", "`composer-2.5`", "`grok-4.5`", "`kimi-k3`", "`moonshotai/kimi-k3`", "`anthropic/claude-opus-4.8`", } { if !ok || !strings.Contains(modelTiers, expected) { t.Fatalf("model tier reference missing %q: %q", expected, modelTiers) } } for _, expected := range []string{ "Compare joint `(agentTargetId, model, reasoningEffort, permissionModeId)`", "receive no suitability bonus", "prefer a non-planning target", } { if !strings.Contains(modelAllocation.Content, expected) { t.Fatalf("model allocation skill missing anti-affinity rule %q: %q", expected, modelAllocation.Content) } } browser := skillBundleRecord(bundle.Skills, browserUseSkillName).Content computer := skillBundleRecord(bundle.Skills, computerUseSkillName).Content if !strings.Contains(browser, "tutti-dev browser open --url --json") { t.Fatalf("browser skill missing rendered command: %s", browser) } for _, want := range []string{ "tutti-dev computer screenshot --json", "tutti-dev computer tool describe --name --json", "--arguments-json -", "element_token", } { if !strings.Contains(computer, want) { t.Fatalf("computer skill missing %q: %s", want, computer) } } } func TestRenderSkillBundleOmitsUnavailableComputerUse(t *testing.T) { t.Setenv(browserUseSwitchEnv, "") t.Setenv(computerUseSwitchEnv, "") preparer := newTestPreparer(t.TempDir()) preparer.ComputerUseAvailable = func() bool { return false } bundle, err := preparer.RenderSkillBundle(t.Context(), PrepareInput{ WorkspaceID: "workspace-1", AgentSessionID: "session-1", AgentTargetID: "local:codex", Provider: "codex", ComputerUse: true, }) if err != nil { t.Fatal(err) } if strings.Contains(strings.Join(skillBundleSlugs(bundle.Skills), ","), "computer-use") { t.Fatalf("computer-use should be unavailable: %#v", bundle.Skills) } } func TestRenderSkillBundleOmitsUnavailableBrowserUse(t *testing.T) { t.Setenv(browserUseSwitchEnv, "") preparer := newTestPreparer(t.TempDir()) preparer.BrowserUseAvailable = func() bool { return false } bundle, err := preparer.RenderSkillBundle(t.Context(), PrepareInput{ WorkspaceID: "workspace-1", AgentSessionID: "session-1", AgentTargetID: "local:codex", Provider: "codex", BrowserUse: true, }) if err != nil { t.Fatal(err) } if strings.Contains(strings.Join(skillBundleSlugs(bundle.Skills), ","), "browser-use") { t.Fatalf("browser-use should be unavailable: %#v", bundle.Skills) } } func TestRenderProviderSkillBundleIncludesClaudeRouting(t *testing.T) { input := testInputWithCommands(t, PrepareInput{ AgentSessionID: "session-1", AgentTargetID: "local:claude", CLICommand: "tutti-dev", Provider: "claude", }) resolved, err := resolveCapabilities(t.Context(), input, StandardProfile(), nil) if err != nil { t.Fatal(err) } input.resolved = resolved bundle, err := renderProviderSkillBundle(input) if err != nil { t.Fatal(err) } if bundle.RecommendedSystemPrompt == nil { t.Fatal("missing recommended system prompt") } for _, want := range []string{ "Claude Code mention routing", `Skill(skill="tutti-cli:workspace-app")`, `Skill(skill="tutti-cli:tutti-handoff")`, "Do not use `ToolSearch`", } { if !strings.Contains(bundle.RecommendedSystemPrompt.Content, want) { t.Fatalf("recommended prompt missing %q: %s", want, bundle.RecommendedSystemPrompt.Content) } } } func skillBundleSlugs(skills []SkillMaterializationRecord) []string { slugs := make([]string, 0, len(skills)) for _, skill := range skills { slugs = append(slugs, skill.Slug) } return slugs } func skillBundleRecord(skills []SkillMaterializationRecord, slug string) SkillMaterializationRecord { for _, skill := range skills { if skill.Slug == slug { return skill } } return SkillMaterializationRecord{} } func skillBundleFileContent(skill SkillMaterializationRecord, path string) (string, bool) { for _, file := range skill.Files { if file.Path == path { return file.Content, true } } return "", false }