@dynamicagents/plugins 0.11.1 → 0.12.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 (94) hide show
  1. package/README.md +12 -13
  2. package/dist/arc-agi/analysis.d.ts +16 -30
  3. package/dist/arc-agi/analysis.js +20 -47
  4. package/dist/arc-agi/game-tools.d.ts +5 -6
  5. package/dist/arc-agi/index.d.ts +6 -8
  6. package/dist/arc-agi/index.js +3 -3
  7. package/dist/arc-agi/main-agent.d.ts +5 -8
  8. package/dist/arc-agi/main-agent.js +5 -8
  9. package/dist/arc-agi/recipe.d.ts +17 -20
  10. package/dist/arc-agi/recipe.js +17 -20
  11. package/dist/arc-agi/soul.d.ts +16 -27
  12. package/dist/arc-agi/soul.js +16 -27
  13. package/dist/arc-agi/store.d.ts +4 -5
  14. package/dist/arc-agi/store.js +4 -5
  15. package/dist/arc-agi/tools.d.ts +1 -1
  16. package/dist/arc-agi/tools.js +10 -12
  17. package/dist/arc-agi/types.d.ts +6 -7
  18. package/dist/claude-code/config.d.ts +88 -11
  19. package/dist/claude-code/config.js +11 -2
  20. package/dist/claude-code/credentials.d.ts +21 -9
  21. package/dist/claude-code/credentials.js +43 -9
  22. package/dist/claude-code/egress.d.ts +7 -13
  23. package/dist/claude-code/egress.js +9 -10
  24. package/dist/claude-code/events.d.ts +47 -0
  25. package/dist/claude-code/events.js +96 -10
  26. package/dist/claude-code/index.d.ts +31 -13
  27. package/dist/claude-code/index.js +62 -15
  28. package/dist/claude-code/run.d.ts +135 -41
  29. package/dist/claude-code/run.js +366 -61
  30. package/dist/computer/advisory.d.ts +31 -18
  31. package/dist/computer/advisory.js +30 -5
  32. package/dist/computer/context.d.ts +23 -0
  33. package/dist/computer/context.js +225 -0
  34. package/dist/computer/file-lock.d.ts +5 -0
  35. package/dist/computer/file-lock.js +39 -0
  36. package/dist/computer/gate.d.ts +8 -8
  37. package/dist/computer/gate.js +7 -7
  38. package/dist/computer/host/ca-trust.d.ts +88 -0
  39. package/dist/computer/host/ca-trust.js +303 -0
  40. package/dist/computer/host/container-deps.d.ts +54 -0
  41. package/dist/computer/host/container-deps.js +224 -0
  42. package/dist/computer/host/container-fault.d.ts +38 -0
  43. package/dist/computer/host/container-fault.js +58 -0
  44. package/dist/computer/host/git-host.d.ts +62 -0
  45. package/dist/computer/host/git-host.js +156 -0
  46. package/dist/computer/host/git-identity.d.ts +57 -0
  47. package/dist/computer/host/git-identity.js +147 -0
  48. package/dist/computer/host/install-job.d.ts +121 -0
  49. package/dist/computer/host/install-job.js +766 -0
  50. package/dist/computer/host/sync.d.ts +111 -0
  51. package/dist/computer/host/sync.js +124 -0
  52. package/dist/computer/host/wake.d.ts +19 -0
  53. package/dist/computer/host/wake.js +10 -0
  54. package/dist/computer/host/workspace.d.ts +424 -0
  55. package/dist/computer/host/workspace.js +1258 -0
  56. package/dist/computer/index.d.ts +70 -50
  57. package/dist/computer/index.js +79 -767
  58. package/dist/computer/install.d.ts +8 -12
  59. package/dist/computer/install.js +6 -9
  60. package/dist/computer/paths.d.ts +21 -16
  61. package/dist/computer/paths.js +40 -38
  62. package/dist/computer/read.d.ts +18 -22
  63. package/dist/computer/read.js +24 -27
  64. package/dist/computer/render.d.ts +28 -0
  65. package/dist/computer/render.js +34 -1
  66. package/dist/computer/shell.d.ts +45 -0
  67. package/dist/computer/shell.js +80 -0
  68. package/dist/computer/tools-exec.d.ts +3 -0
  69. package/dist/computer/tools-exec.js +269 -0
  70. package/dist/computer/tools-file.d.ts +4 -0
  71. package/dist/computer/tools-file.js +124 -0
  72. package/dist/computer/tools-find.d.ts +3 -0
  73. package/dist/computer/tools-find.js +216 -0
  74. package/dist/recall/index.d.ts +9 -3
  75. package/dist/recall/index.js +22 -12
  76. package/dist/repo/checkout.d.ts +34 -4
  77. package/dist/repo/checkout.js +36 -1
  78. package/dist/repo/context.d.ts +109 -0
  79. package/dist/repo/context.js +530 -0
  80. package/dist/repo/index.d.ts +6 -29
  81. package/dist/repo/index.js +17 -1027
  82. package/dist/repo/tools-clone.d.ts +4 -0
  83. package/dist/repo/tools-clone.js +192 -0
  84. package/dist/repo/tools-forge.d.ts +4 -0
  85. package/dist/repo/tools-forge.js +179 -0
  86. package/dist/repo/tools-review.d.ts +3 -0
  87. package/dist/repo/tools-review.js +373 -0
  88. package/dist/repo/tools-worktree.d.ts +4 -0
  89. package/dist/repo/tools-worktree.js +221 -0
  90. package/dist/triage/index.d.ts +6 -1
  91. package/dist/triage/index.js +32 -23
  92. package/dist/workspace/index.d.ts +3 -3
  93. package/dist/workspace/index.js +3 -3
  94. package/package.json +21 -15
@@ -0,0 +1,424 @@
1
+ import { DurableObject } from "cloudflare:workers";
2
+ import { type WorkspaceEgressPolicy, type WorkspaceStub } from "@cloudflare/computer";
3
+ import { CloudflareContainerBackend } from "@cloudflare/computer/backends/container";
4
+ import { type WorkspaceAdvisory } from "../advisory.js";
5
+ import type { InstallPlan, InstallState } from "../install.js";
6
+ import type { RepoGitResult } from "../../repo/index.js";
7
+ /**
8
+ * A workspace: one Durable Object, one container, one repository.
9
+ *
10
+ * **Shared by every agent in a Worker that has a container.** Each subclasses
11
+ * {@link WorkspaceObjectBase}, supplies a {@link WorkspaceObjectConfig}, and
12
+ * inherits everything else. Keep that seam narrow: it is the complete answer to
13
+ * "what is different about this agent's container", and anything that does not
14
+ * fit through it becomes a second copy of this file.
15
+ *
16
+ * A directory inside the plugin rather than a subpath beside it, so that one
17
+ * capability stays one import path and a consumer cannot take the tools without
18
+ * the object they address. The bundle cost of that is nothing: `sideEffects` is
19
+ * false, so an agent that only calls `sb_exec` carries no container backend and
20
+ * no isomorphic-git.
21
+ *
22
+ * What this file must not do is import the plugin barrel. The barrel re-exports
23
+ * this module, and the class below is built at import time — a cycle would
24
+ * evaluate its base as `undefined`. The imports above reach the leaf modules
25
+ * beside it instead.
26
+ *
27
+ * `@cloudflare/computer` pairs a SQLite-backed virtual filesystem in *this*
28
+ * object's storage with a container running `computerd`, which mounts it over
29
+ * FUSE at `/workspace`. Commands run against the same tree the Worker reads over
30
+ * RPC, and the tree outlives the container.
31
+ *
32
+ * **One repository per object**, because `computer` is strictly 1 DO ↔ 1
33
+ * container: the id derives from caller *and* repository (see `workspaceName`),
34
+ * so two repositories for one caller are two objects and two containers.
35
+ *
36
+ * **It constructs `Workspace` rather than using `withWorkspace`.** The mixin
37
+ * stores the `Workspace` under a module-private symbol the package does not
38
+ * export, so a method on the object cannot reach it — and what the host is
39
+ * required to do needs exactly that: driving an outstanding pull (the library
40
+ * owns no alarm and schedules nothing of its own), and the direct `runtime`
41
+ * access the detached install needs. So this object owns the `Workspace` and
42
+ * implements the one method the mixin otherwise provides,
43
+ * `__getWorkspaceStub`. Callers outside see no difference.
44
+ *
45
+ * ## What is here, and what is a module next door
46
+ *
47
+ * This file is the Durable Object: the wake map, the deadlines, the RPC surface
48
+ * and the wiring between them. The work each of those drives has its own home,
49
+ * and each carries the reasoning for its own decisions:
50
+ *
51
+ * - `./install-job.ts` — when a dependency install runs, and every guard on it.
52
+ * - `./sync.ts` — the host's half of syncing, which nothing else will do.
53
+ * - `./ca-trust.ts` — making a container able to speak TLS.
54
+ * - `./git-identity.ts` — who a container's commits belong to.
55
+ * - `./git-host.ts` — the operations that hold the forge credential.
56
+ *
57
+ * Each takes a small explicit seam, and that is the test: a module that needed
58
+ * the whole object back would be a module that did not want extracting.
59
+ */
60
+ /** Where every checkout lives, inside the container and in the VFS. */
61
+ export declare const WORKSPACE_DIR = "/workspace";
62
+ /**
63
+ * The Durable Object name for one caller's checkout of one repository.
64
+ *
65
+ * Exported because several places must agree on it and none can see the others:
66
+ * the plugin that resolves the stub, the agent that hands it down to subagents,
67
+ * and the cancellation path. A pipe rather than a slash, so the caller half
68
+ * cannot forge a repository boundary by containing one.
69
+ *
70
+ * `repo` is undefined only before the first `repo_clone` or `scratch_open` of a
71
+ * session — what a task works in is model-chosen, so there is genuinely nothing
72
+ * to key on until it has chosen. That window resolves to a caller-level
73
+ * workspace, which is never worked in: `beforeCheckout` sets the repository
74
+ * before any git runs, and `scratch_open` sets its sentinel before anything
75
+ * resolves a name.
76
+ *
77
+ * `repo` is not always a repository. A host may pass a sentinel through here as
78
+ * one — a scratchpad is modelled that way, which is what keys it to its own
79
+ * object and its own container. Anything that is not a forge name works, as long
80
+ * as no caller could clone something that collides with it.
81
+ */
82
+ export declare function workspaceName(callerKey: string, repo?: string): string;
83
+ /**
84
+ * `work`, refused once `ms` pass without it settling. The work itself goes on;
85
+ * only the wait for it ends — see {@link READY_DEADLINE_MS}.
86
+ */
87
+ export declare function readyWithin(work: Promise<void>, ms: number): Promise<void>;
88
+ /**
89
+ * Named rather than the anonymous class expression an app would write here.
90
+ *
91
+ * This package emits declarations, and `DurableObject` brings `ctx` and `env` in
92
+ * protected — which makes the mixin's return type unnameable in a `.d.ts` and
93
+ * fails the build with TS4094. A named base gives the emitted type something to
94
+ * refer to. It costs one identifier and is invisible to a consumer.
95
+ */
96
+ declare class WorkspaceContainerHost extends DurableObject<Cloudflare.Env> {
97
+ }
98
+ /**
99
+ * `withWorkspaceContainer` adds one method, `getWorkspaceContainer()`, over
100
+ * `this.ctx.container` — the runtime's own container handle. There is no
101
+ * `@cloudflare/containers` `Container` subclass here and so no `sleepAfter`:
102
+ * idle shutdown is this object's job, and it lands on the wake map with
103
+ * everything else.
104
+ */
105
+ declare const WorkspaceContainerBase: typeof WorkspaceContainerHost & (new (...args: any[]) => WorkspaceContainerHost & {
106
+ getWorkspaceContainer(): import("@cloudflare/computer/backends/container").WorkspaceContainerAPI;
107
+ });
108
+ /**
109
+ * The forge credential, and who a commit made on this side is attributed to.
110
+ *
111
+ * Config rather than an env read, because this class cannot name a consumer's
112
+ * ambient `Env` — it is an interface `wrangler types` generates into *their*
113
+ * app, and a plugin that named it would compile only against the one Worker that
114
+ * happened to spell its bindings that way.
115
+ *
116
+ * It is also the honest direction. These are the repo plugin's secrets, declared
117
+ * by that plugin's `requires.secrets`, and a workspace holds them only because
118
+ * git runs on this side of the container boundary — see
119
+ * {@link file://./git-host.ts} for why it has to.
120
+ */
121
+ export interface WorkspaceGitConfig {
122
+ /**
123
+ * The **name** of the binding holding the forge credential — never the
124
+ * credential itself.
125
+ *
126
+ * `workspaceConfig()` is an ordinary method on the prototype, and on a Durable
127
+ * Object `protected` is a typechecker's opinion rather than a runtime
128
+ * boundary: anything holding the namespace can call it. A config carrying the
129
+ * token would therefore hand it to any caller that asked — exactly what
130
+ * {@link file://./git-host.ts} exists to prevent, since the credential is
131
+ * supposed to be unreachable from anywhere but the moment git authenticates.
132
+ *
133
+ * So the config names the binding and the object reads it. Naming a secret by
134
+ * string is what the plugin contract's `requires.secrets` already does, and it
135
+ * is the only form available here: this package cannot name a consumer's
136
+ * ambient `Env`.
137
+ *
138
+ * An unset binding reads `undefined`, which means an unauthenticated request
139
+ * rather than a throw — that is what lets a workspace with no token still
140
+ * clone a public repository.
141
+ */
142
+ tokenBinding: string;
143
+ /**
144
+ * Who a commit is attributed to — on this side, and in the container.
145
+ *
146
+ * Two things read it: this object's git client, and `./git-identity.ts`,
147
+ * which writes it into the container's system config so a repository the
148
+ * container made for itself is attributed rather than nameless.
149
+ *
150
+ * **The repo plugin is not one of them.** It resolves `RepoConfig.author`
151
+ * independently, with a generic fallback of its own, and nothing in either
152
+ * package checks the two agree — so a commit *can* be attributed differently
153
+ * depending on which side made it, and the only thing preventing that is a
154
+ * consumer answering both from one place. A deployment's fallback for an
155
+ * unset binding belongs where that binding is read; this takes the resolved
156
+ * answer.
157
+ */
158
+ author: {
159
+ name: string;
160
+ email: string;
161
+ };
162
+ }
163
+ /**
164
+ * What one workspace object does not share with the next.
165
+ *
166
+ * Everything else about a workspace is identical between agents, which is why
167
+ * this interface is short and why it is worth having at all: a seam this narrow
168
+ * makes "what is different about this agent's container" a question with a
169
+ * complete answer in one place.
170
+ */
171
+ export interface WorkspaceObjectConfig {
172
+ /**
173
+ * The wrangler Durable Object binding this class is bound as.
174
+ *
175
+ * Not cosmetic and not derivable: `computerd` dials **back** through it. The
176
+ * backend builds the container's loopback from this name plus the object id,
177
+ * so a wrong one produces a container that starts, mounts nothing and fails
178
+ * at the first command with no mention of a binding.
179
+ */
180
+ binding: string;
181
+ /**
182
+ * Where this workspace's container may send traffic, and through what.
183
+ *
184
+ * `direct` is the plain behaviour: the container's own network position.
185
+ * `http-gateway` routes **everything** through a `Fetcher` this Worker
186
+ * supplies, which is what puts the Worker on the model path — see
187
+ * `@dynamicagents/plugins/claude-code`.
188
+ *
189
+ * **Required in practice, and its absence is silent.** It is a policy that
190
+ * defaults to `{ mode: "none" }`, and the backend derives the container's
191
+ * network flag from it. Omit it and the container
192
+ * comes up with no network at all: the workspace mounts, commands run, and
193
+ * the install dies on a registry it cannot reach with nothing naming egress
194
+ * as the cause.
195
+ */
196
+ egress: WorkspaceEgressPolicy;
197
+ /** How this deployment installs dependencies for this agent's checkouts. */
198
+ installPlan: InstallPlan;
199
+ /** Log prefix — `coder-workspace`, `claude-coder-workspace`. */
200
+ label: string;
201
+ /**
202
+ * How long this agent's container stays up after the last command **started**.
203
+ *
204
+ * A per-agent value because the invariant on {@link CONTAINER_IDLE_MS} — it
205
+ * must exceed the longest command the shell allows — is an invariant about the
206
+ * *agent*, and the two differ by a factor of four. The coder's longest command
207
+ * is a tool call; `claude-coder`'s is a whole `claude -p` session that runs
208
+ * detached for its entire timeout.
209
+ *
210
+ * Omit it for the default. Raise it, never lower it, and raise it whenever the
211
+ * agent's longest command grows.
212
+ */
213
+ containerIdleMs?: number;
214
+ /** The forge credential and commit identity — see {@link WorkspaceGitConfig}. */
215
+ git: WorkspaceGitConfig;
216
+ }
217
+ /**
218
+ * One caller's checkout of one repository, and the container that mounts it.
219
+ *
220
+ * Abstract because a Durable Object class takes no constructor arguments, so
221
+ * per-agent configuration cannot arrive that way. {@link workspaceConfig} is the
222
+ * seam, and it is the shape core's own `RecipeSubagentBase.subagentRuntime`
223
+ * uses for exactly the same reason.
224
+ *
225
+ * ## Why `backend` and `#workspace` are lazy
226
+ *
227
+ * **Base class fields run before subclass fields**, so as plain fields they
228
+ * would read `undefined` from any `workspaceConfig()` that touches a subclass
229
+ * field — which `ClaudeCoderWorkspaceDO`'s does, for its credential store.
230
+ * Memoised getters remove the hazard rather than documenting it, which is what
231
+ * lets a subclass implement the seam however it likes.
232
+ */
233
+ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env = Cloudflare.Env> extends WorkspaceContainerBase {
234
+ #private;
235
+ /**
236
+ * This Worker's bindings, as the host's own generated `Env` spells them.
237
+ *
238
+ * Re-declared rather than inherited so a subclass reading a binding of its own
239
+ * — `claude-coder`'s reaches for its credential pool — gets its real type
240
+ * instead of the base's. `declare` because the field is the runtime's; this
241
+ * only narrows what TypeScript believes about it.
242
+ */
243
+ readonly env: TEnv;
244
+ /**
245
+ * Everything this agent's workspace does differently. Called once, lazily.
246
+ *
247
+ * Read through {@link #cfg}, never directly: an implementation may build
248
+ * something real — `claude-coder`'s constructs its egress gateway — and this
249
+ * is consulted on the busiest path in the object.
250
+ */
251
+ protected abstract workspaceConfig(): WorkspaceObjectConfig;
252
+ get backend(): CloudflareContainerBackend;
253
+ /**
254
+ * What `getWorkspace(stub)` calls from outside this object.
255
+ *
256
+ * The one piece of `withWorkspace` reimplemented here — see the file comment
257
+ * for why the mixin is not used. `ready()` first, because the stub is only
258
+ * meaningful once the workspace has opened its store.
259
+ */
260
+ __getWorkspaceStub(): Promise<WorkspaceStub>;
261
+ /**
262
+ * The same workspace, for a caller that will only touch the filesystem.
263
+ *
264
+ * **The container is not started, and that is the whole difference.** The
265
+ * filesystem is this object's own SQLite, so a read or write of it needs no
266
+ * container and an absent one cannot block it. {@link __getWorkspaceStub} adds
267
+ * `#ready()`, and the first command in a fresh container re-pushes the whole
268
+ * tree before it runs, with `timeoutMs` reaching the spawned process and
269
+ * nothing earlier.
270
+ *
271
+ * A stub cannot enforce what the caller does with it: `runtime.exec` on this
272
+ * one would run against a container with no CA. The exec paths take the other
273
+ * method — see `openWorkspaceFs` in `@dynamicagents/plugins/computer`.
274
+ */
275
+ __getWorkspaceFsStub(): Promise<WorkspaceStub>;
276
+ /**
277
+ * Clone, fetch and push — the operations that need the forge token.
278
+ *
279
+ * The work is `./git-host.ts`, which owns why the credential stays on this
280
+ * side of the boundary. What stays here is what only this object can do: the
281
+ * entry-point bookkeeping, and making the workspace current before git reads
282
+ * it.
283
+ */
284
+ gitClone(req: {
285
+ url: string;
286
+ dir: string;
287
+ allowedHosts: string[];
288
+ branch?: string;
289
+ depth?: number;
290
+ }): Promise<RepoGitResult>;
291
+ gitFetch(req: {
292
+ url: string;
293
+ dir: string;
294
+ allowedHosts: string[];
295
+ depth?: number;
296
+ }): Promise<RepoGitResult>;
297
+ gitPush(req: {
298
+ url: string;
299
+ dir: string;
300
+ branch: string;
301
+ allowedHosts: string[];
302
+ }): Promise<RepoGitResult>;
303
+ /**
304
+ * Start this checkout's dependency install, and return without waiting.
305
+ *
306
+ * Called from `repo_clone` through the repo plugin's `afterCheckout` hook, so
307
+ * it runs inside a model turn — see `./install-job.ts` for why the drain's owner
308
+ * is the thing that matters here.
309
+ */
310
+ startInstall(req: {
311
+ dir: string;
312
+ repo?: string;
313
+ }): Promise<InstallState>;
314
+ /**
315
+ * Throw this workspace away if nothing has touched it for `maxIdleMs`.
316
+ *
317
+ * Re-checks the clock rather than trusting the caller: the alarm may have been
318
+ * armed a week ago, and a use since then must win. Safe to call from anywhere
319
+ * for the same reason, which is what lets the agent's cron poke it as a
320
+ * backstop without needing to know anything.
321
+ */
322
+ reclaimIfIdle(maxIdleMs?: number): Promise<{
323
+ reclaimed: boolean;
324
+ idleMs: number;
325
+ bytes: number;
326
+ }>;
327
+ /**
328
+ * Record what was checked out here, and say whether it is actually visible.
329
+ *
330
+ * **The single writer of {@link CHECKOUT_KEY}**, called by the two paths that
331
+ * know a checkout landed: the repo plugin's `afterCheckout` hook, which fires
332
+ * only once a tree is established, and `scratch_open`, which fires once
333
+ * `git init` has exited 0.
334
+ *
335
+ * **Why the checkout is recorded apart from the install**, which call sites
336
+ * point here for rather than restating: an install is conditional and a
337
+ * checkout is not. `resolveInstallCommand` skips a checkout it finds nothing
338
+ * to install in — any without a `package.json` — so a path written only as
339
+ * part of an install is missing exactly there. What that costs is not an
340
+ * optimisation: {@link checkoutDir} is how a delegated session is told where
341
+ * to work, so a repository can clone perfectly, report itself correctly
342
+ * through the repo tools, and never be worked in. The install keeps its own
343
+ * context for the mirror-image reason — what to re-run on a cold container has
344
+ * a different lifetime from what is on disk.
345
+ *
346
+ * Returns the probe as well as the path, so a caller learns in one round trip
347
+ * whether the tree is visible rather than discovering it a delegation later.
348
+ */
349
+ noteCheckout(req: {
350
+ dir: string;
351
+ repo?: string;
352
+ kind: "repo" | "scratch";
353
+ }): Promise<{
354
+ dir: string;
355
+ present: boolean;
356
+ }>;
357
+ /**
358
+ * Where the work is — a directory holding a git repository, or nothing.
359
+ *
360
+ * Two callers depend on that being the meaning rather than "a path somebody
361
+ * wrote down once": a Claude Code session takes it as its cwd, and
362
+ * `discardWorkingTree` runs `git reset --hard` in it. Both want the same
363
+ * question answered, and neither can recover from a confident wrong answer.
364
+ *
365
+ * So it is **probed, not remembered**. A record is where to look; `.git` being
366
+ * there is what makes the answer true. That costs one local read of this
367
+ * object's own SQLite — everything under the checkout is durable, `.git`
368
+ * included — and it is what turns a stale record into `undefined` instead of
369
+ * into a session started in a directory that is no longer a checkout.
370
+ *
371
+ * The record is the only source. A workspace that has never recorded one
372
+ * answers `undefined` until its next checkout does, which `repo_clone` does on
373
+ * any tree it can fetch and reset.
374
+ */
375
+ checkoutDir(): Promise<string | undefined>;
376
+ /**
377
+ * Everything currently true about this workspace that a caller must not assume
378
+ * away — the array `sb_exec`, `sb_write` and `sb_edit` all consult.
379
+ *
380
+ * The policy is not here. `deriveAdvisories` decides which facts matter and
381
+ * how they are worded; this method gathers what only the object can see and
382
+ * hands it over. That split is why a host cannot get the severity of its own
383
+ * workspace wrong.
384
+ *
385
+ * **Nothing that starts a long job belongs on this path**, and the rule is
386
+ * sharper here than anywhere else in the object because every tool call reads
387
+ * it. `startInstall` in particular must never be reached from here: it hands
388
+ * its drain to `ctx.waitUntil`, whose lifetime is the invocation's, and an
389
+ * invocation on this path is a tool call that returns in milliseconds. The
390
+ * drain outlives its owner, dies mid-`npm ci` with "WritableStream RPC stub
391
+ * was disposed without calling close()", and leaves a half-written tree.
392
+ *
393
+ * A missing dependency tree arms an install here, which runs in the alarm,
394
+ * which owns no request and outlives every RPC. Here because a command reads
395
+ * this before its `#ready()` starts a container, so the gate holds it for the
396
+ * install rather than letting it outrun one.
397
+ */
398
+ advisories(): Promise<readonly WorkspaceAdvisory[]>;
399
+ /**
400
+ * `computerd`'s outbound WebSocket upgrade, on its way back in.
401
+ *
402
+ * The container dials the loopback rather than the other way round, so this
403
+ * object is the server for its own container's capnweb session. Everything
404
+ * else on this object is RPC; this is the only HTTP it speaks.
405
+ */
406
+ fetch(request: Request): Promise<Response>;
407
+ /**
408
+ * Every durable wake-up this object has, dispatched from the one alarm.
409
+ *
410
+ * **This must not throw.** The runtime retries a failing alarm handler a
411
+ * bounded number of times and then stops for good — so a throw here would
412
+ * eventually take every *future* wake-up down with it, permanently, and the
413
+ * only symptom is that nothing ever happens again.
414
+ *
415
+ * Nothing else belongs here. Each reason to wake is a registered callback the
416
+ * scheduler dispatches by name, so this method neither matches on keys nor
417
+ * catches per-callback failures — the scheduler retries a failing callback and
418
+ * reports one that fails for good through `onError`. Nor does it sweep for a
419
+ * schedule that neither rescheduled nor cleared itself: a one-shot row is
420
+ * dropped when it runs, so it cannot stay due forever.
421
+ */
422
+ alarm(): Promise<void>;
423
+ }
424
+ export {};