@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
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Everything one `claude-coder` deployment tunes, in one shape.
2
+ * Everything one deployment of this plugin tunes, in one shape.
3
3
  *
4
4
  * Config at instantiation rather than an `env` argument, for the reason the
5
5
  * whole package works this way: `Env` is the ambient interface `wrangler types`
@@ -14,8 +14,16 @@
14
14
  * the exact failure this option exists to end. The CLI validates the value too,
15
15
  * but it does so by exiting 1 with a message on stderr, and stderr reaches an
16
16
  * operator far less reliably than a type error reaches a developer.
17
+ *
18
+ * Two of these are worth knowing about before picking one. `default` is not
19
+ * among the choices the pinned CLI documents, and is accepted and echoed back as
20
+ * `permissionMode: "default"` anyway — so it stays. `manual` is the reverse: the
21
+ * CLI documents it and no session here has been run under it, so it is spellable
22
+ * rather than recommended, and whatever it prompts for the headless path denies.
23
+ * See {@link ClaudeCodeConfig.permissionMode} for why the default is the
24
+ * permissive one.
17
25
  */
18
- export type PermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "plan" | "dontAsk" | "auto";
26
+ export type PermissionMode = "default" | "acceptEdits" | "bypassPermissions" | "plan" | "dontAsk" | "manual" | "auto";
19
27
  /**
20
28
  * What a session runs under when a deployment says nothing.
21
29
  *
@@ -61,18 +69,6 @@ export interface ClaudeCodeConfig {
61
69
  * refused with the reset time.
62
70
  */
63
71
  credentials: () => readonly string[];
64
- /**
65
- * Which workspace this agent's sessions run in.
66
- *
67
- * Resolved on the **parent**, where the verified caller is known: core
68
- * dispatches `resolveRuntime` to the plugin that declared the subtask type,
69
- * which is this one, and the value it writes is how the subagent facet learns
70
- * which Durable Object holds its checkout. A facet cannot work this out for
71
- * itself — `callerKey()` throws there by design — and it is deliberately not a
72
- * subtask param, because a model-authored workspace name would let a model
73
- * name somebody else's.
74
- */
75
- workspaceName: () => string;
76
72
  /** Which model the session runs. Unset, Claude Code picks its own default. */
77
73
  model?: string;
78
74
  /**
@@ -84,9 +80,9 @@ export interface ClaudeCodeConfig {
84
80
  * holds, not just how well one thinks. The README's Costs section carries the
85
81
  * multiples and the sizing they imply.
86
82
  *
87
- * Not to be confused with the host round loop's own reasoning effort, which
88
- * belongs to a different model on the Worker side. This one reaches only the
89
- * CLI in the container.
83
+ * Not to be confused with the agent's own reasoning effort, which belongs to
84
+ * a different model on the Worker side. This one reaches only the CLI in the
85
+ * container.
90
86
  */
91
87
  effort?: EffortLevel;
92
88
  /**
@@ -94,8 +90,8 @@ export interface ClaudeCodeConfig {
94
90
  *
95
91
  * Advisory, and the distinction is the reason there is no turn ceiling beside
96
92
  * them — see the README on what this package deliberately does not do. That
97
- * tree is invisible to Dynamic Agents' scheduler, so it multiplies whatever
98
- * these say; {@link timeoutMs} is the ceiling that actually holds, because the
93
+ * tree is invisible to the agent running the session, so it multiplies
94
+ * whatever these say; {@link timeoutMs} is the ceiling that actually holds, because the
99
95
  * container runtime enforces it.
100
96
  */
101
97
  maxSubagentDepth?: number;
@@ -127,6 +123,9 @@ export interface ClaudeCodeConfig {
127
123
  *
128
124
  * See {@link file://./run.ts buildLaunch} for the root guard this mode trips,
129
125
  * which is the other half of making it work.
126
+ *
127
+ * A run may set its own, over this one: a planning run's is `plan` — see
128
+ * {@link file://./model.ts ClaudeCodeModelOptions.permissionMode}.
130
129
  */
131
130
  permissionMode?: PermissionMode;
132
131
  /**
@@ -137,43 +136,20 @@ export interface ClaudeCodeConfig {
137
136
  * {@link file://./egress.ts EgressConfig.restrictToHosts} for the full table
138
137
  * and for why open is the default.
139
138
  *
140
- * Whatever this says, the container never needs forge access: `/repo` runs
141
- * clone, fetch and push as isomorphic-git inside the workspace object, so the
142
- * forge token stays on the Worker side.
139
+ * A restriction that leaves out `github.com` and `api.github.com` leaves out
140
+ * {@link githubToken} with them.
143
141
  */
144
142
  restrictToHosts?: readonly string[];
145
143
  /**
146
- * How long one chunk blocks before checkpointing and yielding.
147
- *
148
- * **A read deadline on the session's stdout stream, not a halt.** Nothing is
149
- * stopped and nothing is destroyed when it expires: the host detaches, the
150
- * session process inside the container never notices, and the next chunk
151
- * re-attaches to the live session from the cursor. So the window is bounded
152
- * against `STEP_TIMEOUT_MS` rather than against core's `CHUNK_SOFT_MS` —
153
- * there is no in-flight turn to overrun, which is what sizes the recipe
154
- * path's budget.
155
- *
156
- * What prices the headroom instead is the post-exit filesystem pull, which is
157
- * deliberately unbounded because it carries whatever the session wrote,
158
- * dependency trees included, and can outlast the window. So no value here can
159
- * guarantee the chunk fits inside the step timeout: what is left over is
160
- * sized for an *expected* pull, plus the sub-second unwind of yielding and
161
- * the step's own bookkeeping, and a large enough pull overruns it whatever
162
- * this is set to. See {@link file://./run.ts DrainOptions.windowMs}.
163
- *
164
- * **Overrunning is the cheaper failure, which is why the default is
165
- * generous.** The step is killed and retried; the filesystem sync resumes
166
- * from the blocks it has already committed rather than starting over, and the
167
- * retry re-attaches to the session from the cursor — so an overrun costs a
168
- * retry and some replayed notes, never a session's edits. An undersized
169
- * window costs a chunk boundary on *every* session instead — a checkpoint, a
170
- * fresh step, a re-hydrated subagent and a re-attach that replays the tail —
171
- * paid whether or not the pull was ever going to be large.
172
- *
173
- * It must also be long enough that a session does not exhaust its branch's
174
- * chunk allowance while it is still thinking.
144
+ * GitHub as the deployment's account, for a session that pushes its branch,
145
+ * opens its pull request and answers its review. Read per request, on the
146
+ * Worker side.
147
+ *
148
+ * The session gets a placeholder as `GH_TOKEN` and as git's credential, and
149
+ * the egress gateway swaps in this token for GitHub's hosts only — see
150
+ * {@link file://./github.ts}. Unset, `gh` and git reach GitHub anonymously.
175
151
  */
176
- windowMs?: number;
152
+ githubToken?: () => string | undefined;
177
153
  /**
178
154
  * Ceiling on the whole session, enforced by the container runtime.
179
155
  *
@@ -202,16 +178,5 @@ export interface ClaudeCodeConfig {
202
178
  email: string;
203
179
  };
204
180
  }
205
- /**
206
- * Twenty minutes against a 30-minute step timeout, which leaves ~10 minutes for
207
- * the sub-second unwind of yielding the window and for the post-exit filesystem
208
- * pull. An expected pull fits there; a pull that carries an install's
209
- * dependency tree may not, and nothing here promises it will — that pull is
210
- * unbounded by design. Sized for the expected case deliberately: the pull that
211
- * overruns is retried and resumes, while a shorter window would buy that back
212
- * by paying for extra chunk boundaries on every session. See
213
- * {@link ClaudeCodeConfig.windowMs}.
214
- */
215
- export declare const DEFAULT_WINDOW_MS: number;
216
181
  /** Fifteen minutes, well under the coder workspace's 20-minute idle timer. */
217
182
  export declare const DEFAULT_TIMEOUT_MS: number;
@@ -7,16 +7,5 @@
7
7
  * {@link ClaudeCodeConfig.permissionMode}.
8
8
  */
9
9
  export const DEFAULT_PERMISSION_MODE = "bypassPermissions";
10
- /**
11
- * Twenty minutes against a 30-minute step timeout, which leaves ~10 minutes for
12
- * the sub-second unwind of yielding the window and for the post-exit filesystem
13
- * pull. An expected pull fits there; a pull that carries an install's
14
- * dependency tree may not, and nothing here promises it will — that pull is
15
- * unbounded by design. Sized for the expected case deliberately: the pull that
16
- * overruns is retried and resumes, while a shorter window would buy that back
17
- * by paying for extra chunk boundaries on every session. See
18
- * {@link ClaudeCodeConfig.windowMs}.
19
- */
20
- export const DEFAULT_WINDOW_MS = 20 * 60_000;
21
10
  /** Fifteen minutes, well under the coder workspace's 20-minute idle timer. */
22
11
  export const DEFAULT_TIMEOUT_MS = 15 * 60_000;
@@ -22,7 +22,8 @@ import { type CredentialStore } from "./credentials.js";
22
22
  * with a placeholder; the swap happens here. A `postinstall` script in a
23
23
  * cloned repository can read every environment variable the container has and
24
24
  * still learn nothing — which is the repository's standing rule ("hand it the
25
- * action, not the credential") applied to a process nobody can constrain.
25
+ * action, not the credential") applied to a process nobody can constrain. A
26
+ * GitHub token, when configured, is held the same way — see `./github.ts`.
26
27
  * 2. **Any restriction that is applied is total.** Not a policy the container
27
28
  * cooperates with; the only route out. Restriction is **off by default** —
28
29
  * see {@link EgressConfig.restrictToHosts} for why.
@@ -103,6 +104,12 @@ export interface EgressConfig {
103
104
  * restriction, the first time a host serves user-controlled subdomains.
104
105
  */
105
106
  restrictToHosts?: readonly string[];
107
+ /**
108
+ * The GitHub token to swap for its placeholder, read per request. It reaches
109
+ * {@link GITHUB_HOSTS} and nowhere else. Unset, GitHub is reached
110
+ * anonymously like any other host. See `./github.ts`.
111
+ */
112
+ githubToken?: () => string | undefined;
106
113
  /** Named in log lines so one Worker's several gateways stay tellable apart. */
107
114
  label?: string;
108
115
  now?: () => number;
@@ -1,4 +1,5 @@
1
1
  import { credentialPool, readRefusal } from "./credentials.js";
2
+ import { GITHUB_HOSTS, githubAuthorization, githubError, presentsPlaceholder } from "./github.js";
2
3
  /**
3
4
  * `@dynamicagents/plugins/claude-code` — the container's only way out.
4
5
  *
@@ -22,7 +23,8 @@ import { credentialPool, readRefusal } from "./credentials.js";
22
23
  * with a placeholder; the swap happens here. A `postinstall` script in a
23
24
  * cloned repository can read every environment variable the container has and
24
25
  * still learn nothing — which is the repository's standing rule ("hand it the
25
- * action, not the credential") applied to a process nobody can constrain.
26
+ * action, not the credential") applied to a process nobody can constrain. A
27
+ * GitHub token, when configured, is held the same way — see `./github.ts`.
26
28
  * 2. **Any restriction that is applied is total.** Not a policy the container
27
29
  * cooperates with; the only route out. Restriction is **off by default** —
28
30
  * see {@link EgressConfig.restrictToHosts} for why.
@@ -57,9 +59,9 @@ export const ANTHROPIC_HOST = "api.anthropic.com";
57
59
  * Bouncing a request back into it is never a legitimate fetch, so it is refused
58
60
  * before any policy is consulted.
59
61
  *
60
- * A workspace that overrides `egressHost` on its `CloudflareContainerBackend`
61
- * should add that name here; the default is what the backend uses when nothing
62
- * says otherwise.
62
+ * A workspace that overrides `egressHost` on its `ContainerBackend` should add
63
+ * that name here; the default is what the backend uses when nothing says
64
+ * otherwise.
63
65
  */
64
66
  const NEVER_ALLOWED = new Set([
65
67
  "computer.internal",
@@ -206,6 +208,9 @@ export function claudeCodeEgress(config) {
206
208
  });
207
209
  return apiError("permission_error", `${host} is outside this workspace's egress host restriction`, 403);
208
210
  }
211
+ if (config.githubToken && GITHUB_HOSTS.has(host)) {
212
+ return await toGitHub(request, url, config.githubToken);
213
+ }
209
214
  const headers = new Headers(request.headers);
210
215
  if (host !== ANTHROPIC_HOST) {
211
216
  // The placeholder must not leave the boundary either. It is worthless to
@@ -263,6 +268,39 @@ export function claudeCodeEgress(config) {
263
268
  }
264
269
  return await rotate(request, response, lead.id, lead.index);
265
270
  };
271
+ /**
272
+ * A request to GitHub: anonymous unless it presents the placeholder,
273
+ * which is swapped for the real token over https only.
274
+ *
275
+ * `redirect: "manual"`, so the token rides only the request it was swapped
276
+ * into: a redirect goes back to the client, whose next request crosses this
277
+ * gateway again and is judged on its own host.
278
+ */
279
+ const toGitHub = async (request, url, githubToken) => {
280
+ const headers = new Headers(request.headers);
281
+ const presented = presentsPlaceholder(headers.get("authorization"));
282
+ for (const name of CREDENTIAL_HEADERS)
283
+ headers.delete(name);
284
+ if (!presented) {
285
+ return await fetch(new Request(url, new Request(request, { headers })));
286
+ }
287
+ if (url.protocol !== "https:") {
288
+ console.warn(`[${tag}] refused a plaintext request to GitHub`, {
289
+ host: url.hostname,
290
+ method: request.method
291
+ });
292
+ return githubError(`${url.hostname} is reachable over https only; a credential is never ` +
293
+ "attached to a plaintext request", 403);
294
+ }
295
+ const token = githubToken();
296
+ if (!token) {
297
+ console.error(`[${tag}] a session asked for GitHub with no token set`);
298
+ return githubError("no GitHub token is configured for this deployment, so nothing can " +
299
+ "be done as its account", 401);
300
+ }
301
+ headers.set("authorization", githubAuthorization(presented, token));
302
+ return await fetch(new Request(url, new Request(request, { headers, redirect: "manual" })));
303
+ };
266
304
  /**
267
305
  * Read a refusal, move the lead if it earned one, and shape what the client
268
306
  * sees so its own retry lands on the new credential.
@@ -1,4 +1,4 @@
1
- import type { ProgressEvent } from "@dynamicagents/core/subtasks";
1
+ import type { NoteData } from "@dynamicagents/core/subagent";
2
2
  /** What a `result` line carries, of the fields anything here reads. */
3
3
  export interface ClaudeCodeResult {
4
4
  /** `success`, or one of the `error_*` subtypes. */
@@ -16,6 +16,23 @@ export interface ClaudeCodeResult {
16
16
  /** How many inner subagents the run spawned — advisory, for the logs. */
17
17
  subagentsSpawned?: number;
18
18
  permissionDenials: number;
19
+ /**
20
+ * The answer of a session launched with a JSON Schema: the input of its
21
+ * `StructuredOutput` call, which `text` then holds as a JSON string. Absent
22
+ * for any other session, and for one that never made the call.
23
+ */
24
+ structured?: unknown;
25
+ /**
26
+ * What the CLI says went wrong, when it reports an error rather than an
27
+ * answer.
28
+ *
29
+ * The only account there is of a refused resume: a `--resume` naming a
30
+ * conversation the config directory does not hold ends in a `result` line with
31
+ * no `text` at all, and `No conversation found with session ID: …` here. A
32
+ * caller that reports such a run as a failed session rather than as "not
33
+ * resumable" is reporting the one thing it was handed to say.
34
+ */
35
+ errors?: string[];
19
36
  }
20
37
  /**
21
38
  * Token counts, flattened.
@@ -63,6 +80,8 @@ export type ClaudeCodeEvent = {
63
80
  kind: "init";
64
81
  sessionId: string;
65
82
  model?: string;
83
+ cwd?: string;
84
+ permissionMode?: string;
66
85
  }
67
86
  /**
68
87
  * The subscription bucket, as the client sees it. Carried rather than acted
@@ -72,10 +91,44 @@ export type ClaudeCodeEvent = {
72
91
  | {
73
92
  kind: "rateLimit";
74
93
  info: RateLimitInfo;
75
- } | {
76
- kind: "assistant";
94
+ }
95
+ /** One `text` block of an assistant message, verbatim — it is markdown. */
96
+ | {
97
+ kind: "text";
77
98
  text: string;
78
- tools: string[];
99
+ }
100
+ /**
101
+ * One `thinking` block, as how long it took — never what it said. A block can
102
+ * run to thousands of tokens of private reasoning; a reader is told that the
103
+ * model thought, and for how long, the way the CLI itself says it.
104
+ *
105
+ * `durationMs` is measured, never inferred: see
106
+ * {@link ParsedStream.thinkingFrom} for where it starts, and it is absent
107
+ * whenever that cannot be trusted. `messageId` is the API message the block
108
+ * belongs to — a message can carry more than one.
109
+ */
110
+ | {
111
+ kind: "thinking";
112
+ messageId?: string;
113
+ durationMs?: number;
114
+ } | {
115
+ kind: "toolUse";
116
+ id: string;
117
+ name: string;
118
+ input: unknown;
119
+ }
120
+ /**
121
+ * One `tool_result` block of a `user` line: a tool's answer going back to
122
+ * the model. `structured` is the line's `tool_use_result` — the tool's own
123
+ * record of what it did, richer than the text the model reads — and is read
124
+ * only when the line carries a single result, since it is not per block.
125
+ */
126
+ | {
127
+ kind: "toolResult";
128
+ id: string;
129
+ isError: boolean;
130
+ content: string;
131
+ structured?: unknown;
79
132
  } | {
80
133
  kind: "retry";
81
134
  detail: string;
@@ -124,7 +177,46 @@ export interface ParsedStream {
124
177
  sample?: string;
125
178
  /** Lines dropped for carrying `parent_tool_use_id`. See {@link parseStream}. */
126
179
  nested: number;
180
+ /**
181
+ * When the pending thinking block began streaming, on the caller's clock:
182
+ * the `now` of the read that brought its first `system/thinking_tokens`
183
+ * line.
184
+ *
185
+ * That line is the one sign on the stream that thinking is under way. The
186
+ * CLI prints it as the block's text arrives, so it comes after everything
187
+ * that is not thinking: request setup, the wait for a first token, an API
188
+ * retry's back-off, a compaction. The line before a block — the tool result
189
+ * it answers — comes before all of those, and timing from it is how a minute
190
+ * of retries reads as a minute of thought. The token line carries no time of
191
+ * its own, which is why the caller's clock stamps it.
192
+ *
193
+ * Cleared by every line that carries a `timestamp`, so a start belongs to
194
+ * the block that follows the last message and never to work before it. Handed
195
+ * back with `carry`, for the same reason, and kept on the caller's cursor.
196
+ */
197
+ thinkingFrom?: number;
198
+ }
199
+ /** What {@link parseStream} times a thinking block against. */
200
+ export interface ParseClock {
201
+ /** When this read arrived, in epoch milliseconds. */
202
+ now: number;
203
+ /** {@link ParsedStream.thinkingFrom} from the previous read. */
204
+ thinkingFrom?: number;
127
205
  }
206
+ /**
207
+ * How far a thinking line's arrival may be from its own `timestamp` for the
208
+ * block to be timed.
209
+ *
210
+ * Both ends of a block are timed by when they arrive, which is only the truth
211
+ * while the drain reads live. A drain that re-attached after a cut is handed
212
+ * what was printed meanwhile in one burst, and its arrival times are the
213
+ * replay's — so a block whose line arrived far from when the CLI stamped it
214
+ * reads "Thought", with no time. Locally a line arrives 5–31 ms after its
215
+ * stamp; a re-attach has been measured at about a second. A container clock
216
+ * this far from the Worker's degrades every block to no time, rather than to a
217
+ * wrong one.
218
+ */
219
+ export declare const LIVE_SLACK_MS = 5000;
128
220
  /**
129
221
  * Parse whatever has arrived so far into complete events plus a carry.
130
222
  *
@@ -138,8 +230,12 @@ export interface ParsedStream {
138
230
  * the filter.
139
231
  *
140
232
  * `result` and `init` are never nested and are read unconditionally.
233
+ *
234
+ * `clock` is when this read arrived, and the pending thinking block's start
235
+ * from the previous read — see {@link ParsedStream.thinkingFrom}. Without it no
236
+ * block is timed.
141
237
  */
142
- export declare function parseStream(buffer: string): ParsedStream;
238
+ export declare function parseStream(buffer: string, clock?: ParseClock): ParsedStream;
143
239
  /**
144
240
  * The status a healthy bucket reports, and the only one ever observed.
145
241
  *
@@ -152,18 +248,19 @@ export declare function parseStream(buffer: string): ParsedStream;
152
248
  */
153
249
  export declare const RATE_LIMIT_OK = "allowed";
154
250
  /**
155
- * Turn events into progress notes the parent can post.
251
+ * Turn events into progress notes for the parent's transcript.
156
252
  *
157
253
  * **`from` is the number of notes already emitted for this run, and the key is
158
254
  * derived from it.** Not from the note's content, not from a clock, and not from
159
255
  * the Claude Code `uuid` — from position alone.
160
256
  *
161
- * The reason is replay. A chunk that dies mid-drain is retried by the Workflow,
162
- * the re-attach replays the tail, and the same assistant turn is parsed twice. A
257
+ * The reason is replay. A drain that dies mid-stream is resumed from the last
258
+ * stored sequence, whatever arrived after it is read again, and the same
259
+ * assistant turn is parsed twice. A
163
260
  * content-derived key would make two identical notes collide *by luck* (and two
164
261
  * genuinely repeated notes — "Running tests" twice — collide wrongly). A
165
262
  * clock-derived key would never collide, so every replay would re-post
166
263
  * everything. Position is the only choice that dedupes exactly the events that
167
264
  * are the same event.
168
265
  */
169
- export declare function toProgress(events: readonly ClaudeCodeEvent[], from: number): ProgressEvent[];
266
+ export declare function toProgress(events: readonly ClaudeCodeEvent[], from: number): NoteData[];