Files
agent-radar 2ed3415fb7
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
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

543 lines
19 KiB
Go

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 <agent-id> --prompt <prompt> --show --json",
"tutti-dev agent get --session-id <session-id> --view turns --json",
"tutti-dev agent turn-resources --session-id <session-id> --turn-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 <appId> --json",
"Do not call `app open`",
"App id: <appId>",
"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 <agent-id> --prompt <prompt> --show --json",
"tutti-dev agent wait --session-id <session-id> --json",
"tutti-dev app open --app-id <appId> --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/<connectorKey>",
"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/<connectorKey>?authority=<authority>&subject=<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 <issue-id> --json",
"tutti-dev plan issue resume --issue-id <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 <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 <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 <tool> --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
}