@sema-agent/core 5.38.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.
- package/CHANGELOG.md +182 -10
- package/dist/agents/send-message-tool.d.ts +8 -0
- package/dist/agents/send-message-tool.js +8 -0
- package/dist/agents/teacher.js +9 -3
- package/dist/agents/verify.js +9 -3
- package/dist/core/checkpoint-store.d.ts +12 -0
- package/dist/core/governance-codes.js +2 -0
- package/dist/core/hooks.d.ts +23 -0
- package/dist/core/hooks.js +53 -4
- package/dist/core/mailbox-store.d.ts +39 -0
- package/dist/core/mailbox-store.js +9 -0
- package/dist/core/memory-engine/engine.d.ts +27 -0
- package/dist/core/memory-engine/engine.js +103 -1
- package/dist/core/memory-engine/export-bundle.d.ts +192 -0
- package/dist/core/memory-engine/export-bundle.js +306 -0
- package/dist/core/memory-engine/file-backend.d.ts +178 -1
- package/dist/core/memory-engine/file-backend.js +637 -6
- package/dist/core/memory-engine/index.d.ts +2 -1
- package/dist/core/memory-engine/index.js +1 -0
- package/dist/core/memory-engine/layout.d.ts +89 -1
- package/dist/core/memory-engine/layout.js +131 -1
- package/dist/core/memory-engine/memory-backend-contract.d.ts +1 -1
- package/dist/core/memory-engine/memory-backend-contract.js +52 -0
- package/dist/core/memory-engine/tools.js +8 -1
- package/dist/core/permission-rule-consent.d.ts +27 -4
- package/dist/core/permission-rule-consent.js +41 -4
- package/dist/core/permission-rule-model.d.ts +7 -1
- package/dist/core/runner/prepare-task.js +24 -3
- package/dist/core/runner/runtask.js +5 -0
- package/dist/core/runner/synthetic-tools.js +3 -1
- package/dist/core/runner/tool-disclosure.js +2 -1
- package/dist/core/sensitive-path-policy.js +3 -3
- package/dist/core/store-contracts/mailbox-store-contract.d.ts +29 -1
- package/dist/core/store-contracts/mailbox-store-contract.js +78 -0
- package/dist/core/types.d.ts +21 -0
- package/dist/core/write-protect.d.ts +73 -0
- package/dist/core/write-protect.js +195 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/orchestration/governance-baseline-validity.d.ts +44 -0
- package/dist/orchestration/governance-baseline-validity.js +55 -0
- package/dist/orchestration/run-workflow-tool.js +33 -8
- package/dist/orchestration/workflow-script-runner.js +9 -4
- package/dist/tools/fs/read-deny.d.ts +15 -5
- package/dist/tools/fs/read-deny.js +33 -12
- package/dist/tools/fs/safety.d.ts +5 -2
- package/dist/tools/fs/safety.js +5 -3
- package/dist/tools/fs/search.d.ts +33 -0
- package/dist/tools/fs/search.js +72 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +21 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import { assertRetentionPolicy } from "./retention-policy.js";
|
|
2
|
+
export const MAILBOX_TOMBSTONED_RECIPIENT_CODE = "mailbox.recipient_tombstoned";
|
|
3
|
+
export class MailboxStoreError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
constructor(code, message) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.code = code;
|
|
8
|
+
this.name = "MailboxStoreError";
|
|
9
|
+
}
|
|
10
|
+
}
|
|
2
11
|
export function newestSentAt(messages) {
|
|
3
12
|
let newest;
|
|
4
13
|
for (const m of messages) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type CommittedBinding, type EraseMemoryEntriesInput, type MemoryErasureAttestation, type TransferEvidence } from "./file-backend.js";
|
|
2
|
+
import { type MemoryExportBundle, type MemoryImportReport } from "./export-bundle.js";
|
|
2
3
|
import { type ChallengeAssignment, type ChallengeEvent, type ControlPlaneRebuildReceipt, type StrictControlPlaneLedger, type ChallengedHistoryRow, type LineagePendingTxn, type LineagePromotion, type MemoryPartitionIncidentSink, type RetrievedAccountRow, type SessionPollutionRecord } from "./layout.js";
|
|
3
4
|
import type { HarvestReport, MemoryAnnouncement, MemoryBackend, MemorySessionHandle, ScanFinding } from "./types.js";
|
|
4
5
|
/**
|
|
@@ -438,6 +439,32 @@ export declare class MemoryEngine {
|
|
|
438
439
|
* reported honestly, never fabricated and never a throw (#196 absence-reports-not-silent-green).
|
|
439
440
|
*/
|
|
440
441
|
provenanceOf(entryId: string): Promise<EntryProvenanceAccount>;
|
|
442
|
+
/**
|
|
443
|
+
* Export the requested scopes as a GOVERNANCE-COMPLETE bundle (design/178 v2 §4.3 + v2-c):
|
|
444
|
+
* committed entries (audit-snapshot semantics — shadow-backed rows a disk scan would miss are
|
|
445
|
+
* in; unavailable content is enumerated, never dressed), the scope-sliced custody chain, the
|
|
446
|
+
* exported entries' unresolved challenge events and lineage rows, and the named sessions'
|
|
447
|
+
* pollution markers — all read inside the backend's single-lock export composite, so a bundle is
|
|
448
|
+
* never a mixed-epoch scene. Refusals are loud and total (`memory.export_incomplete`): there is
|
|
449
|
+
* no degraded/partial bundle shape — "a truncated package that looks complete" is the one
|
|
450
|
+
* deliverable this API is forbidden to produce.
|
|
451
|
+
*/
|
|
452
|
+
exportMemoryScopes(scopes: readonly string[]): Promise<MemoryExportBundle>;
|
|
453
|
+
/**
|
|
454
|
+
* Import a bundle (design/178 v2-c §1/§7). Validation is TOTAL and lands nothing on failure
|
|
455
|
+
* (`memory.import_rejected`): integrity (fixed section-hash key set, per-entry content-rev
|
|
456
|
+
* recomputation, batch-unique ids/event ids), structure (the governance section is REQUIRED —
|
|
457
|
+
* there is no entries-only escape hatch), and — when `opts.expectedScopes` is present (a service
|
|
458
|
+
* endpoint pins its principal's grant set here) — the scope authorization envelope. Everything
|
|
459
|
+
* after validation runs inside the backend's import composite: the synthetic latch (imported ids
|
|
460
|
+
* stay withheld from every model-visible read face until governance fully lands), the five
|
|
461
|
+
* import judgments IN-LOCK, entry landing through the one transaction skeleton, the governance
|
|
462
|
+
* legs, and the receipt-gated latch release. A crash anywhere converges by re-importing the SAME
|
|
463
|
+
* bundle; a completed import answers its recorded report idempotently.
|
|
464
|
+
*/
|
|
465
|
+
importMemoryBundle(bundle: MemoryExportBundle, opts?: {
|
|
466
|
+
expectedScopes?: readonly string[];
|
|
467
|
+
}): Promise<MemoryImportReport>;
|
|
441
468
|
/** Host API: challenge every entry the lineage ledger attributes to `sessionId` (post-hoc source
|
|
442
469
|
* falsification — trustedTools misconfigured, a tool re-classified, late delegation evidence).
|
|
443
470
|
* Same requestId contract as {@link challengeEntries}. */
|
|
@@ -6,6 +6,7 @@ import { inlineUntrusted } from "../untrusted-text.js";
|
|
|
6
6
|
import { formatMemoryAge } from "../memory-recall.js";
|
|
7
7
|
import { computeEntryRev, parseEntryFile, serializeEntryFile } from "./frontmatter.js";
|
|
8
8
|
import { DEFAULT_MAX_ENTRY_DEPTH, MEMORY_INDEX_FILENAME, canonicalJsonStringify, captureErasureInput, erasureRequestInvalid, erasureSelectHash, scanEntryFiles, } from "./file-backend.js";
|
|
9
|
+
import { assembleMemoryExportBundle, computeMemoryBundleHash, memoryBundleInvalid, } from "./export-bundle.js";
|
|
9
10
|
import { QUARANTINE_DIR, SCAN_FUSE_THRESHOLD, quarantineAndTombstone, readIndexRevs, writeIndexRevs, bumpScanFuse, canonicalize, claimRootScope, clearScanFuse, adoptCanonicalKeyedControlDir, deriveControlPlaneDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, ensureDirExists, isContainedIn, markSessionPolluted, readSessionPollution, recordRetrievedAccount, writeFileNoFollow, readRetrievedAccount, registerScope, registeredScopes, resolveMemoryEngineRoot, scopeDirFor, appendChallengeEvents, appendLineageAudit, rebuildStrictControlPlaneLedger, isStrictControlPlaneLedgerCorrupt, CHALLENGE_LEDGER_MAX_EVENTS, adjudicateLineagePending, challengedEntryIds, clearLineageForEntries, discardLineagePending, lineageAccountOfEntry, lineageContributionsOfSession, lineageLatchedIds, promoteLineagePending, readChallengeEvents, readChallengedHistory, readLineageRecord, recordChallengedHistory, recordLineageCredential, reconcileLineage, resolveChallengeEvent, stageLineagePending, } from "./layout.js";
|
|
10
11
|
import { scanMemoryFileName, scanMemoryWrite, scanRemediation } from "./scan.js";
|
|
11
12
|
export const MEMORY_INSTRUCTION_TEMPLATE = `# Memory
|
|
@@ -521,6 +522,105 @@ export class MemoryEngine {
|
|
|
521
522
|
return { v: 1, id: entryId, binding, contributors: plane.contributors, ...(plane.exclusion !== undefined ? { exclusion: plane.exclusion } : {}), contentState, ...(ingest !== undefined ? { ingest } : {}), custody };
|
|
522
523
|
}
|
|
523
524
|
}
|
|
525
|
+
async exportMemoryScopes(scopes) {
|
|
526
|
+
if (!Array.isArray(scopes) || scopes.length === 0 || scopes.some((s) => typeof s !== "string" || s.length === 0) || new Set(scopes).size !== scopes.length) {
|
|
527
|
+
const e = new Error("exportMemoryScopes: scopes must be a non-empty array of unique non-empty scope names");
|
|
528
|
+
e.code = "config.memory_export_request";
|
|
529
|
+
throw e;
|
|
530
|
+
}
|
|
531
|
+
const requested = [...scopes];
|
|
532
|
+
const face = this.backend.exportSnapshotOf;
|
|
533
|
+
if (typeof face !== "function") {
|
|
534
|
+
const e = new Error("exportMemoryScopes: this backend has no export-snapshot capability (exportSnapshotOf) — a governance-complete bundle cannot be assembled from the generic read faces (they cannot fence the five store faces into one epoch), and a governance-less export is the laundering shape this API refuses by design.");
|
|
535
|
+
e.code = "memory.export_incomplete";
|
|
536
|
+
throw e;
|
|
537
|
+
}
|
|
538
|
+
const snap = await face.call(this.backend, requested);
|
|
539
|
+
const exportedIds = new Set([...snap.entries.map((e) => e.id), ...snap.contentUnavailable.map((r) => r.id)]);
|
|
540
|
+
const resolvedGenerations = new Set();
|
|
541
|
+
for (const e of snap.challenges)
|
|
542
|
+
if (e.kind === "resolve")
|
|
543
|
+
resolvedGenerations.add(`${e.entryId}#${e.generation}`);
|
|
544
|
+
const challenges = [];
|
|
545
|
+
for (const e of snap.challenges) {
|
|
546
|
+
if (e.kind !== "challenge" || !exportedIds.has(e.entryId) || resolvedGenerations.has(`${e.entryId}#${e.generation}`))
|
|
547
|
+
continue;
|
|
548
|
+
challenges.push({ eventId: e.eventId, entryId: e.entryId, reason: e.reason, at: e.at, ...(e.challengedRev !== undefined ? { challengedRev: e.challengedRev } : {}) });
|
|
549
|
+
}
|
|
550
|
+
const lineage = snap.lineage.filter((r) => exportedIds.has(r.entryId));
|
|
551
|
+
const namedSessions = new Set(lineage.map((r) => r.sessionId));
|
|
552
|
+
for (const row of snap.custody) {
|
|
553
|
+
const sessions = row.sessions;
|
|
554
|
+
if (Array.isArray(sessions))
|
|
555
|
+
for (const s of sessions)
|
|
556
|
+
if (typeof s === "string")
|
|
557
|
+
namedSessions.add(s);
|
|
558
|
+
}
|
|
559
|
+
const pollutedSessions = snap.pollutedSessions.filter((p) => namedSessions.has(p.sessionId));
|
|
560
|
+
return assembleMemoryExportBundle({
|
|
561
|
+
at: this.now(),
|
|
562
|
+
scopes: requested,
|
|
563
|
+
storeId: snap.storeId,
|
|
564
|
+
entries: snap.entries,
|
|
565
|
+
challenges,
|
|
566
|
+
pollutedSessions,
|
|
567
|
+
lineage,
|
|
568
|
+
custody: snap.custody,
|
|
569
|
+
residuals: {
|
|
570
|
+
quarantined: snap.quarantined,
|
|
571
|
+
unbound: snap.fullStore ? snap.unbound : [],
|
|
572
|
+
pendingLatch: snap.fullStore ? snap.pendingLatch : [],
|
|
573
|
+
contentUnavailable: snap.contentUnavailable,
|
|
574
|
+
quarantineOpaque: snap.quarantineOpaque,
|
|
575
|
+
unsliceableCustody: snap.unsliceableCustody,
|
|
576
|
+
unboundCount: snap.unbound.length,
|
|
577
|
+
pendingLatchCount: snap.pendingLatch.length,
|
|
578
|
+
},
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
async importMemoryBundle(bundle, opts) {
|
|
582
|
+
let inert;
|
|
583
|
+
try {
|
|
584
|
+
inert = JSON.parse(JSON.stringify(bundle));
|
|
585
|
+
}
|
|
586
|
+
catch (err) {
|
|
587
|
+
const e = new Error(`importMemoryBundle: the bundle is not JSON-serializable (${err instanceof Error ? err.message : String(err)})`);
|
|
588
|
+
e.code = "config.memory_import_request";
|
|
589
|
+
throw e;
|
|
590
|
+
}
|
|
591
|
+
const bad = memoryBundleInvalid(inert, { ...(opts?.expectedScopes !== undefined ? { expectedScopes: opts.expectedScopes } : {}) });
|
|
592
|
+
if (bad !== undefined) {
|
|
593
|
+
const e = new Error(`importMemoryBundle: ${bad} — the whole package is refused, nothing landed`);
|
|
594
|
+
e.code = "memory.import_rejected";
|
|
595
|
+
throw e;
|
|
596
|
+
}
|
|
597
|
+
const b = inert;
|
|
598
|
+
const face = this.backend.importBundleCommit;
|
|
599
|
+
if (typeof face !== "function") {
|
|
600
|
+
const e = new Error("importMemoryBundle: this backend has no bundle-import capability (importBundleCommit) — the import latch/receipt transaction requires it, and an unlatched import would serve entries before their governance lands; nothing was imported.");
|
|
601
|
+
e.code = "memory.import_rejected";
|
|
602
|
+
throw e;
|
|
603
|
+
}
|
|
604
|
+
const bundleHash = computeMemoryBundleHash(b.integrity.sectionHashes);
|
|
605
|
+
const custody = [];
|
|
606
|
+
const seenEv = new Set();
|
|
607
|
+
for (const row of b.governance.custody) {
|
|
608
|
+
if (seenEv.has(row.ev))
|
|
609
|
+
continue;
|
|
610
|
+
seenEv.add(row.ev);
|
|
611
|
+
custody.push(row);
|
|
612
|
+
}
|
|
613
|
+
const plan = {
|
|
614
|
+
bundleHash,
|
|
615
|
+
sourceStoreId: b.storeId,
|
|
616
|
+
entries: b.entries,
|
|
617
|
+
challenges: b.governance.challenges,
|
|
618
|
+
pollutedSessions: b.governance.pollutedSessions,
|
|
619
|
+
lineage: b.governance.lineage,
|
|
620
|
+
custody,
|
|
621
|
+
};
|
|
622
|
+
return face.call(this.backend, plan);
|
|
623
|
+
}
|
|
524
624
|
challengeSession(sessionId, reason, requestId) {
|
|
525
625
|
if (typeof requestId !== "string" || requestId === "") {
|
|
526
626
|
const e = new Error("challengeSession: requestId is required (idempotency identity — retries must reuse it; the engine does not mint one)");
|
|
@@ -822,7 +922,9 @@ export class MemoryEngine {
|
|
|
822
922
|
const rec = reconcileLineage(this.controlDir, this.now);
|
|
823
923
|
this.settlePromotions(rec.promoted);
|
|
824
924
|
for (const u of rec.undecidable) {
|
|
825
|
-
report.warnings.push(
|
|
925
|
+
report.warnings.push(u.kind === "latch-only"
|
|
926
|
+
? `memory bundle-import latch ${u.txnId} is unsettled (an import did not run to completion) — ${u.entryIds.length} entr${u.entryIds.length === 1 ? "y is" : "ies are"} latched (memory reads refuse them) until the SAME bundle is imported again to completion (importMemoryBundle; adjudication is refused on import latches)`
|
|
927
|
+
: `memory lineage transaction ${u.txnId} is unsettled (a crash landed between commit and its durable credential) — ${u.entryIds.length} entr${u.entryIds.length === 1 ? "y is" : "ies are"} latched (memory reads refuse them) until the host adjudicates it (adjudicatePendingLineage)`);
|
|
826
928
|
}
|
|
827
929
|
if (pollutedReason !== undefined && lineageSessionId !== undefined) {
|
|
828
930
|
const rec2 = this.sessionPollution(lineageSessionId);
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import { type TransferEvidence } from "./file-backend.js";
|
|
2
|
+
import type { MemoryEntry } from "./types.js";
|
|
3
|
+
/** One exported challenge event (unresolved generations only). Generations are DESTINATION-minted:
|
|
4
|
+
* the bundle deliberately carries no generation number — the importing store re-mints events under
|
|
5
|
+
* its own idempotent namespace and allocates generations there. */
|
|
6
|
+
export interface BundleChallengeRow {
|
|
7
|
+
eventId: string;
|
|
8
|
+
entryId: string;
|
|
9
|
+
reason: string;
|
|
10
|
+
at: number;
|
|
11
|
+
challengedRev?: string;
|
|
12
|
+
}
|
|
13
|
+
/** One exported durable pollution marker (verbatim source record). */
|
|
14
|
+
export interface BundlePollutedSession {
|
|
15
|
+
sessionId: string;
|
|
16
|
+
at: number;
|
|
17
|
+
reason: string;
|
|
18
|
+
}
|
|
19
|
+
/** One exported committed lineage contribution (verbatim `lastRev`/`lastAt`). */
|
|
20
|
+
export interface BundleLineageRow {
|
|
21
|
+
entryId: string;
|
|
22
|
+
sessionId: string;
|
|
23
|
+
lastRev: string;
|
|
24
|
+
lastAt: number;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* design/178 v2 §4.3 + v2-c §6 — the governance-complete export bundle. The governance section is
|
|
28
|
+
* STRUCTURALLY REQUIRED (a bundle without it is a bad package — there is no "entries-only import"
|
|
29
|
+
* escape hatch: an optional hatch is a laundering channel, and the fixed section-hash key set makes
|
|
30
|
+
* a stripped section fail integrity outright). `doc` is human-readable disclosure text OUTSIDE the
|
|
31
|
+
* hashed sections (documentation, not governance data); unknown root keys are read-tolerated and
|
|
32
|
+
* never re-export-preserved (a re-export always mints a fresh bundle from store state).
|
|
33
|
+
*/
|
|
34
|
+
export interface MemoryExportBundle {
|
|
35
|
+
/** Envelope version — a reader meeting `bundle > 1` must refuse, never reinterpret. */
|
|
36
|
+
bundle: 1;
|
|
37
|
+
at: number;
|
|
38
|
+
scopes: string[];
|
|
39
|
+
/** The source store's durable identity (control-plane `store-identity.json`, minted at the first
|
|
40
|
+
* export) — the import side's `origin` stamp for custody rows that carry none. */
|
|
41
|
+
storeId: string;
|
|
42
|
+
/** Full-fidelity entries (provenance/trust/extra verbatim). A duplicate id is a bad package. */
|
|
43
|
+
entries: MemoryEntry[];
|
|
44
|
+
governance: {
|
|
45
|
+
challenges: BundleChallengeRow[];
|
|
46
|
+
pollutedSessions: BundlePollutedSession[];
|
|
47
|
+
lineage: BundleLineageRow[];
|
|
48
|
+
/** Evidence-chain rows sliced to the exported scopes (v2-c §2 table): open form — a consumer
|
|
49
|
+
* tolerates unknown keys; unknown CHANNELS never leave the exporter (they refuse the export). */
|
|
50
|
+
custody: TransferEvidence[];
|
|
51
|
+
};
|
|
52
|
+
/** Honest enumeration of what the bundle does NOT carry. The `unbound`/`pendingLatch` name lists
|
|
53
|
+
* are populated on a FULL-STORE export only (one trust domain); a subset export ships empty
|
|
54
|
+
* lists and the counts (an id list would leak another tenant's identifiers). The counts are
|
|
55
|
+
* always present and always equal the honest total, so a consumer reads ONE seat either way. */
|
|
56
|
+
residuals: {
|
|
57
|
+
quarantined: string[];
|
|
58
|
+
unbound: string[];
|
|
59
|
+
pendingLatch: string[];
|
|
60
|
+
contentUnavailable: Array<{
|
|
61
|
+
id: string;
|
|
62
|
+
scope: string;
|
|
63
|
+
slug: string;
|
|
64
|
+
rev: string;
|
|
65
|
+
reason: string;
|
|
66
|
+
}>;
|
|
67
|
+
quarantineOpaque: number;
|
|
68
|
+
/** Custody rows a SUBSET export could not slice (no scope to slice by — unbound-deletion
|
|
69
|
+
* history among them): the anti-resurrection guarantee for those ids does not travel. */
|
|
70
|
+
unsliceableCustody: number;
|
|
71
|
+
unboundCount: number;
|
|
72
|
+
pendingLatchCount: number;
|
|
73
|
+
};
|
|
74
|
+
/** Accident detection, not forgery-proofing (see module note). `sectionHashes` carries EXACTLY
|
|
75
|
+
* the four fixed keys (meta/entries/governance/residuals); `revs` and `entries` are mutually
|
|
76
|
+
* pointing (same id set, verbatim revs, `entryCount` ≡ both cardinalities). */
|
|
77
|
+
integrity: {
|
|
78
|
+
entryCount: number;
|
|
79
|
+
revs: Record<string, string>;
|
|
80
|
+
sectionHashes: Record<string, string>;
|
|
81
|
+
};
|
|
82
|
+
/** Human-readable disclosure sentences (free-string carriage, subset residuals). Outside the
|
|
83
|
+
* hashed sections by design — documentation, not governance data. */
|
|
84
|
+
doc: string[];
|
|
85
|
+
}
|
|
86
|
+
/** design/178 v2-c §7 — the import answer (the parent-design draft fields plus the v2-c report
|
|
87
|
+
* seats). Every negative disposition is REPORTED, never silent. */
|
|
88
|
+
export interface MemoryImportReport {
|
|
89
|
+
v: 1;
|
|
90
|
+
/** The bundle identity (sha256 over the canonical section-hash map) — the latch/remint salt. */
|
|
91
|
+
bundleHash: string;
|
|
92
|
+
sourceStoreId: string;
|
|
93
|
+
landed: Array<{
|
|
94
|
+
id: string;
|
|
95
|
+
scope: string;
|
|
96
|
+
slug: string;
|
|
97
|
+
}>;
|
|
98
|
+
/** id+rev+scope three-way idempotent skips (slug tolerates the deterministic `-n` suffix). */
|
|
99
|
+
alreadyPresent: string[];
|
|
100
|
+
/** The destination chain carries a delete row for the id and no live row survives — resurrection
|
|
101
|
+
* refused (no override; a legitimately re-created id has a live row and takes the ordinary
|
|
102
|
+
* CAS/conflict path instead). */
|
|
103
|
+
refusedErased: string[];
|
|
104
|
+
/** Fresh add of a `repo_file`-provenance entry with no `trust` marker — whitewash refused. */
|
|
105
|
+
refusedUntrusted: string[];
|
|
106
|
+
conflicts: Array<{
|
|
107
|
+
id: string;
|
|
108
|
+
reason: string;
|
|
109
|
+
}>;
|
|
110
|
+
/** Entry-attached governance (challenges/lineage) withheld because its entry neither landed nor
|
|
111
|
+
* matched already-present-at-the-same-rev — misattribution is worse than omission. */
|
|
112
|
+
governanceWithheld: string[];
|
|
113
|
+
/** Source chain carries a delete row while the destination holds a LIVE entry for the id: named
|
|
114
|
+
* for deployment adjudication, never deleted on the import's behalf. */
|
|
115
|
+
erasedAtSource: string[];
|
|
116
|
+
pollutionDivergence: Array<{
|
|
117
|
+
sessionId: string;
|
|
118
|
+
kept: "destination";
|
|
119
|
+
}>;
|
|
120
|
+
/** Governance rows pointing at ids outside entries ∪ the destination account (custody rows are
|
|
121
|
+
* exempt — an absent-id delete row is exactly the anti-resurrection payload). */
|
|
122
|
+
referentialOrphans: string[];
|
|
123
|
+
lineageDivergence: Array<{
|
|
124
|
+
entryId: string;
|
|
125
|
+
sessionId: string;
|
|
126
|
+
kept: "destination";
|
|
127
|
+
}>;
|
|
128
|
+
/** Custody rows withheld unappended: unknown channel, or a known-channel row failing the store's
|
|
129
|
+
* own validation — reported per row, never a whole-package refusal (a newer exporter must not
|
|
130
|
+
* brick an older importer) and never a verbatim append (an unvalidatable row would poison the
|
|
131
|
+
* fail-closed chain). `srcEv` is the row's source identity (its own `srcEv` when it carries one,
|
|
132
|
+
* else its `ev`). */
|
|
133
|
+
custodyWithheld: Array<{
|
|
134
|
+
srcEv: string;
|
|
135
|
+
channel: string;
|
|
136
|
+
}>;
|
|
137
|
+
/** Custody rows submitted to the destination chain under this bundle's namespace (idempotent
|
|
138
|
+
* re-imports answer the same number — the reconciliation handle). */
|
|
139
|
+
custodyAppended: number;
|
|
140
|
+
}
|
|
141
|
+
/** What the engine hands the File backend's `importBundleCommit` face after the pure validation
|
|
142
|
+
* pass: the bundle's governed content plus its computed identity. Everything here is inert plain
|
|
143
|
+
* data (the engine clones the caller's bundle through a JSON round-trip before validating). */
|
|
144
|
+
export interface MemoryBundleImportPlan {
|
|
145
|
+
bundleHash: string;
|
|
146
|
+
sourceStoreId: string;
|
|
147
|
+
entries: MemoryEntry[];
|
|
148
|
+
challenges: BundleChallengeRow[];
|
|
149
|
+
pollutedSessions: BundlePollutedSession[];
|
|
150
|
+
lineage: BundleLineageRow[];
|
|
151
|
+
/** Open custody rows (byte-equal duplicates already collapsed by validation). */
|
|
152
|
+
custody: Array<Record<string, unknown>>;
|
|
153
|
+
}
|
|
154
|
+
/** The four fixed integrity section keys (§6.3) — missing OR extra keys are a bad package. */
|
|
155
|
+
export declare const BUNDLE_SECTION_KEYS: readonly ["meta", "entries", "governance", "residuals"];
|
|
156
|
+
/** The canonical per-section hashes of a bundle's four sections (meta = the bundle/at/scopes/
|
|
157
|
+
* storeId scalars). Canonical JSON (recursively sorted keys) + LF, sha256 hex. */
|
|
158
|
+
export declare function computeBundleSectionHashes(bundle: Pick<MemoryExportBundle, "bundle" | "at" | "scopes" | "storeId" | "entries" | "governance" | "residuals">): Record<string, string>;
|
|
159
|
+
/**
|
|
160
|
+
* The bundle IDENTITY: sha256 over the canonical JSON of `integrity.sectionHashes` (full 64-hex —
|
|
161
|
+
* the import latch id, the challenge/custody remint salt, and the report echo all share it). The
|
|
162
|
+
* section hashes already seal the section contents, so their sorted map IS the package identity;
|
|
163
|
+
* a tampered package fails integrity before its hash ever matters.
|
|
164
|
+
*/
|
|
165
|
+
export declare function computeMemoryBundleHash(sectionHashes: Record<string, string>): string;
|
|
166
|
+
/**
|
|
167
|
+
* design/178 v2-c §1-2① — the PURE bundle validation (integrity/structure/self-consistency; zero
|
|
168
|
+
* store reads, zero side effects). Returns the FIRST violation, or undefined (valid). Every
|
|
169
|
+
* violation is a whole-package refusal at the caller — nothing lands, no latch is set.
|
|
170
|
+
*/
|
|
171
|
+
export declare function memoryBundleInvalid(raw: unknown, opts?: {
|
|
172
|
+
expectedScopes?: readonly string[];
|
|
173
|
+
}): string | undefined;
|
|
174
|
+
/** What the exporter feeds {@link assembleMemoryExportBundle} — the store-consistent snapshot plus
|
|
175
|
+
* the already-sliced governance pieces (pure data; the snapshot fence lives in the backend). */
|
|
176
|
+
export interface MemoryExportBundleInput {
|
|
177
|
+
at: number;
|
|
178
|
+
scopes: string[];
|
|
179
|
+
storeId: string;
|
|
180
|
+
entries: MemoryEntry[];
|
|
181
|
+
challenges: BundleChallengeRow[];
|
|
182
|
+
pollutedSessions: BundlePollutedSession[];
|
|
183
|
+
lineage: BundleLineageRow[];
|
|
184
|
+
custody: TransferEvidence[];
|
|
185
|
+
residuals: MemoryExportBundle["residuals"];
|
|
186
|
+
}
|
|
187
|
+
/** The C-1 disclosure sentence (recommended-arm ruling posture: free strings ride verbatim and the
|
|
188
|
+
* bundle SAYS so — coding them away would destroy the identity/attribution keys idempotency and
|
|
189
|
+
* the remint namespaces depend on). */
|
|
190
|
+
export declare const BUNDLE_FREE_TEXT_DISCLOSURE = "Free-string fields in this bundle (pollution/challenge reasons, challenge event ids, custody request ids, session ids) are carried verbatim and may contain text the exporting deployment put there; subset-export tenant isolation does not extend to those literals.";
|
|
191
|
+
/** Assemble the bundle: deterministic ordering, section hashes, disclosure doc. Pure. */
|
|
192
|
+
export declare function assembleMemoryExportBundle(input: MemoryExportBundleInput): MemoryExportBundle;
|