pilotswarm-sdk 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-loader.d.ts +9 -0
- package/dist/agent-loader.d.ts.map +1 -1
- package/dist/agent-loader.js +3 -0
- package/dist/agent-loader.js.map +1 -1
- package/dist/child-notifications.d.ts +10 -0
- package/dist/child-notifications.d.ts.map +1 -1
- package/dist/child-notifications.js +15 -1
- package/dist/child-notifications.js.map +1 -1
- package/dist/client.d.ts +2 -2
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +14 -20
- package/dist/client.js.map +1 -1
- package/dist/cms-migrations.d.ts.map +1 -1
- package/dist/cms-migrations.js +321 -0
- package/dist/cms-migrations.js.map +1 -1
- package/dist/cms.d.ts +132 -8
- package/dist/cms.d.ts.map +1 -1
- package/dist/cms.js +142 -6
- package/dist/cms.js.map +1 -1
- package/dist/duroxide-schema-migration.d.ts +19 -0
- package/dist/duroxide-schema-migration.d.ts.map +1 -0
- package/dist/duroxide-schema-migration.js +122 -0
- package/dist/duroxide-schema-migration.js.map +1 -0
- package/dist/facts-migrations.d.ts.map +1 -1
- package/dist/facts-migrations.js +923 -0
- package/dist/facts-migrations.js.map +1 -1
- package/dist/facts-store.d.ts +278 -28
- package/dist/facts-store.d.ts.map +1 -1
- package/dist/facts-store.js +262 -32
- package/dist/facts-store.js.map +1 -1
- package/dist/facts-tools.d.ts +8 -1
- package/dist/facts-tools.d.ts.map +1 -1
- package/dist/facts-tools.js +458 -25
- package/dist/facts-tools.js.map +1 -1
- package/dist/graph-store.d.ts +243 -0
- package/dist/graph-store.d.ts.map +1 -0
- package/dist/graph-store.js +63 -0
- package/dist/graph-store.js.map +1 -0
- package/dist/graph-tools.d.ts +47 -0
- package/dist/graph-tools.d.ts.map +1 -0
- package/dist/graph-tools.js +554 -0
- package/dist/graph-tools.js.map +1 -0
- package/dist/horizon-env.d.ts +61 -0
- package/dist/horizon-env.d.ts.map +1 -0
- package/dist/horizon-env.js +120 -0
- package/dist/horizon-env.js.map +1 -0
- package/dist/index.d.ts +15 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/inspect-tools.d.ts +2 -2
- package/dist/inspect-tools.d.ts.map +1 -1
- package/dist/inspect-tools.js +303 -6
- package/dist/inspect-tools.js.map +1 -1
- package/dist/knowledge-index.d.ts +33 -2
- package/dist/knowledge-index.d.ts.map +1 -1
- package/dist/knowledge-index.js +90 -19
- package/dist/knowledge-index.js.map +1 -1
- package/dist/managed-session.d.ts.map +1 -1
- package/dist/managed-session.js +61 -2
- package/dist/managed-session.js.map +1 -1
- package/dist/management-client.d.ts +81 -3
- package/dist/management-client.d.ts.map +1 -1
- package/dist/management-client.js +97 -20
- package/dist/management-client.js.map +1 -1
- package/dist/orchestration/agents.d.ts +4 -1
- package/dist/orchestration/agents.d.ts.map +1 -1
- package/dist/orchestration/agents.js +27 -4
- package/dist/orchestration/agents.js.map +1 -1
- package/dist/orchestration/index.d.ts +2 -2
- package/dist/orchestration/index.js +1 -1
- package/dist/orchestration/lifecycle.d.ts +3 -0
- package/dist/orchestration/lifecycle.d.ts.map +1 -1
- package/dist/orchestration/lifecycle.js +6 -1
- package/dist/orchestration/lifecycle.js.map +1 -1
- package/dist/orchestration/queue.d.ts.map +1 -1
- package/dist/orchestration/queue.js +10 -1
- package/dist/orchestration/queue.js.map +1 -1
- package/dist/orchestration/runtime.d.ts +1 -1
- package/dist/orchestration/state.d.ts +1 -0
- package/dist/orchestration/state.d.ts.map +1 -1
- package/dist/orchestration/state.js +1 -0
- package/dist/orchestration/state.js.map +1 -1
- package/dist/orchestration/turn.d.ts +2 -2
- package/dist/orchestration/turn.d.ts.map +1 -1
- package/dist/orchestration/turn.js +42 -12
- package/dist/orchestration/turn.js.map +1 -1
- package/dist/orchestration-registry.d.ts.map +1 -1
- package/dist/orchestration-registry.js +4 -2
- package/dist/orchestration-registry.js.map +1 -1
- package/dist/orchestration-version.d.ts +1 -1
- package/dist/orchestration-version.js +1 -1
- package/dist/orchestration.d.ts +2 -2
- package/dist/orchestration.js +1 -1
- package/dist/orchestration_1_0_46.d.ts +1 -1
- package/dist/orchestration_1_0_47.d.ts +1 -1
- package/dist/orchestration_1_0_48.d.ts +1 -1
- package/dist/orchestration_1_0_49.d.ts +1 -1
- package/dist/orchestration_1_0_50.d.ts +1 -1
- package/dist/orchestration_1_0_52/agents.d.ts +38 -0
- package/dist/orchestration_1_0_52/agents.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/agents.js +735 -0
- package/dist/orchestration_1_0_52/agents.js.map +1 -0
- package/dist/orchestration_1_0_52/index.d.ts +24 -0
- package/dist/orchestration_1_0_52/index.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/index.js +13 -0
- package/dist/orchestration_1_0_52/index.js.map +1 -0
- package/dist/orchestration_1_0_52/lifecycle.d.ts +38 -0
- package/dist/orchestration_1_0_52/lifecycle.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/lifecycle.js +504 -0
- package/dist/orchestration_1_0_52/lifecycle.js.map +1 -0
- package/dist/orchestration_1_0_52/queue.d.ts +7 -0
- package/dist/orchestration_1_0_52/queue.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/queue.js +635 -0
- package/dist/orchestration_1_0_52/queue.js.map +1 -0
- package/dist/orchestration_1_0_52/runtime.d.ts +29 -0
- package/dist/orchestration_1_0_52/runtime.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/runtime.js +190 -0
- package/dist/orchestration_1_0_52/runtime.js.map +1 -0
- package/dist/orchestration_1_0_52/state.d.ts +124 -0
- package/dist/orchestration_1_0_52/state.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/state.js +104 -0
- package/dist/orchestration_1_0_52/state.js.map +1 -0
- package/dist/orchestration_1_0_52/turn.d.ts +6 -0
- package/dist/orchestration_1_0_52/turn.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/turn.js +834 -0
- package/dist/orchestration_1_0_52/turn.js.map +1 -0
- package/dist/orchestration_1_0_52/utils.d.ts +22 -0
- package/dist/orchestration_1_0_52/utils.d.ts.map +1 -0
- package/dist/orchestration_1_0_52/utils.js +226 -0
- package/dist/orchestration_1_0_52/utils.js.map +1 -0
- package/dist/resourcemgr-tools.d.ts +2 -2
- package/dist/resourcemgr-tools.d.ts.map +1 -1
- package/dist/resourcemgr-tools.js +1 -1
- package/dist/resourcemgr-tools.js.map +1 -1
- package/dist/session-dumper.d.ts +2 -2
- package/dist/session-dumper.d.ts.map +1 -1
- package/dist/session-dumper.js.map +1 -1
- package/dist/session-manager.d.ts +8 -2
- package/dist/session-manager.d.ts.map +1 -1
- package/dist/session-manager.js +86 -4
- package/dist/session-manager.js.map +1 -1
- package/dist/session-messages.d.ts +2 -2
- package/dist/session-messages.d.ts.map +1 -1
- package/dist/session-messages.js +0 -6
- package/dist/session-messages.js.map +1 -1
- package/dist/session-proxy.d.ts +47 -2
- package/dist/session-proxy.d.ts.map +1 -1
- package/dist/session-proxy.js +79 -59
- package/dist/session-proxy.js.map +1 -1
- package/dist/storage-config.d.ts +62 -0
- package/dist/storage-config.d.ts.map +1 -0
- package/dist/storage-config.js +138 -0
- package/dist/storage-config.js.map +1 -0
- package/dist/storage-providers.d.ts +25 -0
- package/dist/storage-providers.d.ts.map +1 -0
- package/dist/storage-providers.js +150 -0
- package/dist/storage-providers.js.map +1 -0
- package/dist/sweeper-tools.d.ts +2 -2
- package/dist/sweeper-tools.d.ts.map +1 -1
- package/dist/system-agents.d.ts +2 -2
- package/dist/system-agents.d.ts.map +1 -1
- package/dist/types.d.ts +103 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/worker.d.ts +5 -4
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +108 -33
- package/dist/worker.js.map +1 -1
- package/package.json +3 -1
- package/plugins/mgmt/agents/agent-tuner.agent.md +56 -4
- package/plugins/mgmt/agents/facts-manager.agent.md +81 -3
- package/plugins/mgmt/skills/graph-debug/SKILL.md +133 -0
|
@@ -0,0 +1,735 @@
|
|
|
1
|
+
import { clearPendingChildDigest, publishStatus, queueFollowup, writeCommandResponse, } from "./lifecycle.js";
|
|
2
|
+
import { MAX_NESTING_LEVEL, MAX_SUB_AGENTS, SHUTDOWN_POLL_INTERVAL_MS, SHUTDOWN_TIMEOUT_MS, } from "./state.js";
|
|
3
|
+
// ─── Pure helpers ───────────────────────────────────────────
|
|
4
|
+
export function isSubAgentTerminalStatus(status) {
|
|
5
|
+
return status === "completed" || status === "failed" || status === "cancelled";
|
|
6
|
+
}
|
|
7
|
+
export function parseChildUpdate(promptText) {
|
|
8
|
+
if (typeof promptText !== "string")
|
|
9
|
+
return null;
|
|
10
|
+
const match = promptText.match(/^\[CHILD_UPDATE from=(\S+) type=(\S+)/);
|
|
11
|
+
if (!match)
|
|
12
|
+
return null;
|
|
13
|
+
return {
|
|
14
|
+
sessionId: match[1],
|
|
15
|
+
updateType: match[2].replace(/\]$/, ""),
|
|
16
|
+
content: promptText.split("\n").slice(1).join("\n").trim(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export function defaultShutdownReason(mode) {
|
|
20
|
+
switch (mode) {
|
|
21
|
+
case "done":
|
|
22
|
+
return "Completed by user";
|
|
23
|
+
case "cancel":
|
|
24
|
+
return "Cancelled by user";
|
|
25
|
+
case "delete":
|
|
26
|
+
return "Deleted by user";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function buildShutdownWaitReason(shutdown) {
|
|
30
|
+
switch (shutdown.mode) {
|
|
31
|
+
case "done":
|
|
32
|
+
return `Waiting for ${shutdown.targetAgentIds.length} child session(s) to complete before closing.`;
|
|
33
|
+
case "cancel":
|
|
34
|
+
return `Waiting for ${shutdown.targetAgentIds.length} child session(s) to cancel before closing.`;
|
|
35
|
+
case "delete":
|
|
36
|
+
return `Waiting for ${shutdown.targetAgentIds.length} child session(s) to cancel before deletion.`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function findTrackedAgentByOrchId(subAgents, orchId) {
|
|
40
|
+
return subAgents.find((agent) => agent.orchId === orchId);
|
|
41
|
+
}
|
|
42
|
+
export function areTrackedAgentsTerminal(subAgents, agentIds) {
|
|
43
|
+
return agentIds.every((agentId) => {
|
|
44
|
+
const agent = findTrackedAgentByOrchId(subAgents, agentId);
|
|
45
|
+
return Boolean(agent && isSubAgentTerminalStatus(agent.status));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
export function getStillRunningAgentIds(subAgents, agentIds) {
|
|
49
|
+
return agentIds.filter((agentId) => {
|
|
50
|
+
const agent = findTrackedAgentByOrchId(subAgents, agentId);
|
|
51
|
+
return agent && !isSubAgentTerminalStatus(agent.status);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
export function buildWaitForAgentsFollowup(subAgents, targetIds) {
|
|
55
|
+
const summaries = targetIds
|
|
56
|
+
.map((targetId) => subAgents.find((agent) => agent.orchId === targetId))
|
|
57
|
+
.filter((agent) => Boolean(agent))
|
|
58
|
+
.map((agent) => ` - Agent ${agent.orchId}\n` +
|
|
59
|
+
` Task: "${agent.task.slice(0, 120)}"\n` +
|
|
60
|
+
` Status: ${agent.status}\n` +
|
|
61
|
+
` Result: ${agent.result ?? "(no result)"}`);
|
|
62
|
+
if (summaries.length === 0) {
|
|
63
|
+
return `[SYSTEM: No tracked sub-agents produced a completion summary.]`;
|
|
64
|
+
}
|
|
65
|
+
if (summaries.length === 1) {
|
|
66
|
+
return `[SYSTEM: Sub-agent completed. If the user asked you to relay the child's final output, return the single sub-agent Result text verbatim.\n${summaries[0]}]`;
|
|
67
|
+
}
|
|
68
|
+
return `[SYSTEM: Sub-agents completed:\n${summaries.join("\n")}]`;
|
|
69
|
+
}
|
|
70
|
+
export function buildSubAgentSystemMessage(options) {
|
|
71
|
+
const { parentSessionId, childNestingLevel, maxNestingLevel, agentTask, agentIsSystem, parentSystemMessage, } = options;
|
|
72
|
+
const canSpawnMore = childNestingLevel < maxNestingLevel;
|
|
73
|
+
const timingInstruction = agentIsSystem
|
|
74
|
+
? `- For recurring or periodic work, use the \`cron\` or \`cron_at\` tool instead of ending every cycle with \`wait\`. ` +
|
|
75
|
+
`Call \`cron(seconds=<N>, reason="...")\` to start or update the durable recurring schedule, ` +
|
|
76
|
+
`or \`cron_at(minute=<M>, hour=<H>, tz="...", reason="...")\` for wall-clock schedules. ` +
|
|
77
|
+
`then finish turns normally so the orchestration wakes you automatically on each cron cycle. ` +
|
|
78
|
+
`Use \`wait\` only for one-shot delays inside a turn. ` +
|
|
79
|
+
`Call \`cron(action="cancel")\` or \`cron_at(action="cancel")\` only when you intentionally want to stop the recurring loop.\n`
|
|
80
|
+
: `- For ANY waiting, sleeping, delaying, or scheduling, you MUST use the \`wait\`, \`wait_on_worker\`, \`cron\`, or \`cron_at\` tools. ` +
|
|
81
|
+
`Use \`wait\` or \`wait_on_worker\` for one-shot delays. Use \`cron\` for fixed intervals and \`cron_at\` for wall-clock schedules. ` +
|
|
82
|
+
`Do NOT burn tokens polling inside one LLM turn; after a brief immediate re-check at most, yield with a durable timer. ` +
|
|
83
|
+
`NEVER use setTimeout, sleep, setInterval, or any other timing mechanism. ` +
|
|
84
|
+
`Durable waits survive process restarts.\n`;
|
|
85
|
+
const subAgentPreamble = `[SUB-AGENT CONTEXT]\n` +
|
|
86
|
+
`You are a sub-agent spawned by a parent session (ID: session-${parentSessionId}).\n` +
|
|
87
|
+
`Your nesting level: ${childNestingLevel} (max: ${maxNestingLevel}).\n` +
|
|
88
|
+
`Your task: "${agentTask.slice(0, 500)}"\n\n` +
|
|
89
|
+
`Instructions:\n` +
|
|
90
|
+
`- Focus exclusively on your assigned task.\n` +
|
|
91
|
+
`- Your final response will be automatically forwarded to the parent agent.\n` +
|
|
92
|
+
`- Be thorough but concise — the parent will synthesize results from multiple agents.\n` +
|
|
93
|
+
`- Do NOT ask the user for input — you are autonomous.\n` +
|
|
94
|
+
`- You are autonomous and goal-driven. If the task implies ongoing monitoring or follow-through until done, keep yourself alive with durable timers until the goal is complete or you can no longer make progress.\n` +
|
|
95
|
+
`- If it is ambiguous whether the task should become a long-running recurring workflow, report that ambiguity back to the parent instead of guessing or asking the user directly.\n` +
|
|
96
|
+
`- When your task is complete, provide a clear summary of your findings/results. Your final assistant message is automatically forwarded to the parent.\n` +
|
|
97
|
+
`- After you finish a task you stay ALIVE and idle, ready for the parent to send you a follow-up via \`message_agent\`. You are NOT auto-terminated when you produce a final answer.\n` +
|
|
98
|
+
`- Only the parent decides when you are no longer needed. The parent will close you with \`complete_agent\`, \`cancel_agent\`, or \`delete_agent\`. Do not assume you have been shut down just because you produced a final reply.\n` +
|
|
99
|
+
`- Prefer using \`store_fact\` for larger structured context handoffs across your spawn tree. Put the durable details in facts, then pass fact keys or \`read_facts\` pointers in messages/prompts instead of pasting large context blobs. Sibling and cousin agents under the same root can read your session-scoped facts directly via \`read_facts\` — you do NOT need to mark them \`shared=true\` just to share with peers.\n` +
|
|
100
|
+
`- Do NOT assume the local filesystem persists. Files written with \`bash\` are tied to one worker pod and may vanish on the next turn, after a durable wait, or on worker restart — and they are not visible to your parent, siblings, or other sub-agents. If something needs to outlive the turn or be shared, use \`write_artifact\` + \`export_artifact\` (for files) or \`store_fact\` (for structured state).\n` +
|
|
101
|
+
`- If you write any files with write_artifact, you MUST also call export_artifact and include the artifact:// link in your response.\n` +
|
|
102
|
+
`- If you override a sub-agent model, you MUST first call list_available_models in this session and use only an exact provider:model value returned there. ` +
|
|
103
|
+
`NEVER invent, guess, shorten, or reuse a stale model name.\n` +
|
|
104
|
+
`- Worker-managed system agents are not valid spawn targets. If you expect one and it is missing, report that the workers likely need to be restarted.\n` +
|
|
105
|
+
timingInstruction +
|
|
106
|
+
(canSpawnMore
|
|
107
|
+
? `- If your parent task explicitly asks you to spawn sub-agents, delegate, fan out, or parallelize work, you SHOULD do so within runtime limits instead of collapsing the task into a direct answer. ` +
|
|
108
|
+
`If delegation was not explicitly requested, use your judgment and avoid unnecessary fan-out. ` +
|
|
109
|
+
`You have ${maxNestingLevel - childNestingLevel} level(s) of nesting remaining. After spawning, call wait_for_agents to block until they finish.\n`
|
|
110
|
+
: `- You CANNOT spawn sub-agents — you are at the maximum nesting depth. Handle everything directly.\n`);
|
|
111
|
+
return subAgentPreamble + (parentSystemMessage ? "\n\n" + parentSystemMessage : "");
|
|
112
|
+
}
|
|
113
|
+
// ─── Child agent tracking ───────────────────────────────────
|
|
114
|
+
export function* applyChildUpdate(runtime, update) {
|
|
115
|
+
runtime.ctx.traceInfo(`[orch] child update from=${update.sessionId} type=${update.updateType}`);
|
|
116
|
+
const agent = runtime.state.subAgents.find(a => a.sessionId === update.sessionId);
|
|
117
|
+
if (!agent) {
|
|
118
|
+
runtime.ctx.traceInfo(`[orch] ignoring child update from untracked session ${update.sessionId}`);
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (update.content) {
|
|
122
|
+
agent.result = update.content.slice(0, 2000);
|
|
123
|
+
}
|
|
124
|
+
if (update.updateType === "completed") {
|
|
125
|
+
agent.status = "completed";
|
|
126
|
+
}
|
|
127
|
+
else if (update.updateType === "cancelled" || update.updateType === "deleted") {
|
|
128
|
+
agent.status = "cancelled";
|
|
129
|
+
}
|
|
130
|
+
else if (update.updateType === "failed") {
|
|
131
|
+
agent.status = "failed";
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const rawStatus = yield runtime.manager.getSessionStatus(agent.sessionId);
|
|
135
|
+
const parsed = JSON.parse(rawStatus);
|
|
136
|
+
if (parsed.status === "failed") {
|
|
137
|
+
agent.status = "failed";
|
|
138
|
+
}
|
|
139
|
+
else if (parsed.status === "completed") {
|
|
140
|
+
agent.status = "completed";
|
|
141
|
+
}
|
|
142
|
+
else if (parsed.status === "cancelled") {
|
|
143
|
+
agent.status = "cancelled";
|
|
144
|
+
}
|
|
145
|
+
else if (parsed.status === "waiting") {
|
|
146
|
+
agent.status = "waiting";
|
|
147
|
+
}
|
|
148
|
+
if (parsed.result && parsed.result !== "done") {
|
|
149
|
+
agent.result = parsed.result.slice(0, 2000);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
catch { }
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
export function* refreshTrackedSubAgents(runtime) {
|
|
156
|
+
try {
|
|
157
|
+
const rawChildren = yield runtime.manager.listChildSessions(runtime.input.sessionId);
|
|
158
|
+
const directChildren = JSON.parse(rawChildren);
|
|
159
|
+
runtime.state.subAgents = directChildren
|
|
160
|
+
.filter(child => !child.isSystem)
|
|
161
|
+
.map((child) => {
|
|
162
|
+
const existing = runtime.state.subAgents.find(agent => agent.sessionId === child.sessionId || agent.orchId === child.orchId);
|
|
163
|
+
const contract = child.contract ?? existing?.contract;
|
|
164
|
+
const localStatus = existing?.status;
|
|
165
|
+
if (localStatus && isSubAgentTerminalStatus(localStatus)) {
|
|
166
|
+
return {
|
|
167
|
+
orchId: child.orchId,
|
|
168
|
+
sessionId: child.sessionId,
|
|
169
|
+
task: existing?.task ?? child.title ?? "(spawned sub-agent)",
|
|
170
|
+
status: localStatus,
|
|
171
|
+
result: child.result ?? existing?.result,
|
|
172
|
+
agentId: child.agentId ?? existing?.agentId,
|
|
173
|
+
contract,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
const rawStatus = child.status ?? localStatus ?? "running";
|
|
177
|
+
const normalizedStatus = rawStatus === "failed" ? "failed"
|
|
178
|
+
: rawStatus === "cancelled" ? "cancelled"
|
|
179
|
+
: rawStatus === "waiting" ? "waiting"
|
|
180
|
+
: rawStatus === "completed" ? "completed"
|
|
181
|
+
: "running";
|
|
182
|
+
return {
|
|
183
|
+
orchId: child.orchId,
|
|
184
|
+
sessionId: child.sessionId,
|
|
185
|
+
task: existing?.task ?? child.title ?? "(spawned sub-agent)",
|
|
186
|
+
status: normalizedStatus,
|
|
187
|
+
result: child.result ?? existing?.result,
|
|
188
|
+
agentId: child.agentId ?? existing?.agentId,
|
|
189
|
+
contract,
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
runtime.ctx.traceInfo(`[orch] refreshTrackedSubAgents failed (non-fatal): ${err.message ?? err}`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// ─── Graceful shutdown cascade ──────────────────────────────
|
|
198
|
+
export function* notifyParentOfTerminalState(runtime, updateType, reason) {
|
|
199
|
+
if (!runtime.options.parentSessionId)
|
|
200
|
+
return;
|
|
201
|
+
try {
|
|
202
|
+
yield runtime.manager.sendToSession(runtime.options.parentSessionId, `[CHILD_UPDATE from=${runtime.input.sessionId} type=${updateType} iter=${runtime.state.iteration}]\n${reason}`);
|
|
203
|
+
}
|
|
204
|
+
catch (err) {
|
|
205
|
+
runtime.ctx.traceInfo(`[orch] sendToSession(parent) on ${updateType} failed: ${err.message} (non-fatal)`);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
export function* completeSession(runtime, reason, commandId) {
|
|
209
|
+
runtime.state.pendingShutdown = null;
|
|
210
|
+
runtime.state.waitingForAgentIds = null;
|
|
211
|
+
clearPendingChildDigest(runtime);
|
|
212
|
+
runtime.state.activeTimer = null;
|
|
213
|
+
yield runtime.manager.updateCmsState(runtime.input.sessionId, "completed", null, null);
|
|
214
|
+
publishStatus(runtime, "completed");
|
|
215
|
+
yield* notifyParentOfTerminalState(runtime, "completed", reason);
|
|
216
|
+
try {
|
|
217
|
+
yield runtime.session.destroy();
|
|
218
|
+
}
|
|
219
|
+
catch { }
|
|
220
|
+
if (commandId) {
|
|
221
|
+
const resp = {
|
|
222
|
+
id: commandId,
|
|
223
|
+
cmd: "done",
|
|
224
|
+
result: { ok: true, message: "Session completed" },
|
|
225
|
+
};
|
|
226
|
+
yield* writeCommandResponse(runtime, resp);
|
|
227
|
+
}
|
|
228
|
+
runtime.state.orchestrationResult = "done";
|
|
229
|
+
}
|
|
230
|
+
export function* cancelSession(runtime, reason, commandId, deleteAfterCancel = false) {
|
|
231
|
+
runtime.state.pendingShutdown = null;
|
|
232
|
+
runtime.state.waitingForAgentIds = null;
|
|
233
|
+
clearPendingChildDigest(runtime);
|
|
234
|
+
runtime.state.activeTimer = null;
|
|
235
|
+
const commandName = deleteAfterCancel ? "delete" : "cancel";
|
|
236
|
+
if (!deleteAfterCancel) {
|
|
237
|
+
yield runtime.manager.updateCmsState(runtime.input.sessionId, "cancelled", null, null);
|
|
238
|
+
publishStatus(runtime, "cancelled");
|
|
239
|
+
}
|
|
240
|
+
yield* notifyParentOfTerminalState(runtime, "cancelled", reason);
|
|
241
|
+
try {
|
|
242
|
+
yield runtime.session.destroy();
|
|
243
|
+
}
|
|
244
|
+
catch { }
|
|
245
|
+
if (commandId) {
|
|
246
|
+
const resp = {
|
|
247
|
+
id: commandId,
|
|
248
|
+
cmd: commandName,
|
|
249
|
+
result: {
|
|
250
|
+
ok: true,
|
|
251
|
+
message: deleteAfterCancel ? "Session deleted" : "Session cancelled",
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
yield* writeCommandResponse(runtime, resp);
|
|
255
|
+
}
|
|
256
|
+
if (deleteAfterCancel) {
|
|
257
|
+
const deleteReason = reason || "Deleted by user";
|
|
258
|
+
let descendants = [];
|
|
259
|
+
try {
|
|
260
|
+
descendants = yield runtime.manager.getDescendantSessionIds(runtime.input.sessionId);
|
|
261
|
+
}
|
|
262
|
+
catch (err) {
|
|
263
|
+
runtime.ctx.traceInfo(`[orch] delete: failed to enumerate descendants: ${err.message}`);
|
|
264
|
+
}
|
|
265
|
+
for (const descendantId of descendants) {
|
|
266
|
+
try {
|
|
267
|
+
yield runtime.manager.deleteSession(descendantId, `Ancestor ${runtime.input.sessionId} deleted: ${deleteReason}`);
|
|
268
|
+
}
|
|
269
|
+
catch (err) {
|
|
270
|
+
runtime.ctx.traceInfo(`[orch] delete: failed to delete descendant ${descendantId}: ${err.message} (non-fatal)`);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
try {
|
|
274
|
+
yield runtime.manager.deleteSession(runtime.input.sessionId, deleteReason);
|
|
275
|
+
}
|
|
276
|
+
catch (err) {
|
|
277
|
+
runtime.ctx.traceInfo(`[orch] delete: failed to delete ${runtime.input.sessionId}: ${err.message}`);
|
|
278
|
+
}
|
|
279
|
+
runtime.state.orchestrationResult = "deleted";
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
runtime.state.orchestrationResult = "cancelled";
|
|
283
|
+
}
|
|
284
|
+
export function* failPendingShutdown(runtime, errorMessage) {
|
|
285
|
+
const shutdown = runtime.state.pendingShutdown;
|
|
286
|
+
runtime.state.pendingShutdown = null;
|
|
287
|
+
runtime.state.waitingForAgentIds = null;
|
|
288
|
+
clearPendingChildDigest(runtime);
|
|
289
|
+
runtime.state.activeTimer = null;
|
|
290
|
+
try {
|
|
291
|
+
yield runtime.session.destroy();
|
|
292
|
+
}
|
|
293
|
+
catch { }
|
|
294
|
+
if (shutdown?.commandId) {
|
|
295
|
+
const resp = {
|
|
296
|
+
id: shutdown.commandId,
|
|
297
|
+
cmd: shutdown.mode,
|
|
298
|
+
error: errorMessage,
|
|
299
|
+
};
|
|
300
|
+
yield* writeCommandResponse(runtime, resp);
|
|
301
|
+
}
|
|
302
|
+
publishStatus(runtime, "failed", { error: errorMessage });
|
|
303
|
+
yield runtime.manager.updateCmsState(runtime.input.sessionId, "failed", errorMessage, null);
|
|
304
|
+
runtime.state.orchestrationResult = "failed";
|
|
305
|
+
}
|
|
306
|
+
export function* finalizePendingShutdown(runtime) {
|
|
307
|
+
if (!runtime.state.pendingShutdown)
|
|
308
|
+
return;
|
|
309
|
+
const shutdown = runtime.state.pendingShutdown;
|
|
310
|
+
if (shutdown.mode === "done") {
|
|
311
|
+
yield* completeSession(runtime, shutdown.reason, shutdown.commandId);
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
yield* cancelSession(runtime, shutdown.reason, shutdown.commandId, shutdown.mode === "delete");
|
|
315
|
+
}
|
|
316
|
+
export function* maybeResolveAgentWaitCompletion(runtime) {
|
|
317
|
+
const { state } = runtime;
|
|
318
|
+
if (!state.waitingForAgentIds || !areTrackedAgentsTerminal(state.subAgents, state.waitingForAgentIds)) {
|
|
319
|
+
return false;
|
|
320
|
+
}
|
|
321
|
+
if (state.pendingShutdown) {
|
|
322
|
+
yield* finalizePendingShutdown(runtime);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
queueFollowup(runtime, buildWaitForAgentsFollowup(state.subAgents, state.waitingForAgentIds));
|
|
326
|
+
state.waitingForAgentIds = null;
|
|
327
|
+
clearPendingChildDigest(runtime);
|
|
328
|
+
state.activeTimer = null;
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
export function* beginGracefulShutdown(runtime, mode, cmdMsg) {
|
|
332
|
+
const { state } = runtime;
|
|
333
|
+
if (state.pendingShutdown) {
|
|
334
|
+
const now = yield runtime.ctx.utcNow();
|
|
335
|
+
const resp = {
|
|
336
|
+
id: cmdMsg.id,
|
|
337
|
+
cmd: cmdMsg.cmd,
|
|
338
|
+
result: {
|
|
339
|
+
ok: true,
|
|
340
|
+
message: `Shutdown already in progress (${state.pendingShutdown.mode}).`,
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
yield* writeCommandResponse(runtime, resp);
|
|
344
|
+
publishStatus(runtime, "waiting", {
|
|
345
|
+
waitReason: buildShutdownWaitReason(state.pendingShutdown),
|
|
346
|
+
waitStartedAt: state.pendingShutdown.startedAtMs,
|
|
347
|
+
waitSeconds: Math.max(0, Math.ceil((state.pendingShutdown.deadlineAtMs - now) / 1000)),
|
|
348
|
+
});
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
yield* refreshTrackedSubAgents(runtime);
|
|
352
|
+
const shutdownReason = String(cmdMsg.args?.reason || defaultShutdownReason(mode));
|
|
353
|
+
const targetAgents = state.subAgents.filter((agent) => !isSubAgentTerminalStatus(agent.status));
|
|
354
|
+
if (targetAgents.length === 0) {
|
|
355
|
+
if (mode === "done") {
|
|
356
|
+
yield* completeSession(runtime, shutdownReason, cmdMsg.id);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
yield* cancelSession(runtime, shutdownReason, cmdMsg.id, mode === "delete");
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
const childCmd = mode === "done" ? "done" : "cancel";
|
|
363
|
+
const childReason = mode === "done"
|
|
364
|
+
? "Parent session completing"
|
|
365
|
+
: shutdownReason;
|
|
366
|
+
runtime.ctx.traceInfo(`[orch] ${cmdMsg.cmd}: cascading ${childCmd} to ${targetAgents.length} child session(s)`);
|
|
367
|
+
for (const child of targetAgents) {
|
|
368
|
+
try {
|
|
369
|
+
const childCmdId = `${cmdMsg.cmd}-cascade-${state.iteration}-${child.sessionId.slice(0, 8)}`;
|
|
370
|
+
yield runtime.manager.sendCommandToSession(child.sessionId, { type: "cmd", cmd: childCmd, id: childCmdId, args: { reason: childReason } });
|
|
371
|
+
}
|
|
372
|
+
catch (err) {
|
|
373
|
+
runtime.ctx.traceInfo(`[orch] ${cmdMsg.cmd}: failed to signal child ${child.sessionId}: ${err.message} (non-fatal)`);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
const startedAtMs = yield runtime.ctx.utcNow();
|
|
377
|
+
state.pendingShutdown = {
|
|
378
|
+
mode,
|
|
379
|
+
reason: shutdownReason,
|
|
380
|
+
startedAtMs,
|
|
381
|
+
deadlineAtMs: startedAtMs + SHUTDOWN_TIMEOUT_MS,
|
|
382
|
+
targetAgentIds: targetAgents.map((agent) => agent.orchId),
|
|
383
|
+
commandId: cmdMsg.id,
|
|
384
|
+
};
|
|
385
|
+
state.waitingForAgentIds = [...state.pendingShutdown.targetAgentIds];
|
|
386
|
+
clearPendingChildDigest(runtime);
|
|
387
|
+
state.activeTimer = {
|
|
388
|
+
deadlineMs: startedAtMs + SHUTDOWN_POLL_INTERVAL_MS,
|
|
389
|
+
originalDurationMs: SHUTDOWN_POLL_INTERVAL_MS,
|
|
390
|
+
reason: buildShutdownWaitReason(state.pendingShutdown),
|
|
391
|
+
type: "agent-poll",
|
|
392
|
+
agentIds: state.waitingForAgentIds,
|
|
393
|
+
};
|
|
394
|
+
publishStatus(runtime, "waiting", {
|
|
395
|
+
waitReason: buildShutdownWaitReason(state.pendingShutdown),
|
|
396
|
+
waitStartedAt: startedAtMs,
|
|
397
|
+
waitSeconds: Math.ceil(SHUTDOWN_TIMEOUT_MS / 1000),
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
// ─── Sub-agent tool actions (spawn/message/check/wait/etc.) ─
|
|
401
|
+
export function* handleSubAgentAction(runtime, result) {
|
|
402
|
+
const { ctx, state } = runtime;
|
|
403
|
+
switch (result.type) {
|
|
404
|
+
case "spawn_agent": {
|
|
405
|
+
const childNestingLevel = runtime.options.nestingLevel + 1;
|
|
406
|
+
if (childNestingLevel > MAX_NESTING_LEVEL) {
|
|
407
|
+
ctx.traceInfo(`[orch] spawn_agent denied: nesting level ${runtime.options.nestingLevel} is at max (${MAX_NESTING_LEVEL})`);
|
|
408
|
+
queueFollowup(runtime, `[SYSTEM: spawn_agent failed — you are already at nesting level ${runtime.options.nestingLevel} (max ${MAX_NESTING_LEVEL}). ` +
|
|
409
|
+
`Sub-agents at this depth cannot spawn further sub-agents. Handle the task directly instead.]`);
|
|
410
|
+
return true;
|
|
411
|
+
}
|
|
412
|
+
const activeCount = state.subAgents.filter(a => a.status === "running").length;
|
|
413
|
+
if (activeCount >= MAX_SUB_AGENTS) {
|
|
414
|
+
ctx.traceInfo(`[orch] spawn_agent denied: ${activeCount}/${MAX_SUB_AGENTS} agents running`);
|
|
415
|
+
queueFollowup(runtime, `[SYSTEM: spawn_agent failed — you already have ${activeCount} running sub-agents (max ${MAX_SUB_AGENTS}). ` +
|
|
416
|
+
`Wait for some to complete before spawning more.]`);
|
|
417
|
+
return true;
|
|
418
|
+
}
|
|
419
|
+
let agentTask = result.task;
|
|
420
|
+
let agentSystemMessage = result.systemMessage;
|
|
421
|
+
let agentToolNames = result.toolNames;
|
|
422
|
+
const agentModel = result.model;
|
|
423
|
+
const agentReasoningEffort = result.reasoningEffort;
|
|
424
|
+
let agentIsSystem = false;
|
|
425
|
+
const explicitAgentTitle = typeof result.title === "string" && result.title.trim() ? result.title.trim() : undefined;
|
|
426
|
+
let agentTitle = explicitAgentTitle;
|
|
427
|
+
let agentTitleIsExplicit = Boolean(explicitAgentTitle);
|
|
428
|
+
let agentId;
|
|
429
|
+
let agentSplash;
|
|
430
|
+
let boundAgentName;
|
|
431
|
+
let promptLayeringKind;
|
|
432
|
+
const resolvedAgentName = result.agentName;
|
|
433
|
+
const applyAgentDef = (agentDef, useDefinitionDefaults = false) => {
|
|
434
|
+
agentTask = useDefinitionDefaults
|
|
435
|
+
? (agentDef.initialPrompt || `You are the ${agentDef.name} agent. Begin your work.`)
|
|
436
|
+
: (result.task || agentDef.initialPrompt || `You are the ${agentDef.name} agent. Begin your work.`);
|
|
437
|
+
agentSystemMessage = useDefinitionDefaults ? undefined : result.systemMessage;
|
|
438
|
+
agentToolNames = useDefinitionDefaults
|
|
439
|
+
? (agentDef.tools ?? undefined)
|
|
440
|
+
: (result.toolNames ?? agentDef.tools ?? undefined);
|
|
441
|
+
agentIsSystem = agentDef.system ?? false;
|
|
442
|
+
if (!agentTitleIsExplicit)
|
|
443
|
+
agentTitle = agentDef.title;
|
|
444
|
+
agentId = agentDef.id ?? resolvedAgentName;
|
|
445
|
+
agentSplash = agentDef.splash;
|
|
446
|
+
boundAgentName = agentDef.name;
|
|
447
|
+
promptLayeringKind = agentDef.promptLayerKind
|
|
448
|
+
?? (agentDef.system
|
|
449
|
+
? ((agentDef.namespace || "pilotswarm") === "pilotswarm"
|
|
450
|
+
? "pilotswarm-system-agent"
|
|
451
|
+
: "app-system-agent")
|
|
452
|
+
: "app-agent");
|
|
453
|
+
};
|
|
454
|
+
if (resolvedAgentName) {
|
|
455
|
+
ctx.traceInfo(`[orch] resolving agent config for: ${resolvedAgentName}`);
|
|
456
|
+
const agentDef = yield runtime.manager.resolveAgentConfig(resolvedAgentName);
|
|
457
|
+
if (!agentDef) {
|
|
458
|
+
queueFollowup(runtime, `[SYSTEM: spawn_agent failed — agent "${resolvedAgentName}" not found. Use ps_list_agents to see available agents.]`);
|
|
459
|
+
return true;
|
|
460
|
+
}
|
|
461
|
+
if (agentDef.system && agentDef.creatable === false) {
|
|
462
|
+
queueFollowup(runtime, `[SYSTEM: spawn_agent failed — agent "${resolvedAgentName}" is a worker-managed system agent and cannot be spawned from a session. ` +
|
|
463
|
+
`If it is missing, the workers likely need to be restarted.]`);
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
applyAgentDef(agentDef, resolvedAgentName !== result.agentName);
|
|
467
|
+
}
|
|
468
|
+
if (agentModel && !agentModel.includes(":")) {
|
|
469
|
+
ctx.traceInfo(`[orch] spawn_agent denied: unqualified model override "${agentModel}"`);
|
|
470
|
+
queueFollowup(runtime, `[SYSTEM: spawn_agent failed — model "${agentModel}" is not allowed. ` +
|
|
471
|
+
`When overriding a sub-agent model, first call list_available_models and then use the exact provider:model value from that list. ` +
|
|
472
|
+
`If you are unsure, omit model so the sub-agent inherits your current model.]`);
|
|
473
|
+
return true;
|
|
474
|
+
}
|
|
475
|
+
if (!agentTitle && agentIsSystem) {
|
|
476
|
+
const text = agentTask || "";
|
|
477
|
+
const titleMatch = text.match(/You are the \*{0,2}([^*\n]+?)\*{0,2}\s*[—–-]/i)
|
|
478
|
+
|| text.match(/You are the \*{0,2}([^*\n]+?Agent)\*{0,2}/i);
|
|
479
|
+
if (titleMatch) {
|
|
480
|
+
agentTitle = titleMatch[1].trim();
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
ctx.traceInfo(`[orch] spawning sub-agent via SDK: task="${agentTask.slice(0, 80)}" model=${agentModel || "inherit"} agent=${resolvedAgentName || "custom"} nestingLevel=${childNestingLevel}`);
|
|
484
|
+
const { boundAgentName: _parentBoundAgentName, promptLayering: _parentPromptLayering, ...parentConfig } = state.config;
|
|
485
|
+
const childConfig = {
|
|
486
|
+
...parentConfig,
|
|
487
|
+
...(agentModel ? { model: agentModel } : {}),
|
|
488
|
+
...(agentReasoningEffort ? { reasoningEffort: agentReasoningEffort } : {}),
|
|
489
|
+
...(agentSystemMessage ? { systemMessage: agentSystemMessage } : {}),
|
|
490
|
+
...(boundAgentName ? { boundAgentName } : {}),
|
|
491
|
+
...(promptLayeringKind ? { promptLayering: { kind: promptLayeringKind } } : {}),
|
|
492
|
+
...(agentToolNames ? { toolNames: agentToolNames } : {}),
|
|
493
|
+
...(result.contract ? { childContract: result.contract } : {}),
|
|
494
|
+
};
|
|
495
|
+
const parentSystemMsg = typeof childConfig.systemMessage === "string"
|
|
496
|
+
? childConfig.systemMessage
|
|
497
|
+
: childConfig.systemMessage?.content ?? "";
|
|
498
|
+
childConfig.systemMessage = buildSubAgentSystemMessage({
|
|
499
|
+
parentSessionId: runtime.input.sessionId,
|
|
500
|
+
childNestingLevel,
|
|
501
|
+
maxNestingLevel: MAX_NESTING_LEVEL,
|
|
502
|
+
agentTask,
|
|
503
|
+
agentIsSystem,
|
|
504
|
+
parentSystemMessage: parentSystemMsg,
|
|
505
|
+
});
|
|
506
|
+
let childSessionId;
|
|
507
|
+
try {
|
|
508
|
+
childSessionId = yield runtime.manager.spawnChildSession(runtime.input.sessionId, childConfig, agentTask, childNestingLevel, agentIsSystem, agentTitle, agentId, agentSplash, agentTitleIsExplicit);
|
|
509
|
+
}
|
|
510
|
+
catch (err) {
|
|
511
|
+
ctx.traceInfo(`[orch] spawnChildSession failed: ${err.message}`);
|
|
512
|
+
queueFollowup(runtime, `[SYSTEM: spawn_agent failed: ${err.message}]`);
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
const childOrchId = `session-${childSessionId}`;
|
|
516
|
+
yield runtime.manager.recordSessionEvent(runtime.input.sessionId, [{
|
|
517
|
+
eventType: "session.agent_spawned",
|
|
518
|
+
data: { childSessionId, agentId: agentId || undefined, task: agentTask.slice(0, 500) },
|
|
519
|
+
}]);
|
|
520
|
+
state.subAgents.push({
|
|
521
|
+
orchId: childOrchId,
|
|
522
|
+
sessionId: childSessionId,
|
|
523
|
+
task: agentTask.slice(0, 500),
|
|
524
|
+
status: "running",
|
|
525
|
+
agentId: agentId || undefined,
|
|
526
|
+
contract: result.contract,
|
|
527
|
+
});
|
|
528
|
+
queueFollowup(runtime, `[SYSTEM: Sub-agent spawned successfully.\n` +
|
|
529
|
+
` Agent ID: ${childOrchId}\n` +
|
|
530
|
+
` ${resolvedAgentName ? `Agent: ${resolvedAgentName}\n ` : ``}Task: "${agentTask.slice(0, 200)}"\n` +
|
|
531
|
+
` The agent is now running autonomously. Continue your work in this SAME turn and keep following the user's remaining steps. ` +
|
|
532
|
+
`Do NOT stop just because the child started. If you need to pause, call wait or wait_for_agents explicitly. ` +
|
|
533
|
+
`You can also use check_agents to poll status, ` +
|
|
534
|
+
`or message_agent to send instructions.]`);
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
case "message_agent": {
|
|
538
|
+
const targetOrchId = result.agentId;
|
|
539
|
+
const agentEntry = state.subAgents.find(a => a.orchId === targetOrchId);
|
|
540
|
+
if (!agentEntry) {
|
|
541
|
+
ctx.traceInfo(`[orch] message_agent: unknown agent ${targetOrchId}`);
|
|
542
|
+
queueFollowup(runtime, `[SYSTEM: message_agent failed — agent "${targetOrchId}" not found. ` +
|
|
543
|
+
`Known agents: ${state.subAgents.map(a => a.orchId).join(", ") || "none"}]`);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
546
|
+
ctx.traceInfo(`[orch] message_agent via SDK: ${agentEntry.sessionId} msg="${result.message.slice(0, 60)}"`);
|
|
547
|
+
if (result.contractPatch && typeof result.contractPatch === "object") {
|
|
548
|
+
agentEntry.contract = {
|
|
549
|
+
...(agentEntry.contract ?? {}),
|
|
550
|
+
...result.contractPatch,
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
try {
|
|
554
|
+
yield runtime.manager.sendToSession(agentEntry.sessionId, result.message);
|
|
555
|
+
}
|
|
556
|
+
catch (err) {
|
|
557
|
+
ctx.traceInfo(`[orch] message_agent failed: ${err.message}`);
|
|
558
|
+
queueFollowup(runtime, `[SYSTEM: message_agent failed: ${err.message}]`);
|
|
559
|
+
return true;
|
|
560
|
+
}
|
|
561
|
+
queueFollowup(runtime, `[SYSTEM: Message sent to sub-agent ${targetOrchId}: "${result.message.slice(0, 200)}". ` +
|
|
562
|
+
`Continue your work in this SAME turn. If you are waiting on the child, call wait_for_agents explicitly rather than stopping here.]`);
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
case "check_agents": {
|
|
566
|
+
ctx.traceInfo(`[orch] check_agents: ${state.subAgents.length} agents tracked`);
|
|
567
|
+
if (state.subAgents.length === 0) {
|
|
568
|
+
queueFollowup(runtime, `[SYSTEM: No sub-agents have been spawned yet.]`);
|
|
569
|
+
return true;
|
|
570
|
+
}
|
|
571
|
+
const statusLines = [];
|
|
572
|
+
for (const agent of state.subAgents) {
|
|
573
|
+
try {
|
|
574
|
+
const rawStatus = yield runtime.manager.getSessionStatus(agent.sessionId);
|
|
575
|
+
const parsed = JSON.parse(rawStatus);
|
|
576
|
+
if (parsed.status === "completed" || parsed.status === "failed" || parsed.status === "idle") {
|
|
577
|
+
agent.status = parsed.status === "failed" ? "failed" : "completed";
|
|
578
|
+
if (parsed.result)
|
|
579
|
+
agent.result = parsed.result.slice(0, 1000);
|
|
580
|
+
}
|
|
581
|
+
statusLines.push(` - Agent ${agent.orchId}\n` +
|
|
582
|
+
` Task: "${agent.task.slice(0, 120)}"\n` +
|
|
583
|
+
` Status: ${parsed.status}\n` +
|
|
584
|
+
` Iterations: ${parsed.iterations ?? 0}\n` +
|
|
585
|
+
` Output: ${parsed.result ?? "(no output yet)"}`);
|
|
586
|
+
}
|
|
587
|
+
catch (err) {
|
|
588
|
+
statusLines.push(` - Agent ${agent.orchId}\n` +
|
|
589
|
+
` Task: "${agent.task.slice(0, 120)}"\n` +
|
|
590
|
+
` Status: unknown (error: ${err.message})`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
queueFollowup(runtime, `[SYSTEM: Sub-agent status report (${state.subAgents.length} agents):\n${statusLines.join("\n")}]`);
|
|
594
|
+
return true;
|
|
595
|
+
}
|
|
596
|
+
case "list_sessions": {
|
|
597
|
+
ctx.traceInfo(`[orch] list_sessions`);
|
|
598
|
+
const rawSessions = yield runtime.manager.listSessions({
|
|
599
|
+
includeSystem: result.includeSystem,
|
|
600
|
+
ownerQuery: result.ownerQuery,
|
|
601
|
+
ownerKind: result.ownerKind,
|
|
602
|
+
});
|
|
603
|
+
const sessions = JSON.parse(rawSessions);
|
|
604
|
+
if (!Array.isArray(sessions) || sessions.length === 0) {
|
|
605
|
+
queueFollowup(runtime, "[SYSTEM: Active sessions (0). No sessions matched the requested filters.]");
|
|
606
|
+
return true;
|
|
607
|
+
}
|
|
608
|
+
const lines = sessions.map((s) => ` - ${s.sessionId}${s.sessionId === runtime.input.sessionId ? " (this session)" : ""}\n` +
|
|
609
|
+
` Title: ${s.title ?? "(untitled)"}\n` +
|
|
610
|
+
` Owner: ${s.ownerKind === "system"
|
|
611
|
+
? "system"
|
|
612
|
+
: s.ownerKind === "unowned"
|
|
613
|
+
? "unowned"
|
|
614
|
+
: (s.owner?.displayName || s.owner?.email || [s.owner?.provider, s.owner?.subject].filter(Boolean).join(":") || "user")}\n` +
|
|
615
|
+
` Status: ${s.status}, Iterations: ${s.iterations ?? 0}\n` +
|
|
616
|
+
` Parent: ${s.parentSessionId ?? "none"}`);
|
|
617
|
+
queueFollowup(runtime, `[SYSTEM: Active sessions (${sessions.length}):\n${lines.join("\n")}]`);
|
|
618
|
+
return true;
|
|
619
|
+
}
|
|
620
|
+
case "wait_for_agents": {
|
|
621
|
+
let targetIds = result.agentIds;
|
|
622
|
+
if (!targetIds || targetIds.length === 0) {
|
|
623
|
+
const runningAgentIds = state.subAgents.filter(a => a.status === "running").map(a => a.orchId);
|
|
624
|
+
targetIds = runningAgentIds.length > 0
|
|
625
|
+
? runningAgentIds
|
|
626
|
+
: state.subAgents.map(a => a.orchId);
|
|
627
|
+
}
|
|
628
|
+
if (targetIds.length === 0) {
|
|
629
|
+
ctx.traceInfo(`[orch] wait_for_agents: no running agents to wait for`);
|
|
630
|
+
queueFollowup(runtime, `[SYSTEM: No running sub-agents to wait for. All agents have already completed.]`);
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
const stillRunning = targetIds.filter((id) => {
|
|
634
|
+
const agent = state.subAgents.find(a => a.orchId === id);
|
|
635
|
+
return agent && !isSubAgentTerminalStatus(agent.status);
|
|
636
|
+
});
|
|
637
|
+
if (stillRunning.length === 0) {
|
|
638
|
+
queueFollowup(runtime, buildWaitForAgentsFollowup(state.subAgents, targetIds));
|
|
639
|
+
return true;
|
|
640
|
+
}
|
|
641
|
+
ctx.traceInfo(`[orch] wait_for_agents: waiting for ${targetIds.length} agents`);
|
|
642
|
+
publishStatus(runtime, "running");
|
|
643
|
+
state.waitingForAgentIds = targetIds;
|
|
644
|
+
const agentPollNow = yield ctx.utcNow();
|
|
645
|
+
state.activeTimer = {
|
|
646
|
+
deadlineMs: agentPollNow + 30_000,
|
|
647
|
+
originalDurationMs: 30_000,
|
|
648
|
+
reason: `waiting for ${targetIds.length} agent(s)`,
|
|
649
|
+
type: "agent-poll",
|
|
650
|
+
agentIds: targetIds,
|
|
651
|
+
};
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
case "complete_agent": {
|
|
655
|
+
const targetOrchId = result.agentId;
|
|
656
|
+
const agentEntry = state.subAgents.find(a => a.orchId === targetOrchId);
|
|
657
|
+
if (!agentEntry) {
|
|
658
|
+
ctx.traceInfo(`[orch] complete_agent: unknown agent ${targetOrchId}`);
|
|
659
|
+
queueFollowup(runtime, `[SYSTEM: complete_agent failed — agent "${targetOrchId}" not found. ` +
|
|
660
|
+
`Known agents: ${state.subAgents.map(a => a.orchId).join(", ") || "none"}]`);
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
ctx.traceInfo(`[orch] complete_agent: sending /done to ${agentEntry.sessionId}`);
|
|
664
|
+
try {
|
|
665
|
+
const cmdId = `done-${state.iteration}`;
|
|
666
|
+
yield runtime.manager.sendCommandToSession(agentEntry.sessionId, { type: "cmd", cmd: "done", id: cmdId, args: { reason: "Completed by parent" } });
|
|
667
|
+
}
|
|
668
|
+
catch (err) {
|
|
669
|
+
ctx.traceInfo(`[orch] complete_agent failed: ${err.message}`);
|
|
670
|
+
queueFollowup(runtime, `[SYSTEM: complete_agent failed: ${err.message}]`);
|
|
671
|
+
return true;
|
|
672
|
+
}
|
|
673
|
+
queueFollowup(runtime, `[SYSTEM: Graceful completion requested for sub-agent ${targetOrchId}. ` +
|
|
674
|
+
`Use check_agents or wait_for_agents to observe final completion.]`);
|
|
675
|
+
return true;
|
|
676
|
+
}
|
|
677
|
+
case "cancel_agent": {
|
|
678
|
+
const targetOrchId = result.agentId;
|
|
679
|
+
const agentEntry = state.subAgents.find(a => a.orchId === targetOrchId);
|
|
680
|
+
if (!agentEntry) {
|
|
681
|
+
ctx.traceInfo(`[orch] cancel_agent: unknown agent ${targetOrchId}`);
|
|
682
|
+
queueFollowup(runtime, `[SYSTEM: cancel_agent failed — agent "${targetOrchId}" not found. ` +
|
|
683
|
+
`Known agents: ${state.subAgents.map(a => a.orchId).join(", ") || "none"}]`);
|
|
684
|
+
return true;
|
|
685
|
+
}
|
|
686
|
+
const cancelReason = result.reason ?? "Cancelled by parent";
|
|
687
|
+
ctx.traceInfo(`[orch] cancel_agent: sending cancel to ${agentEntry.sessionId} reason="${cancelReason}"`);
|
|
688
|
+
try {
|
|
689
|
+
const cmdId = `cancel-${state.iteration}-${agentEntry.sessionId.slice(0, 8)}`;
|
|
690
|
+
yield runtime.manager.sendCommandToSession(agentEntry.sessionId, { type: "cmd", cmd: "cancel", id: cmdId, args: { reason: cancelReason } });
|
|
691
|
+
}
|
|
692
|
+
catch (err) {
|
|
693
|
+
ctx.traceInfo(`[orch] cancel_agent failed: ${err.message}`);
|
|
694
|
+
queueFollowup(runtime, `[SYSTEM: cancel_agent failed: ${err.message}]`);
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
queueFollowup(runtime, `[SYSTEM: Graceful cancellation requested for sub-agent ${targetOrchId}. ` +
|
|
698
|
+
`Use check_agents or wait_for_agents to observe final termination.${result.reason ? ` Reason: ${result.reason}` : ""}]`);
|
|
699
|
+
return true;
|
|
700
|
+
}
|
|
701
|
+
case "delete_agent": {
|
|
702
|
+
const targetOrchId = result.agentId;
|
|
703
|
+
const agentEntry = state.subAgents.find(a => a.orchId === targetOrchId);
|
|
704
|
+
if (!agentEntry) {
|
|
705
|
+
ctx.traceInfo(`[orch] delete_agent: unknown agent ${targetOrchId}`);
|
|
706
|
+
queueFollowup(runtime, `[SYSTEM: delete_agent failed — agent "${targetOrchId}" not found. ` +
|
|
707
|
+
`Known agents: ${state.subAgents.map(a => a.orchId).join(", ") || "none"}]`);
|
|
708
|
+
return true;
|
|
709
|
+
}
|
|
710
|
+
const deleteReason = result.reason ?? "Deleted by parent";
|
|
711
|
+
ctx.traceInfo(`[orch] delete_agent: deleting ${agentEntry.sessionId} reason="${deleteReason}"`);
|
|
712
|
+
try {
|
|
713
|
+
if (isSubAgentTerminalStatus(agentEntry.status)) {
|
|
714
|
+
yield runtime.manager.deleteSession(agentEntry.sessionId, deleteReason);
|
|
715
|
+
state.subAgents = state.subAgents.filter((agent) => agent.orchId !== targetOrchId);
|
|
716
|
+
queueFollowup(runtime, `[SYSTEM: Sub-agent ${targetOrchId} has been deleted.${result.reason ? ` Reason: ${result.reason}` : ""}]`);
|
|
717
|
+
return true;
|
|
718
|
+
}
|
|
719
|
+
const cmdId = `delete-${state.iteration}-${agentEntry.sessionId.slice(0, 8)}`;
|
|
720
|
+
yield runtime.manager.sendCommandToSession(agentEntry.sessionId, { type: "cmd", cmd: "delete", id: cmdId, args: { reason: deleteReason } });
|
|
721
|
+
}
|
|
722
|
+
catch (err) {
|
|
723
|
+
ctx.traceInfo(`[orch] delete_agent failed: ${err.message}`);
|
|
724
|
+
queueFollowup(runtime, `[SYSTEM: delete_agent failed: ${err.message}]`);
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
queueFollowup(runtime, `[SYSTEM: Graceful deletion requested for sub-agent ${targetOrchId}. ` +
|
|
728
|
+
`It will cancel its descendants first and then delete itself.${result.reason ? ` Reason: ${result.reason}` : ""}]`);
|
|
729
|
+
return true;
|
|
730
|
+
}
|
|
731
|
+
default:
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
//# sourceMappingURL=agents.js.map
|