@dynamicagents/plugins 0.11.1 → 0.13.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 +59 -79
- package/dist/browser/index.d.ts +0 -2
- package/dist/browser/index.js +21 -27
- package/dist/claude-code/cards.d.ts +53 -0
- package/dist/claude-code/cards.js +272 -0
- package/dist/claude-code/config.d.ts +73 -31
- package/dist/claude-code/config.js +0 -2
- package/dist/claude-code/credentials.d.ts +21 -9
- package/dist/claude-code/credentials.js +43 -9
- package/dist/claude-code/egress.d.ts +15 -14
- package/dist/claude-code/egress.js +51 -14
- package/dist/claude-code/events.d.ts +153 -9
- package/dist/claude-code/events.js +289 -58
- package/dist/claude-code/github.d.ts +39 -0
- package/dist/claude-code/github.js +82 -0
- package/dist/claude-code/index.d.ts +24 -80
- package/dist/claude-code/index.js +19 -234
- package/dist/claude-code/model.d.ts +152 -0
- package/dist/claude-code/model.js +342 -0
- package/dist/claude-code/run.d.ts +283 -92
- package/dist/claude-code/run.js +620 -114
- package/dist/claude-code/session.d.ts +131 -0
- package/dist/claude-code/session.js +285 -0
- package/dist/claude-code/spec.d.ts +46 -0
- package/dist/claude-code/spec.js +83 -0
- package/dist/claude-code/verified.d.ts +15 -0
- package/dist/claude-code/verified.js +15 -0
- package/dist/computer/context.d.ts +19 -0
- package/dist/computer/context.js +222 -0
- package/dist/computer/file-lock.d.ts +5 -0
- package/dist/computer/file-lock.js +40 -0
- package/dist/computer/gate.d.ts +12 -12
- package/dist/computer/gate.js +12 -12
- package/dist/computer/index.d.ts +57 -255
- package/dist/computer/index.js +98 -1039
- package/dist/computer/paths.d.ts +47 -22
- package/dist/computer/paths.js +64 -49
- package/dist/computer/proxy.d.ts +45 -0
- package/dist/computer/proxy.js +234 -0
- package/dist/computer/render.d.ts +27 -38
- package/dist/computer/render.js +32 -65
- package/dist/computer/tools-exec.d.ts +3 -0
- package/dist/computer/tools-exec.js +268 -0
- package/dist/computer/tools-file.d.ts +12 -0
- package/dist/computer/tools-file.js +64 -0
- package/dist/computer/tools-grep.d.ts +3 -0
- package/dist/computer/tools-grep.js +129 -0
- package/dist/repo/checkout.d.ts +35 -6
- package/dist/repo/checkout.js +64 -6
- package/dist/repo/context.d.ts +135 -0
- package/dist/repo/context.js +582 -0
- package/dist/repo/index.d.ts +79 -57
- package/dist/repo/index.js +118 -1112
- package/dist/repo/tools-checks.d.ts +3 -0
- package/dist/repo/tools-checks.js +112 -0
- package/dist/repo/tools-clone.d.ts +4 -0
- package/dist/repo/tools-clone.js +192 -0
- package/dist/repo/tools-pr.d.ts +14 -0
- package/dist/repo/tools-pr.js +196 -0
- package/dist/repo/tools-review.d.ts +12 -0
- package/dist/repo/tools-review.js +393 -0
- package/dist/repo/tools-worktree.d.ts +4 -0
- package/dist/repo/tools-worktree.js +305 -0
- package/dist/repo/tools-worktrees.d.ts +12 -0
- package/dist/repo/tools-worktrees.js +48 -0
- package/dist/repo/url.d.ts +2 -2
- package/dist/repo/url.js +7 -7
- package/dist/scratch/index.d.ts +4 -4
- package/dist/scratch/index.js +12 -11
- package/dist/{computer → workspace}/advisory.d.ts +33 -20
- package/dist/{computer → workspace}/advisory.js +32 -7
- package/dist/workspace/ca-trust.d.ts +95 -0
- package/dist/workspace/ca-trust.js +312 -0
- package/dist/workspace/container-deps.d.ts +54 -0
- package/dist/workspace/container-deps.js +224 -0
- package/dist/workspace/container-fault.d.ts +38 -0
- package/dist/workspace/container-fault.js +73 -0
- package/dist/workspace/exec.d.ts +94 -0
- package/dist/workspace/exec.js +153 -0
- package/dist/workspace/format.d.ts +36 -0
- package/dist/workspace/format.js +57 -0
- package/dist/workspace/git-host.d.ts +79 -0
- package/dist/workspace/git-host.js +226 -0
- package/dist/workspace/git-identity.d.ts +57 -0
- package/dist/workspace/git-identity.js +147 -0
- package/dist/workspace/index.d.ts +43 -47
- package/dist/workspace/index.js +52 -112
- package/dist/workspace/install-job.d.ts +121 -0
- package/dist/workspace/install-job.js +866 -0
- package/dist/{computer → workspace}/install.d.ts +15 -19
- package/dist/{computer → workspace}/install.js +8 -11
- package/dist/workspace/object.d.ts +502 -0
- package/dist/workspace/object.js +1427 -0
- package/dist/workspace/open.d.ts +95 -0
- package/dist/workspace/open.js +63 -0
- package/dist/workspace/read.d.ts +16 -0
- package/dist/workspace/read.js +22 -0
- package/dist/workspace/shell.d.ts +45 -0
- package/dist/workspace/shell.js +80 -0
- package/dist/workspace/sync.d.ts +111 -0
- package/dist/workspace/sync.js +124 -0
- package/dist/workspace/wake.d.ts +19 -0
- package/dist/workspace/wake.js +10 -0
- package/package.json +28 -42
- package/dist/arc-agi/analysis.d.ts +0 -336
- package/dist/arc-agi/analysis.js +0 -829
- package/dist/arc-agi/client.d.ts +0 -82
- package/dist/arc-agi/client.js +0 -113
- package/dist/arc-agi/game-tools.d.ts +0 -25
- package/dist/arc-agi/game-tools.js +0 -26
- package/dist/arc-agi/index.d.ts +0 -42
- package/dist/arc-agi/index.js +0 -175
- package/dist/arc-agi/main-agent.d.ts +0 -27
- package/dist/arc-agi/main-agent.js +0 -65
- package/dist/arc-agi/recipe.d.ts +0 -51
- package/dist/arc-agi/recipe.js +0 -100
- package/dist/arc-agi/scorecard.d.ts +0 -118
- package/dist/arc-agi/scorecard.js +0 -163
- package/dist/arc-agi/soul.d.ts +0 -40
- package/dist/arc-agi/soul.js +0 -90
- package/dist/arc-agi/store.d.ts +0 -154
- package/dist/arc-agi/store.js +0 -216
- package/dist/arc-agi/tools.d.ts +0 -36
- package/dist/arc-agi/tools.js +0 -576
- package/dist/arc-agi/types.d.ts +0 -212
- package/dist/arc-agi/types.js +0 -19
- package/dist/claude-code/recipe.d.ts +0 -53
- package/dist/claude-code/recipe.js +0 -100
- package/dist/computer/read.d.ts +0 -137
- package/dist/computer/read.js +0 -203
- package/dist/recall/index.d.ts +0 -99
- package/dist/recall/index.js +0 -171
- package/dist/triage/index.d.ts +0 -355
- package/dist/triage/index.js +0 -302
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
import { isPlatformTransientError } from "agents";
|
|
54
|
+
import { claudeCodeSession, requireCredentials } from "./session.js";
|
|
55
|
+
import { execIdFor, followUpExecIdFor } from "./run.js";
|
|
56
|
+
/** Zeroed: the session's tokens are the CLI's context, not this conversation's. */
|
|
57
|
+
const USAGE = {
|
|
58
|
+
inputTokens: {
|
|
59
|
+
total: 0,
|
|
60
|
+
noCache: 0,
|
|
61
|
+
cacheRead: 0,
|
|
62
|
+
cacheWrite: 0
|
|
63
|
+
},
|
|
64
|
+
outputTokens: { total: 0, text: 0, reasoning: 0 }
|
|
65
|
+
};
|
|
66
|
+
export function claudeCodeModel(options) {
|
|
67
|
+
requireCredentials(options.config);
|
|
68
|
+
const { storage, runId } = options;
|
|
69
|
+
const session = claudeCodeSession(options.config);
|
|
70
|
+
const key = (name) => `claude-code:${runId}:${name}`;
|
|
71
|
+
const KEYS = {
|
|
72
|
+
brief: key("brief"),
|
|
73
|
+
cursor: key("cursor"),
|
|
74
|
+
session: key("session"),
|
|
75
|
+
followUp: key("follow-up"),
|
|
76
|
+
followUpEnd: key("follow-up-end"),
|
|
77
|
+
report: key("report")
|
|
78
|
+
};
|
|
79
|
+
const run = async (call) => {
|
|
80
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
81
|
+
try {
|
|
82
|
+
// The once-guarantee: a run that already reported says so again.
|
|
83
|
+
const reported = await storage.get(KEYS.report);
|
|
84
|
+
if (reported !== undefined)
|
|
85
|
+
return reported;
|
|
86
|
+
const signal = call.abortSignal;
|
|
87
|
+
// Opened again after a cut stream — see `drain`. Whichever is current is
|
|
88
|
+
// released when the run ends.
|
|
89
|
+
let workspace = await options.workspace();
|
|
90
|
+
const _release = __addDisposableResource(env_1, { [Symbol.dispose]: () => workspace[Symbol.dispose]() }, false);
|
|
91
|
+
/** The exec being drained, which leads each note's key. */
|
|
92
|
+
let current = "";
|
|
93
|
+
/**
|
|
94
|
+
* The handle already recorded, so one id is not announced twice over — and
|
|
95
|
+
* set only once the host has taken it, so a record that failed is tried
|
|
96
|
+
* again at the next checkpoint rather than given up on.
|
|
97
|
+
*/
|
|
98
|
+
let announced = "";
|
|
99
|
+
/**
|
|
100
|
+
* Tell the host the session's handle, and never fail the run over it.
|
|
101
|
+
*
|
|
102
|
+
* Not on the note chain: a note is one line of a transcript, and this is the
|
|
103
|
+
* only way back to the conversation. It is awaited where it is called so a
|
|
104
|
+
* handle is recorded before whatever comes next can lose it.
|
|
105
|
+
*/
|
|
106
|
+
const announce = async (record) => {
|
|
107
|
+
const sink = options.onSession;
|
|
108
|
+
if (!sink)
|
|
109
|
+
return;
|
|
110
|
+
try {
|
|
111
|
+
await sink(record);
|
|
112
|
+
announced = record.sessionId;
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
console.warn("[claude-code] the session's handle was not recorded", {
|
|
116
|
+
runId,
|
|
117
|
+
sessionId: record.sessionId,
|
|
118
|
+
err: String(err)
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Each note, filed as it is parsed, then the cursor behind it. Keyed on the
|
|
124
|
+
* exec as well as the position: the parent's transcript is one per task and
|
|
125
|
+
* dedupes on the key, so a run's notes must not collide with another run's
|
|
126
|
+
* — or with its own follow-up's, which counts from zero again.
|
|
127
|
+
*/
|
|
128
|
+
const sinks = {
|
|
129
|
+
onProgress: (note) => options.note(`${current}:${note.key}`, note.text, note.detail),
|
|
130
|
+
// The drain offers a cursor the moment the session id appears, which is
|
|
131
|
+
// what makes this the earliest the handle can be recorded — seconds in,
|
|
132
|
+
// rather than at the end a killed session never reaches.
|
|
133
|
+
onCheckpoint: async (cursor) => {
|
|
134
|
+
await storage.put(KEYS.cursor, cursor);
|
|
135
|
+
if (cursor.sessionId && cursor.sessionId !== announced) {
|
|
136
|
+
await announce({ sessionId: cursor.sessionId });
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
...(signal ? { signal } : {})
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Drain one exec to its end, starting it or re-attaching from the cursor.
|
|
143
|
+
*
|
|
144
|
+
* **A cut stream is re-attached here, not failed.** The session is still
|
|
145
|
+
* running, and a failed run is stopped — the one outcome that loses it. The
|
|
146
|
+
* retry opens the workspace afresh, since the stub the stream came over may
|
|
147
|
+
* not survive the cut. Two attempts in a row that end in a cut with no
|
|
148
|
+
* checkpoint stored fail the run as before: a stream that breaks every time
|
|
149
|
+
* it opens is not a blip.
|
|
150
|
+
*/
|
|
151
|
+
const drain = async (execId, begin) => {
|
|
152
|
+
current = execId;
|
|
153
|
+
let outcome;
|
|
154
|
+
let stalled = false;
|
|
155
|
+
while (!outcome) {
|
|
156
|
+
const cursor = await storage.get(KEYS.cursor);
|
|
157
|
+
try {
|
|
158
|
+
outcome =
|
|
159
|
+
cursor?.execId === execId
|
|
160
|
+
? await session.resume(workspace.runtime, cursor, sinks)
|
|
161
|
+
: await begin(workspace.runtime);
|
|
162
|
+
}
|
|
163
|
+
catch (err) {
|
|
164
|
+
const after = await storage.get(KEYS.cursor);
|
|
165
|
+
const moved = after?.execId === execId &&
|
|
166
|
+
(cursor?.execId !== execId || after.seq > cursor.seq);
|
|
167
|
+
if (!isStreamCut(err) || signal?.aborted || (stalled && !moved))
|
|
168
|
+
throw err;
|
|
169
|
+
stalled = !moved;
|
|
170
|
+
console.warn("[claude-code] the session's stream was cut — re-attaching", {
|
|
171
|
+
execId,
|
|
172
|
+
err: String(err)
|
|
173
|
+
});
|
|
174
|
+
const fresh = await options.workspace();
|
|
175
|
+
workspace[Symbol.dispose]();
|
|
176
|
+
workspace = fresh;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
await storage.put(KEYS.cursor, outcome.cursor);
|
|
180
|
+
if (!outcome.done) {
|
|
181
|
+
// Only the signal stops a drain short of the end: the turn was
|
|
182
|
+
// cancelled, so the session goes with it.
|
|
183
|
+
await session.stop(workspace.runtime, runId).catch((err) => console.warn("[claude-code] could not stop a cancelled session", {
|
|
184
|
+
runId,
|
|
185
|
+
err: String(err)
|
|
186
|
+
}));
|
|
187
|
+
throw signal?.reason ?? new Error("the session was stopped");
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
exitCode: outcome.exitCode,
|
|
191
|
+
...(outcome.result ? { result: outcome.result } : {}),
|
|
192
|
+
...(outcome.stderr ? { stderr: outcome.stderr } : {}),
|
|
193
|
+
...(outcome.rateLimit ? { rateLimit: outcome.rateLimit } : {}),
|
|
194
|
+
...(outcome.cursor.sessionId
|
|
195
|
+
? { sessionId: outcome.cursor.sessionId }
|
|
196
|
+
: {})
|
|
197
|
+
};
|
|
198
|
+
};
|
|
199
|
+
let ended = await storage.get(KEYS.session);
|
|
200
|
+
if (!ended) {
|
|
201
|
+
let prompt = await storage.get(KEYS.brief);
|
|
202
|
+
if (prompt === undefined) {
|
|
203
|
+
const task = lastUserText(call.prompt);
|
|
204
|
+
prompt = options.brief ? await options.brief(task) : task;
|
|
205
|
+
await storage.put(KEYS.brief, prompt);
|
|
206
|
+
}
|
|
207
|
+
const brief = prompt;
|
|
208
|
+
ended = await drain(execIdFor(runId), (runtime) => session.start(runtime, runId, {
|
|
209
|
+
prompt: brief,
|
|
210
|
+
dir: options.dir,
|
|
211
|
+
...(options.jsonSchema ? { jsonSchema: options.jsonSchema } : {}),
|
|
212
|
+
...(options.permissionMode
|
|
213
|
+
? { permissionMode: options.permissionMode }
|
|
214
|
+
: {}),
|
|
215
|
+
...(options.resume ? { resume: options.resume } : {})
|
|
216
|
+
}, sinks));
|
|
217
|
+
await storage.put(KEYS.session, ended);
|
|
218
|
+
}
|
|
219
|
+
// After the end is stored, so a turn that dies here does not report an end
|
|
220
|
+
// the run has not kept — and outside the guard above, so a turn cut between
|
|
221
|
+
// storing the end and telling the host tells it on the next attempt. Telling
|
|
222
|
+
// it twice is harmless: its record is keyed on the id, see `SessionRecord`.
|
|
223
|
+
if (ended.sessionId)
|
|
224
|
+
await announce({ sessionId: ended.sessionId, end: ended });
|
|
225
|
+
let followUpEnd = await storage.get(KEYS.followUpEnd);
|
|
226
|
+
if (!followUpEnd && options.followUp) {
|
|
227
|
+
let asked = await storage.get(KEYS.followUp);
|
|
228
|
+
if (!asked) {
|
|
229
|
+
const prompt = await options.followUp(ended);
|
|
230
|
+
asked = prompt ? { prompt } : {};
|
|
231
|
+
await storage.put(KEYS.followUp, asked);
|
|
232
|
+
}
|
|
233
|
+
// The cursor's id as well as the result's: a session that was stopped
|
|
234
|
+
// before it reported still has a conversation worth one more turn.
|
|
235
|
+
const sessionId = ended.result?.sessionId ?? ended.sessionId;
|
|
236
|
+
if (asked.prompt && sessionId) {
|
|
237
|
+
const prompt = asked.prompt;
|
|
238
|
+
followUpEnd = await drain(followUpExecIdFor(runId), (runtime) => session.followUp(runtime, runId, {
|
|
239
|
+
sessionId,
|
|
240
|
+
prompt,
|
|
241
|
+
dir: options.dir,
|
|
242
|
+
...(options.permissionMode
|
|
243
|
+
? { permissionMode: options.permissionMode }
|
|
244
|
+
: {})
|
|
245
|
+
}, sinks));
|
|
246
|
+
await storage.put(KEYS.followUpEnd, followUpEnd);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const text = await options.report({
|
|
250
|
+
session: ended,
|
|
251
|
+
...(followUpEnd ? { followUp: followUpEnd } : {})
|
|
252
|
+
});
|
|
253
|
+
await storage.put(KEYS.report, text);
|
|
254
|
+
return text;
|
|
255
|
+
}
|
|
256
|
+
catch (e_1) {
|
|
257
|
+
env_1.error = e_1;
|
|
258
|
+
env_1.hasError = true;
|
|
259
|
+
}
|
|
260
|
+
finally {
|
|
261
|
+
__disposeResources(env_1);
|
|
262
|
+
}
|
|
263
|
+
};
|
|
264
|
+
return {
|
|
265
|
+
specificationVersion: "v3",
|
|
266
|
+
provider: "claude-code",
|
|
267
|
+
modelId: options.config.model ?? "claude-code",
|
|
268
|
+
supportedUrls: {},
|
|
269
|
+
async doStream(call) {
|
|
270
|
+
// The stream is returned at once and the session runs inside it, so
|
|
271
|
+
// nothing waits on a promise for the length of a session.
|
|
272
|
+
const stream = new ReadableStream({
|
|
273
|
+
async start(controller) {
|
|
274
|
+
controller.enqueue({ type: "stream-start", warnings: [] });
|
|
275
|
+
try {
|
|
276
|
+
const text = await run(call);
|
|
277
|
+
controller.enqueue({ type: "text-start", id: "report" });
|
|
278
|
+
controller.enqueue({
|
|
279
|
+
type: "text-delta",
|
|
280
|
+
id: "report",
|
|
281
|
+
delta: text
|
|
282
|
+
});
|
|
283
|
+
controller.enqueue({ type: "text-end", id: "report" });
|
|
284
|
+
controller.enqueue({
|
|
285
|
+
type: "finish",
|
|
286
|
+
usage: USAGE,
|
|
287
|
+
finishReason: { unified: "stop", raw: undefined }
|
|
288
|
+
});
|
|
289
|
+
controller.close();
|
|
290
|
+
}
|
|
291
|
+
catch (err) {
|
|
292
|
+
controller.error(err);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
return { stream };
|
|
297
|
+
},
|
|
298
|
+
async doGenerate(call) {
|
|
299
|
+
const text = await run(call);
|
|
300
|
+
return {
|
|
301
|
+
content: [{ type: "text", text }],
|
|
302
|
+
finishReason: { unified: "stop", raw: undefined },
|
|
303
|
+
usage: USAGE,
|
|
304
|
+
warnings: []
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Whether a drain failed because the transport under a live session gave way.
|
|
311
|
+
*
|
|
312
|
+
* An exec's events reach the sub-agent as a stream over RPC, relayed by the
|
|
313
|
+
* workspace object from the container's own RPC session — so a drop anywhere on
|
|
314
|
+
* that path cuts the stream while the session runs on. Measured: a container's
|
|
315
|
+
* session dropped after 21 minutes and was attached again within a second, the
|
|
316
|
+
* process untouched. The message is workerd's for a stream whose far side went
|
|
317
|
+
* away, and it carries no code.
|
|
318
|
+
*/
|
|
319
|
+
function isStreamCut(err) {
|
|
320
|
+
if (isPlatformTransientError(err))
|
|
321
|
+
return true;
|
|
322
|
+
for (let e = err; e instanceof Error; e = e.cause) {
|
|
323
|
+
if (e.message.includes("ReadableStream received over RPC disconnected"))
|
|
324
|
+
return true;
|
|
325
|
+
}
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* The task: the last user message's text. On a continued turn the prompt ends
|
|
330
|
+
* with the interrupted assistant message, so the last message is not it.
|
|
331
|
+
*/
|
|
332
|
+
function lastUserText(prompt) {
|
|
333
|
+
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
334
|
+
const message = prompt[i];
|
|
335
|
+
if (message.role !== "user")
|
|
336
|
+
continue;
|
|
337
|
+
return message.content
|
|
338
|
+
.map((part) => (part.type === "text" ? part.text : ""))
|
|
339
|
+
.join("");
|
|
340
|
+
}
|
|
341
|
+
throw new Error("claude-code: the run has no task — no user message");
|
|
342
|
+
}
|