@dynamicagents/plugins 0.12.0 → 0.14.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 (132) hide show
  1. package/README.md +58 -77
  2. package/dist/browser/index.d.ts +0 -2
  3. package/dist/browser/index.js +21 -27
  4. package/dist/claude-code/cards.d.ts +55 -0
  5. package/dist/claude-code/cards.js +283 -0
  6. package/dist/claude-code/config.d.ts +28 -63
  7. package/dist/claude-code/config.js +0 -11
  8. package/dist/claude-code/egress.d.ts +8 -1
  9. package/dist/claude-code/egress.js +42 -4
  10. package/dist/claude-code/events.d.ts +106 -9
  11. package/dist/claude-code/events.js +201 -56
  12. package/dist/claude-code/github.d.ts +39 -0
  13. package/dist/claude-code/github.js +82 -0
  14. package/dist/claude-code/index.d.ts +16 -90
  15. package/dist/claude-code/index.js +14 -276
  16. package/dist/claude-code/model.d.ts +152 -0
  17. package/dist/claude-code/model.js +366 -0
  18. package/dist/claude-code/run.d.ts +230 -116
  19. package/dist/claude-code/run.js +380 -167
  20. package/dist/claude-code/session.d.ts +131 -0
  21. package/dist/claude-code/session.js +288 -0
  22. package/dist/claude-code/spec.d.ts +46 -0
  23. package/dist/claude-code/spec.js +83 -0
  24. package/dist/claude-code/verified.d.ts +15 -0
  25. package/dist/claude-code/verified.js +15 -0
  26. package/dist/computer/context.d.ts +1 -5
  27. package/dist/computer/context.js +7 -10
  28. package/dist/computer/file-lock.js +5 -4
  29. package/dist/computer/gate.d.ts +4 -4
  30. package/dist/computer/gate.js +5 -5
  31. package/dist/computer/index.d.ts +40 -258
  32. package/dist/computer/index.js +71 -324
  33. package/dist/computer/paths.d.ts +36 -16
  34. package/dist/computer/paths.js +42 -29
  35. package/dist/computer/proxy.d.ts +45 -0
  36. package/dist/computer/proxy.js +234 -0
  37. package/dist/computer/render.d.ts +1 -40
  38. package/dist/computer/render.js +2 -68
  39. package/dist/computer/tools-exec.js +12 -13
  40. package/dist/computer/tools-file.d.ts +9 -1
  41. package/dist/computer/tools-file.js +22 -82
  42. package/dist/computer/{tools-find.d.ts → tools-grep.d.ts} +1 -1
  43. package/dist/computer/tools-grep.js +129 -0
  44. package/dist/repo/checkout.d.ts +4 -5
  45. package/dist/repo/checkout.js +31 -8
  46. package/dist/repo/context.d.ts +35 -9
  47. package/dist/repo/context.js +101 -49
  48. package/dist/repo/index.d.ts +73 -28
  49. package/dist/repo/index.js +112 -96
  50. package/dist/repo/tools-checks.d.ts +3 -0
  51. package/dist/repo/tools-checks.js +112 -0
  52. package/dist/repo/tools-clone.js +1 -1
  53. package/dist/repo/tools-pr.d.ts +14 -0
  54. package/dist/repo/{tools-forge.js → tools-pr.js} +53 -36
  55. package/dist/repo/tools-review.d.ts +9 -0
  56. package/dist/repo/tools-review.js +67 -47
  57. package/dist/repo/tools-worktree.js +144 -60
  58. package/dist/repo/tools-worktrees.d.ts +12 -0
  59. package/dist/repo/tools-worktrees.js +48 -0
  60. package/dist/repo/url.d.ts +2 -2
  61. package/dist/repo/url.js +7 -7
  62. package/dist/scratch/index.d.ts +4 -4
  63. package/dist/scratch/index.js +12 -11
  64. package/dist/{computer → workspace}/advisory.d.ts +4 -4
  65. package/dist/{computer → workspace}/advisory.js +2 -2
  66. package/dist/{computer/host → workspace}/ca-trust.d.ts +8 -1
  67. package/dist/{computer/host → workspace}/ca-trust.js +12 -3
  68. package/dist/{computer/host → workspace}/container-deps.d.ts +4 -0
  69. package/dist/{computer/host → workspace}/container-deps.js +26 -1
  70. package/dist/{computer/host → workspace}/container-fault.d.ts +3 -3
  71. package/dist/{computer/host → workspace}/container-fault.js +23 -8
  72. package/dist/workspace/deps-snapshot.d.ts +110 -0
  73. package/dist/workspace/deps-snapshot.js +424 -0
  74. package/dist/workspace/exec.d.ts +94 -0
  75. package/dist/workspace/exec.js +153 -0
  76. package/dist/workspace/format.d.ts +36 -0
  77. package/dist/workspace/format.js +57 -0
  78. package/dist/{computer/host → workspace}/git-host.d.ts +21 -4
  79. package/dist/{computer/host → workspace}/git-host.js +72 -2
  80. package/dist/{computer/host → workspace}/git-identity.d.ts +1 -1
  81. package/dist/{computer/host → workspace}/git-identity.js +1 -1
  82. package/dist/workspace/index.d.ts +44 -47
  83. package/dist/workspace/index.js +52 -112
  84. package/dist/{computer/host → workspace}/install-job.d.ts +53 -4
  85. package/dist/{computer/host → workspace}/install-job.js +346 -178
  86. package/dist/{computer → workspace}/install.d.ts +17 -8
  87. package/dist/{computer → workspace}/install.js +24 -6
  88. package/dist/{computer/host/workspace.d.ts → workspace/object.d.ts} +125 -27
  89. package/dist/{computer/host/workspace.js → workspace/object.js} +341 -58
  90. package/dist/workspace/open.d.ts +95 -0
  91. package/dist/workspace/open.js +63 -0
  92. package/dist/workspace/read.d.ts +16 -0
  93. package/dist/workspace/read.js +22 -0
  94. package/dist/{computer → workspace}/shell.d.ts +5 -5
  95. package/dist/{computer → workspace}/shell.js +6 -6
  96. package/dist/{computer/host → workspace}/wake.d.ts +3 -1
  97. package/package.json +17 -37
  98. package/dist/arc-agi/analysis.d.ts +0 -322
  99. package/dist/arc-agi/analysis.js +0 -802
  100. package/dist/arc-agi/client.d.ts +0 -82
  101. package/dist/arc-agi/client.js +0 -113
  102. package/dist/arc-agi/game-tools.d.ts +0 -24
  103. package/dist/arc-agi/game-tools.js +0 -26
  104. package/dist/arc-agi/index.d.ts +0 -40
  105. package/dist/arc-agi/index.js +0 -175
  106. package/dist/arc-agi/main-agent.d.ts +0 -24
  107. package/dist/arc-agi/main-agent.js +0 -62
  108. package/dist/arc-agi/recipe.d.ts +0 -48
  109. package/dist/arc-agi/recipe.js +0 -97
  110. package/dist/arc-agi/scorecard.d.ts +0 -118
  111. package/dist/arc-agi/scorecard.js +0 -163
  112. package/dist/arc-agi/soul.d.ts +0 -29
  113. package/dist/arc-agi/soul.js +0 -79
  114. package/dist/arc-agi/store.d.ts +0 -153
  115. package/dist/arc-agi/store.js +0 -215
  116. package/dist/arc-agi/tools.d.ts +0 -36
  117. package/dist/arc-agi/tools.js +0 -574
  118. package/dist/arc-agi/types.d.ts +0 -211
  119. package/dist/arc-agi/types.js +0 -19
  120. package/dist/claude-code/recipe.d.ts +0 -53
  121. package/dist/claude-code/recipe.js +0 -100
  122. package/dist/computer/read.d.ts +0 -133
  123. package/dist/computer/read.js +0 -200
  124. package/dist/computer/tools-find.js +0 -216
  125. package/dist/recall/index.d.ts +0 -105
  126. package/dist/recall/index.js +0 -181
  127. package/dist/repo/tools-forge.d.ts +0 -4
  128. package/dist/triage/index.d.ts +0 -360
  129. package/dist/triage/index.js +0 -311
  130. /package/dist/{computer/host → workspace}/sync.d.ts +0 -0
  131. /package/dist/{computer/host → workspace}/sync.js +0 -0
  132. /package/dist/{computer/host → workspace}/wake.js +0 -0
@@ -0,0 +1,131 @@
1
+ import type { CredentialStore, Lead } from "./credentials.js";
2
+ import { type DrainCursor, type DrainOutcome, type DrainOptions, type SessionRuntime } from "./run.js";
3
+ import { type ClaudeCodeConfig, type PermissionMode } from "./config.js";
4
+ /**
5
+ * What continuing an interrupted conversation takes — see
6
+ * {@link file://./run.ts LaunchOptions.resume}.
7
+ */
8
+ export interface ResumeSession {
9
+ sessionId: string;
10
+ /** Continue under a new id, leaving the original conversation whole. */
11
+ fork?: boolean;
12
+ }
13
+ /** What one session needs to be launched: see {@link ClaudeCodeSession.start}. */
14
+ export interface StartSession {
15
+ /** The whole of what this session is asked to do, or its next user turn. */
16
+ prompt: string;
17
+ /** The checkout. */
18
+ dir: string;
19
+ jsonSchema?: Record<string, unknown>;
20
+ /**
21
+ * This session's mode, over the config's — see
22
+ * {@link file://./model.ts ClaudeCodeModelOptions.permissionMode}.
23
+ */
24
+ permissionMode?: PermissionMode;
25
+ /** A Claude Code conversation to continue rather than start. */
26
+ resume?: ResumeSession;
27
+ }
28
+ /**
29
+ * Bind a config to a workspace runtime: start, follow up, resume, stop.
30
+ *
31
+ * What `claudeCodeModel` drives a run with, and what a host's `settle` stops
32
+ * one with. State lives in the {@link DrainCursor} the caller stores, so this
33
+ * holds none and a fresh isolate picks up exactly where the last one stopped.
34
+ *
35
+ * ## Two senses of "resume", and they are not the same mechanism
36
+ *
37
+ * {@link ClaudeCodeSession.resume} re-attaches to a running **exec** — the
38
+ * process is alive in the container and a drain that was cut reads on from the
39
+ * cursor's sequence. Nothing about the conversation changes; this is plumbing.
40
+ *
41
+ * {@link StartSession.resume} continues a Claude Code **conversation** that has
42
+ * already ended, in a new process with a new exec: the session's transcript is
43
+ * read back into context and the prompt is the next user turn. Where that
44
+ * transcript lives — and so how long this is possible at all — is
45
+ * {@link file://./run.ts SESSION_CONFIG_DIR}.
46
+ */
47
+ export declare function claudeCodeSession(config: ClaudeCodeConfig): {
48
+ /**
49
+ * Spawn a session for one run and drain it.
50
+ *
51
+ * `runId` namespaces the exec id. Runs are concurrent, so two sessions in
52
+ * one workspace under one id would spawn over each other — see
53
+ * {@link execIdFor}.
54
+ *
55
+ * `resume` continues a conversation on this session's **own** exec rather
56
+ * than on a second one: a run given a conversation to continue has no first
57
+ * session of its own, so there is nothing for a follow-up exec to follow.
58
+ */
59
+ start(runtime: SessionRuntime, runId: string, options: StartSession, sinks?: DrainOptions): Promise<DrainOutcome>;
60
+ /**
61
+ * Give a finished writing session one more turn, and drain it.
62
+ *
63
+ * `sessionId` is the one the session reported — on its `result` line, or on
64
+ * its cursor when it ended without one. This runs in the workspace the
65
+ * session did, because that workspace's {@link file://./run.ts SESSION_CONFIG_DIR}
66
+ * is where the transcript it is continuing can be found. A drain cut short goes
67
+ * on through {@link resume} with the cursor it stored, like any other
68
+ * session's.
69
+ *
70
+ * Never forked: a follow-up is one more turn of the same conversation, which
71
+ * is exactly what a fork is for *not* doing. For the same reason it runs
72
+ * under the session's own `permissionMode`.
73
+ */
74
+ followUp(runtime: SessionRuntime, runId: string, options: {
75
+ sessionId: string;
76
+ prompt: string;
77
+ dir: string;
78
+ permissionMode?: PermissionMode;
79
+ }, sinks?: DrainOptions): Promise<DrainOutcome>;
80
+ /**
81
+ * Re-attach to a running session and drain it to its end.
82
+ *
83
+ * **A session whose container was replaced is reported, not thrown.** The
84
+ * attachment is the first thing to notice a replacement, and the caller is a
85
+ * recovered turn with a cursor it will happily keep presenting: a throw here
86
+ * retries the same dead id until the recoveries run out, and the run ends on
87
+ * a stack trace that names neither the session nor the cause. A
88
+ * terminal outcome ends it at the first attempt instead, with the exit code
89
+ * the runtime uses for a killed process and an explanation on `stderr`,
90
+ * which is exactly the channel a session that died without a result line
91
+ * already reports through. The wording is {@link containerLost}'s.
92
+ */
93
+ resume(runtime: SessionRuntime, cursor: DrainCursor, sinks?: DrainOptions): Promise<DrainOutcome>;
94
+ /** Stop a session — `SIGTERM`, so its own process tree goes with it. */
95
+ stop(runtime: SessionRuntime, runId: string): Promise<void>;
96
+ /**
97
+ * Is any credential usable right now, and if not, when?
98
+ *
99
+ * The same question {@link egress} answers per request, asked ahead of time
100
+ * so a host can decline to start a session it cannot pay for. That is not a
101
+ * micro-optimisation: an invocation carries an 18.7-27k-token cached prefix
102
+ * before it does anything, so starting one and letting the gateway refuse
103
+ * its first model call costs a container start and that prefix to learn what
104
+ * this returns for free — and reports it as a failed run rather than as a
105
+ * rate limit with a time on it.
106
+ *
107
+ * Reads only. Nothing here marks anything spent.
108
+ */
109
+ credentials: (store: CredentialStore) => Promise<Lead>;
110
+ /**
111
+ * The `Fetcher` the workspace object installs as its egress policy.
112
+ *
113
+ * `store` is an **argument rather than a config field**, and that is the
114
+ * point: the credential pool's state belongs to whichever object has
115
+ * storage, and this same config object is also held by the agents that run
116
+ * sessions, which have none of their own for it. Making it a field would
117
+ * force them to invent one.
118
+ */
119
+ egress: (store: CredentialStore) => {
120
+ fetch(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
121
+ connect(address: SocketAddress | string, options?: SocketOptions): Socket;
122
+ };
123
+ };
124
+ export type ClaudeCodeSession = ReturnType<typeof claudeCodeSession>;
125
+ /**
126
+ * Refuse a deployment with no credential when the model is built, naming this
127
+ * plugin, rather than at the first model call inside a run somebody is waiting
128
+ * on. The thunk is still what the gateway calls per request, so a rotation is
129
+ * picked up.
130
+ */
131
+ export declare function requireCredentials(config: ClaudeCodeConfig): void;
@@ -0,0 +1,288 @@
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 { claudeCodeEgress } from "./egress.js";
54
+ import { credentialPool } from "./credentials.js";
55
+ import { attachRun, containerLost, drainRun, execIdFor, followUpExecIdFor, freshCursor, isExecLost, killRun, startRun } from "./run.js";
56
+ import { DEFAULT_TIMEOUT_MS } from "./config.js";
57
+ /**
58
+ * Bind a config to a workspace runtime: start, follow up, resume, stop.
59
+ *
60
+ * What `claudeCodeModel` drives a run with, and what a host's `settle` stops
61
+ * one with. State lives in the {@link DrainCursor} the caller stores, so this
62
+ * holds none and a fresh isolate picks up exactly where the last one stopped.
63
+ *
64
+ * ## Two senses of "resume", and they are not the same mechanism
65
+ *
66
+ * {@link ClaudeCodeSession.resume} re-attaches to a running **exec** — the
67
+ * process is alive in the container and a drain that was cut reads on from the
68
+ * cursor's sequence. Nothing about the conversation changes; this is plumbing.
69
+ *
70
+ * {@link StartSession.resume} continues a Claude Code **conversation** that has
71
+ * already ended, in a new process with a new exec: the session's transcript is
72
+ * read back into context and the prompt is the next user turn. Where that
73
+ * transcript lives — and so how long this is possible at all — is
74
+ * {@link file://./run.ts SESSION_CONFIG_DIR}.
75
+ */
76
+ export function claudeCodeSession(config) {
77
+ const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
78
+ const launch = (prompt, dir, permissionMode = config.permissionMode) => ({
79
+ prompt,
80
+ dir,
81
+ ...(config.model ? { model: config.model } : {}),
82
+ ...(config.effort ? { effort: config.effort } : {}),
83
+ ...(config.maxSubagentDepth === undefined
84
+ ? {}
85
+ : { maxSubagentDepth: config.maxSubagentDepth }),
86
+ ...(config.maxConcurrentSubagents === undefined
87
+ ? {}
88
+ : { maxConcurrentSubagents: config.maxConcurrentSubagents }),
89
+ // Forwarded only when set, so `buildLaunch` owns the default in one place
90
+ // rather than this line resolving it and the flag being written twice.
91
+ ...(permissionMode ? { permissionMode } : {}),
92
+ ...(config.env ? { env: config.env } : {}),
93
+ ...(config.author ? { author: config.author } : {}),
94
+ ...(config.githubToken ? { github: true } : {})
95
+ });
96
+ return {
97
+ /**
98
+ * Spawn a session for one run and drain it.
99
+ *
100
+ * `runId` namespaces the exec id. Runs are concurrent, so two sessions in
101
+ * one workspace under one id would spawn over each other — see
102
+ * {@link execIdFor}.
103
+ *
104
+ * `resume` continues a conversation on this session's **own** exec rather
105
+ * than on a second one: a run given a conversation to continue has no first
106
+ * session of its own, so there is nothing for a follow-up exec to follow.
107
+ */
108
+ async start(runtime, runId, options, sinks = {}) {
109
+ const env_1 = { stack: [], error: void 0, hasError: false };
110
+ try {
111
+ const execId = execIdFor(runId);
112
+ // `using`, so the attachment is released even when the drain throws.
113
+ const handle = __addDisposableResource(env_1, await startRun(runtime, {
114
+ ...launch(options.prompt, options.dir, options.permissionMode),
115
+ ...(options.jsonSchema ? { jsonSchema: options.jsonSchema } : {}),
116
+ ...resumption(options.resume),
117
+ execId,
118
+ timeoutMs,
119
+ signal: sinks.signal
120
+ }), false);
121
+ return await drainRun(handle, freshCursor(execId), sinks);
122
+ }
123
+ catch (e_1) {
124
+ env_1.error = e_1;
125
+ env_1.hasError = true;
126
+ }
127
+ finally {
128
+ __disposeResources(env_1);
129
+ }
130
+ },
131
+ /**
132
+ * Give a finished writing session one more turn, and drain it.
133
+ *
134
+ * `sessionId` is the one the session reported — on its `result` line, or on
135
+ * its cursor when it ended without one. This runs in the workspace the
136
+ * session did, because that workspace's {@link file://./run.ts SESSION_CONFIG_DIR}
137
+ * is where the transcript it is continuing can be found. A drain cut short goes
138
+ * on through {@link resume} with the cursor it stored, like any other
139
+ * session's.
140
+ *
141
+ * Never forked: a follow-up is one more turn of the same conversation, which
142
+ * is exactly what a fork is for *not* doing. For the same reason it runs
143
+ * under the session's own `permissionMode`.
144
+ */
145
+ async followUp(runtime, runId, options, sinks = {}) {
146
+ const env_2 = { stack: [], error: void 0, hasError: false };
147
+ try {
148
+ // Without one, `--resume` would be dropped and a new session started
149
+ // under the follow-up's id — unrelated work, reported as the follow-up.
150
+ if (!options.sessionId) {
151
+ throw new Error("claude-code: a follow-up needs the session id its result reported");
152
+ }
153
+ const execId = followUpExecIdFor(runId);
154
+ const handle = __addDisposableResource(env_2, await startRun(runtime, {
155
+ ...launch(options.prompt, options.dir, options.permissionMode),
156
+ resume: options.sessionId,
157
+ execId,
158
+ timeoutMs,
159
+ signal: sinks.signal
160
+ }), false);
161
+ return await drainRun(handle, freshCursor(execId), sinks);
162
+ }
163
+ catch (e_2) {
164
+ env_2.error = e_2;
165
+ env_2.hasError = true;
166
+ }
167
+ finally {
168
+ __disposeResources(env_2);
169
+ }
170
+ },
171
+ /**
172
+ * Re-attach to a running session and drain it to its end.
173
+ *
174
+ * **A session whose container was replaced is reported, not thrown.** The
175
+ * attachment is the first thing to notice a replacement, and the caller is a
176
+ * recovered turn with a cursor it will happily keep presenting: a throw here
177
+ * retries the same dead id until the recoveries run out, and the run ends on
178
+ * a stack trace that names neither the session nor the cause. A
179
+ * terminal outcome ends it at the first attempt instead, with the exit code
180
+ * the runtime uses for a killed process and an explanation on `stderr`,
181
+ * which is exactly the channel a session that died without a result line
182
+ * already reports through. The wording is {@link containerLost}'s.
183
+ */
184
+ async resume(runtime, cursor, sinks = {}) {
185
+ const env_3 = { stack: [], error: void 0, hasError: false };
186
+ try {
187
+ let handle;
188
+ try {
189
+ handle = await attachRun(runtime, cursor, {
190
+ signal: sinks.signal,
191
+ maxWaitMs: timeoutMs
192
+ });
193
+ }
194
+ catch (err) {
195
+ if (!isExecLost(err))
196
+ throw err;
197
+ console.warn("[claude-code] the session's container was replaced", {
198
+ execId: cursor.execId,
199
+ err: String(err)
200
+ });
201
+ return {
202
+ done: true,
203
+ cursor,
204
+ progress: [],
205
+ exitCode: -1,
206
+ stderr: containerLost()
207
+ };
208
+ }
209
+ const session = __addDisposableResource(env_3, handle, false);
210
+ return await drainRun(session, cursor, sinks);
211
+ }
212
+ catch (e_3) {
213
+ env_3.error = e_3;
214
+ env_3.hasError = true;
215
+ }
216
+ finally {
217
+ __disposeResources(env_3);
218
+ }
219
+ },
220
+ /** Stop a session — `SIGTERM`, so its own process tree goes with it. */
221
+ async stop(runtime, runId) {
222
+ // First, and allowed to fail: most sessions never had a follow-up, and
223
+ // one that did has usually finished its first exec already.
224
+ await killRun(runtime, followUpExecIdFor(runId)).catch(() => { });
225
+ await killRun(runtime, execIdFor(runId));
226
+ },
227
+ /**
228
+ * Is any credential usable right now, and if not, when?
229
+ *
230
+ * The same question {@link egress} answers per request, asked ahead of time
231
+ * so a host can decline to start a session it cannot pay for. That is not a
232
+ * micro-optimisation: an invocation carries an 18.7-27k-token cached prefix
233
+ * before it does anything, so starting one and letting the gateway refuse
234
+ * its first model call costs a container start and that prefix to learn what
235
+ * this returns for free — and reports it as a failed run rather than as a
236
+ * rate limit with a time on it.
237
+ *
238
+ * Reads only. Nothing here marks anything spent.
239
+ */
240
+ credentials: (store) => credentialPool({ credentials: config.credentials, store }).lead(),
241
+ /**
242
+ * The `Fetcher` the workspace object installs as its egress policy.
243
+ *
244
+ * `store` is an **argument rather than a config field**, and that is the
245
+ * point: the credential pool's state belongs to whichever object has
246
+ * storage, and this same config object is also held by the agents that run
247
+ * sessions, which have none of their own for it. Making it a field would
248
+ * force them to invent one.
249
+ */
250
+ egress: (store) => claudeCodeEgress({
251
+ credentials: config.credentials,
252
+ store,
253
+ // Forwarded only when set. Defaulting it to `[]` here would turn "the
254
+ // host said nothing" into "Anthropic only", which is the one reading
255
+ // the three-way semantics exist to keep distinct.
256
+ ...(config.restrictToHosts === undefined
257
+ ? {}
258
+ : { restrictToHosts: config.restrictToHosts }),
259
+ ...(config.githubToken ? { githubToken: config.githubToken } : {}),
260
+ label: "claude-code"
261
+ })
262
+ };
263
+ }
264
+ /**
265
+ * A conversation to continue, as launch flags.
266
+ *
267
+ * One place rather than a spread at each call site, so `fork` can never travel
268
+ * without the id it forks — which {@link file://./run.ts buildLaunch} refuses,
269
+ * but refuses at launch rather than here.
270
+ */
271
+ function resumption(resume) {
272
+ if (!resume)
273
+ return undefined;
274
+ return { resume: resume.sessionId, ...(resume.fork ? { fork: true } : {}) };
275
+ }
276
+ /**
277
+ * Refuse a deployment with no credential when the model is built, naming this
278
+ * plugin, rather than at the first model call inside a run somebody is waiting
279
+ * on. The thunk is still what the gateway calls per request, so a rotation is
280
+ * picked up.
281
+ */
282
+ export function requireCredentials(config) {
283
+ if (config.credentials().filter(Boolean).length === 0) {
284
+ throw new Error("claude-code: no credentials. Set at least one `claude setup-token` " +
285
+ "credential and pass it as " +
286
+ "`credentials: () => [env.CLAUDE_CODE_OAUTH_TOKEN_1]`.");
287
+ }
288
+ }
@@ -0,0 +1,46 @@
1
+ import type { SubAgentSpec } from "@dynamicagents/core";
2
+ import { z } from "zod";
3
+ /**
4
+ * The Claude Code writer, as data: what the parent's model is offered. The host
5
+ * binds it to a `SubAgent` class whose model is
6
+ * {@link file://./model.ts claudeCodeModel}, and adds the `prepare` and
7
+ * `settle` that claim and release its workspace. A planner is the same spec
8
+ * under another name, description and input, whose model runs under `plan` —
9
+ * see {@link file://./model.ts ClaudeCodeModelOptions.permissionMode}.
10
+ *
11
+ * A module of its own for its `zod` schemas. `zod` costs a Worker bundle
12
+ * ~460 KiB, and the workspace Durable Object imports this subpath for its
13
+ * egress gateway alone; with `sideEffects: false`, a module nothing it uses
14
+ * reaches is dropped from that bundle whole.
15
+ *
16
+ * It runs **detached**: a session runs for up to its `timeoutMs`, past the
17
+ * point a parent's turn is cut.
18
+ */
19
+ /**
20
+ * The writer's input: the task, and optionally the branch to add to.
21
+ *
22
+ * *Which* repository is not the parent model's to choose: the workspace is one
23
+ * Durable Object, one container and one checkout, keyed by caller and
24
+ * repository, and the parent already tracks which one is active. A repository
25
+ * named here would let a model name somebody else's — the same reasoning that
26
+ * keeps the workspace name out of model input in `/computer`.
27
+ *
28
+ * A branch is different: the model quotes it from an earlier report or a pull
29
+ * request, and the host's `prepare` resolves it before anything runs.
30
+ *
31
+ * Exported so a host can extend it with an input of its own that its `prepare`
32
+ * resolves.
33
+ */
34
+ export declare const CLAUDE_CODE_WRITE_INPUT: z.ZodObject<{
35
+ task: z.ZodString;
36
+ continue: z.ZodOptional<z.ZodString>;
37
+ }, z.core.$strip>;
38
+ export type ClaudeCodeInput = z.infer<typeof CLAUDE_CODE_WRITE_INPUT>;
39
+ /**
40
+ * Sized deliberately. An invocation carries an 18.7-27k token cached prefix
41
+ * before it does anything — a ten-call burst billed twenty raw input tokens
42
+ * against 187,130 cache reads — so a session has to be worth starting. The
43
+ * description says that in the terms the parent's model can act on: one
44
+ * coherent change, not one file edit.
45
+ */
46
+ export declare const CLAUDE_CODE_AGENT: SubAgentSpec<ClaudeCodeInput>;
@@ -0,0 +1,83 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * The Claude Code writer, as data: what the parent's model is offered. The host
4
+ * binds it to a `SubAgent` class whose model is
5
+ * {@link file://./model.ts claudeCodeModel}, and adds the `prepare` and
6
+ * `settle` that claim and release its workspace. A planner is the same spec
7
+ * under another name, description and input, whose model runs under `plan` —
8
+ * see {@link file://./model.ts ClaudeCodeModelOptions.permissionMode}.
9
+ *
10
+ * A module of its own for its `zod` schemas. `zod` costs a Worker bundle
11
+ * ~460 KiB, and the workspace Durable Object imports this subpath for its
12
+ * egress gateway alone; with `sideEffects: false`, a module nothing it uses
13
+ * reaches is dropped from that bundle whole.
14
+ *
15
+ * It runs **detached**: a session runs for up to its `timeoutMs`, past the
16
+ * point a parent's turn is cut.
17
+ */
18
+ /**
19
+ * The writer's input: the task, and optionally the branch to add to.
20
+ *
21
+ * *Which* repository is not the parent model's to choose: the workspace is one
22
+ * Durable Object, one container and one checkout, keyed by caller and
23
+ * repository, and the parent already tracks which one is active. A repository
24
+ * named here would let a model name somebody else's — the same reasoning that
25
+ * keeps the workspace name out of model input in `/computer`.
26
+ *
27
+ * A branch is different: the model quotes it from an earlier report or a pull
28
+ * request, and the host's `prepare` resolves it before anything runs.
29
+ *
30
+ * Exported so a host can extend it with an input of its own that its `prepare`
31
+ * resolves.
32
+ */
33
+ export const CLAUDE_CODE_WRITE_INPUT = z.object({
34
+ task: z
35
+ .string()
36
+ .describe("The change to make, described as you would to an engineer: what should be true when it is done, and how to tell"),
37
+ continue: z
38
+ .string()
39
+ .optional()
40
+ .describe("The branch to add to — one an earlier claude_code report named, or an open pull request's head branch; omit to start a new branch")
41
+ });
42
+ /**
43
+ * Nothing reads it: the model is the CLI, whose system prompt is its own. It
44
+ * exists because a spec must declare a soul, and says so, so nobody tunes it.
45
+ */
46
+ const SOUL = "Not a prompt: this sub-agent's model is a Claude Code session, which brings its own.";
47
+ /**
48
+ * Sized deliberately. An invocation carries an 18.7-27k token cached prefix
49
+ * before it does anything — a ten-call burst billed twenty raw input tokens
50
+ * against 187,130 cache reads — so a session has to be worth starting. The
51
+ * description says that in the terms the parent's model can act on: one
52
+ * coherent change, not one file edit.
53
+ */
54
+ export const CLAUDE_CODE_AGENT = {
55
+ name: "claude_code",
56
+ description: [
57
+ "Hand a coding task to a Claude Code session running in your workspace",
58
+ "container, in whatever you have open there — a repository you checked out,",
59
+ "or a scratchpad. It has its own tools — it reads, edits, runs the test suite",
60
+ "and iterates — and it reports back what it did. It runs in the background:",
61
+ "the call returns at once, and its report arrives in a later turn.",
62
+ "",
63
+ "Give it **one coherent change**, described the way you would describe it to",
64
+ "an engineer: what should be true when it is done, and how to tell. It is",
65
+ "expensive to start and cheap to let run, so 'add the endpoint, its tests and",
66
+ "wire it up' is one session, not three. A session that is only asked to edit",
67
+ "a single line costs about what a substantial one costs.",
68
+ "",
69
+ "It cannot ask you anything mid-run. Anything it would need to ask, decide",
70
+ "first — or ask the user yourself before delegating.",
71
+ "",
72
+ "To correct or extend work that is already on a branch — the one a previous",
73
+ "session's report named, or an open pull request's head branch — set",
74
+ "`continue` to that branch: the new session starts from its tip and adds",
75
+ "commits to it, so pushing the branch updates its pull request. Without it, a",
76
+ "session starts a branch of its own, and changing an open pull request from",
77
+ "there means moving commits between branches."
78
+ ].join("\n"),
79
+ inputSchema: CLAUDE_CODE_WRITE_INPUT,
80
+ soul: SOUL,
81
+ detached: true,
82
+ formatInput: (input) => input.task
83
+ };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The Claude Code version this package's egress gateway and stream parser were
3
+ * last verified against, and the one a deployment's image pins.
4
+ *
5
+ * Both are written against that version's traffic — its headers, its beta list,
6
+ * its `stream-json` lines — and the probe's capture,
7
+ * `test/fixtures/claude-code-probe-capture.json`, is that traffic, recorded. The
8
+ * specs read the capture, so they check the gateway and the parser against what
9
+ * this version actually sends and prints.
10
+ *
11
+ * Written by `npm run probe:claude-code -- --record`, never by hand: the value
12
+ * and the capture move together. AGENTS.md's "Updating Claude Code" is the
13
+ * procedure.
14
+ */
15
+ export declare const VERIFIED_CLAUDE_CODE_VERSION = "2.1.296";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * The Claude Code version this package's egress gateway and stream parser were
3
+ * last verified against, and the one a deployment's image pins.
4
+ *
5
+ * Both are written against that version's traffic — its headers, its beta list,
6
+ * its `stream-json` lines — and the probe's capture,
7
+ * `test/fixtures/claude-code-probe-capture.json`, is that traffic, recorded. The
8
+ * specs read the capture, so they check the gateway and the parser against what
9
+ * this version actually sends and prints.
10
+ *
11
+ * Written by `npm run probe:claude-code -- --record`, never by hand: the value
12
+ * and the capture move together. AGENTS.md's "Updating Claude Code" is the
13
+ * procedure.
14
+ */
15
+ export const VERIFIED_CLAUDE_CODE_VERSION = "2.1.296";
@@ -1,11 +1,7 @@
1
1
  import type { WorkspaceClient } from "@cloudflare/computer";
2
2
  import { type ExecGate } from "./gate.js";
3
- import type { WorkspaceAdvisory } from "./advisory.js";
3
+ import type { WorkspaceAdvisory } from "../workspace/advisory.js";
4
4
  import type { ComputerConfig } from "./index.js";
5
- /** A command that has not finished in this long is a hung command. */
6
- export declare const DEFAULT_TIMEOUT_MS: number;
7
- /** Where checkouts live, in the container and in the workspace alike. */
8
- export declare const DEFAULT_CWD = "/workspace";
9
5
  /** What {@link computerContext} hands each group of tools. */
10
6
  export interface ComputerContext {
11
7
  config: ComputerConfig;
@@ -50,6 +50,7 @@ var __disposeResources = (this && this.__disposeResources) || (function (Suppres
50
50
  var e = new Error(message);
51
51
  return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
52
52
  });
53
+ import { DEFAULT_CWD, DEFAULT_TIMEOUT_MS } from "../workspace/exec.js";
53
54
  import { withAbort } from "@dynamicagents/core";
54
55
  import { execGate, writeGate } from "./gate.js";
55
56
  /**
@@ -66,10 +67,6 @@ import { execGate, writeGate } from "./gate.js";
66
67
  * says what actually failed.
67
68
  */
68
69
  const DEFAULT_MAX_OUTPUT_CHARS = 16_000;
69
- /** A command that has not finished in this long is a hung command. */
70
- export const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
71
- /** Where checkouts live, in the container and in the workspace alike. */
72
- export const DEFAULT_CWD = "/workspace";
73
70
  /**
74
71
  * How often the install gate re-reads the install's status.
75
72
  *
@@ -108,7 +105,7 @@ export function computerContext(workspace, config, advisories, fsWorkspace = wor
108
105
  // is a read of another Durable Object's state. An error must not take out a
109
106
  // working shell — running the command is exactly what would have happened
110
107
  // before the gate existed, and a spurious blockage would strand the
111
- // subagent.
108
+ // agent.
112
109
  const read = async () => {
113
110
  try {
114
111
  signal?.throwIfAborted();
@@ -178,18 +175,18 @@ export function computerContext(workspace, config, advisories, fsWorkspace = wor
178
175
  /**
179
176
  * Open the workspace, run one file operation in it, and own the failure.
180
177
  *
181
- * Five lines every file tool would otherwise repeat with only the verb changed
182
- * — five chances for the seventh to be written without them. The catch is the
178
+ * Lines every file tool would otherwise repeat with only the verb changed,
179
+ * and a chance for the next one to be written without them. The catch is the
183
180
  * point: a failed file operation is the model's to recover from, and it can
184
181
  * only recover from what it is told, so this returns the sentence rather than
185
182
  * throwing it.
186
183
  *
187
- * `sb_exec` deliberately does not use it. That one recognises `EEXEC_LOST`,
184
+ * `bash` deliberately does not use it. That one recognises `EEXEC_LOST`,
188
185
  * logs, and carries an install warning through the failure path — a bespoke
189
186
  * catch saying something this cannot.
190
187
  *
191
- * It opens through `fsWorkspace`, which is what makes the capability sentence
192
- * about these tools true: they answer while the container is down.
188
+ * It opens through `fsWorkspace`, which is what makes the context block's
189
+ * sentence about these tools true: they answer while the container is down.
193
190
  */
194
191
  const inWorkspace = async (gerund, subject, body) => {
195
192
  try {
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * One mutation per file at a time, across every tool set in this isolate.
3
3
  *
4
- * `sb_edit` reads, replaces and writes back over RPC, and the AI SDK runs one
4
+ * `edit` reads, replaces and writes back over RPC, and the AI SDK runs one
5
5
  * step's tool calls concurrently — so two edits to one file would both report
6
- * success and the first would be lost. Module-level because a parent and a
7
- * subagent can hold separate tool sets over one workspace; another isolate is
8
- * out of reach.
6
+ * success and the first would be lost. The workspace's `writeFile` takes the
7
+ * same lock, so Think's `write` never lands between an edit's read and its
8
+ * write. Module-level because a parent and a sub-agent can hold separate tool
9
+ * sets over one workspace; another isolate is out of reach.
9
10
  */
10
11
  const tails = new Map();
11
12
  /**