@dynamicagents/plugins 0.11.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +59 -79
  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 +53 -0
  5. package/dist/claude-code/cards.js +272 -0
  6. package/dist/claude-code/config.d.ts +73 -31
  7. package/dist/claude-code/config.js +0 -2
  8. package/dist/claude-code/credentials.d.ts +21 -9
  9. package/dist/claude-code/credentials.js +43 -9
  10. package/dist/claude-code/egress.d.ts +15 -14
  11. package/dist/claude-code/egress.js +51 -14
  12. package/dist/claude-code/events.d.ts +153 -9
  13. package/dist/claude-code/events.js +289 -58
  14. package/dist/claude-code/github.d.ts +39 -0
  15. package/dist/claude-code/github.js +82 -0
  16. package/dist/claude-code/index.d.ts +24 -80
  17. package/dist/claude-code/index.js +19 -234
  18. package/dist/claude-code/model.d.ts +152 -0
  19. package/dist/claude-code/model.js +342 -0
  20. package/dist/claude-code/run.d.ts +283 -92
  21. package/dist/claude-code/run.js +620 -114
  22. package/dist/claude-code/session.d.ts +131 -0
  23. package/dist/claude-code/session.js +285 -0
  24. package/dist/claude-code/spec.d.ts +46 -0
  25. package/dist/claude-code/spec.js +83 -0
  26. package/dist/claude-code/verified.d.ts +15 -0
  27. package/dist/claude-code/verified.js +15 -0
  28. package/dist/computer/context.d.ts +19 -0
  29. package/dist/computer/context.js +222 -0
  30. package/dist/computer/file-lock.d.ts +5 -0
  31. package/dist/computer/file-lock.js +40 -0
  32. package/dist/computer/gate.d.ts +12 -12
  33. package/dist/computer/gate.js +12 -12
  34. package/dist/computer/index.d.ts +57 -255
  35. package/dist/computer/index.js +98 -1039
  36. package/dist/computer/paths.d.ts +47 -22
  37. package/dist/computer/paths.js +64 -49
  38. package/dist/computer/proxy.d.ts +45 -0
  39. package/dist/computer/proxy.js +234 -0
  40. package/dist/computer/render.d.ts +27 -38
  41. package/dist/computer/render.js +32 -65
  42. package/dist/computer/tools-exec.d.ts +3 -0
  43. package/dist/computer/tools-exec.js +268 -0
  44. package/dist/computer/tools-file.d.ts +12 -0
  45. package/dist/computer/tools-file.js +64 -0
  46. package/dist/computer/tools-grep.d.ts +3 -0
  47. package/dist/computer/tools-grep.js +129 -0
  48. package/dist/repo/checkout.d.ts +35 -6
  49. package/dist/repo/checkout.js +64 -6
  50. package/dist/repo/context.d.ts +135 -0
  51. package/dist/repo/context.js +582 -0
  52. package/dist/repo/index.d.ts +79 -57
  53. package/dist/repo/index.js +118 -1112
  54. package/dist/repo/tools-checks.d.ts +3 -0
  55. package/dist/repo/tools-checks.js +112 -0
  56. package/dist/repo/tools-clone.d.ts +4 -0
  57. package/dist/repo/tools-clone.js +192 -0
  58. package/dist/repo/tools-pr.d.ts +14 -0
  59. package/dist/repo/tools-pr.js +196 -0
  60. package/dist/repo/tools-review.d.ts +12 -0
  61. package/dist/repo/tools-review.js +393 -0
  62. package/dist/repo/tools-worktree.d.ts +4 -0
  63. package/dist/repo/tools-worktree.js +305 -0
  64. package/dist/repo/tools-worktrees.d.ts +12 -0
  65. package/dist/repo/tools-worktrees.js +48 -0
  66. package/dist/repo/url.d.ts +2 -2
  67. package/dist/repo/url.js +7 -7
  68. package/dist/scratch/index.d.ts +4 -4
  69. package/dist/scratch/index.js +12 -11
  70. package/dist/{computer → workspace}/advisory.d.ts +33 -20
  71. package/dist/{computer → workspace}/advisory.js +32 -7
  72. package/dist/workspace/ca-trust.d.ts +95 -0
  73. package/dist/workspace/ca-trust.js +312 -0
  74. package/dist/workspace/container-deps.d.ts +54 -0
  75. package/dist/workspace/container-deps.js +224 -0
  76. package/dist/workspace/container-fault.d.ts +38 -0
  77. package/dist/workspace/container-fault.js +73 -0
  78. package/dist/workspace/exec.d.ts +94 -0
  79. package/dist/workspace/exec.js +153 -0
  80. package/dist/workspace/format.d.ts +36 -0
  81. package/dist/workspace/format.js +57 -0
  82. package/dist/workspace/git-host.d.ts +79 -0
  83. package/dist/workspace/git-host.js +226 -0
  84. package/dist/workspace/git-identity.d.ts +57 -0
  85. package/dist/workspace/git-identity.js +147 -0
  86. package/dist/workspace/index.d.ts +43 -47
  87. package/dist/workspace/index.js +52 -112
  88. package/dist/workspace/install-job.d.ts +121 -0
  89. package/dist/workspace/install-job.js +866 -0
  90. package/dist/{computer → workspace}/install.d.ts +15 -19
  91. package/dist/{computer → workspace}/install.js +8 -11
  92. package/dist/workspace/object.d.ts +502 -0
  93. package/dist/workspace/object.js +1427 -0
  94. package/dist/workspace/open.d.ts +95 -0
  95. package/dist/workspace/open.js +63 -0
  96. package/dist/workspace/read.d.ts +16 -0
  97. package/dist/workspace/read.js +22 -0
  98. package/dist/workspace/shell.d.ts +45 -0
  99. package/dist/workspace/shell.js +80 -0
  100. package/dist/workspace/sync.d.ts +111 -0
  101. package/dist/workspace/sync.js +124 -0
  102. package/dist/workspace/wake.d.ts +19 -0
  103. package/dist/workspace/wake.js +10 -0
  104. package/package.json +28 -42
  105. package/dist/arc-agi/analysis.d.ts +0 -336
  106. package/dist/arc-agi/analysis.js +0 -829
  107. package/dist/arc-agi/client.d.ts +0 -82
  108. package/dist/arc-agi/client.js +0 -113
  109. package/dist/arc-agi/game-tools.d.ts +0 -25
  110. package/dist/arc-agi/game-tools.js +0 -26
  111. package/dist/arc-agi/index.d.ts +0 -42
  112. package/dist/arc-agi/index.js +0 -175
  113. package/dist/arc-agi/main-agent.d.ts +0 -27
  114. package/dist/arc-agi/main-agent.js +0 -65
  115. package/dist/arc-agi/recipe.d.ts +0 -51
  116. package/dist/arc-agi/recipe.js +0 -100
  117. package/dist/arc-agi/scorecard.d.ts +0 -118
  118. package/dist/arc-agi/scorecard.js +0 -163
  119. package/dist/arc-agi/soul.d.ts +0 -40
  120. package/dist/arc-agi/soul.js +0 -90
  121. package/dist/arc-agi/store.d.ts +0 -154
  122. package/dist/arc-agi/store.js +0 -216
  123. package/dist/arc-agi/tools.d.ts +0 -36
  124. package/dist/arc-agi/tools.js +0 -576
  125. package/dist/arc-agi/types.d.ts +0 -212
  126. package/dist/arc-agi/types.js +0 -19
  127. package/dist/claude-code/recipe.d.ts +0 -53
  128. package/dist/claude-code/recipe.js +0 -100
  129. package/dist/computer/read.d.ts +0 -137
  130. package/dist/computer/read.js +0 -203
  131. package/dist/recall/index.d.ts +0 -99
  132. package/dist/recall/index.js +0 -171
  133. package/dist/triage/index.d.ts +0 -355
  134. package/dist/triage/index.js +0 -302
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * ## Why this exists at all
12
12
  *
13
- * The alternative is a sentence in the subagent's soul — "install dependencies
13
+ * The alternative is a sentence in the sub-agent's soul — "install dependencies
14
14
  * before you do anything else" — and a standard with no command attached is one a
15
15
  * model satisfies with whichever check is cheapest: edit a README, run
16
16
  * `prettier --check` on that one file, report the change verified, and never run
@@ -36,25 +36,24 @@ export interface InstallProbe {
36
36
  * Where the install has got to, as the host reports it and the tools read it.
37
37
  *
38
38
  * Declared here rather than in the host because both sides depend on it and
39
- * neither owns the other: the Durable Object runs the job, and `sb_exec` refuses
39
+ * neither owns the other: the Durable Object runs the job, and `bash` refuses
40
40
  * to run against a tree that is still being built. A shape they agreed on
41
41
  * informally would drift, and the drift would show up as a shell command running
42
42
  * halfway through an `npm ci`.
43
43
  *
44
- * ## Why it is core's `JobState` with one field intersected in
44
+ * ## Why it is the host's `JobState` with one field intersected in
45
45
  *
46
- * Core owns the *choreography* around a job a Durable Object drives through its
47
- * alarm — arming, the single-flight guard, the staleness bound, the generation
48
- * marker a drain checks before it writes. This package owns what an install
46
+ * Core's `/job` owns the *choreography* around a job a Durable Object drives
47
+ * through its alarm — arming, the single-flight guard, the staleness bound, the
48
+ * generation a drain writes under. This package owns what an install
49
49
  * specifically **is**: which package manager, which lockfile, what counts as
50
50
  * skippable. Splitting them along that line is what lets a second job on the
51
51
  * same object — a coding-agent run, say — reuse every rule without inheriting
52
52
  * `npm ci`.
53
53
  *
54
54
  * `JobState` intersects `TExtra` into its `running`/`done`/`failed` variants
55
- * instead of nesting it under `meta`, so this alias is byte-identical to the
56
- * hand-written union it replaced. Every reader still spells `status.command` at
57
- * the top level and no call site moved.
55
+ * rather than nesting it under `meta`, so a reader spells `status.command` at
56
+ * the top level.
58
57
  */
59
58
  export type InstallState = JobState<{
60
59
  command: string;
@@ -137,8 +136,7 @@ export declare function resolveInstallCommand(fs: InstallProbe, dir: string, pla
137
136
  *
138
137
  * Content, not mtime: a `git fetch && reset --hard` onto a new commit rewrites
139
138
  * these files whether or not their dependencies changed, and re-installing on
140
- * every commit would throw away the thing that makes a warm container worth
141
- * having.
139
+ * every commit would throw away a tree that is already correct.
142
140
  *
143
141
  * ## Both files, not just the lockfile
144
142
  *
@@ -148,7 +146,7 @@ export declare function resolveInstallCommand(fs: InstallProbe, dir: string, pla
148
146
  * from `pnpm@9` to `pnpm@10`, or add a dependency without regenerating the lock,
149
147
  * and every one of those changes what an install produces while leaving the
150
148
  * lockfile byte-identical. Hashing the lockfile alone calls that a match, skips
151
- * the install, and hands the subagent a `node_modules` that is quietly wrong —
149
+ * the install, and hands the agent a `node_modules` that is quietly wrong —
152
150
  * which surfaces as a missing module in a build, three tool calls later, with
153
151
  * nothing pointing back at the install that never ran.
154
152
  *
@@ -171,8 +169,8 @@ export declare function resolveInstallCommand(fs: InstallProbe, dir: string, pla
171
169
  * {@link INSTALL_CONFIG_FILES} is hashed too, for the same reason `package.json`
172
170
  * is: a lockfile says which versions, and `.npmrc` says how they are laid out.
173
171
  * Flipping pnpm's `node-linker` to `hoisted` produces a different
174
- * `node_modules` from a byte-identical lockfile, and a warm container would
175
- * otherwise skip the reinstall that change requires.
172
+ * `node_modules` from a byte-identical lockfile, and a matching fingerprint
173
+ * would otherwise skip the reinstall that change requires.
176
174
  *
177
175
  * The residual is worth stating because it cannot be closed here: an override
178
176
  * that *builds* — `npm ci && npm run build`, the documented example — is not
@@ -184,10 +182,8 @@ export declare function resolveInstallCommand(fs: InstallProbe, dir: string, pla
184
182
  * ## This is only half of the skip condition
185
183
  *
186
184
  * A matching fingerprint means "the same install would produce the same tree".
187
- * It does **not** mean the tree is there. `node_modules` lives in the container
188
- * and dies with it, while this fingerprint is stored in the Durable Object and
189
- * does not — so on a cold container the two disagree, and a caller that skips on
190
- * the fingerprint alone skips the install that the empty tree needs most. The
191
- * caller must also confirm `node_modules` is actually present.
185
+ * It does **not** mean the tree is there: it is on the container's disk, and
186
+ * goes with the container. The caller also needs a record that this container
187
+ * finished it — see `./install-job.ts`.
192
188
  */
193
189
  export declare function installFingerprint(fs: InstallProbe, dir: string, resolution: InstallResolution): Promise<string | null>;
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * ## Why this exists at all
12
12
  *
13
- * The alternative is a sentence in the subagent's soul — "install dependencies
13
+ * The alternative is a sentence in the sub-agent's soul — "install dependencies
14
14
  * before you do anything else" — and a standard with no command attached is one a
15
15
  * model satisfies with whichever check is cheapest: edit a README, run
16
16
  * `prettier --check` on that one file, report the change verified, and never run
@@ -211,8 +211,7 @@ async function firstPresent(fs, dir, names) {
211
211
  *
212
212
  * Content, not mtime: a `git fetch && reset --hard` onto a new commit rewrites
213
213
  * these files whether or not their dependencies changed, and re-installing on
214
- * every commit would throw away the thing that makes a warm container worth
215
- * having.
214
+ * every commit would throw away a tree that is already correct.
216
215
  *
217
216
  * ## Both files, not just the lockfile
218
217
  *
@@ -222,7 +221,7 @@ async function firstPresent(fs, dir, names) {
222
221
  * from `pnpm@9` to `pnpm@10`, or add a dependency without regenerating the lock,
223
222
  * and every one of those changes what an install produces while leaving the
224
223
  * lockfile byte-identical. Hashing the lockfile alone calls that a match, skips
225
- * the install, and hands the subagent a `node_modules` that is quietly wrong —
224
+ * the install, and hands the agent a `node_modules` that is quietly wrong —
226
225
  * which surfaces as a missing module in a build, three tool calls later, with
227
226
  * nothing pointing back at the install that never ran.
228
227
  *
@@ -245,8 +244,8 @@ async function firstPresent(fs, dir, names) {
245
244
  * {@link INSTALL_CONFIG_FILES} is hashed too, for the same reason `package.json`
246
245
  * is: a lockfile says which versions, and `.npmrc` says how they are laid out.
247
246
  * Flipping pnpm's `node-linker` to `hoisted` produces a different
248
- * `node_modules` from a byte-identical lockfile, and a warm container would
249
- * otherwise skip the reinstall that change requires.
247
+ * `node_modules` from a byte-identical lockfile, and a matching fingerprint
248
+ * would otherwise skip the reinstall that change requires.
250
249
  *
251
250
  * The residual is worth stating because it cannot be closed here: an override
252
251
  * that *builds* — `npm ci && npm run build`, the documented example — is not
@@ -258,11 +257,9 @@ async function firstPresent(fs, dir, names) {
258
257
  * ## This is only half of the skip condition
259
258
  *
260
259
  * A matching fingerprint means "the same install would produce the same tree".
261
- * It does **not** mean the tree is there. `node_modules` lives in the container
262
- * and dies with it, while this fingerprint is stored in the Durable Object and
263
- * does not — so on a cold container the two disagree, and a caller that skips on
264
- * the fingerprint alone skips the install that the empty tree needs most. The
265
- * caller must also confirm `node_modules` is actually present.
260
+ * It does **not** mean the tree is there: it is on the container's disk, and
261
+ * goes with the container. The caller also needs a record that this container
262
+ * finished it — see `./install-job.ts`.
266
263
  */
267
264
  export async function installFingerprint(fs, dir, resolution) {
268
265
  if (resolution.kind !== "run")
@@ -0,0 +1,502 @@
1
+ import { DurableObject } from "cloudflare:workers";
2
+ import { type WorkspaceEgressPolicy, type WorkspaceStub } from "@cloudflare/computer";
3
+ import { ContainerBackend, type ContainerInstanceSize } 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 `bash` 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 `@cloudflare/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 GitHub 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 sub-agents,
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 GitHub 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
+ * How long a workspace survives without being used.
85
+ *
86
+ * A Durable Object is **never** reclaimed by the platform, and a namespace
87
+ * cannot be enumerated from a Worker, so nothing else is coming to clean up.
88
+ * Source-only workspaces are small (6.3 MB for slack-gatekeeper), which makes
89
+ * this hygiene rather than cost control — but unbounded hygiene is still
90
+ * unbounded.
91
+ *
92
+ * Exported because a host keeping work in a workspace has to say how long that
93
+ * work survives untouched.
94
+ */
95
+ export declare const IDLE_RECLAIM_MS: number;
96
+ /**
97
+ * `work`, refused once `ms` pass without it settling. The work itself goes on;
98
+ * only the wait for it ends — see {@link READY_DEADLINE_MS}.
99
+ */
100
+ export declare function readyWithin(work: Promise<void>, ms: number): Promise<void>;
101
+ /**
102
+ * Named rather than the anonymous class expression an app would write here.
103
+ *
104
+ * This package emits declarations, and `DurableObject` brings `ctx` and `env` in
105
+ * protected — which makes the mixin's return type unnameable in a `.d.ts` and
106
+ * fails the build with TS4094. A named base gives the emitted type something to
107
+ * refer to. It costs one identifier and is invisible to a consumer.
108
+ */
109
+ declare class WorkspaceContainerHost extends DurableObject<Cloudflare.Env> {
110
+ }
111
+ /**
112
+ * `withWorkspaceContainer` adds one method, `getWorkspaceContainer()`, over
113
+ * `this.ctx.container` — the runtime's own container handle, started with the
114
+ * image and instance size this object asks for. Stopping it is this object's
115
+ * job — see {@link CONTAINER_IDLE_MS}.
116
+ */
117
+ declare const WorkspaceContainerBase: typeof WorkspaceContainerHost & (new (...args: any[]) => WorkspaceContainerHost & {
118
+ getWorkspaceContainer(): import("@cloudflare/computer/backends/container").WorkspaceContainerAPI;
119
+ });
120
+ /**
121
+ * The GitHub credential, and who a commit made on this side is attributed to.
122
+ *
123
+ * Config rather than an env read, because this class cannot name a consumer's
124
+ * ambient `Env` — it is an interface `wrangler types` generates into *their*
125
+ * app, and a plugin that named it would compile only against the one Worker that
126
+ * happened to spell its bindings that way.
127
+ *
128
+ * It is also the honest direction. These are the repo plugin's secrets, declared
129
+ * by that plugin's `requires.secrets`, and a workspace holds them only because
130
+ * git runs on this side of the container boundary — see
131
+ * {@link file://./git-host.ts} for why it has to.
132
+ */
133
+ export interface WorkspaceGitConfig {
134
+ /**
135
+ * The **name** of the binding holding the GitHub credential — never the
136
+ * credential itself.
137
+ *
138
+ * `workspaceConfig()` is an ordinary method on the prototype, and on a Durable
139
+ * Object `protected` is a typechecker's opinion rather than a runtime
140
+ * boundary: anything holding the namespace can call it. A config carrying the
141
+ * token would therefore hand it to any caller that asked — exactly what
142
+ * {@link file://./git-host.ts} exists to prevent, since the credential is
143
+ * supposed to be unreachable from anywhere but the moment git authenticates.
144
+ *
145
+ * So the config names the binding and the object reads it. Naming a secret by
146
+ * string is what the plugin contract's `requires.secrets` already does, and it
147
+ * is the only form available here: this package cannot name a consumer's
148
+ * ambient `Env`.
149
+ *
150
+ * An unset binding reads `undefined`, which means an unauthenticated request
151
+ * rather than a throw — that is what lets a workspace with no token still
152
+ * clone a public repository.
153
+ */
154
+ tokenBinding: string;
155
+ /**
156
+ * Who a commit is attributed to — on this side, and in the container.
157
+ *
158
+ * Two things read it: this object's git client, and `./git-identity.ts`,
159
+ * which writes it into the container's system config so a repository the
160
+ * container made for itself is attributed rather than nameless.
161
+ *
162
+ * **The repo plugin is not one of them.** It resolves `RepoConfig.author`
163
+ * independently, with a generic fallback of its own, and nothing in either
164
+ * package checks the two agree — so a commit *can* be attributed differently
165
+ * depending on which side made it, and the only thing preventing that is a
166
+ * consumer answering both from one place. A deployment's fallback for an
167
+ * unset binding belongs where that binding is read; this takes the resolved
168
+ * answer.
169
+ */
170
+ author: {
171
+ name: string;
172
+ email: string;
173
+ };
174
+ }
175
+ /**
176
+ * What one workspace object does not share with the next.
177
+ *
178
+ * Everything else about a workspace is identical between agents, which is why
179
+ * this interface is short and why it is worth having at all: a seam this narrow
180
+ * makes "what is different about this agent's container" a question with a
181
+ * complete answer in one place.
182
+ */
183
+ export interface WorkspaceObjectConfig {
184
+ /**
185
+ * The wrangler Durable Object binding this class is bound as.
186
+ *
187
+ * Not cosmetic and not derivable: `computerd` dials **back** through it. The
188
+ * backend builds the container's loopback from this name plus the object id,
189
+ * so a wrong one produces a container that starts, mounts nothing and fails
190
+ * at the first command with no mention of a binding.
191
+ */
192
+ binding: string;
193
+ /**
194
+ * Where this workspace's container may send traffic, and through what.
195
+ *
196
+ * `direct` is the plain behaviour: the container's own network position.
197
+ * `http-gateway` routes **everything** through a `Fetcher` this Worker
198
+ * supplies, which is what puts the Worker on the model path — see
199
+ * `@dynamicagents/plugins/claude-code`.
200
+ *
201
+ * **Required in practice, and its absence is silent.** It is a policy that
202
+ * defaults to `{ mode: "none" }`, and the backend derives the container's
203
+ * network flag from it. Omit it and the container
204
+ * comes up with no network at all: the workspace mounts, commands run, and
205
+ * the install dies on a registry it cannot reach with nothing naming egress
206
+ * as the cause.
207
+ */
208
+ egress: WorkspaceEgressPolicy;
209
+ /** How this deployment installs dependencies for this agent's checkouts. */
210
+ installPlan: InstallPlan;
211
+ /** Log prefix — `coding-workspace`, `claude-coordinator-workspace`. */
212
+ label: string;
213
+ /**
214
+ * How long this agent's container stays up after the last command **started**.
215
+ *
216
+ * A per-agent value because the invariant on {@link CONTAINER_IDLE_MS} — it
217
+ * must exceed the longest command the shell allows — is an invariant about the
218
+ * *agent*, and the two differ by a factor of four. The coder's longest command
219
+ * is a tool call; `claude-coordinator`'s is a whole `claude -p` session that runs
220
+ * detached for its entire timeout.
221
+ *
222
+ * Omit it for the default. Raise it, never lower it, and raise it whenever the
223
+ * agent's longest command grows.
224
+ */
225
+ containerIdleMs?: number;
226
+ /**
227
+ * The size this agent's container starts at — a tier such as `standard-2`, or
228
+ * `{ vcpu, memoryMib, diskMb }`.
229
+ *
230
+ * Requested on every start, and part of what a running container is matched
231
+ * against: change it and the next connect replaces a container started at the
232
+ * old size. Which size, and why there is no default: {@link file://./README.md}.
233
+ */
234
+ instance: ContainerInstanceSize;
235
+ /** The GitHub credential and commit identity — see {@link WorkspaceGitConfig}. */
236
+ git: WorkspaceGitConfig;
237
+ }
238
+ /**
239
+ * One caller's checkout of one repository, and the container that mounts it.
240
+ *
241
+ * Abstract because a Durable Object class takes no constructor arguments, so
242
+ * per-agent configuration cannot arrive that way. {@link workspaceConfig} is the
243
+ * seam, for the same reason Think's own hooks are methods rather than options.
244
+ *
245
+ * ## Why `backend` and `#workspace` are lazy
246
+ *
247
+ * **Base class fields run before subclass fields**, so as plain fields they
248
+ * would read `undefined` from any `workspaceConfig()` that touches a subclass
249
+ * field — which `ClaudeCoordinatorWorkspace`'s does, for its credential store.
250
+ * Memoised getters remove the hazard rather than documenting it, which is what
251
+ * lets a subclass implement the seam however it likes.
252
+ */
253
+ export declare abstract class WorkspaceObjectBase<TEnv extends Cloudflare.Env = Cloudflare.Env> extends WorkspaceContainerBase {
254
+ #private;
255
+ /**
256
+ * This Worker's bindings, as the host's own generated `Env` spells them.
257
+ *
258
+ * Re-declared rather than inherited so a subclass reading a binding of its own
259
+ * — `claude-coordinator`'s reaches for its credential pool — gets its real type
260
+ * instead of the base's. `declare` because the field is the runtime's; this
261
+ * only narrows what TypeScript believes about it.
262
+ */
263
+ readonly env: TEnv;
264
+ /**
265
+ * Everything this agent's workspace does differently. Called once, lazily.
266
+ *
267
+ * Read through {@link #cfg}, never directly: an implementation may build
268
+ * something real — `claude-coordinator`'s constructs its egress gateway — and this
269
+ * is consulted on the busiest path in the object.
270
+ */
271
+ protected abstract workspaceConfig(): WorkspaceObjectConfig;
272
+ get backend(): ContainerBackend;
273
+ /**
274
+ * What `getWorkspace(stub)` calls from outside this object.
275
+ *
276
+ * The one piece of `withWorkspace` reimplemented here — see the file comment
277
+ * for why the mixin is not used. `ready()` first, because the stub is only
278
+ * meaningful once the workspace has opened its store.
279
+ */
280
+ __getWorkspaceStub(): Promise<WorkspaceStub>;
281
+ /**
282
+ * The same workspace, for a caller that will only touch the filesystem.
283
+ *
284
+ * **The container is not started, and that is the whole difference.** The
285
+ * filesystem is this object's own SQLite, so a read or write of it needs no
286
+ * container and an absent one cannot block it. {@link __getWorkspaceStub} adds
287
+ * `#ready()`, and the first command in a fresh container re-pushes the whole
288
+ * tree before it runs, with `timeoutMs` reaching the spawned process and
289
+ * nothing earlier.
290
+ *
291
+ * A stub cannot enforce what the caller does with it: `runtime.exec` on this
292
+ * one would run against a container with no CA. The exec paths take the other
293
+ * method — see `openWorkspaceFs` in `./open.ts`.
294
+ */
295
+ __getWorkspaceFsStub(): Promise<WorkspaceStub>;
296
+ /**
297
+ * Clone, fetch and push — the operations that need the GitHub token.
298
+ *
299
+ * The work is `./git-host.ts`, which owns why the credential stays on this
300
+ * side of the boundary. What stays here is what only this object can do: the
301
+ * entry-point bookkeeping, and making the workspace current before git reads
302
+ * it.
303
+ */
304
+ gitClone(req: {
305
+ url: string;
306
+ dir: string;
307
+ allowedHosts: string[];
308
+ branch?: string;
309
+ depth?: number;
310
+ }): Promise<RepoGitResult>;
311
+ gitFetch(req: {
312
+ url: string;
313
+ dir: string;
314
+ allowedHosts: string[];
315
+ depth?: number;
316
+ }): Promise<RepoGitResult>;
317
+ gitPush(req: {
318
+ url: string;
319
+ dir: string;
320
+ branch: string;
321
+ allowedHosts: string[];
322
+ }): Promise<RepoGitResult>;
323
+ /**
324
+ * A checkout's `origin`, from this object's storage. Starts no container and
325
+ * moves no deadline, like {@link checkoutDir}: a pull request polled for its
326
+ * review is not the workspace in use.
327
+ */
328
+ gitOrigin(dir: string): Promise<string | undefined>;
329
+ /**
330
+ * Start this checkout's dependency install, and return without waiting.
331
+ *
332
+ * Called from `repo_clone` through the repo plugin's `afterCheckout` hook, so
333
+ * it runs inside a model turn — see `./install-job.ts` for why the drain's owner
334
+ * is the thing that matters here.
335
+ */
336
+ startInstall(req: {
337
+ dir: string;
338
+ repo?: string;
339
+ }): Promise<InstallState>;
340
+ /**
341
+ * Stop the container, and **keep everything on disk**.
342
+ *
343
+ * The counterpart to {@link reclaimIfIdle}, and the difference is the whole
344
+ * reason both exist. A reclaim is for a workspace nobody wants again: it empties
345
+ * storage, so the checkout and the dependency tree go with the container. This is
346
+ * for a workspace that will be worked in again — the next task on the same
347
+ * repository has to skip the clone and the install, which is the cost the whole
348
+ * design is arranged around.
349
+ *
350
+ * **Why a caller needs this at all.** The idle deadline would eventually do it,
351
+ * but it cannot be tuned down to meet a cost target: `containerIdleMs` must
352
+ * exceed the longest command the agent allows, and a container that outlives its
353
+ * work by that much bills for the difference. So a host that knows the work is
354
+ * finished says so, and the deadline goes back to being a backstop for the case
355
+ * where nothing got to say anything.
356
+ *
357
+ * Deliberately does **not** touch: `#touch()` would push the very deadline this
358
+ * is standing in for.
359
+ *
360
+ * `released: false` means the container is still up and the standing deadline
361
+ * still owns it — an install is running, or a pull is still moving blocks. Both
362
+ * resolve themselves: the drain re-arms the deadline to come back and finish,
363
+ * which is the same path `#onContainerIdle` takes. Under a {@link hold} the
364
+ * release is deferred instead, to the last `unhold`.
365
+ */
366
+ releaseContainer(): Promise<{
367
+ released: boolean;
368
+ }>;
369
+ /**
370
+ * Keep the container up for work this object cannot see, until `untilMs` or
371
+ * {@link unhold}.
372
+ *
373
+ * A Claude Code session a host starts here runs detached: nothing calls in
374
+ * while it works, and the runtime cannot list
375
+ * what is running. So a host done with the workspace cannot tell that the
376
+ * container is not, and {@link releaseContainer} would stop the session with
377
+ * it. A hold is the host saying so. It defers a release; it is not use, so it
378
+ * does not touch, and the idle deadline already outlasts a session.
379
+ *
380
+ * `untilMs` bounds a hold whose `unhold` never comes — a run cut between its
381
+ * start and its end. Past it the hold is ignored.
382
+ */
383
+ hold(key: string, untilMs: number): Promise<void>;
384
+ /**
385
+ * The work a {@link hold} stood for is over. When it was the last hold, a
386
+ * release asked for while it stood happens now — unless the workspace was
387
+ * used since, in which case the next release is asked for by whoever used it.
388
+ */
389
+ unhold(key: string): Promise<{
390
+ released: boolean;
391
+ }>;
392
+ /**
393
+ * Throw this workspace away if nothing has touched it for `maxIdleMs`.
394
+ *
395
+ * Re-checks the clock rather than trusting the caller: the alarm may have been
396
+ * armed a week ago, and a use since then must win. Safe to call from anywhere
397
+ * for the same reason, which is what lets the agent's cron poke it as a
398
+ * backstop without needing to know anything.
399
+ */
400
+ reclaimIfIdle(maxIdleMs?: number): Promise<{
401
+ reclaimed: boolean;
402
+ idleMs: number;
403
+ bytes: number;
404
+ }>;
405
+ /**
406
+ * Record what was checked out here, and say whether it is actually visible.
407
+ *
408
+ * **The single writer of {@link CHECKOUT_KEY}**, called by the two paths that
409
+ * know a checkout landed: the repo plugin's `afterCheckout` hook, which fires
410
+ * only once a tree is established, and `scratch_open`, which fires once
411
+ * `git init` has exited 0.
412
+ *
413
+ * **Why the checkout is recorded apart from the install**, which call sites
414
+ * point here for rather than restating: an install is conditional and a
415
+ * checkout is not. `resolveInstallCommand` skips a checkout it finds nothing
416
+ * to install in — any without a `package.json` — so a path written only as
417
+ * part of an install is missing exactly there. What that costs is not an
418
+ * optimisation: {@link checkoutDir} is how a delegated session is told where
419
+ * to work, so a repository can clone perfectly, report itself correctly
420
+ * through the repo tools, and never be worked in. The install keeps its own
421
+ * context for the mirror-image reason — what to re-run on a cold container has
422
+ * a different lifetime from what is on disk.
423
+ *
424
+ * Returns the probe as well as the path, so a caller learns in one round trip
425
+ * whether the tree is visible rather than discovering it a delegation later.
426
+ */
427
+ noteCheckout(req: {
428
+ dir: string;
429
+ repo?: string;
430
+ kind: "repo" | "scratch";
431
+ }): Promise<{
432
+ dir: string;
433
+ present: boolean;
434
+ }>;
435
+ /**
436
+ * Where the work is — a directory holding a git repository, or nothing.
437
+ *
438
+ * Two callers depend on that being the meaning rather than "a path somebody
439
+ * wrote down once": a Claude Code session takes it as its cwd, and
440
+ * `discardWorkingTree` runs `git reset --hard` in it. Both want the same
441
+ * question answered, and neither can recover from a confident wrong answer.
442
+ *
443
+ * So it is **probed, not remembered**. A record is where to look; `.git` being
444
+ * there is what makes the answer true. That costs one local read of this
445
+ * object's own SQLite — everything under the checkout is durable, `.git`
446
+ * included — and it is what turns a stale record into `undefined` instead of
447
+ * into a session started in a directory that is no longer a checkout.
448
+ *
449
+ * The record is the only source. A workspace that has never recorded one
450
+ * answers `undefined` until its next checkout does, which `repo_clone` does on
451
+ * any tree it can fetch and reset.
452
+ */
453
+ checkoutDir(): Promise<string | undefined>;
454
+ /**
455
+ * Everything currently true about this workspace that a caller must not assume
456
+ * away — the array `bash`, `edit` and the workspace's writes all consult.
457
+ *
458
+ * The policy is not here. `deriveAdvisories` decides which facts matter and
459
+ * how they are worded; this method gathers what only the object can see and
460
+ * hands it over. That split is why a host cannot get the severity of its own
461
+ * workspace wrong.
462
+ *
463
+ * **Nothing that starts a long job belongs on this path**, and the rule is
464
+ * sharper here than anywhere else in the object because every tool call reads
465
+ * it. `startInstall` in particular must never be reached from here: it hands
466
+ * its drain to `ctx.waitUntil`, whose lifetime is the invocation's, and an
467
+ * invocation on this path is a tool call that returns in milliseconds. The
468
+ * drain outlives its owner, dies mid-`npm ci` with "WritableStream RPC stub
469
+ * was disposed without calling close()", and leaves a half-written tree.
470
+ *
471
+ * A missing dependency tree arms an install here, which runs in the alarm,
472
+ * which owns no request and outlives every RPC. Here because a command reads
473
+ * this before its `#ready()` starts a container, so the gate holds it for the
474
+ * install rather than letting it outrun one.
475
+ */
476
+ advisories(): Promise<readonly WorkspaceAdvisory[]>;
477
+ /**
478
+ * `computerd`'s outbound WebSocket upgrade, on its way back in.
479
+ *
480
+ * The container dials the loopback rather than the other way round, so this
481
+ * object is the server for its own container's capnweb session. Everything
482
+ * else on this object is RPC; this is the only HTTP it speaks.
483
+ */
484
+ fetch(request: Request): Promise<Response>;
485
+ /**
486
+ * Every durable wake-up this object has, dispatched from the one alarm.
487
+ *
488
+ * **This must not throw.** The runtime retries a failing alarm handler a
489
+ * bounded number of times and then stops for good — so a throw here would
490
+ * eventually take every *future* wake-up down with it, permanently, and the
491
+ * only symptom is that nothing ever happens again.
492
+ *
493
+ * Nothing else belongs here. Each reason to wake is a registered callback the
494
+ * scheduler dispatches by name, so this method neither matches on keys nor
495
+ * catches per-callback failures — the scheduler retries a failing callback and
496
+ * reports one that fails for good through `onError`. Nor does it sweep for a
497
+ * schedule that neither rescheduled nor cleared itself: a one-shot row is
498
+ * dropped when it runs, so it cannot stay due forever.
499
+ */
500
+ alarm(): Promise<void>;
501
+ }
502
+ export {};