package main import ( "time" "git.yeij.top/AskaEth/Cyrene_v2/internal/logger" "git.yeij.top/AskaEth/Cyrene_v2/internal/runtime/orchestration" ) // defaultSubagentTimeout bounds one sub-agent's whole run when nothing else // configures it. DevDocs/design/05-子代理.md §7 requires a timeout and an // anti-runaway bound; the scheduler already implements both (PerAgentTimeout, // Abort/CancelAll), so production must supply a value or the per-agent timeout // stays inert (the scheduler's zero means "no timeout"). The number is // deliberately generous — a sub-agent runs up to 8 tool rounds like any loop — // and is the fallback behind SUBAGENT_TIMEOUT_SEC (裁决 02.4.4: 默认 10min, // 可配置;subagentToolTimeout resolves the effective value). const defaultSubagentTimeout = 10 * time.Minute // subagentToolTimeout resolves the dispatch tool's default sub-agent timeout: // SUBAGENT_TIMEOUT_SEC wins when positive, otherwise the 10-minute code default // applies (design/05 §7 裁决 02.4.4). A per-dispatch timeout_seconds argument // still overrides this per call at the tool layer. func (a *app) subagentToolTimeout() time.Duration { if a.cfg != nil && a.cfg.Subagent.TimeoutSec > 0 { return time.Duration(a.cfg.Subagent.TimeoutSec) * time.Second } return defaultSubagentTimeout } // wireSubagentTool registers the sub-agent dispatch tool with the dispatcher, so // the agent can finally reach the orchestration layer at runtime (before this, // (*orchestration.Scheduler).Spawn had no production caller and design/05 was // library-only). // // The capability is registered with its default reachability markers (both // false), which keeps it owner-only: the design/11 §3 policy engine hides the // schema from a group/stranger session and the loop's security gate rejects the // call there. Design/05 §6/§7 give no group/stranger allowance for spawning // background agents, and being able to spawn one is a heavy operation, so the // safe default is chosen deliberately rather than enabled by accident. func (a *app) wireSubagentTool() { log := logger.WithSource(a.log, "cmd/cyrene") if a.scheduler == nil { log.Warn("sub-agent tool skipped: no scheduler") return } spawner := orchestration.NewSpawnTool(a.scheduler, orchestration.WithSpawnLogger(a.log), orchestration.WithSpawnTimeout(a.subagentToolTimeout())) if err := a.dispatcher.Register(spawner); err != nil { log.Warn("sub-agent tool register skipped", "error", err) return } log.Info("sub-agent tool registered", "tool", orchestration.SpawnToolName, "owner_only", true) }