@akira-tl/forgerelay 0.6.1 → 0.7.1

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.
Files changed (63) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +38 -0
  3. package/capabilities/subagents/GUIDE.md +100 -40
  4. package/dist/activity/lifecycle.js +1 -1
  5. package/dist/activity/mcp-query-tools.js +1 -0
  6. package/dist/activity/query-service.js +1 -0
  7. package/dist/capabilities.js +1 -1
  8. package/dist/capability-registry.js +34 -0
  9. package/dist/cli.js +80 -165
  10. package/dist/composite-activity.js +155 -0
  11. package/dist/composite-workspaces.js +197 -0
  12. package/dist/db/migrations.js +14 -0
  13. package/dist/db/schema.js +6 -0
  14. package/dist/remote-workspace-relay.js +16 -0
  15. package/dist/server.js +698 -172
  16. package/dist/{local-agent-targets.js → subagents/cli-target.js} +6 -6
  17. package/dist/{local-agent-profiles.js → subagents/profiles.js} +13 -5
  18. package/dist/subagents/providers/adapters/acp.js +148 -0
  19. package/dist/subagents/providers/adapters/claude.js +75 -0
  20. package/dist/{local-agent-runtime.js → subagents/providers/adapters/codex.js} +10 -4
  21. package/dist/subagents/providers/adapters/opencode.js +137 -0
  22. package/dist/subagents/providers/adapters/pi.js +232 -0
  23. package/dist/{local-agent-availability.js → subagents/providers/availability.js} +24 -10
  24. package/dist/subagents/providers/continuation.js +11 -0
  25. package/dist/subagents/providers/contract.js +1 -0
  26. package/dist/subagents/providers/registry.js +26 -0
  27. package/dist/subagents/providers/shared.js +40 -0
  28. package/dist/subagents/sessions/capability.js +214 -0
  29. package/dist/subagents/sessions/delivery-mailbox.js +115 -0
  30. package/dist/subagents/sessions/execution.js +171 -0
  31. package/dist/subagents/sessions/manager.js +107 -0
  32. package/dist/subagents/sessions/mcp/audit.js +85 -0
  33. package/dist/subagents/sessions/mcp/runtime.js +19 -0
  34. package/dist/{local-agent-store.js → subagents/sessions/store.js} +75 -39
  35. package/dist/ui/.vite/manifest.json +33 -33
  36. package/dist/ui/activity-panel-app.html +3 -3
  37. package/dist/ui/assets/{activity-panel-app-CjZVvVNc.js → activity-panel-app-E1ju2dqI.js} +1 -1
  38. package/dist/ui/assets/{heavy-payload-vGgBRvNX.js → heavy-payload-CeW-n9w5.js} +1 -1
  39. package/dist/ui/assets/{review-payload-4erWKckt.js → review-payload-B9CO298v.js} +1 -1
  40. package/dist/ui/assets/{scrollbar-CaOPzUJd.js → scrollbar-C2twAENW.js} +1 -1
  41. package/dist/ui/assets/workspace-app-BztEvZIC.js +5 -0
  42. package/dist/ui/assets/{workspace-app-DkAiSl_0.js → workspace-app-CwbJnb_w.js} +1 -1
  43. package/dist/ui/assets/workspace-app-YnUST8IP.css +1 -0
  44. package/dist/ui/assets/workspace-app-rKuhdae8.js +1 -0
  45. package/dist/ui/assets/workspace-lifecycle-app-CEfMdudP.js +1 -0
  46. package/dist/ui/workspace-app.html +4 -4
  47. package/dist/ui/workspace-lifecycle-app.html +4 -4
  48. package/dist/workspaces.js +3 -3
  49. package/docs/chatgpt-coding-workflow.md +2 -9
  50. package/docs/configuration.md +23 -0
  51. package/docs/debugging.md +7 -0
  52. package/docs/roadmap.md +42 -7
  53. package/package.json +2 -2
  54. package/scripts/debug/runtime.mjs +23 -1
  55. package/scripts/debug/runtime.test.mjs +14 -2
  56. package/scripts/debug/serve.mjs +4 -4
  57. package/scripts/release/release-gate.test.mjs +2 -2
  58. package/dist/local-agent-adapters.js +0 -653
  59. package/dist/ui/assets/workspace-app-CcrHAUIn.css +0 -1
  60. package/dist/ui/assets/workspace-app-DJmkPYJC.js +0 -1
  61. package/dist/ui/assets/workspace-app-QyauBrJX.js +0 -5
  62. package/dist/ui/assets/workspace-lifecycle-app-BIXEo53I.js +0 -1
  63. /package/dist/{local-agent-path.js → subagents/providers/path.js} +0 -0
@@ -0,0 +1,232 @@
1
+ import { spawn } from "node:child_process";
2
+ import { removeDevspaceNodeModulesBinFromPath } from "../path.js";
3
+ import { asRecord, assertPipedChild, errorMessage, readArray, readNestedString, requireFinalResponse, unwrapProviderPayload, } from "../shared.js";
4
+ const PI_AGENT_TIMEOUT_MS = 120_000;
5
+ export class PiRpcSubagentAdapter {
6
+ provider = "pi";
7
+ async run(input) {
8
+ const args = ["--mode", "rpc"];
9
+ if (input.model)
10
+ args.push("--model", input.model);
11
+ if (input.thinking)
12
+ args.push("--thinking", input.thinking);
13
+ if (input.providerSessionId)
14
+ args.push("--session", input.providerSessionId);
15
+ const child = spawn(process.env.PI_COMMAND ?? "pi", args, {
16
+ cwd: input.workspace,
17
+ env: piCommandEnvironment(process.env),
18
+ stdio: ["pipe", "pipe", "pipe"],
19
+ windowsHide: true,
20
+ });
21
+ assertPipedChild(child);
22
+ const rpc = new JsonLineRpc(child);
23
+ let streamingText = "";
24
+ let streamingProviderError = "";
25
+ rpc.onEvent((event) => {
26
+ const text = extractPiStreamingText([event]);
27
+ if (text)
28
+ streamingText += text;
29
+ const providerError = extractPiProviderError(event);
30
+ if (providerError)
31
+ streamingProviderError = providerError;
32
+ });
33
+ try {
34
+ const state = await rpc.request({ type: "get_state" });
35
+ const providerSessionId = readNestedString(state, ["sessionId"]) ?? input.providerSessionId ?? null;
36
+ const done = rpc.waitForEvent((event) => asRecord(event)?.type === "agent_end", PI_AGENT_TIMEOUT_MS);
37
+ await rpc.request({ type: "prompt", message: input.prompt });
38
+ const agentEnd = await done;
39
+ const sessionMessages = await rpc.request({ type: "get_messages" });
40
+ const finalResponse = extractPiFinalResponse(agentEnd) ||
41
+ extractPiFinalResponse(sessionMessages) ||
42
+ streamingText.trim();
43
+ if (!finalResponse) {
44
+ const providerError = extractPiProviderError(agentEnd) ||
45
+ extractPiProviderError(sessionMessages) ||
46
+ streamingProviderError;
47
+ if (providerError)
48
+ throw new Error(`Pi returned an error: ${providerError}`);
49
+ }
50
+ requireFinalResponse("Pi", finalResponse);
51
+ return {
52
+ provider: this.provider,
53
+ providerSessionId,
54
+ finalResponse,
55
+ };
56
+ }
57
+ finally {
58
+ child.kill();
59
+ }
60
+ }
61
+ }
62
+ export function piCommandEnvironment(env) {
63
+ if (env.PI_COMMAND)
64
+ return env;
65
+ const path = env.PATH;
66
+ if (!path)
67
+ return env;
68
+ return {
69
+ ...env,
70
+ PATH: removeDevspaceNodeModulesBinFromPath(path),
71
+ };
72
+ }
73
+ class JsonLineRpc {
74
+ child;
75
+ pending = new Map();
76
+ eventSubscribers = new Set();
77
+ buffer = "";
78
+ nextId = 1;
79
+ stderr = "";
80
+ fatalError;
81
+ constructor(child) {
82
+ this.child = child;
83
+ child.stdout.on("data", (chunk) => this.handleStdout(chunk.toString("utf8")));
84
+ child.stderr.on("data", (chunk) => {
85
+ this.stderr += chunk.toString("utf8");
86
+ });
87
+ child.on("exit", (code, signal) => {
88
+ this.failAll(new Error(`Pi RPC process exited with code ${code ?? "null"} and signal ${signal ?? "null"}\n${this.stderr}`.trim()));
89
+ });
90
+ }
91
+ request(command) {
92
+ if (this.fatalError) {
93
+ return Promise.reject(this.fatalError);
94
+ }
95
+ const id = `req_${this.nextId}`;
96
+ this.nextId += 1;
97
+ return new Promise((resolve, reject) => {
98
+ this.pending.set(id, { resolve, reject });
99
+ this.child.stdin.write(`${JSON.stringify({ ...command, id })}\n`);
100
+ });
101
+ }
102
+ onEvent(callback) {
103
+ this.eventSubscribers.add(callback);
104
+ return () => this.eventSubscribers.delete(callback);
105
+ }
106
+ waitForEvent(predicate, timeoutMs) {
107
+ return new Promise((resolve, reject) => {
108
+ const timer = setTimeout(() => {
109
+ unsubscribe();
110
+ reject(new Error(`Pi RPC timed out waiting for agent completion\n${this.stderr}`.trim()));
111
+ }, timeoutMs);
112
+ const unsubscribe = this.onEvent((event) => {
113
+ if (!predicate(event))
114
+ return;
115
+ clearTimeout(timer);
116
+ unsubscribe();
117
+ resolve(event);
118
+ });
119
+ });
120
+ }
121
+ handleStdout(chunk) {
122
+ this.buffer += chunk;
123
+ for (;;) {
124
+ const newline = this.buffer.indexOf("\n");
125
+ if (newline === -1)
126
+ return;
127
+ const line = this.buffer.slice(0, newline).trim();
128
+ this.buffer = this.buffer.slice(newline + 1);
129
+ if (!line)
130
+ continue;
131
+ let message;
132
+ try {
133
+ message = JSON.parse(line);
134
+ }
135
+ catch {
136
+ this.stderr += `${line}\n`;
137
+ this.failAll(new Error(`Pi RPC emitted malformed JSON on stdout: ${line}`));
138
+ return;
139
+ }
140
+ if (message.type !== "response") {
141
+ for (const subscriber of this.eventSubscribers)
142
+ subscriber(message);
143
+ continue;
144
+ }
145
+ const id = typeof message.id === "string" ? message.id : undefined;
146
+ if (!id)
147
+ continue;
148
+ const pending = this.pending.get(id);
149
+ if (!pending)
150
+ continue;
151
+ this.pending.delete(id);
152
+ if (message.success === false || message.error) {
153
+ pending.reject(new Error(errorMessage(message.error ?? `Pi RPC request failed: ${message.command ?? id}`)));
154
+ }
155
+ else {
156
+ pending.resolve(message.data ?? message.result ?? message);
157
+ }
158
+ }
159
+ }
160
+ failAll(error) {
161
+ this.fatalError = error;
162
+ for (const pending of this.pending.values()) {
163
+ pending.reject(error);
164
+ }
165
+ this.pending.clear();
166
+ }
167
+ }
168
+ export function extractPiFinalResponse(value) {
169
+ const root = unwrapProviderPayload(value);
170
+ const messages = Array.isArray(root) ? root : readArray(root, "messages");
171
+ if (!messages)
172
+ return "";
173
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
174
+ const message = asRecord(messages[index]);
175
+ if (!message || message.role !== "assistant")
176
+ continue;
177
+ const text = extractPiAssistantMessageText(message);
178
+ if (text)
179
+ return text;
180
+ }
181
+ return "";
182
+ }
183
+ export function extractPiStreamingText(events) {
184
+ return events
185
+ .map((event) => {
186
+ const record = asRecord(event);
187
+ if (!record || record.type !== "message_update")
188
+ return "";
189
+ const update = asRecord(record.assistantMessageEvent);
190
+ if (!update || update.type !== "text_delta")
191
+ return "";
192
+ return typeof update.delta === "string" ? update.delta : "";
193
+ })
194
+ .filter(Boolean)
195
+ .join("")
196
+ .trim();
197
+ }
198
+ export function extractPiProviderError(value) {
199
+ const root = unwrapProviderPayload(value);
200
+ if (Array.isArray(root)) {
201
+ for (let index = root.length - 1; index >= 0; index -= 1) {
202
+ const error = extractPiProviderError(root[index]);
203
+ if (error)
204
+ return error;
205
+ }
206
+ return "";
207
+ }
208
+ const messages = readArray(root, "messages");
209
+ if (messages)
210
+ return extractPiProviderError(messages);
211
+ const message = asRecord(root)?.message ?? root;
212
+ const record = asRecord(message);
213
+ if (!record)
214
+ return "";
215
+ const error = record.errorMessage ?? record.error;
216
+ return typeof error === "string" ? error.trim() : "";
217
+ }
218
+ function extractPiAssistantMessageText(message) {
219
+ const content = message.content;
220
+ if (!Array.isArray(content))
221
+ return "";
222
+ return content
223
+ .map((part) => {
224
+ const partRecord = asRecord(part);
225
+ if (!partRecord || partRecord.type !== "text")
226
+ return "";
227
+ return typeof partRecord.text === "string" ? partRecord.text : "";
228
+ })
229
+ .filter(Boolean)
230
+ .join("\n\n")
231
+ .trim();
232
+ }
@@ -1,11 +1,12 @@
1
1
  import { spawnSync } from "node:child_process";
2
2
  import { delimiter, resolve } from "node:path";
3
- import { removeDevspaceNodeModulesBinFromPath } from "./local-agent-path.js";
4
- import { LOCAL_AGENT_PROVIDERS, } from "./local-agent-profiles.js";
5
- export function getLocalAgentProviderAvailabilitySnapshot(env = process.env) {
6
- return LOCAL_AGENT_PROVIDERS.map((provider) => checkLocalAgentProviderAvailability(provider, env));
3
+ import { subagentProviderContinuationSupported } from "./continuation.js";
4
+ import { removeDevspaceNodeModulesBinFromPath } from "./path.js";
5
+ import { SUBAGENT_PROVIDERS, } from "../profiles.js";
6
+ export function getSubagentProviderAvailabilitySnapshot(env = process.env) {
7
+ return SUBAGENT_PROVIDERS.map((provider) => checkSubagentProviderAvailability(provider, env));
7
8
  }
8
- export function checkLocalAgentProviderAvailability(provider, env = process.env) {
9
+ export function checkSubagentProviderAvailability(provider, env = process.env) {
9
10
  switch (provider) {
10
11
  case "codex":
11
12
  return packageAvailability(provider, "@openai/codex-sdk");
@@ -23,13 +24,16 @@ export function checkLocalAgentProviderAvailability(provider, env = process.env)
23
24
  return commandAvailability(provider, "copilot");
24
25
  }
25
26
  }
26
- export function assertLocalAgentProviderAvailable(provider, env = process.env) {
27
- const availability = checkLocalAgentProviderAvailability(provider, env);
27
+ export function assertSubagentProviderAvailable(provider, env = process.env) {
28
+ const availability = checkSubagentProviderAvailability(provider, env);
28
29
  if (availability.available)
29
30
  return;
30
31
  throw new Error(`${provider} provider is not available: ${availability.reason ?? "provider preflight failed"}`);
31
32
  }
32
- export function formatLocalAgentProviderAvailabilitySummary(providers) {
33
+ export function formatUnavailableSubagentProvider(provider) {
34
+ return `${provider.name} (${provider.reason ?? "unavailable"})`;
35
+ }
36
+ export function formatSubagentProviderAvailabilitySummary(providers) {
33
37
  const available = providers
34
38
  .filter((provider) => provider.available)
35
39
  .map((provider) => provider.name);
@@ -44,12 +48,17 @@ export function formatLocalAgentProviderAvailabilitySummary(providers) {
44
48
  function packageAvailability(provider, packageName) {
45
49
  try {
46
50
  import.meta.resolve(packageName);
47
- return { name: provider, available: true };
51
+ return {
52
+ name: provider,
53
+ available: true,
54
+ continuationSupported: subagentProviderContinuationSupported(provider),
55
+ };
48
56
  }
49
57
  catch {
50
58
  return {
51
59
  name: provider,
52
60
  available: false,
61
+ continuationSupported: subagentProviderContinuationSupported(provider),
53
62
  reason: `${packageName} package not found`,
54
63
  };
55
64
  }
@@ -60,10 +69,15 @@ function commandAvailability(provider, command, options = {}) {
60
69
  return {
61
70
  name: provider,
62
71
  available: false,
72
+ continuationSupported: subagentProviderContinuationSupported(provider),
63
73
  reason: `${command} executable not found`,
64
74
  };
65
75
  }
66
- return { name: provider, available: true };
76
+ return {
77
+ name: provider,
78
+ available: true,
79
+ continuationSupported: subagentProviderContinuationSupported(provider),
80
+ };
67
81
  }
68
82
  function resolveCommand(command, env = process.env) {
69
83
  const commandHasPath = command.includes("/") || command.includes("\\");
@@ -0,0 +1,11 @@
1
+ const CONTINUATION_SUPPORT = {
2
+ codex: true,
3
+ claude: true,
4
+ opencode: true,
5
+ pi: true,
6
+ cursor: false,
7
+ copilot: false,
8
+ };
9
+ export function subagentProviderContinuationSupported(provider) {
10
+ return CONTINUATION_SUPPORT[provider];
11
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,26 @@
1
+ import { AcpSubagentAdapter } from "./adapters/acp.js";
2
+ import { ClaudeSubagentAdapter } from "./adapters/claude.js";
3
+ import { CodexSubagentAdapter } from "./adapters/codex.js";
4
+ import { extractOpenCodeFinalResponse, OpencodeSubagentAdapter } from "./adapters/opencode.js";
5
+ import { extractPiFinalResponse, PiRpcSubagentAdapter, } from "./adapters/pi.js";
6
+ export async function runSubagentProvider(provider, input) {
7
+ return createSubagentProviderAdapter(provider).run(input);
8
+ }
9
+ export function createSubagentProviderAdapter(provider) {
10
+ switch (provider) {
11
+ case "codex":
12
+ return new CodexSubagentAdapter();
13
+ case "claude":
14
+ return new ClaudeSubagentAdapter();
15
+ case "opencode":
16
+ return new OpencodeSubagentAdapter();
17
+ case "pi":
18
+ return new PiRpcSubagentAdapter();
19
+ case "cursor":
20
+ case "copilot":
21
+ return new AcpSubagentAdapter(provider);
22
+ }
23
+ }
24
+ export function extractSubagentResponseText(value) {
25
+ return extractOpenCodeFinalResponse(value) || extractPiFinalResponse(value);
26
+ }
@@ -0,0 +1,40 @@
1
+ export function directString(value) {
2
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
3
+ }
4
+ export function assertPipedChild(child) {
5
+ if (!child.stdin || !child.stdout || !child.stderr) {
6
+ throw new Error("Agent process did not expose stdio pipes.");
7
+ }
8
+ }
9
+ export function unwrapProviderPayload(value) {
10
+ const record = asRecord(value);
11
+ if (!record)
12
+ return value;
13
+ return record.data ?? record.result ?? value;
14
+ }
15
+ export function readArray(record, key) {
16
+ const value = asRecord(record)?.[key];
17
+ return Array.isArray(value) ? value : undefined;
18
+ }
19
+ export function asRecord(value) {
20
+ if (!value || typeof value !== "object" || Array.isArray(value))
21
+ return undefined;
22
+ return value;
23
+ }
24
+ export function readNestedString(value, path) {
25
+ let current = value;
26
+ for (const key of path) {
27
+ current = asRecord(current)?.[key];
28
+ }
29
+ return typeof current === "string" ? current : undefined;
30
+ }
31
+ export function errorMessage(error) {
32
+ return error instanceof Error ? error.message : String(error);
33
+ }
34
+ export function requireFinalResponse(provider, response) {
35
+ const trimmed = response.trim();
36
+ if (!trimmed) {
37
+ throw new Error(`${provider} did not return a final assistant response.`);
38
+ }
39
+ return trimmed;
40
+ }
@@ -0,0 +1,214 @@
1
+ import { CapabilityError, } from "../../capability-registry.js";
2
+ import { isSubagentProvider } from "../profiles.js";
3
+ import { subagentProviderContinuationSupported } from "../providers/continuation.js";
4
+ import { SubagentDeliveryMailbox } from "./delivery-mailbox.js";
5
+ import { executeSubagentRun, } from "./execution.js";
6
+ import { SubagentSessionError, SubagentSessionManager, } from "./manager.js";
7
+ export class SubagentSessionCapability {
8
+ config;
9
+ activityLifecycle;
10
+ mailbox;
11
+ providerRunner;
12
+ constructor(config, activityLifecycle, options = {}) {
13
+ this.config = config;
14
+ this.activityLifecycle = activityLifecycle;
15
+ this.mailbox = new SubagentDeliveryMailbox(config.stateDir);
16
+ this.providerRunner = options.providerRunner ?? defaultProviderRunner;
17
+ }
18
+ async run(input, context, options) {
19
+ const manager = new SubagentSessionManager(this.config, {
20
+ launch: (request) => this.launch(request),
21
+ });
22
+ try {
23
+ switch (input.operation) {
24
+ case "start": {
25
+ const started = await manager.start({
26
+ workspaceId: context.workspaceId,
27
+ workspaceRoot: context.workspaceRoot,
28
+ target: input.target,
29
+ prompt: input.prompt,
30
+ model: input.model,
31
+ thinking: input.thinking,
32
+ activityId: options.activityId,
33
+ });
34
+ return {
35
+ value: {
36
+ operation: "start",
37
+ session: publicSession(started.session),
38
+ run: publicRun(started.run),
39
+ },
40
+ };
41
+ }
42
+ case "resume": {
43
+ const resumed = manager.resume({
44
+ sessionId: input.sessionId,
45
+ prompt: input.prompt,
46
+ activityId: options.activityId,
47
+ }, { workspaceId: context.workspaceId });
48
+ return {
49
+ value: {
50
+ operation: "resume",
51
+ session: publicSession(resumed.session),
52
+ run: publicRun(resumed.run),
53
+ },
54
+ };
55
+ }
56
+ case "status": {
57
+ const session = manager.get(input.sessionId, { workspaceId: context.workspaceId });
58
+ if (!session) {
59
+ throw new SubagentSessionError("subagent.session_not_found", `Unknown Subagent Session in this Workspace: ${input.sessionId}`);
60
+ }
61
+ return {
62
+ value: {
63
+ operation: "status",
64
+ session: publicSession(session),
65
+ ...(session.activeRun ? { activeRun: publicRun(session.activeRun) } : {}),
66
+ ...(session.latestRun ? { latestRun: publicRun(session.latestRun) } : {}),
67
+ },
68
+ };
69
+ }
70
+ case "list":
71
+ return {
72
+ value: {
73
+ operation: "list",
74
+ sessions: manager.list({ workspaceId: context.workspaceId }).map(publicSessionSummary),
75
+ },
76
+ };
77
+ }
78
+ }
79
+ catch (error) {
80
+ if (error instanceof SubagentSessionError) {
81
+ throw new CapabilityError(error.code, error.message);
82
+ }
83
+ throw error;
84
+ }
85
+ finally {
86
+ manager.close();
87
+ }
88
+ }
89
+ decorateResult(workspaceId, result) {
90
+ if (typeof result !== "object" || result === null)
91
+ return result;
92
+ const content = result.content;
93
+ if (!Array.isArray(content))
94
+ return result;
95
+ const excludeRunId = currentRunId(result);
96
+ const deliveries = this.mailbox.claimWorkspace(workspaceId, excludeRunId);
97
+ if (deliveries.length === 0)
98
+ return result;
99
+ return {
100
+ ...result,
101
+ content: [
102
+ ...content,
103
+ ...deliveries.map((delivery) => ({ type: "text", text: deliveryText(delivery) })),
104
+ ],
105
+ };
106
+ }
107
+ launch(request) {
108
+ void executeSubagentRun(this.config, request, this.providerRunner)
109
+ .then((completion) => this.recordCompletion(completion))
110
+ .catch(() => {
111
+ // The worker writes durable Session/mailbox state for provider failures.
112
+ // An unexpected orchestration failure must not become an unhandled rejection.
113
+ });
114
+ }
115
+ recordCompletion(completion) {
116
+ if (!completion.activityId)
117
+ return;
118
+ this.activityLifecycle.recordLinked({
119
+ sourceActivityId: completion.activityId,
120
+ tool: "subagent_result",
121
+ request: {
122
+ sessionId: completion.sessionId,
123
+ runId: completion.runId,
124
+ },
125
+ result: {
126
+ sessionId: completion.sessionId,
127
+ runId: completion.runId,
128
+ provider: completion.provider,
129
+ status: completion.outcome,
130
+ },
131
+ outcome: completion.outcome === "failed"
132
+ ? { type: "failed", error: "Subagent Run failed." }
133
+ : { type: "succeeded" },
134
+ });
135
+ }
136
+ }
137
+ function publicSession(session) {
138
+ const continuationSupported = sessionContinuationSupported(session);
139
+ return {
140
+ id: session.id,
141
+ status: session.status,
142
+ profileName: session.profileName,
143
+ provider: session.provider,
144
+ continuationSupported,
145
+ resumable: continuationSupported && session.status === "idle" && Boolean(session.providerSessionId),
146
+ ...(session.model ? { model: session.model } : {}),
147
+ ...(session.thinking ? { thinking: session.thinking } : {}),
148
+ ...(session.activeRun ? { activeRun: publicRun(session.activeRun) } : {}),
149
+ ...(session.latestRun ? { latestRun: publicRun(session.latestRun) } : {}),
150
+ createdAt: session.createdAt,
151
+ updatedAt: session.updatedAt,
152
+ };
153
+ }
154
+ function publicSessionSummary(session) {
155
+ const continuationSupported = sessionContinuationSupported(session);
156
+ return {
157
+ id: session.id,
158
+ status: session.status,
159
+ profileName: session.profileName,
160
+ provider: session.provider,
161
+ continuationSupported,
162
+ resumable: continuationSupported && session.status === "idle" && Boolean(session.providerSessionId),
163
+ ...(session.model ? { model: session.model } : {}),
164
+ ...(session.thinking ? { thinking: session.thinking } : {}),
165
+ ...(session.activeRun ? { activeRunId: session.activeRun.id } : {}),
166
+ ...(session.latestRun ? {
167
+ latestRun: {
168
+ id: session.latestRun.id,
169
+ status: session.latestRun.status,
170
+ },
171
+ } : {}),
172
+ updatedAt: session.updatedAt,
173
+ };
174
+ }
175
+ function sessionContinuationSupported(session) {
176
+ return isSubagentProvider(session.provider)
177
+ ? subagentProviderContinuationSupported(session.provider)
178
+ : false;
179
+ }
180
+ function publicRun(run) {
181
+ return {
182
+ id: run.id,
183
+ status: run.status,
184
+ ...(run.startedAt ? { startedAt: run.startedAt } : {}),
185
+ ...(run.finishedAt ? { finishedAt: run.finishedAt } : {}),
186
+ };
187
+ }
188
+ function currentRunId(result) {
189
+ if (typeof result !== "object" || result === null)
190
+ return undefined;
191
+ const structured = result.structuredContent;
192
+ if (typeof structured !== "object" || structured === null)
193
+ return undefined;
194
+ const capabilityResult = structured.result;
195
+ if (typeof capabilityResult !== "object" || capabilityResult === null)
196
+ return undefined;
197
+ const run = capabilityResult.run;
198
+ if (typeof run !== "object" || run === null)
199
+ return undefined;
200
+ const id = run.id;
201
+ return typeof id === "string" ? id : undefined;
202
+ }
203
+ function deliveryText(delivery) {
204
+ const header = `Subagent ${delivery.sessionId} Run ${delivery.runId} ${delivery.outcome}.`;
205
+ const body = delivery.outcome === "succeeded"
206
+ ? delivery.finalResponse
207
+ : delivery.error;
208
+ const suffix = delivery.truncated ? "\n[Subagent result truncated for delivery.]" : "";
209
+ return body ? `${header}\n${body}${suffix}` : `${header}${suffix}`;
210
+ }
211
+ async function defaultProviderRunner(provider, input) {
212
+ const { runSubagentProvider } = await import("../providers/registry.js");
213
+ return runSubagentProvider(provider, input);
214
+ }