@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
@@ -1,380 +1,33 @@
1
1
  import { z } from "zod";
2
- import { appendDebugLog } from "../../telemetry/debug-log.js";
3
- import { SUBAGENT_SESSION_VERSION } from "../session.js";
4
- import { createProjectionDriver } from "../harness/execution-handle.js";
5
- import { activityCallFromNamedTool, boundActivityExcerpt } from "../harness/activity-values.js";
6
- import { buildSubagentEnv, coerceString, createAbortError, emitProviderEvidence, linkAbortSignal, sha256Utf8 } from "../harness/runtime.js";
7
- import { AGENT_PLAN_ITEM_TEXT_MAX_CHARS, AGENT_PLAN_MAX_ITEMS, AGENT_THOUGHT_TEXT_MAX_CHARS, } from "../harness/events.js";
8
- const CLAUDE_MAPPED = new Set(["assistant", "user", "result", "system", "fallback"]);
9
- const CLAUDE_ACKNOWLEDGED_UNPERSISTED = new Set([
10
- "stream_event",
11
- "rate_limit_event",
12
- "control_cancel_request",
13
- "control_request",
14
- "control_response",
15
- "conversation_reset",
16
- "keep_alive",
17
- "local",
18
- "prompt_suggestion",
19
- "tool_progress",
20
- "tool_use_summary",
21
- "auth_status",
22
- ]);
23
- function claudeIngressDisposition(type) {
24
- if (CLAUDE_MAPPED.has(type))
25
- return "mapped";
26
- if (CLAUDE_ACKNOWLEDGED_UNPERSISTED.has(type))
27
- return "acknowledged-unpersisted";
28
- return "unknown";
29
- }
2
+ import { appendDebugLog } from "../../../telemetry/debug-log.js";
3
+ import { SUBAGENT_SESSION_VERSION } from "../../session.js";
4
+ import { createProjectionDriver } from "../../harness/execution-handle.js";
5
+ import { buildSubagentEnv, createAbortError, emitProviderEvidence, linkAbortSignal, sha256Utf8 } from "../../harness/runtime.js";
6
+ import { SubagentFailure } from "../../types.js";
7
+ import { claudeIngressDisposition, emitClaudeThoughts, emitClaudeToolResults, emitClaudeToolStarts, emitClaudeUsage, extractAssistantMessage, extractAssistantMessageId, extractAssistantMessageModel, extractOverloadFallbackWarning, extractResultText, extractResumeToken, isClaudeApiRetryEvent, isClaudeModelRefusalFallbackEvent, isClaudeModelRefusalNoFallbackEvent, mapClaudeApiRetryWarning, } from "./events.js";
8
+ import { CLAUDE_AGENT_SDK_PROVIDER, createPushableUserMessageStream, toClaudeUserMessage, } from "./session.js";
30
9
  function toClaudeSdkEffort(effort) {
31
10
  return effort; // opacity boundary
32
11
  }
33
12
  export const claudeSessionSchema = z.object({ sessionId: z.string().trim().min(1) });
34
- const CLAUDE_AGENT_SDK_PROVIDER = "claude-agent-sdk";
35
13
  let claudeAgentSdkLoader;
36
- /** Sentinel for IteratorResult<SDKUserMessage> done=true returns.
37
- * The SDK's AsyncIterator protocol requires the value field even when done=true.
38
- * Consumers never read it, but TypeScript needs the type to match. */
39
- const DONE_SENTINEL = undefined;
40
- function extractMessageText(content) {
41
- if (!Array.isArray(content))
42
- return undefined;
43
- const text = content
44
- .map((block) => {
45
- if (typeof block !== "object" || block === null || !("text" in block))
46
- return "";
47
- const candidate = block.text;
48
- return typeof candidate === "string" ? candidate : "";
49
- })
50
- .join("")
51
- .trim();
52
- return text || undefined;
53
- }
54
- function extractAssistantMessage(event) {
55
- return extractMessageText(event.message.content);
56
- }
57
- function extractAssistantMessageModel(event) {
58
- return coerceString(event.message.model);
59
- }
60
- function claudeActivityCall(name, input) {
61
- if (!isRecord(input))
62
- return { kind: "other", display: name };
63
- return activityCallFromNamedTool(name, {
64
- ...(typeof input.command === "string" ? { command: input.command } : {}),
65
- ...(typeof input.description === "string" ? { description: input.description } : {}),
66
- ...(typeof input.file_path === "string" ? { path: input.file_path } : {}),
67
- ...(typeof input.offset === "number" && Number.isFinite(input.offset) ? { offset: input.offset } : {}),
68
- ...(typeof input.limit === "number" && Number.isFinite(input.limit) ? { limit: input.limit } : {}),
69
- ...(typeof input.query === "string" ? { query: input.query } : {}),
70
- });
71
- }
72
- function claudeResultMessage(content) {
73
- if (typeof content === "string")
74
- return content ? boundActivityExcerpt(content) : undefined;
75
- if (!Array.isArray(content))
76
- return undefined;
77
- const text = content.flatMap((entry) => {
78
- if (!isRecord(entry) || entry.type !== "text" || typeof entry.text !== "string")
79
- return [];
80
- return [entry.text];
81
- }).join("\n");
82
- return text ? boundActivityExcerpt(text) : undefined;
83
- }
84
- function emitClaudeToolStarts(builder, event, seenToolUses) {
85
- if (!Array.isArray(event.message.content))
86
- return;
87
- for (const block of event.message.content) {
88
- if (!isRecord(block) || block.type !== "tool_use")
89
- continue;
90
- const id = coerceString(block.id);
91
- const name = coerceString(block.name);
92
- if (!id || !name)
93
- continue;
94
- const call = claudeActivityCall(name, block.input);
95
- seenToolUses.set(id, { tool: name, call });
96
- builder.emit({
97
- type: "activity",
98
- phase: "started",
99
- id,
100
- tool: name,
101
- call,
102
- });
103
- if (name === "TodoWrite" && isRecord(block.input) && Array.isArray(block.input.todos)) {
104
- const items = block.input.todos.slice(0, AGENT_PLAN_MAX_ITEMS).flatMap((todo) => {
105
- if (!isRecord(todo) || typeof todo.content !== "string" || typeof todo.status !== "string") {
106
- return [];
107
- }
108
- return [{
109
- text: todo.content.slice(0, AGENT_PLAN_ITEM_TEXT_MAX_CHARS),
110
- done: todo.status === "completed",
111
- ...(todo.status === "in_progress" ? { active: true } : {}),
112
- }];
113
- });
114
- builder.emit({ type: "plan", id, items });
115
- }
116
- }
117
- }
118
- function emitClaudeThoughts(builder, event) {
119
- if (!Array.isArray(event.message.content))
120
- return;
121
- event.message.content.forEach((block, blockIndex) => {
122
- if (!isRecord(block) || block.type !== "thinking" || typeof block.thinking !== "string")
123
- return;
124
- const truncated = block.thinking.length > AGENT_THOUGHT_TEXT_MAX_CHARS;
125
- builder.emit({
126
- type: "thought",
127
- id: `${event.uuid}:${blockIndex}`,
128
- text: block.thinking.slice(0, AGENT_THOUGHT_TEXT_MAX_CHARS),
129
- final: true,
130
- ...(truncated ? { truncated: true } : {}),
131
- });
132
- });
133
- }
134
- function emitClaudeToolResults(builder, event, seenToolUses) {
135
- const content = event.message.content;
136
- if (!Array.isArray(content))
137
- return;
138
- for (const block of content) {
139
- if (!isRecord(block) || block.type !== "tool_result")
140
- continue;
141
- const id = coerceString(block.tool_use_id);
142
- if (!id)
143
- continue;
144
- const observed = seenToolUses.get(id);
145
- if (!observed)
146
- continue;
147
- seenToolUses.delete(id);
148
- const message = claudeResultMessage(block.content);
149
- builder.emit({
150
- type: "activity",
151
- phase: "completed",
152
- id,
153
- tool: observed.tool,
154
- call: observed.call,
155
- result: {
156
- status: block.is_error === true ? "error" : "ok",
157
- ...(message ? { message } : {}),
158
- },
159
- });
160
- }
161
- }
162
- function emitClaudeUsage(builder, event, model) {
163
- const usage = event.usage;
164
- builder.emit({
165
- type: "usage",
166
- inputTokens: usage.input_tokens,
167
- outputTokens: usage.output_tokens,
168
- cachedInputTokens: usage.cache_read_input_tokens,
169
- ...(typeof event.total_cost_usd === "number" ? { costUsd: event.total_cost_usd } : {}),
170
- ...(model ? { model } : {}),
171
- });
172
- }
173
- function isRecord(value) {
174
- return typeof value === "object" && value !== null;
175
- }
176
- function isClaudeModelRefusalFallbackEvent(event) {
177
- return event.type === "system" && event.subtype === "model_refusal_fallback";
178
- }
179
- function isClaudeModelRefusalNoFallbackEvent(event) {
180
- return event.type === "system" && event.subtype === "model_refusal_no_fallback";
181
- }
182
- function extractFallbackRequestId(value) {
183
- if (value === null)
184
- return null;
185
- return coerceString(value);
186
- }
187
- function extractOverloadFallbackWarning(event) {
188
- if (!isRecord(event) || event.type !== "fallback") {
189
- return undefined;
190
- }
191
- const originalModel = coerceString(event.original_model);
192
- const fallbackModel = coerceString(event.fallback_model);
193
- const direction = coerceString(event.direction);
194
- const requestId = extractFallbackRequestId(event.request_id);
195
- return {
196
- reason: "overload",
197
- ...(originalModel ? { originalModel } : {}),
198
- ...(fallbackModel ? { fallbackModel } : {}),
199
- ...(direction ? { direction } : {}),
200
- ...(requestId !== undefined ? { requestId } : {}),
201
- };
202
- }
203
- function extractResumeToken(event) {
204
- return coerceString(event.session_id);
205
- }
206
- function extractResultText(event) {
207
- if (event.subtype === "success") {
208
- return coerceString(event.result);
14
+ async function loadClaudeAgentSdk(loader) {
15
+ if (loader) {
16
+ return loader();
209
17
  }
210
- const errorText = event.errors.join("\n").trim();
211
- return errorText || undefined;
212
- }
213
- async function loadClaudeAgentSdk() {
214
18
  if (claudeAgentSdkLoader) {
215
19
  return claudeAgentSdkLoader();
216
20
  }
217
21
  return (await import("@anthropic-ai/claude-agent-sdk"));
218
22
  }
23
+ /** @deprecated Use ClaudeProviderOptions.loader instead. Kept for pipeline-based tests pending loader channel. */
219
24
  export function setClaudeAgentSdkLoaderForTests(loader) {
220
25
  claudeAgentSdkLoader = loader;
221
26
  }
27
+ /** @deprecated Use ClaudeProviderOptions.loader instead. */
222
28
  export function resetClaudeAgentSdkLoaderForTests() {
223
29
  claudeAgentSdkLoader = undefined;
224
30
  }
225
- function createMessageAck() {
226
- let resolve;
227
- let reject;
228
- const promise = new Promise((res, rej) => {
229
- resolve = res;
230
- reject = rej;
231
- });
232
- // Prevent unhandled rejection if nobody awaits before reject-on-end.
233
- promise.catch(() => undefined);
234
- const ack = {
235
- settled: false,
236
- settleResolve() {
237
- if (ack.settled)
238
- return;
239
- ack.settled = true;
240
- resolve();
241
- },
242
- settleReject(error) {
243
- if (ack.settled)
244
- return;
245
- ack.settled = true;
246
- reject(error);
247
- },
248
- wait() {
249
- return promise;
250
- },
251
- };
252
- return ack;
253
- }
254
- /**
255
- * Long-lived push source for Claude Query.
256
- * Submitted fence is per-yield: entering next() first acknowledges the previously
257
- * yielded message, then yields the next item. That next-entry after a yield is the
258
- * post-yield ack (transport.write completed for the prior yield).
259
- */
260
- export function createPushableUserMessageStream(onYield) {
261
- const queue = [];
262
- const waiters = [];
263
- /** Ack for the message most recently yielded; resolved on the subsequent next() entry. */
264
- let pendingYieldAck;
265
- let closed = false;
266
- let failure;
267
- let pullCount = 0;
268
- const terminalError = (error) => error ?? new Error(`${CLAUDE_AGENT_SDK_PROVIDER} input stream ended before submitted ack`);
269
- const rejectUnresolvedAcks = (error) => {
270
- const err = terminalError(error);
271
- if (pendingYieldAck) {
272
- pendingYieldAck.settleReject(err);
273
- pendingYieldAck = undefined;
274
- }
275
- while (queue.length > 0) {
276
- queue.shift().ack.settleReject(err);
277
- }
278
- };
279
- /** First action on every next() entry: post-yield ack for the prior yield. */
280
- const acknowledgePreviousYield = () => {
281
- if (!pendingYieldAck)
282
- return;
283
- pendingYieldAck.settleResolve();
284
- pendingYieldAck = undefined;
285
- };
286
- const yieldQueued = (entry) => {
287
- pullCount += 1;
288
- pendingYieldAck = entry.ack;
289
- onYield?.(entry.message);
290
- return { value: entry.message, done: false };
291
- };
292
- return {
293
- get pullCount() {
294
- return pullCount;
295
- },
296
- get queueLength() {
297
- return queue.length;
298
- },
299
- push(message) {
300
- const ack = createMessageAck();
301
- if (closed || failure !== undefined) {
302
- ack.settleReject(terminalError(failure));
303
- return { waitSubmitted: () => ack.wait() };
304
- }
305
- const waiter = waiters.shift();
306
- if (waiter) {
307
- // Consumer is already inside next() (previous yield already acked at entry).
308
- pullCount += 1;
309
- pendingYieldAck = ack;
310
- onYield?.(message);
311
- waiter.resolve({ value: message, done: false });
312
- return { waitSubmitted: () => ack.wait() };
313
- }
314
- queue.push({ message, ack });
315
- return { waitSubmitted: () => ack.wait() };
316
- },
317
- end(error) {
318
- if (closed)
319
- return;
320
- closed = true;
321
- if (error !== undefined) {
322
- failure = error;
323
- // Failure: reject every unresolved ack (yielded + still queued) so enqueue cannot hang.
324
- rejectUnresolvedAcks(error);
325
- while (waiters.length > 0) {
326
- waiters.shift().reject(failure);
327
- }
328
- return;
329
- }
330
- // Clean end: leave queued messages readable for a late consumer drain.
331
- // Only reject a yielded-but-not-post-acked message (no further next() will ack it
332
- // if the consumer has already stopped). If a consumer is parked, they get done
333
- // after any remaining queue is taken on subsequent next() calls — parked waiters
334
- // here mean queue is empty, so resolve done.
335
- if (pendingYieldAck) {
336
- pendingYieldAck.settleReject(terminalError());
337
- pendingYieldAck = undefined;
338
- }
339
- while (waiters.length > 0) {
340
- waiters.shift().resolve({ value: DONE_SENTINEL, done: true });
341
- }
342
- },
343
- iterable: {
344
- [Symbol.asyncIterator]() {
345
- return {
346
- next() {
347
- // Per-yield submitted fence: next entry acks the previously yielded message.
348
- acknowledgePreviousYield();
349
- if (queue.length > 0) {
350
- return Promise.resolve(yieldQueued(queue.shift()));
351
- }
352
- if (failure !== undefined) {
353
- return Promise.reject(failure);
354
- }
355
- if (closed) {
356
- return Promise.resolve({ value: DONE_SENTINEL, done: true });
357
- }
358
- return new Promise((resolve, reject) => {
359
- waiters.push({ resolve, reject });
360
- });
361
- },
362
- };
363
- },
364
- },
365
- };
366
- }
367
- function toUserMessage(text) {
368
- return {
369
- type: "user",
370
- message: {
371
- role: "user",
372
- content: text,
373
- },
374
- parent_tool_use_id: null,
375
- session_id: "",
376
- };
377
- }
378
31
  export async function startClaudeAgentSdk(prompt, cwd, options) {
379
32
  const sessionId = options.sessionId?.trim();
380
33
  let liveQuery;
@@ -450,7 +103,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
450
103
  // The pump calls enqueue at a successful result checkpoint. Push now;
451
104
  // waiting for another result before pushing would deadlock the next turn.
452
105
  // text is already core-rendered; send byte-for-byte. id is for the frame only (inside text).
453
- const submitted = inputStream.push(toUserMessage(message.text));
106
+ const submitted = inputStream.push(toClaudeUserMessage(message.text));
454
107
  // submitted = NEXT source next() after this tell is yielded (not the yield pull itself).
455
108
  await submitted.waitSubmitted();
456
109
  emitTurnStarted?.();
@@ -470,10 +123,16 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
470
123
  initialTurn: options.initialTurn,
471
124
  enqueue,
472
125
  onIngress: options.onIngress,
473
- async abort() {
126
+ async abort(mode) {
474
127
  queryEnded = true;
475
128
  queryActive = false;
476
129
  const abortError = createAbortError(`${CLAUDE_AGENT_SDK_PROVIDER} exec cancelled by client`);
130
+ if (mode === "force") {
131
+ inputStream?.end(abortError);
132
+ rejectPendingEnqueueWaiters(abortError);
133
+ liveQuery?.close();
134
+ return;
135
+ }
477
136
  try {
478
137
  await liveQuery?.interrupt();
479
138
  }
@@ -491,12 +150,12 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
491
150
  pendingEvidenceDiagnostics -= 1;
492
151
  builderDiagnostic("provider evidence callback failed");
493
152
  }
494
- const sdk = await loadClaudeAgentSdk();
153
+ const sdk = await loadClaudeAgentSdk(options.loader);
495
154
  const queryAbortController = new AbortController();
496
155
  const unlinkBuilderAbort = linkAbortSignal(builder.signal, queryAbortController, () => undefined);
497
156
  const unlinkOuterAbort = linkAbortSignal(options.signal, queryAbortController, () => undefined);
498
157
  try {
499
- const initialMessage = toUserMessage(prompt);
158
+ const initialMessage = toClaudeUserMessage(prompt);
500
159
  inputStream = createPushableUserMessageStream((message) => {
501
160
  if (!options.onEvidence)
502
161
  return;
@@ -515,7 +174,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
515
174
  cwd,
516
175
  model: options.model,
517
176
  resume: sessionId,
518
- env: buildSubagentEnv(),
177
+ env: buildSubagentEnv(options.env),
519
178
  abortController: queryAbortController,
520
179
  persistSession: true,
521
180
  ...(options.systemPrompt ? { systemPrompt: options.systemPrompt } : {}),
@@ -554,10 +213,23 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
554
213
  let resumedSessionId = sessionId;
555
214
  let finalMessage;
556
215
  let latestAssistantMessageModel;
216
+ // Provider acceptance / pre-terminal retry evidence for no-turn classification.
217
+ // turn.started is a harness-local pre-query event and is not acceptance.
218
+ let acceptedTurn = false;
219
+ let lastPreTerminalEvidenceWasRetry = false;
557
220
  const seenToolUses = new Map();
558
221
  for await (const event of query) {
559
222
  builder.ingress(CLAUDE_AGENT_SDK_PROVIDER, event.type, event, claudeIngressDisposition(event.type), () => {
560
223
  resumedSessionId = extractResumeToken(event) ?? resumedSessionId;
224
+ if (isClaudeApiRetryEvent(event)) {
225
+ lastPreTerminalEvidenceWasRetry = true;
226
+ builder.emit({ type: "warning", warning: mapClaudeApiRetryWarning(event) });
227
+ }
228
+ else if (!(event.type === "result" && event.subtype !== "success")) {
229
+ // Any other non-terminal-failure native event clears the last-retry predicate.
230
+ // Failed result itself is terminal and must not overwrite prior evidence.
231
+ lastPreTerminalEvidenceWasRetry = false;
232
+ }
561
233
  const overloadWarning = extractOverloadFallbackWarning(event);
562
234
  if (overloadWarning) {
563
235
  builder.emit({ type: "warning", warning: overloadWarning });
@@ -588,22 +260,25 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
588
260
  }
589
261
  switch (event.type) {
590
262
  case "assistant": {
263
+ acceptedTurn = true;
591
264
  latestAssistantMessageModel = extractAssistantMessageModel(event) ?? latestAssistantMessageModel;
592
265
  emitClaudeThoughts(builder, event);
593
266
  emitClaudeToolStarts(builder, event, seenToolUses);
594
267
  finalMessage = extractAssistantMessage(event) ?? finalMessage;
595
268
  if (finalMessage) {
596
- builder.emit({ type: "message", text: finalMessage });
269
+ builder.emit({ type: "message", id: extractAssistantMessageId(event), text: finalMessage });
597
270
  }
598
271
  break;
599
272
  }
600
273
  case "user": {
274
+ acceptedTurn = true;
601
275
  emitClaudeToolResults(builder, event, seenToolUses);
602
276
  break;
603
277
  }
604
278
  case "result": {
605
279
  const resultText = extractResultText(event);
606
280
  if (event.subtype === "success") {
281
+ acceptedTurn = true;
607
282
  finalMessage = resultText ?? finalMessage;
608
283
  emitClaudeUsage(builder, event, latestAssistantMessageModel);
609
284
  builder.emit({ type: "turn", phase: "completed" });
@@ -612,6 +287,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
612
287
  bumpResultOrdinal();
613
288
  const checkpoint = {
614
289
  fence: { kind: "ordinal", ordinal: completedResultOrdinal },
290
+ consumptionEvidence: true,
615
291
  };
616
292
  // Record the native result before the synchronous pump can
617
293
  // consume any tell fenced to an earlier result.
@@ -619,7 +295,7 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
619
295
  emitProviderEvidence(options.onEvidence, {
620
296
  type: "checkpoint",
621
297
  provider: CLAUDE_AGENT_SDK_PROVIDER,
622
- ...checkpoint,
298
+ fence: checkpoint.fence,
623
299
  source: "response",
624
300
  }, (text) => builder.emit({ type: "diagnostic", text }));
625
301
  }
@@ -634,9 +310,11 @@ export async function startClaudeAgentSdk(prompt, cwd, options) {
634
310
  builder.emit({ type: "diagnostic", text: resultText });
635
311
  }
636
312
  const message = resultText ?? `${CLAUDE_AGENT_SDK_PROVIDER} exec failed`;
637
- builder.emit({ type: "fail", message });
313
+ const unavailable = !acceptedTurn && lastPreTerminalEvidenceWasRetry;
314
+ const failCode = unavailable ? "SUBAGENT_PROVIDER_UNAVAILABLE" : "SUBAGENT_EXEC_ERROR";
315
+ builder.emit({ type: "fail", message, code: failCode });
638
316
  inputStream?.end();
639
- throw new Error(message);
317
+ throw new SubagentFailure(failCode, message);
640
318
  }
641
319
  default:
642
320
  break;