@astrosheep/keiyaku 0.1.87 → 1.0.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.
- package/README.md +12 -22
- package/build/.tsbuildinfo +1 -1
- package/build/agents/effective-policy.js +32 -0
- package/build/agents/harness/event-channel.js +178 -0
- package/build/agents/harness/execution-handle.js +117 -0
- package/build/agents/harness/index.js +622 -0
- package/build/agents/harness/pump.js +152 -0
- package/build/agents/harness/runtime.js +68 -0
- package/build/agents/index.js +104 -213
- package/build/agents/opencode-sdk.js +12 -18
- package/build/agents/providers/claude-agent-sdk.js +529 -0
- package/build/agents/providers/codex-app-server.js +656 -0
- package/build/agents/providers/codex-sdk.js +193 -0
- package/build/agents/providers/opencode-sdk.js +257 -0
- package/build/agents/providers/pi.js +345 -0
- package/build/agents/selector.js +15 -6
- package/build/agents/session.js +29 -0
- package/build/agents/types.js +5 -11
- package/build/cli/commands/akuma.js +79 -0
- package/build/cli/completion.js +71 -0
- package/build/cli/draft-artifact.js +50 -0
- package/build/cli/flags.js +96 -0
- package/build/cli/help.js +108 -0
- package/build/cli/index.js +204 -912
- package/build/cli/parse.js +280 -0
- package/build/cli/render/arc.js +63 -0
- package/build/{responses/tool-errors.js → cli/render/errors.js} +15 -16
- package/build/cli/render/format.js +199 -0
- package/build/cli/render/misc.js +40 -0
- package/build/cli/render/petition.js +32 -0
- package/build/{responses → cli/render}/response-style.js +4 -4
- package/build/cli/render/shared.js +122 -0
- package/build/cli/render/status.js +69 -0
- package/build/cli/render/summon.js +17 -0
- package/build/cli/render/tell.js +11 -0
- package/build/cli/skills-install.js +237 -0
- package/build/cli/subagent-guard.js +6 -3
- package/build/config/akuma-loader.js +268 -0
- package/build/config/env-keys.js +0 -10
- package/build/config/{schema.js → env.js} +117 -23
- package/build/config/settings.js +137 -260
- package/build/core/amend.js +120 -0
- package/build/core/arc.js +225 -0
- package/build/core/atomic-publish.js +64 -0
- package/build/core/bind.js +281 -0
- package/build/core/claim.js +360 -0
- package/build/core/command-io.js +174 -0
- package/build/core/context.js +108 -0
- package/build/core/contract.js +14 -0
- package/build/core/draft.js +82 -0
- package/build/core/entry.js +203 -0
- package/build/core/forfeit.js +60 -0
- package/build/core/hints.js +108 -0
- package/build/core/ids.js +70 -0
- package/build/core/ledger.js +117 -0
- package/build/core/log.js +24 -0
- package/build/{protocol → core}/markdown/parser.js +68 -0
- package/build/{protocol → core}/markdown/sections.js +2 -28
- package/build/core/markdown/titles.js +9 -0
- package/build/{tools/petition/claim-gates.js → core/petition-claim-gates.js} +43 -29
- package/build/core/petition-claim.js +116 -0
- package/build/core/petition-forfeit.js +39 -0
- package/build/{tools/round/head-guard.js → core/petition-head-guard.js} +2 -2
- package/build/core/petition-run.js +81 -0
- package/build/core/petition.js +96 -0
- package/build/core/places.js +82 -0
- package/build/core/projection-core.js +541 -0
- package/build/core/projection-mint.js +79 -0
- package/build/core/projection-status.js +179 -0
- package/build/core/projection-wake.js +252 -0
- package/build/core/queue.js +73 -0
- package/build/core/ref-log.js +94 -0
- package/build/core/registry.js +96 -0
- package/build/core/render.js +326 -0
- package/build/core/renew.js +195 -0
- package/build/core/scope.js +163 -0
- package/build/core/seal.js +195 -0
- package/build/core/status.js +476 -0
- package/build/{tools/summon/persist.js → core/summon-persist.js} +10 -9
- package/build/core/summon.js +377 -0
- package/build/{protocol/response-history.js → core/transcripts.js} +83 -39
- package/build/core/verdict.js +96 -0
- package/build/core/worktree-path.js +134 -0
- package/build/flow-error.js +80 -4
- package/build/generated/version.js +1 -1
- package/build/git/branches.js +8 -9
- package/build/git/core.js +1 -1
- package/build/git/diff/pathspec.js +2 -3
- package/build/git/diff/preview.js +7 -2
- package/build/git/diff/read.js +4 -3
- package/build/git/staging.js +1 -1
- package/build/index.js +12 -6
- package/build/keiyaku.js +17 -13
- package/build/telemetry/debug-log.js +1 -1
- package/package.json +16 -9
- package/skills/keiyaku/SKILL.md +16 -13
- package/build/agents/claude-agent-sdk-exec.js +0 -352
- package/build/agents/codex-cli-exec.js +0 -237
- package/build/agents/codex-sdk-exec.js +0 -291
- package/build/agents/gemini-cli-core-exec.js +0 -499
- package/build/agents/opencode-exec.js +0 -297
- package/build/agents/process-runner.js +0 -239
- package/build/agents/progress-reporter.js +0 -137
- package/build/agents/round-runner.js +0 -53
- package/build/config/apply-argument-descriptions.js +0 -16
- package/build/config/architect-hints.js +0 -374
- package/build/config/base-rules.js +0 -23
- package/build/config/config.js +0 -50
- package/build/config/dotenv.js +0 -61
- package/build/config/keiyaku-home.js +0 -9
- package/build/config/path-utils.js +0 -11
- package/build/config/render-template.js +0 -8
- package/build/git/diff/filter.js +0 -67
- package/build/git/keiyaku-state.js +0 -58
- package/build/git/snapshot.js +0 -41
- package/build/git/stash.js +0 -62
- package/build/protocol/bind-draft.js +0 -195
- package/build/protocol/draft-artifacts.js +0 -99
- package/build/protocol/file-guards.js +0 -24
- package/build/protocol/keiyaku-draft.js +0 -168
- package/build/protocol/ledger.js +0 -374
- package/build/protocol/markdown/index.js +0 -14
- package/build/protocol/markdown/normalization.js +0 -71
- package/build/protocol/markdown/render.js +0 -68
- package/build/protocol/oath.js +0 -36
- package/build/protocol/render-keiyaku.js +0 -37
- package/build/protocol/review.js +0 -1
- package/build/protocol/status-previews.js +0 -68
- package/build/responses/responses.js +0 -515
- package/build/tools/amend/index.js +0 -18
- package/build/tools/amend/run.js +0 -44
- package/build/tools/bind/index.js +0 -44
- package/build/tools/bind/run.js +0 -237
- package/build/tools/contracts.js +0 -1
- package/build/tools/create-handler.js +0 -31
- package/build/tools/help.js +0 -9
- package/build/tools/petition/claim.js +0 -174
- package/build/tools/petition/forfeit.js +0 -100
- package/build/tools/petition/index.js +0 -83
- package/build/tools/petition/run.js +0 -66
- package/build/tools/round/guidance.js +0 -7
- package/build/tools/round/prompt.js +0 -70
- package/build/tools/round/report.js +0 -153
- package/build/tools/round/worktree.js +0 -55
- package/build/tools/round-close/index.js +0 -19
- package/build/tools/round-close/run.js +0 -98
- package/build/tools/round-open/index.js +0 -20
- package/build/tools/round-open/run.js +0 -77
- package/build/tools/schema.js +0 -69
- package/build/tools/status/index.js +0 -16
- package/build/tools/status/read.js +0 -155
- package/build/tools/summon/index.js +0 -27
- package/build/tools/summon/run.js +0 -80
- /package/build/{config/toml.js → agents/harness/events.js} +0 -0
- /package/build/{git/diff/contracts.js → cli/render/types.js} +0 -0
- /package/build/{protocol/bind-input.js → cli/types.js} +0 -0
- /package/build/{protocol → core}/markdown/lex.js +0 -0
- /package/build/{protocol → core}/markdown/types.js +0 -0
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { appendDebugLog } from "../../telemetry/debug-log.js";
|
|
3
|
+
import { SUBAGENT_SESSION_VERSION } from "../session.js";
|
|
4
|
+
import { coerceString } from "../index.js";
|
|
5
|
+
import { createProjectionDriver } from "../harness/execution-handle.js";
|
|
6
|
+
import { buildSubagentEnv, createAbortError, emitProviderEvidence, linkAbortSignal, sha256Utf8 } from "../harness/runtime.js";
|
|
7
|
+
export const claudeSessionSchema = z.object({ sessionId: z.string().trim().min(1) });
|
|
8
|
+
const CLAUDE_AGENT_SDK_PROVIDER = "claude-agent-sdk";
|
|
9
|
+
let claudeAgentSdkLoader;
|
|
10
|
+
/** Sentinel for IteratorResult<SDKUserMessage> done=true returns.
|
|
11
|
+
* The SDK's AsyncIterator protocol requires the value field even when done=true.
|
|
12
|
+
* Consumers never read it, but TypeScript needs the type to match. */
|
|
13
|
+
const DONE_SENTINEL = undefined;
|
|
14
|
+
function extractMessageText(content) {
|
|
15
|
+
if (!Array.isArray(content))
|
|
16
|
+
return undefined;
|
|
17
|
+
const text = content
|
|
18
|
+
.map((block) => {
|
|
19
|
+
if (typeof block !== "object" || block === null || !("text" in block))
|
|
20
|
+
return "";
|
|
21
|
+
const candidate = block.text;
|
|
22
|
+
return typeof candidate === "string" ? candidate : "";
|
|
23
|
+
})
|
|
24
|
+
.join("")
|
|
25
|
+
.trim();
|
|
26
|
+
return text || undefined;
|
|
27
|
+
}
|
|
28
|
+
function extractAssistantMessage(event) {
|
|
29
|
+
return extractMessageText(event.message.content);
|
|
30
|
+
}
|
|
31
|
+
function extractAssistantMessageModel(event) {
|
|
32
|
+
return coerceString(event.message.model);
|
|
33
|
+
}
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return typeof value === "object" && value !== null;
|
|
36
|
+
}
|
|
37
|
+
function isClaudeModelRefusalFallbackEvent(event) {
|
|
38
|
+
return event.type === "system" && event.subtype === "model_refusal_fallback";
|
|
39
|
+
}
|
|
40
|
+
function isClaudeModelRefusalNoFallbackEvent(event) {
|
|
41
|
+
return event.type === "system" && event.subtype === "model_refusal_no_fallback";
|
|
42
|
+
}
|
|
43
|
+
function extractFallbackRequestId(value) {
|
|
44
|
+
if (value === null)
|
|
45
|
+
return null;
|
|
46
|
+
return coerceString(value);
|
|
47
|
+
}
|
|
48
|
+
function extractOverloadFallbackWarning(event) {
|
|
49
|
+
if (!isRecord(event) || event.type !== "fallback") {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
const originalModel = coerceString(event.original_model);
|
|
53
|
+
const fallbackModel = coerceString(event.fallback_model);
|
|
54
|
+
const direction = coerceString(event.direction);
|
|
55
|
+
const requestId = extractFallbackRequestId(event.request_id);
|
|
56
|
+
return {
|
|
57
|
+
reason: "overload",
|
|
58
|
+
...(originalModel ? { originalModel } : {}),
|
|
59
|
+
...(fallbackModel ? { fallbackModel } : {}),
|
|
60
|
+
...(direction ? { direction } : {}),
|
|
61
|
+
...(requestId !== undefined ? { requestId } : {}),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function extractResumeToken(event) {
|
|
65
|
+
return coerceString(event.session_id);
|
|
66
|
+
}
|
|
67
|
+
function extractResultText(event) {
|
|
68
|
+
if (event.subtype === "success") {
|
|
69
|
+
return coerceString(event.result);
|
|
70
|
+
}
|
|
71
|
+
const errorText = event.errors.join("\n").trim();
|
|
72
|
+
return errorText || undefined;
|
|
73
|
+
}
|
|
74
|
+
async function loadClaudeAgentSdk() {
|
|
75
|
+
if (claudeAgentSdkLoader) {
|
|
76
|
+
return claudeAgentSdkLoader();
|
|
77
|
+
}
|
|
78
|
+
return (await import("@anthropic-ai/claude-agent-sdk"));
|
|
79
|
+
}
|
|
80
|
+
export function setClaudeAgentSdkLoaderForTests(loader) {
|
|
81
|
+
claudeAgentSdkLoader = loader;
|
|
82
|
+
}
|
|
83
|
+
export function resetClaudeAgentSdkLoaderForTests() {
|
|
84
|
+
claudeAgentSdkLoader = undefined;
|
|
85
|
+
}
|
|
86
|
+
function createMessageAck() {
|
|
87
|
+
let resolve;
|
|
88
|
+
let reject;
|
|
89
|
+
const promise = new Promise((res, rej) => {
|
|
90
|
+
resolve = res;
|
|
91
|
+
reject = rej;
|
|
92
|
+
});
|
|
93
|
+
// Prevent unhandled rejection if nobody awaits before reject-on-end.
|
|
94
|
+
promise.catch(() => undefined);
|
|
95
|
+
const ack = {
|
|
96
|
+
settled: false,
|
|
97
|
+
settleResolve() {
|
|
98
|
+
if (ack.settled)
|
|
99
|
+
return;
|
|
100
|
+
ack.settled = true;
|
|
101
|
+
resolve();
|
|
102
|
+
},
|
|
103
|
+
settleReject(error) {
|
|
104
|
+
if (ack.settled)
|
|
105
|
+
return;
|
|
106
|
+
ack.settled = true;
|
|
107
|
+
reject(error);
|
|
108
|
+
},
|
|
109
|
+
wait() {
|
|
110
|
+
return promise;
|
|
111
|
+
},
|
|
112
|
+
};
|
|
113
|
+
return ack;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Long-lived push source for Claude Query.
|
|
117
|
+
* Submitted fence is per-yield: entering next() first acknowledges the previously
|
|
118
|
+
* yielded message, then yields the next item. That next-entry after a yield is the
|
|
119
|
+
* post-yield ack (transport.write completed for the prior yield).
|
|
120
|
+
*/
|
|
121
|
+
export function createPushableUserMessageStream(onYield) {
|
|
122
|
+
const queue = [];
|
|
123
|
+
const waiters = [];
|
|
124
|
+
/** Ack for the message most recently yielded; resolved on the subsequent next() entry. */
|
|
125
|
+
let pendingYieldAck;
|
|
126
|
+
let closed = false;
|
|
127
|
+
let failure;
|
|
128
|
+
let pullCount = 0;
|
|
129
|
+
const terminalError = (error) => error ?? new Error(`${CLAUDE_AGENT_SDK_PROVIDER} input stream ended before submitted ack`);
|
|
130
|
+
const rejectUnresolvedAcks = (error) => {
|
|
131
|
+
const err = terminalError(error);
|
|
132
|
+
if (pendingYieldAck) {
|
|
133
|
+
pendingYieldAck.settleReject(err);
|
|
134
|
+
pendingYieldAck = undefined;
|
|
135
|
+
}
|
|
136
|
+
while (queue.length > 0) {
|
|
137
|
+
queue.shift().ack.settleReject(err);
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
/** First action on every next() entry: post-yield ack for the prior yield. */
|
|
141
|
+
const acknowledgePreviousYield = () => {
|
|
142
|
+
if (!pendingYieldAck)
|
|
143
|
+
return;
|
|
144
|
+
pendingYieldAck.settleResolve();
|
|
145
|
+
pendingYieldAck = undefined;
|
|
146
|
+
};
|
|
147
|
+
const yieldQueued = (entry) => {
|
|
148
|
+
pullCount += 1;
|
|
149
|
+
pendingYieldAck = entry.ack;
|
|
150
|
+
onYield?.(entry.message);
|
|
151
|
+
return { value: entry.message, done: false };
|
|
152
|
+
};
|
|
153
|
+
return {
|
|
154
|
+
get pullCount() {
|
|
155
|
+
return pullCount;
|
|
156
|
+
},
|
|
157
|
+
get queueLength() {
|
|
158
|
+
return queue.length;
|
|
159
|
+
},
|
|
160
|
+
push(message) {
|
|
161
|
+
const ack = createMessageAck();
|
|
162
|
+
if (closed || failure !== undefined) {
|
|
163
|
+
ack.settleReject(terminalError(failure));
|
|
164
|
+
return { waitSubmitted: () => ack.wait() };
|
|
165
|
+
}
|
|
166
|
+
const waiter = waiters.shift();
|
|
167
|
+
if (waiter) {
|
|
168
|
+
// Consumer is already inside next() (previous yield already acked at entry).
|
|
169
|
+
pullCount += 1;
|
|
170
|
+
pendingYieldAck = ack;
|
|
171
|
+
onYield?.(message);
|
|
172
|
+
waiter.resolve({ value: message, done: false });
|
|
173
|
+
return { waitSubmitted: () => ack.wait() };
|
|
174
|
+
}
|
|
175
|
+
queue.push({ message, ack });
|
|
176
|
+
return { waitSubmitted: () => ack.wait() };
|
|
177
|
+
},
|
|
178
|
+
end(error) {
|
|
179
|
+
if (closed)
|
|
180
|
+
return;
|
|
181
|
+
closed = true;
|
|
182
|
+
if (error !== undefined) {
|
|
183
|
+
failure = error;
|
|
184
|
+
// Failure: reject every unresolved ack (yielded + still queued) so enqueue cannot hang.
|
|
185
|
+
rejectUnresolvedAcks(error);
|
|
186
|
+
while (waiters.length > 0) {
|
|
187
|
+
waiters.shift().reject(failure);
|
|
188
|
+
}
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
// Clean end: leave queued messages readable for a late consumer drain.
|
|
192
|
+
// Only reject a yielded-but-not-post-acked message (no further next() will ack it
|
|
193
|
+
// if the consumer has already stopped). If a consumer is parked, they get done
|
|
194
|
+
// after any remaining queue is taken on subsequent next() calls — parked waiters
|
|
195
|
+
// here mean queue is empty, so resolve done.
|
|
196
|
+
if (pendingYieldAck) {
|
|
197
|
+
pendingYieldAck.settleReject(terminalError());
|
|
198
|
+
pendingYieldAck = undefined;
|
|
199
|
+
}
|
|
200
|
+
while (waiters.length > 0) {
|
|
201
|
+
waiters.shift().resolve({ value: DONE_SENTINEL, done: true });
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
iterable: {
|
|
205
|
+
[Symbol.asyncIterator]() {
|
|
206
|
+
return {
|
|
207
|
+
next() {
|
|
208
|
+
// Per-yield submitted fence: next entry acks the previously yielded message.
|
|
209
|
+
acknowledgePreviousYield();
|
|
210
|
+
if (queue.length > 0) {
|
|
211
|
+
return Promise.resolve(yieldQueued(queue.shift()));
|
|
212
|
+
}
|
|
213
|
+
if (failure !== undefined) {
|
|
214
|
+
return Promise.reject(failure);
|
|
215
|
+
}
|
|
216
|
+
if (closed) {
|
|
217
|
+
return Promise.resolve({ value: DONE_SENTINEL, done: true });
|
|
218
|
+
}
|
|
219
|
+
return new Promise((resolve, reject) => {
|
|
220
|
+
waiters.push({ resolve, reject });
|
|
221
|
+
});
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
function toUserMessage(text) {
|
|
229
|
+
return {
|
|
230
|
+
type: "user",
|
|
231
|
+
message: {
|
|
232
|
+
role: "user",
|
|
233
|
+
content: text,
|
|
234
|
+
},
|
|
235
|
+
parent_tool_use_id: null,
|
|
236
|
+
session_id: "",
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
export async function startClaudeAgentSdk(prompt, cwd, options) {
|
|
240
|
+
const sessionId = options.sessionId?.trim();
|
|
241
|
+
let liveQuery;
|
|
242
|
+
let pendingEvidenceDiagnostics = 0;
|
|
243
|
+
let builderDiagnostic = () => {
|
|
244
|
+
pendingEvidenceDiagnostics += 1;
|
|
245
|
+
appendDebugLog("provider evidence callback failed before Claude driver start", {
|
|
246
|
+
cwd,
|
|
247
|
+
section: CLAUDE_AGENT_SDK_PROVIDER,
|
|
248
|
+
});
|
|
249
|
+
};
|
|
250
|
+
let inputStream;
|
|
251
|
+
let queryActive = false;
|
|
252
|
+
let queryEnded = false;
|
|
253
|
+
let completedResultOrdinal = 0;
|
|
254
|
+
let openSteerCount = 0;
|
|
255
|
+
let sawSuccessfulResult = false;
|
|
256
|
+
const activeWaiters = new Set();
|
|
257
|
+
const notifyActive = () => {
|
|
258
|
+
for (const resolve of [...activeWaiters]) {
|
|
259
|
+
activeWaiters.delete(resolve);
|
|
260
|
+
resolve();
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
const waitUntilQueryActive = async () => {
|
|
264
|
+
if (queryActive && inputStream && liveQuery)
|
|
265
|
+
return;
|
|
266
|
+
if (queryEnded) {
|
|
267
|
+
throw new Error(`${CLAUDE_AGENT_SDK_PROVIDER} cannot enqueue: query ended`);
|
|
268
|
+
}
|
|
269
|
+
await new Promise((resolve) => {
|
|
270
|
+
activeWaiters.add(resolve);
|
|
271
|
+
});
|
|
272
|
+
if (!queryActive || !inputStream || !liveQuery) {
|
|
273
|
+
throw new Error(`${CLAUDE_AGENT_SDK_PROVIDER} cannot enqueue: query is not active`);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
const bumpResultOrdinal = () => {
|
|
277
|
+
completedResultOrdinal += 1;
|
|
278
|
+
};
|
|
279
|
+
const rejectPendingEnqueueWaiters = (_error) => {
|
|
280
|
+
for (const resolve of [...activeWaiters]) {
|
|
281
|
+
activeWaiters.delete(resolve);
|
|
282
|
+
resolve();
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
const maybeEndInputStream = () => {
|
|
286
|
+
if (!inputStream)
|
|
287
|
+
return;
|
|
288
|
+
if (openSteerCount > 0)
|
|
289
|
+
return;
|
|
290
|
+
if (inputStream.queueLength > 0)
|
|
291
|
+
return;
|
|
292
|
+
if (!sawSuccessfulResult)
|
|
293
|
+
return;
|
|
294
|
+
inputStream.end();
|
|
295
|
+
};
|
|
296
|
+
appendDebugLog(`startClaudeAgentSdk: executable=${options.executable} model=${options.model ?? "default"} mode=${sessionId ? "resume" : "fresh"}`, { cwd, section: CLAUDE_AGENT_SDK_PROVIDER });
|
|
297
|
+
const enqueue = async (message) => {
|
|
298
|
+
// Reserve the stream before awaiting activation so the result checkpoint's
|
|
299
|
+
// deferred close cannot race a mailbox delivery queued in that checkpoint.
|
|
300
|
+
openSteerCount += 1;
|
|
301
|
+
try {
|
|
302
|
+
// Projection may be returned before run() arms the long-lived Query; wait, do not race-fail.
|
|
303
|
+
await waitUntilQueryActive();
|
|
304
|
+
if (!inputStream || !liveQuery || queryEnded) {
|
|
305
|
+
throw new Error(`${CLAUDE_AGENT_SDK_PROVIDER} cannot enqueue: query ended before yield`);
|
|
306
|
+
}
|
|
307
|
+
if (message.effort) {
|
|
308
|
+
await liveQuery.applyFlagSettings({ effortLevel: message.effort === "max" ? "xhigh" : message.effort });
|
|
309
|
+
}
|
|
310
|
+
// The pump calls enqueue at a successful result checkpoint. Push now;
|
|
311
|
+
// waiting for another result before pushing would deadlock the next turn.
|
|
312
|
+
// text is already core-rendered; send byte-for-byte. id is for the frame only (inside text).
|
|
313
|
+
const submitted = inputStream.push(toUserMessage(message.text));
|
|
314
|
+
// submitted = NEXT source next() after this tell is yielded (not the yield pull itself).
|
|
315
|
+
await submitted.waitSubmitted();
|
|
316
|
+
// 落账:记录提交时越过的 result ordinal(generation fence 落点)。
|
|
317
|
+
const submittedAfterResultOrdinal = completedResultOrdinal;
|
|
318
|
+
if (options.onEvidence)
|
|
319
|
+
emitProviderEvidence(options.onEvidence, { type: "ack", provider: CLAUDE_AGENT_SDK_PROVIDER, fence: { kind: "ordinal", ordinal: submittedAfterResultOrdinal }, outbound: "steer" }, builderDiagnostic);
|
|
320
|
+
return { fence: { kind: "ordinal", ordinal: submittedAfterResultOrdinal } };
|
|
321
|
+
}
|
|
322
|
+
finally {
|
|
323
|
+
openSteerCount -= 1;
|
|
324
|
+
maybeEndInputStream();
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
return createProjectionDriver({
|
|
328
|
+
steerMode: "mid-execution",
|
|
329
|
+
enqueue,
|
|
330
|
+
async abort() {
|
|
331
|
+
queryEnded = true;
|
|
332
|
+
queryActive = false;
|
|
333
|
+
const abortError = createAbortError(`${CLAUDE_AGENT_SDK_PROVIDER} exec cancelled by client`);
|
|
334
|
+
try {
|
|
335
|
+
await liveQuery?.interrupt();
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
// interrupt is best-effort acceleration for dismiss/abort only
|
|
339
|
+
}
|
|
340
|
+
liveQuery?.close();
|
|
341
|
+
inputStream?.end(abortError);
|
|
342
|
+
rejectPendingEnqueueWaiters(abortError);
|
|
343
|
+
},
|
|
344
|
+
async run(builder) {
|
|
345
|
+
builderDiagnostic = (text) => builder.emit({ type: "diagnostic", text });
|
|
346
|
+
while (pendingEvidenceDiagnostics > 0) {
|
|
347
|
+
pendingEvidenceDiagnostics -= 1;
|
|
348
|
+
builderDiagnostic("provider evidence callback failed");
|
|
349
|
+
}
|
|
350
|
+
const sdk = await loadClaudeAgentSdk();
|
|
351
|
+
const queryAbortController = new AbortController();
|
|
352
|
+
const unlinkBuilderAbort = linkAbortSignal(builder.signal, queryAbortController, () => undefined);
|
|
353
|
+
const unlinkOuterAbort = linkAbortSignal(options.signal, queryAbortController, () => undefined);
|
|
354
|
+
try {
|
|
355
|
+
const initialMessage = toUserMessage(prompt);
|
|
356
|
+
inputStream = createPushableUserMessageStream((message) => {
|
|
357
|
+
if (!options.onEvidence)
|
|
358
|
+
return;
|
|
359
|
+
if (message === initialMessage)
|
|
360
|
+
return;
|
|
361
|
+
const content = message.message.content;
|
|
362
|
+
const text = typeof content === "string"
|
|
363
|
+
? content
|
|
364
|
+
: content.map((block) => typeof block === "object" && block !== null && "text" in block && typeof block.text === "string" ? block.text : "").join("");
|
|
365
|
+
if (text)
|
|
366
|
+
emitProviderEvidence(options.onEvidence, { type: "outbound", provider: CLAUDE_AGENT_SDK_PROVIDER, direction: "input", kind: "steer", ...sha256Utf8(text) }, (text) => builder.emit({ type: "diagnostic", text }));
|
|
367
|
+
});
|
|
368
|
+
inputStream.push(initialMessage);
|
|
369
|
+
const queryOptions = {
|
|
370
|
+
cwd,
|
|
371
|
+
model: options.model,
|
|
372
|
+
permissionMode: options.permissionMode,
|
|
373
|
+
allowDangerouslySkipPermissions: options.permissionMode === "bypassPermissions",
|
|
374
|
+
settingSources: [...options.settingSources],
|
|
375
|
+
resume: sessionId,
|
|
376
|
+
env: buildSubagentEnv(),
|
|
377
|
+
abortController: queryAbortController,
|
|
378
|
+
persistSession: true,
|
|
379
|
+
...(options.systemPrompt ? { systemPrompt: options.systemPrompt } : {}),
|
|
380
|
+
stderr: (data) => {
|
|
381
|
+
builder.emit({ type: "diagnostic", text: data });
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
const execPathKey = `pathToClaudeCode${"Ex"}ecutable`;
|
|
385
|
+
queryOptions[execPathKey] = options.executable;
|
|
386
|
+
if (options.effort !== undefined) {
|
|
387
|
+
queryOptions.effort = options.effort;
|
|
388
|
+
}
|
|
389
|
+
// Long-lived push-AsyncIterable: one stream for the whole Query lifetime (Tier 1).
|
|
390
|
+
// Never mount short per-tell streams (that would endInput and break stdin).
|
|
391
|
+
if (options.onEvidence && options.systemPrompt)
|
|
392
|
+
emitProviderEvidence(options.onEvidence, { type: "outbound", provider: CLAUDE_AGENT_SDK_PROVIDER, direction: "input", kind: "system-prompt", ...sha256Utf8(options.systemPrompt.append) }, (text) => builder.emit({ type: "diagnostic", text }));
|
|
393
|
+
const query = sdk.query({
|
|
394
|
+
prompt: inputStream.iterable,
|
|
395
|
+
options: queryOptions,
|
|
396
|
+
});
|
|
397
|
+
liveQuery = query;
|
|
398
|
+
queryActive = true;
|
|
399
|
+
notifyActive();
|
|
400
|
+
let resumedSessionId = sessionId;
|
|
401
|
+
let finalMessage;
|
|
402
|
+
let latestAssistantMessageModel;
|
|
403
|
+
for await (const event of query) {
|
|
404
|
+
builder.emit({ type: "output", value: event });
|
|
405
|
+
resumedSessionId = extractResumeToken(event) ?? resumedSessionId;
|
|
406
|
+
const overloadWarning = extractOverloadFallbackWarning(event);
|
|
407
|
+
if (overloadWarning) {
|
|
408
|
+
builder.emit({ type: "warning", warning: overloadWarning });
|
|
409
|
+
}
|
|
410
|
+
if (isClaudeModelRefusalFallbackEvent(event)) {
|
|
411
|
+
builder.emit({
|
|
412
|
+
type: "warning",
|
|
413
|
+
warning: {
|
|
414
|
+
reason: "refusal",
|
|
415
|
+
originalModel: event.original_model,
|
|
416
|
+
fallbackModel: event.fallback_model,
|
|
417
|
+
direction: event.direction,
|
|
418
|
+
requestId: event.request_id,
|
|
419
|
+
...(latestAssistantMessageModel ? { signerModel: latestAssistantMessageModel } : {}),
|
|
420
|
+
},
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
else if (isClaudeModelRefusalNoFallbackEvent(event)) {
|
|
424
|
+
builder.emit({
|
|
425
|
+
type: "warning",
|
|
426
|
+
warning: {
|
|
427
|
+
reason: "refusal",
|
|
428
|
+
originalModel: event.original_model,
|
|
429
|
+
requestId: event.request_id,
|
|
430
|
+
...(latestAssistantMessageModel ? { signerModel: latestAssistantMessageModel } : {}),
|
|
431
|
+
},
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
switch (event.type) {
|
|
435
|
+
case "assistant": {
|
|
436
|
+
latestAssistantMessageModel = extractAssistantMessageModel(event) ?? latestAssistantMessageModel;
|
|
437
|
+
finalMessage = extractAssistantMessage(event) ?? finalMessage;
|
|
438
|
+
if (finalMessage) {
|
|
439
|
+
builder.emit({ type: "message", text: finalMessage });
|
|
440
|
+
}
|
|
441
|
+
break;
|
|
442
|
+
}
|
|
443
|
+
case "result": {
|
|
444
|
+
const resultText = extractResultText(event);
|
|
445
|
+
if (event.subtype === "success") {
|
|
446
|
+
finalMessage = resultText ?? finalMessage;
|
|
447
|
+
if (finalMessage) {
|
|
448
|
+
builder.emit({ type: "message", text: finalMessage });
|
|
449
|
+
}
|
|
450
|
+
sawSuccessfulResult = true;
|
|
451
|
+
// Advance generation fence BEFORE maybe-ending the stream so gated tells can yield.
|
|
452
|
+
bumpResultOrdinal();
|
|
453
|
+
const checkpoint = {
|
|
454
|
+
fence: { kind: "ordinal", ordinal: completedResultOrdinal },
|
|
455
|
+
};
|
|
456
|
+
// Record the native result before the synchronous pump can
|
|
457
|
+
// consume any tell fenced to an earlier result.
|
|
458
|
+
if (options.onEvidence) {
|
|
459
|
+
emitProviderEvidence(options.onEvidence, {
|
|
460
|
+
type: "checkpoint",
|
|
461
|
+
provider: CLAUDE_AGENT_SDK_PROVIDER,
|
|
462
|
+
...checkpoint,
|
|
463
|
+
source: "response",
|
|
464
|
+
}, (text) => builder.emit({ type: "diagnostic", text }));
|
|
465
|
+
}
|
|
466
|
+
// Sync checkpoint: claim inbox tells and deliver them via steer control.
|
|
467
|
+
options.onSyncCheckpoint?.({ enqueue }, checkpoint);
|
|
468
|
+
// Defer end so concurrent enqueue after this result can register openSteerCount.
|
|
469
|
+
queueMicrotask(() => maybeEndInputStream());
|
|
470
|
+
break;
|
|
471
|
+
}
|
|
472
|
+
bumpResultOrdinal();
|
|
473
|
+
if (resultText) {
|
|
474
|
+
builder.emit({ type: "diagnostic", text: resultText });
|
|
475
|
+
}
|
|
476
|
+
const message = resultText ?? `${CLAUDE_AGENT_SDK_PROVIDER} exec failed`;
|
|
477
|
+
builder.emit({ type: "fail", message });
|
|
478
|
+
inputStream.end();
|
|
479
|
+
throw new Error(message);
|
|
480
|
+
}
|
|
481
|
+
default:
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
if (queryAbortController.signal.aborted) {
|
|
486
|
+
throw createAbortError(`${CLAUDE_AGENT_SDK_PROVIDER} exec cancelled by client`);
|
|
487
|
+
}
|
|
488
|
+
if (!resumedSessionId) {
|
|
489
|
+
const message = `${CLAUDE_AGENT_SDK_PROVIDER} exec could not determine session id`;
|
|
490
|
+
builder.emit({ type: "fail", message });
|
|
491
|
+
throw new Error(message);
|
|
492
|
+
}
|
|
493
|
+
const session = {
|
|
494
|
+
provider: "claude-agent-sdk",
|
|
495
|
+
version: SUBAGENT_SESSION_VERSION,
|
|
496
|
+
handle: { sessionId: resumedSessionId },
|
|
497
|
+
};
|
|
498
|
+
if (options.onEvidence)
|
|
499
|
+
emitProviderEvidence(options.onEvidence, { type: "session", provider: CLAUDE_AGENT_SDK_PROVIDER, sessionId: resumedSessionId }, (text) => builder.emit({ type: "diagnostic", text }));
|
|
500
|
+
const resolvedFinal = finalMessage ?? "";
|
|
501
|
+
builder.emit({
|
|
502
|
+
type: "done",
|
|
503
|
+
finalMessage: resolvedFinal,
|
|
504
|
+
session,
|
|
505
|
+
});
|
|
506
|
+
// Clean end on success: do not reject the stream (that would poison completion).
|
|
507
|
+
// Any still-pending enqueue waiters are rejected below as "query ended".
|
|
508
|
+
inputStream?.end();
|
|
509
|
+
return { finalMessage: resolvedFinal, session };
|
|
510
|
+
}
|
|
511
|
+
finally {
|
|
512
|
+
unlinkBuilderAbort?.();
|
|
513
|
+
unlinkOuterAbort?.();
|
|
514
|
+
queryEnded = true;
|
|
515
|
+
queryActive = false;
|
|
516
|
+
liveQuery = undefined;
|
|
517
|
+
// Unblock any mid-flight enqueue/generation waiters without throwing from run().
|
|
518
|
+
const endError = new Error(`${CLAUDE_AGENT_SDK_PROVIDER} query ended`);
|
|
519
|
+
if (inputStream) {
|
|
520
|
+
// If abort/failure already ended with an error, this is a no-op (closed).
|
|
521
|
+
// If success path already clean-ended, also a no-op.
|
|
522
|
+
// If we exited via throw before clean end, reject unresolved post-yield acks.
|
|
523
|
+
inputStream.end(endError);
|
|
524
|
+
}
|
|
525
|
+
rejectPendingEnqueueWaiters(endError);
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
});
|
|
529
|
+
}
|