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. 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 a single knob to revisit when design/05 fixes a // concrete figure. const defaultSubagentTimeout = 10 * time.Minute // 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)) 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) }