@dynamicagents/plugins 0.14.0 → 0.15.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 (141) hide show
  1. package/README.md +14 -14
  2. package/dist/claude-code/cards.d.ts +2 -0
  3. package/dist/claude-code/cards.js +11 -0
  4. package/dist/claude-code/config.d.ts +10 -83
  5. package/dist/claude-code/config.js +0 -2
  6. package/dist/claude-code/credentials.d.ts +32 -50
  7. package/dist/claude-code/credentials.js +115 -74
  8. package/dist/claude-code/daemon/index.d.ts +24 -0
  9. package/dist/claude-code/daemon/index.js +203 -0
  10. package/dist/claude-code/egress.d.ts +35 -117
  11. package/dist/claude-code/egress.js +80 -325
  12. package/dist/claude-code/events.d.ts +14 -17
  13. package/dist/claude-code/events.js +19 -15
  14. package/dist/claude-code/harness-wire.d.ts +45 -0
  15. package/dist/claude-code/harness-wire.js +47 -0
  16. package/dist/claude-code/index.d.ts +19 -29
  17. package/dist/claude-code/index.js +17 -25
  18. package/dist/claude-code/language-model.d.ts +19 -0
  19. package/dist/claude-code/language-model.js +71 -0
  20. package/dist/claude-code/model.d.ts +77 -70
  21. package/dist/claude-code/model.js +327 -327
  22. package/dist/claude-code/placeholder.d.ts +12 -0
  23. package/dist/claude-code/placeholder.js +12 -0
  24. package/dist/claude-code/spec.d.ts +4 -4
  25. package/dist/claude-code/spec.js +4 -4
  26. package/dist/computer/client.d.ts +88 -0
  27. package/dist/computer/client.js +6 -0
  28. package/dist/computer/context.d.ts +7 -7
  29. package/dist/computer/context.js +14 -18
  30. package/dist/computer/gate.d.ts +4 -23
  31. package/dist/computer/gate.js +1 -37
  32. package/dist/computer/index.d.ts +44 -43
  33. package/dist/computer/index.js +29 -50
  34. package/dist/computer/paths.d.ts +5 -27
  35. package/dist/computer/paths.js +7 -30
  36. package/dist/computer/proxy.d.ts +17 -31
  37. package/dist/computer/proxy.js +9 -44
  38. package/dist/computer/render.d.ts +3 -40
  39. package/dist/computer/render.js +3 -36
  40. package/dist/computer/sandbox.d.ts +55 -0
  41. package/dist/computer/sandbox.js +236 -0
  42. package/dist/computer/shell.d.ts +36 -0
  43. package/dist/computer/shell.js +41 -0
  44. package/dist/computer/tools-exec.js +3 -9
  45. package/dist/computer/tools-file.d.ts +1 -1
  46. package/dist/computer/tools-file.js +1 -1
  47. package/dist/{repo → repo-github}/checkout.d.ts +3 -4
  48. package/dist/{repo → repo-github}/checkout.js +3 -4
  49. package/dist/{repo → repo-github}/context.d.ts +14 -32
  50. package/dist/{repo → repo-github}/context.js +46 -81
  51. package/dist/{repo → repo-github}/index.d.ts +30 -13
  52. package/dist/{repo → repo-github}/index.js +25 -6
  53. package/dist/repo-github/mcp.d.ts +95 -0
  54. package/dist/repo-github/mcp.js +107 -0
  55. package/dist/repo-github/sandbox-egress.d.ts +68 -0
  56. package/dist/repo-github/sandbox-egress.js +272 -0
  57. package/dist/repo-github/sandbox-git.d.ts +30 -0
  58. package/dist/repo-github/sandbox-git.js +113 -0
  59. package/dist/{repo → repo-github}/tools-checks.js +43 -4
  60. package/dist/{repo → repo-github}/tools-clone.js +2 -21
  61. package/dist/{repo → repo-github}/tools-pr.d.ts +0 -1
  62. package/dist/{repo → repo-github}/tools-pr.js +85 -20
  63. package/dist/{repo → repo-github}/tools-review.d.ts +6 -0
  64. package/dist/{repo → repo-github}/tools-review.js +62 -176
  65. package/dist/{repo → repo-github}/url.d.ts +11 -0
  66. package/dist/{repo → repo-github}/url.js +12 -1
  67. package/dist/{workspace → sandbox}/advisory.d.ts +16 -35
  68. package/dist/{workspace → sandbox}/advisory.js +2 -29
  69. package/dist/sandbox/agent-exec.d.ts +31 -0
  70. package/dist/sandbox/agent-exec.js +62 -0
  71. package/dist/sandbox/ca-trust.d.ts +9 -0
  72. package/dist/sandbox/ca-trust.js +21 -0
  73. package/dist/sandbox/constants.d.ts +22 -0
  74. package/dist/sandbox/constants.js +22 -0
  75. package/dist/sandbox/daemon/composite.d.ts +9 -0
  76. package/dist/sandbox/daemon/composite.js +35 -0
  77. package/dist/sandbox/daemon/index.d.ts +47 -0
  78. package/dist/sandbox/daemon/index.js +116 -0
  79. package/dist/sandbox/egress.d.ts +81 -0
  80. package/dist/sandbox/egress.js +109 -0
  81. package/dist/sandbox/exec.d.ts +57 -0
  82. package/dist/sandbox/exec.js +52 -0
  83. package/dist/sandbox/format.d.ts +2 -0
  84. package/dist/sandbox/format.js +7 -0
  85. package/dist/sandbox/header.d.ts +50 -0
  86. package/dist/sandbox/header.js +94 -0
  87. package/dist/sandbox/index.d.ts +17 -0
  88. package/dist/sandbox/index.js +17 -0
  89. package/dist/{workspace/install.d.ts → sandbox/install-plan.d.ts} +2 -2
  90. package/dist/{workspace/install.js → sandbox/install-plan.js} +2 -2
  91. package/dist/sandbox/name.d.ts +27 -0
  92. package/dist/sandbox/name.js +32 -0
  93. package/dist/sandbox/object.d.ts +244 -0
  94. package/dist/sandbox/object.js +1139 -0
  95. package/dist/scratch/index.d.ts +1 -9
  96. package/dist/scratch/index.js +2 -18
  97. package/package.json +22 -18
  98. package/dist/claude-code/github.d.ts +0 -39
  99. package/dist/claude-code/github.js +0 -82
  100. package/dist/claude-code/run.d.ts +0 -491
  101. package/dist/claude-code/run.js +0 -913
  102. package/dist/claude-code/session.d.ts +0 -131
  103. package/dist/claude-code/session.js +0 -288
  104. package/dist/workspace/ca-trust.d.ts +0 -95
  105. package/dist/workspace/ca-trust.js +0 -312
  106. package/dist/workspace/container-deps.d.ts +0 -58
  107. package/dist/workspace/container-deps.js +0 -249
  108. package/dist/workspace/container-fault.d.ts +0 -38
  109. package/dist/workspace/container-fault.js +0 -73
  110. package/dist/workspace/deps-snapshot.d.ts +0 -110
  111. package/dist/workspace/deps-snapshot.js +0 -424
  112. package/dist/workspace/exec.d.ts +0 -94
  113. package/dist/workspace/exec.js +0 -153
  114. package/dist/workspace/format.d.ts +0 -36
  115. package/dist/workspace/format.js +0 -57
  116. package/dist/workspace/git-host.d.ts +0 -79
  117. package/dist/workspace/git-host.js +0 -226
  118. package/dist/workspace/git-identity.d.ts +0 -57
  119. package/dist/workspace/git-identity.js +0 -147
  120. package/dist/workspace/index.d.ts +0 -50
  121. package/dist/workspace/index.js +0 -58
  122. package/dist/workspace/install-job.d.ts +0 -170
  123. package/dist/workspace/install-job.js +0 -934
  124. package/dist/workspace/object.d.ts +0 -522
  125. package/dist/workspace/object.js +0 -1541
  126. package/dist/workspace/open.d.ts +0 -95
  127. package/dist/workspace/open.js +0 -63
  128. package/dist/workspace/read.d.ts +0 -16
  129. package/dist/workspace/read.js +0 -22
  130. package/dist/workspace/shell.d.ts +0 -45
  131. package/dist/workspace/shell.js +0 -80
  132. package/dist/workspace/sync.d.ts +0 -111
  133. package/dist/workspace/sync.js +0 -124
  134. package/dist/workspace/wake.d.ts +0 -21
  135. package/dist/workspace/wake.js +0 -10
  136. /package/dist/{repo → repo-github}/tools-checks.d.ts +0 -0
  137. /package/dist/{repo → repo-github}/tools-clone.d.ts +0 -0
  138. /package/dist/{repo → repo-github}/tools-worktree.d.ts +0 -0
  139. /package/dist/{repo → repo-github}/tools-worktree.js +0 -0
  140. /package/dist/{repo → repo-github}/tools-worktrees.d.ts +0 -0
  141. /package/dist/{repo → repo-github}/tools-worktrees.js +0 -0
@@ -0,0 +1,47 @@
1
+ /** The host the CLI is pointed at by `ANTHROPIC_BASE_URL`, which the sandbox intercepts. */
2
+ export const ANTHROPIC_PLACEHOLDER_HOST = "anthropic.harness.internal";
3
+ const END = "claude-code-end:";
4
+ const NOTE = "claude-code-note:";
5
+ const SESSION = "claude-code-session:";
6
+ export function encodeRunEnd(end) {
7
+ return `${END}${JSON.stringify(end)}`;
8
+ }
9
+ /** The end in `text`, or `undefined` for a string the daemon did not write — the harness's own `container_lost`, say. */
10
+ export function decodeRunEnd(text) {
11
+ return decode(END, text);
12
+ }
13
+ /**
14
+ * One transcript note, as one message's only text part.
15
+ *
16
+ * The daemon makes the note (`./events.ts` `toProgress`) rather than sending
17
+ * the event, because a card clips its sections and an event does not: a tool's
18
+ * output is whatever the tool printed, and the harness stores every message in
19
+ * a row of the object's SQLite.
20
+ */
21
+ export function encodeNote(note) {
22
+ return `${NOTE}${JSON.stringify(note)}`;
23
+ }
24
+ export function decodeNote(text) {
25
+ return decode(NOTE, text);
26
+ }
27
+ /**
28
+ * The CLI's session id, as its own message: the handle a host records the
29
+ * moment the `init` line names it, since a session that dies before its end
30
+ * reports no other.
31
+ */
32
+ export function encodeSessionId(sessionId) {
33
+ return `${SESSION}${JSON.stringify(sessionId)}`;
34
+ }
35
+ export function decodeSessionId(text) {
36
+ return decode(SESSION, text);
37
+ }
38
+ function decode(prefix, text) {
39
+ if (!text?.startsWith(prefix))
40
+ return undefined;
41
+ try {
42
+ return JSON.parse(text.slice(prefix.length));
43
+ }
44
+ catch {
45
+ return undefined;
46
+ }
47
+ }
@@ -17,19 +17,15 @@
17
17
  * an agent loop to drive: the session *is* the sub-agent's model —
18
18
  * {@link claudeCodeModel} — and Think's recovery drives that. A host binds
19
19
  * {@link CLAUDE_CODE_AGENT} to a `SubAgent` class whose `getModel()` returns
20
- * one, and adds the `prepare` and `settle` that claim and release a workspace.
20
+ * one, and adds the `prepare` and `settle` that claim and release a sandbox.
21
21
  *
22
22
  * ## The credential never enters the container
23
23
  *
24
- * The session is launched with a placeholder. Every request out of the container
25
- * is intercepted by `computerd` and handed to {@link claudeCodeEgress} on the
26
- * Worker side, which swaps in a real credential and strips credential headers
27
- * from every other destination.
28
- *
29
- * It can **also** restrict which hosts the container may reach, but that is
30
- * `restrictToHosts` and it is **off unless a deployment sets it** — so do not
31
- * read it as a boundary that exists by default. The containment that always
32
- * holds is the credential swap.
24
+ * The session is launched with a placeholder and a placeholder host. The
25
+ * sandbox's egress rule, {@link claudeCodeEgress}, swaps in a real
26
+ * credential on the Worker side, leased from the pool the sandbox object holds
27
+ * ({@link credentialHost}). The image's daemon runs
28
+ * `@dynamicagents/plugins/claude-code/daemon`'s CLI.
33
29
  *
34
30
  * ## The 5-hour and weekly limits are routed around, not predicted
35
31
  *
@@ -37,35 +33,29 @@
37
33
  * guess about a bucket nobody can read, and it only moves when a run *ends* — so
38
34
  * it caps nothing, it only refuses to start.
39
35
  *
40
- * The gateway sees Anthropic's actual response, which is the one place the
36
+ * The egress sees Anthropic's actual response, which is the one place the
41
37
  * bucket announces itself. So the credential is a **pool**: the first usable
42
38
  * entry is used, a refusal marks it spent until its reset and advances the lead,
43
39
  * and the client's own retry — prompted by a rewritten `retry-after` — lands on
44
- * the next one. When every entry is spent the upstream wait passes through
45
- * unchanged and the run fails cleanly with the reset time. See
46
- * {@link file://./credentials.ts}.
40
+ * the next one. When every entry is spent the run fails cleanly with the reset
41
+ * time. See {@link file://./credentials.ts}.
47
42
  *
48
43
  * ## Requires
49
44
  *
50
- * One or more `claude setup-token` credentials, a container image with the CLI
51
- * installed at a pinned version, and a workspace Durable Object whose egress
52
- * policy is `{ mode: "http-gateway" }` and which supplies a
53
- * {@link CredentialStore}. See the README.
45
+ * One or more `claude setup-token` credentials and a `/sandbox` image with the
46
+ * CLI installed at a pinned version. See the README.
54
47
  */
55
- export { claudeCodeModel } from "./model.js";
56
- export type { ClaudeCodeModelOptions, SessionEnd, SessionOutcome, SessionRecord, SessionWorkspace } from "./model.js";
57
- export { claudeCodeSession } from "./session.js";
58
- export type { ClaudeCodeSession, ResumeSession, StartSession } from "./session.js";
48
+ export { claudeCodeModel, CONTAINER_LOST_PROMPT, DEFAULT_TIMEOUT_MS } from "./model.js";
49
+ export type { ClaudeCodeModelOptions, SandboxRuns, SessionEnd, SessionOutcome, SessionRecord } from "./model.js";
50
+ export { ANTHROPIC_HOST, claudeCodeEgress, credentialHost } from "./egress.js";
51
+ export type { ClaudeCodeEgressOptions, CredentialLease } from "./egress.js";
52
+ export { ANTHROPIC_PLACEHOLDER_HOST } from "./harness-wire.js";
53
+ export { CREDENTIAL_PLACEHOLDER } from "./placeholder.js";
59
54
  export { CLAUDE_CODE_AGENT, CLAUDE_CODE_WRITE_INPUT, type ClaudeCodeInput } from "./spec.js";
60
- export { ANTHROPIC_HOST, claudeCodeEgress } from "./egress.js";
61
55
  export { VERIFIED_CLAUDE_CODE_VERSION } from "./verified.js";
62
- export type { EgressConfig } from "./egress.js";
63
- export { GITHUB_HOSTS, GITHUB_TOKEN_PLACEHOLDER } from "./github.js";
64
- export { credentialPool, readRefusal, readRateLimitEvent } from "./credentials.js";
56
+ export { credentialPool, readRefusal } from "./credentials.js";
65
57
  export type { CredentialPool, CredentialPoolConfig, CredentialState, CredentialStore, Lead, Refusal } from "./credentials.js";
66
- export { buildLaunch, CLAUDE_EXEC_PREFIX, CREDENTIAL_PLACEHOLDER, execIdFor, followUpExecIdFor, freshCursor } from "./run.js";
67
- export type { DrainCursor, DrainOptions, DrainOutcome, Launch, LaunchOptions, SessionRuntime } from "./run.js";
68
58
  export { parseStream, toProgress, RATE_LIMIT_OK } from "./events.js";
69
59
  export type { ClaudeCodeEvent, ClaudeCodeResult, ClaudeCodeUsage, RateLimitInfo } from "./events.js";
70
- export { DEFAULT_PERMISSION_MODE, DEFAULT_TIMEOUT_MS } from "./config.js";
60
+ export { DEFAULT_PERMISSION_MODE } from "./config.js";
71
61
  export type { ClaudeCodeConfig, EffortLevel, PermissionMode } from "./config.js";
@@ -17,19 +17,15 @@
17
17
  * an agent loop to drive: the session *is* the sub-agent's model —
18
18
  * {@link claudeCodeModel} — and Think's recovery drives that. A host binds
19
19
  * {@link CLAUDE_CODE_AGENT} to a `SubAgent` class whose `getModel()` returns
20
- * one, and adds the `prepare` and `settle` that claim and release a workspace.
20
+ * one, and adds the `prepare` and `settle` that claim and release a sandbox.
21
21
  *
22
22
  * ## The credential never enters the container
23
23
  *
24
- * The session is launched with a placeholder. Every request out of the container
25
- * is intercepted by `computerd` and handed to {@link claudeCodeEgress} on the
26
- * Worker side, which swaps in a real credential and strips credential headers
27
- * from every other destination.
28
- *
29
- * It can **also** restrict which hosts the container may reach, but that is
30
- * `restrictToHosts` and it is **off unless a deployment sets it** — so do not
31
- * read it as a boundary that exists by default. The containment that always
32
- * holds is the credential swap.
24
+ * The session is launched with a placeholder and a placeholder host. The
25
+ * sandbox's egress rule, {@link claudeCodeEgress}, swaps in a real
26
+ * credential on the Worker side, leased from the pool the sandbox object holds
27
+ * ({@link credentialHost}). The image's daemon runs
28
+ * `@dynamicagents/plugins/claude-code/daemon`'s CLI.
33
29
  *
34
30
  * ## The 5-hour and weekly limits are routed around, not predicted
35
31
  *
@@ -37,28 +33,24 @@
37
33
  * guess about a bucket nobody can read, and it only moves when a run *ends* — so
38
34
  * it caps nothing, it only refuses to start.
39
35
  *
40
- * The gateway sees Anthropic's actual response, which is the one place the
36
+ * The egress sees Anthropic's actual response, which is the one place the
41
37
  * bucket announces itself. So the credential is a **pool**: the first usable
42
38
  * entry is used, a refusal marks it spent until its reset and advances the lead,
43
39
  * and the client's own retry — prompted by a rewritten `retry-after` — lands on
44
- * the next one. When every entry is spent the upstream wait passes through
45
- * unchanged and the run fails cleanly with the reset time. See
46
- * {@link file://./credentials.ts}.
40
+ * the next one. When every entry is spent the run fails cleanly with the reset
41
+ * time. See {@link file://./credentials.ts}.
47
42
  *
48
43
  * ## Requires
49
44
  *
50
- * One or more `claude setup-token` credentials, a container image with the CLI
51
- * installed at a pinned version, and a workspace Durable Object whose egress
52
- * policy is `{ mode: "http-gateway" }` and which supplies a
53
- * {@link CredentialStore}. See the README.
45
+ * One or more `claude setup-token` credentials and a `/sandbox` image with the
46
+ * CLI installed at a pinned version. See the README.
54
47
  */
55
- export { claudeCodeModel } from "./model.js";
56
- export { claudeCodeSession } from "./session.js";
48
+ export { claudeCodeModel, CONTAINER_LOST_PROMPT, DEFAULT_TIMEOUT_MS } from "./model.js";
49
+ export { ANTHROPIC_HOST, claudeCodeEgress, credentialHost } from "./egress.js";
50
+ export { ANTHROPIC_PLACEHOLDER_HOST } from "./harness-wire.js";
51
+ export { CREDENTIAL_PLACEHOLDER } from "./placeholder.js";
57
52
  export { CLAUDE_CODE_AGENT, CLAUDE_CODE_WRITE_INPUT } from "./spec.js";
58
- export { ANTHROPIC_HOST, claudeCodeEgress } from "./egress.js";
59
53
  export { VERIFIED_CLAUDE_CODE_VERSION } from "./verified.js";
60
- export { GITHUB_HOSTS, GITHUB_TOKEN_PLACEHOLDER } from "./github.js";
61
- export { credentialPool, readRefusal, readRateLimitEvent } from "./credentials.js";
62
- export { buildLaunch, CLAUDE_EXEC_PREFIX, CREDENTIAL_PLACEHOLDER, execIdFor, followUpExecIdFor, freshCursor } from "./run.js";
54
+ export { credentialPool, readRefusal } from "./credentials.js";
63
55
  export { parseStream, toProgress, RATE_LIMIT_OK } from "./events.js";
64
- export { DEFAULT_PERMISSION_MODE, DEFAULT_TIMEOUT_MS } from "./config.js";
56
+ export { DEFAULT_PERMISSION_MODE } from "./config.js";
@@ -0,0 +1,19 @@
1
+ import type { LanguageModel } from "ai";
2
+ /**
3
+ * A run whose only output is its report, as a sub-agent's model.
4
+ *
5
+ * Both Claude Code models are this: the session is the whole turn, so the
6
+ * stream carries nothing but the report, and Think's stall watchdog is off, so
7
+ * a silent stream is not cut.
8
+ */
9
+ /** The provider interface this implements, read off `ai` rather than imported. */
10
+ export type LanguageModelV3 = Extract<LanguageModel, {
11
+ specificationVersion: "v3";
12
+ }>;
13
+ export type CallOptions = Parameters<LanguageModelV3["doStream"]>[0];
14
+ export declare function reportingModel(modelId: string, run: (call: CallOptions) => Promise<string>): LanguageModelV3;
15
+ /**
16
+ * The task: the last user message's text. On a continued turn the prompt ends
17
+ * with the interrupted assistant message, so the last message is not it.
18
+ */
19
+ export declare function lastUserText(prompt: CallOptions["prompt"]): string;
@@ -0,0 +1,71 @@
1
+ /** Zeroed: the session's tokens are the CLI's context, not this conversation's. */
2
+ const USAGE = {
3
+ inputTokens: {
4
+ total: 0,
5
+ noCache: 0,
6
+ cacheRead: 0,
7
+ cacheWrite: 0
8
+ },
9
+ outputTokens: { total: 0, text: 0, reasoning: 0 }
10
+ };
11
+ export function reportingModel(modelId, run) {
12
+ return {
13
+ specificationVersion: "v3",
14
+ provider: "claude-code",
15
+ modelId,
16
+ supportedUrls: {},
17
+ async doStream(call) {
18
+ // The stream is returned at once and the session runs inside it, so
19
+ // nothing waits on a promise for the length of a session.
20
+ const stream = new ReadableStream({
21
+ async start(controller) {
22
+ controller.enqueue({ type: "stream-start", warnings: [] });
23
+ try {
24
+ const text = await run(call);
25
+ controller.enqueue({ type: "text-start", id: "report" });
26
+ controller.enqueue({
27
+ type: "text-delta",
28
+ id: "report",
29
+ delta: text
30
+ });
31
+ controller.enqueue({ type: "text-end", id: "report" });
32
+ controller.enqueue({
33
+ type: "finish",
34
+ usage: USAGE,
35
+ finishReason: { unified: "stop", raw: undefined }
36
+ });
37
+ controller.close();
38
+ }
39
+ catch (err) {
40
+ controller.error(err);
41
+ }
42
+ }
43
+ });
44
+ return { stream };
45
+ },
46
+ async doGenerate(call) {
47
+ const text = await run(call);
48
+ return {
49
+ content: [{ type: "text", text }],
50
+ finishReason: { unified: "stop", raw: undefined },
51
+ usage: USAGE,
52
+ warnings: []
53
+ };
54
+ }
55
+ };
56
+ }
57
+ /**
58
+ * The task: the last user message's text. On a continued turn the prompt ends
59
+ * with the interrupted assistant message, so the last message is not it.
60
+ */
61
+ export function lastUserText(prompt) {
62
+ for (let i = prompt.length - 1; i >= 0; i--) {
63
+ const message = prompt[i];
64
+ if (message.role !== "user")
65
+ continue;
66
+ return message.content
67
+ .map((part) => (part.type === "text" ? part.text : ""))
68
+ .join("");
69
+ }
70
+ throw new Error("claude-code: the run has no task — no user message");
71
+ }
@@ -1,31 +1,9 @@
1
- import type { LanguageModel } from "ai";
2
1
  import type { ArtifactEntryDetail } from "@dynamicagents/core/artifacts";
3
- import type { ClaudeCodeConfig, PermissionMode } from "./config.js";
2
+ import type { PollResult } from "../sandbox/object.js";
3
+ import type { EffortLevel, PermissionMode } from "./config.js";
4
4
  import type { ClaudeCodeResult, RateLimitInfo } from "./events.js";
5
- import { type SessionRuntime } from "./run.js";
6
- /**
7
- * A Claude Code session as a sub-agent's **model**.
8
- *
9
- * Claude Code brings its own tools, its own loop and its own context, so there
10
- * is nothing for Think to drive but the one call that runs it — and making that
11
- * call the model is what puts the session under Think's recovery. A turn cut by
12
- * an eviction or a deploy is continued; the continuation calls `doStream`
13
- * again, and every step below it has already taken is stored, so it resumes
14
- * where it was: the session is re-attached from its cursor, and a report
15
- * already made is emitted again rather than made again.
16
- *
17
- * ## The report is the only text
18
- *
19
- * A run's result is what its assistant messages say, so narration streamed as
20
- * text would become the result and bury the report. What the session says as
21
- * it works goes to the parent as notes instead, through `note`. Nothing else is
22
- * streamed at all: Think's stall watchdog is off, so a silent stream is not cut.
23
- */
24
- /** The provider interface this implements, read off `ai` rather than imported. */
25
- type LanguageModelV3 = Extract<LanguageModel, {
26
- specificationVersion: "v3";
27
- }>;
28
- /** How one exec of a session ended. */
5
+ import { type LanguageModelV3 } from "./language-model.js";
6
+ /** How one turn of a session ended. */
29
7
  export interface SessionEnd {
30
8
  exitCode: number;
31
9
  /** Absent when the process died without ever emitting a `result` line. */
@@ -35,13 +13,13 @@ export interface SessionEnd {
35
13
  /** The subscription bucket as the client last reported it, if it did. */
36
14
  rateLimit?: RateLimitInfo;
37
15
  /**
38
- * The Claude Code session id, from the `init` line by way of the drain's
39
- * cursor — see {@link file://./run.ts DrainCursor.sessionId}.
16
+ * The Claude Code session id, from the `init` line by way of the daemon's
17
+ * parser.
40
18
  *
41
19
  * Here as well as on `result` because the sessions worth continuing are often
42
- * the ones with no result: a session ended by its time limit, a cancel or a
43
- * container rollout reports none, and this is then the only handle to the
44
- * conversation it got half-way through.
20
+ * the ones with no result: a session ended by its time limit or a cancel
21
+ * reports none, and this is then the only handle to the conversation it got
22
+ * half-way through.
45
23
  */
46
24
  sessionId?: string;
47
25
  }
@@ -69,60 +47,82 @@ export interface SessionOutcome {
69
47
  /** The follow-up's end, when `followUp` asked for one. */
70
48
  followUp?: SessionEnd;
71
49
  }
72
- /** The container a session runs in, released when the call that opened it ends. */
73
- export interface SessionWorkspace extends Disposable {
74
- readonly runtime: SessionRuntime;
50
+ /**
51
+ * A Claude Code session in a sandbox, as a sub-agent's model.
52
+ *
53
+ * Claude Code brings its own tools, its own loop and its own context, so there
54
+ * is nothing for Think to drive but the one call that runs it — and making that
55
+ * call the model is what puts the session under Think's recovery. The report
56
+ * is its only text: what the session says as it works goes to the parent as
57
+ * notes, through `note`. Every step's outcome is kept in the sub-agent's
58
+ * storage, so a recovered turn picks up where it was. What can be lost:
59
+ *
60
+ * - **Nothing streams.** The session runs as a harness operation the sandbox
61
+ * keeps, and this polls it. A poll is short, so a reset of either object
62
+ * costs one poll, and Think's recovery of this turn polls the same operation.
63
+ * - **A lost container resumes the session.** The operation settles as
64
+ * `container_lost`, and this submits the next attempt to the same harness
65
+ * session, whose CLI conversation the harness kept: the session is told what
66
+ * happened, not briefed again.
67
+ */
68
+ /** The sandbox RPCs a run uses. */
69
+ export interface SandboxRuns {
70
+ createRun(runId: string, options: {
71
+ cwd: string;
72
+ forkOf?: string;
73
+ continues?: string;
74
+ }): Promise<{
75
+ sessionId: string;
76
+ created: boolean;
77
+ }>;
78
+ submit(runId: string, text: string, operationId: string): Promise<{
79
+ operationId: string;
80
+ accepted: boolean;
81
+ }>;
82
+ poll(runId: string, operationId: string, waitMs?: number, from?: number): Promise<PollResult>;
83
+ stopRun(runId: string): Promise<{
84
+ stopped: boolean;
85
+ }>;
75
86
  }
76
87
  export interface ClaudeCodeModelOptions {
77
- config: ClaudeCodeConfig;
78
88
  /**
79
- * Opens the run's container — the workspace the run's `runtime()` names.
80
- * Called again to re-attach after a cut stream, so each call should reach the
81
- * object through a new stub rather than one held from the last.
89
+ * The sandbox the run is in. Called again after a failed call, so each call
90
+ * should reach the object through a new stub.
82
91
  */
83
- workspace: () => Promise<SessionWorkspace>;
92
+ sandbox: () => SandboxRuns;
84
93
  /** The sub-agent's own storage, where every step's outcome is kept. */
85
94
  storage: DurableObjectStorage;
86
- /** The sub-agent's name, which is the run's id. The exec ids derive from it. */
95
+ /** The sub-agent's name, which is the run's id in the sandbox. */
87
96
  runId: string;
88
- /** The checkout. */
97
+ /** The checkout, under the sandbox's `/workspace`. */
89
98
  dir: string;
90
- /**
91
- * A JSON Schema the session's answer must match. Its `result` then carries
92
- * the answer as `structured`, for `report` to read.
93
- */
94
- jsonSchema?: Record<string, unknown>;
95
- /**
96
- * This run's mode, over {@link file://./config.ts ClaudeCodeConfig.permissionMode}.
97
- *
98
- * A planning run is a writing session under `plan` with a `jsonSchema`: it
99
- * reads, edits nothing, and answers through `StructuredOutput` rather than an
100
- * approval nobody is there to give. Carrying the plan out is a later run that
101
- * resumes it with `fork` under the config's mode — the CLI takes a new
102
- * `--permission-mode` on `--resume` — so the plan's own conversation stays
103
- * whole for an edit.
104
- */
99
+ /** The CLI's model. Unset, the daemon's. */
100
+ model?: string;
101
+ effort?: EffortLevel;
102
+ /** A JSON schema the daemon was built with, by name, that the answer must match. */
103
+ schema?: string;
104
+ /** `plan` runs read-only and answers to {@link schema}; anything else is the daemon's mode. */
105
105
  permissionMode?: PermissionMode;
106
106
  /**
107
- * A Claude Code conversation for this run to continue instead of starting one
108
- * — see {@link file://./session.ts StartSession.resume}.
109
- *
110
- * Resolved by the host, in `prepare`, against whatever it recorded through
111
- * {@link ClaudeCodeModelOptions.onSession}: which conversations exist in a
112
- * workspace, and which of them a caller may continue, are the host's
113
- * questions. The run's prompt is then the next user turn — its `brief` hook
114
- * is the place to say what changed and nothing the session already holds.
107
+ * Another run in this sandbox whose conversation this one carries on: as more
108
+ * of it, or with `fork`, as a new branch of it. Its prompt is then the next
109
+ * user turn.
115
110
  */
116
111
  resume?: {
117
- sessionId: string;
112
+ run: string;
118
113
  fork?: boolean;
119
114
  };
120
115
  /**
121
- * Files one note on the parent's transcript: the sub-agent's `note`. A tool
122
- * call, its result and the session's own start and end arrive with a card —
123
- * see {@link file://./cards.ts Card}.
116
+ * How long a session may run, from its first submission, before it is
117
+ * stopped. Default {@link DEFAULT_TIMEOUT_MS}.
118
+ */
119
+ timeoutMs?: number;
120
+ /**
121
+ * File one note on the parent's transcript. `at` is when the daemon read the
122
+ * line it came from: a poll files a batch at once, so the filing time would
123
+ * give a tool's call and its result the same moment.
124
124
  */
125
- note: (key: string, text: string, detail?: ArtifactEntryDetail) => Promise<void>;
125
+ note: (key: string, text: string, detail?: ArtifactEntryDetail, at?: number) => Promise<void>;
126
126
  /**
127
127
  * The session's handle, as soon as there is one and again when the session
128
128
  * ends — see {@link SessionRecord}.
@@ -148,5 +148,12 @@ export interface ClaudeCodeModelOptions {
148
148
  /** The text the parent receives. Run once, and kept. */
149
149
  report: (outcome: SessionOutcome) => Promise<string>;
150
150
  }
151
+ /**
152
+ * Three hours: a ceiling on a session that has stopped making progress, not a
153
+ * budget for one that is. No timer in the sandbox races it, since the
154
+ * container's idle stop never fires while an operation is open.
155
+ */
156
+ export declare const DEFAULT_TIMEOUT_MS: number;
157
+ /** What a session is told when it resumes in a new container. */
158
+ export declare const CONTAINER_LOST_PROMPT: string;
151
159
  export declare function claudeCodeModel(options: ClaudeCodeModelOptions): LanguageModelV3;
152
- export {};