@sema-agent/core 5.39.0 → 5.40.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 (32) hide show
  1. package/CHANGELOG.md +99 -11
  2. package/dist/core/checkpoint-store.d.ts +12 -0
  3. package/dist/core/governance-codes.js +2 -0
  4. package/dist/core/hooks.js +9 -1
  5. package/dist/core/memory-engine/engine.d.ts +27 -0
  6. package/dist/core/memory-engine/engine.js +103 -1
  7. package/dist/core/memory-engine/export-bundle.d.ts +192 -0
  8. package/dist/core/memory-engine/export-bundle.js +306 -0
  9. package/dist/core/memory-engine/file-backend.d.ts +178 -1
  10. package/dist/core/memory-engine/file-backend.js +637 -6
  11. package/dist/core/memory-engine/index.d.ts +2 -1
  12. package/dist/core/memory-engine/index.js +1 -0
  13. package/dist/core/memory-engine/layout.d.ts +89 -1
  14. package/dist/core/memory-engine/layout.js +131 -1
  15. package/dist/core/memory-engine/memory-backend-contract.d.ts +1 -1
  16. package/dist/core/memory-engine/memory-backend-contract.js +52 -0
  17. package/dist/core/memory-engine/tools.js +8 -1
  18. package/dist/core/permission-rule-consent.js +14 -2
  19. package/dist/core/runner/prepare-task.js +13 -0
  20. package/dist/core/runner/runtask.js +5 -0
  21. package/dist/core/runner/synthetic-tools.js +3 -1
  22. package/dist/core/runner/tool-disclosure.js +2 -1
  23. package/dist/core/write-protect.d.ts +0 -20
  24. package/dist/core/write-protect.js +4 -3
  25. package/dist/index.d.ts +1 -1
  26. package/dist/index.js +1 -1
  27. package/dist/tools/fs/safety.d.ts +1 -1
  28. package/dist/tools/fs/safety.js +1 -1
  29. package/dist/tools/fs/search.d.ts +33 -0
  30. package/dist/tools/fs/search.js +72 -0
  31. package/package.json +1 -1
  32. package/test/export-surface.snapshot.json +9 -1
@@ -1,7 +1,8 @@
1
1
  export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, renderAnnouncements, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, } from "./engine.js";
2
2
  export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, } from "./tools.js";
3
3
  export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
4
- export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, type ScannedEntryFile, type TransferEvidence, type CommittedBinding, type CommittedEntrySnapshot, type CommittedScopeSnapshots, type EntryCustodyReport, erasureSelectHash, type EraseMemoryEntriesInput, type ErasureSelect, type ErasedBinding, type MemoryErasureAttestation, } from "./file-backend.js";
4
+ export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, type ScannedEntryFile, type TransferEvidence, type CommittedBinding, type CommittedEntrySnapshot, type CommittedScopeSnapshots, type EntryCustodyReport, erasureSelectHash, type EraseMemoryEntriesInput, type ErasureSelect, type ErasedBinding, type MemoryErasureAttestation, type MemoryExportSnapshot, } from "./file-backend.js";
5
+ export { computeBundleSectionHashes, computeMemoryBundleHash, memoryBundleInvalid, type MemoryExportBundle, type MemoryImportReport, type MemoryBundleImportPlan, type BundleChallengeRow, type BundleLineageRow, type BundlePollutedSession, } from "./export-bundle.js";
5
6
  export { ControlPlaneCorruptError, deriveControlPlaneDir, deriveRepoControlPlaneDir, deriveRepoKey, deriveRepoMemoryDir, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, resolveMemoryEngineRoot, scopeDirFor, scopeDirName, claimRootScope, rootScopeOf, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAppendResult, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, } from "./layout.js";
6
7
  export { readV2HeaderHints, type V2HeaderHints } from "./header-hints.js";
7
8
  export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, type ParsedEntryFile } from "./frontmatter.js";
@@ -2,6 +2,7 @@ export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION
2
2
  export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, } from "./tools.js";
3
3
  export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
4
4
  export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, erasureSelectHash, } from "./file-backend.js";
5
+ export { computeBundleSectionHashes, computeMemoryBundleHash, memoryBundleInvalid, } from "./export-bundle.js";
5
6
  export { ControlPlaneCorruptError, deriveControlPlaneDir, deriveRepoControlPlaneDir, deriveRepoKey, deriveRepoMemoryDir, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, resolveMemoryEngineRoot, scopeDirFor, scopeDirName, claimRootScope, rootScopeOf, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, } from "./layout.js";
6
7
  export { readV2HeaderHints } from "./header-hints.js";
7
8
  export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile } from "./frontmatter.js";
@@ -284,6 +284,25 @@ export declare function markSessionPolluted(controlDir: string, sessionId: strin
284
284
  * record stands in. Only ABSENCE is clean, so a control plane that cannot be stat'ed at all reads
285
285
  * clean for every session rather than disabling memory deployment-wide. */
286
286
  export declare function readSessionPollution(controlDir: string, sessionId: string): SessionPollutionRecord | undefined;
287
+ /**
288
+ * design/178 v2-c §3-3 — enumerate EVERY durable pollution marker (the export snapshot's pollution
289
+ * face; `readSessionPollution` stays the per-session read). Same fail-closed record posture as the
290
+ * per-session read: a marker file that exists but cannot be read or parsed answers a synthesized
291
+ * record, never a silent omission (corruption must not launder the state out of an export). An
292
+ * absent marker directory is the genuine empty state (ENOENT-only); any OTHER enumeration failure
293
+ * THROWS — a listing that silently dropped markers would export a package that launders pollution.
294
+ */
295
+ export declare function listSessionPollution(controlDir: string): Record<string, SessionPollutionRecord>;
296
+ /**
297
+ * design/178 v2-c §5.3 — the STRICT durable pollution import. Deliberately NOT
298
+ * `markSessionPolluted`: that leg is best-effort-with-disclosure and mints its own `at`, so a
299
+ * bundle's source record (the governance fact being carried) could neither land verbatim nor prove
300
+ * durability. This leg writes the SOURCE record verbatim (`wx`); on EEXIST it reads the standing
301
+ * record back and COMPARES — equal = converged no-op, different = the destination's first-mark-wins
302
+ * record stands and the divergence is REPORTED (`"divergent"`), never silently absorbed. Any other
303
+ * write failure THROWS (the import aborts with its latch standing — §1-2⑤).
304
+ */
305
+ export declare function importSessionPollution(controlDir: string, sessionId: string, record: SessionPollutionRecord): "written" | "already" | "divergent";
287
306
  /** The retrieved-account sidecar (control plane): entry id → { count, lastAt }. */
288
307
  export declare const USAGE_RETRIEVED_FILE = "usage-retrieved.json";
289
308
  /** Bound on tracked ids — over the cap, the entries with the OLDEST `lastAt` are evicted first
@@ -320,10 +339,15 @@ export interface LineageContribution {
320
339
  lastRev: string;
321
340
  lastAt: number;
322
341
  }
323
- /** One staged (pre-commit) row: an entry this transaction WOULD commit. */
342
+ /** One staged (pre-commit) row: an entry this transaction WOULD commit. `kind: "latch-only"` marks
343
+ * a bundle-import SYNTHETIC latch row (design/178 v2-c §1): it withholds the id on the model-visible
344
+ * read faces exactly like a real staged row, but it is NOT a contribution — the erasure lane's
345
+ * capture-and-clear strikes it without crediting its txn's sessionId, and the host adjudication
346
+ * refuses to touch its transaction (the import protocol's receipt-gated release is the only exit). */
324
347
  export interface LineagePendingRow {
325
348
  entryId: string;
326
349
  rev: string;
350
+ kind?: "latch-only";
327
351
  }
328
352
  /** One staged transaction: rows land BEFORE `applyPatches`; the commit credential lands after it
329
353
  * succeeds; promotion consumes both. A pending txn without a credential is the crash window —
@@ -338,6 +362,12 @@ export interface LineagePendingTxn {
338
362
  appliedIds: string[];
339
363
  at: number;
340
364
  };
365
+ /** design/178 v2-c §1 — present ⇔ this txn is a bundle-import SYNTHETIC LATCH: it never promotes
366
+ * (no credential is ever written), the generic adjudication REFUSES both arms on it, and the only
367
+ * release channel is {@link releaseImportLatch} (durable completion-receipt precondition). An
368
+ * older binary reads it as an ordinary uncredentialed pending txn and reports it undecidable —
369
+ * the accepted mixed-version posture (the drain precondition of the ledger family applies). */
370
+ kind?: "latch-only";
341
371
  }
342
372
  /** Stage this transaction's would-commit rows BEFORE `applyPatches` (write-ahead). Throws on any
343
373
  * write/lock/corruption failure — the caller refuses the whole harvest (fail-closed). */
@@ -373,8 +403,39 @@ export declare function reconcileLineage(controlDir: string, now: () => number):
373
403
  txnId: string;
374
404
  sessionId: string;
375
405
  entryIds: string[];
406
+ kind?: "latch-only";
376
407
  }>;
377
408
  };
409
+ /** design/178 v2-c §1 — the import COMPLETION-RECEIPT directory (control plane): one `wx`-minted
410
+ * JSON per bundle hash, written only after the import's governance legs all landed. Its presence
411
+ * is {@link releaseImportLatch}'s hard precondition and the idempotent re-import's short-circuit
412
+ * credential. */
413
+ export declare const IMPORT_RECEIPTS_DIR = "import-receipts";
414
+ /** The lineage txn id of a bundle import's synthetic latch (ONE latch per bundle — a re-import of
415
+ * the same bundle re-stages the same txn id, which is what makes the latch idempotent). */
416
+ export declare function importLatchTxnId(bundleHash: string): string;
417
+ /**
418
+ * design/178 v2-c §1-1 — stage the bundle import's SYNTHETIC LATCH: one pending lineage txn
419
+ * (txnId = {@link importLatchTxnId}) whose rows name every bundle entry id, every row and the txn
420
+ * itself carrying the structural `kind: "latch-only"` seat. From this write until
421
+ * {@link releaseImportLatch}, those ids are withheld on every model-visible read face (the same
422
+ * dirty-latch consumption real staged rows get) — a crash anywhere inside the import leaves the
423
+ * withhold standing (durable pending row), and re-staging the same bundle is an idempotent
424
+ * overwrite with identical content. Never credentialed, never promoted.
425
+ */
426
+ export declare function stageImportLatch(controlDir: string, bundleHash: string, rows: ReadonlyArray<{
427
+ entryId: string;
428
+ rev: string;
429
+ }>, now: () => number): string;
430
+ /**
431
+ * design/178 v2-c §1-2⑥ — the ONE latch release channel. Hard precondition: the durable completion
432
+ * receipt for the bundle is IN PLACE (`import-receipts/<bundleHash>.json`, minted after the
433
+ * governance legs all landed) — absent receipt ⇒ loud refusal, the latch stands, and convergence is
434
+ * re-running the same bundle import. Receipt-presence is ENOENT-only-absent (a probe failure never
435
+ * reads as "no receipt" — fail-closed). Idempotent: an already-released (absent) txn is a no-op;
436
+ * a NON-latch txn under the id is refused (this is never a generic discard).
437
+ */
438
+ export declare function releaseImportLatch(controlDir: string, txnId: string): void;
378
439
  /** The DIRTY-LATCH set: every entry id named by ANY pending row (credentialed or not). While an id
379
440
  * is latched, its model-visible read faces (index mechanical rows / memory_search / memory_get)
380
441
  * refuse — "the account is not settled, the content does not go on the table". Throws on a
@@ -429,6 +490,33 @@ export declare function clearLineageForEntries(controlDir: string, entryIds: rea
429
490
  * carries the chain's pre-captured set (honest downgrade, never a fabricated rebuild).
430
491
  */
431
492
  export declare function captureAndClearLineageForEntries(controlDir: string, entryIds: readonly string[]): Map<string, string[]>;
493
+ /** One (entryId, sessionId) pair whose imported value LOST to the destination's standing row —
494
+ * design/178 v2-c §5.1's disclosure seat (kept is always "destination": the in-account fact is
495
+ * never overwritten by an equal-time divergent import, only reported). */
496
+ export interface LineageImportDivergence {
497
+ entryId: string;
498
+ sessionId: string;
499
+ kept: "destination";
500
+ }
501
+ /**
502
+ * design/178 v2-c §5.1 — the lineage FIDELITY importer: write bundle lineage rows into the
503
+ * committed set VERBATIM (`lastRev`/`lastAt` carried byte-for-byte — the promote leg mints its own
504
+ * `now` and is therefore unusable for carriage). Deterministic same-key merge, idempotent under
505
+ * re-import of the same bundle:
506
+ * - destination has no (entryId, sessionId) row ⇒ the source row lands verbatim;
507
+ * - both present, source `lastAt` is GREATER ⇒ source wins ("last contribution" is a time-axis
508
+ * fact); destination greater ⇒ destination stands (no divergence — the merge is deterministic);
509
+ * - equal `lastAt`, equal `lastRev` ⇒ no-op; equal `lastAt`, DIFFERENT `lastRev` ⇒ the
510
+ * destination row stands and the pair is reported (first-mark-wins side: an in-account fact is
511
+ * never rewritten by an外来 package; the disagreement must not be silent).
512
+ * Throws on a corrupt ledger (fail-closed family).
513
+ */
514
+ export declare function importLineageCommitted(controlDir: string, rows: ReadonlyArray<{
515
+ entryId: string;
516
+ sessionId: string;
517
+ lastRev: string;
518
+ lastAt: number;
519
+ }>): LineageImportDivergence[];
432
520
  /** Full-ledger read (tests / host observability). Throws on corruption. */
433
521
  export declare function readLineageRecord(controlDir: string): {
434
522
  committed: Record<string, Record<string, LineageContribution>>;
@@ -766,6 +766,48 @@ export function readSessionPollution(controlDir, sessionId) {
766
766
  }
767
767
  return { at: 0, reason: "pollution marker present but unreadable (kept fail-closed)" };
768
768
  }
769
+ export function listSessionPollution(controlDir) {
770
+ const dir = join(controlDir, SESSION_POLLUTION_DIR);
771
+ let names;
772
+ try {
773
+ names = readdirSync(dir);
774
+ }
775
+ catch (err) {
776
+ if (err.code === "ENOENT")
777
+ return {};
778
+ throw new ControlPlaneCorruptError(`session pollution markers could not be enumerated (${err.code ?? "io error"}) at ${dir} — an enumeration failure is not an empty set (fail-closed)`, { cause: err });
779
+ }
780
+ const out = Object.create(null);
781
+ for (const name of names.sort()) {
782
+ if (!name.endsWith(".json"))
783
+ continue;
784
+ let sessionId;
785
+ try {
786
+ sessionId = decodeURIComponent(name.slice(0, -".json".length));
787
+ }
788
+ catch {
789
+ continue;
790
+ }
791
+ const rec = readSessionPollution(controlDir, sessionId);
792
+ if (rec !== undefined)
793
+ out[sessionId] = rec;
794
+ }
795
+ return out;
796
+ }
797
+ export function importSessionPollution(controlDir, sessionId, record) {
798
+ const path = pollutionPath(controlDir, sessionId);
799
+ ensureDirExists(dirname(path));
800
+ try {
801
+ writeFileSync(path, `${JSON.stringify({ at: record.at, reason: record.reason }, null, 2)}\n`, { encoding: "utf8", flag: "wx" });
802
+ return "written";
803
+ }
804
+ catch (err) {
805
+ if (!(err instanceof Error && "code" in err && err.code === "EEXIST"))
806
+ throw err;
807
+ }
808
+ const standing = readSessionPollution(controlDir, sessionId);
809
+ return standing !== undefined && standing.at === record.at && standing.reason === record.reason ? "already" : "divergent";
810
+ }
769
811
  export const USAGE_RETRIEVED_FILE = "usage-retrieved.json";
770
812
  export const USAGE_RETRIEVED_MAX_IDS = 4096;
771
813
  function coerceRetrievedAccount(raw) {
@@ -932,10 +974,14 @@ function coerceLineage(raw) {
932
974
  if (!txn || typeof txn !== "object" || typeof txn.sessionId !== "string" || typeof txn.at !== "number" || !Array.isArray(txn.rows)) {
933
975
  throw badShape(`pending[${JSON.stringify(txnId)}]`);
934
976
  }
977
+ if (txn.kind !== undefined && txn.kind !== "latch-only")
978
+ throw badShape(`pending[${JSON.stringify(txnId)}] kind`);
935
979
  for (const row of txn.rows) {
936
980
  const p = row;
937
981
  if (!p || typeof p !== "object" || typeof p.entryId !== "string" || typeof p.rev !== "string")
938
982
  throw badShape(`pending[${JSON.stringify(txnId)}] row`);
983
+ if (p.kind !== undefined && p.kind !== "latch-only")
984
+ throw badShape(`pending[${JSON.stringify(txnId)}] row kind`);
939
985
  }
940
986
  if (txn.credential !== undefined) {
941
987
  const c = txn.credential;
@@ -1006,6 +1052,9 @@ export function adjudicateLineagePending(controlDir, txnId, action, now) {
1006
1052
  const txn = rec.pending[txnId];
1007
1053
  if (txn === undefined)
1008
1054
  return { result: [] };
1055
+ if (isLatchOnlyTxn(txn)) {
1056
+ throw new ControlPlaneCorruptError(`memory lineage ledger: pending transaction ${txnId} is a bundle-import latch — it cannot be ${action === "promote" ? "promoted (its rows are not contributions)" : "discarded (its withhold guards not-yet-governed imported entries)"}; re-run the same bundle import to converge it (releaseImportLatch is receipt-gated)`);
1057
+ }
1009
1058
  if (action === "discard") {
1010
1059
  delete rec.pending[txnId];
1011
1060
  return { next: rec, result: [] };
@@ -1032,7 +1081,7 @@ export function reconcileLineage(controlDir, now) {
1032
1081
  let changed = false;
1033
1082
  for (const [txnId, txn] of Object.entries(rec.pending)) {
1034
1083
  if (txn.credential === undefined) {
1035
- undecidable.push({ txnId, sessionId: txn.sessionId, entryIds: txn.rows.map((r) => r.entryId) });
1084
+ undecidable.push({ txnId, sessionId: txn.sessionId, entryIds: txn.rows.map((r) => r.entryId), ...(isLatchOnlyTxn(txn) ? { kind: "latch-only" } : {}) });
1036
1085
  continue;
1037
1086
  }
1038
1087
  const applied = new Set(txn.credential.appliedIds);
@@ -1050,6 +1099,66 @@ export function reconcileLineage(controlDir, now) {
1050
1099
  return { ...(changed ? { next: rec } : {}), result: { promoted, undecidable } };
1051
1100
  });
1052
1101
  }
1102
+ function isLatchOnlyTxn(txn) {
1103
+ if (txn.kind === "latch-only")
1104
+ return true;
1105
+ return txn.rows.some((row) => row.kind === "latch-only");
1106
+ }
1107
+ export const IMPORT_RECEIPTS_DIR = "import-receipts";
1108
+ export function importLatchTxnId(bundleHash) {
1109
+ return `import:${bundleHash}`;
1110
+ }
1111
+ function importReceiptPath(controlDir, bundleHash) {
1112
+ return join(controlDir, IMPORT_RECEIPTS_DIR, `${bundleHash}.json`);
1113
+ }
1114
+ export function stageImportLatch(controlDir, bundleHash, rows, now) {
1115
+ const txnId = importLatchTxnId(bundleHash);
1116
+ lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
1117
+ const existing = rec.pending[txnId];
1118
+ if (existing !== undefined && !isLatchOnlyTxn(existing)) {
1119
+ throw new ControlPlaneCorruptError(`memory lineage ledger: pending transaction ${txnId} exists and is NOT an import latch — refusing to overwrite a real staged transaction`);
1120
+ }
1121
+ rec.pending[txnId] = {
1122
+ sessionId: txnId,
1123
+ at: now(),
1124
+ kind: "latch-only",
1125
+ rows: rows.map((r) => ({ entryId: r.entryId, rev: r.rev, kind: "latch-only" })),
1126
+ };
1127
+ return { next: rec, result: undefined };
1128
+ });
1129
+ return txnId;
1130
+ }
1131
+ export function releaseImportLatch(controlDir, txnId) {
1132
+ const prefix = "import:";
1133
+ if (!txnId.startsWith(prefix) || !/^[0-9a-f]{64}$/.test(txnId.slice(prefix.length))) {
1134
+ throw new ControlPlaneCorruptError(`releaseImportLatch: ${JSON.stringify(txnId)} is not an import-latch transaction id (import:<64-hex bundleHash>)`);
1135
+ }
1136
+ const receipt = importReceiptPath(controlDir, txnId.slice(prefix.length));
1137
+ let receiptPresent;
1138
+ try {
1139
+ readFileSync(receipt);
1140
+ receiptPresent = true;
1141
+ }
1142
+ catch (err) {
1143
+ if (err.code !== "ENOENT") {
1144
+ throw new ControlPlaneCorruptError(`releaseImportLatch: the completion receipt at ${receipt} could not be read (${err.code ?? "io error"}) — a probe failure is not absence (fail-closed), and absence would refuse the release`, { cause: err });
1145
+ }
1146
+ receiptPresent = false;
1147
+ }
1148
+ if (!receiptPresent) {
1149
+ throw new ControlPlaneCorruptError(`releaseImportLatch: no completion receipt at ${receipt} — the import's governance legs are not proven landed, the latch stays; re-run the same bundle import to converge`);
1150
+ }
1151
+ lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
1152
+ const txn = rec.pending[txnId];
1153
+ if (txn === undefined)
1154
+ return { result: undefined };
1155
+ if (!isLatchOnlyTxn(txn)) {
1156
+ throw new ControlPlaneCorruptError(`releaseImportLatch: pending transaction ${txnId} is NOT an import latch — refusing (this channel never discards a real staged transaction)`);
1157
+ }
1158
+ delete rec.pending[txnId];
1159
+ return { next: rec, result: undefined };
1160
+ });
1161
+ }
1053
1162
  export function lineageLatchedIds(controlDir) {
1054
1163
  const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
1055
1164
  const out = new Set();
@@ -1134,6 +1243,27 @@ export function captureAndClearLineageForEntries(controlDir, entryIds) {
1134
1243
  return { ...(changed ? { next: rec } : {}), result };
1135
1244
  });
1136
1245
  }
1246
+ export function importLineageCommitted(controlDir, rows) {
1247
+ if (rows.length === 0)
1248
+ return [];
1249
+ return lockedStrictUpdate(controlDir, LINEAGE_FILE, "memory lineage ledger", coerceLineage, (rec) => {
1250
+ const divergence = [];
1251
+ let changed = false;
1252
+ for (const row of rows) {
1253
+ const sessions = (rec.committed[row.entryId] ??= Object.create(null));
1254
+ const standing = Object.prototype.hasOwnProperty.call(sessions, row.sessionId) ? sessions[row.sessionId] : undefined;
1255
+ if (standing === undefined || row.lastAt > standing.lastAt) {
1256
+ sessions[row.sessionId] = { lastRev: row.lastRev, lastAt: row.lastAt };
1257
+ changed = true;
1258
+ continue;
1259
+ }
1260
+ if (row.lastAt === standing.lastAt && row.lastRev !== standing.lastRev) {
1261
+ divergence.push({ entryId: row.entryId, sessionId: row.sessionId, kept: "destination" });
1262
+ }
1263
+ }
1264
+ return { ...(changed ? { next: rec } : {}), result: divergence };
1265
+ });
1266
+ }
1137
1267
  export function readLineageRecord(controlDir) {
1138
1268
  const rec = coerceLineage(readStrictSidecar(controlDir, LINEAGE_FILE, "memory lineage ledger"));
1139
1269
  return { committed: rec.committed, pending: rec.pending };
@@ -22,7 +22,7 @@ export interface MemoryBackendContractHooks {
22
22
  * assembly answers it as capability-absent.
23
23
  */
24
24
  onOptionalMember?: (report: {
25
- member: "committedSnapshotOf" | "committedSnapshotsOfScopes" | "custodyOf" | "eraseWithEvidence";
25
+ member: "committedSnapshotOf" | "committedSnapshotsOfScopes" | "custodyOf" | "eraseWithEvidence" | "exportSnapshotOf" | "governanceExport" | "custodyImport" | "importBundleCommit";
26
26
  status: "verified" | "absent";
27
27
  }) => void;
28
28
  }
@@ -384,6 +384,58 @@ export async function memoryBackendContract(hooks) {
384
384
  await assert.rejects(async () => erase.call(b, { requestId: "req-contract-1", select: { ids: ["id-other-7777"] } }), (err) => err.code === "memory.erasure_selector_mismatch", "a reused requestId with a different selector refuses loudly");
385
385
  hooks.onOptionalMember?.({ member: "eraseWithEvidence", status: "verified" });
386
386
  });
387
+ defer("design/178 v2-c optional export/import faces: export composite / governance slice / custody carriage (absent ⇒ reported)", async () => {
388
+ const b = await hooks.make();
389
+ const seats = [
390
+ ["exportSnapshotOf", b.exportSnapshotOf],
391
+ ["governanceExport", b.governanceExport],
392
+ ["custodyImport", b.custodyImport],
393
+ ["importBundleCommit", b.importBundleCommit],
394
+ ];
395
+ for (const [member, seat] of seats) {
396
+ assert.ok(seat === undefined || typeof seat === "function", `${member} must be undefined (absent) or a function — a non-function occupant is a defect, not absence`);
397
+ }
398
+ const e = entry("id-export-0001", "s1", "exported", "exported body", { name: "Exported" });
399
+ await b.applyPatches([{ op: "add", id: e.id, entry: e }]);
400
+ const exportSnapshotOf = typeof seats[0]?.[1] === "function" ? seats[0][1] : undefined;
401
+ if (exportSnapshotOf === undefined) {
402
+ hooks.onOptionalMember?.({ member: "exportSnapshotOf", status: "absent" });
403
+ }
404
+ else {
405
+ const snap = await exportSnapshotOf.call(b, ["s1"]);
406
+ assert.ok(typeof snap.storeId === "string" && snap.storeId.length > 0, "the export snapshot names the store identity");
407
+ assert.strictEqual(typeof snap.fullStore, "boolean");
408
+ assert.deepStrictEqual(snap.entries.map((r) => r.id), [e.id], "the composite answers the committed entries of the requested scopes");
409
+ assert.strictEqual(snap.entries[0]?.rev, e.rev, "entries carry the committed rev");
410
+ assert.ok(Array.isArray(snap.custody) && Array.isArray(snap.unbound) && Array.isArray(snap.pendingLatch));
411
+ hooks.onOptionalMember?.({ member: "exportSnapshotOf", status: "verified" });
412
+ }
413
+ const governanceExport = typeof seats[1]?.[1] === "function" ? seats[1][1] : undefined;
414
+ if (governanceExport === undefined) {
415
+ hooks.onOptionalMember?.({ member: "governanceExport", status: "absent" });
416
+ }
417
+ else {
418
+ const slice = await governanceExport.call(b, ["s1"]);
419
+ assert.strictEqual(slice.complete, true, "a quiet store's governance slice answers complete");
420
+ assert.ok(Array.isArray(slice.custody) && Array.isArray(slice.unbound));
421
+ hooks.onOptionalMember?.({ member: "governanceExport", status: "verified" });
422
+ }
423
+ const custodyImport = typeof seats[2]?.[1] === "function" ? seats[2][1] : undefined;
424
+ if (custodyImport === undefined) {
425
+ hooks.onOptionalMember?.({ member: "custodyImport", status: "absent" });
426
+ }
427
+ else {
428
+ const opts = { bundleHash: "c".repeat(64), sourceStoreId: "contract-src-store" };
429
+ const outcome = await custodyImport.call(b, [{ ev: "kit-del-1", channel: "delete", id: "id-ghost-kit-1", rev: "deadbeefdeadbeef", at: 1 }], opts);
430
+ assert.strictEqual(outcome.appended, 1, "a well-formed delete row for an absent id is carried (anti-resurrection evidence needs no living entry)");
431
+ assert.deepStrictEqual(outcome.withheld, []);
432
+ const unknown = await custodyImport.call(b, [{ ev: "kit-unk-1", channel: "kit-unknown-channel", payload: 1, at: 1 }], opts);
433
+ assert.strictEqual(unknown.appended, 0, "an unknown channel is never appended verbatim");
434
+ assert.deepStrictEqual(unknown.withheld, [{ srcEv: "kit-unk-1", channel: "kit-unknown-channel" }], "an unknown channel is withheld AND reported — never a whole-package refusal");
435
+ hooks.onOptionalMember?.({ member: "custodyImport", status: "verified" });
436
+ }
437
+ hooks.onOptionalMember?.({ member: "importBundleCommit", status: typeof seats[3]?.[1] === "function" ? "verified" : "absent" });
438
+ });
387
439
  if (!hooks.makeSibling) {
388
440
  const reason = hooks.skipCrossInstanceCas?.trim();
389
441
  if (reason) {
@@ -163,7 +163,14 @@ export function createMemoryEngineTools(opts) {
163
163
  throw err;
164
164
  return refusedSearch("error", GENERIC_FAILURE, "failed");
165
165
  }
166
- const live = top.filter((h) => bodyById.has(h.id));
166
+ let terminalExclusions;
167
+ try {
168
+ terminalExclusions = planes.map((plane) => plane.challengeExclusions?.());
169
+ }
170
+ catch {
171
+ return refusedSearch("challenge_ledger_unavailable", "Memory search is unavailable: the challenge ledger for a mounted memory plane cannot be read (fail-closed). Report this to the operator.", "failed");
172
+ }
173
+ const live = top.filter((h) => bodyById.has(h.id) && terminalExclusions[h.planeIndex]?.has(h.id) !== true);
167
174
  if (live.length === 0) {
168
175
  const details = { outcome: "ok", hits: [] };
169
176
  return { content: `No memory entries matched ${JSON.stringify(inlineUntrusted(query, 120))}. If the user expected you to know this, say that you checked memory and found nothing.`, details };
@@ -357,14 +357,26 @@ export async function prepareCcImport(opts) {
357
357
  continue;
358
358
  }
359
359
  layers.push({ path: layer.path, layer: layer.layer, found: true });
360
- let container;
360
+ let settingsRoot;
361
361
  try {
362
- container = JSON.parse(raw)?.permissions;
362
+ settingsRoot = JSON.parse(raw);
363
363
  }
364
364
  catch (err) {
365
365
  skipped.push({ rule: layer.path, reason: `settings file is not valid JSON (${errText(err)})` });
366
366
  continue;
367
367
  }
368
+ let container;
369
+ if (settingsRoot === null)
370
+ container = undefined;
371
+ else if (typeof settingsRoot === "object" && !Array.isArray(settingsRoot))
372
+ container = settingsRoot.permissions;
373
+ else {
374
+ skipped.push({
375
+ rule: layer.path,
376
+ reason: `settings file is not a JSON object — no bucket in this layer could be read, so it is reported as unread rather than as empty (it stays in the settings file either way)`,
377
+ });
378
+ continue;
379
+ }
368
380
  let permissions;
369
381
  if (container === undefined || container === null)
370
382
  permissions = undefined;
@@ -327,6 +327,13 @@ function orgRevisionEvidenceOf(resolution, onDefect) {
327
327
  function persistedRuleHitOf(admitting) {
328
328
  return admitting === undefined ? undefined : { rule: admitting.rule, dots: admitting.adds.map((a) => ({ actor: a.dot.actor, counter: a.dot.counter })) };
329
329
  }
330
+ function cwdConflictsRestoreError(requestedCwd) {
331
+ const e = new Error(`RunInternals.requestedCwd ("${requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
332
+ `the restored workspace's own mount path is authoritative for the task root, so a requested cwd on this leg ` +
333
+ `would be ignored (or worse, probed against an unrestored environment). Drop requestedCwd on resume legs.`);
334
+ e.code = "config.cwd_conflicts_restore";
335
+ return e;
336
+ }
330
337
  export async function prepareTask(spec, deps, sessions, resume, internals, runnerSelf) {
331
338
  const doors = prepareConfigDoors({ spec, deps, sessions, resume, internals });
332
339
  spec = doors.spec;
@@ -421,6 +428,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
421
428
  e.code = "config.cwd_unsupported";
422
429
  throw e;
423
430
  }
431
+ if (internals?.requestedCwd !== undefined && resume?.workspaceHandle !== undefined) {
432
+ await settleTeardownLeg(() => forgetOnThrow(), "forgetOnThrow (cwd-conflicts-restore leg)", (err) => deps.onError?.(err, { phase: "config", sessionId }));
433
+ throw cwdConflictsRestoreError(internals.requestedCwd);
434
+ }
424
435
  try {
425
436
  ownedEnv = deps.executionEnvFactory
426
437
  ? await deps.executionEnvFactory({
@@ -1248,6 +1259,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
1248
1259
  if (c.ok)
1249
1260
  additionalRootsCanonical.push(c.value);
1250
1261
  }
1262
+ for (const a of [additionalRootsCanonical, additionalReadRootsCanonical])
1263
+ a.splice(0, a.length, ...new Set(a));
1251
1264
  const readFileState = new Map((resume?.seed.readFileState ?? []).map(([k, v]) => [rebaseRestoredPath(k), v]));
1252
1265
  readFileStateForCheckpoint = readFileState;
1253
1266
  seedContextFiles = async (files) => {
@@ -4126,6 +4126,11 @@ export class Runner {
4126
4126
  if (cp.state.workspaceHandle !== undefined && this.deps.executionEnvFactory === undefined) {
4127
4127
  throw new CheckpointError("checkpoint.unsupported_version", "checkpoint has a remote workspaceHandle but no RunnerDeps.executionEnvFactory is wired to rebuild the env", { reason: "env_factory_missing" });
4128
4128
  }
4129
+ if (cp.state.workspaceHandle !== undefined && internals?.requestedCwd !== undefined) {
4130
+ throw new CheckpointError("checkpoint.cwd_conflicts_restore", `internals.requestedCwd ("${internals.requestedCwd}") cannot be combined with a checkpoint workspace restore — ` +
4131
+ "the restored workspace's own mount path is authoritative for the task root. Drop requestedCwd on resume legs " +
4132
+ "(the checkpoint stays pending and is resumable without it).");
4133
+ }
4129
4134
  if (cp.state.inheritedGate?.requiresParentConstraint === true) {
4130
4135
  const supplied = internals?.inheritedGate?.parentConstraints?.length ?? 0;
4131
4136
  if (supplied === 0) {
@@ -196,7 +196,9 @@ export function createSkillTool(skills, scope) {
196
196
  const name = String(a.skill ?? "");
197
197
  const skill = byName.get(name);
198
198
  if (!skill) {
199
- throw new Error(`Unknown skill "${name}". Available skills: ${names}.`);
199
+ throw new Error(`Unknown skill "${name}". Available skills: ${names}. ` +
200
+ `(Only skills registered with this deployment are loadable — other products' skills directories are not read. ` +
201
+ `If the user wants one of those here, read its source file and recreate its content for this deployment instead.)`);
200
202
  }
201
203
  if (scope && skill.manifest) {
202
204
  scope.push({ kind: "manifest", manifest: skill.manifest });
@@ -45,7 +45,8 @@ export function classifyDeferred(opts) {
45
45
  if (!pinned.has(name))
46
46
  deferred.add(name);
47
47
  if (opts.deferMode === "auto") {
48
- const candidates = opts.fullTools.filter((t) => !deferred.has(t.name) && !pinned.has(t.name));
48
+ const callerNames = new Set(opts.specs.map((s) => s.name));
49
+ const candidates = opts.fullTools.filter((t) => !deferred.has(t.name) && !pinned.has(t.name) && callerNames.has(t.name));
49
50
  const inlineFace = opts.fullTools.filter((t) => !deferred.has(t.name));
50
51
  const total = inlineFace.reduce((n, t) => n + inlinedChars(t), 0);
51
52
  const window = (opts.model.contextTokens ?? opts.model.contextWindow ?? 0) * CHARS_PER_TOKEN;
@@ -25,26 +25,6 @@ export interface WriteProtectedHit {
25
25
  readonly name: string;
26
26
  readonly kind: WriteProtectedKind;
27
27
  }
28
- /**
29
- * WRITE_PROTECTED_DEFAULT_TABLE — the default-active table (the material basis of the deployment
30
- * admin face: what an unconfigured deployment demotes to `ask`; visible here, deletable by
31
- * replacing the seat with a filtered copy). CC 2.1.233 triple VERBATIM first, then the sema rows,
32
- * each with its argument.
33
- *
34
- * NOT listed, deliberately (each a ruled-out candidate, recorded so the next reader does not
35
- * re-litigate silently):
36
- * · `.env` / `.env.*` — CC's own table excludes them too (only `.envrc`, the direnv AUTO-EXECUTION
37
- * vector, is in): a plain `.env` is application config and routine workspace material for the
38
- * tasks this engine runs (the read deny set rules it out on the same grounds); the opt-in write
39
- * DENY (`RECOMMENDED_SENSITIVE_PATTERNS`) covers deployments that want it guarded.
40
- * · key-material FILE patterns (`id_rsa*`, `*.pem`, …) — they are glob-shaped, and this table
41
- * speaks literals; the opt-in deny policy owns that vocabulary.
42
- * · cloud credential dirs (`.aws`, `.kube`, `.azure`, `.config/gcloud`) — writing cloud config IS
43
- * the routine "configure this environment" action tasks are asked to perform, so a default ask
44
- * on every such write is recurring friction without CC precedent; the opt-in deny policy covers
45
- * them, and the READ side already default-refuses them (reading credentials exfiltrates; writing
46
- * a fresh config file does not).
47
- */
48
28
  export declare const WRITE_PROTECTED_DEFAULT_TABLE: readonly WriteProtectedRow[];
49
29
  /**
50
30
  * The ONE case fold of this module, applied to BOTH sides of every comparison (table names at
@@ -1,6 +1,7 @@
1
1
  import { writeTargetPath } from "../tools/fs/safety.js";
2
2
  import { PATH_CONFINABLE_WRITE_TOOLS } from "./runner/session-rule-policy.js";
3
- export const WRITE_PROTECTED_DEFAULT_TABLE = [
3
+ const freezeTable = (rows) => Object.freeze(rows.map((r) => Object.freeze(r)));
4
+ export const WRITE_PROTECTED_DEFAULT_TABLE = freezeTable([
4
5
  { name: ".gitconfig", kind: "basename" },
5
6
  { name: ".gitmodules", kind: "basename" },
6
7
  { name: ".bashrc", kind: "basename" },
@@ -50,7 +51,7 @@ export const WRITE_PROTECTED_DEFAULT_TABLE = [
50
51
  { name: ".config/git", kind: "segment-run" },
51
52
  { name: ".ssh", kind: "segment" },
52
53
  { name: ".gnupg", kind: "segment" },
53
- ];
54
+ ]);
54
55
  export function foldWriteProtectCase(s) {
55
56
  return s.toLowerCase().replace(/ı/g, "i").replace(/ſ/g, "s");
56
57
  }
@@ -107,7 +108,7 @@ export function resolveWriteProtectedTable(entries) {
107
108
  seen.add(key);
108
109
  out.push(row);
109
110
  }
110
- return out;
111
+ return freezeTable(out);
111
112
  }
112
113
  export function compileWriteProtection(entries) {
113
114
  const rows = resolveWriteProtectedTable(entries);
package/dist/index.d.ts CHANGED
@@ -166,7 +166,7 @@ export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootA
166
166
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, type AdoptionStatus, type AdoptLocalDataRootOptions, type AdoptLocalDataRootResult, type AdoptionCarriageLeg, type AdoptionCarriageLegContext, type AdoptionConfigWitnessReceipt, } from "./stores/file/adoption/adopt.js";
167
167
  export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, type PersistedRuleHit, type PersistedRuleUnreadable, type PersistedRuleAnswer, normalizePersistedRuleHit, type Hooks, type HookToolContext, type HookInvocationIdentity, type UserPromptSubmitContext, type PostToolBatchContext, type HookEnvCapabilities, type HookToolOutput, type PreToolUseResult, type PostToolUseResult, type UserPromptSubmitResult, type HookToolFailure, type PostToolUseFailureResult, type PostToolBatchCall, type PostToolBatchResult, type PreCompactContext, type PreCompactResult, type PostCompactContext, type StopFailureContext, type PermissionDeniedPayload, type PermissionDeniedSource, } from "./core/hooks.js";
168
168
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, type NormalizedMemorySpec, type MemorySpecInput, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, type Embedder, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, type UtilityGate, type MemoryStore, type MemoryVectorMode, type ScoredMemory, type MemoryNoteHeader, type MemoryNoteRecord, type MemoryNoteType, type StructuredNoteInput, } from "./core/memory.js";
169
- export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, type V2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, type TransferEvidence, type CommittedBinding, type CommittedEntrySnapshot, type CommittedScopeSnapshots, type EntryCustodyReport, erasureSelectHash, type EraseMemoryEntriesInput, type ErasureSelect, type ErasedBinding, type MemoryErasureAttestation, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
169
+ export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, type V2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengeAssignment, type ChallengeEvent, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, type MemoryAnnouncement, type ScanFinding, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, type TransferEvidence, type CommittedBinding, type CommittedEntrySnapshot, type CommittedScopeSnapshots, type EntryCustodyReport, erasureSelectHash, type EraseMemoryEntriesInput, type ErasureSelect, type ErasedBinding, type MemoryErasureAttestation, computeMemoryBundleHash, type MemoryExportBundle, type MemoryImportReport, type MemoryExportSnapshot, type MemoryBundleImportPlan, type BundleChallengeRow, type BundleLineageRow, type BundlePollutedSession, type MemoryBackend, type MemoryEntry, type MemoryEntryFrontmatter, type MemoryEntryHeader, type ScoredMemoryEntry, type NotePatch, type PatchReport, type MaterializedFile, type MemorySessionHandle, type HarvestReport, type HarvestRejection, type HarvestRejectionCode, type ParsedEntryFile, type ScannedEntryFile, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, type ParsedScopeKey, migrateScope, type MigrateScopeReport, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, type RemoteMemoryFile, type RemoteMemoryBaseline, type RemoteMaterialization, type RemoteHarvestResult, type InboundEntryFinding, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, type MemorySyncCursor, type MemorySyncConflict, type MemorySyncPlan, type RecallSource, syncMemoryScope, type MemorySyncTransport, type MemorySyncRequestBody, type MemorySyncResponseBody, type MemorySyncClientConflict, type SyncMemoryScopeOptions, type MemorySyncClientResult, } from "./core/memory-engine/index.js";
170
170
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, type SharedMemoryStoreProvider, type SharedMemoryStoreReader, type SharedMemoryPagedList, type SharedMemoryStoreInfo, type SharedMemoryDocumentEntry, type SharedMemorySnapshot, type SharedMemoryRequestContext, type MemoryListDetails, type MemoryReadDetails, } from "./core/shared-memory/types.js";
171
171
  export { sharedMemoryStoreContract, type SharedMemoryFixture, type SharedMemoryStoreContractHooks, } from "./core/shared-memory/contract.js";
172
172
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";
package/dist/index.js CHANGED
@@ -128,7 +128,7 @@ export { AdoptionError, assertAdoptionBootGate, readRootAdoptionFile, writeRootA
128
128
  export { adoptLocalDataRoot, ackAdoptionConfig, witnessAdoptionConfig, listAdoptionQuarantine, readAdoptionStatus, } from "./stores/file/adoption/adopt.js";
129
129
  export { formatHookFeedback, runToolGate, createHookEnvCapabilities, createPreToolUseConstraintPolicy, normalizePersistedRuleHit, } from "./core/hooks.js";
130
130
  export { InMemoryMemoryStore, composeMemoryBlock, composeLayeredMemoryBlock, normalizeMemorySpec, supportsConsolidation, supportsPeriodicConsolidation, firstSentence, expandLexicalTerms, lexicalSearchMatch, classifyPromotable, enforcePromotableWriteGate, guardedMemoryStore, MemoryGateError, detectSecret, enforceSecretWriteGate, enforceStructuredNoteSecretGate, } from "./core/memory.js";
131
- export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, erasureSelectHash, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, migrateScope, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, syncMemoryScope, } from "./core/memory-engine/index.js";
131
+ export { MemoryEngine, FileMemoryEngineBackend, memoryBackendContract, assertMemoryBackendSearchEquivalence, buildMemoryInstruction, truncateIndex, scanEntryFiles, deriveRepoKey, deriveRepoMemoryDir, deriveControlPlaneDir, deriveRepoControlPlaneDir, resolveMemoryEngineRoot, scopeDirName, ControlPlaneCorruptError, parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, readV2HeaderHints, LINEAGE_FILE, CHALLENGES_FILE, CHALLENGED_HISTORY_FILE, CHALLENGE_LEDGER_MAX_EVENTS, rebuildStrictControlPlaneLedger, MEMORY_INDEX_FILENAME, MEMORY_INDEX_MAX_LINES, MEMORY_INDEX_MAX_BYTES, MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, STUB_ARCHIVED_LINE, DEFAULT_MAX_MEMORY_FILES, DEFAULT_MAX_ENTRY_DEPTH, DEFAULT_HARVEST_DEADLINE_MS, DEFAULT_HARVEST_FILE_BUDGET, MASS_DELETION_FUSE_RATIO, scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE, renderAnnouncements, enqueueMemoryAnnouncement, drainMemoryAnnouncements, peekMemoryAnnouncements, bumpScanFuse, scanFuseCount, clearScanFuse, ANNOUNCEMENTS_FILE, MEMORY_ANNOUNCEMENTS_MAX, SCAN_FUSE_FILE, SCAN_FUSE_THRESHOLD, erasureSelectHash, computeMemoryBundleHash, SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, migrateScope, materializeEntriesToFiles, harvestFilesToPatches, projectionsToWriteBack, screenInboundEntries, REMOTE_HARVEST_PER_FILE_BYTES, REMOTE_MASS_DELETION_FUSE_RATIO, deriveProjectMemoryDir, deriveProjectControlDir, recordProjectIdHint, lookupProjectIdHint, PROJECT_ID_HINTS_FILE, reconcileMemoryEntries, nextSyncBaseline, mergeRecallHits, syncMemoryScope, } from "./core/memory-engine/index.js";
132
132
  export { SHARED_MEMORY_READ_CAP_BYTES, SHARED_MEMORY_LIST_PAGE_SIZE, SharedMemoryStoreError, } from "./core/shared-memory/types.js";
133
133
  export { sharedMemoryStoreContract, } from "./core/shared-memory/contract.js";
134
134
  export { termSet, jaccardDistance, cosineDistance } from "./core/memory-vector.js";