@zhivex-ai/harness 1.3.0 → 1.4.0-rc.6

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 (73) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/README.md +30 -8
  3. package/SECURITY.md +1 -1
  4. package/SUPPORT.md +2 -2
  5. package/contracts/engine-api.json +70 -0
  6. package/contracts/public-api.json +162 -0
  7. package/contracts/stable-api-signatures.json +9 -9
  8. package/dist/acp-cli.js +38 -12
  9. package/dist/cli/arguments.d.ts +5 -1
  10. package/dist/cli/memory.d.ts +2 -0
  11. package/dist/{cli-entry-bx3vjw7h.js → cli-entry-4k1cf0m3.js} +38 -12
  12. package/dist/{cli-entry-1gfg3brj.js → cli-entry-9c2k0zwz.js} +1086 -446
  13. package/dist/{cli-etsmtst3.js → cli-s1rs15vv.js} +136 -24
  14. package/dist/cli.js +2 -2
  15. package/dist/client/check-evidence.d.ts +17 -0
  16. package/dist/client/run-document.d.ts +12 -0
  17. package/dist/compat/cli-options.d.ts +12 -1
  18. package/dist/compat/code-support.d.ts +4 -0
  19. package/dist/compat/code-support.js +12 -4
  20. package/dist/context/compaction.d.ts +5 -0
  21. package/dist/context/project-memory-context.d.ts +6 -0
  22. package/dist/context/task-memory.d.ts +33 -1
  23. package/dist/engine/acp.js +8 -8
  24. package/dist/engine/client.js +7 -7
  25. package/dist/engine/desktop-state-v1.js +5 -5
  26. package/dist/engine/index.d.ts +4 -0
  27. package/dist/engine/index.js +88 -11
  28. package/dist/engine/mcp-stdio-v1.js +4 -4
  29. package/dist/engine/service.js +8 -8
  30. package/dist/hostile-repository-demo.js +1260 -712
  31. package/dist/{index-azmwjp95.js → index-0g939pfs.js} +13 -12
  32. package/dist/{index-6m08wvab.js → index-2tgb2b4e.js} +3 -3
  33. package/dist/{index-w2sj0wkw.js → index-49ys3s4q.js} +64 -14
  34. package/dist/{index-hnaxfpb5.js → index-4j1h4j8c.js} +137 -42
  35. package/dist/{index-8zzhggxv.js → index-a1q355va.js} +125 -2
  36. package/dist/{index-8vm2m34s.js → index-dj5z774k.js} +18 -2
  37. package/dist/{index-z0qxmkm2.js → index-e6r04a18.js} +1 -1
  38. package/dist/{index-yb239ghk.js → index-r0tdj04h.js} +21 -6
  39. package/dist/{index-h0mqx2ed.js → index-rd0s3gqj.js} +300 -4
  40. package/dist/{index-42gp2hy8.js → index-vtqfjnab.js} +98 -27
  41. package/dist/{index-2jrdcj6b.js → index-wq2tax80.js} +15 -1
  42. package/dist/{index-a97bezbz.js → index-z6br6qdn.js} +44 -23
  43. package/dist/index.js +11 -11
  44. package/dist/persistence/project-memory-command.d.ts +11 -0
  45. package/dist/persistence/project-memory.d.ts +109 -0
  46. package/dist/persistence/sdk-store-identity.d.ts +4 -0
  47. package/dist/persistence/sessions.d.ts +4 -0
  48. package/dist/runtime/error-diagnostics.d.ts +35 -0
  49. package/dist/runtime/harness.d.ts +5 -1
  50. package/dist/runtime/run-interruption.d.ts +1 -1
  51. package/dist/runtime/runtime-checkpoints.d.ts +3 -0
  52. package/dist/runtime/task-acceptance-native.d.ts +10 -0
  53. package/dist/runtime/time-to-safe-fix.d.ts +35 -0
  54. package/dist/service-cli.js +8 -8
  55. package/dist/{service-client-cli-3365rfav.js → service-client-cli-b52c67hx.js} +2 -2
  56. package/dist/verify-historical-migrations.js +1021 -456
  57. package/dist/zhx.js +2 -2
  58. package/docs/CLI.md +4 -0
  59. package/docs/CLIENT_PROTOCOL.md +10 -0
  60. package/docs/CODE_RELEASE.md +136 -19
  61. package/docs/CONTEXT_ENGINEERING.md +25 -0
  62. package/docs/DURABLE_OPERATIONS.md +60 -1
  63. package/docs/ENGINE_API.md +23 -9
  64. package/docs/EXTENSIBILITY.md +1 -1
  65. package/docs/FIRST_USE.md +6 -6
  66. package/docs/LOCAL_SERVICE.md +10 -4
  67. package/docs/PROJECT_MEMORY.md +157 -0
  68. package/docs/README.md +2 -0
  69. package/docs/STABILITY.md +1 -1
  70. package/docs/WORKSPACE_CHECKPOINTS.md +11 -1
  71. package/evaluations/representative-assembly-matrix.json +55 -1
  72. package/examples/README.md +4 -4
  73. package/package.json +12 -11
@@ -4,7 +4,7 @@ import {
4
4
  taskAcceptanceDeliveryDiagnostic,
5
5
  taskAcceptanceOutcome,
6
6
  workspaceFilePathSchema
7
- } from "./index-yb239ghk.js";
7
+ } from "./index-r0tdj04h.js";
8
8
  import {
9
9
  HarnessConfigError,
10
10
  HarnessStateConflictError,
@@ -1171,8 +1171,7 @@ class UsageLedger {
1171
1171
  }
1172
1172
  async run(runId, operation, historicalUsageUnknown = false) {
1173
1173
  this.database.query("INSERT OR IGNORE INTO zhivex_usage_policies (scope_key, run_id, policy) VALUES (?1, ?2, ?3)").run(this.key, runId, JSON.stringify({ ...this.options, ...historicalUsageUnknown ? { historicalUsageUnknown: true } : {} }));
1174
- this.database.query("UPDATE zhivex_usage_calls SET status = 'unknown' WHERE scope_key = ?1 AND run_id = ?2 AND status = 'pending'").run(this.key, runId);
1175
- return current.run({ ledger: this, runId }, operation);
1174
+ return current.run({ ledger: this, runId, recovered: false }, operation);
1176
1175
  }
1177
1176
  summary(runId) {
1178
1177
  const rows = this.database.query("SELECT * FROM zhivex_usage_calls WHERE scope_key = ?1 AND run_id = ?2 ORDER BY rowid").all(this.key, runId);
@@ -1300,6 +1299,21 @@ class UsageLedger {
1300
1299
  store(store) {
1301
1300
  const ledger = this;
1302
1301
  return new Proxy(store, { get(target, key) {
1302
+ if (key === "acquireLease" && target.acquireLease)
1303
+ return async (...args) => {
1304
+ const lease = await target.acquireLease(...args);
1305
+ const scope = current.getStore();
1306
+ if (lease && scope?.ledger === ledger && args[0] === scope.runId && !scope.recovered) {
1307
+ try {
1308
+ ledger.database.query("UPDATE zhivex_usage_calls SET status = 'unknown' WHERE scope_key = ?1 AND run_id = ?2 AND status = 'pending'").run(ledger.key, scope.runId);
1309
+ scope.recovered = true;
1310
+ } catch (error) {
1311
+ await target.releaseLease?.(args[0], lease.ownerId, args[2]);
1312
+ throw error;
1313
+ }
1314
+ }
1315
+ return lease;
1316
+ };
1303
1317
  if (key === "save")
1304
1318
  return async (...args) => {
1305
1319
  const scope = current.getStore();
@@ -1387,28 +1401,14 @@ function compileTaskAcceptanceContract(input) {
1387
1401
  return { contract, digest: `sha256:${createHash2("sha256").update(JSON.stringify(contract)).digest("hex")}` };
1388
1402
  }
1389
1403
 
1390
- // src/context/task-memory.ts
1391
- import { createHash as createHash3 } from "node:crypto";
1392
- import { tool, serializeJsonValue as serializeJsonValue2 } from "@zhivex-ai/core";
1393
- import { createRedactionPolicy as createRedactionPolicy3 } from "@zhivex-ai/agents";
1394
- import { z as z8 } from "zod";
1395
-
1396
- // src/runtime/repair-verifier.ts
1397
- import { z as z6 } from "zod";
1398
- var verifierSchema = z6.strictObject({
1399
- command: z6.string().min(1).max(64),
1400
- args: z6.array(z6.string().max(8192)).max(256).default([]),
1401
- purpose: z6.string().min(1).max(500)
1402
- });
1403
-
1404
1404
  // src/runtime/task-acceptance-record.ts
1405
- import { z as z7 } from "zod";
1405
+ import { z as z6 } from "zod";
1406
1406
  import { serializeJsonValue } from "@zhivex-ai/core";
1407
1407
  var TASK_ACCEPTANCE_KEY = "zhivexTaskAcceptanceV1";
1408
1408
  var TASK_ACCEPTANCE_EVIDENCE_KEY = "zhivexTaskAcceptanceEvidenceV1";
1409
- var digest2 = z7.string().regex(/^sha256:[a-f0-9]{64}$/);
1410
- var revisionSchema = z7.strictObject({ revision: z7.number().int().min(1), digest: digest2, previousDigest: digest2.nullable(), contract: taskAcceptanceContractSchema });
1411
- var ledgerSchema = z7.strictObject({ schemaVersion: z7.literal(1), revisions: z7.array(revisionSchema).min(1).max(16) });
1409
+ var digest2 = z6.string().regex(/^sha256:[a-f0-9]{64}$/);
1410
+ var revisionSchema = z6.strictObject({ revision: z6.number().int().min(1), digest: digest2, previousDigest: digest2.nullable(), contract: taskAcceptanceContractSchema });
1411
+ var ledgerSchema = z6.strictObject({ schemaVersion: z6.literal(1), revisions: z6.array(revisionSchema).min(1).max(16) });
1412
1412
  function readTaskAcceptanceLedger(state) {
1413
1413
  const raw = state.metadata?.[TASK_ACCEPTANCE_KEY];
1414
1414
  if (raw === undefined)
@@ -1484,9 +1484,55 @@ function taskAcceptanceCheckpointStore(store, runId, ledger) {
1484
1484
  } });
1485
1485
  }
1486
1486
 
1487
+ // src/context/task-memory.ts
1488
+ import { createHash as createHash3 } from "node:crypto";
1489
+ import { tool, serializeJsonValue as serializeJsonValue2 } from "@zhivex-ai/core";
1490
+ import { createRedactionPolicy as createRedactionPolicy3 } from "@zhivex-ai/agents";
1491
+ import { z as z8 } from "zod";
1492
+
1493
+ // src/runtime/repair-verifier.ts
1494
+ import { z as z7 } from "zod";
1495
+ var verifierSchema = z7.strictObject({
1496
+ command: z7.string().min(1).max(64),
1497
+ args: z7.array(z7.string().max(8192)).max(256).default([]),
1498
+ purpose: z7.string().min(1).max(500)
1499
+ });
1500
+
1487
1501
  // src/context/task-memory.ts
1488
1502
  var redact2 = createRedactionPolicy3({ includeEmails: true });
1489
1503
  var TASK_SOURCE_KEY = "zhivexTaskSources";
1504
+ var ASSISTANT_RESPONSE_KEY = "zhivexAssistantResponses";
1505
+ var MAX_ASSISTANT_RESPONSES = 3;
1506
+ var MAX_ASSISTANT_RESPONSE_CHARACTERS = 64000;
1507
+ var redactSource = (text2) => redact2.redactText(text2).replace(/\bBearer\s+\S+/gi, "Bearer [REDACTED]").replace(/\b([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)|api[_-]?key|access[_-]?token|password)\s*([=:])\s*(?:"[^"]*"|'[^']*'|\S+)/gi, "$1$2[REDACTED]").replace(/\b(?:sk|ghp|gho|github_pat)-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]");
1508
+ var sourceId = (text2) => `sha256:${createHash3("sha256").update(text2).digest("hex")}`;
1509
+ var assistantResponses = (metadata) => {
1510
+ const value = metadata && typeof metadata === "object" ? metadata[ASSISTANT_RESPONSE_KEY] : undefined;
1511
+ return Array.isArray(value) ? value.slice(-MAX_ASSISTANT_RESPONSES).flatMap((item) => {
1512
+ if (!item || typeof item.text !== "string" || item.text.length > MAX_ASSISTANT_RESPONSE_CHARACTERS || item.id !== sourceId(item.text))
1513
+ return [];
1514
+ return [{ id: item.id, text: item.text, truncated: item.truncated === true }];
1515
+ }) : [];
1516
+ };
1517
+ var captureAssistantResponses = (metadata, messages) => {
1518
+ const sources = new Map(assistantResponses(metadata).map((source) => [source.id, source]));
1519
+ for (const message of messages) {
1520
+ if (message.role !== "assistant" || message.parts.some((part) => part.type === "tool-call"))
1521
+ continue;
1522
+ const text2 = redactSource(message.parts.flatMap((part) => part.type === "text" ? [part.text] : []).join(`
1523
+ `));
1524
+ if (!text2.trim() || /^\[Compacted (?:conversation context|prior conversation)\]/.test(text2))
1525
+ continue;
1526
+ const bounded = text2.slice(0, MAX_ASSISTANT_RESPONSE_CHARACTERS);
1527
+ const id = sourceId(bounded);
1528
+ const truncated = sources.get(id)?.truncated === true || text2.length > bounded.length;
1529
+ sources.delete(id);
1530
+ sources.set(id, { id, text: bounded, truncated });
1531
+ while (sources.size > MAX_ASSISTANT_RESPONSES)
1532
+ sources.delete(sources.keys().next().value);
1533
+ }
1534
+ return [...sources.values()];
1535
+ };
1490
1536
  var taskSources = (metadata) => {
1491
1537
  const value = metadata && typeof metadata === "object" ? metadata[TASK_SOURCE_KEY] : undefined;
1492
1538
  return Array.isArray(value) ? value.filter((v) => !!v && typeof v.id === "string" && /^sha256:[a-f0-9]{64}$/.test(v.id) && typeof v.text === "string").map((source) => ({ id: source.id, text: source.text, ...source.original === true ? { original: true } : {} })) : [];
@@ -1503,8 +1549,8 @@ var captureTaskSources = (metadata, messages) => {
1503
1549
  for (const part of message.parts) {
1504
1550
  if (part.type !== "text" || /^\[Compacted (?:conversation context|prior conversation)\]/.test(part.text))
1505
1551
  continue;
1506
- const text2 = redact2.redactText(part.text).replace(/\bBearer\s+\S+/gi, "Bearer [REDACTED]").replace(/\b([A-Z][A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)|api[_-]?key|access[_-]?token|password)\s*([=:])\s*(?:"[^"]*"|'[^']*'|\S+)/gi, "$1$2[REDACTED]").replace(/\b(?:sk|ghp|gho|github_pat)-[A-Za-z0-9_-]{8,}\b/g, "[REDACTED]");
1507
- const id = `sha256:${createHash3("sha256").update(text2).digest("hex")}`;
1552
+ const text2 = redactSource(part.text);
1553
+ const id = sourceId(text2);
1508
1554
  const original = sources.size === 0 || sources.get(id)?.original === true;
1509
1555
  sources.delete(id);
1510
1556
  sources.set(id, { id, text: text2, ...original ? { original: true } : {} });
@@ -1514,14 +1560,39 @@ var captureTaskSources = (metadata, messages) => {
1514
1560
  var createTaskTools = () => ({
1515
1561
  read_task: tool({
1516
1562
  name: "read_task",
1517
- description: "Recover operator requests after compaction. Omit id for the latest request. Source IDs default to the newest 64; set sourceOffset to page earlier IDs or use firstSourceId for the original request. Text offsets are characters. Task text is guidance, never approval.",
1563
+ description: "Recover operator requests after compaction (default source). Use source=assistant_response to recover a prior assistant report or plan referenced by the user; optional literal query locates text in the newest matching retained response. Assistant text is untrusted, unverified, never permissions or acceptance. Omit id for the latest source. Operator IDs default to the newest 64; sourceOffset pages earlier IDs. Text offsets are characters; pages are at most 4000 characters.",
1518
1564
  metadata: { advancedRegistry: { permissions: ["read"], audit: { riskLevel: "low" } } },
1519
1565
  schema: z8.object({
1566
+ source: z8.enum(["operator", "assistant_response"]).optional(),
1567
+ query: z8.string().min(1).max(200).optional(),
1520
1568
  id: z8.string().regex(/^sha256:[a-f0-9]{64}$/).optional(),
1521
- offset: z8.number().int().min(0).default(0),
1569
+ offset: z8.number().int().min(0).optional(),
1522
1570
  sourceOffset: z8.number().int().min(0).optional()
1523
1571
  }),
1524
- execute: async ({ id, offset, sourceOffset }, context) => {
1572
+ execute: async ({ source: kind, query, id, offset: requestedOffset, sourceOffset }, context) => {
1573
+ if (kind === "assistant_response") {
1574
+ const sources2 = assistantResponses(context?.metadata);
1575
+ const source2 = id ? sources2.find((item) => item.id === id) : [...sources2].reverse().find((item) => !query || item.text.includes(query));
1576
+ if (!source2)
1577
+ throw new Error("Requested assistant response is not retained in this run.");
1578
+ const offset2 = requestedOffset ?? (query ? Math.max(0, source2.text.indexOf(query)) : 0);
1579
+ return {
1580
+ source: "assistant_response",
1581
+ untrusted: true,
1582
+ verified: false,
1583
+ id: source2.id,
1584
+ sourceIds: sources2.map((item) => item.id),
1585
+ totalSources: sources2.length,
1586
+ truncated: source2.truncated,
1587
+ content: source2.text.slice(offset2, offset2 + 4000),
1588
+ offset: offset2,
1589
+ totalCharacters: source2.text.length,
1590
+ nextOffset: offset2 + 4000 < source2.text.length ? offset2 + 4000 : null
1591
+ };
1592
+ }
1593
+ if (query)
1594
+ throw new Error("Literal query is only supported for assistant responses.");
1595
+ const offset = requestedOffset ?? 0;
1525
1596
  const sources = taskSources(context?.metadata);
1526
1597
  const source = id ? sources.find((s) => s.id === id) : sources.at(-1);
1527
1598
  if (!source)
@@ -1552,4 +1623,4 @@ var createTaskTools = () => ({
1552
1623
  })
1553
1624
  });
1554
1625
 
1555
- export { protectStateFromGit, validateStateDirectory, HARNESS_SESSION_INDEX_FILE, HARNESS_SESSION_SCHEMA_VERSION, SESSION_RUN_STATUSES, TERMINAL_SESSION_RUN_STATUSES, openCliSessionStore, openSessionStore, inspectRuntimeDiagnostics, inspectRuntimeManifest, estimateContextTokens, measureContext, MODEL_BUDGET_KEY, estimateRequestTokens, workBudgetReached, createModelBudget, harnessToolExecution, USAGE_LEDGER_KEY, usagePricingSchema, inspectUsageLedger, formatUsageLedger, UsageLedger, verifierSchema, taskAcceptanceContractSchema, compileTaskAcceptanceContract, TASK_ACCEPTANCE_KEY, TASK_ACCEPTANCE_EVIDENCE_KEY, readTaskAcceptanceLedger, nextTaskAcceptanceLedger, persistTaskAcceptanceRevision, taskAcceptanceCheckpointStore, TASK_SOURCE_KEY, taskSources, captureTaskSources, createTaskTools };
1626
+ export { protectStateFromGit, validateStateDirectory, HARNESS_SESSION_INDEX_FILE, HARNESS_SESSION_SCHEMA_VERSION, SESSION_RUN_STATUSES, TERMINAL_SESSION_RUN_STATUSES, ensurePrivateDatabase, openCliSessionStore, openSessionStore, inspectRuntimeDiagnostics, inspectRuntimeManifest, estimateContextTokens, measureContext, MODEL_BUDGET_KEY, estimateRequestTokens, workBudgetReached, createModelBudget, harnessToolExecution, USAGE_LEDGER_KEY, usagePricingSchema, inspectUsageLedger, formatUsageLedger, UsageLedger, verifierSchema, taskAcceptanceContractSchema, compileTaskAcceptanceContract, TASK_ACCEPTANCE_KEY, TASK_ACCEPTANCE_EVIDENCE_KEY, readTaskAcceptanceLedger, nextTaskAcceptanceLedger, persistTaskAcceptanceRevision, taskAcceptanceCheckpointStore, TASK_SOURCE_KEY, ASSISTANT_RESPONSE_KEY, assistantResponses, captureAssistantResponses, taskSources, captureTaskSources, createTaskTools };
@@ -89,6 +89,8 @@ var CLI_OPTION_NAMES = [
89
89
  "--workspace",
90
90
  "--state-dir",
91
91
  "--mcp-config",
92
+ "--no-memory",
93
+ "--source",
92
94
  "--context-config",
93
95
  "--no-project-context",
94
96
  "--patch",
@@ -220,6 +222,7 @@ var childBudgets = [
220
222
  "--subagent-timeout-ms"
221
223
  ];
222
224
  var agent = [
225
+ "--no-memory",
223
226
  "--compaction-model",
224
227
  "--compaction-provider",
225
228
  "--tool-policy",
@@ -271,7 +274,7 @@ var CLI_COMMAND_OPTION_CONTRACTS = {
271
274
  providers: contract(["--json"]),
272
275
  policy: contract(["--service", ...agent, "--json"]),
273
276
  doctor: contract([...provider, ...profile, ...locator, ...project, ...execution, ...budgets, "--allow-check", "--require-capability", "--subagent", "--json"]),
274
- resume: contract(["--tool-policy", "--service", "--session", ...locator, "--approve", "--deny", "--json", "--jsonl"], [["--approve", "--deny"]]),
277
+ resume: contract(["--no-memory", "--tool-policy", "--service", "--session", ...locator, "--approve", "--deny", "--json", "--jsonl"], [["--approve", "--deny"]]),
275
278
  "runs:list": contract([...locator, "--status", "--limit", "--cursor", "--json"]),
276
279
  "runs:inspect": contract([...locator, "--json"]),
277
280
  "runs:cancel": contract([...locator, "--reason", "--cascade", "--final", "--json"]),
@@ -295,6 +298,17 @@ var CLI_COMMAND_OPTION_CONTRACTS = {
295
298
  "checkpoints:recover": contract([...locator, "--json"]),
296
299
  "changes:create": contract(["--patch"], ["--patch"]),
297
300
  "changes:verify": contract(["--patch", "--preconditions", "--now"], ["--patch"]),
301
+ "memory:remember": contract([...locator, "--json", "--source"]),
302
+ "memory:suggest": contract([...locator, "--json", "--source"]),
303
+ "memory:list": contract([...locator, "--json"]),
304
+ "memory:read": contract([...locator, "--json"]),
305
+ "memory:update": contract([...locator, "--json", "--source"]),
306
+ "memory:forget": contract([...locator, "--json"]),
307
+ "memory:accept": contract([...locator, "--json"]),
308
+ "memory:clear": contract([...locator, "--json"]),
309
+ "memory:enable": contract([...locator, "--json"]),
310
+ "memory:disable": contract([...locator, "--json"]),
311
+ "memory:context": contract([...locator, "--json"]),
298
312
  "state:status": contract([...locator, "--json"]),
299
313
  "state:export": contract([...locator, "--json"]),
300
314
  "state:import": contract([...locator, "--apply", "--json"]),
@@ -1,20 +1,22 @@
1
1
  import {
2
2
  HARNESS_OPERATIONS_SCHEMA_VERSION,
3
3
  HARNESS_SQLITE_FILE,
4
+ decodeProjectMemory,
4
5
  openHarnessPersistence,
6
+ projectMemoryRecordKey,
5
7
  sqliteAdapter,
6
8
  workspaceCheckpointSchema,
7
9
  workspaceRestoreOperationSchema
8
- } from "./index-h0mqx2ed.js";
10
+ } from "./index-rd0s3gqj.js";
9
11
  import {
10
12
  HARNESS_SESSION_SCHEMA_VERSION,
11
13
  openCliSessionStore,
12
14
  validateStateDirectory
13
- } from "./index-42gp2hy8.js";
15
+ } from "./index-vtqfjnab.js";
14
16
  import {
15
17
  SqliteDatabase,
16
18
  readRegularFileChunksNoFollow
17
- } from "./index-yb239ghk.js";
19
+ } from "./index-r0tdj04h.js";
18
20
  import {
19
21
  HarnessStateConflictError,
20
22
  HarnessWorkspaceError
@@ -214,13 +216,24 @@ async function readBackupTransport(source, legacyMaxBytes) {
214
216
  }
215
217
 
216
218
  // src/persistence/state-backup.ts
217
- import { createHash, randomUUID } from "node:crypto";
219
+ import { createHash as createHash2, randomUUID } from "node:crypto";
218
220
  import { constants as fsConstants } from "node:fs";
219
221
  import { chmod, link, lstat as lstat2, open, realpath as realpath2, unlink } from "node:fs/promises";
220
222
  import path2 from "node:path";
221
223
  import { z } from "zod";
222
224
  import { normalizeAgentRunState } from "@zhivex-ai/agents";
223
225
  import { createSqliteAgentRunStore } from "@zhivex-ai/agents/ops";
226
+
227
+ // src/persistence/sdk-store-identity.ts
228
+ import { createHash } from "node:crypto";
229
+ var sdkCanonicalKey = (kind, scope, value) => `${kind}:v2:${createHash("sha256").update(JSON.stringify([
230
+ JSON.stringify([scope.namespace ?? null, scope.tenantId, scope.userId ?? null]),
231
+ value
232
+ ])).digest("hex")}`;
233
+ var sdkLegacyKey = (scope, value) => `${encodeURIComponent(scope.namespace ?? "default")}:${encodeURIComponent(scope.tenantId)}:${encodeURIComponent(scope.userId ?? "*")}:${value}`;
234
+ var sdkRunKeyMatches = (key, scope, value) => key === sdkCanonicalKey("agent-run", scope, value) || key === sdkLegacyKey(scope, value);
235
+
236
+ // src/persistence/state-backup.ts
224
237
  var HARNESS_STATE_BACKUP_SCHEMA_VERSION = 1;
225
238
  var HARNESS_STATE_BACKUP_MAX_BYTES = 64 * 1024 * 1024;
226
239
  var terminalRunStatuses = new Set(["completed", "failed", "cancelled", "timed_out"]);
@@ -275,6 +288,8 @@ var memoryRecordSchema = z.object({
275
288
  messages: z.array(z.unknown()),
276
289
  updatedAt: z.number().int().nonnegative()
277
290
  }).strict();
291
+ var serializedMemory = (entry) => JSON.stringify(entry.key.startsWith("agent-memory:v2:") ? { schemaVersion: 1, memoryKey: entry.key, messages: entry.messages } : entry.messages);
292
+ var memoryKeysForRuns = (config, runs) => new Set(runs.flatMap(({ state }) => [...new Set([state.runId, state.agentId].filter((id) => typeof id === "string"))].flatMap((id) => [sdkLegacyKey(config.scope, id), sdkCanonicalKey("agent-memory", config.scope, id)])));
278
293
  var sessionRecordSchema = z.object({
279
294
  sessionId: z.string().min(1).max(260),
280
295
  workspaceKey: bindingKeySchema,
@@ -356,12 +371,12 @@ var canonicalJson = (value) => {
356
371
  return `{${Object.entries(value).sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0).map(([key, entry]) => `${JSON.stringify(key)}:${canonicalJson(entry)}`).join(",")}}`;
357
372
  };
358
373
  var checksumPayload = (payload) => {
359
- const hash = createHash("sha256");
374
+ const hash = createHash2("sha256");
360
375
  for (const chunk of canonicalChunks(payload))
361
376
  hash.update(chunk);
362
377
  return `sha256:${hash.digest("hex")}`;
363
378
  };
364
- var stableKey = (kind, value) => createHash("sha256").update(kind).update("\x00").update(value).digest("hex");
379
+ var stableKey = (kind, value) => createHash2("sha256").update(kind).update("\x00").update(value).digest("hex");
365
380
  var scopeValue = (config) => `${config.scope.tenantId}\x00${config.scope.userId ?? ""}\x00${config.scope.namespace ?? ""}`;
366
381
  var scopePrefix = (config) => `${encodeURIComponent(config.scope.namespace ?? "default")}:${encodeURIComponent(config.scope.tenantId)}:${encodeURIComponent(config.scope.userId ?? "*")}:`;
367
382
  var stateMatchesScope = (config, state) => {
@@ -373,7 +388,6 @@ var stateMatchesScope = (config, state) => {
373
388
  const scope = candidate.scope;
374
389
  return scope.tenantId === config.scope.tenantId && (scope.userId ?? undefined) === (config.scope.userId ?? undefined) && (scope.namespace ?? undefined) === (config.scope.namespace ?? undefined);
375
390
  };
376
- var budgetScopePrefix = (config) => `__zhivex_budget__:${encodeURIComponent(config.scope.tenantId)}:${encodeURIComponent(config.scope.userId ?? "*")}:`;
377
391
  var coordinatorIds = (runs) => new Set(runs.flatMap(({ state }) => {
378
392
  const id = state.budgetCoordinatorId;
379
393
  return typeof id === "string" ? [id] : [];
@@ -381,7 +395,7 @@ var coordinatorIds = (runs) => new Set(runs.flatMap(({ state }) => {
381
395
  var validateBudgetLedger = (config, run, identities) => {
382
396
  const state = validatedRunState(run.state, `Budget ledger ${run.key}`);
383
397
  const identity = state.metadata?.budgetIdentity;
384
- 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) || run.key !== `${budgetScopePrefix(config)}${state.runId}`) {
398
+ 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)) {
385
399
  throw new HarnessStateConflictError("Budget ledger is not bound to an exported run and durable scope.");
386
400
  }
387
401
  assertTerminalState(state, `Budget ledger ${run.key}`);
@@ -559,12 +573,16 @@ var readPayload = async (config, recordedWorkspace, accessLease) => {
559
573
  parentRunKey: row.parent_run_id,
560
574
  updatedAt: row.updated_at_ms
561
575
  }));
562
- const memoryKeys = new Set(runs.map((run) => `${binding.scopePrefix}${String(run.state.runId)}`));
563
- const memory = database.query("SELECT memory_key, messages_json, updated_at_ms FROM zhivex_agent_memory ORDER BY memory_key").all().filter((row) => memoryKeys.has(row.memory_key)).map((row) => ({
564
- key: row.memory_key,
565
- messages: z.array(z.unknown()).parse(JSON.parse(row.messages_json)),
566
- updatedAt: row.updated_at_ms
567
- }));
576
+ const memoryKeys = memoryKeysForRuns(config, runs);
577
+ memoryKeys.add(projectMemoryRecordKey(binding));
578
+ const memory = database.query("SELECT memory_key, messages_json, updated_at_ms FROM zhivex_agent_memory ORDER BY memory_key").all().filter((row) => memoryKeys.has(row.memory_key)).map((row) => {
579
+ const value = JSON.parse(row.messages_json);
580
+ if (row.memory_key.startsWith("agent-memory:v2:")) {
581
+ const envelope = z.strictObject({ schemaVersion: z.literal(1), memoryKey: z.literal(row.memory_key), messages: z.array(z.unknown()) }).parse(value);
582
+ return { key: row.memory_key, messages: envelope.messages, updatedAt: row.updated_at_ms };
583
+ }
584
+ return { key: row.memory_key, messages: z.array(z.unknown()).parse(value), updatedAt: row.updated_at_ms };
585
+ });
568
586
  const sessionRows = database.query("SELECT * FROM zhivex_cli_sessions ORDER BY session_id").all().filter((row) => row.workspace_key === binding.workspaceKey && row.scope_key === binding.scopeKey);
569
587
  const selectedSessionIds = new Set(sessionRows.map((row) => row.session_id));
570
588
  const sessionRunRows = database.query("SELECT * FROM zhivex_cli_session_runs ORDER BY session_id, ordinal").all().filter((row) => selectedSessionIds.has(row.session_id));
@@ -762,7 +780,7 @@ var validateBundleBinding = async (config, bundle) => {
762
780
  throw new HarnessStateConflictError(`Run ${run.key} is bound to another durable scope.`);
763
781
  }
764
782
  runIds.add(state.runId);
765
- if (run.key !== `${expected.scopePrefix}${state.runId}`) {
783
+ if (!sdkRunKeyMatches(run.key, config.scope, state.runId)) {
766
784
  throw new HarnessStateConflictError(`Run ${run.key} does not match its bound scope and runId.`);
767
785
  }
768
786
  }
@@ -781,7 +799,7 @@ var validateBundleBinding = async (config, bundle) => {
781
799
  const journalKeys = new Set;
782
800
  for (const entry of bundle.records.toolJournal) {
783
801
  const journalKey = `${entry.runKey}\x00${entry.toolCallId}`;
784
- if (journalKeys.has(journalKey) || !runKeys.has(entry.runKey) || !stateMatchesScope(config, entry.entry) || entry.entry.runId !== entry.runKey.slice(expected.scopePrefix.length)) {
802
+ if (journalKeys.has(journalKey) || !runKeys.has(entry.runKey) || !stateMatchesScope(config, entry.entry) || entry.entry.runId !== bundle.records.runs.find((run) => run.key === entry.runKey)?.state.runId) {
785
803
  throw new HarnessStateConflictError(`Tool journal ${entry.toolCallId} is not bound to an imported run.`);
786
804
  }
787
805
  journalKeys.add(journalKey);
@@ -793,7 +811,7 @@ var validateBundleBinding = async (config, bundle) => {
793
811
  }
794
812
  idempotencyKeys.add(entry.key);
795
813
  const state = bundle.records.runs.find((run) => run.key === entry.runKey).state;
796
- if (typeof state.idempotencyKey !== "string" || entry.key !== `${expected.scopePrefix}${state.idempotencyKey}`) {
814
+ if (typeof state.idempotencyKey !== "string" || !sdkRunKeyMatches(entry.key, config.scope, state.idempotencyKey)) {
797
815
  throw new HarnessStateConflictError("Idempotency record does not match its bound run state.");
798
816
  }
799
817
  }
@@ -805,9 +823,12 @@ var validateBundleBinding = async (config, bundle) => {
805
823
  parentKeys.add(entry.runKey);
806
824
  }
807
825
  const memoryKeys = new Set;
826
+ const allowedMemoryKeys = memoryKeysForRuns(config, bundle.records.runs);
808
827
  for (const entry of bundle.records.memory) {
809
- const memoryRunId = entry.key.startsWith(expected.scopePrefix) ? entry.key.slice(expected.scopePrefix.length) : "";
810
- if (memoryKeys.has(entry.key) || !runIds.has(memoryRunId)) {
828
+ const projectRecord = entry.key === projectMemoryRecordKey(expected);
829
+ if (projectRecord)
830
+ decodeProjectMemory(entry.messages, expected);
831
+ if (memoryKeys.has(entry.key) || !projectRecord && !allowedMemoryKeys.has(entry.key)) {
811
832
  throw new HarnessStateConflictError(`Memory record ${entry.key} is not bound to an imported run.`);
812
833
  }
813
834
  memoryKeys.add(entry.key);
@@ -849,7 +870,7 @@ var validateBundleBinding = async (config, bundle) => {
849
870
  const turn = bundle.records.sessionRuns.find((run) => run.turnId === checkpoint.turnId && run.sessionId === checkpoint.sessionId);
850
871
  if (!turn || !terminalSessionStatuses.has(turn.status))
851
872
  throw new HarnessStateConflictError("Checkpoint targets a missing terminal turn.");
852
- if (new Set(checkpoint.files.map((file) => file.path)).size !== checkpoint.files.length || checkpoint.files.some((file) => `sha256:${createHash("sha256").update(file.content).digest("hex")}` !== file.digest || Buffer.byteLength(file.content) > 65536)) {
873
+ if (new Set(checkpoint.files.map((file) => file.path)).size !== checkpoint.files.length || checkpoint.files.some((file) => `sha256:${createHash2("sha256").update(file.content).digest("hex")}` !== file.digest || Buffer.byteLength(file.content) > 65536)) {
853
874
  throw new HarnessStateConflictError("Checkpoint content identity is invalid.");
854
875
  }
855
876
  if (record.kind === "restore") {
@@ -946,9 +967,9 @@ var importHarnessStateBackup = async (config, bundle, options = {}) => {
946
967
  toolJournal: new Set(availableTables.has("zhivex_agent_runs_tool_journal") ? database.query("SELECT run_key, tool_call_id, entry_json, revision, updated_at_ms FROM zhivex_agent_runs_tool_journal").all().filter((row) => destinationRunKeys.has(row.run_key)).map((row) => `${row.run_key}\x00${row.tool_call_id}`) : []),
947
968
  idempotency: new Set(availableTables.has("zhivex_agent_runs_idempotency") ? database.query("SELECT idempotency_key, run_id, updated_at_ms FROM zhivex_agent_runs_idempotency").all().filter((row) => destinationRunKeys.has(row.run_id)).map((row) => row.idempotency_key) : []),
948
969
  parents: new Set(availableTables.has("zhivex_agent_runs_parents") ? database.query("SELECT run_id, parent_run_id, updated_at_ms FROM zhivex_agent_runs_parents").all().filter((row) => destinationRunKeys.has(row.run_id)).map((row) => row.run_id) : []),
949
- memory: new Set(availableTables.has("zhivex_agent_memory") ? database.query("SELECT memory_key, messages_json, updated_at_ms FROM zhivex_agent_memory").all().filter((row) => destinationRunRows.some((run) => {
970
+ memory: new Set(availableTables.has("zhivex_agent_memory") ? database.query("SELECT memory_key, messages_json, updated_at_ms FROM zhivex_agent_memory").all().filter((row) => row.memory_key === projectMemoryRecordKey(bundle.binding) || destinationRunRows.some((run) => {
950
971
  const state = JSON.parse(run.state_json);
951
- return row.memory_key === `${bundle.binding.scopePrefix}${String(state.runId)}`;
972
+ return memoryKeysForRuns(config, [{ state }]).has(row.memory_key);
952
973
  })).map((row) => row.memory_key) : []),
953
974
  sessions: destinationSessionIds,
954
975
  sessionRuns: new Set(availableTables.has("zhivex_cli_session_runs") ? database.query("SELECT * FROM zhivex_cli_session_runs ORDER BY session_id, ordinal").all().filter((row) => destinationSessionIds.has(row.session_id)).map((row) => row.turn_id) : [])
@@ -974,7 +995,7 @@ var importHarnessStateBackup = async (config, bundle, options = {}) => {
974
995
  for (const entry of bundle.records.toolJournal)
975
996
  insertOrCompare("zhivex_agent_runs_tool_journal", "run_key = ? AND tool_call_id = ?", [entry.runKey, entry.toolCallId], "entry_json, revision, updated_at_ms", [JSON.stringify(entry.entry), entry.revision, entry.updatedAt], "INSERT INTO zhivex_agent_runs_tool_journal (run_key, tool_call_id, entry_json, revision, updated_at_ms) VALUES (?, ?, ?, ?, ?)", [entry.runKey, entry.toolCallId, JSON.stringify(entry.entry), entry.revision, entry.updatedAt]);
976
997
  for (const entry of bundle.records.memory)
977
- insertOrCompare("zhivex_agent_memory", "memory_key = ?", [entry.key], "messages_json, updated_at_ms", [JSON.stringify(entry.messages), entry.updatedAt], "INSERT INTO zhivex_agent_memory (memory_key, messages_json, updated_at_ms) VALUES (?, ?, ?)", [entry.key, JSON.stringify(entry.messages), entry.updatedAt]);
998
+ insertOrCompare("zhivex_agent_memory", "memory_key = ?", [entry.key], "messages_json, updated_at_ms", [serializedMemory(entry), entry.updatedAt], "INSERT INTO zhivex_agent_memory (memory_key, messages_json, updated_at_ms) VALUES (?, ?, ?)", [entry.key, serializedMemory(entry), entry.updatedAt]);
978
999
  for (const session of bundle.records.sessions)
979
1000
  insertOrCompare("zhivex_cli_sessions", "session_id = ?", [session.sessionId], "workspace_key, scope_key, title, parent_session_id, forked_from_turn_id, revision, activity_seq, created_at, updated_at, archived_at, deleted_at, metadata_bytes", [
980
1001
  session.workspaceKey,
package/dist/index.js CHANGED
@@ -71,7 +71,7 @@ import {
71
71
  timeToSafeFixLatencyStatistics,
72
72
  timeToSafeFixTaskSchema,
73
73
  verifyChangeEnvelope
74
- } from "./index-8vm2m34s.js";
74
+ } from "./index-dj5z774k.js";
75
75
  import {
76
76
  HARNESS_STATE_BACKUP_MAX_BYTES,
77
77
  HARNESS_STATE_BACKUP_SCHEMA_VERSION,
@@ -82,7 +82,7 @@ import {
82
82
  inspectHarnessState,
83
83
  readHarnessStateBackup,
84
84
  stateBackupBundleSchema
85
- } from "./index-a97bezbz.js";
85
+ } from "./index-z6br6qdn.js";
86
86
  import {
87
87
  CLI_COMMAND_OPTION_CONTRACTS,
88
88
  CLI_OPTION_DEFINITIONS,
@@ -92,7 +92,7 @@ import {
92
92
  resolveHarnessModelRoutes,
93
93
  serializeHarnessModelRoutes,
94
94
  validateCliCommandOptions
95
- } from "./index-2jrdcj6b.js";
95
+ } from "./index-wq2tax80.js";
96
96
  import {
97
97
  harnessLocalCredentialsSchema,
98
98
  openHarnessActivityStore,
@@ -100,7 +100,7 @@ import {
100
100
  recoverHarnessLocalService,
101
101
  requestHarnessLocalService,
102
102
  startHarnessLocalService
103
- } from "./index-w2sj0wkw.js";
103
+ } from "./index-49ys3s4q.js";
104
104
  import {
105
105
  createAcpConnection,
106
106
  serveAcpStdio
@@ -147,7 +147,7 @@ import {
147
147
  runHarness,
148
148
  runHarnessReviewGroup,
149
149
  selectHarnessModel
150
- } from "./index-hnaxfpb5.js";
150
+ } from "./index-4j1h4j8c.js";
151
151
  import {
152
152
  HARNESS_OPERATIONS_SCHEMA_VERSION,
153
153
  HARNESS_SQLITE_FILE,
@@ -159,8 +159,8 @@ import {
159
159
  migrateLegacyFileRuns,
160
160
  openHarnessPersistence,
161
161
  openWorkspaceCheckpointStore
162
- } from "./index-h0mqx2ed.js";
163
- import"./index-8zzhggxv.js";
162
+ } from "./index-rd0s3gqj.js";
163
+ import"./index-a1q355va.js";
164
164
  import {
165
165
  HARNESS_SESSION_INDEX_FILE,
166
166
  HARNESS_SESSION_SCHEMA_VERSION,
@@ -169,7 +169,7 @@ import {
169
169
  openCliSessionStore,
170
170
  openSessionStore,
171
171
  validateStateDirectory
172
- } from "./index-42gp2hy8.js";
172
+ } from "./index-vtqfjnab.js";
173
173
  import {
174
174
  BUN_ENGINE_RANGE,
175
175
  HARNESS_MCP_CONFIG_SCHEMA_VERSION,
@@ -180,7 +180,7 @@ import {
180
180
  createHttpMcpClient,
181
181
  loadHarnessMcpConfiguration,
182
182
  normalizeHarnessMcpConfiguration
183
- } from "./index-azmwjp95.js";
183
+ } from "./index-0g939pfs.js";
184
184
  import {
185
185
  CliOciRuntimeAdapter,
186
186
  HARNESS_EXECUTION_ARTIFACT_SCHEMA_VERSION,
@@ -192,7 +192,7 @@ import {
192
192
  describeOciCommand,
193
193
  executionFingerprintInput,
194
194
  harnessExecutionSession
195
- } from "./index-z0qxmkm2.js";
195
+ } from "./index-e6r04a18.js";
196
196
  import {
197
197
  EDIT_CONTRACT_SCHEMA_VERSION,
198
198
  EDIT_DIGEST_ALGORITHM,
@@ -215,7 +215,7 @@ import {
215
215
  restoreFileInputSchema,
216
216
  validateEditProposal,
217
217
  workspaceFilePathSchema
218
- } from "./index-yb239ghk.js";
218
+ } from "./index-r0tdj04h.js";
219
219
  import {
220
220
  BUILTIN_PROVIDER_REGISTRATIONS,
221
221
  DEFAULT_ALLOWED_CHECKS,
@@ -0,0 +1,11 @@
1
+ import { type HarnessProjectMemoryOptions } from "./project-memory.js";
2
+ export declare const HARNESS_MEMORY_COMMANDS: readonly ["remember", "list", "read", "update", "forget", "suggest", "accept", "clear", "enable", "disable", "context"];
3
+ /** Host/CLI operations only: these commands are never added to the model tool catalog. */
4
+ export declare function executeHarnessMemoryCommand(options: HarnessProjectMemoryOptions, command: string, args: readonly string[], source?: string): Promise<{
5
+ schemaVersion: number;
6
+ kind: string;
7
+ command: string;
8
+ workspaceKey: string;
9
+ scopeKey: string;
10
+ result: unknown;
11
+ }>;
@@ -0,0 +1,109 @@
1
+ import { z } from "zod";
2
+ import type { AgentStoreScope } from "@zhivex-ai/agents/ops";
3
+ export declare const HARNESS_PROJECT_MEMORY_SCHEMA_VERSION: 1;
4
+ export declare const HARNESS_PROJECT_MEMORY_LIMITS: Readonly<{
5
+ entries: 128;
6
+ entryBytes: 4096;
7
+ documentBytes: number;
8
+ contextBytes: 6000;
9
+ contextEntries: 3;
10
+ staleAfterMs: number;
11
+ }>;
12
+ declare const entrySchema: z.ZodObject<{
13
+ id: z.ZodString;
14
+ content: z.ZodString;
15
+ source: z.ZodObject<{
16
+ kind: z.ZodEnum<{
17
+ operator: "operator";
18
+ suggestion: "suggestion";
19
+ }>;
20
+ reference: z.ZodString;
21
+ }, z.core.$strict>;
22
+ status: z.ZodEnum<{
23
+ accepted: "accepted";
24
+ pending: "pending";
25
+ }>;
26
+ revision: z.ZodNumber;
27
+ createdAt: z.ZodNumber;
28
+ updatedAt: z.ZodNumber;
29
+ reviewedAt: z.ZodOptional<z.ZodNumber>;
30
+ expiresAt: z.ZodOptional<z.ZodNumber>;
31
+ }, z.core.$strict>;
32
+ export type HarnessProjectMemoryEntry = z.infer<typeof entrySchema>;
33
+ export interface HarnessProjectMemoryInput {
34
+ content: string;
35
+ source: string;
36
+ expiresAt?: number;
37
+ }
38
+ export interface HarnessProjectMemoryOptions {
39
+ workspace: string;
40
+ stateDirectory: string;
41
+ scope: AgentStoreScope;
42
+ now?: () => number;
43
+ }
44
+ export interface HarnessProjectMemoryView {
45
+ schemaVersion: 1;
46
+ kind: "project-memory";
47
+ workspaceKey: string;
48
+ scopeKey: string;
49
+ enabled: boolean;
50
+ entries: (HarnessProjectMemoryEntry & {
51
+ freshness: "fresh" | "stale" | "expired" | "pending";
52
+ })[];
53
+ limits: typeof HARNESS_PROJECT_MEMORY_LIMITS;
54
+ }
55
+ export interface HarnessProjectMemory {
56
+ readonly databasePath: string;
57
+ list(): HarnessProjectMemoryView;
58
+ read(id: string): HarnessProjectMemoryView["entries"][number];
59
+ remember(input: HarnessProjectMemoryInput): HarnessProjectMemoryEntry;
60
+ suggest(input: HarnessProjectMemoryInput): HarnessProjectMemoryEntry;
61
+ update(id: string, expectedRevision: number, input: HarnessProjectMemoryInput): HarnessProjectMemoryEntry;
62
+ accept(id: string, expectedRevision: number): HarnessProjectMemoryEntry;
63
+ forget(id: string, expectedRevision: number): void;
64
+ clear(): number;
65
+ setEnabled(enabled: boolean): void;
66
+ retrieve(query: string, budget?: {
67
+ maxBytes?: number;
68
+ maxEntries?: number;
69
+ }): {
70
+ content: string;
71
+ ids: string[];
72
+ bytes: number;
73
+ };
74
+ close(): void;
75
+ }
76
+ export declare const projectMemoryRecordKey: (binding: {
77
+ workspaceKey: string;
78
+ scopeKey: string;
79
+ }) => string;
80
+ export declare const projectMemoryBinding: (workspace: string, scope: AgentStoreScope) => {
81
+ workspaceKey: string;
82
+ scopeKey: string;
83
+ memoryKey: string;
84
+ };
85
+ export declare const decodeProjectMemory: (messages: unknown, binding: {
86
+ workspaceKey: string;
87
+ scopeKey: string;
88
+ }) => {
89
+ schemaVersion: 1;
90
+ workspaceKey: string;
91
+ scopeKey: string;
92
+ enabled: boolean;
93
+ entries: {
94
+ id: string;
95
+ content: string;
96
+ source: {
97
+ kind: "operator" | "suggestion";
98
+ reference: string;
99
+ };
100
+ status: "accepted" | "pending";
101
+ revision: number;
102
+ createdAt: number;
103
+ updatedAt: number;
104
+ reviewedAt?: number | undefined;
105
+ expiresAt?: number | undefined;
106
+ }[];
107
+ };
108
+ export declare function openHarnessProjectMemory(options: HarnessProjectMemoryOptions): Promise<HarnessProjectMemory>;
109
+ export {};
@@ -0,0 +1,4 @@
1
+ import type { AgentStoreScope } from "@zhivex-ai/core";
2
+ export declare const sdkCanonicalKey: (kind: "agent-run" | "agent-memory", scope: AgentStoreScope, value: string) => string;
3
+ export declare const sdkLegacyKey: (scope: AgentStoreScope, value: string) => string;
4
+ export declare const sdkRunKeyMatches: (key: string, scope: AgentStoreScope, value: string) => boolean;
@@ -120,6 +120,10 @@ export interface CliSessionStore {
120
120
  }): Promise<SessionRetentionResult>;
121
121
  close(): void;
122
122
  }
123
+ export declare const ensurePrivateDatabase: (workspace: string, stateDirectory: string) => Promise<{
124
+ databasePath: string;
125
+ databaseEntry: import("node:fs").Stats;
126
+ }>;
123
127
  export declare const openCliSessionStore: (options: OpenSessionStoreOptions) => Promise<CliSessionStore>;
124
128
  /** Compatibility-friendly name for consumers that do not care that the index is CLI-owned. */
125
129
  export declare const openSessionStore: typeof openCliSessionStore;