@zhivex-ai/harness 1.4.1 → 1.5.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 (63) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/README.md +1 -1
  3. package/SUPPORT.md +1 -1
  4. package/contracts/stable-api-signatures.json +7 -7
  5. package/dist/acp-cli.js +10 -10
  6. package/dist/approvals/approval-diff.d.ts +2 -1
  7. package/dist/approvals/approval-preview.d.ts +1 -1
  8. package/dist/audit/index.js +6 -6
  9. package/dist/{audit-pack-e4vaq7np.js → audit-pack-kvw7ng34.js} +3 -3
  10. package/dist/{cli-entry-rgs6bxmy.js → cli-entry-75j3r49b.js} +2 -2
  11. package/dist/{cli-entry-7f0cen32.js → cli-entry-928p8h41.js} +135 -58
  12. package/dist/{cli-entry-a890x3kq.js → cli-entry-h2yc9x37.js} +7 -5
  13. package/dist/{cli-entry-3s63zn4w.js → cli-entry-q8bgkn9m.js} +1 -1
  14. package/dist/{cli-1cf4dcnj.js → cli-vyrytn3m.js} +14 -8
  15. package/dist/cli.js +2 -2
  16. package/dist/compat/code-support.js +8 -8
  17. package/dist/engine/acp.js +11 -11
  18. package/dist/engine/client.js +10 -10
  19. package/dist/engine/desktop-state-v1.js +6 -6
  20. package/dist/engine/index.js +15 -15
  21. package/dist/engine/mcp-stdio-v1.js +5 -5
  22. package/dist/engine/service.js +11 -11
  23. package/dist/hostile-repository-demo.js +138 -60
  24. package/dist/{index-dtb2c6ds.js → index-5zbq9ztj.js} +2 -2
  25. package/dist/{index-6599mpmy.js → index-63gbp0cz.js} +3 -3
  26. package/dist/{index-xdbkcccf.js → index-6dxrbnxm.js} +2 -2
  27. package/dist/{index-0zs2dz26.js → index-6fp7kdpx.js} +10 -9
  28. package/dist/{index-mv8sb1ns.js → index-96ezrvg2.js} +55 -13
  29. package/dist/{index-wm9xeq1n.js → index-bt2p8x7r.js} +1 -1
  30. package/dist/{index-sk3cap35.js → index-eps3c3g6.js} +1 -1
  31. package/dist/{index-zxg4ve4j.js → index-fqqz1yt8.js} +77 -4
  32. package/dist/{index-d9zkmhz3.js → index-ggstwr0v.js} +53 -18
  33. package/dist/{index-j6zxnc8m.js → index-jpdvn09j.js} +5 -5
  34. package/dist/{index-4fx5f3k5.js → index-pq3kz1r8.js} +1 -1
  35. package/dist/{index-02pqtbbg.js → index-vc7yxz50.js} +10 -10
  36. package/dist/{index-crta5wzv.js → index-vvzc5ezz.js} +4 -4
  37. package/dist/{index-f6mqa5gk.js → index-wwthv10a.js} +11 -39
  38. package/dist/{index-74kkpyqk.js → index-xfedewpt.js} +12 -6
  39. package/dist/{index-sstgcsr0.js → index-yx52bt4a.js} +1 -1
  40. package/dist/index.js +14 -14
  41. package/dist/integrations/mcp-stdio-tools.d.ts +4 -1
  42. package/dist/runtime/budget-ledger-scope.d.ts +14 -0
  43. package/dist/service-cli.js +11 -11
  44. package/dist/{service-client-cli-eqrdc9zq.js → service-client-cli-v6qptg9x.js} +4 -4
  45. package/dist/tools/workspace.d.ts +2 -2
  46. package/dist/verify-historical-migrations.js +143 -62
  47. package/dist/workspace/edit-contracts.d.ts +2 -2
  48. package/dist/workspace/path-policy.d.ts +2 -1
  49. package/dist/workspace/workspace.d.ts +13 -0
  50. package/dist/zhx.js +2 -2
  51. package/docs/CODE_RELEASE.md +28 -6
  52. package/docs/CODE_WEB_TASKS.md +1 -1
  53. package/docs/ENGINE_API.md +1 -1
  54. package/docs/EXTENSIBILITY.md +6 -2
  55. package/docs/FIRST_USE.md +6 -6
  56. package/docs/PROJECT_MEMORY.md +1 -1
  57. package/docs/PUBLIC_SECURITY.md +14 -1
  58. package/docs/REPOSITORY_EDITING.md +1 -1
  59. package/docs/TOOL_POLICY.md +4 -1
  60. package/docs/adr/0002-isolated-mcp-stdio.md +1 -1
  61. package/evaluations/representative-assembly-matrix.json +10 -1
  62. package/examples/README.md +3 -3
  63. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -6,6 +6,9 @@ The project follows Semantic Versioning. During `0.x`, minor releases may change
6
6
 
7
7
  ## Unreleased
8
8
 
9
+ ## 1.5.0 - 2026-10-16
10
+
11
+ - Prepare minor Harness 1.5.0 and Code 0.4.0 for npm `latest`. The minor covers the new experimental audit pack, provisional Bedrock and Azure OpenAI routes, and Code web task control; stable root exports and command contracts are unchanged. It ships with the exact Code engine pin, private Desktop binding and an additive representative mapping for `v1.5.0` using the unchanged Meta, Qwen and OpenAI models. Published `latest` is Harness 1.4.1 and Code 0.3.2. Tags and publication follow only through the protected release workflows.
9
12
  - Add provisional Amazon Bedrock and Azure OpenAI routes. Bedrock pins `@zhivex-ai/bedrock@2.0.7` (Converse uses the AWS credential chain and region and reports Tier B; `BEDROCK_RUNTIME=openai` is the Tier A path). Azure OpenAI pins `@zhivex-ai/azure-openai@0.7.8` (https endpoint, API key, optional API version and deployments map). Core is `@zhivex-ai/core@1.34.0`, which those adapters require. An incomplete Bedrock or Azure environment stays out of the durable transport fingerprint. Desktop forwards the selected route's environment credentials through the worker bootstrap. These routes are uncertified: no live calls, no default certification cohort, and no publish or tags.
10
13
  - Add an experimental audit pack on `@zhivex-ai/harness/audit`. `zhx export-audit <task-id> --out <directory>` writes one directory per run, or one directory containing every run on a guided task account. The pack reuses the governance report, every approval decision page up to 512 records, the durable redacted activity journal as JSONL, and the usage ledger projection plus transport summary. Approvals, usage, and the governance report come from one captured run snapshot. A change to that run, its journal, or the task account before signing fails with `AUDIT_PACK_STATE_CHANGED` and writes nothing. The 32 MiB limit includes `manifest.json`. `zhx verify-audit --out <directory>` checks the manifest hashes and an HMAC-SHA256 seal without resolving provider, store, or budget configuration. The signing key is `ZHIVEX_AUDIT_SIGNING_KEY` or `--signing-key-file` and is not stored in the pack. Stable root exports, stable declaration signatures, and existing command option contracts are unchanged. This note does not publish a release.
11
14
  - Add experimental Code web task definition, bounded exact delivery review, explicit human acceptance, requirement correction, and budgeted continuation through additive host-owned client commands. Keep check verification, human decisions, and provider usage uncertainty distinct. Failed pre-admission continuations retain scoped previous evidence without inventing budget admissions; ordinary chat cannot bypass a governed task's budget.
package/README.md CHANGED
@@ -159,6 +159,6 @@ Contracts, stability tiers, and the experimental ACP and MCP entry points are in
159
159
 
160
160
  ## Releases
161
161
 
162
- Version `1.4.0` is the current public npm release. The paired Code package on npm `latest` is `0.3.0`. This checkout prepares Version `1.4.1` and Code `0.3.1` for `latest`. That publication is pending, so the quick start above follows the published `latest` tag. npm `next` is Harness `1.4.0-rc.8` and Code `0.3.0-rc.8`. Confirm dist-tags on the [Code versions](https://www.npmjs.com/package/@zhivex-ai/code?activeTab=versions) and [Harness versions](https://www.npmjs.com/package/@zhivex-ai/harness?activeTab=versions) pages before you install a prerelease.
162
+ Version `1.4.1` is the current public npm release. The paired Code package on npm `latest` is `0.3.2`. This checkout prepares Version `1.5.0` and Code `0.4.0` for `latest`. That publication is pending, so the quick start above follows the published `latest` tag. npm `next` is Harness `1.4.0-rc.8` and Code `0.3.0-rc.8`. Confirm dist-tags on the [Code versions](https://www.npmjs.com/package/@zhivex-ai/code?activeTab=versions) and [Harness versions](https://www.npmjs.com/package/@zhivex-ai/harness?activeTab=versions) pages before you install a prerelease.
163
163
 
164
164
  Release channels, publication, and certification records are in the [release procedure](https://github.com/Zhivex/zhivex-harness/blob/main/docs/RELEASE.md). Releases are verified with npm provenance. Source in this checkout can be newer than the public package. See the [changelog](CHANGELOG.md) and the [repository release status](https://raw.githubusercontent.com/Zhivex/zhivex-harness/main/release-status.json).
package/SUPPORT.md CHANGED
@@ -13,7 +13,7 @@ The 1.1 support scope is tracked in the [machine-readable support matrix](./docs
13
13
  - the provider/model combinations marked certified in the package documentation; and
14
14
  - HTTPS or explicitly allowed loopback-HTTP MCP endpoints using the documented bounded JSON-RPC subset.
15
15
 
16
- Only the most recent published stable minor receives routine fixes. The exact `v1.3.0` tag passed the protected artifact, six-provider live and 42-case representative release jobs. Harness 1.3.x and Code 0.2.x are the supported stable lines until 1.4.1 and Code 0.3.1 complete verified publication. Gemini, Anthropic and Vertex remain provisional despite the release-bound live gates; the representative repository matrix covers only Meta, Qwen and OpenAI. Live provider evidence is account-, model-, endpoint-, artifact-, and date-dependent; see [docs/LIVE_CERTIFICATION.md](https://github.com/Zhivex/zhivex-harness/blob/main/docs/LIVE_CERTIFICATION.md).
16
+ Only the most recent published stable minor receives routine fixes. The exact `v1.3.0` tag passed the protected artifact, six-provider live and 42-case representative release jobs. Harness 1.3.x and Code 0.2.x are the supported stable lines until 1.5.0 and Code 0.4.0 complete verified publication. Gemini, Anthropic and Vertex remain provisional despite the release-bound live gates; the representative repository matrix covers only Meta, Qwen and OpenAI. Live provider evidence is account-, model-, endpoint-, artifact-, and date-dependent; see [docs/LIVE_CERTIFICATION.md](https://github.com/Zhivex/zhivex-harness/blob/main/docs/LIVE_CERTIFICATION.md).
17
17
 
18
18
  ## Installation diagnostics
19
19
 
@@ -125,7 +125,7 @@
125
125
  },
126
126
  {
127
127
  "name": "Workspace",
128
- "sha256": "sha256:3e26e6e06d28107c6191ff010e80470beaa34a548bef1556caa46c96a2f966fe"
128
+ "sha256": "sha256:42b53815d22e2734371ae5360f059779516f7304bc9a1941dc208d12b67f6a60"
129
129
  },
130
130
  {
131
131
  "name": "appendUserMessage",
@@ -165,7 +165,7 @@
165
165
  },
166
166
  {
167
167
  "name": "createHarness",
168
- "sha256": "sha256:e93ba19038466b7ba57cdb6db2c8f0342e659473d09d522c019d3a631c575904"
168
+ "sha256": "sha256:0bf4f22db95c409dfa85fce488984f06efa84491fc7d4ef2351a053ea32701f3"
169
169
  },
170
170
  {
171
171
  "name": "createHarnessMcpTools",
@@ -173,7 +173,7 @@
173
173
  },
174
174
  {
175
175
  "name": "createHarnessOciExecutionEnvironment",
176
- "sha256": "sha256:c4f8f25e3a78c2bcbdf4a6a2238e4afd2e80cf14bc91c414874088126d6f92cc"
176
+ "sha256": "sha256:6ae2d5238e49299c4c0ee9d9b5f0ddae4de86db9387c8469f7fd6f4518e20e6e"
177
177
  },
178
178
  {
179
179
  "name": "createHarnessStateBackup",
@@ -273,7 +273,7 @@
273
273
  },
274
274
  {
275
275
  "name": "runHarness",
276
- "sha256": "sha256:d7bb7f4e0ebd6a3b1ad88bc60ec02847a13a6839c37abb2a0602d6b4250859fb"
276
+ "sha256": "sha256:82a768885893267b4c24aae1923726ae59c048f73c34ce1e92d0355981c6d46a"
277
277
  },
278
278
  {
279
279
  "name": "serializeStreamEvent",
@@ -427,7 +427,7 @@
427
427
  },
428
428
  {
429
429
  "name": "HarnessExecutionSession",
430
- "sha256": "sha256:3bf8a21fccb5793b92bbbd10c6a99f07bf404096422246e8c18d93bff328c743"
430
+ "sha256": "sha256:15843c59747ef8d072a66f19c88031a2a2981be3cdebf3aabb836132b6f7c4b9"
431
431
  },
432
432
  {
433
433
  "name": "HarnessMcpConfiguration",
@@ -487,8 +487,8 @@
487
487
  },
488
488
  {
489
489
  "name": "ZhivexHarness",
490
- "sha256": "sha256:c4af70e955997b69b0f48f52f52efca6f2374e13f9bc536760078a4ee08e984e"
490
+ "sha256": "sha256:2911bde71fdae2dff26f766d068c560375757fb6575f5aaec02ff3346c46ab94"
491
491
  }
492
492
  ],
493
- "digest": "sha256:044531ba474a2576fadb3e9a091094daa81e9c8495b697d7ff6c69205b37d5e4"
493
+ "digest": "sha256:50be2a81cd0e7601d5610008081c406cecf9855c5ffd42d2faffba521b08873e"
494
494
  }
package/dist/acp-cli.js CHANGED
@@ -11,14 +11,14 @@ import {
11
11
  } from "./index-3ah1f369.js";
12
12
  import {
13
13
  createHarnessClientAdapter
14
- } from "./index-02pqtbbg.js";
15
- import"./index-6599mpmy.js";
14
+ } from "./index-vc7yxz50.js";
15
+ import"./index-63gbp0cz.js";
16
16
  import {
17
17
  createHarness
18
- } from "./index-0zs2dz26.js";
19
- import"./index-f6mqa5gk.js";
20
- import"./index-dtb2c6ds.js";
21
- import"./index-d9zkmhz3.js";
18
+ } from "./index-6fp7kdpx.js";
19
+ import"./index-wwthv10a.js";
20
+ import"./index-5zbq9ztj.js";
21
+ import"./index-ggstwr0v.js";
22
22
  import {
23
23
  HARNESS_REQUIRED_CAPABILITIES,
24
24
  HARNESS_SUBAGENT_PROFILES,
@@ -31,17 +31,17 @@ import {
31
31
  } from "./index-1265j7t2.js";
32
32
  import"./index-c39197wq.js";
33
33
  import"./index-mzydbzp8.js";
34
- import"./index-wm9xeq1n.js";
34
+ import"./index-bt2p8x7r.js";
35
35
  import {
36
36
  usagePricingSchema
37
- } from "./index-zxg4ve4j.js";
38
- import"./index-4fx5f3k5.js";
37
+ } from "./index-fqqz1yt8.js";
38
+ import"./index-pq3kz1r8.js";
39
39
  import {
40
40
  FileChangedWhileReadingError,
41
41
  FileSizeLimitError,
42
42
  UnsafeFileTypeError,
43
43
  readRegularFileNoFollow
44
- } from "./index-xdbkcccf.js";
44
+ } from "./index-6dxrbnxm.js";
45
45
  import"./index-4gt045j0.js";
46
46
  import {
47
47
  HarnessConfigError,
@@ -14,6 +14,7 @@ declare const file: z.ZodObject<{
14
14
  operation: z.ZodOptional<z.ZodEnum<{
15
15
  create: "create";
16
16
  delete: "delete";
17
+ restore: "restore";
17
18
  update: "update";
18
19
  }>>;
19
20
  }, z.core.$strict>;
@@ -37,7 +38,7 @@ export declare function captureApprovalDiffs(state: AgentRunState, run: HarnessC
37
38
  afterDigest: string | null;
38
39
  beforeMode?: number | undefined;
39
40
  afterMode?: number | undefined;
40
- operation?: "create" | "delete" | "update" | undefined;
41
+ operation?: "create" | "delete" | "restore" | "update" | undefined;
41
42
  }[];
42
43
  }[];
43
44
  /** Only expose saved content when exact journal effects certify every file. */
@@ -10,7 +10,7 @@ export interface ApprovalPreviewFile {
10
10
  afterDigest: string | null;
11
11
  beforeMode?: number;
12
12
  afterMode?: number;
13
- operation?: "create" | "update" | "delete";
13
+ operation?: "create" | "update" | "delete" | "restore";
14
14
  }
15
15
  export type ApprovalFilePreview = {
16
16
  status: "complete";
@@ -1,26 +1,26 @@
1
1
  import {
2
2
  exportHarnessGovernanceReport,
3
3
  harnessGovernanceReportSchema
4
- } from "../index-j6zxnc8m.js";
5
- import"../index-sstgcsr0.js";
4
+ } from "../index-jpdvn09j.js";
5
+ import"../index-yx52bt4a.js";
6
6
  import {
7
7
  APPROVAL_HISTORY_KEY,
8
8
  approvalDecisionViews,
9
9
  harnessPolicyDecisionEventSchema
10
- } from "../index-wm9xeq1n.js";
10
+ } from "../index-bt2p8x7r.js";
11
11
  import {
12
12
  TASK_BUDGET_ACCOUNT_KEY,
13
13
  TaskBudget,
14
14
  USAGE_LEDGER_KEY,
15
15
  UsageLedger,
16
16
  inspectUsageLedger
17
- } from "../index-zxg4ve4j.js";
17
+ } from "../index-fqqz1yt8.js";
18
18
  import {
19
19
  HARNESS_VERSION
20
- } from "../index-4fx5f3k5.js";
20
+ } from "../index-pq3kz1r8.js";
21
21
  import {
22
22
  readRegularFileNoFollow
23
- } from "../index-xdbkcccf.js";
23
+ } from "../index-6dxrbnxm.js";
24
24
  import"../index-bpc596v3.js";
25
25
  import"../index-fm84men1.js";
26
26
  import"../index-1bx83sw4.js";
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  exportHarnessGovernanceReport,
3
3
  harnessGovernanceReportSchema
4
- } from "./cli-entry-rgs6bxmy.js";
4
+ } from "./cli-entry-75j3r49b.js";
5
5
  import {
6
6
  APPROVAL_HISTORY_KEY,
7
7
  CliUsageError,
@@ -17,10 +17,10 @@ import {
17
17
  openHarnessPersistence,
18
18
  readRegularFileNoFollow,
19
19
  validateStateDirectory
20
- } from "./cli-entry-7f0cen32.js";
20
+ } from "./cli-entry-928p8h41.js";
21
21
  import {
22
22
  HARNESS_VERSION
23
- } from "./cli-entry-3s63zn4w.js";
23
+ } from "./cli-entry-q8bgkn9m.js";
24
24
  import {
25
25
  resolveHarnessConfig
26
26
  } from "./cli-entry-hdtfef27.js";
@@ -10,10 +10,10 @@ import {
10
10
  readTaskAcceptanceLedger,
11
11
  taskCheckReceiptSchema,
12
12
  verifyChangeEnvelope
13
- } from "./cli-entry-7f0cen32.js";
13
+ } from "./cli-entry-928p8h41.js";
14
14
  import {
15
15
  HARNESS_VERSION
16
- } from "./cli-entry-3s63zn4w.js";
16
+ } from "./cli-entry-q8bgkn9m.js";
17
17
 
18
18
  // src/persistence/governance-report.ts
19
19
  import { createHash as createHash2 } from "node:crypto";
@@ -2,7 +2,7 @@ import {
2
2
  CLI_OPTION_DEFINITIONS,
3
3
  resolveHelpTopic,
4
4
  validateCliCommandOptions
5
- } from "./cli-entry-3s63zn4w.js";
5
+ } from "./cli-entry-q8bgkn9m.js";
6
6
  import {
7
7
  DEFAULT_PROVIDER_REGISTRY,
8
8
  HARNESS_EXECUTION_POLICY_VERSION,
@@ -3656,8 +3656,8 @@ var quarantineFileInputSchema = z10.strictObject({
3656
3656
  });
3657
3657
  var restoreFileInputSchema = z10.strictObject({
3658
3658
  quarantineId: z10.string().min(1).max(200).regex(/^[A-Za-z0-9._:-]+$/),
3659
- destination: workspaceFilePathSchema.optional(),
3660
- expectedDigest: fileDigestSchema.optional()
3659
+ destination: workspaceFilePathSchema.describe("Required workspace path for the quarantined bytes. Restore does not fall back to the original path."),
3660
+ expectedDigest: fileDigestSchema.describe("Required sha256 of the quarantined bytes the operator reviewed.")
3661
3661
  });
3662
3662
  var mutationOperationSchema = z10.enum(["create", "update", "move", "quarantine", "restore"]);
3663
3663
  var mutationAuditEntrySchema = z10.strictObject({
@@ -3997,10 +3997,10 @@ var runPortableProcess = async (command, options = {}) => {
3997
3997
  // src/workspace/path-policy.ts
3998
3998
  var HARD_IGNORES = new Set([".git", ".next", ".turbo", ".zhivex-harness", "coverage", "dist", "node_modules"]);
3999
3999
  var isSensitiveName = (name) => {
4000
- const normalized = name.toLocaleLowerCase();
4000
+ const normalized = name.toLowerCase();
4001
4001
  return normalized === ".env" || normalized.startsWith(".env.") && normalized !== ".env.example" || normalized === ".npmrc" || normalized === "id_rsa" || normalized === "id_ed25519" || [".key", ".pem", ".p12", ".pfx"].some((extension) => normalized.endsWith(extension));
4002
4002
  };
4003
- var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.toLocaleLowerCase()) || isSensitiveName(segment));
4003
+ var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.toLowerCase()) || isSensitiveName(segment));
4004
4004
 
4005
4005
  // src/workspace/workspace.ts
4006
4006
  var IGNORE_FILES = [".gitignore", ".zhivex-harnessignore"];
@@ -4280,7 +4280,7 @@ class Workspace {
4280
4280
  throw new HarnessWorkspaceError(`The path escapes the workspace: ${relativePath}`);
4281
4281
  const relative = wirePath(path11.relative(this.root, candidate));
4282
4282
  if (relative && isHardIgnored(relative)) {
4283
- const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.toLocaleLowerCase()) || isSensitiveName(segment));
4283
+ const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.toLowerCase()) || isSensitiveName(segment));
4284
4284
  throw new HarnessWorkspaceError(`The path is protected by the harness policy: ${blocked}`);
4285
4285
  }
4286
4286
  return candidate;
@@ -5263,30 +5263,65 @@ class Workspace {
5263
5263
  const audit = await this.audit({ operation: "quarantine", path: parsed.path, beforeDigest: source.digest, quarantineId });
5264
5264
  return { quarantineId, path: parsed.path, digest: source.digest, audit };
5265
5265
  }
5266
- async restoreQuarantined(input) {
5267
- return withWorkspaceMutation(this.root, () => this.restoreQuarantinedLocked(input));
5268
- }
5269
- async restoreQuarantinedLocked(input) {
5270
- const parsed = restoreFileInputSchema.parse(input);
5271
- this.checkAuditCapacity();
5266
+ async readRestorableQuarantine(quarantineId) {
5272
5267
  const directory = await this.secureQuarantineDirectory(false);
5273
5268
  let manifest;
5274
5269
  try {
5275
- const manifestFile = await this.readInternalRegularFile(path11.join(directory, `${parsed.quarantineId}.json`), "Quarantine manifest", 64 * 1024);
5270
+ const manifestFile = await this.readInternalRegularFile(path11.join(directory, `${quarantineId}.json`), "Quarantine manifest", 64 * 1024);
5276
5271
  manifest = JSON.parse(manifestFile.contents.toString("utf8"));
5277
5272
  } catch {
5278
- throw new HarnessWorkspaceError(`Quarantine entry was not found: ${parsed.quarantineId}.`);
5273
+ throw new HarnessWorkspaceError(`Quarantine entry was not found: ${quarantineId}.`);
5279
5274
  }
5280
- if (manifest.schemaVersion !== 1 || manifest.quarantineId !== parsed.quarantineId || manifest.status !== "quarantined") {
5281
- throw new HarnessWorkspaceError(`Quarantine entry is not restorable: ${parsed.quarantineId}.`);
5275
+ if (manifest.schemaVersion !== 1 || manifest.quarantineId !== quarantineId || manifest.status !== "quarantined") {
5276
+ throw new HarnessWorkspaceError(`Quarantine entry is not restorable: ${quarantineId}.`);
5282
5277
  }
5283
- const dataPath = path11.join(directory, `${parsed.quarantineId}.data`);
5278
+ const dataPath = path11.join(directory, `${quarantineId}.data`);
5284
5279
  const dataFile = await this.readInternalRegularFile(dataPath, "Quarantine payload", MAX_FILE_BYTES);
5285
5280
  const digest3 = digestBytes(dataFile.contents);
5286
- if (digest3 !== manifest.digest || parsed.expectedDigest && parsed.expectedDigest !== digest3) {
5281
+ if (digest3 !== manifest.digest) {
5282
+ throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${quarantineId}.`);
5283
+ }
5284
+ return { manifest, dataPath, dataFile, digest: digest3 };
5285
+ }
5286
+ async previewRestore(input) {
5287
+ const parsed = restoreFileInputSchema.parse(input);
5288
+ const quarantined = await this.readRestorableQuarantine(parsed.quarantineId);
5289
+ if (parsed.expectedDigest !== quarantined.digest) {
5290
+ throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${parsed.quarantineId}.`);
5291
+ }
5292
+ const destination = await this.safePath(parsed.destination, { allowMissing: true });
5293
+ if (destination.exists)
5294
+ throw new HarnessWorkspaceError(`Restore destination already exists: ${parsed.destination}.`);
5295
+ let after;
5296
+ try {
5297
+ after = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(quarantined.dataFile.contents);
5298
+ } catch {
5299
+ throw new HarnessWorkspaceError("Preview requires valid UTF-8 text.");
5300
+ }
5301
+ return {
5302
+ proposalId: parsed.expectedDigest,
5303
+ files: [{
5304
+ path: parsed.destination,
5305
+ expectedDigest: parsed.expectedDigest,
5306
+ before: null,
5307
+ after,
5308
+ afterDigest: quarantined.digest,
5309
+ operation: "restore"
5310
+ }]
5311
+ };
5312
+ }
5313
+ async restoreQuarantined(input) {
5314
+ return withWorkspaceMutation(this.root, () => this.restoreQuarantinedLocked(input));
5315
+ }
5316
+ async restoreQuarantinedLocked(input) {
5317
+ const parsed = restoreFileInputSchema.parse(input);
5318
+ this.checkAuditCapacity();
5319
+ const quarantined = await this.readRestorableQuarantine(parsed.quarantineId);
5320
+ if (parsed.expectedDigest !== quarantined.digest) {
5287
5321
  throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${parsed.quarantineId}.`);
5288
5322
  }
5289
- const destinationPath = parsed.destination ?? manifest.originalPath;
5323
+ const { manifest, dataPath, dataFile, digest: digest3 } = quarantined;
5324
+ const destinationPath = parsed.destination;
5290
5325
  const destination = await this.safePath(destinationPath, { allowMissing: true });
5291
5326
  if (destination.exists)
5292
5327
  throw new HarnessWorkspaceError(`Restore destination already exists: ${destinationPath}.`);
@@ -6511,7 +6546,7 @@ var importPatch = async (runId, expectedPatchId, host, base, current2, maxFileWr
6511
6546
  const rollbackErrors = [];
6512
6547
  for (const item of [...quarantined].reverse()) {
6513
6548
  try {
6514
- await host.restoreQuarantined({ quarantineId: item.quarantineId, expectedDigest: item.digest });
6549
+ await host.restoreQuarantined({ quarantineId: item.quarantineId, destination: item.path, expectedDigest: item.digest });
6515
6550
  } catch (rollbackError) {
6516
6551
  rollbackErrors.push(rollbackError);
6517
6552
  }
@@ -7885,6 +7920,79 @@ import { createAgentRunLedger } from "@zhivex-ai/agents/control-plane";
7885
7920
  import { AsyncLocalStorage as AsyncLocalStorage5 } from "node:async_hooks";
7886
7921
  import { randomUUID as randomUUID7 } from "node:crypto";
7887
7922
  import { z as z17 } from "zod";
7923
+
7924
+ // src/runtime/budget-ledger-scope.ts
7925
+ var BUDGET_LEDGER_NAMESPACE = "__zhivex_budget__";
7926
+ var BUDGET_OWNER_NAMESPACE_KEY = "budgetOwnerNamespace";
7927
+ var isReservedBudgetNamespace = (namespace) => namespace === BUDGET_LEDGER_NAMESPACE || (namespace?.startsWith(`${BUDGET_LEDGER_NAMESPACE}:`) ?? false);
7928
+ var budgetLedgerNamespace = (namespace) => {
7929
+ if (!namespace || isReservedBudgetNamespace(namespace)) {
7930
+ throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
7931
+ }
7932
+ return `${BUDGET_LEDGER_NAMESPACE}:${namespace}`;
7933
+ };
7934
+ var budgetLedgerScope = (scope) => {
7935
+ if (scope.namespace && isReservedBudgetNamespace(scope.namespace)) {
7936
+ throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
7937
+ }
7938
+ return {
7939
+ tenantId: scope.tenantId,
7940
+ ...scope.userId ? { userId: scope.userId } : {},
7941
+ namespace: scope.namespace ? budgetLedgerNamespace(scope.namespace) : `${BUDGET_LEDGER_NAMESPACE}:`
7942
+ };
7943
+ };
7944
+ var sameIdentity = (left, right) => left?.tenantId === right.tenantId && (left.userId ?? undefined) === (right.userId ?? undefined);
7945
+ var bindBudgetLedgerStore = (primary, scope, budget = primary) => {
7946
+ const bound = budgetLedgerScope(scope);
7947
+ const ownerNamespace = scope.namespace ?? "";
7948
+ const isSdkBudget = (value) => sameIdentity(value, bound) && value?.namespace === BUDGET_LEDGER_NAMESPACE;
7949
+ const isBoundBudget = (value) => sameIdentity(value, bound) && value?.namespace === bound.namespace;
7950
+ const assertId = (id) => {
7951
+ if (!/^budget_[a-f0-9]{64}$/.test(id))
7952
+ throw new HarnessStateConflictError("Invalid budget ledger identity.");
7953
+ };
7954
+ const bindState = (state) => {
7955
+ const metadata = { ...state.metadata ?? {} };
7956
+ const owner = metadata[BUDGET_OWNER_NAMESPACE_KEY];
7957
+ if (owner !== undefined && owner !== ownerNamespace) {
7958
+ throw new HarnessStateConflictError("Budget ledger is bound to another namespace.");
7959
+ }
7960
+ return {
7961
+ ...state,
7962
+ scope: bound,
7963
+ metadata: { ...metadata, [BUDGET_OWNER_NAMESPACE_KEY]: ownerNamespace }
7964
+ };
7965
+ };
7966
+ const assertStored = (state) => {
7967
+ if (!state)
7968
+ return state;
7969
+ const owner = state.metadata?.[BUDGET_OWNER_NAMESPACE_KEY];
7970
+ if (state.scope?.namespace !== bound.namespace || owner !== ownerNamespace) {
7971
+ throw new HarnessStateConflictError("Budget ledger is not bound to this namespace.");
7972
+ }
7973
+ return state;
7974
+ };
7975
+ return {
7976
+ ...primary,
7977
+ load: async (id, requestedScope) => {
7978
+ if (!isSdkBudget(requestedScope) && !isBoundBudget(requestedScope))
7979
+ return primary.load(id, requestedScope);
7980
+ assertId(id);
7981
+ return assertStored(await budget.load(id, bound));
7982
+ },
7983
+ save: async (state, options) => {
7984
+ if (!isSdkBudget(state.scope) && !isBoundBudget(state.scope))
7985
+ return primary.save(state, options);
7986
+ assertId(state.runId);
7987
+ if (state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true) {
7988
+ throw new HarnessStateConflictError("Only SDK budget ledger states may use the reserved namespace.");
7989
+ }
7990
+ return budget.save(bindState(state), options);
7991
+ }
7992
+ };
7993
+ };
7994
+
7995
+ // src/runtime/task-budget.ts
7888
7996
  import {
7889
7997
  createAgentBudgetCoordinator,
7890
7998
  fingerprintAgentHarness as fingerprintAgentHarness2,
@@ -8003,10 +8111,10 @@ class TaskBudget {
8003
8111
  this.accountRunId = TaskBudget.accountId(scope, taskId);
8004
8112
  this.policy = structuredClone(account.policy);
8005
8113
  const budgetId = this.accountRunId;
8006
- this.coordinator = createAgentBudgetCoordinator({ store, scope, budgetId, limits: this.policy.limits });
8114
+ this.coordinator = createAgentBudgetCoordinator({ store: bindBudgetLedgerStore(store, scope), scope, budgetId, limits: this.policy.limits });
8007
8115
  this.coordinatorId = this.coordinator.id;
8008
8116
  this.budgetRunId = `budget_${fingerprintAgentHarness2({ budgetId, scope }).slice("sha256:".length)}`;
8009
- this.budgetScope = { tenantId: scope.tenantId, ...scope.userId ? { userId: scope.userId } : {}, namespace: "__zhivex_budget__" };
8117
+ this.budgetScope = budgetLedgerScope(scope);
8010
8118
  }
8011
8119
  static accountId(scope, taskId) {
8012
8120
  return `task_budget_${fingerprintAgentHarness2({ scope, taskId }).slice("sha256:".length)}`;
@@ -8521,37 +8629,7 @@ class TaskBudget {
8521
8629
  }
8522
8630
 
8523
8631
  // src/persistence/operations.ts
8524
- var budgetScopeFor = (config) => ({
8525
- tenantId: config.scope.tenantId,
8526
- ...config.scope.userId ? { userId: config.scope.userId } : {},
8527
- namespace: "__zhivex_budget__"
8528
- });
8529
- var withBudgetLedgerStore = (primary, budget, config) => {
8530
- const scope = budgetScopeFor(config);
8531
- const budgetScope = (value) => value?.namespace === scope.namespace && value.tenantId === scope.tenantId && (value.userId ?? undefined) === (scope.userId ?? undefined);
8532
- const assertId = (id) => {
8533
- if (!/^budget_[a-f0-9]{64}$/.test(id))
8534
- throw new HarnessStateConflictError("Invalid budget ledger identity.");
8535
- };
8536
- return {
8537
- ...primary,
8538
- load: async (id, requestedScope) => {
8539
- if (!budgetScope(requestedScope))
8540
- return primary.load(id, requestedScope);
8541
- assertId(id);
8542
- return budget.load(id, requestedScope);
8543
- },
8544
- save: async (state, options) => {
8545
- if (!budgetScope(state.scope))
8546
- return primary.save(state, options);
8547
- assertId(state.runId);
8548
- if (state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true) {
8549
- throw new HarnessStateConflictError("Only SDK budget ledger states may use the reserved namespace.");
8550
- }
8551
- return budget.save(state, options);
8552
- }
8553
- };
8554
- };
8632
+ var withBudgetLedgerStore = (primary, budget, config) => bindBudgetLedgerStore(primary, config.scope, budget);
8555
8633
  var HARNESS_SQLITE_FILE = "operations.sqlite";
8556
8634
  var HARNESS_OPERATIONS_SCHEMA_VERSION = 1;
8557
8635
  var TERMINAL_RUN_STATUSES = ["completed", "failed", "cancelled", "timed_out"];
@@ -8806,13 +8884,12 @@ var migrateLegacyFileRuns = async (directory, target, config) => {
8806
8884
  return result;
8807
8885
  };
8808
8886
  var openHarnessPersistence = async (config, options = {}) => {
8809
- if (config.scope.namespace === "__zhivex_budget__")
8810
- throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
8887
+ const budgetScope = budgetLedgerScope(config.scope);
8811
8888
  await validateStateDirectory(config.workspace, config.stateDirectory);
8812
8889
  await ensurePrivateStateDirectory(config.stateDirectory);
8813
8890
  await protectStateFromGit(config.workspace, config.stateDirectory);
8814
8891
  if (config.storeBackend === "file") {
8815
- const store = withBudgetLedgerStore(createFileAgentRunStore({ directory: config.stateDirectory, scope: config.scope }), createFileAgentRunStore({ directory: config.stateDirectory, scope: budgetScopeFor(config) }), config);
8892
+ const store = withBudgetLedgerStore(createFileAgentRunStore({ directory: config.stateDirectory, scope: config.scope }), createFileAgentRunStore({ directory: config.stateDirectory, scope: budgetScope }), config);
8816
8893
  const migration = shouldMigrateLegacyRuns(config, options.migrateLegacyFileStore) ? await migrateLegacyFileRuns(config.stateDirectory, store, config) : emptyMigration();
8817
8894
  return {
8818
8895
  backend: "file",
@@ -8857,7 +8934,7 @@ var openHarnessPersistence = async (config, options = {}) => {
8857
8934
  database.exec("PRAGMA foreign_keys = ON");
8858
8935
  await chmod3(databasePath, 384);
8859
8936
  const databaseLike = sqliteAdapter(database);
8860
- const store = withBudgetLedgerStore(createSqliteAgentRunStore({ db: databaseLike, scope: config.scope, history: "incremental" }), createSqliteAgentRunStore({ db: databaseLike, scope: budgetScopeFor(config) }), config);
8937
+ const store = withBudgetLedgerStore(createSqliteAgentRunStore({ db: databaseLike, scope: config.scope, history: "incremental" }), createSqliteAgentRunStore({ db: databaseLike, scope: budgetScope }), config);
8861
8938
  database.exec(`CREATE TRIGGER IF NOT EXISTS zhivex_task_budget_retain_run
8862
8939
  BEFORE DELETE ON zhivex_agent_runs
8863
8940
  WHEN json_type(OLD.state_json, '$.metadata.zhivexTaskBudgetAccountV1') IS NOT NULL
@@ -9987,4 +10064,4 @@ var verifyChangeEnvelope = (input, options = {}) => {
9987
10064
  };
9988
10065
  };
9989
10066
 
9990
- export { UnsafeFileTypeError, FileSizeLimitError, FileChangedWhileReadingError, statRegularFileNoFollow, readRegularFileNoFollow, readRegularFileChunksNoFollow, loadCliProfile, createCliProfile, updateCliProfile, applyCliProfile, resolveCliDefaults, parseHarnessModelRoute, resolveHarnessModelRoutes, createHarnessRouteModels, serializeHarnessModelRoutes, CLI_COMMANDS, CLI_RUNS_COMMANDS, CLI_SESSIONS_COMMANDS, CLI_CHANGES_COMMANDS, CLI_STATE_COMMANDS, CliUsageError, parseCliArgs, SqliteDatabase, validateStateDirectory, HARNESS_SESSION_SCHEMA_VERSION, TERMINAL_SESSION_RUN_STATUSES, ensurePrivateDatabase, openCliSessionStore, inspectRuntimeDiagnostics, inspectRuntimeManifest, estimateContextTokens, measureContext, MODEL_BUDGET_KEY, estimateRequestTokens, workBudgetReached, createModelBudget, USAGE_LEDGER_KEY, usagePricingSchema, inspectUsageLedger, formatUsageLedger, runUsageLedgerWithPolicy, UsageLedger, inspectTaskMonetaryUsage, CLI_JSON_SCHEMA_VERSION, CLI_EVENT_SCHEMA_VERSION, serializeStreamEvent, serializeStreamResult, MAX_EDIT_CHANGES, MAX_EDIT_FILE_BYTES, fileDigestSchema, workspaceFilePathSchema, editProposalInputSchema, applyEditProposalInputSchema, moveFileInputSchema, quarantineFileInputSchema, restoreFileInputSchema, createEditProposal, validateEditProposal, editContractDocument, APPROVAL_HISTORY_KEY, approvalInputDigest, readApprovalDecisions, approvalDecisionViews, createHarnessToolPolicy, applyHarnessToolPolicy, bindHarnessPolicyInspection, inspectHarnessPolicy, withRuntimeInstruction, withFreshSystemInstructions, TASK_BUDGET_KEY, TASK_BUDGET_ACCOUNT_KEY, inspectTaskBudgetSummary, assertTaskBudgetBackupLinks, TaskBudget, compileTaskAcceptanceContract, taskCheckReceiptSchema, createTaskAcceptanceChecks, withTaskAcceptanceDelivery, taskAcceptanceChecks, confirmTaskAcceptanceImport, TASK_ACCEPTANCE_KEY, TASK_ACCEPTANCE_EVIDENCE_KEY, readTaskAcceptanceLedger, nextTaskAcceptanceLedger, taskAcceptanceCheckpointStore, TASK_SOURCE_KEY, ASSISTANT_RESPONSE_KEY, assistantResponses, captureAssistantResponses, taskSources, captureTaskSources, createTaskTools, replacementEditSchema, EnvironmentPatchDriftError, harnessToolExecution, effectiveRuntimeBudget, createRuntimeBudget, runtimeManifest, childRuntimeSafety, createCheckpointTokenCap, withWorkspaceMutation, boundedBatches, resolvePackageManager, resolvePackageCheckCommand, runPortableProcess, isHardIgnored, Workspace, CliOciRuntimeAdapter, harnessExecutionSession, createHarnessOciExecutionEnvironment, cleanupHarnessExecutionArtifacts, executionFingerprintInput, createRepairController, createRepairProgress, RUNTIME_DIAGNOSTICS_KEY, assistantResponseCheckpointStore, runtimeCheckpointStore, tokenUsageCheckpointStore, HARNESS_SQLITE_FILE, HARNESS_OPERATIONS_SCHEMA_VERSION, listHarnessRuns, inspectHarnessRun, cancelHarnessRun, cleanupHarnessRuns, sqliteAdapter, openHarnessPersistence, harnessPolicyDecisionEventSchema, observeHarnessPolicyDecisions, publishHarnessPolicyDecision, observeBaselineToolPolicy, publishPendingPolicyDecision, createPolicyDecisionEvidence, changeEnvelopeSchema, digestChangeEnvelopeArtifact, createChangeEnvelope, canonicalizeChangeEnvelope, changeEnvelopePreconditionsSchema, verifyChangeEnvelope, openHarnessActivityStore };
10067
+ export { UnsafeFileTypeError, FileSizeLimitError, FileChangedWhileReadingError, statRegularFileNoFollow, readRegularFileNoFollow, readRegularFileChunksNoFollow, loadCliProfile, createCliProfile, updateCliProfile, applyCliProfile, resolveCliDefaults, parseHarnessModelRoute, resolveHarnessModelRoutes, createHarnessRouteModels, serializeHarnessModelRoutes, CLI_COMMANDS, CLI_RUNS_COMMANDS, CLI_SESSIONS_COMMANDS, CLI_CHANGES_COMMANDS, CLI_STATE_COMMANDS, CliUsageError, parseCliArgs, SqliteDatabase, validateStateDirectory, HARNESS_SESSION_SCHEMA_VERSION, TERMINAL_SESSION_RUN_STATUSES, ensurePrivateDatabase, openCliSessionStore, inspectRuntimeDiagnostics, inspectRuntimeManifest, estimateContextTokens, measureContext, MODEL_BUDGET_KEY, estimateRequestTokens, workBudgetReached, createModelBudget, USAGE_LEDGER_KEY, usagePricingSchema, inspectUsageLedger, formatUsageLedger, runUsageLedgerWithPolicy, UsageLedger, inspectTaskMonetaryUsage, CLI_JSON_SCHEMA_VERSION, CLI_EVENT_SCHEMA_VERSION, serializeStreamEvent, serializeStreamResult, MAX_EDIT_CHANGES, MAX_EDIT_FILE_BYTES, fileDigestSchema, workspaceFilePathSchema, editProposalInputSchema, applyEditProposalInputSchema, moveFileInputSchema, quarantineFileInputSchema, restoreFileInputSchema, createEditProposal, validateEditProposal, editContractDocument, APPROVAL_HISTORY_KEY, approvalInputDigest, readApprovalDecisions, approvalDecisionViews, createHarnessToolPolicy, applyHarnessToolPolicy, bindHarnessPolicyInspection, inspectHarnessPolicy, BUDGET_LEDGER_NAMESPACE, BUDGET_OWNER_NAMESPACE_KEY, isReservedBudgetNamespace, budgetLedgerNamespace, withRuntimeInstruction, withFreshSystemInstructions, TASK_BUDGET_KEY, TASK_BUDGET_ACCOUNT_KEY, inspectTaskBudgetSummary, assertTaskBudgetBackupLinks, TaskBudget, compileTaskAcceptanceContract, taskCheckReceiptSchema, createTaskAcceptanceChecks, withTaskAcceptanceDelivery, taskAcceptanceChecks, confirmTaskAcceptanceImport, TASK_ACCEPTANCE_KEY, TASK_ACCEPTANCE_EVIDENCE_KEY, readTaskAcceptanceLedger, nextTaskAcceptanceLedger, taskAcceptanceCheckpointStore, TASK_SOURCE_KEY, ASSISTANT_RESPONSE_KEY, assistantResponses, captureAssistantResponses, taskSources, captureTaskSources, createTaskTools, replacementEditSchema, EnvironmentPatchDriftError, harnessToolExecution, effectiveRuntimeBudget, createRuntimeBudget, runtimeManifest, childRuntimeSafety, createCheckpointTokenCap, withWorkspaceMutation, boundedBatches, resolvePackageManager, resolvePackageCheckCommand, runPortableProcess, isHardIgnored, Workspace, CliOciRuntimeAdapter, harnessExecutionSession, createHarnessOciExecutionEnvironment, cleanupHarnessExecutionArtifacts, executionFingerprintInput, createRepairController, createRepairProgress, RUNTIME_DIAGNOSTICS_KEY, assistantResponseCheckpointStore, runtimeCheckpointStore, tokenUsageCheckpointStore, HARNESS_SQLITE_FILE, HARNESS_OPERATIONS_SCHEMA_VERSION, listHarnessRuns, inspectHarnessRun, cancelHarnessRun, cleanupHarnessRuns, sqliteAdapter, openHarnessPersistence, harnessPolicyDecisionEventSchema, observeHarnessPolicyDecisions, publishHarnessPolicyDecision, observeBaselineToolPolicy, publishPendingPolicyDecision, createPolicyDecisionEvidence, changeEnvelopeSchema, digestChangeEnvelopeArtifact, createChangeEnvelope, canonicalizeChangeEnvelope, changeEnvelopePreconditionsSchema, verifyChangeEnvelope, openHarnessActivityStore };
@@ -59,6 +59,7 @@ import {
59
59
  inspectTaskMonetaryUsage,
60
60
  inspectUsageLedger,
61
61
  isHardIgnored,
62
+ isReservedBudgetNamespace,
62
63
  measureContext,
63
64
  moveFileInputSchema,
64
65
  nextTaskAcceptanceLedger,
@@ -96,10 +97,10 @@ import {
96
97
  withWorkspaceMutation,
97
98
  workBudgetReached,
98
99
  workspaceFilePathSchema
99
- } from "./cli-entry-7f0cen32.js";
100
+ } from "./cli-entry-928p8h41.js";
100
101
  import {
101
102
  HARNESS_VERSION
102
- } from "./cli-entry-3s63zn4w.js";
103
+ } from "./cli-entry-q8bgkn9m.js";
103
104
  import {
104
105
  DEFAULT_PROVIDER_REGISTRY,
105
106
  HARNESS_CONFIG_SCHEMA_VERSION,
@@ -6394,7 +6395,7 @@ var projectMemoryBinding = (workspace, scope) => {
6394
6395
  segment(scope.userId);
6395
6396
  if (scope.namespace !== undefined)
6396
6397
  segment(scope.namespace);
6397
- if (scope.namespace === "__zhivex_budget__")
6398
+ if (isReservedBudgetNamespace(scope.namespace))
6398
6399
  throw new HarnessConfigError("Reserved memory namespace.");
6399
6400
  const key = (kind, value) => createHash10("sha256").update(kind).update("\x00").update(value).digest("hex");
6400
6401
  const workspaceKey = key("workspace", workspace);
@@ -6720,7 +6721,8 @@ var resolvers = {
6720
6721
  const parsed = moveFileInputSchema.parse(input);
6721
6722
  return [parsed.source, parsed.destination];
6722
6723
  },
6723
- quarantine_file: (input) => [quarantineFileInputSchema.parse(input).path]
6724
+ quarantine_file: (input) => [quarantineFileInputSchema.parse(input).path],
6725
+ restore_file: (input) => [restoreFileInputSchema.parse(input).destination]
6724
6726
  };
6725
6727
  var validateBuiltinToolPolicy = (policy, tools) => {
6726
6728
  for (const rule of policy.rules)
@@ -8153,7 +8155,7 @@ var createWorkspaceTools = (workspace, allowedChecks) => ({
8153
8155
  }),
8154
8156
  restore_file: tool4({
8155
8157
  name: "restore_file",
8156
- description: "Restore a quarantined file to its original path or an explicit safe destination without overwriting content unexpectedly.",
8158
+ description: "Restore a quarantined file to the required destination after the reviewed expectedDigest matches the quarantined bytes. Restore does not fall back to the original path and never overwrites an existing file.",
8157
8159
  schema: restoreFileInputSchema,
8158
8160
  ...mutationApproval,
8159
8161
  execute: async (input, context) => serializeJsonValue6(editContractDocument("restore-result", await (harnessExecutionSession(context)?.workspace ?? workspace).restoreQuarantined(input)))
@@ -444,7 +444,7 @@ Use zhx help all for the full reference and exit codes.`;
444
444
  // package.json
445
445
  var package_default = {
446
446
  name: "@zhivex-ai/harness",
447
- version: "1.4.1",
447
+ version: "1.5.0",
448
448
  description: "Governed, provider-portable runtime for coding agents with durable approvals and isolated repository execution.",
449
449
  type: "module",
450
450
  license: "MIT",