@snappedly-tools/shipyard 0.8.0 → 0.9.1

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 (45) hide show
  1. package/README.md +24 -14
  2. package/dist/MountConfig-K5ILnfht.d.ts +26 -0
  3. package/dist/{MountConfig-BHnKnA4h.d.ts → SandboxProvider-oUAwYlWm.d.ts} +1 -26
  4. package/dist/{chunk-L6PX5QTU.js → chunk-57EEKW3R.js} +57 -55
  5. package/dist/chunk-57EEKW3R.js.map +1 -0
  6. package/dist/{chunk-JI3HDDMS.js → chunk-HXSZM52J.js} +3 -3
  7. package/dist/{chunk-JI3HDDMS.js.map → chunk-HXSZM52J.js.map} +1 -1
  8. package/dist/{chunk-44I2BL6E.js → chunk-JZUBT4WG.js} +20 -4
  9. package/dist/chunk-JZUBT4WG.js.map +1 -0
  10. package/dist/chunk-NQRFVKCU.js +1840 -0
  11. package/dist/chunk-NQRFVKCU.js.map +1 -0
  12. package/dist/chunk-Z5C4LHVP.js +137 -0
  13. package/dist/chunk-Z5C4LHVP.js.map +1 -0
  14. package/dist/createSandbox-DmbnWAZv.d.ts +739 -0
  15. package/dist/index.d.ts +7 -2534
  16. package/dist/index.js +216 -7269
  17. package/dist/index.js.map +1 -1
  18. package/dist/integrations/github.d.ts +52 -0
  19. package/dist/integrations/github.js +1049 -0
  20. package/dist/integrations/github.js.map +1 -0
  21. package/dist/integrations/releases.d.ts +136 -0
  22. package/dist/integrations/releases.js +500 -0
  23. package/dist/integrations/releases.js.map +1 -0
  24. package/dist/main.js +641 -383
  25. package/dist/main.js.map +1 -1
  26. package/dist/publication-BPoy_M9M.d.ts +1200 -0
  27. package/dist/sandboxes/docker.d.ts +2 -1
  28. package/dist/sandboxes/docker.js +2 -2
  29. package/dist/templates/parallel-planner/main.mts +11 -7
  30. package/dist/templates/parallel-planner/setup.sh +1 -0
  31. package/dist/templates/parallel-planner-with-review/main.mts +11 -7
  32. package/dist/templates/parallel-planner-with-review/setup.sh +1 -0
  33. package/dist/templates/sequential-reviewer/main.mts +7 -3
  34. package/dist/templates/sequential-reviewer/setup.sh +1 -0
  35. package/dist/templates/shared/setup.sh +1 -0
  36. package/dist/templates/simple-loop/main.mts +7 -3
  37. package/dist/templates/simple-loop/setup.sh +1 -0
  38. package/dist/workflow/coordinator/migrations/002_workflow_phase_records.sql +11 -0
  39. package/dist/workflow/coordinator/migrations/003_phase_record_schema_version.sql +6 -0
  40. package/dist/workflow.d.ts +472 -0
  41. package/dist/workflow.js +4345 -0
  42. package/dist/workflow.js.map +1 -0
  43. package/package.json +13 -1
  44. package/dist/chunk-44I2BL6E.js.map +0 -1
  45. package/dist/chunk-L6PX5QTU.js.map +0 -1
@@ -0,0 +1,739 @@
1
+ import { f as SessionTransferHandle, S as SandboxProvider, B as BranchStrategy, E as ExecResult } from './SandboxProvider-oUAwYlWm.js';
2
+ import { StandardSchemaV1 } from '@standard-schema/spec';
3
+
4
+ /** Reasoning efforts accepted by the Codex CLI. */
5
+ declare const CODEX_REASONING_EFFORTS: readonly ["low", "medium", "high", "xhigh", "max"];
6
+ type CodexReasoningEffort = (typeof CODEX_REASONING_EFFORTS)[number];
7
+ /** A model identifier plus the reasoning policy used for that model role. */
8
+ interface CodexModelConfig {
9
+ readonly model: string;
10
+ readonly effort: CodexReasoningEffort;
11
+ }
12
+ declare const CODEX_MODELS: {
13
+ readonly routine: {
14
+ readonly model: string;
15
+ readonly effort: "low" | "medium" | "high" | "xhigh" | "max";
16
+ };
17
+ readonly strong: {
18
+ readonly model: string;
19
+ readonly effort: "low" | "medium" | "high" | "xhigh" | "max";
20
+ };
21
+ };
22
+
23
+ type ParsedStreamEvent = {
24
+ type: "text";
25
+ text: string;
26
+ } | {
27
+ type: "result";
28
+ result: string;
29
+ } | {
30
+ type: "tool_call";
31
+ name: string;
32
+ args: string;
33
+ } | {
34
+ type: "session_id";
35
+ sessionId: string;
36
+ } | {
37
+ type: "usage";
38
+ usage: IterationUsage;
39
+ };
40
+ /** Options passed to buildPrintCommand and buildInteractiveArgs. */
41
+ interface AgentCommandOptions {
42
+ readonly prompt: string;
43
+ readonly dangerouslySkipPermissions: boolean;
44
+ /** Tools the controlled phase is permitted to invoke. */
45
+ readonly toolAllowlist?: readonly string[];
46
+ /** When set, the agent should resume the given session ID instead of starting fresh. */
47
+ readonly resumeSession?: string;
48
+ /**
49
+ * When true alongside `resumeSession`, the agent should fork the session
50
+ * instead of mutating it — Claude's `--fork-session`, Codex's
51
+ * `codex exec fork`. The parent session JSONL is left intact and the agent
52
+ * writes a new session under a fresh id.
53
+ */
54
+ readonly forkSession?: boolean;
55
+ }
56
+ /** Return type of buildPrintCommand — command string plus optional stdin content.
57
+ * When `stdin` is set, the sandbox pipes it to the child process's stdin
58
+ * instead of inlining the prompt in argv, avoiding the Linux 128 KB per-arg limit. */
59
+ interface PrintCommand {
60
+ readonly command: string;
61
+ readonly stdin?: string;
62
+ }
63
+ /** Per-iteration token usage snapshot extracted from the agent session. */
64
+ interface IterationUsage {
65
+ readonly inputTokens: number;
66
+ readonly cacheCreationInputTokens: number;
67
+ readonly cacheReadInputTokens: number;
68
+ readonly outputTokens: number;
69
+ }
70
+ interface AgentSessionStorage {
71
+ /** Transfer a session JSONL from the sandbox into the host store. */
72
+ captureToHost(args: {
73
+ hostCwd: string;
74
+ sandboxCwd: string;
75
+ sessionId: string;
76
+ handle: SessionTransferHandle;
77
+ }): Promise<void>;
78
+ /** Transfer a session JSONL from the host store into the sandbox. */
79
+ resumeIntoSandbox(args: {
80
+ hostCwd: string;
81
+ sandboxCwd: string;
82
+ sessionId: string;
83
+ handle: SessionTransferHandle;
84
+ }): Promise<void>;
85
+ /** Read a captured session JSONL from the host store. Returns undefined when absent. */
86
+ readHostSession(cwd: string, sessionId: string): Promise<string | undefined>;
87
+ /** Whether a session with the given id exists in the host store keyed on cwd. */
88
+ existsOnHost(cwd: string, sessionId: string): Promise<boolean>;
89
+ /** Absolute host path where a session would be stored (for not-found error messages). */
90
+ hostSessionFilePath(cwd: string, sessionId: string): string | undefined;
91
+ }
92
+ interface AgentProvider {
93
+ readonly name: string;
94
+ /** Environment variables injected by this agent provider. Merged at launch time with env resolver and sandbox provider env. */
95
+ readonly env: Record<string, string>;
96
+ /** Set only when this provider enforces `AgentCommandOptions.toolAllowlist`. */
97
+ readonly supportsToolAllowlist?: boolean;
98
+ /** When true, session capture is enabled for this provider. Default: true for file-backed providers. */
99
+ readonly captureSessions: boolean;
100
+ /** Provider-owned storage and transfer behavior for resumable agent sessions. */
101
+ readonly sessionStorage?: AgentSessionStorage;
102
+ buildPrintCommand(options: AgentCommandOptions): PrintCommand;
103
+ buildInteractiveArgs?(options: AgentCommandOptions): string[];
104
+ parseStreamLine(line: string): ParsedStreamEvent[];
105
+ /** Parse token usage from the captured session JSONL content. Only implemented by Claude Code. */
106
+ parseSessionUsage?(content: string): IterationUsage | undefined;
107
+ }
108
+ /** Options for the codex agent provider. */
109
+ interface CodexOptions {
110
+ /** Set to `null` to use the provider default instead of a configured role effort. */
111
+ readonly effort?: CodexReasoningEffort | null;
112
+ /** Environment variables injected by this agent provider. */
113
+ readonly env?: Record<string, string>;
114
+ /** When false, session capture is disabled. Default: true. */
115
+ readonly captureSessions?: boolean;
116
+ /** Override Codex session directories for tests or non-standard installs. */
117
+ readonly sessionStorage?: {
118
+ readonly hostSessionsDir?: string;
119
+ readonly sandboxSessionsDir?: string;
120
+ };
121
+ /**
122
+ * Maps to Codex's `approvals_reviewer` config key (set via
123
+ * `-c approvals_reviewer="<value>"`). When set to `"auto_review"`, the
124
+ * provider swaps the default `--dangerously-bypass-approvals-and-sandbox`
125
+ * for an interactive approval policy (`-a on-request`) and Codex's most
126
+ * permissive sandbox (`-s danger-full-access`) — auto-review needs
127
+ * something to review, and the safety boundary is the reviewer agent
128
+ * rather than the filesystem sandbox.
129
+ */
130
+ readonly approvalsReviewer?: "user" | "auto_review";
131
+ }
132
+ declare const codex: (model: string | CodexModelConfig, options?: CodexOptions) => AgentProvider & {
133
+ readonly sessionStorage: AgentSessionStorage;
134
+ };
135
+ interface ClaudeCodeOptions {
136
+ /** Set to `null` to leave effort selection to the provider. */
137
+ readonly effort?: "low" | "medium" | "high" | "xhigh" | "max" | null;
138
+ /** Environment variables injected by this agent provider. */
139
+ readonly env?: Record<string, string>;
140
+ /** When false, session capture is disabled. Default: true. */
141
+ readonly captureSessions?: boolean;
142
+ /** Override Claude session directories for tests or non-standard installs. */
143
+ readonly sessionStorage?: {
144
+ readonly hostProjectsDir?: string;
145
+ readonly sandboxProjectsDir?: string;
146
+ };
147
+ /**
148
+ * Maps directly to Claude's `--permission-mode` flag. When set, replaces the
149
+ * default `--dangerously-skip-permissions` Shipyard passes on AFK runs —
150
+ * the two flags are mutually exclusive on Claude's CLI. Use `"auto"` for
151
+ * AI-mediated per-tool approve/deny on unsandboxed host runs.
152
+ */
153
+ readonly permissionMode?: "default" | "acceptEdits" | "plan" | "auto" | "dontAsk" | "bypassPermissions";
154
+ }
155
+ declare const claudeCode: (model: string, options?: ClaudeCodeOptions) => AgentProvider & {
156
+ readonly sessionStorage: AgentSessionStorage;
157
+ };
158
+
159
+ /**
160
+ * A single event in the agent's output stream, surfaced to callers of `run()`
161
+ * so they can forward it to their own observability system.
162
+ *
163
+ * Emitted only in log-to-file mode when an `onAgentStreamEvent` callback is
164
+ * provided via `logging`. See `run()`.
165
+ *
166
+ * The `"raw"` variant carries every stdout line the agent emits, verbatim and
167
+ * before parsing — including lines that the provider's stream parser would
168
+ * otherwise drop (e.g. tool-use blocks for unrecognised tools). Intended for
169
+ * debugging when the typed `"text"` / `"toolCall"` events don't surface
170
+ * enough detail.
171
+ */
172
+ type AgentStreamEvent = {
173
+ readonly type: "text";
174
+ readonly message: string;
175
+ readonly iteration: number;
176
+ readonly timestamp: Date;
177
+ } | {
178
+ readonly type: "toolCall";
179
+ readonly name: string;
180
+ readonly formattedArgs: string;
181
+ readonly iteration: number;
182
+ readonly timestamp: Date;
183
+ } | {
184
+ readonly type: "raw";
185
+ readonly line: string;
186
+ readonly iteration: number;
187
+ readonly timestamp: Date;
188
+ };
189
+
190
+ type SandboxHooks = {
191
+ readonly host?: {
192
+ readonly onWorktreeReady?: ReadonlyArray<{
193
+ readonly command: string;
194
+ readonly timeoutMs?: number;
195
+ }>;
196
+ readonly onSandboxReady?: ReadonlyArray<{
197
+ readonly command: string;
198
+ readonly timeoutMs?: number;
199
+ }>;
200
+ };
201
+ readonly sandbox?: {
202
+ readonly onSandboxReady?: ReadonlyArray<{
203
+ readonly command: string;
204
+ readonly sudo?: boolean;
205
+ readonly timeoutMs?: number;
206
+ }>;
207
+ };
208
+ };
209
+
210
+ /** Per-iteration result carrying an optional session ID. */
211
+ interface IterationResult {
212
+ /** Agent session ID extracted from the provider stream, when available. */
213
+ readonly sessionId?: string;
214
+ /** Absolute host path to the captured session record, when capture is enabled. */
215
+ readonly sessionFilePath?: string;
216
+ /** Token usage snapshot from the last assistant message in the session, or undefined when capture is disabled or provider does not support usage parsing. */
217
+ readonly usage?: IterationUsage;
218
+ }
219
+
220
+ /**
221
+ * A map of named values used for prompt argument substitution.
222
+ * Each key corresponds to a `{{KEY}}` placeholder in the prompt; the value
223
+ * replaces it before the prompt is passed to the agent.
224
+ */
225
+ type PromptArgs = Record<string, string | number | boolean>;
226
+
227
+ /** Branded output definition for `Output.object({ tag, schema })`. */
228
+ interface OutputObjectDefinition<T> {
229
+ readonly _tag: "object";
230
+ readonly tag: string;
231
+ readonly schema: StandardSchemaV1<unknown, T>;
232
+ /**
233
+ * Maximum number of additional attempts after the first if structured output
234
+ * extraction or validation fails. Each retry resumes the failed run's agent
235
+ * session and feeds back a token-efficient description of the error so the
236
+ * agent can re-emit a corrected tag. Default: `0` (no retries).
237
+ *
238
+ * Retries require the agent provider to support session resumption (i.e.
239
+ * `provider.sessionStorage` is populated — Codex or Claude Code). `run()`
240
+ * fails at entry with a clear error when retries are requested but the
241
+ * provider cannot resume.
242
+ */
243
+ readonly maxRetries?: number;
244
+ }
245
+ /** Branded output definition for `Output.string({ tag })`. */
246
+ interface OutputStringDefinition {
247
+ readonly _tag: "string";
248
+ readonly tag: string;
249
+ /**
250
+ * Maximum number of additional attempts after the first if structured output
251
+ * extraction fails. Each retry resumes the failed run's agent session and
252
+ * feeds back a token-efficient description of the error so the agent can
253
+ * re-emit a corrected tag. Default: `0` (no retries).
254
+ *
255
+ * Retries require the agent provider to support session resumption (i.e.
256
+ * `provider.sessionStorage` is populated — Codex or Claude Code). `run()`
257
+ * fails at entry with a clear error when retries are requested but the
258
+ * provider cannot resume.
259
+ */
260
+ readonly maxRetries?: number;
261
+ }
262
+ /** Union of all output definition shapes accepted by `run()`. */
263
+ type OutputDefinition = OutputObjectDefinition<any> | OutputStringDefinition;
264
+ /**
265
+ * Helpers for declaring structured output on `run()`.
266
+ *
267
+ * ```ts
268
+ * import { Output, run } from "@snappedly-tools/shipyard";
269
+ * import { z } from "zod";
270
+ *
271
+ * const result = await run({
272
+ * output: Output.object({ tag: "result", schema: z.object({ answer: z.number() }) }),
273
+ * // ...
274
+ * });
275
+ * console.log(result.output.answer); // typed as number
276
+ * ```
277
+ */
278
+ declare const Output: {
279
+ /**
280
+ * Declare an object-typed structured output extracted from an XML tag in
281
+ * the agent's stdout. The tag contents are JSON-parsed (with fence-aware
282
+ * unwrapping) and validated against the provided Standard Schema validator.
283
+ *
284
+ * Set `maxRetries` to have `run()` automatically resume the failed session
285
+ * and ask the agent to re-emit corrected output when extraction or
286
+ * validation fails. Default: `0` (no retries).
287
+ */
288
+ readonly object: <Schema extends StandardSchemaV1>(opts: {
289
+ tag: string;
290
+ schema: Schema;
291
+ maxRetries?: number;
292
+ }) => OutputObjectDefinition<StandardSchemaV1.InferOutput<Schema>>;
293
+ /**
294
+ * Declare a string-typed structured output extracted from an XML tag in
295
+ * the agent's stdout. The tag contents are whitespace-trimmed and returned
296
+ * as a plain string — no JSON parsing, no schema validation.
297
+ *
298
+ * Set `maxRetries` to have `run()` automatically resume the failed session
299
+ * and ask the agent to re-emit corrected output when extraction fails.
300
+ * Default: `0` (no retries).
301
+ */
302
+ readonly string: (opts: {
303
+ tag: string;
304
+ maxRetries?: number;
305
+ }) => OutputStringDefinition;
306
+ };
307
+ interface StructuredOutputErrorOptions {
308
+ readonly tag: string;
309
+ readonly rawMatched: string | undefined;
310
+ readonly cause?: unknown;
311
+ readonly commits: {
312
+ sha: string;
313
+ }[];
314
+ readonly branch: string;
315
+ readonly preservedWorktreePath?: string;
316
+ readonly sessionId?: string;
317
+ readonly sessionFilePath?: string;
318
+ }
319
+ /**
320
+ * Thrown by `run()` when structured output extraction or validation fails.
321
+ *
322
+ * Possible failure modes:
323
+ * - The configured XML tag was not found in stdout (`rawMatched` is `undefined`).
324
+ * - The tag contents failed `JSON.parse` (`cause` carries the parse error).
325
+ * - The parsed JSON failed schema validation (`cause` carries the Standard Schema issues).
326
+ *
327
+ * The error carries `commits`, `branch`, and optionally `preservedWorktreePath`
328
+ * so callers can decide recovery without losing the run's side effects.
329
+ *
330
+ * It also carries `sessionId` (and `sessionFilePath` when the session was
331
+ * captured to the host) of the iteration that produced the bad output, so a
332
+ * caller can resume that same session and ask the agent to re-emit corrected
333
+ * output:
334
+ *
335
+ * ```ts
336
+ * try {
337
+ * return await run({ ...opts, output });
338
+ * } catch (e) {
339
+ * if (e instanceof StructuredOutputError && e.sessionId) {
340
+ * return await run({
341
+ * ...opts,
342
+ * output,
343
+ * resumeSession: e.sessionId,
344
+ * prompt: feedback(e),
345
+ * });
346
+ * }
347
+ * throw e;
348
+ * }
349
+ * ```
350
+ */
351
+ declare class StructuredOutputError extends Error {
352
+ readonly tag: string;
353
+ readonly rawMatched: string | undefined;
354
+ readonly cause: unknown;
355
+ readonly commits: {
356
+ sha: string;
357
+ }[];
358
+ readonly branch: string;
359
+ readonly preservedWorktreePath?: string;
360
+ /** Session ID of the iteration that produced the bad output, when available. */
361
+ readonly sessionId?: string;
362
+ /** Host path to the captured session JSONL, when the session was captured. */
363
+ readonly sessionFilePath?: string;
364
+ constructor(message: string, options: StructuredOutputErrorOptions);
365
+ }
366
+
367
+ /**
368
+ * Controls where Shipyard writes iteration progress and agent output.
369
+ * Use `"file"` (log-to-file mode) to write to a log file on disk, or
370
+ * `"stdout"` (terminal mode) to render an interactive UI in the terminal.
371
+ */
372
+ type LoggingOption =
373
+ /** Write progress and agent output to a log file at the given path (log-to-file mode). */
374
+ {
375
+ readonly type: "file";
376
+ readonly path: string;
377
+ /**
378
+ * Optional callback invoked for each agent stream event (text chunk,
379
+ * tool call, or raw stdout line) in addition to being written to the
380
+ * log file. Intended for forwarding the agent's output stream to
381
+ * external observability systems. Errors thrown by the callback are
382
+ * swallowed.
383
+ */
384
+ readonly onAgentStreamEvent?: (event: AgentStreamEvent) => void;
385
+ /**
386
+ * When `true`, every raw stdout line the agent emits is appended
387
+ * verbatim to the same log file at `path`, in real time. Includes
388
+ * lines the provider's stream parser would otherwise drop (e.g.
389
+ * tool-use blocks for unrecognised tools). Intended for debugging
390
+ * stuck or unexpected agent behavior — note that the raw JSON is
391
+ * interleaved with the human-readable log output. Default: `false`.
392
+ */
393
+ readonly verbose?: boolean;
394
+ }
395
+ /** Render progress and agent output as an interactive UI in the terminal (terminal mode). */
396
+ | {
397
+ readonly type: "stdout";
398
+ /**
399
+ * When `true`, every raw stdout line the agent emits is written
400
+ * verbatim to `process.stdout`, in real time. Includes lines the
401
+ * provider's stream parser would otherwise drop. Intended for
402
+ * debugging stuck or unexpected agent behavior. Note: the raw output
403
+ * is interleaved with the interactive terminal UI. Default: `false`.
404
+ */
405
+ readonly verbose?: boolean;
406
+ };
407
+ /** Override default timeouts for built-in lifecycle steps. Unset keys keep their defaults. */
408
+ interface Timeouts {
409
+ /** Timeout (ms) for copying selected paths into a worktree or Docker sandbox. Default: 60_000. */
410
+ readonly copyToWorktreeMs?: number;
411
+ /** Timeout (ms) for each in-sandbox git setup command (safe.directory, user.name/email, branch discovery). Default: 10_000. */
412
+ readonly gitSetupMs?: number;
413
+ /** Timeout (ms) for collecting the commits produced during the run. Default: 30_000. */
414
+ readonly commitCollectionMs?: number;
415
+ /** Timeout (ms) for merging the temp branch back to the host branch (merge-to-head strategy). Default: 30_000. */
416
+ readonly mergeToHostMs?: number;
417
+ }
418
+ interface RunOptions<A extends AgentProvider = AgentProvider> {
419
+ /** Agent provider to use (e.g. codex(CODEX_MODELS.routine)) */
420
+ readonly agent: A;
421
+ /** Sandbox provider (e.g. docker({ imageName: "shipyard:myrepo" })). */
422
+ readonly sandbox: SandboxProvider;
423
+ /**
424
+ * Host repo directory. Replaces `process.cwd()` as the anchor for
425
+ * `.shipyard/worktrees/`, `.shipyard/.env`, `.shipyard/logs/YYYY-MM-DD/`,
426
+ * `.shipyard/patches/`, and git operations.
427
+ *
428
+ * - Relative paths are resolved against `process.cwd()`.
429
+ * - Absolute paths are used as-is.
430
+ * - Defaults to `process.cwd()` when omitted.
431
+ */
432
+ readonly cwd?: string;
433
+ /** Inline prompt string (mutually exclusive with promptFile) */
434
+ readonly prompt?: string;
435
+ /**
436
+ * Path to a prompt file (mutually exclusive with prompt).
437
+ *
438
+ * **Note:** `promptFile` is always resolved against `process.cwd()`, not
439
+ * against the `cwd` option. If you set a custom `cwd`, pass an absolute
440
+ * `promptFile` to avoid ambiguity.
441
+ */
442
+ readonly promptFile?: string;
443
+ /** Maximum iterations to run (default: 1) */
444
+ readonly maxIterations?: number;
445
+ /** Lifecycle hooks grouped by execution location (host or sandbox). */
446
+ readonly hooks?: SandboxHooks;
447
+ /** Key-value map for {{KEY}} placeholder substitution in prompts */
448
+ readonly promptArgs?: PromptArgs;
449
+ /** Logging mode (default: { type: 'file' } with auto-generated path under .shipyard/logs/YYYY-MM-DD/) */
450
+ readonly logging?: LoggingOption;
451
+ /** Substring(s) the agent emits to stop the iteration loop early. Matched via `includes` against agent output. (default: `"<promise>COMPLETE</promise>"`) */
452
+ readonly completionSignal?: string | string[];
453
+ /** Idle timeout in seconds. If the agent produces no output for this long, it fails. Default: 600 (10 minutes) */
454
+ readonly idleTimeoutSeconds?: number;
455
+ /**
456
+ * Grace window in seconds after a completion signal is observed in the
457
+ * agent's output. The agent process is expected to exit shortly after
458
+ * emitting the signal; if it does not (typically because a spawned child —
459
+ * a `gh`/git subprocess or long-lived MCP server — keeps stdout open),
460
+ * Shipyard force-completes the iteration with a warning. Resets on every
461
+ * subsequent output line so trailing data (token-usage events, terminal
462
+ * `result` events, structured-output tags) is still captured. Independent
463
+ * of `idleTimeoutSeconds`. Default: 60.
464
+ */
465
+ readonly completionTimeoutSeconds?: number;
466
+ /** Optional name for the run, shown as a prefix in log output */
467
+ readonly name?: string;
468
+ /** Paths relative to the host repo root to copy into Docker after Git sync. */
469
+ readonly copyToWorktree?: string[];
470
+ /** Tools the provider must enforce for this controlled phase. */
471
+ readonly toolAllowlist?: readonly string[];
472
+ /** Branch strategy; defaults to merge-to-head. */
473
+ readonly branchStrategy?: BranchStrategy;
474
+ /** Resume a prior agent session by ID. The session record must exist on the host. Incompatible with maxIterations > 1. */
475
+ readonly resumeSession?: string;
476
+ /**
477
+ * An `AbortSignal` that cancels the run when aborted.
478
+ *
479
+ * - If `signal.aborted` is already `true` at entry, `run()` rejects
480
+ * immediately without doing any setup work.
481
+ * - Aborting mid-iteration kills the in-flight agent subprocess.
482
+ * - Phase boundaries (between iterations) also check the signal.
483
+ * - The rejected promise surfaces `signal.reason` via
484
+ * `signal.throwIfAborted()` — no Shipyard-specific wrapping.
485
+ * - The worktree is preserved on disk after abort (error-path behavior).
486
+ */
487
+ readonly signal?: AbortSignal;
488
+ /** Override default timeouts for built-in lifecycle steps. Unset keys keep their defaults. */
489
+ readonly timeouts?: Timeouts;
490
+ /**
491
+ * Structured output definition. When provided, the agent's stdout is
492
+ * scanned for the configured XML tag after the iteration completes, and the
493
+ * result is parsed/validated and returned on `RunResult.output`.
494
+ *
495
+ * Use `Output.object({ tag, schema })` for JSON+schema or
496
+ * `Output.string({ tag })` for raw string extraction.
497
+ *
498
+ * Constraints:
499
+ * - `maxIterations` must be `1` (the default).
500
+ * - The resolved prompt must contain the configured opening tag literal.
501
+ *
502
+ * See ADR 0010 for design rationale.
503
+ */
504
+ readonly output?: OutputDefinition;
505
+ }
506
+
507
+ type ResumeRunResultOptions = Omit<RunOptions, "agent" | "sandbox" | "prompt" | "promptFile" | "resumeSession" | "forkSession" | "maxIterations">;
508
+ interface RunResult {
509
+ /** Per-iteration results (use `iterations.length` for the count). */
510
+ readonly iterations: IterationResult[];
511
+ /** The matched completion signal string, or undefined if no signal fired before the iteration limit. */
512
+ readonly completionSignal?: string;
513
+ /** Combined stdout output from all agent iterations. */
514
+ readonly stdout: string;
515
+ /** List of commits made by the agent during the run, each identified by its SHA. */
516
+ readonly commits: {
517
+ sha: string;
518
+ }[];
519
+ /** The branch name the agent worked on inside the sandbox. */
520
+ readonly branch: string;
521
+ /** Path to the log file, if logging was drained to a file. */
522
+ readonly logFilePath?: string;
523
+ /** Host path to the preserved worktree, set when the run succeeded but the worktree had uncommitted changes. */
524
+ readonly preservedWorktreePath?: string;
525
+ /** Continue the last captured agent session for exactly one iteration.
526
+ * Present only when the provider supports resume (`sessionStorage` populated). */
527
+ readonly resume?: (prompt: string, options?: ResumeRunResultOptions) => Promise<RunResult>;
528
+ /**
529
+ * Fork the last captured agent session for exactly one iteration: the
530
+ * parent session JSONL is left intact and the child run gets its own
531
+ * session id, enabling fan-out patterns where multiple children diverge
532
+ * from a single parent. Present only when the provider supports resume
533
+ * (`sessionStorage` populated).
534
+ *
535
+ * Sessions only: fork isolates the agent session, not the branch or
536
+ * sandbox. Safe concurrent fan-out (`Promise.all([r.fork(a), r.fork(b)])`)
537
+ * requires the caller to give each fork a distinct `branch` — `head` and
538
+ * `merge-to-head` are not safe for concurrent forks. See ADR 0018.
539
+ */
540
+ readonly fork?: (prompt: string, options?: ResumeRunResultOptions) => Promise<RunResult>;
541
+ }
542
+ /** Overload: with `Output.object`, returns `RunResult` with typed `output: T`. */
543
+ declare function run<T, A extends AgentProvider>(options: RunOptions<A> & {
544
+ output: OutputObjectDefinition<T>;
545
+ }): Promise<RunResult & {
546
+ output: T;
547
+ }>;
548
+ /** Overload: with `Output.string`, returns `RunResult` with `output: string`. */
549
+ declare function run<A extends AgentProvider>(options: RunOptions<A> & {
550
+ output: OutputStringDefinition;
551
+ }): Promise<RunResult & {
552
+ output: string;
553
+ }>;
554
+ /** Overload: without `output`, returns the standard `RunResult`. */
555
+ declare function run<A extends AgentProvider>(options: RunOptions<A>): Promise<RunResult>;
556
+
557
+ interface CreateSandboxOptions {
558
+ /** Explicit branch for the worktree (required). */
559
+ readonly branch: string;
560
+ /**
561
+ * Ref to fork from when `branch` does not yet exist. Ignored when the branch
562
+ * already exists. Defaults to `HEAD`.
563
+ */
564
+ readonly baseBranch?: string;
565
+ /** Sandbox provider (e.g. docker({ imageName: "shipyard:myrepo" })). */
566
+ readonly sandbox: SandboxProvider;
567
+ /**
568
+ * Host repo directory. Replaces `process.cwd()` as the anchor for
569
+ * `.shipyard/worktrees/`, `.shipyard/.env`, and git operations.
570
+ *
571
+ * - Relative paths are resolved against `process.cwd()`.
572
+ * - Absolute paths are used as-is.
573
+ * - Defaults to `process.cwd()` when omitted.
574
+ */
575
+ readonly cwd?: string;
576
+ /** Lifecycle hooks grouped by execution location (host or sandbox). */
577
+ readonly hooks?: SandboxHooks;
578
+ /** Paths relative to the host repo root to copy into Docker after Git sync. */
579
+ readonly copyToWorktree?: string[];
580
+ /** Override default timeouts for built-in lifecycle steps. Unset keys keep their defaults. */
581
+ readonly timeouts?: Timeouts;
582
+ }
583
+ /**
584
+ * Options accepted by `SandboxRunResult.resume()` / `.fork()`. Mirrors
585
+ * `ResumeRunResultOptions` in `run.ts` — drops the fields owned by the
586
+ * captured run (prompt, iteration count, resumeSession/forkSession bookkeeping).
587
+ *
588
+ * Defined as the base interface that `SandboxRunOptions` extends — the
589
+ * interface-extends shape is cheaper for the TS checker than
590
+ * `Omit<SandboxRunOptions, ...>` (which forces a mapped-type computation
591
+ * on every reference).
592
+ */
593
+ interface ResumeSandboxRunResultOptions {
594
+ /** Key-value map for {{KEY}} placeholder substitution in prompts. */
595
+ readonly promptArgs?: PromptArgs;
596
+ /** Substring(s) the agent emits to stop the iteration loop early. */
597
+ readonly completionSignal?: string | string[];
598
+ /** Idle timeout in seconds. Default: 600. */
599
+ readonly idleTimeoutSeconds?: number;
600
+ /** Grace window in seconds after a completion signal is observed but the agent process has not exited. See ADR 0019. Default: 60. */
601
+ readonly completionTimeoutSeconds?: number;
602
+ /** Display name for this run. */
603
+ readonly name?: string;
604
+ /** Tools the provider must enforce for this controlled phase. */
605
+ readonly toolAllowlist?: readonly string[];
606
+ /** Logging mode. */
607
+ readonly logging?: LoggingOption;
608
+ /**
609
+ * An `AbortSignal` that cancels the run when aborted.
610
+ *
611
+ * - Pre-aborted signal rejects immediately without setup.
612
+ * - Mid-iteration abort kills the in-flight agent subprocess.
613
+ * - The rejected promise surfaces `signal.reason` verbatim.
614
+ * - The `Sandbox` handle remains usable after abort — call `.run()` again
615
+ * with a fresh signal, or `.close()` to tear down.
616
+ */
617
+ readonly signal?: AbortSignal;
618
+ }
619
+ interface SandboxRunOptions extends ResumeSandboxRunResultOptions {
620
+ /** Agent provider to use (e.g. codex(CODEX_MODELS.routine)). */
621
+ readonly agent: AgentProvider;
622
+ /** Inline prompt string (mutually exclusive with promptFile). */
623
+ readonly prompt?: string;
624
+ /** Path to a prompt file (mutually exclusive with prompt). */
625
+ readonly promptFile?: string;
626
+ /** Maximum iterations to run (default: 1). */
627
+ readonly maxIterations?: number;
628
+ /** Resume a prior agent session by id. The session JSONL must exist on the host (captured by a prior `sandbox.run()`). Incompatible with `maxIterations > 1`. */
629
+ readonly resumeSession?: string;
630
+ }
631
+ interface SandboxRunResult {
632
+ /** Per-iteration results (use `iterations.length` for the count). */
633
+ readonly iterations: IterationResult[];
634
+ /** The matched completion signal string, or undefined if none fired. */
635
+ readonly completionSignal?: string;
636
+ /** Combined stdout output from all agent iterations. */
637
+ readonly stdout: string;
638
+ /** List of commits made by the agent during the run. */
639
+ readonly commits: {
640
+ sha: string;
641
+ }[];
642
+ /** Path to the log file, if logging was drained to a file. */
643
+ readonly logFilePath?: string;
644
+ /**
645
+ * Continue the last captured agent session for exactly one iteration inside
646
+ * the same long-lived sandbox. Present only when the provider supports
647
+ * resume (`sessionStorage` populated) and a session id was captured.
648
+ */
649
+ readonly resume?: (prompt: string, options?: ResumeSandboxRunResultOptions) => Promise<SandboxRunResult>;
650
+ /**
651
+ * Fork the last captured agent session for exactly one iteration inside the
652
+ * same long-lived sandbox: the parent session JSONL is left intact and the
653
+ * child run gets its own session id. Present only when the provider
654
+ * supports resume (`sessionStorage` populated) and a session id was
655
+ * captured. See ADR 0018 for fork semantics.
656
+ */
657
+ readonly fork?: (prompt: string, options?: ResumeSandboxRunResultOptions) => Promise<SandboxRunResult>;
658
+ }
659
+ interface SandboxInteractiveOptions {
660
+ /** Agent provider to use (e.g. codex(CODEX_MODELS.routine)). */
661
+ readonly agent: AgentProvider;
662
+ /** Inline prompt string (mutually exclusive with promptFile). */
663
+ readonly prompt?: string;
664
+ /** Path to a prompt file (mutually exclusive with prompt). */
665
+ readonly promptFile?: string;
666
+ /** Key-value map for {{KEY}} placeholder substitution in prompts. */
667
+ readonly promptArgs?: PromptArgs;
668
+ /** Display name for this interactive session. */
669
+ readonly name?: string;
670
+ /**
671
+ * An `AbortSignal` that cancels the interactive session when aborted.
672
+ *
673
+ * - Pre-aborted signal rejects immediately without setup.
674
+ * - The rejected promise surfaces `signal.reason` verbatim.
675
+ * - The `Sandbox` handle remains usable after abort.
676
+ */
677
+ readonly signal?: AbortSignal;
678
+ }
679
+ interface SandboxInteractiveResult {
680
+ /** List of commits made during the interactive session. */
681
+ readonly commits: {
682
+ sha: string;
683
+ }[];
684
+ /** Exit code of the interactive process. */
685
+ readonly exitCode: number;
686
+ }
687
+ interface CloseResult {
688
+ /** Host path to the preserved worktree, set when the worktree had uncommitted changes. */
689
+ readonly preservedWorktreePath?: string;
690
+ }
691
+ interface Sandbox {
692
+ /** The branch the worktree is on. */
693
+ readonly branch: string;
694
+ /** Host path to the worktree. */
695
+ readonly worktreePath: string;
696
+ /** Invoke an agent inside the existing sandbox. */
697
+ run(options: SandboxRunOptions): Promise<SandboxRunResult>;
698
+ /** Launch an interactive agent session inside the existing sandbox. */
699
+ interactive(options: SandboxInteractiveOptions): Promise<SandboxInteractiveResult>;
700
+ /**
701
+ * Execute a command inside the existing sandbox.
702
+ *
703
+ * `cwd` defaults to the sandbox repo path (same default `interactive()`
704
+ * uses), so callers get the same working directory across providers. Pass
705
+ * `cwd` to override.
706
+ *
707
+ * Returns the full `ExecResult` — non-zero `exitCode` is surfaced, not
708
+ * thrown. Callers that want strict semantics should check `result.exitCode`
709
+ * themselves (matching the contract of `SessionTransferHandle.exec`).
710
+ */
711
+ exec(command: string, options?: SandboxExecOptions): Promise<ExecResult>;
712
+ /** Tear down the sandbox and worktree. */
713
+ close(): Promise<CloseResult>;
714
+ /** Auto teardown via `await using`. */
715
+ [Symbol.asyncDispose](): Promise<void>;
716
+ }
717
+ /** Options accepted by `Sandbox.exec()`. Mirrors the provider handle's `exec` options. */
718
+ interface SandboxExecOptions {
719
+ /** Per-line stdout callback for streaming output. */
720
+ readonly onLine?: (line: string) => void;
721
+ /** Working directory for the command. Defaults to the sandbox repo path. */
722
+ readonly cwd?: string;
723
+ /** Run the command with sudo, when the provider supports it. */
724
+ readonly sudo?: boolean;
725
+ /** Stdin payload — piped to the child process and then closed. Avoids the Linux 128 KB per-arg limit. */
726
+ readonly stdin?: string;
727
+ /** Abort the command when the surrounding operation is cancelled. */
728
+ readonly signal?: AbortSignal;
729
+ /** Reject/terminate the command after this many combined output bytes. */
730
+ readonly maxOutputBytes?: number;
731
+ }
732
+ /**
733
+ * Eagerly creates a git worktree on the provided explicit branch and starts
734
+ * a Docker sandbox with a synced Git workspace. Returns a Sandbox handle that
735
+ * can be reused across multiple `run()` calls.
736
+ */
737
+ declare const createSandbox: (options: CreateSandboxOptions) => Promise<Sandbox>;
738
+
739
+ export { type AgentProvider as A, run as B, type CloseResult as C, type IterationResult as I, type LoggingOption as L, Output as O, type PromptArgs as P, type ResumeSandboxRunResultOptions as R, type SandboxHooks as S, type Timeouts as T, type Sandbox as a, type AgentCommandOptions as b, type AgentStreamEvent as c, CODEX_MODELS as d, CODEX_REASONING_EFFORTS as e, type ClaudeCodeOptions as f, type CodexModelConfig as g, type CodexOptions as h, type CodexReasoningEffort as i, type CreateSandboxOptions as j, type IterationUsage as k, type OutputDefinition as l, type OutputObjectDefinition as m, type OutputStringDefinition as n, type PrintCommand as o, type RunOptions as p, type RunResult as q, type SandboxExecOptions as r, type SandboxInteractiveOptions as s, type SandboxInteractiveResult as t, type SandboxRunOptions as u, type SandboxRunResult as v, StructuredOutputError as w, claudeCode as x, codex as y, createSandbox as z };