@astrosheep/keiyaku 2.8.4 → 2.9.0

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 (229) hide show
  1. package/README.md +35 -15
  2. package/build/.tsbuildinfo +1 -1
  3. package/build/agents/{call-terms_v2.js → call-terms.js} +12 -8
  4. package/build/agents/harness/control-types.js +1 -0
  5. package/build/agents/harness/execution-handle.js +23 -3
  6. package/build/agents/harness/index.js +6 -607
  7. package/build/agents/harness/outcome.js +141 -0
  8. package/build/agents/harness/projection.js +310 -0
  9. package/build/agents/harness/router.js +201 -0
  10. package/build/agents/harness/runtime.js +2 -1
  11. package/build/agents/{launch-snapshot_v2.js → launch-snapshot/model.js} +69 -74
  12. package/build/agents/launch-snapshot/resolve.js +72 -0
  13. package/build/agents/opencode-sdk.js +24 -3
  14. package/build/agents/providers/{claude-agent-sdk.js → claude-agent-sdk/adapter.js} +46 -368
  15. package/build/agents/providers/claude-agent-sdk/events.js +218 -0
  16. package/build/agents/providers/claude-agent-sdk/session.js +158 -0
  17. package/build/agents/providers/codex-app-server/adapter.js +347 -0
  18. package/build/agents/providers/codex-app-server/events.js +352 -0
  19. package/build/agents/providers/codex-app-server/session.js +266 -0
  20. package/build/agents/providers/codex-sdk.js +23 -5
  21. package/build/agents/providers/opencode-sdk/adapter.js +344 -0
  22. package/build/agents/providers/opencode-sdk/events.js +354 -0
  23. package/build/agents/providers/opencode-sdk/session.js +180 -0
  24. package/build/agents/providers/pi/adapter.js +269 -0
  25. package/build/agents/providers/pi/checkpoint.js +41 -0
  26. package/build/agents/providers/pi/events.js +282 -0
  27. package/build/agents/{selector_v2.js → selector.js} +8 -6
  28. package/build/cli/commands/akuma/akuma/handler.js +4 -0
  29. package/build/cli/commands/akuma/akuma/meta.js +16 -0
  30. package/build/cli/commands/akuma/catalog.js +8 -0
  31. package/build/cli/commands/akuma/list/handler.js +15 -0
  32. package/build/cli/commands/akuma/list/meta-ls.js +9 -0
  33. package/build/cli/commands/akuma/view/handler.js +34 -0
  34. package/build/cli/commands/akuma/view/meta.js +8 -0
  35. package/build/cli/commands/akuma.js +1 -1
  36. package/build/cli/commands/contract/amend/handler.js +10 -0
  37. package/build/cli/commands/contract/amend/meta.js +12 -0
  38. package/build/cli/commands/contract/arc/handler.js +9 -0
  39. package/build/cli/commands/contract/arc/meta.js +26 -0
  40. package/build/cli/commands/contract/bind/handler.js +23 -0
  41. package/build/cli/commands/contract/bind/meta.js +30 -0
  42. package/build/cli/commands/contract/catalog.js +16 -0
  43. package/build/cli/commands/contract/forfeit/handler.js +13 -0
  44. package/build/cli/commands/contract/forfeit/meta.js +12 -0
  45. package/build/cli/commands/contract/log/handler.js +12 -0
  46. package/build/cli/commands/contract/log/meta.js +12 -0
  47. package/build/cli/commands/contract/petition/handler.js +26 -0
  48. package/build/cli/commands/contract/petition/meta.js +23 -0
  49. package/build/cli/commands/contract/renew/handler.js +10 -0
  50. package/build/cli/commands/contract/renew/meta.js +17 -0
  51. package/build/cli/commands/projection/call/handler.js +39 -0
  52. package/build/cli/commands/projection/call/meta.js +9 -0
  53. package/build/cli/commands/projection/catalog.js +14 -0
  54. package/build/cli/commands/projection/kill/handler.js +35 -0
  55. package/build/cli/commands/projection/kill/meta.js +11 -0
  56. package/build/cli/commands/projection/revive/handler.js +53 -0
  57. package/build/cli/commands/projection/revive/meta.js +9 -0
  58. package/build/cli/commands/projection/status/handler.js +23 -0
  59. package/build/cli/commands/projection/status/meta.js +9 -0
  60. package/build/cli/commands/projection/tell/handler.js +34 -0
  61. package/build/cli/commands/projection/tell/meta.js +9 -0
  62. package/build/cli/commands/projection/wait/handler.js +21 -0
  63. package/build/cli/commands/projection/wait/meta.js +11 -0
  64. package/build/cli/commands/registry.js +100 -0
  65. package/build/cli/commands/shared.js +71 -0
  66. package/build/cli/commands/skills/catalog.js +6 -0
  67. package/build/cli/commands/skills/install/handler.js +10 -0
  68. package/build/cli/commands/skills/install/meta.js +9 -0
  69. package/build/cli/commands/skills/skills/handler.js +4 -0
  70. package/build/cli/commands/skills/skills/meta.js +10 -0
  71. package/build/cli/commands/system/catalog.js +8 -0
  72. package/build/cli/commands/system/completion/handler.js +10 -0
  73. package/build/cli/commands/system/completion/meta.js +9 -0
  74. package/build/cli/commands/system/dump-env/handler.js +3 -0
  75. package/build/cli/commands/system/dump-env/meta.js +10 -0
  76. package/build/cli/commands/system/guide/handler.js +4 -0
  77. package/build/cli/commands/system/guide/meta.js +9 -0
  78. package/build/cli/commands/task/add/handler.js +21 -0
  79. package/build/cli/commands/task/add/meta.js +8 -0
  80. package/build/cli/commands/task/catalog.js +22 -0
  81. package/build/cli/commands/task/doctor/handler.js +4 -0
  82. package/build/cli/commands/task/doctor/meta.js +8 -0
  83. package/build/cli/commands/task/done/handler.js +17 -0
  84. package/build/cli/commands/task/done/meta.js +8 -0
  85. package/build/cli/commands/task/drop/handler.js +6 -0
  86. package/build/cli/commands/task/drop/meta.js +8 -0
  87. package/build/cli/commands/task/ls/handler.js +13 -0
  88. package/build/cli/commands/task/ls/meta.js +8 -0
  89. package/build/cli/commands/task/shared.js +131 -0
  90. package/build/cli/commands/task/start/handler.js +6 -0
  91. package/build/cli/commands/task/start/meta.js +8 -0
  92. package/build/cli/commands/task/stop/handler.js +17 -0
  93. package/build/cli/commands/task/stop/meta.js +8 -0
  94. package/build/cli/commands/task/task/handler.js +5 -0
  95. package/build/cli/commands/task/task/meta.js +11 -0
  96. package/build/cli/commands/task/update/handler.js +18 -0
  97. package/build/cli/commands/task/update/meta.js +8 -0
  98. package/build/cli/commands/task/view/handler.js +14 -0
  99. package/build/cli/commands/task/view/meta.js +8 -0
  100. package/build/cli/completion.js +26 -27
  101. package/build/cli/flags.js +79 -2
  102. package/build/cli/help.js +27 -49
  103. package/build/cli/index.js +121 -374
  104. package/build/cli/output-stream-error-policy.js +17 -0
  105. package/build/cli/parse.js +159 -72
  106. package/build/cli/projection-address.js +5 -0
  107. package/build/cli/render/arc.js +1 -1
  108. package/build/cli/render/errors.js +4 -3
  109. package/build/cli/render/format.js +14 -0
  110. package/build/cli/render/kanshi.js +106 -0
  111. package/build/cli/render/petition.js +24 -1
  112. package/build/cli/render/projection-activity.js +5 -1
  113. package/build/cli/render/shared.js +2 -2
  114. package/build/cli/render/status.js +79 -13
  115. package/build/cli/render/task.js +113 -0
  116. package/build/cli/render/wait.js +23 -3
  117. package/build/cli/types.js +1 -1
  118. package/build/config/{akuma-loader_v2.js → akuma-loader.js} +2 -2
  119. package/build/config/env.js +9 -4
  120. package/build/config/settings/disease.js +26 -0
  121. package/build/config/settings/knobs.js +8 -0
  122. package/build/config/settings/loader.js +244 -0
  123. package/build/config/settings/schema.js +276 -0
  124. package/build/core/addressing.js +1 -1
  125. package/build/core/amend.js +3 -3
  126. package/build/core/arc.js +5 -4
  127. package/build/core/bind-workspace.js +123 -0
  128. package/build/core/bind.js +75 -155
  129. package/build/core/call/call.js +224 -0
  130. package/build/core/call/context.js +238 -0
  131. package/build/core/call/execution.js +217 -0
  132. package/build/core/call/prompt.js +42 -0
  133. package/build/core/call/reference.js +24 -0
  134. package/build/core/call-persist.js +1 -1
  135. package/build/core/claim-delivery.js +247 -0
  136. package/build/core/claim.js +39 -277
  137. package/build/core/context.js +2 -1
  138. package/build/core/{render.js → contract-view.js} +12 -140
  139. package/build/core/draft.js +107 -9
  140. package/build/core/entry.js +9 -1
  141. package/build/core/execution-coordinate.js +4 -20
  142. package/build/core/execution-pact.js +16 -31
  143. package/build/core/forfeit.js +35 -22
  144. package/build/core/hints.js +2 -2
  145. package/build/core/log.js +2 -2
  146. package/build/core/petition-claim-gates.js +8 -6
  147. package/build/core/petition-preview.js +97 -0
  148. package/build/core/petition-run.js +3 -2
  149. package/build/core/petition.js +39 -26
  150. package/build/core/projection/generation/database.js +306 -0
  151. package/build/core/projection/generation/identity.js +84 -0
  152. package/build/core/projection/generation/model.js +201 -0
  153. package/build/core/projection/generation/store.js +73 -0
  154. package/build/core/projection/generation/transitions.js +258 -0
  155. package/build/core/projection/heart.js +0 -307
  156. package/build/core/projection/tell/database.js +109 -0
  157. package/build/core/projection/tell/model.js +97 -0
  158. package/build/core/{projection-tells.js → projection/tell/store.js} +3 -158
  159. package/build/core/projection-activity.js +23 -2
  160. package/build/core/projection-core.js +2 -1
  161. package/build/core/projection-execution-observer.js +259 -0
  162. package/build/core/projection-generation-continuation.js +1 -1
  163. package/build/core/projection-generation-execution.js +90 -20
  164. package/build/core/projection-generation-launcher.js +1 -1
  165. package/build/core/projection-generation-runner.js +198 -4
  166. package/build/core/projection-identity.js +1 -1
  167. package/build/core/projection-kill.js +50 -17
  168. package/build/core/projection-life-observer.js +1 -1
  169. package/build/core/projection-life-protocol.js +10 -0
  170. package/build/core/projection-mint.js +1 -1
  171. package/build/core/projection-status.js +45 -32
  172. package/build/core/projection-wait.js +12 -2
  173. package/build/core/projection-wake.js +5 -2
  174. package/build/core/{queue_v2.js → queue.js} +2 -1
  175. package/build/core/registry.js +1 -1
  176. package/build/core/renew.js +7 -7
  177. package/build/core/response-artifact-catalog-sqlite.js +112 -0
  178. package/build/core/response-artifact-catalog.js +7 -0
  179. package/build/core/seal.js +44 -16
  180. package/build/core/status/board.js +286 -0
  181. package/build/core/status/drift.js +99 -0
  182. package/build/core/status/lifecycle.js +84 -0
  183. package/build/core/status/reconciliation.js +131 -0
  184. package/build/core/stored-agent-event.js +28 -4
  185. package/build/core/target-ref.js +10 -0
  186. package/build/core/task/board.js +175 -0
  187. package/build/core/task/commands.js +211 -0
  188. package/build/core/task/document.js +189 -0
  189. package/build/core/task/model.js +1 -0
  190. package/build/core/task/settlement-git.js +207 -0
  191. package/build/core/task/settlement-policy.js +82 -0
  192. package/build/core/task/source-board.js +84 -0
  193. package/build/core/task/source-model.js +1 -0
  194. package/build/core/task/validation.js +44 -0
  195. package/build/core/task-contract.js +224 -0
  196. package/build/core/task-doctor.js +14 -0
  197. package/build/core/task-git-actor.js +38 -0
  198. package/build/core/task-git-runtime.js +34 -0
  199. package/build/core/task-git-store.js +375 -0
  200. package/build/core/task-repository.js +1 -0
  201. package/build/core/task-store-repository.js +86 -0
  202. package/build/core/task-store.js +1 -0
  203. package/build/core/task-worktree.js +51 -0
  204. package/build/core/task.js +4 -0
  205. package/build/core/transcripts.js +138 -56
  206. package/build/core/validation.js +17 -0
  207. package/build/core/verdict.js +2 -2
  208. package/build/core/worktree-path.js +24 -6
  209. package/build/flow-error.js +3 -2
  210. package/build/generated/version.js +1 -1
  211. package/build/git/commits.js +3 -0
  212. package/build/git/diff/parsers.js +0 -94
  213. package/build/index.js +2 -0
  214. package/build/keiyaku-constants.js +2 -0
  215. package/build/keiyaku.js +4 -3
  216. package/package.json +3 -2
  217. package/skills/keiyaku/SKILL.md +1 -1
  218. package/skills/keiyaku-akuma/SKILL.md +11 -6
  219. package/build/agents/harness/pump.js +0 -158
  220. package/build/agents/providers/codex-app-server.js +0 -864
  221. package/build/agents/providers/opencode-sdk.js +0 -363
  222. package/build/agents/providers/pi.js +0 -557
  223. package/build/config/settings.js +0 -533
  224. package/build/core/call.js +0 -686
  225. package/build/core/projection/generation.js +0 -412
  226. package/build/core/projection/identity.js +0 -81
  227. package/build/core/projection/mint.js +0 -174
  228. package/build/core/projection-generation-store.js +0 -707
  229. package/build/core/status.js +0 -539
@@ -23,9 +23,16 @@ function withCodexSdkModelReasoningEffort(threadOptions) {
23
23
  };
24
24
  }
25
25
  let codexConstructor = Codex;
26
+ function getCodexConstructor(override) {
27
+ if (override)
28
+ return override;
29
+ return codexConstructor;
30
+ }
31
+ /** @deprecated Use CodexSdkOptions.codexConstructor instead. */
26
32
  export function setCodexSdkConstructorForTests(constructor) {
27
33
  codexConstructor = constructor;
28
34
  }
35
+ /** @deprecated Use CodexSdkOptions.codexConstructor instead. */
29
36
  export function resetCodexSdkConstructorForTests() {
30
37
  codexConstructor = Codex;
31
38
  }
@@ -60,7 +67,12 @@ function extractAgentMessage(event) {
60
67
  if (event.type !== CODEX_EVENT_ITEM_COMPLETED && event.type !== CODEX_EVENT_ITEM_UPDATED) {
61
68
  return undefined;
62
69
  }
63
- return event.item.type === "agent_message" ? coerceString(event.item.text) : undefined;
70
+ if (event.item.type !== "agent_message")
71
+ return undefined;
72
+ const text = coerceString(event.item.text);
73
+ if (!text)
74
+ return undefined;
75
+ return { id: event.item.id, text };
64
76
  }
65
77
  function codexActivityCall(item) {
66
78
  switch (item.type) {
@@ -157,18 +169,23 @@ function emitCodexSemanticEvent(builder, event) {
157
169
  ...(phase === "started" ? {} : { result: codexActivityResult(event.item) }),
158
170
  });
159
171
  }
172
+ /** @deprecated Use the codex-app-server adapter. */
160
173
  export async function startCodexSdk(prompt, cwd, options) {
161
174
  const threadId = options.threadId?.trim();
162
175
  const recordedEventTypes = [];
163
176
  let totalEventCount = 0;
164
177
  let activeAbort;
178
+ let runReturned = false;
165
179
  appendDebugLog(`startCodexSdk: executable=${options.executable ?? "codex"} model=${options.model ?? "default"} ${CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY}=${String(options.config?.[CODEX_MODEL_REASONING_EFFORT_CONFIG_KEY] ?? "default")} mode=${threadId ? "resume" : "fresh"}`, { cwd, section: CODEX_SDK_PROVIDER });
166
180
  return createProjectionDriver({
167
181
  steerMode: "replay",
168
182
  initialTurn: options.initialTurn,
169
183
  onIngress: options.onIngress,
170
- async abort() {
184
+ async abort(mode) {
171
185
  activeAbort?.abort();
186
+ if (mode === "force" && !runReturned) {
187
+ throw new Error("codex-sdk cannot guarantee forced termination");
188
+ }
172
189
  },
173
190
  async run(builder) {
174
191
  activeAbort = new AbortController();
@@ -186,7 +203,7 @@ export async function startCodexSdk(prompt, cwd, options) {
186
203
  config: options.config,
187
204
  env,
188
205
  };
189
- const sdk = new codexConstructor(codexOptions);
206
+ const sdk = new (getCodexConstructor(options.codexConstructor))(codexOptions);
190
207
  const threadOptions = withCodexSdkModelReasoningEffort(options.threadOptions);
191
208
  const thread = threadId ? sdk.resumeThread(threadId, threadOptions) : sdk.startThread(threadOptions);
192
209
  const streamed = await thread.runStreamed(prompt, { signal: activeAbort.signal });
@@ -255,8 +272,8 @@ export async function startCodexSdk(prompt, cwd, options) {
255
272
  emitCodexSemanticEvent(builder, event);
256
273
  const message = extractAgentMessage(event);
257
274
  if (message) {
258
- lastAgentMessage = message;
259
- builder.emit({ type: "message", text: message });
275
+ lastAgentMessage = message.text;
276
+ builder.emit({ type: "message", id: message.id, text: message.text });
260
277
  }
261
278
  });
262
279
  }
@@ -304,6 +321,7 @@ export async function startCodexSdk(prompt, cwd, options) {
304
321
  throw error;
305
322
  }
306
323
  finally {
324
+ runReturned = true;
307
325
  linked.removeEventListener("abort", onOuterAbort);
308
326
  }
309
327
  },
@@ -0,0 +1,344 @@
1
+ import { z } from "zod";
2
+ import { FlowError } from "../../../flow-error.js";
3
+ import { appendDebugBlock, appendDebugLog } from "../../../telemetry/debug-log.js";
4
+ import { SUBAGENT_SESSION_VERSION } from "../../session.js";
5
+ import { createProjectionDriver } from "../../harness/execution-handle.js";
6
+ import { buildSubagentEnv, coerceString, emitProviderEvidence, sha256Utf8 } from "../../harness/runtime.js";
7
+ import { renderEventTypeSummary } from "../event-type-tail.js";
8
+ import { createOpencodeEventController } from "./events.js";
9
+ import { OPENCODE_DEFAULT_EXECUTABLE, OPENCODE_SDK_PROVIDER, OpencodeIdleMonitor, loadOpencodeSdk, sessionIdFrom, withAbort, setOpencodeSdkLoaderForTests as setLoader, resetOpencodeSdkLoaderForTests as resetLoader, } from "./session.js";
10
+ export const opencodeSessionSchema = z.object({ sessionId: z.string().trim().min(1) });
11
+ export function parseOpencodeModelRef(model, effort) {
12
+ const trimmed = model.trim();
13
+ const slash = trimmed.indexOf("/");
14
+ if (slash <= 0 || slash === trimmed.length - 1) {
15
+ throw new FlowError("INVALID_SETTINGS", `Invalid opencode model '${model}'. Expected opaque shape '<provider>/<model>' with non-empty segments split at the first slash.`);
16
+ }
17
+ return {
18
+ providerID: trimmed.slice(0, slash),
19
+ id: trimmed.slice(slash + 1),
20
+ ...(effort !== undefined ? { variant: effort } : {}),
21
+ };
22
+ }
23
+ export function setOpencodeSdkLoaderForTests(loader) {
24
+ setLoader(loader);
25
+ }
26
+ export function resetOpencodeSdkLoaderForTests() {
27
+ resetLoader();
28
+ }
29
+ export async function startOpencode(prompt, cwd, options = {}) {
30
+ const sessionId = options.sessionId?.trim();
31
+ const executable = options.executable ?? OPENCODE_DEFAULT_EXECUTABLE;
32
+ const baseModelRef = options.model === undefined
33
+ ? undefined
34
+ : parseOpencodeModelRef(options.model, options.effort);
35
+ const baseEffort = options.effort;
36
+ let activeSessionId;
37
+ let sdk;
38
+ let streamAbort;
39
+ let executionSignal;
40
+ let idleMonitor;
41
+ let enqueueTail = Promise.resolve();
42
+ let enqueueFailure;
43
+ let acceptingEnqueue = true;
44
+ let builderRef;
45
+ let resolveRunSettled;
46
+ let rejectRunSettled;
47
+ const runSettled = new Promise((resolve, reject) => {
48
+ resolveRunSettled = resolve;
49
+ rejectRunSettled = reject;
50
+ });
51
+ runSettled.catch(() => undefined);
52
+ let eventController;
53
+ const asProviderError = (error) => error instanceof Error ? error : new Error(String(error));
54
+ const rememberEnqueueFailure = (error) => {
55
+ const failure = asProviderError(error);
56
+ enqueueFailure ??= failure;
57
+ return failure;
58
+ };
59
+ const promptSteer = async (client, text, tellId) => {
60
+ if (options.onEvidence) {
61
+ emitProviderEvidence(options.onEvidence, {
62
+ type: "outbound",
63
+ provider: OPENCODE_SDK_PROVIDER,
64
+ direction: "input",
65
+ kind: tellId ? "steer" : "turn-start",
66
+ ...sha256Utf8(text),
67
+ }, (textDiagnostic) => builderRef?.emit({ type: "diagnostic", text: textDiagnostic }));
68
+ }
69
+ const receipt = await client.v2.session.prompt({
70
+ sessionID: activeSessionId,
71
+ // OpenCode documents no client-id idempotence; omit it and retain the
72
+ // at-least-once retry window until such a contract exists.
73
+ prompt: { text },
74
+ delivery: "steer",
75
+ }, { throwOnError: true, signal: executionSignal });
76
+ const admitted = receipt.data.data;
77
+ const ordinal = admitted.admittedSeq;
78
+ if (options.onEvidence) {
79
+ emitProviderEvidence(options.onEvidence, {
80
+ type: "ack",
81
+ provider: OPENCODE_SDK_PROVIDER,
82
+ fence: { kind: "ordinal", ordinal },
83
+ outbound: tellId ? "steer" : "run",
84
+ }, (textDiagnostic) => builderRef?.emit({ type: "diagnostic", text: textDiagnostic }));
85
+ }
86
+ return { fence: { kind: "ordinal", ordinal } };
87
+ };
88
+ const waitIdle = async () => {
89
+ if (!idleMonitor)
90
+ throw new Error(`${OPENCODE_SDK_PROVIDER} cannot observe idle: no active session`);
91
+ await idleMonitor.waitForIdle();
92
+ };
93
+ const applyBaseModel = async (client) => {
94
+ if (!baseModelRef)
95
+ return;
96
+ await client.v2.session.switchModel({ sessionID: activeSessionId, model: baseModelRef }, { throwOnError: true, signal: executionSignal });
97
+ };
98
+ const submitTell = async (tell) => {
99
+ if (!sdk || !activeSessionId) {
100
+ throw new Error(`${OPENCODE_SDK_PROVIDER} cannot steer: no active session`);
101
+ }
102
+ const client = sdk.client;
103
+ const tellEffort = tell.effort;
104
+ if (tellEffort !== undefined && tellEffort !== baseEffort) {
105
+ if (!baseModelRef) {
106
+ throw new FlowError("INVALID_EFFORT_OVERRIDE", `Effort '${tellEffort}' requires an explicit model.`);
107
+ }
108
+ const signal = executionSignal ?? new AbortController().signal;
109
+ await waitIdle();
110
+ await client.v2.session.switchModel({
111
+ sessionID: activeSessionId,
112
+ model: { providerID: baseModelRef.providerID, id: baseModelRef.id, variant: tellEffort },
113
+ }, { throwOnError: true, signal });
114
+ const ack = await promptSteer(client, tell.text, tell.id);
115
+ return {
116
+ ack,
117
+ settle: (async () => {
118
+ await waitIdle();
119
+ await applyBaseModel(client);
120
+ })(),
121
+ };
122
+ }
123
+ return { ack: await promptSteer(client, tell.text, tell.id), settle: Promise.resolve() };
124
+ };
125
+ const enqueue = (tell) => {
126
+ if (!acceptingEnqueue) {
127
+ return Promise.reject(enqueueFailure ?? new Error(`${OPENCODE_SDK_PROVIDER} session is no longer accepting tells`));
128
+ }
129
+ let resolveAck;
130
+ let rejectAck;
131
+ let ackResolved = false;
132
+ const ack = new Promise((resolve, reject) => {
133
+ resolveAck = resolve;
134
+ rejectAck = reject;
135
+ });
136
+ const job = enqueueTail.then(async () => {
137
+ if (enqueueFailure) {
138
+ rejectAck(enqueueFailure);
139
+ return;
140
+ }
141
+ try {
142
+ const submission = await submitTell(tell);
143
+ ackResolved = true;
144
+ resolveAck(submission.ack);
145
+ await submission.settle;
146
+ }
147
+ catch (error) {
148
+ const failure = rememberEnqueueFailure(error);
149
+ if (!ackResolved)
150
+ rejectAck(failure);
151
+ throw failure;
152
+ }
153
+ });
154
+ enqueueTail = job.catch((error) => {
155
+ rememberEnqueueFailure(error);
156
+ });
157
+ return ack;
158
+ };
159
+ const drainEnqueueTail = async () => {
160
+ for (;;) {
161
+ const observed = enqueueTail;
162
+ await observed;
163
+ if (observed !== enqueueTail)
164
+ continue;
165
+ acceptingEnqueue = false;
166
+ if (enqueueFailure)
167
+ throw enqueueFailure;
168
+ return;
169
+ }
170
+ };
171
+ appendDebugLog(`startOpencode: executable=${executable} model=${options.model ?? "default"} mode=${sessionId ? "resume" : "fresh"}`, { cwd, section: OPENCODE_SDK_PROVIDER });
172
+ return createProjectionDriver({
173
+ steerMode: "mid-execution",
174
+ initialTurn: options.initialTurn,
175
+ enqueue,
176
+ onIngress: options.onIngress,
177
+ async abort(mode) {
178
+ acceptingEnqueue = false;
179
+ streamAbort?.abort();
180
+ if (mode === "force") {
181
+ const failures = [];
182
+ if (sdk && activeSessionId) {
183
+ try {
184
+ await sdk.client.v2.session.interrupt({ sessionID: activeSessionId }, { throwOnError: true });
185
+ }
186
+ catch (error) {
187
+ failures.push(error);
188
+ }
189
+ }
190
+ try {
191
+ await sdk?.close?.("force");
192
+ }
193
+ catch (error) {
194
+ failures.push(error);
195
+ }
196
+ try {
197
+ await runSettled;
198
+ }
199
+ catch (error) {
200
+ failures.push(error);
201
+ }
202
+ if (failures.length === 1)
203
+ throw failures[0];
204
+ if (failures.length > 1) {
205
+ throw new AggregateError(failures, `${OPENCODE_SDK_PROVIDER} forced abort failed`);
206
+ }
207
+ return;
208
+ }
209
+ if (sdk && activeSessionId) {
210
+ try {
211
+ await sdk.client.v2.session.interrupt({ sessionID: activeSessionId }, { throwOnError: true });
212
+ }
213
+ catch {
214
+ // idle interrupt may error
215
+ }
216
+ }
217
+ },
218
+ async run(builder) {
219
+ builderRef = builder;
220
+ streamAbort = new AbortController();
221
+ const combined = new AbortController();
222
+ executionSignal = combined.signal;
223
+ const forward = () => combined.abort();
224
+ builder.signal.addEventListener("abort", forward, { once: true });
225
+ options.signal?.addEventListener("abort", forward, { once: true });
226
+ if (builder.signal.aborted || options.signal?.aborted)
227
+ forward();
228
+ const control = { enqueue };
229
+ try {
230
+ const env = buildSubagentEnv(options.env);
231
+ sdk = await withAbort(loadOpencodeSdk(executable, cwd, env, combined.signal), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
232
+ const sessionResponse = sessionId
233
+ ? await withAbort(sdk.client.v2.session.get({ sessionID: sessionId }, { throwOnError: true, signal: combined.signal }), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`)
234
+ : await withAbort(sdk.client.v2.session.create({
235
+ location: { directory: cwd },
236
+ ...(baseModelRef ? { model: baseModelRef } : {}),
237
+ }, { throwOnError: true, signal: combined.signal }), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
238
+ activeSessionId = sessionIdFrom(sessionResponse.data.data, sessionId);
239
+ if (!activeSessionId) {
240
+ const message = `${OPENCODE_SDK_PROVIDER} exec could not determine session id`;
241
+ builder.emit({ type: "fail", message });
242
+ throw new Error(message);
243
+ }
244
+ idleMonitor = new OpencodeIdleMonitor(sdk.client.v2.session, activeSessionId, combined.signal);
245
+ eventController = createOpencodeEventController({
246
+ builder,
247
+ control,
248
+ idleMonitorIngress: () => idleMonitor?.ingress(),
249
+ onEvidence: options.onEvidence,
250
+ onSyncCheckpoint: options.onSyncCheckpoint,
251
+ onDeliveryOpportunity: options.onDeliveryOpportunity,
252
+ executionSignal: combined.signal,
253
+ getSessionId: () => activeSessionId,
254
+ client: sdk.client,
255
+ });
256
+ if (options.onEvidence) {
257
+ emitProviderEvidence(options.onEvidence, { type: "session", provider: OPENCODE_SDK_PROVIDER, sessionId: activeSessionId }, (text) => builder.emit({ type: "diagnostic", text }));
258
+ }
259
+ if (sessionId && baseModelRef) {
260
+ await applyBaseModel(sdk.client);
261
+ }
262
+ const eventsResult = await sdk.client.v2.session.events({ sessionID: activeSessionId }, { signal: streamAbort.signal });
263
+ const streamTask = (async () => {
264
+ try {
265
+ for await (const item of eventsResult.stream) {
266
+ if (streamAbort.signal.aborted || combined.signal.aborted)
267
+ break;
268
+ eventController.processUnknownStreamItem(item);
269
+ }
270
+ }
271
+ catch (error) {
272
+ if (!streamAbort.signal.aborted && !combined.signal.aborted)
273
+ throw error;
274
+ }
275
+ })();
276
+ await promptSteer(sdk.client, prompt);
277
+ await waitIdle();
278
+ await drainEnqueueTail();
279
+ streamAbort.abort();
280
+ await streamTask;
281
+ await eventController.drainHistory(eventController.lastSeenSeq());
282
+ const lastTurnFailure = eventController.lastTurnFailure();
283
+ if (lastTurnFailure)
284
+ throw lastTurnFailure;
285
+ const messagesResponse = await withAbort(sdk.client.v2.session.messages({ sessionID: activeSessionId, order: "desc", limit: 32 }, { throwOnError: true, signal: combined.signal }), combined.signal, `${OPENCODE_SDK_PROVIDER} exec cancelled by client`);
286
+ const messagesBody = messagesResponse.data;
287
+ const messages = messagesBody.data ?? [];
288
+ const { finalAssistant, finalMessage } = eventController.finalAssistantText(messages);
289
+ if (finalAssistant?.error) {
290
+ const message = coerceString(finalAssistant.error.message) ?? `${OPENCODE_SDK_PROVIDER} assistant error`;
291
+ builder.emit({ type: "diagnostic", text: message });
292
+ builder.emit({ type: "fail", message });
293
+ throw new Error(message);
294
+ }
295
+ if (finalMessage) {
296
+ builder.emit({
297
+ type: "message",
298
+ id: finalAssistant?.id ?? "opencode-final",
299
+ text: finalMessage,
300
+ });
301
+ }
302
+ eventController.closeTurn();
303
+ const sessionEnvelope = {
304
+ provider: "opencode-sdk",
305
+ version: SUBAGENT_SESSION_VERSION,
306
+ handle: { sessionId: activeSessionId },
307
+ };
308
+ const resolvedFinal = finalMessage ?? "";
309
+ builder.emit({
310
+ type: "done",
311
+ finalMessage: resolvedFinal,
312
+ session: sessionEnvelope,
313
+ });
314
+ return { finalMessage: resolvedFinal, session: sessionEnvelope };
315
+ }
316
+ catch (error) {
317
+ appendDebugLog(`startOpencode error: ${error instanceof Error ? `${error.name}: ${error.message}` : String(error)}`, { cwd, section: OPENCODE_SDK_PROVIDER });
318
+ throw error;
319
+ }
320
+ finally {
321
+ acceptingEnqueue = false;
322
+ if (activeSessionId) {
323
+ appendDebugBlock("opencode-sdk stdout event types", renderEventTypeSummary(eventController?.recordedEventTypes() ?? [], eventController?.totalEventCount() ?? 0), { cwd, section: OPENCODE_SDK_PROVIDER });
324
+ }
325
+ streamAbort?.abort();
326
+ idleMonitor?.dispose();
327
+ idleMonitor = undefined;
328
+ executionSignal = undefined;
329
+ try {
330
+ await sdk?.close?.();
331
+ }
332
+ catch (error) {
333
+ rejectRunSettled(error);
334
+ throw error;
335
+ }
336
+ finally {
337
+ builder.signal.removeEventListener("abort", forward);
338
+ options.signal?.removeEventListener("abort", forward);
339
+ resolveRunSettled();
340
+ }
341
+ }
342
+ },
343
+ });
344
+ }