@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
@@ -5,7 +5,7 @@ import {
5
5
  taskAcceptanceOutcome,
6
6
  taskCheckReceiptSchema,
7
7
  workspaceFilePathSchema
8
- } from "./index-xdbkcccf.js";
8
+ } from "./index-6dxrbnxm.js";
9
9
  import {
10
10
  HarnessConfigError,
11
11
  HarnessStateConflictError,
@@ -1604,6 +1604,79 @@ function taskAcceptanceCheckpointStore(store, runId, ledger) {
1604
1604
  import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
1605
1605
  import { randomUUID as randomUUID3 } from "node:crypto";
1606
1606
  import { z as z7 } from "zod";
1607
+
1608
+ // src/runtime/budget-ledger-scope.ts
1609
+ var BUDGET_LEDGER_NAMESPACE = "__zhivex_budget__";
1610
+ var BUDGET_OWNER_NAMESPACE_KEY = "budgetOwnerNamespace";
1611
+ var isReservedBudgetNamespace = (namespace) => namespace === BUDGET_LEDGER_NAMESPACE || (namespace?.startsWith(`${BUDGET_LEDGER_NAMESPACE}:`) ?? false);
1612
+ var budgetLedgerNamespace = (namespace) => {
1613
+ if (!namespace || isReservedBudgetNamespace(namespace)) {
1614
+ throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
1615
+ }
1616
+ return `${BUDGET_LEDGER_NAMESPACE}:${namespace}`;
1617
+ };
1618
+ var budgetLedgerScope = (scope) => {
1619
+ if (scope.namespace && isReservedBudgetNamespace(scope.namespace)) {
1620
+ throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
1621
+ }
1622
+ return {
1623
+ tenantId: scope.tenantId,
1624
+ ...scope.userId ? { userId: scope.userId } : {},
1625
+ namespace: scope.namespace ? budgetLedgerNamespace(scope.namespace) : `${BUDGET_LEDGER_NAMESPACE}:`
1626
+ };
1627
+ };
1628
+ var sameIdentity = (left, right) => left?.tenantId === right.tenantId && (left.userId ?? undefined) === (right.userId ?? undefined);
1629
+ var bindBudgetLedgerStore = (primary, scope, budget = primary) => {
1630
+ const bound = budgetLedgerScope(scope);
1631
+ const ownerNamespace = scope.namespace ?? "";
1632
+ const isSdkBudget = (value) => sameIdentity(value, bound) && value?.namespace === BUDGET_LEDGER_NAMESPACE;
1633
+ const isBoundBudget = (value) => sameIdentity(value, bound) && value?.namespace === bound.namespace;
1634
+ const assertId = (id) => {
1635
+ if (!/^budget_[a-f0-9]{64}$/.test(id))
1636
+ throw new HarnessStateConflictError("Invalid budget ledger identity.");
1637
+ };
1638
+ const bindState = (state) => {
1639
+ const metadata = { ...state.metadata ?? {} };
1640
+ const owner = metadata[BUDGET_OWNER_NAMESPACE_KEY];
1641
+ if (owner !== undefined && owner !== ownerNamespace) {
1642
+ throw new HarnessStateConflictError("Budget ledger is bound to another namespace.");
1643
+ }
1644
+ return {
1645
+ ...state,
1646
+ scope: bound,
1647
+ metadata: { ...metadata, [BUDGET_OWNER_NAMESPACE_KEY]: ownerNamespace }
1648
+ };
1649
+ };
1650
+ const assertStored = (state) => {
1651
+ if (!state)
1652
+ return state;
1653
+ const owner = state.metadata?.[BUDGET_OWNER_NAMESPACE_KEY];
1654
+ if (state.scope?.namespace !== bound.namespace || owner !== ownerNamespace) {
1655
+ throw new HarnessStateConflictError("Budget ledger is not bound to this namespace.");
1656
+ }
1657
+ return state;
1658
+ };
1659
+ return {
1660
+ ...primary,
1661
+ load: async (id, requestedScope) => {
1662
+ if (!isSdkBudget(requestedScope) && !isBoundBudget(requestedScope))
1663
+ return primary.load(id, requestedScope);
1664
+ assertId(id);
1665
+ return assertStored(await budget.load(id, bound));
1666
+ },
1667
+ save: async (state, options) => {
1668
+ if (!isSdkBudget(state.scope) && !isBoundBudget(state.scope))
1669
+ return primary.save(state, options);
1670
+ assertId(state.runId);
1671
+ if (state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true) {
1672
+ throw new HarnessStateConflictError("Only SDK budget ledger states may use the reserved namespace.");
1673
+ }
1674
+ return budget.save(bindState(state), options);
1675
+ }
1676
+ };
1677
+ };
1678
+
1679
+ // src/runtime/task-budget.ts
1607
1680
  import {
1608
1681
  createAgentBudgetCoordinator,
1609
1682
  fingerprintAgentHarness as fingerprintAgentHarness2,
@@ -1751,10 +1824,10 @@ class TaskBudget {
1751
1824
  this.accountRunId = TaskBudget.accountId(scope, taskId);
1752
1825
  this.policy = structuredClone(account.policy);
1753
1826
  const budgetId = this.accountRunId;
1754
- this.coordinator = createAgentBudgetCoordinator({ store, scope, budgetId, limits: this.policy.limits });
1827
+ this.coordinator = createAgentBudgetCoordinator({ store: bindBudgetLedgerStore(store, scope), scope, budgetId, limits: this.policy.limits });
1755
1828
  this.coordinatorId = this.coordinator.id;
1756
1829
  this.budgetRunId = `budget_${fingerprintAgentHarness2({ budgetId, scope }).slice("sha256:".length)}`;
1757
- this.budgetScope = { tenantId: scope.tenantId, ...scope.userId ? { userId: scope.userId } : {}, namespace: "__zhivex_budget__" };
1830
+ this.budgetScope = budgetLedgerScope(scope);
1758
1831
  }
1759
1832
  static accountId(scope, taskId) {
1760
1833
  return `task_budget_${fingerprintAgentHarness2({ scope, taskId }).slice("sha256:".length)}`;
@@ -2268,4 +2341,4 @@ class TaskBudget {
2268
2341
  }
2269
2342
  }
2270
2343
 
2271
- export { protectStateFromGit, validateStateDirectory, HARNESS_SESSION_INDEX_FILE, HARNESS_SESSION_SCHEMA_VERSION, SESSION_RUN_STATUSES, TERMINAL_SESSION_RUN_STATUSES, ensurePrivateDatabase, openCliSessionStore, openSessionStore, withRuntimeInstruction, withFreshSystemInstructions, inspectRuntimeDiagnostics, inspectRuntimeManifest, estimateContextTokens, measureContext, MODEL_BUDGET_KEY, estimateRequestTokens, workBudgetReached, createModelBudget, USAGE_LEDGER_KEY, usagePricingSchema, inspectUsageLedger, formatUsageLedger, runUsageLedgerWithPolicy, UsageLedger, inspectTaskMonetaryUsage, taskAcceptanceContractSchema, compileTaskAcceptanceContract, TASK_ACCEPTANCE_KEY, TASK_ACCEPTANCE_EVIDENCE_KEY, readTaskAcceptanceLedger, nextTaskAcceptanceLedger, persistTaskAcceptanceRevision, taskAcceptanceCheckpointStore, TASK_BUDGET_KEY, TASK_BUDGET_ACCOUNT_KEY, inspectTaskBudgetSummary, assertTaskBudgetBackupLinks, TaskBudget };
2344
+ export { protectStateFromGit, validateStateDirectory, HARNESS_SESSION_INDEX_FILE, HARNESS_SESSION_SCHEMA_VERSION, SESSION_RUN_STATUSES, TERMINAL_SESSION_RUN_STATUSES, ensurePrivateDatabase, openCliSessionStore, openSessionStore, withRuntimeInstruction, withFreshSystemInstructions, inspectRuntimeDiagnostics, inspectRuntimeManifest, estimateContextTokens, measureContext, MODEL_BUDGET_KEY, estimateRequestTokens, workBudgetReached, createModelBudget, USAGE_LEDGER_KEY, usagePricingSchema, inspectUsageLedger, formatUsageLedger, runUsageLedgerWithPolicy, UsageLedger, inspectTaskMonetaryUsage, taskAcceptanceContractSchema, compileTaskAcceptanceContract, TASK_ACCEPTANCE_KEY, TASK_ACCEPTANCE_EVIDENCE_KEY, readTaskAcceptanceLedger, nextTaskAcceptanceLedger, persistTaskAcceptanceRevision, taskAcceptanceCheckpointStore, BUDGET_LEDGER_NAMESPACE, BUDGET_OWNER_NAMESPACE_KEY, isReservedBudgetNamespace, budgetLedgerNamespace, budgetLedgerScope, bindBudgetLedgerStore, TASK_BUDGET_KEY, TASK_BUDGET_ACCOUNT_KEY, inspectTaskBudgetSummary, assertTaskBudgetBackupLinks, TaskBudget };
@@ -20,7 +20,7 @@ import {
20
20
  taskAcceptanceChecks,
21
21
  validateEditProposal,
22
22
  workspaceFilePathSchema
23
- } from "./index-xdbkcccf.js";
23
+ } from "./index-6dxrbnxm.js";
24
24
  import {
25
25
  HarnessError,
26
26
  HarnessExecutionError,
@@ -406,10 +406,10 @@ import path3 from "node:path";
406
406
  // src/workspace/path-policy.ts
407
407
  var HARD_IGNORES = new Set([".git", ".next", ".turbo", ".zhivex-harness", "coverage", "dist", "node_modules"]);
408
408
  var isSensitiveName = (name) => {
409
- const normalized = name.toLocaleLowerCase();
409
+ const normalized = name.toLowerCase();
410
410
  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));
411
411
  };
412
- var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.toLocaleLowerCase()) || isSensitiveName(segment));
412
+ var isHardIgnored = (relativePath) => relativePath.split("/").some((segment) => HARD_IGNORES.has(segment.toLowerCase()) || isSensitiveName(segment));
413
413
 
414
414
  // src/workspace/workspace.ts
415
415
  var IGNORE_FILES = [".gitignore", ".zhivex-harnessignore"];
@@ -689,7 +689,7 @@ class Workspace {
689
689
  throw new HarnessWorkspaceError(`The path escapes the workspace: ${relativePath}`);
690
690
  const relative = wirePath(path3.relative(this.root, candidate));
691
691
  if (relative && isHardIgnored(relative)) {
692
- const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.toLocaleLowerCase()) || isSensitiveName(segment));
692
+ const blocked = relative.split("/").find((segment) => HARD_IGNORES.has(segment.toLowerCase()) || isSensitiveName(segment));
693
693
  throw new HarnessWorkspaceError(`The path is protected by the harness policy: ${blocked}`);
694
694
  }
695
695
  return candidate;
@@ -1672,30 +1672,65 @@ class Workspace {
1672
1672
  const audit = await this.audit({ operation: "quarantine", path: parsed.path, beforeDigest: source.digest, quarantineId });
1673
1673
  return { quarantineId, path: parsed.path, digest: source.digest, audit };
1674
1674
  }
1675
- async restoreQuarantined(input) {
1676
- return withWorkspaceMutation(this.root, () => this.restoreQuarantinedLocked(input));
1677
- }
1678
- async restoreQuarantinedLocked(input) {
1679
- const parsed = restoreFileInputSchema.parse(input);
1680
- this.checkAuditCapacity();
1675
+ async readRestorableQuarantine(quarantineId) {
1681
1676
  const directory = await this.secureQuarantineDirectory(false);
1682
1677
  let manifest;
1683
1678
  try {
1684
- const manifestFile = await this.readInternalRegularFile(path3.join(directory, `${parsed.quarantineId}.json`), "Quarantine manifest", 64 * 1024);
1679
+ const manifestFile = await this.readInternalRegularFile(path3.join(directory, `${quarantineId}.json`), "Quarantine manifest", 64 * 1024);
1685
1680
  manifest = JSON.parse(manifestFile.contents.toString("utf8"));
1686
1681
  } catch {
1687
- throw new HarnessWorkspaceError(`Quarantine entry was not found: ${parsed.quarantineId}.`);
1682
+ throw new HarnessWorkspaceError(`Quarantine entry was not found: ${quarantineId}.`);
1688
1683
  }
1689
- if (manifest.schemaVersion !== 1 || manifest.quarantineId !== parsed.quarantineId || manifest.status !== "quarantined") {
1690
- throw new HarnessWorkspaceError(`Quarantine entry is not restorable: ${parsed.quarantineId}.`);
1684
+ if (manifest.schemaVersion !== 1 || manifest.quarantineId !== quarantineId || manifest.status !== "quarantined") {
1685
+ throw new HarnessWorkspaceError(`Quarantine entry is not restorable: ${quarantineId}.`);
1691
1686
  }
1692
- const dataPath = path3.join(directory, `${parsed.quarantineId}.data`);
1687
+ const dataPath = path3.join(directory, `${quarantineId}.data`);
1693
1688
  const dataFile = await this.readInternalRegularFile(dataPath, "Quarantine payload", MAX_FILE_BYTES);
1694
1689
  const digest = digestBytes(dataFile.contents);
1695
- if (digest !== manifest.digest || parsed.expectedDigest && parsed.expectedDigest !== digest) {
1690
+ if (digest !== manifest.digest) {
1691
+ throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${quarantineId}.`);
1692
+ }
1693
+ return { manifest, dataPath, dataFile, digest };
1694
+ }
1695
+ async previewRestore(input) {
1696
+ const parsed = restoreFileInputSchema.parse(input);
1697
+ const quarantined = await this.readRestorableQuarantine(parsed.quarantineId);
1698
+ if (parsed.expectedDigest !== quarantined.digest) {
1699
+ throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${parsed.quarantineId}.`);
1700
+ }
1701
+ const destination = await this.safePath(parsed.destination, { allowMissing: true });
1702
+ if (destination.exists)
1703
+ throw new HarnessWorkspaceError(`Restore destination already exists: ${parsed.destination}.`);
1704
+ let after;
1705
+ try {
1706
+ after = new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(quarantined.dataFile.contents);
1707
+ } catch {
1708
+ throw new HarnessWorkspaceError("Preview requires valid UTF-8 text.");
1709
+ }
1710
+ return {
1711
+ proposalId: parsed.expectedDigest,
1712
+ files: [{
1713
+ path: parsed.destination,
1714
+ expectedDigest: parsed.expectedDigest,
1715
+ before: null,
1716
+ after,
1717
+ afterDigest: quarantined.digest,
1718
+ operation: "restore"
1719
+ }]
1720
+ };
1721
+ }
1722
+ async restoreQuarantined(input) {
1723
+ return withWorkspaceMutation(this.root, () => this.restoreQuarantinedLocked(input));
1724
+ }
1725
+ async restoreQuarantinedLocked(input) {
1726
+ const parsed = restoreFileInputSchema.parse(input);
1727
+ this.checkAuditCapacity();
1728
+ const quarantined = await this.readRestorableQuarantine(parsed.quarantineId);
1729
+ if (parsed.expectedDigest !== quarantined.digest) {
1696
1730
  throw new HarnessWorkspaceError(`Quarantine digest mismatch: ${parsed.quarantineId}.`);
1697
1731
  }
1698
- const destinationPath = parsed.destination ?? manifest.originalPath;
1732
+ const { manifest, dataPath, dataFile, digest } = quarantined;
1733
+ const destinationPath = parsed.destination;
1699
1734
  const destination = await this.safePath(destinationPath, { allowMissing: true });
1700
1735
  if (destination.exists)
1701
1736
  throw new HarnessWorkspaceError(`Restore destination already exists: ${destinationPath}.`);
@@ -2998,7 +3033,7 @@ var importPatch = async (runId, expectedPatchId, host, base, current, maxFileWri
2998
3033
  const rollbackErrors = [];
2999
3034
  for (const item of [...quarantined].reverse()) {
3000
3035
  try {
3001
- await host.restoreQuarantined({ quarantineId: item.quarantineId, expectedDigest: item.digest });
3036
+ await host.restoreQuarantined({ quarantineId: item.quarantineId, destination: item.path, expectedDigest: item.digest });
3002
3037
  } catch (rollbackError) {
3003
3038
  rollbackErrors.push(rollbackError);
3004
3039
  }
@@ -1,25 +1,25 @@
1
1
  import {
2
2
  changeEnvelopeSchema,
3
3
  verifyChangeEnvelope
4
- } from "./index-sstgcsr0.js";
4
+ } from "./index-yx52bt4a.js";
5
5
  import {
6
6
  APPROVAL_HISTORY_KEY,
7
7
  approvalDecisionViews,
8
8
  harnessPolicyDecisionEventSchema
9
- } from "./index-wm9xeq1n.js";
9
+ } from "./index-bt2p8x7r.js";
10
10
  import {
11
11
  TASK_ACCEPTANCE_EVIDENCE_KEY,
12
12
  USAGE_LEDGER_KEY,
13
13
  inspectRuntimeManifest,
14
14
  inspectUsageLedger,
15
15
  readTaskAcceptanceLedger
16
- } from "./index-zxg4ve4j.js";
16
+ } from "./index-fqqz1yt8.js";
17
17
  import {
18
18
  HARNESS_VERSION
19
- } from "./index-4fx5f3k5.js";
19
+ } from "./index-pq3kz1r8.js";
20
20
  import {
21
21
  taskCheckReceiptSchema
22
- } from "./index-xdbkcccf.js";
22
+ } from "./index-6dxrbnxm.js";
23
23
 
24
24
  // src/persistence/governance-report.ts
25
25
  import { createHash as createHash2 } from "node:crypto";
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var package_default = {
3
3
  name: "@zhivex-ai/harness",
4
- version: "1.4.1",
4
+ version: "1.5.0",
5
5
  description: "Governed, provider-portable runtime for coding agents with durable approvals and isolated repository execution.",
6
6
  type: "module",
7
7
  license: "MIT",
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  openWorkspaceCheckpointStore
3
- } from "./index-6599mpmy.js";
3
+ } from "./index-63gbp0cz.js";
4
4
  import {
5
5
  appendUserMessage,
6
6
  checkEvidence,
@@ -16,17 +16,17 @@ import {
16
16
  reviseHarnessTaskAcceptance,
17
17
  runHarness,
18
18
  runHarnessTask
19
- } from "./index-0zs2dz26.js";
19
+ } from "./index-6fp7kdpx.js";
20
20
  import {
21
21
  ASSISTANT_RESPONSE_KEY,
22
22
  TASK_SOURCE_KEY,
23
23
  assistantResponses,
24
24
  cancelHarnessRun,
25
25
  captureTaskSources
26
- } from "./index-f6mqa5gk.js";
26
+ } from "./index-wwthv10a.js";
27
27
  import {
28
28
  resolvePackageCheckCommand
29
- } from "./index-d9zkmhz3.js";
29
+ } from "./index-ggstwr0v.js";
30
30
  import {
31
31
  APPROVAL_HISTORY_KEY,
32
32
  approvalDecisionViews,
@@ -35,7 +35,7 @@ import {
35
35
  inspectHarnessPolicy,
36
36
  observeHarnessPolicyDecisions,
37
37
  readApprovalDecisions
38
- } from "./index-wm9xeq1n.js";
38
+ } from "./index-bt2p8x7r.js";
39
39
  import {
40
40
  SESSION_RUN_STATUSES,
41
41
  TASK_ACCEPTANCE_KEY,
@@ -48,12 +48,12 @@ import {
48
48
  nextTaskAcceptanceLedger,
49
49
  openCliSessionStore,
50
50
  readTaskAcceptanceLedger
51
- } from "./index-zxg4ve4j.js";
51
+ } from "./index-fqqz1yt8.js";
52
52
  import {
53
53
  createEditProposal,
54
54
  editProposalInputSchema,
55
55
  fileDigestSchema
56
- } from "./index-xdbkcccf.js";
56
+ } from "./index-6dxrbnxm.js";
57
57
  import {
58
58
  CLI_JSON_SCHEMA_VERSION,
59
59
  harnessClientRequestSchema,
@@ -476,7 +476,7 @@ import { createHash as createHash3 } from "node:crypto";
476
476
  import { z } from "zod";
477
477
  var APPROVAL_DIFFS_KEY = "clientApprovalDiffsV1";
478
478
  var sha2 = z.string().regex(/^sha256:[a-f0-9]{64}$/);
479
- var file = z.object({ path: z.string().max(1024), expectedDigest: sha2.nullable(), before: z.string().nullable(), after: z.string().nullable(), afterDigest: sha2.nullable(), beforeMode: z.number().int().optional(), afterMode: z.number().int().optional(), operation: z.enum(["create", "update", "delete"]).optional() }).strict();
479
+ var file = z.object({ path: z.string().max(1024), expectedDigest: sha2.nullable(), before: z.string().nullable(), after: z.string().nullable(), afterDigest: sha2.nullable(), beforeMode: z.number().int().optional(), afterMode: z.number().int().optional(), operation: z.enum(["create", "update", "delete", "restore"]).optional() }).strict();
480
480
  var archive = z.object({ approvalId: z.string().max(256), digest: z.string().regex(/^[a-f0-9]{64}$/), proposalId: sha2, files: z.array(file).min(1).max(50) }).strict();
481
481
  var archives = z.array(archive).max(512);
482
482
  var maxBytes = 2 * 1024 * 1024;
@@ -534,14 +534,14 @@ async function attachApprovalPreviews(run, workspace, options = {}) {
534
534
  let remaining = 256 * 1024;
535
535
  for (const approval of run.approvals) {
536
536
  const action = approval.action;
537
- if (!action || !["apply_patch", "apply_reviewed_edits", "apply_reviewed_replacement", "apply_environment_patch", "verify_and_apply_environment_patch", "verify_and_apply_reviewed_edits"].includes(String(action.name)))
537
+ if (!action || !["apply_patch", "apply_reviewed_edits", "apply_reviewed_replacement", "apply_environment_patch", "verify_and_apply_environment_patch", "verify_and_apply_reviewed_edits", "restore_file"].includes(String(action.name)))
538
538
  continue;
539
539
  approval.filePreview = { status: "unavailable" };
540
540
  try {
541
541
  if (typeof action.arguments !== "string" || Buffer.byteLength(action.arguments) > 256 * 1024)
542
542
  continue;
543
543
  const args = JSON.parse(action.arguments);
544
- const preview = action.name === "apply_environment_patch" || action.name === "verify_and_apply_environment_patch" ? await (async () => {
544
+ const preview = action.name === "restore_file" ? await workspace.previewRestore(args) : action.name === "apply_environment_patch" || action.name === "verify_and_apply_environment_patch" ? await (async () => {
545
545
  if (!options.environment)
546
546
  throw new Error("OCI_REVIEW_UNAVAILABLE");
547
547
  const patchId = fileDigestSchema.parse(args?.patchId);
@@ -1,15 +1,15 @@
1
1
  import {
2
2
  createHarnessClientAdapter
3
- } from "./index-02pqtbbg.js";
3
+ } from "./index-vc7yxz50.js";
4
4
  import {
5
5
  harnessPolicyDecisionEventSchema
6
- } from "./index-wm9xeq1n.js";
6
+ } from "./index-bt2p8x7r.js";
7
7
  import {
8
8
  openCliSessionStore
9
- } from "./index-zxg4ve4j.js";
9
+ } from "./index-fqqz1yt8.js";
10
10
  import {
11
11
  SqliteDatabase
12
- } from "./index-xdbkcccf.js";
12
+ } from "./index-6dxrbnxm.js";
13
13
  import {
14
14
  harnessClientRequestSchema,
15
15
  streamEventDocument
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  harnessExecutionSession
3
- } from "./index-d9zkmhz3.js";
3
+ } from "./index-ggstwr0v.js";
4
4
  import {
5
5
  defaultHarnessNamespace
6
6
  } from "./index-1265j7t2.js";
@@ -9,20 +9,23 @@ import {
9
9
  TASK_BUDGET_KEY,
10
10
  TaskBudget,
11
11
  USAGE_LEDGER_KEY,
12
+ bindBudgetLedgerStore,
13
+ budgetLedgerScope,
12
14
  ensurePrivateDatabase,
13
15
  estimateRequestTokens,
14
16
  inspectRuntimeDiagnostics,
15
17
  inspectRuntimeManifest,
16
18
  inspectTaskBudgetSummary,
17
19
  inspectUsageLedger,
20
+ isReservedBudgetNamespace,
18
21
  protectStateFromGit,
19
22
  readTaskAcceptanceLedger,
20
23
  validateStateDirectory,
21
24
  withRuntimeInstruction
22
- } from "./index-zxg4ve4j.js";
25
+ } from "./index-fqqz1yt8.js";
23
26
  import {
24
27
  SqliteDatabase
25
- } from "./index-xdbkcccf.js";
28
+ } from "./index-6dxrbnxm.js";
26
29
  import {
27
30
  HarnessConfigError,
28
31
  HarnessStateConflictError,
@@ -869,37 +872,7 @@ import {
869
872
  createSqliteAgentRunStore
870
873
  } from "@zhivex-ai/agents/ops";
871
874
  import { createAgentRunLedger } from "@zhivex-ai/agents/control-plane";
872
- var budgetScopeFor = (config) => ({
873
- tenantId: config.scope.tenantId,
874
- ...config.scope.userId ? { userId: config.scope.userId } : {},
875
- namespace: "__zhivex_budget__"
876
- });
877
- var withBudgetLedgerStore = (primary, budget, config) => {
878
- const scope = budgetScopeFor(config);
879
- const budgetScope = (value) => value?.namespace === scope.namespace && value.tenantId === scope.tenantId && (value.userId ?? undefined) === (scope.userId ?? undefined);
880
- const assertId = (id) => {
881
- if (!/^budget_[a-f0-9]{64}$/.test(id))
882
- throw new HarnessStateConflictError("Invalid budget ledger identity.");
883
- };
884
- return {
885
- ...primary,
886
- load: async (id, requestedScope) => {
887
- if (!budgetScope(requestedScope))
888
- return primary.load(id, requestedScope);
889
- assertId(id);
890
- return budget.load(id, requestedScope);
891
- },
892
- save: async (state, options) => {
893
- if (!budgetScope(state.scope))
894
- return primary.save(state, options);
895
- assertId(state.runId);
896
- if (state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true) {
897
- throw new HarnessStateConflictError("Only SDK budget ledger states may use the reserved namespace.");
898
- }
899
- return budget.save(state, options);
900
- }
901
- };
902
- };
875
+ var withBudgetLedgerStore = (primary, budget, config) => bindBudgetLedgerStore(primary, config.scope, budget);
903
876
  var HARNESS_SQLITE_FILE = "operations.sqlite";
904
877
  var HARNESS_OPERATIONS_SCHEMA_VERSION = 1;
905
878
  var TERMINAL_RUN_STATUSES = ["completed", "failed", "cancelled", "timed_out"];
@@ -1154,13 +1127,12 @@ var migrateLegacyFileRuns = async (directory, target, config) => {
1154
1127
  return result;
1155
1128
  };
1156
1129
  var openHarnessPersistence = async (config, options = {}) => {
1157
- if (config.scope.namespace === "__zhivex_budget__")
1158
- throw new HarnessStateConflictError("The budget ledger namespace is reserved.");
1130
+ const budgetScope = budgetLedgerScope(config.scope);
1159
1131
  await validateStateDirectory(config.workspace, config.stateDirectory);
1160
1132
  await ensurePrivateStateDirectory(config.stateDirectory);
1161
1133
  await protectStateFromGit(config.workspace, config.stateDirectory);
1162
1134
  if (config.storeBackend === "file") {
1163
- const store = withBudgetLedgerStore(createFileAgentRunStore({ directory: config.stateDirectory, scope: config.scope }), createFileAgentRunStore({ directory: config.stateDirectory, scope: budgetScopeFor(config) }), config);
1135
+ const store = withBudgetLedgerStore(createFileAgentRunStore({ directory: config.stateDirectory, scope: config.scope }), createFileAgentRunStore({ directory: config.stateDirectory, scope: budgetScope }), config);
1164
1136
  const migration = shouldMigrateLegacyRuns(config, options.migrateLegacyFileStore) ? await migrateLegacyFileRuns(config.stateDirectory, store, config) : emptyMigration();
1165
1137
  return {
1166
1138
  backend: "file",
@@ -1205,7 +1177,7 @@ var openHarnessPersistence = async (config, options = {}) => {
1205
1177
  database.exec("PRAGMA foreign_keys = ON");
1206
1178
  await chmod(databasePath, 384);
1207
1179
  const databaseLike = sqliteAdapter(database);
1208
- const store = withBudgetLedgerStore(createSqliteAgentRunStore({ db: databaseLike, scope: config.scope, history: "incremental" }), createSqliteAgentRunStore({ db: databaseLike, scope: budgetScopeFor(config) }), config);
1180
+ const store = withBudgetLedgerStore(createSqliteAgentRunStore({ db: databaseLike, scope: config.scope, history: "incremental" }), createSqliteAgentRunStore({ db: databaseLike, scope: budgetScope }), config);
1209
1181
  database.exec(`CREATE TRIGGER IF NOT EXISTS zhivex_task_budget_retain_run
1210
1182
  BEFORE DELETE ON zhivex_agent_runs
1211
1183
  WHEN json_type(OLD.state_json, '$.metadata.zhivexTaskBudgetAccountV1') IS NOT NULL
@@ -1302,7 +1274,7 @@ var projectMemoryBinding = (workspace, scope) => {
1302
1274
  segment(scope.userId);
1303
1275
  if (scope.namespace !== undefined)
1304
1276
  segment(scope.namespace);
1305
- if (scope.namespace === "__zhivex_budget__")
1277
+ if (isReservedBudgetNamespace(scope.namespace))
1306
1278
  throw new HarnessConfigError("Reserved memory namespace.");
1307
1279
  const key = (kind, value) => createHash4("sha256").update(kind).update("\x00").update(value).digest("hex");
1308
1280
  const workspaceKey = key("workspace", workspace);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  workspaceCheckpointSchema,
3
3
  workspaceRestoreOperationSchema
4
- } from "./index-6599mpmy.js";
4
+ } from "./index-63gbp0cz.js";
5
5
  import {
6
6
  HARNESS_OPERATIONS_SCHEMA_VERSION,
7
7
  HARNESS_SQLITE_FILE,
@@ -9,17 +9,20 @@ import {
9
9
  openHarnessPersistence,
10
10
  projectMemoryRecordKey,
11
11
  sqliteAdapter
12
- } from "./index-f6mqa5gk.js";
12
+ } from "./index-wwthv10a.js";
13
13
  import {
14
+ BUDGET_LEDGER_NAMESPACE,
15
+ BUDGET_OWNER_NAMESPACE_KEY,
14
16
  HARNESS_SESSION_SCHEMA_VERSION,
15
17
  assertTaskBudgetBackupLinks,
18
+ budgetLedgerNamespace,
16
19
  openCliSessionStore,
17
20
  validateStateDirectory
18
- } from "./index-zxg4ve4j.js";
21
+ } from "./index-fqqz1yt8.js";
19
22
  import {
20
23
  SqliteDatabase,
21
24
  readRegularFileChunksNoFollow
22
- } from "./index-xdbkcccf.js";
25
+ } from "./index-6dxrbnxm.js";
23
26
  import {
24
27
  HarnessStateConflictError,
25
28
  HarnessWorkspaceError
@@ -398,7 +401,8 @@ var coordinatorIds = (runs) => new Set(runs.flatMap(({ state }) => {
398
401
  var validateBudgetLedger = (config, run, identities) => {
399
402
  const state = validatedRunState(run.state, `Budget ledger ${run.key}`);
400
403
  const identity = state.metadata?.budgetIdentity;
401
- if (state.scope?.namespace !== "__zhivex_budget__" || state.scope?.tenantId !== config.scope.tenantId || (state.scope?.userId ?? undefined) !== (config.scope.userId ?? undefined) || state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true || typeof identity !== "string" || !identities.has(identity) || !/^budget_[a-f0-9]{64}$/.test(state.runId) || !sdkRunKeyMatches(run.key, state.scope, state.runId)) {
404
+ const ownerNamespace = config.scope.namespace;
405
+ if (!ownerNamespace || state.scope?.namespace !== budgetLedgerNamespace(ownerNamespace) || state.metadata?.[BUDGET_OWNER_NAMESPACE_KEY] !== ownerNamespace || state.scope?.tenantId !== config.scope.tenantId || (state.scope?.userId ?? undefined) !== (config.scope.userId ?? undefined) || state.provider !== "zhivex" || state.modelId !== "budget-coordinator" || state.metadata?.budgetCoordinator !== true || typeof identity !== "string" || !identities.has(identity) || !/^budget_[a-f0-9]{64}$/.test(state.runId) || !sdkRunKeyMatches(run.key, state.scope, state.runId)) {
402
406
  throw new HarnessStateConflictError("Budget ledger is not bound to an exported run and durable scope.");
403
407
  }
404
408
  assertTerminalState(state, `Budget ledger ${run.key}`);
@@ -532,7 +536,9 @@ var readPayload = async (config, recordedWorkspace, accessLease) => {
532
536
  const metadata = state.metadata;
533
537
  if (typeof metadata?.budgetIdentity !== "string" || !identities.has(metadata.budgetIdentity)) {
534
538
  const scope = state.scope;
535
- if (scope?.namespace === "__zhivex_budget__" && scope.tenantId === config.scope.tenantId && (scope.userId ?? undefined) === (config.scope.userId ?? undefined)) {
539
+ const sameTenant = scope?.tenantId === config.scope.tenantId && (scope?.userId ?? undefined) === (config.scope.userId ?? undefined);
540
+ const boundNamespace = config.scope.namespace ? budgetLedgerNamespace(config.scope.namespace) : undefined;
541
+ if (sameTenant && (scope?.namespace === BUDGET_LEDGER_NAMESPACE || scope?.namespace === boundNamespace)) {
536
542
  throw new HarnessStateConflictError("An unlinked shared budget ledger cannot be safely assigned to this backup scope; retain the original database.");
537
543
  }
538
544
  return [];
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  fileDigestSchema
3
- } from "./index-xdbkcccf.js";
3
+ } from "./index-6dxrbnxm.js";
4
4
 
5
5
  // src/workspace/change-envelope.ts
6
6
  import { createHash } from "node:crypto";
package/dist/index.js CHANGED
@@ -52,7 +52,7 @@ import {
52
52
  timeToSafeFixDriverResultSchema,
53
53
  timeToSafeFixLatencyStatistics,
54
54
  timeToSafeFixTaskSchema
55
- } from "./index-sk3cap35.js";
55
+ } from "./index-eps3c3g6.js";
56
56
  import {
57
57
  HARNESS_STATE_BACKUP_MAX_BYTES,
58
58
  HARNESS_STATE_BACKUP_SCHEMA_VERSION,
@@ -63,7 +63,7 @@ import {
63
63
  inspectHarnessState,
64
64
  readHarnessStateBackup,
65
65
  stateBackupBundleSchema
66
- } from "./index-74kkpyqk.js";
66
+ } from "./index-xfedewpt.js";
67
67
  import {
68
68
  CLI_COMMAND_OPTION_CONTRACTS,
69
69
  CLI_OPTION_DEFINITIONS,
@@ -101,7 +101,7 @@ import {
101
101
  digestChangeEnvelopeArtifact,
102
102
  externalAttestationReferenceSchema,
103
103
  verifyChangeEnvelope
104
- } from "./index-sstgcsr0.js";
104
+ } from "./index-yx52bt4a.js";
105
105
  import {
106
106
  harnessLocalCredentialsSchema,
107
107
  openHarnessActivityStore,
@@ -109,13 +109,13 @@ import {
109
109
  recoverHarnessLocalService,
110
110
  requestHarnessLocalService,
111
111
  startHarnessLocalService
112
- } from "./index-crta5wzv.js";
112
+ } from "./index-vvzc5ezz.js";
113
113
  import {
114
114
  createHarnessClientAdapter
115
- } from "./index-02pqtbbg.js";
115
+ } from "./index-vc7yxz50.js";
116
116
  import {
117
117
  openWorkspaceCheckpointStore
118
- } from "./index-6599mpmy.js";
118
+ } from "./index-63gbp0cz.js";
119
119
  import {
120
120
  DEFAULT_HARNESS_CONTEXT_MANIFEST,
121
121
  DEFAULT_HARNESS_PROJECT_INSTRUCTIONS,
@@ -154,7 +154,7 @@ import {
154
154
  runHarness,
155
155
  runHarnessReviewGroup,
156
156
  selectHarnessModel
157
- } from "./index-0zs2dz26.js";
157
+ } from "./index-6fp7kdpx.js";
158
158
  import {
159
159
  HARNESS_OPERATIONS_SCHEMA_VERSION,
160
160
  HARNESS_SQLITE_FILE,
@@ -165,7 +165,7 @@ import {
165
165
  listHarnessRuns,
166
166
  migrateLegacyFileRuns,
167
167
  openHarnessPersistence
168
- } from "./index-f6mqa5gk.js";
168
+ } from "./index-wwthv10a.js";
169
169
  import {
170
170
  HARNESS_MCP_CONFIG_SCHEMA_VERSION,
171
171
  HARNESS_MCP_PERMISSIONS,
@@ -173,7 +173,7 @@ import {
173
173
  createHttpMcpClient,
174
174
  loadHarnessMcpConfiguration,
175
175
  normalizeHarnessMcpConfiguration
176
- } from "./index-dtb2c6ds.js";
176
+ } from "./index-5zbq9ztj.js";
177
177
  import {
178
178
  CliOciRuntimeAdapter,
179
179
  HARNESS_EXECUTION_ARTIFACT_SCHEMA_VERSION,
@@ -185,7 +185,7 @@ import {
185
185
  describeOciCommand,
186
186
  executionFingerprintInput,
187
187
  harnessExecutionSession
188
- } from "./index-d9zkmhz3.js";
188
+ } from "./index-ggstwr0v.js";
189
189
  import {
190
190
  BUILTIN_PROVIDER_REGISTRATIONS,
191
191
  DEFAULT_ALLOWED_CHECKS,
@@ -217,7 +217,7 @@ import {
217
217
  applyHarnessToolPolicy,
218
218
  createHarnessToolPolicy,
219
219
  harnessToolPolicySchema
220
- } from "./index-wm9xeq1n.js";
220
+ } from "./index-bt2p8x7r.js";
221
221
  import {
222
222
  HARNESS_SESSION_INDEX_FILE,
223
223
  HARNESS_SESSION_SCHEMA_VERSION,
@@ -226,12 +226,12 @@ import {
226
226
  openCliSessionStore,
227
227
  openSessionStore,
228
228
  validateStateDirectory
229
- } from "./index-zxg4ve4j.js";
229
+ } from "./index-fqqz1yt8.js";
230
230
  import {
231
231
  BUN_ENGINE_RANGE,
232
232
  HARNESS_VERSION,
233
233
  NODE_ENGINE_RANGE
234
- } from "./index-4fx5f3k5.js";
234
+ } from "./index-pq3kz1r8.js";
235
235
  import {
236
236
  EDIT_CONTRACT_SCHEMA_VERSION,
237
237
  EDIT_DIGEST_ALGORITHM,
@@ -254,7 +254,7 @@ import {
254
254
  restoreFileInputSchema,
255
255
  validateEditProposal,
256
256
  workspaceFilePathSchema
257
- } from "./index-xdbkcccf.js";
257
+ } from "./index-6dxrbnxm.js";
258
258
  import {
259
259
  CLI_EVENT_SCHEMA_VERSION,
260
260
  CLI_JSON_SCHEMA_VERSION,