@arnilo/prism-coding-agent 0.0.8 → 0.0.10

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.
package/CHANGELOG.md CHANGED
@@ -1,4 +1,27 @@
1
1
  # Changelog
2
+ ## [0.0.10] - 2026-07-21
3
+
4
+ ### Changed
5
+
6
+ - Released with exact 0.0.10 graph.
7
+
8
+ ### Notes
9
+
10
+ - Sandbox composition / workspace-mode contract lives in `@arnilo/prism-coding-security` (required `workspaceMode`, same-tree Git via `createGitTools(composition.workspaceRoot, { execFile })`). Coding-agent tool surfaces unchanged for 0.0.10.
11
+
12
+ ## [0.0.96] - 2026-07-21
13
+
14
+ ### Changed
15
+
16
+ - Released with exact 0.0.96 graph.
17
+
18
+ ## [0.0.9] - 2026-07-21
19
+
20
+ - Added bounded native `repo_list` / `repo_search` tools with streaming walks, literal/regex search, finite depth/entry/match/scan/time caps, and pluggable `RepositoryOperations`.
21
+ - `createCodingTools()` / `createAllTools()` now include list/search; `createReadOnlyTools()` deliberately expands to `read` + `repo_list` + `repo_search`.
22
+ - Added opt-in structured Git tools via `createGitTools()` / `createGitOperations()`: status (porcelain v2), bounded diff, branch validate/create/switch, worktree add/list/remove, patch check/apply/reverse with rollback, explicit-path commit (host `commitIdentity`), and bounded PR handoff artifacts. Named `coding_check` runs host-declared executables only. Git uses argument arrays with hooks/credential prompts/external diff disabled and never pushes.
23
+ - Added bounded durable coding-plan/checkpoint helpers (`writeCodingPlanFile`, `buildCodingCheckpointMetadata`, `assertCodingResumeAllowed`, `fingerprintJson`) so hosts compose plans/todos/background resume from existing workflow primitives without a second runtime.
24
+ - Added network-free adversarial evaluation fixtures (`eval-fixtures.test.ts`) grading safe native list vs shell, Git injection, dirty-tree rollback, named-check failure, PR-handoff artifacts, and prompt-injection file content via `@arnilo/prism-evals`.
2
25
 
3
26
  ## [0.0.8] - 2026-07-20
4
27
 
@@ -8,11 +31,6 @@
8
31
 
9
32
  - Released with the exact 0.0.7 first-party package graph.
10
33
 
11
- All notable changes to this project will be documented in this file.
12
-
13
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
14
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
15
-
16
34
  ## [0.0.6] - 2026-07-19
17
35
 
18
36
  ### Added
@@ -32,7 +50,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
32
50
 
33
51
  - Pinned the required `@arnilo/prism` peer and package metadata to 0.0.5; runtime behavior is unchanged.
34
52
 
35
-
36
53
  ## [0.0.4] - 2026-07-14
37
54
 
38
55
  ### Added
package/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # @arnilo/prism-coding-agent
2
2
 
3
- Optional first-party coding tools package for [Prism](https://www.npmjs.com/package/@arnilo/prism). Provides host shell/filesystem tools — `shell`, `read`, `write`, `edit` — as Prism `ToolDefinition` objects. **Inert until a host imports it and registers the tools into a `ToolRegistry`.**
3
+ Optional first-party coding tools package for [Prism](https://www.npmjs.com/package/@arnilo/prism). Provides host shell/filesystem/repository tools — `shell`, `read`, `write`, `edit`, `repo_list`, `repo_search` plus opt-in structured Git/check tools via `createGitTools()` and durable plan/checkpoint helpers for workflow composition — as Prism `ToolDefinition` objects. **Inert until a host imports it and registers the tools into a `ToolRegistry`.**
4
4
 
5
- Behavior is a behavioral port of the pi coding agent's `bash`/`read`/`write`/`edit` tools, adapted to Prism's `ToolDefinition` / `ToolResult` contracts (no `@earendil-works/*` or `typebox` dependencies).
5
+ Behavior is a behavioral port of the pi coding agent's `bash`/`read`/`write`/`edit` tools, adapted to Prism's `ToolDefinition` / `ToolResult` contracts (no `@earendil-works/*` or `typebox` dependencies). List/search/Git are native Prism tools.
6
6
 
7
- > ⚠️ **These tools perform real shell and filesystem operations on the host. They provide no sandbox.** Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. See the [coding agent tools docs](https://github.com/ashiqrniloy/prism/blob/main/docs/coding-agent-tools.md) and the [host security guide](https://github.com/ashiqrniloy/prism/blob/main/docs/host-security.md).
7
+ > ⚠️ **These tools perform real shell and filesystem operations on the host. They provide no sandbox.** Gate them with Prism `PermissionPolicy` / `ToolValidator` / trust policies before registering them for any provider turn. For disposable sandbox composition with required `workspaceMode`, use `@arnilo/prism-coding-security` (`createSandboxCodingComposition`). See the [coding agent tools docs](https://github.com/ashiqrniloy/prism/blob/main/docs/coding-agent-tools.md) and the [host security guide](https://github.com/ashiqrniloy/prism/blob/main/docs/host-security.md).
8
8
 
9
9
  ## Install
10
10
 
@@ -64,6 +64,9 @@ const remoteWrite = createWriteTool(process.cwd(), {
64
64
  | `read` | `{ path, offset?, limit? }` | Streamed bounded text page or bounded `[note, ImageContent]`. |
65
65
  | `write` | `{ path, content }` | Bounded UTF-8 input; `Successfully wrote N bytes (M lines) to <abs>`. |
66
66
  | `edit` | `{ path, edits: [{oldText,newText}] }` | Bounded target/input/count; `Successfully replaced N block(s)` + diff metadata. |
67
+ | `repo_list` | `{ path?, includeHidden?, maxDepth?, maxResults?, offset? }` | Deterministic relative entries; skips hidden/excluded basenames; does not follow symlinks; paginates with `nextOffset`. |
68
+ | `repo_search` | `{ query, path?, mode?, caseSensitive?, includeHidden?, context?, maxMatches? }` | Literal (default) or bounded regex matches with context; skips binary/excluded paths; finite scan/match/time caps. |
69
+ | `git_*` / `coding_check` | via `createGitTools(cwd, { commitIdentity, checks? })` | Opt-in structured Git status/diff/branch/worktree/apply/commit/PR-handoff and named checks. Not in `createCodingTools()`. |
67
70
 
68
71
  ### pi name mapping
69
72
 
@@ -71,17 +74,20 @@ const remoteWrite = createWriteTool(process.cwd(), {
71
74
  | --- | --- |
72
75
  | `shell` | `bash` |
73
76
  | `read` / `write` / `edit` | `read` / `write` / `edit` |
77
+ | `repo_list` / `repo_search` | _(native; no pi equivalent shipped)_ |
74
78
 
75
79
  ## Exports
76
80
 
77
- Factories: `createShellTool`, `createReadTool`, `createWriteTool`, `createEditTool`, `createCodingTools`, `createReadOnlyTools`, `createAllTools`, `createLocalBashOperations`.
81
+ Factories: `createShellTool`, `createReadTool`, `createWriteTool`, `createEditTool`, `createRepoListTool`, `createRepoSearchTool`, `createCodingTools`, `createReadOnlyTools`, `createAllTools`, `createGitTools`, `createCodingCheckTool`, `createLocalBashOperations`, `createLocalRepositoryOperations`, `createGitOperations`.
78
82
 
79
- Helpers: `detectSupportedImageMimeType`, `detectSupportedImageMimeTypeFromFile`, `getShellConfig`, `killProcessTree`, `waitForChildProcess`, `withFileMutationQueue`. Default/hard coding limit constants are exported for host configuration.
83
+ Helpers: `detectSupportedImageMimeType`, `detectSupportedImageMimeTypeFromFile`, `getShellConfig`, `killProcessTree`, `waitForChildProcess`, `withFileMutationQueue`, `resolveRepositoryLimits`, `writeCodingPlanFile`, `readCodingPlanFile`, `buildCodingCheckpointMetadata`, `validateCodingCheckpointMetadata`, `assertCodingResumeAllowed`, `fingerprintJson`. Default/hard coding, repository, Git, and plan/checkpoint limit constants are exported for host configuration.
80
84
 
81
85
  Option/operation types: `ToolsOptions`, `ShellToolOptions`/`BashOperations`, `ReadToolOptions`/`ReadOperations`/`ReadTextOptions`/`ReadTextResult`, `WriteToolOptions`/`WriteOperations`, `EditToolOptions`/`EditOperations`/`EditToolDetails`.
82
86
 
83
87
  Text reads stop after one page or `maxScanBytes` instead of loading the file. Custom `ReadOperations` must implement bounded `readText` and `statFile`; custom `EditOperations` must implement `statFile` and honor the supplied read cap/signal. Successful truncated shell output is retained in an exclusive Unix `0600` temp file owned by the host; timeout, abort, output-limit, and spill failures remove unpublished spill files. Hosts should delete published `metadata.fullOutputPath` files after use.
84
88
 
89
+ Network-free adversarial evaluation fixtures live in `src/__tests__/eval-fixtures.test.ts` and reuse `@arnilo/prism-evals` for CI thresholds. See `examples/coding-browser-evaluation.ts` and `docs/evaluations.md`.
90
+
85
91
  ## License
86
92
 
87
93
  MIT
@@ -0,0 +1,6 @@
1
+ import type { ArtifactWriter } from "./git.js";
2
+ export declare function sha256Hex(data: Buffer): string;
3
+ /** Write artifacts under a host directory as `file://` URIs with SHA-256 metadata. */
4
+ export declare function createDirectoryArtifactWriter(rootDir: string): ArtifactWriter;
5
+ /** Spill into the process temp directory (tests / ephemeral hosts). */
6
+ export declare function createTempArtifactWriter(prefix?: string): ArtifactWriter;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Host-owned artifact helpers for Git patch/bundle/PR-handoff spill.
3
+ *
4
+ * Prism never pushes, authenticates to GitHub/GitLab, or opens a PR. Artifacts
5
+ * are written only through an explicit host callback or a bounded temp-file writer.
6
+ */
7
+ import { createHash, randomBytes } from "node:crypto";
8
+ import { mkdir, writeFile } from "node:fs/promises";
9
+ import { tmpdir } from "node:os";
10
+ import { join } from "node:path";
11
+ export function sha256Hex(data) {
12
+ return createHash("sha256").update(data).digest("hex");
13
+ }
14
+ /** Write artifacts under a host directory as `file://` URIs with SHA-256 metadata. */
15
+ export function createDirectoryArtifactWriter(rootDir) {
16
+ return async ({ kind, filename, bytes }) => {
17
+ const safeName = filename.replace(/[^A-Za-z0-9._-]+/g, "_");
18
+ const dir = join(rootDir, kind);
19
+ await mkdir(dir, { recursive: true });
20
+ const path = join(dir, `${randomBytes(8).toString("hex")}-${safeName}`);
21
+ await writeFile(path, bytes, { mode: 0o600 });
22
+ return {
23
+ kind,
24
+ uri: `file://${path}`,
25
+ sha256: sha256Hex(bytes),
26
+ bytes: bytes.length,
27
+ };
28
+ };
29
+ }
30
+ /** Spill into the process temp directory (tests / ephemeral hosts). */
31
+ export function createTempArtifactWriter(prefix = "prism-git-artifact") {
32
+ const root = join(tmpdir(), `${prefix}-${randomBytes(6).toString("hex")}`);
33
+ return createDirectoryArtifactWriter(root);
34
+ }
35
+ //# sourceMappingURL=artifacts.js.map
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Named check tool: host declares fixed executable+args; model selects only a name.
3
+ */
4
+ import type { ExecutionPolicy, ToolDefinition } from "@arnilo/prism";
5
+ export interface NamedCheckDefinition {
6
+ /** Absolute executable path, or a basename resolved only via host-supplied env PATH. */
7
+ readonly file: string;
8
+ readonly args: readonly string[];
9
+ readonly cwd?: string;
10
+ /** Exact env allow-list (never inherits process.env unless host copies values in). */
11
+ readonly env?: Readonly<Record<string, string>>;
12
+ readonly timeoutMs?: number;
13
+ }
14
+ export interface CodingCheckToolOptions {
15
+ readonly executionPolicy?: ExecutionPolicy;
16
+ readonly checks: Readonly<Record<string, NamedCheckDefinition>>;
17
+ readonly maxConcurrency?: number;
18
+ readonly maxDiagnosticLines?: number;
19
+ readonly maxOutputBytes?: number;
20
+ readonly defaultTimeoutMs?: number;
21
+ }
22
+ /**
23
+ * Create the `coding_check` tool. Model may only select a declared name — never
24
+ * executable path or arguments.
25
+ */
26
+ export declare function createCodingCheckTool(cwd: string, options: CodingCheckToolOptions): ToolDefinition;
package/dist/checks.js ADDED
@@ -0,0 +1,249 @@
1
+ import { spawn } from "node:child_process";
2
+ import { rm } from "node:fs/promises";
3
+ import { isAbsolute } from "node:path";
4
+ import { enforceExecutionPolicy } from "./execution-policy.js";
5
+ import { DEFAULT_CHECK_TIMEOUT_MS, DEFAULT_MAX_CHECK_CONCURRENCY, DEFAULT_MAX_CHECK_DIAGNOSTIC_LINES, DEFAULT_MAX_CHECK_NAMES, DEFAULT_MAX_CHECK_OUTPUT_BYTES, HARD_CHECK_TIMEOUT_MS, HARD_MAX_BYTES, HARD_MAX_CHECK_CONCURRENCY, HARD_MAX_CHECK_DIAGNOSTIC_LINES, HARD_MAX_CHECK_NAMES, HARD_MAX_CHECK_OUTPUT_BYTES, validateCodingLimit, } from "./limits.js";
6
+ import { OutputAccumulator } from "./output-accumulator.js";
7
+ class Semaphore {
8
+ max;
9
+ active = 0;
10
+ waiters = [];
11
+ constructor(max) {
12
+ this.max = max;
13
+ }
14
+ async acquire() {
15
+ if (this.active < this.max) {
16
+ this.active++;
17
+ return;
18
+ }
19
+ await new Promise((resolve) => this.waiters.push(resolve));
20
+ this.active++;
21
+ }
22
+ release() {
23
+ this.active--;
24
+ const next = this.waiters.shift();
25
+ if (next)
26
+ next();
27
+ }
28
+ }
29
+ function errorResult(toolCallId, message) {
30
+ return {
31
+ toolCallId,
32
+ name: "coding_check",
33
+ content: [{ type: "text", text: message }],
34
+ error: { message },
35
+ };
36
+ }
37
+ function validateCheckMap(checks) {
38
+ const names = Object.keys(checks);
39
+ if (names.length < 1)
40
+ throw new Error("checks must declare at least one named command");
41
+ validateCodingLimit("checkNames", names.length, HARD_MAX_CHECK_NAMES);
42
+ const map = new Map();
43
+ for (const name of names) {
44
+ if (!/^[A-Za-z][A-Za-z0-9_-]{0,63}$/.test(name)) {
45
+ throw new Error(`invalid check name: ${name}`);
46
+ }
47
+ const def = checks[name];
48
+ if (!def || typeof def.file !== "string" || def.file.length === 0) {
49
+ throw new Error(`check ${name} requires a non-empty file`);
50
+ }
51
+ if (!Array.isArray(def.args) || def.args.some((a) => typeof a !== "string" || a.includes("\0"))) {
52
+ throw new Error(`check ${name} args must be strings without NUL`);
53
+ }
54
+ map.set(name, def);
55
+ }
56
+ void DEFAULT_MAX_CHECK_NAMES;
57
+ return map;
58
+ }
59
+ async function runNamedCheck(def, cwd, options) {
60
+ if (options.signal?.aborted) {
61
+ return { exitCode: null, output: "", timedOut: false, aborted: true };
62
+ }
63
+ const displayBytes = Math.min(50 * 1024, HARD_MAX_BYTES, options.maxBytes);
64
+ const accumulator = new OutputAccumulator({
65
+ maxLines: options.maxLines,
66
+ maxBytes: displayBytes,
67
+ maxTotalOutputBytes: options.maxBytes,
68
+ tempFilePrefix: "prism-check",
69
+ });
70
+ return await new Promise((resolve, reject) => {
71
+ let settled = false;
72
+ let timedOut = false;
73
+ let aborted = false;
74
+ let child;
75
+ let timer;
76
+ const finish = (exitCode) => {
77
+ if (settled)
78
+ return;
79
+ settled = true;
80
+ if (timer)
81
+ clearTimeout(timer);
82
+ options.signal?.removeEventListener("abort", onAbort);
83
+ accumulator.finish();
84
+ const snap = accumulator.snapshot({ persistIfTruncated: false });
85
+ if (snap.fullOutputPath) {
86
+ void rm(snap.fullOutputPath, { force: true }).catch(() => undefined);
87
+ }
88
+ resolve({
89
+ exitCode,
90
+ output: snap.content,
91
+ timedOut,
92
+ aborted,
93
+ });
94
+ };
95
+ const onAbort = () => {
96
+ aborted = true;
97
+ try {
98
+ if (child.pid)
99
+ process.kill(-child.pid, "SIGKILL");
100
+ }
101
+ catch {
102
+ try {
103
+ child.kill("SIGKILL");
104
+ }
105
+ catch {
106
+ /* ignore */
107
+ }
108
+ }
109
+ };
110
+ try {
111
+ const env = { PATH: "/usr/bin:/bin", LANG: "C", ...(def.env ?? {}) };
112
+ child = spawn(def.file, [...def.args], {
113
+ cwd: def.cwd ?? cwd,
114
+ env,
115
+ stdio: ["ignore", "pipe", "pipe"],
116
+ detached: process.platform !== "win32",
117
+ windowsHide: true,
118
+ shell: false,
119
+ });
120
+ }
121
+ catch (error) {
122
+ reject(error instanceof Error ? error : new Error(String(error)));
123
+ return;
124
+ }
125
+ child.stdout.on("data", (chunk) => accumulator.append(chunk));
126
+ child.stderr.on("data", (chunk) => accumulator.append(chunk));
127
+ child.on("error", (error) => {
128
+ if (!settled) {
129
+ settled = true;
130
+ reject(error);
131
+ }
132
+ });
133
+ child.on("close", (code) => finish(code));
134
+ timer = setTimeout(() => {
135
+ timedOut = true;
136
+ try {
137
+ if (child.pid)
138
+ process.kill(-child.pid, "SIGKILL");
139
+ }
140
+ catch {
141
+ child.kill("SIGKILL");
142
+ }
143
+ }, options.timeoutMs);
144
+ options.signal?.addEventListener("abort", onAbort, { once: true });
145
+ });
146
+ }
147
+ /**
148
+ * Create the `coding_check` tool. Model may only select a declared name — never
149
+ * executable path or arguments.
150
+ */
151
+ export function createCodingCheckTool(cwd, options) {
152
+ const checks = validateCheckMap(options.checks);
153
+ const maxConcurrency = validateCodingLimit("maxConcurrency", options.maxConcurrency ?? DEFAULT_MAX_CHECK_CONCURRENCY, HARD_MAX_CHECK_CONCURRENCY);
154
+ const maxDiagnosticLines = validateCodingLimit("maxDiagnosticLines", options.maxDiagnosticLines ?? DEFAULT_MAX_CHECK_DIAGNOSTIC_LINES, HARD_MAX_CHECK_DIAGNOSTIC_LINES);
155
+ const maxOutputBytes = validateCodingLimit("maxOutputBytes", options.maxOutputBytes ?? DEFAULT_MAX_CHECK_OUTPUT_BYTES, HARD_MAX_CHECK_OUTPUT_BYTES);
156
+ const defaultTimeoutMs = validateCodingLimit("defaultTimeoutMs", options.defaultTimeoutMs ?? DEFAULT_CHECK_TIMEOUT_MS, HARD_CHECK_TIMEOUT_MS);
157
+ const semaphore = new Semaphore(maxConcurrency);
158
+ const names = [...checks.keys()].sort();
159
+ return {
160
+ name: "coding_check",
161
+ description: `Run a host-declared named check. Allowed names: ${names.join(", ")}. The model cannot choose executables or arguments.`,
162
+ exclusive: true,
163
+ parameters: {
164
+ type: "object",
165
+ properties: {
166
+ name: {
167
+ type: "string",
168
+ description: "Declared check name",
169
+ enum: names,
170
+ },
171
+ },
172
+ required: ["name"],
173
+ additionalProperties: false,
174
+ },
175
+ async execute(args, context) {
176
+ const toolCallId = context.toolCallId;
177
+ if (context.signal?.aborted)
178
+ return errorResult(toolCallId, "Operation aborted");
179
+ const name = typeof args.name === "string" ? args.name : "";
180
+ const def = checks.get(name);
181
+ if (!def)
182
+ return errorResult(toolCallId, `unknown check name: ${name}`);
183
+ if (!isAbsolute(def.file) && !(def.env && typeof def.env.PATH === "string")) {
184
+ return errorResult(toolCallId, `check ${name} file must be absolute unless env.PATH is explicitly provided`);
185
+ }
186
+ const timeoutMs = validateCodingLimit("timeoutMs", def.timeoutMs ?? defaultTimeoutMs, HARD_CHECK_TIMEOUT_MS);
187
+ const policyCheck = await enforceExecutionPolicy(options.executionPolicy, {
188
+ kind: "check",
189
+ operation: name,
190
+ paths: [def.cwd ?? cwd],
191
+ risk: "medium",
192
+ metadata: {
193
+ checkName: name,
194
+ file: def.file,
195
+ args: def.args,
196
+ sessionId: context.sessionId,
197
+ runId: context.runId,
198
+ signal: context.signal,
199
+ },
200
+ }, toolCallId, "coding_check");
201
+ if (!policyCheck.allowed)
202
+ return policyCheck.result;
203
+ await semaphore.acquire();
204
+ try {
205
+ const result = await runNamedCheck(def, cwd, {
206
+ signal: context.signal,
207
+ timeoutMs,
208
+ maxLines: maxDiagnosticLines,
209
+ maxBytes: maxOutputBytes,
210
+ });
211
+ if (result.aborted)
212
+ return errorResult(toolCallId, "Operation aborted");
213
+ if (result.timedOut) {
214
+ return {
215
+ toolCallId,
216
+ name: "coding_check",
217
+ content: [{ type: "text", text: `${result.output}\n[check timed out]`.trim() }],
218
+ error: { message: `check ${name} timed out` },
219
+ metadata: { name, exitCode: result.exitCode, timedOut: true },
220
+ };
221
+ }
222
+ const footer = result.exitCode === 0 ? "" : `\n[check exited with code ${result.exitCode}]`;
223
+ return {
224
+ toolCallId,
225
+ name: "coding_check",
226
+ content: [
227
+ {
228
+ type: "text",
229
+ text: `${result.output}${footer}`.trim() || `(check ${name} produced no output)`,
230
+ },
231
+ ],
232
+ metadata: {
233
+ name,
234
+ exitCode: result.exitCode,
235
+ summary: result.exitCode === 0 ? "passed" : `failed (${result.exitCode})`,
236
+ },
237
+ };
238
+ }
239
+ catch (error) {
240
+ const message = error instanceof Error ? error.message : String(error);
241
+ return errorResult(toolCallId, message);
242
+ }
243
+ finally {
244
+ semaphore.release();
245
+ }
246
+ },
247
+ };
248
+ }
249
+ //# sourceMappingURL=checks.js.map
@@ -0,0 +1,159 @@
1
+ import type { JsonObject } from "@arnilo/prism";
2
+ export declare const CODING_CHECKPOINT_SCHEMA_VERSION: 1;
3
+ /** Workflow shared-state key that holds coding checkpoint metadata. */
4
+ export declare const CODING_STATE_KEY = "coding";
5
+ export type CodingArtifactKind = "plan" | "workspace" | "patch" | "bundle" | "diff" | "other";
6
+ export type CodingTaskStatus = "planned" | "editing" | "checking" | "awaiting_approval" | "ready_for_handoff" | "completed" | "failed" | "cancelled";
7
+ export interface CodingArtifactRef {
8
+ readonly kind: CodingArtifactKind;
9
+ readonly uri: string;
10
+ readonly sha256: string;
11
+ readonly bytes: number;
12
+ }
13
+ export interface CodingTodoItem {
14
+ readonly id: string;
15
+ readonly text: string;
16
+ readonly done: boolean;
17
+ }
18
+ export interface CodingCheckSummary {
19
+ readonly name: string;
20
+ readonly exitCode: number;
21
+ readonly summary: string;
22
+ }
23
+ export interface CodingFingerprints {
24
+ /** Workflow definition revision string. */
25
+ readonly workflowRevision: string;
26
+ /** Optional definition hash captured after `defineWorkflow`. */
27
+ readonly definitionHash?: string;
28
+ /** Host-pinned sandbox/image digest when a disposable sandbox is used. */
29
+ readonly imageDigest?: string;
30
+ /** Host-computed hash of the selected tool surface. */
31
+ readonly toolFingerprint: string;
32
+ /** Host-computed hash of the selected execution/approval policy. */
33
+ readonly policyFingerprint: string;
34
+ }
35
+ export interface CodingHandoffSummary {
36
+ readonly base: string;
37
+ readonly head: string;
38
+ readonly changedPathCount: number;
39
+ readonly checkCount: number;
40
+ readonly artifact?: CodingArtifactRef;
41
+ }
42
+ export interface CodingCheckpointMetadata {
43
+ readonly schemaVersion: typeof CODING_CHECKPOINT_SCHEMA_VERSION;
44
+ readonly taskId: string;
45
+ readonly workspaceRoot: string;
46
+ readonly baseBranch: string;
47
+ readonly branch: string;
48
+ readonly worktreePath?: string;
49
+ /** Workspace-relative plan path (for example `plans/task-1.md`). */
50
+ readonly planPath: string;
51
+ readonly plan: CodingArtifactRef;
52
+ readonly workspaceExport?: CodingArtifactRef;
53
+ readonly artifacts: readonly CodingArtifactRef[];
54
+ readonly checks: readonly CodingCheckSummary[];
55
+ readonly handoff?: CodingHandoffSummary;
56
+ readonly status: CodingTaskStatus;
57
+ readonly fingerprints: CodingFingerprints;
58
+ readonly todos: readonly CodingTodoItem[];
59
+ readonly updatedAt: string;
60
+ }
61
+ export interface CodingCheckpointLimitOptions {
62
+ readonly maxPlanBytes?: number;
63
+ readonly maxTodos?: number;
64
+ readonly maxTodoTextBytes?: number;
65
+ readonly maxArtifacts?: number;
66
+ readonly maxArtifactBytes?: number;
67
+ readonly maxCheckSummaryBytes?: number;
68
+ readonly maxCheckpointBytes?: number;
69
+ }
70
+ export interface ResolvedCodingCheckpointLimits {
71
+ readonly maxPlanBytes: number;
72
+ readonly maxTodos: number;
73
+ readonly maxTodoTextBytes: number;
74
+ readonly maxArtifacts: number;
75
+ readonly maxArtifactBytes: number;
76
+ readonly maxCheckSummaryBytes: number;
77
+ readonly maxCheckpointBytes: number;
78
+ }
79
+ export declare class CodingCheckpointError extends Error {
80
+ readonly code = "ERR_PRISM_CODING_CHECKPOINT";
81
+ constructor(message: string);
82
+ }
83
+ export declare function resolveCodingCheckpointLimits(options?: CodingCheckpointLimitOptions): ResolvedCodingCheckpointLimits;
84
+ /** Deterministic SHA-256 fingerprint over a JSON-stable encoding. */
85
+ export declare function fingerprintJson(value: unknown): string;
86
+ export declare function createCodingArtifactRef(input: {
87
+ readonly kind: CodingArtifactKind;
88
+ readonly uri: string;
89
+ readonly bytes: Buffer;
90
+ readonly maxBytes?: number;
91
+ }): CodingArtifactRef;
92
+ export declare function verifyCodingArtifactBytes(ref: CodingArtifactRef, bytes: Buffer, limits?: CodingCheckpointLimitOptions): void;
93
+ export declare function createCodingPlanMarkdown(input: {
94
+ readonly title: string;
95
+ readonly taskId: string;
96
+ readonly status?: CodingTaskStatus;
97
+ readonly todos: readonly {
98
+ readonly id?: string;
99
+ readonly text: string;
100
+ readonly done?: boolean;
101
+ }[];
102
+ readonly notes?: string;
103
+ readonly limits?: CodingCheckpointLimitOptions;
104
+ }): string;
105
+ export declare function parseCodingPlanTodos(markdown: string, limits?: CodingCheckpointLimitOptions): CodingTodoItem[];
106
+ export declare function writeCodingPlanFile(input: {
107
+ readonly workspaceRoot: string;
108
+ readonly planPath: string;
109
+ readonly markdown: string;
110
+ readonly limits?: CodingCheckpointLimitOptions;
111
+ }): Promise<CodingArtifactRef>;
112
+ export declare function readCodingPlanFile(input: {
113
+ readonly workspaceRoot: string;
114
+ readonly planPath: string;
115
+ readonly expected?: CodingArtifactRef;
116
+ readonly limits?: CodingCheckpointLimitOptions;
117
+ }): Promise<{
118
+ markdown: string;
119
+ artifact: CodingArtifactRef;
120
+ todos: CodingTodoItem[];
121
+ }>;
122
+ export declare function buildCodingCheckpointMetadata(input: {
123
+ readonly taskId: string;
124
+ readonly workspaceRoot: string;
125
+ readonly baseBranch: string;
126
+ readonly branch: string;
127
+ readonly planPath: string;
128
+ readonly plan: CodingArtifactRef;
129
+ readonly fingerprints: CodingFingerprints;
130
+ readonly status?: CodingTaskStatus;
131
+ readonly todos?: readonly CodingTodoItem[];
132
+ readonly worktreePath?: string;
133
+ readonly workspaceExport?: CodingArtifactRef;
134
+ readonly artifacts?: readonly CodingArtifactRef[];
135
+ readonly checks?: readonly CodingCheckSummary[];
136
+ readonly handoff?: CodingHandoffSummary;
137
+ readonly updatedAt?: string;
138
+ readonly limits?: CodingCheckpointLimitOptions;
139
+ }): CodingCheckpointMetadata;
140
+ export declare function validateCodingCheckpointMetadata(value: unknown, limits?: CodingCheckpointLimitOptions): CodingCheckpointMetadata;
141
+ /**
142
+ * Fail closed before resume/import when ownership-equivalent fingerprints diverge
143
+ * or artifact references cannot be verified.
144
+ */
145
+ export declare function assertCodingResumeAllowed(input: {
146
+ readonly metadata: CodingCheckpointMetadata;
147
+ readonly expected: CodingFingerprints;
148
+ readonly expectedWorkspaceRoot?: string;
149
+ readonly expectedBaseBranch?: string;
150
+ readonly planBytes?: Buffer;
151
+ readonly workspaceExportBytes?: Buffer;
152
+ readonly limits?: CodingCheckpointLimitOptions;
153
+ }): CodingCheckpointMetadata;
154
+ /** Extract and validate `state.coding` when present. */
155
+ export declare function readCodingCheckpointFromState(state: Readonly<Record<string, unknown>>, limits?: CodingCheckpointLimitOptions): CodingCheckpointMetadata | undefined;
156
+ export declare function codingCheckpointStatePatch(metadata: CodingCheckpointMetadata): JsonObject;
157
+ /** Exported for tests that need a quick digest helper without importing crypto. */
158
+ export declare function codingSha256Hex(data: Buffer | string): string;
159
+ export declare function codingPlanPathForTask(taskId: string): string;