@sema-agent/core 5.46.0 → 5.48.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 +93 -0
- package/dist/agents/agent-transcript-tool.d.ts +4 -0
- package/dist/agents/agent-transcript-tool.js +10 -3
- package/dist/agents/send-message-tool.d.ts +43 -1
- package/dist/agents/send-message-tool.js +50 -11
- package/dist/agents/subagent.d.ts +18 -0
- package/dist/agents/subagent.js +231 -4
- package/dist/config/defaults.d.ts +20 -0
- package/dist/config/defaults.js +5 -0
- package/dist/core/background-agent-store.d.ts +1 -0
- package/dist/core/background-agent-store.js +13 -0
- package/dist/core/governance-codes.d.ts +13 -0
- package/dist/core/governance-codes.js +33 -0
- package/dist/core/mcp.d.ts +6 -1
- package/dist/core/mcp.js +34 -7
- package/dist/core/memory-engine/delegation-settlement.d.ts +318 -0
- package/dist/core/memory-engine/delegation-settlement.js +661 -0
- package/dist/core/memory-engine/engine.d.ts +159 -1
- package/dist/core/memory-engine/engine.js +699 -15
- package/dist/core/memory-engine/file-backend.d.ts +1 -0
- package/dist/core/memory-engine/file-backend.js +3 -1
- package/dist/core/memory-engine/frontmatter.d.ts +46 -19
- package/dist/core/memory-engine/frontmatter.js +91 -77
- package/dist/core/memory-engine/index.d.ts +4 -3
- package/dist/core/memory-engine/index.js +3 -2
- package/dist/core/memory-engine/layout.d.ts +14 -0
- package/dist/core/memory-engine/layout.js +2 -2
- package/dist/core/memory-engine/memory-backend-contract.js +43 -0
- package/dist/core/memory-engine/origin-clearance.d.ts +66 -0
- package/dist/core/memory-engine/origin-clearance.js +84 -0
- package/dist/core/memory-engine/provenance-wording.d.ts +50 -0
- package/dist/core/memory-engine/provenance-wording.js +15 -0
- package/dist/core/memory-engine/tools.d.ts +61 -7
- package/dist/core/memory-engine/tools.js +34 -9
- package/dist/core/memory-engine/types.d.ts +70 -2
- package/dist/core/reminder-disclosure.d.ts +90 -0
- package/dist/core/reminder-disclosure.js +64 -0
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +6 -0
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -1
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
- package/dist/core/runner/prepare-hands-readface.js +1 -0
- package/dist/core/runner/prepare-memory.js +50 -15
- package/dist/core/runner/prepare-task.d.ts +39 -0
- package/dist/core/runner/prepare-task.js +128 -40
- package/dist/core/runner/runtask.js +3 -1
- package/dist/core/session-reconcile.js +3 -2
- package/dist/core/session-store.d.ts +59 -1
- package/dist/core/session-store.js +82 -14
- package/dist/core/session.d.ts +83 -1
- package/dist/core/task-registry-agent.d.ts +28 -0
- package/dist/core/task-registry-agent.js +63 -2
- package/dist/core/task-registry.d.ts +21 -0
- package/dist/core/task-registry.js +4 -1
- package/dist/core/types.d.ts +98 -3
- package/dist/core/types.js +3 -0
- package/dist/core/untrusted-text.d.ts +63 -0
- package/dist/core/untrusted-text.js +48 -0
- package/dist/core/wiring-manifest.d.ts +35 -0
- package/dist/core/wiring-manifest.js +21 -1
- package/dist/engine/harness/types.d.ts +36 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +6 -4
- package/dist/internal/harness-types.d.ts +1 -0
- package/dist/stores/file/index.d.ts +19 -3
- package/dist/stores/file/index.js +24 -1
- package/dist/stores/file/session-store.d.ts +18 -4
- package/dist/stores/file/session-store.js +73 -12
- package/dist/tools/fs/fs-pdf.d.ts +12 -1
- package/dist/tools/fs/fs-pdf.js +17 -3
- package/dist/tools/fs/fs-read.d.ts +2 -1
- package/dist/tools/fs/fs-read.js +33 -5
- package/dist/tools/fs/fs-shared.d.ts +6 -2
- package/dist/tools/fs/index.d.ts +7 -0
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/task-list.d.ts +5 -1
- package/dist/tools/web.js +21 -2
- package/package.json +3 -2
- package/test/export-surface.snapshot.json +24 -2
|
@@ -900,6 +900,7 @@ export declare class FileMemoryEngineBackend implements MemoryBackend {
|
|
|
900
900
|
/** Lexical floor (design/138 §2.1): `1 - Jaccard` distance over name+description+body, ascending. */
|
|
901
901
|
search(query: string, scopes: readonly string[], opts?: {
|
|
902
902
|
limit?: number;
|
|
903
|
+
exposureBands?: boolean;
|
|
903
904
|
}): Promise<ScoredMemoryEntry[]>;
|
|
904
905
|
private searchFrom;
|
|
905
906
|
/**
|
|
@@ -2135,7 +2135,8 @@ export class FileMemoryEngineBackend {
|
|
|
2135
2135
|
scored.push({ ...headerOf(e, join(dir, `${e.slug}.md`)), score: d });
|
|
2136
2136
|
}
|
|
2137
2137
|
}
|
|
2138
|
-
|
|
2138
|
+
const band = opts?.exposureBands === true ? (h) => (h.exposure === "external" ? 1 : 0) : () => 0;
|
|
2139
|
+
scored.sort((a, b) => band(a) - band(b) || a.score - b.score || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0));
|
|
2139
2140
|
return scored.slice(0, limit);
|
|
2140
2141
|
}
|
|
2141
2142
|
async applyPatches(patches) {
|
|
@@ -3694,6 +3695,7 @@ function headerOf(e, path) {
|
|
|
3694
3695
|
mtimeMs,
|
|
3695
3696
|
rev: e.rev,
|
|
3696
3697
|
sizeBytes: Buffer.byteLength(serializeEntryFile(e), "utf8"),
|
|
3698
|
+
...(committedOriginOf(e.frontmatter) !== undefined ? { exposure: "external" } : {}),
|
|
3697
3699
|
};
|
|
3698
3700
|
}
|
|
3699
3701
|
function readSafe(path) {
|
|
@@ -27,32 +27,59 @@ export declare function computeEntryRev(entry: Pick<MemoryEntry, "id" | "frontma
|
|
|
27
27
|
/** True ⇔ `extra` carries origin-form bytes (an invalid/foreign origin block preserved verbatim,
|
|
28
28
|
* or an origin block a pre-336 vintage folded into `extra`). */
|
|
29
29
|
export declare function hasOriginFormExtra(fm: Pick<MemoryEntryFrontmatter, "extra">): boolean;
|
|
30
|
+
/**
|
|
31
|
+
* design/336 slice 2 — the ONE serialization-closed tokenization of `extra`'s origin-form bytes
|
|
32
|
+
* (the adversarial round-3 deep fix; every predicate below reads THROUGH this walk, never through
|
|
33
|
+
* a private re-implementation). A CARRIER is one independent origin representation:
|
|
34
|
+
* - a bare block header (`origin:` — judged on the TRIMMED line, so indented spellings count),
|
|
35
|
+
* together with its indented continuation lines: ONE carrier, normalized field-by-field
|
|
36
|
+
* (first-wins per key; unknown sub-lines tolerated — an in-block `origin: …` scalar is part of
|
|
37
|
+
* THIS carrier, never its own);
|
|
38
|
+
* - a scalar `origin: …` line OUTSIDE any block: ONE carrier, always the sentinel (a scalar names
|
|
39
|
+
* no fields).
|
|
40
|
+
* Per-carrier normalization is INDEPENDENT on purpose (adversarial round 2: a cross-carrier
|
|
41
|
+
* accumulation let two individually-invalid blocks combine into a valid marker, and parse-time
|
|
42
|
+
* block reordering then changed which fields won): a carrier whose own fields form a valid origin
|
|
43
|
+
* (taint external, `at` present AND serialization-stable, cause absent-or-known) normalizes to
|
|
44
|
+
* that value; anything else normalizes to the `{ taint: "external", at: 0 }` sentinel (cause
|
|
45
|
+
* absent, `at` = 0 — a clock here would make engine and backend normalize to different values and
|
|
46
|
+
* the engine's own patches would never converge). Marker-form bytes are NEVER dropped and NEVER
|
|
47
|
+
* read as clean absence.
|
|
48
|
+
*/
|
|
49
|
+
export declare function extraOriginCarriers(extra: readonly string[] | undefined): MemoryEntryOrigin[];
|
|
50
|
+
/**
|
|
51
|
+
* design/336 §2.2 — the ONE reading of an entry's committed external-origin marker: the typed
|
|
52
|
+
* field when present, else the first `extra` carrier's deterministic normalization
|
|
53
|
+
* ({@link extraOriginCarriers}). Returns `undefined` ⇔ the entry carries no marker in any form.
|
|
54
|
+
* The first-carrier choice is immaterial on every committable shape: a representation whose
|
|
55
|
+
* carriers DISAGREE is refused at every inbound boundary ({@link ambiguousOriginRepresentation}),
|
|
56
|
+
* so a committed multi-carrier shape always answers the one agreed value.
|
|
57
|
+
*/
|
|
30
58
|
export declare function committedOriginOf(fm: Pick<MemoryEntryFrontmatter, "origin" | "extra">): MemoryEntryOrigin | undefined;
|
|
31
59
|
/** Deep equality over the origin marker (every member, absence included) — the immutability law's
|
|
32
60
|
* comparator: a downgraded cause or a rewritten `at` is as refused as a strip. */
|
|
33
61
|
export declare function originEquals(a: MemoryEntryOrigin | undefined, b: MemoryEntryOrigin | undefined): boolean;
|
|
34
62
|
/**
|
|
35
|
-
* design/336 (adversarial rounds 1
|
|
63
|
+
* design/336 (adversarial rounds 1-3) — the AMBIGUOUS origin-representation predicate, ONE law for
|
|
36
64
|
* every inbound boundary (backend patch law, sync wire, bundle validation, out-of-band adoption):
|
|
37
|
-
* a
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* old committed forms never bricks. The model-writable face never reaches this predicate — the
|
|
45
|
-
* harvest strips every origin form and re-judges (§2.3-1).
|
|
65
|
+
* a representation is refused ⇔ its carriers DISAGREE — any two of {the typed field, each `extra`
|
|
66
|
+
* carrier's independent normalization ({@link extraOriginCarriers})} differ on any member — or the
|
|
67
|
+
* typed `at` is not serialization-stable (a value that cannot survive its own serialization is a
|
|
68
|
+
* representation conflict with the entry's next parse). Covered vehicles: typed A + disagreeing
|
|
69
|
+
* extra block B (round 1 — the typed member satisfies the immutability comparator while B rides
|
|
70
|
+
* into storage and a reparse answers B); disagreeing extra-only blocks (round 2 — cross-block
|
|
71
|
+
* field combination plus parse-time block reordering rewrite members with no typed origin at all).
|
|
46
72
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
73
|
+
* The refusal boundary keys on DISAGREEMENT, not on carrier count (slice-2 serialization closure,
|
|
74
|
+
* the round-3 deep fix): agreeing carriers are never a member-rewrite vehicle — refusing them by
|
|
75
|
+
* count alone made the verdict flip across a serialize/parse cycle (a promotion reseats one
|
|
76
|
+
* carrier without changing any value). This generalizes the old "typed beside EXACTLY ONE equal
|
|
77
|
+
* extra form" legacy-carriage exception: N agreeing representations are that same carriage, so
|
|
78
|
+
* carry-forward of old committed forms never bricks. The model-writable face never reaches this
|
|
79
|
+
* predicate — the harvest strips every origin form and re-judges (§2.3-1). Together with the
|
|
80
|
+
* pristine-only typed seating (parse) and the carrier-canonical rev ({@link computeEntryRev}),
|
|
81
|
+
* the accepted set is SERIALIZATION-CLOSED: carrier count, normalized origin, ambiguity verdict
|
|
82
|
+
* and rev are all invariant across serialize/parse at every boundary (property-tested).
|
|
56
83
|
*/
|
|
57
84
|
export declare function ambiguousOriginRepresentation(fm: Pick<MemoryEntryFrontmatter, "origin" | "extra">): boolean;
|
|
58
85
|
/**
|
|
@@ -5,6 +5,10 @@ const ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{7,63}$/;
|
|
|
5
5
|
export function isValidEntryId(value) {
|
|
6
6
|
return ID_RE.test(value);
|
|
7
7
|
}
|
|
8
|
+
const ORIGIN_AT_RE = /^-?\d+(\.\d+)?$/;
|
|
9
|
+
function atSerializationStable(at) {
|
|
10
|
+
return Number.isFinite(at) && ORIGIN_AT_RE.test(String(at));
|
|
11
|
+
}
|
|
8
12
|
export function parseEntryFile(text) {
|
|
9
13
|
const fm = {};
|
|
10
14
|
if (!text.startsWith(`${FM_FENCE}\n`) && text.trimStart() !== FM_FENCE) {
|
|
@@ -31,7 +35,7 @@ export function parseEntryFile(text) {
|
|
|
31
35
|
const prov = {};
|
|
32
36
|
let inOrigin = false;
|
|
33
37
|
const originRaw = [];
|
|
34
|
-
|
|
38
|
+
let originPristine = true;
|
|
35
39
|
const orig = {};
|
|
36
40
|
for (let i = 1; i < end; i++) {
|
|
37
41
|
const line = lines[i] ?? "";
|
|
@@ -83,17 +87,17 @@ export function parseEntryFile(text) {
|
|
|
83
87
|
const m = /^\s+([A-Za-z][A-Za-z0-9_-]*):\s*(.*?)\s*$/.exec(line);
|
|
84
88
|
if (m) {
|
|
85
89
|
const [, k = "", v = ""] = m;
|
|
86
|
-
if (k === "taint")
|
|
90
|
+
if (k === "taint" && orig.taint === undefined)
|
|
87
91
|
orig.taint = v;
|
|
88
|
-
else if (k === "cause")
|
|
92
|
+
else if (k === "cause" && orig.cause === undefined)
|
|
89
93
|
orig.cause = v;
|
|
90
|
-
else if (k === "at")
|
|
91
|
-
orig.at =
|
|
94
|
+
else if (k === "at" && orig.at === undefined)
|
|
95
|
+
orig.at = ORIGIN_AT_RE.test(v) ? Number(v) : Number.NaN;
|
|
92
96
|
else
|
|
93
|
-
|
|
97
|
+
originPristine = false;
|
|
94
98
|
}
|
|
95
99
|
else {
|
|
96
|
-
|
|
100
|
+
originPristine = false;
|
|
97
101
|
}
|
|
98
102
|
continue;
|
|
99
103
|
}
|
|
@@ -157,9 +161,12 @@ export function parseEntryFile(text) {
|
|
|
157
161
|
}
|
|
158
162
|
}
|
|
159
163
|
if (originRaw.length > 0) {
|
|
160
|
-
if (
|
|
164
|
+
if (originPristine &&
|
|
165
|
+
orig.taint === "external" &&
|
|
166
|
+
typeof orig.at === "number" &&
|
|
167
|
+
atSerializationStable(orig.at) &&
|
|
168
|
+
(orig.cause === undefined || MEMORY_ORIGIN_CAUSES.includes(orig.cause))) {
|
|
161
169
|
fm.origin = { taint: "external", ...(orig.cause !== undefined ? { cause: orig.cause } : {}), at: orig.at };
|
|
162
|
-
extra.push(...originUnknown);
|
|
163
170
|
}
|
|
164
171
|
else {
|
|
165
172
|
extra.push(...originRaw);
|
|
@@ -188,29 +195,31 @@ export function serializeEntryFile(entry) {
|
|
|
188
195
|
}
|
|
189
196
|
if (fm.trust !== undefined)
|
|
190
197
|
lines.push(`trust: ${fm.trust}`);
|
|
198
|
+
if (fm.extra)
|
|
199
|
+
lines.push(...fm.extra);
|
|
191
200
|
if (fm.origin !== undefined) {
|
|
192
201
|
lines.push("origin:", ` taint: ${fm.origin.taint}`, ...(fm.origin.cause !== undefined ? [` cause: ${fm.origin.cause}`] : []), ` at: ${fm.origin.at}`);
|
|
193
202
|
}
|
|
194
|
-
if (fm.extra)
|
|
195
|
-
lines.push(...fm.extra);
|
|
196
203
|
lines.push(FM_FENCE, "");
|
|
197
204
|
const body = entry.body.replace(/\s+$/, "");
|
|
198
205
|
return `${lines.join("\n")}${body}${body ? "\n" : ""}`;
|
|
199
206
|
}
|
|
200
207
|
export function computeEntryRev(entry) {
|
|
201
208
|
const fm = entry.frontmatter;
|
|
209
|
+
const hasCarrierExtra = fm.extra !== undefined && fm.extra.some((line) => ORIGIN_FORM_LINE_RE.test(line));
|
|
210
|
+
const canonicalOrigin = fm.origin !== undefined ? fm.origin : hasCarrierExtra ? committedOriginOf(fm) : undefined;
|
|
202
211
|
const canonical = JSON.stringify([
|
|
203
212
|
entry.id,
|
|
204
213
|
fm.name ?? null,
|
|
205
214
|
fm.description ?? null,
|
|
206
215
|
fm.type ?? null,
|
|
207
216
|
fm.deleted === true,
|
|
208
|
-
fm.extra ?? [],
|
|
217
|
+
hasCarrierExtra ? partitionOriginLines(fm.extra).kept : (fm.extra ?? []),
|
|
209
218
|
entry.body.replace(/\s+$/, ""),
|
|
210
219
|
...(fm.provenance !== undefined || fm.trust !== undefined
|
|
211
220
|
? [fm.trust ?? null, fm.provenance !== undefined ? [fm.provenance.kind, fm.provenance.path, fm.provenance.contentHash, fm.provenance.ingestedAt] : null]
|
|
212
221
|
: []),
|
|
213
|
-
...(
|
|
222
|
+
...(canonicalOrigin !== undefined ? [["origin", canonicalOrigin.taint, canonicalOrigin.cause ?? null, canonicalOrigin.at]] : []),
|
|
214
223
|
]);
|
|
215
224
|
return createHash("sha256").update(canonical, "utf8").digest("hex").slice(0, 16);
|
|
216
225
|
}
|
|
@@ -218,55 +227,53 @@ const ORIGIN_FORM_LINE_RE = /^\s*origin\s*:/;
|
|
|
218
227
|
export function hasOriginFormExtra(fm) {
|
|
219
228
|
return fm.extra !== undefined && fm.extra.some((line) => ORIGIN_FORM_LINE_RE.test(line));
|
|
220
229
|
}
|
|
221
|
-
function
|
|
230
|
+
export function extraOriginCarriers(extra) {
|
|
222
231
|
if (extra === undefined)
|
|
223
|
-
return
|
|
224
|
-
|
|
225
|
-
let
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
232
|
+
return [];
|
|
233
|
+
const carriers = [];
|
|
234
|
+
let block;
|
|
235
|
+
const closeBlock = () => {
|
|
236
|
+
if (block === undefined)
|
|
237
|
+
return;
|
|
238
|
+
const { taint, cause, at } = block;
|
|
239
|
+
block = undefined;
|
|
240
|
+
if (taint === "external" && typeof at === "number" && atSerializationStable(at) && (cause === undefined || MEMORY_ORIGIN_CAUSES.includes(cause))) {
|
|
241
|
+
carriers.push({ taint: "external", ...(cause !== undefined ? { cause: cause } : {}), at });
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
carriers.push({ taint: "external", at: 0 });
|
|
245
|
+
}
|
|
246
|
+
};
|
|
230
247
|
for (const line of extra) {
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
inBlock = true;
|
|
235
|
-
blockIndex += 1;
|
|
248
|
+
if (/^\s*origin\s*:\s*$/.test(line)) {
|
|
249
|
+
closeBlock();
|
|
250
|
+
block = {};
|
|
236
251
|
continue;
|
|
237
252
|
}
|
|
238
|
-
if (
|
|
239
|
-
|
|
253
|
+
if (block !== undefined && /^\s/.test(line)) {
|
|
254
|
+
const m = /^\s+([A-Za-z][A-Za-z0-9_-]*):\s*(.*?)\s*$/.exec(line);
|
|
255
|
+
if (m !== null) {
|
|
256
|
+
const [, k = "", v = ""] = m;
|
|
257
|
+
if (k === "taint" && block.taint === undefined)
|
|
258
|
+
block.taint = v;
|
|
259
|
+
else if (k === "cause" && block.cause === undefined)
|
|
260
|
+
block.cause = v;
|
|
261
|
+
else if (k === "at" && block.at === undefined)
|
|
262
|
+
block.at = ORIGIN_AT_RE.test(v) ? Number(v) : Number.NaN;
|
|
263
|
+
}
|
|
240
264
|
continue;
|
|
241
265
|
}
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
continue;
|
|
246
|
-
const m = /^\s+([A-Za-z][A-Za-z0-9_-]*):\s*(.*?)\s*$/.exec(line);
|
|
247
|
-
if (m === null)
|
|
248
|
-
continue;
|
|
249
|
-
const [, k = "", v = ""] = m;
|
|
250
|
-
if (k === "taint" && taint === undefined)
|
|
251
|
-
taint = v;
|
|
252
|
-
else if (k === "cause" && cause === undefined)
|
|
253
|
-
cause = v;
|
|
254
|
-
else if (k === "at" && at === undefined)
|
|
255
|
-
at = /^-?\d+(\.\d+)?$/.test(v) ? Number(v) : Number.NaN;
|
|
266
|
+
closeBlock();
|
|
267
|
+
if (ORIGIN_FORM_LINE_RE.test(line))
|
|
268
|
+
carriers.push({ taint: "external", at: 0 });
|
|
256
269
|
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
if (taint === "external" && typeof at === "number" && Number.isFinite(at) && (cause === undefined || MEMORY_ORIGIN_CAUSES.includes(cause))) {
|
|
260
|
-
return { carriers, first: { taint: "external", ...(cause !== undefined ? { cause: cause } : {}), at } };
|
|
261
|
-
}
|
|
262
|
-
return { carriers, first: { taint: "external", at: 0 } };
|
|
270
|
+
closeBlock();
|
|
271
|
+
return carriers;
|
|
263
272
|
}
|
|
264
273
|
export function committedOriginOf(fm) {
|
|
265
274
|
if (fm.origin !== undefined)
|
|
266
275
|
return { taint: fm.origin.taint, ...(fm.origin.cause !== undefined ? { cause: fm.origin.cause } : {}), at: fm.origin.at };
|
|
267
|
-
|
|
268
|
-
return undefined;
|
|
269
|
-
return walkExtraOrigin(fm.extra).first ?? { taint: "external", at: 0 };
|
|
276
|
+
return extraOriginCarriers(fm.extra)[0];
|
|
270
277
|
}
|
|
271
278
|
export function originEquals(a, b) {
|
|
272
279
|
if (a === undefined || b === undefined)
|
|
@@ -274,13 +281,31 @@ export function originEquals(a, b) {
|
|
|
274
281
|
return a.taint === b.taint && a.cause === b.cause && a.at === b.at;
|
|
275
282
|
}
|
|
276
283
|
export function ambiguousOriginRepresentation(fm) {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
+
if (fm.origin !== undefined && !atSerializationStable(fm.origin.at))
|
|
285
|
+
return true;
|
|
286
|
+
const values = extraOriginCarriers(fm.extra);
|
|
287
|
+
if (fm.origin !== undefined)
|
|
288
|
+
values.push({ taint: fm.origin.taint, ...(fm.origin.cause !== undefined ? { cause: fm.origin.cause } : {}), at: fm.origin.at });
|
|
289
|
+
return values.some((v) => !originEquals(v, values[0]));
|
|
290
|
+
}
|
|
291
|
+
function partitionOriginLines(extra) {
|
|
292
|
+
const kept = [];
|
|
293
|
+
let removedAny = false;
|
|
294
|
+
let inBlock = false;
|
|
295
|
+
for (const line of extra) {
|
|
296
|
+
if (ORIGIN_FORM_LINE_RE.test(line)) {
|
|
297
|
+
inBlock = /^\s*origin\s*:\s*$/.test(line);
|
|
298
|
+
removedAny = true;
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (inBlock && /^\s/.test(line)) {
|
|
302
|
+
removedAny = true;
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
inBlock = false;
|
|
306
|
+
kept.push(line);
|
|
307
|
+
}
|
|
308
|
+
return { kept, removedAny };
|
|
284
309
|
}
|
|
285
310
|
export function stripModelWrittenOrigin(fm) {
|
|
286
311
|
let stripped = false;
|
|
@@ -288,26 +313,15 @@ export function stripModelWrittenOrigin(fm) {
|
|
|
288
313
|
delete fm.origin;
|
|
289
314
|
stripped = true;
|
|
290
315
|
}
|
|
291
|
-
if (fm.extra !== undefined
|
|
292
|
-
const kept =
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
if (
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
}
|
|
300
|
-
if (inBlock && /^\s/.test(line)) {
|
|
301
|
-
stripped = true;
|
|
302
|
-
continue;
|
|
303
|
-
}
|
|
304
|
-
inBlock = false;
|
|
305
|
-
kept.push(line);
|
|
316
|
+
if (fm.extra !== undefined) {
|
|
317
|
+
const { kept, removedAny } = partitionOriginLines(fm.extra);
|
|
318
|
+
if (removedAny) {
|
|
319
|
+
stripped = true;
|
|
320
|
+
if (kept.length > 0)
|
|
321
|
+
fm.extra = kept;
|
|
322
|
+
else
|
|
323
|
+
delete fm.extra;
|
|
306
324
|
}
|
|
307
|
-
if (kept.length > 0)
|
|
308
|
-
fm.extra = kept;
|
|
309
|
-
else
|
|
310
|
-
delete fm.extra;
|
|
311
325
|
}
|
|
312
326
|
return stripped;
|
|
313
327
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
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
|
-
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type MemoryGetDetails, } from "./tools.js";
|
|
1
|
+
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, memoryRecallDisciplineSegment, entryFileHeadCarriesOrigin, 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
|
+
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type CleanMemorySearchHit, type ExposedMemorySearchHit, type MemoryGetDetails, } from "./tools.js";
|
|
3
|
+
export { MEMORY_EXPOSURE_BANNER, MEMORY_EXPOSURE_HANDLE_TAG, MEMORY_PROVENANCE_RECALL_SENTENCE, MEMORY_PROVENANCE_SEARCH_SENTENCE, memoryExposureIndexRow, parseMemoryExposureIndexRow, } from "./provenance-wording.js";
|
|
3
4
|
export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
|
|
4
5
|
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
6
|
export { computeBundleSectionHashes, computeMemoryBundleHash, memoryBundleInvalid, type MemoryExportBundle, type MemoryImportReport, type MemoryBundleImportPlan, type BundleChallengeRow, type BundleLineageRow, type BundlePollutedSession, } from "./export-bundle.js";
|
|
6
7
|
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";
|
|
7
8
|
export { readV2HeaderHints, isInstructionEntry, type V2HeaderHints } from "./header-hints.js";
|
|
8
|
-
export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, committedOriginOf, originEquals, type ParsedEntryFile } from "./frontmatter.js";
|
|
9
|
+
export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, committedOriginOf, originEquals, ambiguousOriginRepresentation, type ParsedEntryFile } from "./frontmatter.js";
|
|
9
10
|
export { MEMORY_ORIGIN_CAUSES } from "./types.js";
|
|
10
11
|
export type { MemoryBackend, MemoryEntry, MemoryEntryFrontmatter, MemoryEntryOrigin, MemoryOriginCause, MemoryEntryHeader, ScoredMemoryEntry, NotePatch, PatchReport, MaterializedFile, MemorySessionHandle, HarvestReport, HarvestRejection, HarvestRejectionCode, MemoryAnnouncement, ScanFinding, } from "./types.js";
|
|
11
12
|
export { memoryBackendContract, assertMemoryBackendSearchEquivalence, type MemoryBackendContractHooks, } from "./memory-backend-contract.js";
|
|
@@ -1,11 +1,12 @@
|
|
|
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, } from "./engine.js";
|
|
1
|
+
export { MemoryEngine, buildMemoryInstruction, truncateIndex, MEMORY_INSTRUCTION_TEMPLATE, MEMORY_RECALL_DISCIPLINE, memoryRecallDisciplineSegment, entryFileHeadCarriesOrigin, 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, } from "./engine.js";
|
|
2
2
|
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, } from "./tools.js";
|
|
3
|
+
export { MEMORY_EXPOSURE_BANNER, MEMORY_EXPOSURE_HANDLE_TAG, MEMORY_PROVENANCE_RECALL_SENTENCE, MEMORY_PROVENANCE_SEARCH_SENTENCE, memoryExposureIndexRow, parseMemoryExposureIndexRow, } from "./provenance-wording.js";
|
|
3
4
|
export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
|
|
4
5
|
export { FileMemoryEngineBackend, scanEntryFiles, MEMORY_INDEX_FILENAME, DEFAULT_MAX_ENTRY_DEPTH, erasureSelectHash, } from "./file-backend.js";
|
|
5
6
|
export { computeBundleSectionHashes, computeMemoryBundleHash, memoryBundleInvalid, } from "./export-bundle.js";
|
|
6
7
|
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";
|
|
7
8
|
export { readV2HeaderHints, isInstructionEntry } from "./header-hints.js";
|
|
8
|
-
export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, committedOriginOf, originEquals } from "./frontmatter.js";
|
|
9
|
+
export { parseEntryFile, serializeEntryFile, computeEntryRev, entryFromFile, committedOriginOf, originEquals, ambiguousOriginRepresentation } from "./frontmatter.js";
|
|
9
10
|
export { MEMORY_ORIGIN_CAUSES } from "./types.js";
|
|
10
11
|
export { memoryBackendContract, assertMemoryBackendSearchEquivalence, } from "./memory-backend-contract.js";
|
|
11
12
|
export { SCOPE_SEGMENT_MAX_ENCODED, PROJECT_MARKER_PATH, encodeScopeSegment, decodeScopeSegment, parseScopeKey, formatUserScope, formatOrgScope, formatProjScope, formatUserProjScope, isPersonalScope, assertScopeContractPlacement, formatProjectMarker, parseProjectMarker, resolveProjectId, PROJECT_ID_REGEX, } from "./scope-contract.js";
|
|
@@ -360,6 +360,20 @@ export declare const LINEAGE_AUDIT_MAX_ROWS = 2048;
|
|
|
360
360
|
/** The challenged-history account (design/180 B-3: collect-only, fail-open bookkeeping —
|
|
361
361
|
* the mechanical shadow of "user saw a retrieved entry and corrected it in the same session"). */
|
|
362
362
|
export declare const CHALLENGED_HISTORY_FILE = "usage-challenged-history.json";
|
|
363
|
+
/** The locked, journaled, FAIL-CLOSED sidecar update. `coerce` must throw
|
|
364
|
+
* {@link ControlPlaneCorruptError} on any unacceptable shape; only ENOENT reads as the empty
|
|
365
|
+
* initial state (`undefined` handed to `coerce`). Exported module-level (not on the root barrel)
|
|
366
|
+
* for the strict-account family's siblings (design/336 slice 2: the delegation-settlement /
|
|
367
|
+
* session-account / instruction-hold sidecars live in their own module but MUST be this exact
|
|
368
|
+
* lock+journal discipline — a second implementation would be a drift seam). */
|
|
369
|
+
export declare function lockedStrictUpdate<S, T>(controlDir: string, fileName: string, what: string, coerce: (raw: unknown) => S, fn: (current: S) => {
|
|
370
|
+
next?: S;
|
|
371
|
+
result: T;
|
|
372
|
+
}): T;
|
|
373
|
+
/** Lock-less strict READ (journal-aware): a parseable journal wins (committed next state); an
|
|
374
|
+
* unparseable journal, or an unreadable/unparseable file, is corrupt. Exported module-level for
|
|
375
|
+
* the strict-account family's siblings (see {@link lockedStrictUpdate}). */
|
|
376
|
+
export declare function readStrictSidecar(controlDir: string, fileName: string, what: string): unknown;
|
|
363
377
|
/** One committed contribution: session → entry, latest rev/at only (the load-bearing invariant is
|
|
364
378
|
* "which sessions contributed this id", not the per-commit history — that is the audit account). */
|
|
365
379
|
export interface LineageContribution {
|
|
@@ -894,7 +894,7 @@ function rollForwardStrictSidecar(file, journal) {
|
|
|
894
894
|
atomicWriteFileSync(file, raw);
|
|
895
895
|
rmSync(journal, { force: true });
|
|
896
896
|
}
|
|
897
|
-
function lockedStrictUpdate(controlDir, fileName, what, coerce, fn) {
|
|
897
|
+
export function lockedStrictUpdate(controlDir, fileName, what, coerce, fn) {
|
|
898
898
|
ensureDirExists(controlDir);
|
|
899
899
|
const file = join(controlDir, fileName);
|
|
900
900
|
const journal = `${file}.journal`;
|
|
@@ -934,7 +934,7 @@ function readStrictSidecarRaw(file, what) {
|
|
|
934
934
|
throw new ControlPlaneCorruptError(`${what} is unparseable: ${file}`);
|
|
935
935
|
}
|
|
936
936
|
}
|
|
937
|
-
function readStrictSidecar(controlDir, fileName, what) {
|
|
937
|
+
export function readStrictSidecar(controlDir, fileName, what) {
|
|
938
938
|
const file = join(controlDir, fileName);
|
|
939
939
|
const journal = `${file}.journal`;
|
|
940
940
|
let journalRaw;
|
|
@@ -350,6 +350,49 @@ export async function memoryBackendContract(hooks) {
|
|
|
350
350
|
const hits = await b.search("same words", ["s1"]);
|
|
351
351
|
assert.deepStrictEqual(hits.map((h) => h.id), ["id-tie-a-01", "id-tie-b-01"]);
|
|
352
352
|
});
|
|
353
|
+
defer("design/336: headers carry the exposure fact on BOTH faces (listHeaders + search), typed and extra-form marker alike; unmarked entries carry none", async () => {
|
|
354
|
+
const b = await hooks.make();
|
|
355
|
+
const marked = entry("id-exp-typed-01", "s1", "marked-typed", "same words here");
|
|
356
|
+
marked.frontmatter.origin = { taint: "external", cause: "observed", at: 1700000000600 };
|
|
357
|
+
marked.rev = computeEntryRev(marked);
|
|
358
|
+
const extraForm = entry("id-exp-extra-01", "s1", "marked-extra", "same words here");
|
|
359
|
+
extraForm.frontmatter.extra = ["origin:", " taint: external", " at: 5"];
|
|
360
|
+
extraForm.rev = computeEntryRev(extraForm);
|
|
361
|
+
const clean = entry("id-exp-clean-01", "s1", "unmarked", "same words here");
|
|
362
|
+
const rep = await b.applyPatches([
|
|
363
|
+
{ op: "add", id: marked.id, entry: marked },
|
|
364
|
+
{ op: "add", id: extraForm.id, entry: extraForm },
|
|
365
|
+
{ op: "add", id: clean.id, entry: clean },
|
|
366
|
+
]);
|
|
367
|
+
assert.deepStrictEqual(rep.conflicts, []);
|
|
368
|
+
const headers = await b.listHeaders(["s1"]);
|
|
369
|
+
assert.strictEqual(headers.find((h) => h.id === marked.id)?.exposure, "external", "typed marker ⇒ header exposure");
|
|
370
|
+
assert.strictEqual(headers.find((h) => h.id === extraForm.id)?.exposure, "external", "extra-form marker ⇒ header exposure (one normalization)");
|
|
371
|
+
assert.strictEqual(headers.find((h) => h.id === clean.id)?.exposure, undefined, "unmarked ⇒ no exposure member");
|
|
372
|
+
const hits = await b.search("same words", ["s1"]);
|
|
373
|
+
assert.strictEqual(hits.find((h) => h.id === marked.id)?.exposure, "external", "search hits carry the same fact");
|
|
374
|
+
assert.strictEqual(hits.find((h) => h.id === clean.id)?.exposure, undefined);
|
|
375
|
+
});
|
|
376
|
+
defer("design/336: search exposureBands orders unmarked-first BEFORE truncation (the limit-boundary clean entry is served, never starved); absent flag keeps the single-band contract order", async () => {
|
|
377
|
+
const b = await hooks.make();
|
|
378
|
+
const mk = (id, slug, marked) => {
|
|
379
|
+
const e = entry(id, "s1", slug, "same words here");
|
|
380
|
+
if (marked) {
|
|
381
|
+
e.frontmatter.origin = { taint: "external", cause: "observed", at: 1700000000700 };
|
|
382
|
+
e.rev = computeEntryRev(e);
|
|
383
|
+
}
|
|
384
|
+
return e;
|
|
385
|
+
};
|
|
386
|
+
const m1 = mk("id-band-a-01", "band-a", true);
|
|
387
|
+
const m2 = mk("id-band-b-01", "band-b", true);
|
|
388
|
+
const m3 = mk("id-band-c-01", "band-c", true);
|
|
389
|
+
const clean = mk("id-band-z-01", "band-z", false);
|
|
390
|
+
assert.deepStrictEqual((await b.applyPatches([m1, m2, m3, clean].map((e) => ({ op: "add", id: e.id, entry: e })))).conflicts, []);
|
|
391
|
+
const banded = await b.search("same words", ["s1"], { limit: 3, exposureBands: true });
|
|
392
|
+
assert.deepStrictEqual(banded.map((h) => h.id), ["id-band-z-01", "id-band-a-01", "id-band-b-01"], "two-band order must apply before the limit truncation (clean first, contract order within the band)");
|
|
393
|
+
const flat = await b.search("same words", ["s1"], { limit: 3 });
|
|
394
|
+
assert.deepStrictEqual(flat.map((h) => h.id), ["id-band-a-01", "id-band-b-01", "id-band-c-01"]);
|
|
395
|
+
});
|
|
353
396
|
defer("consolidation cursor round-trips per scope; unset → undefined", async () => {
|
|
354
397
|
const b = await hooks.make();
|
|
355
398
|
assert.strictEqual(await b.getConsolidationCursor("s1"), undefined);
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { MemoryEntryOrigin } from "./types.js";
|
|
2
|
+
export declare const ORIGIN_CLEARANCES_FILE = "origin-clearances.json";
|
|
3
|
+
/** One clearance's terminal event (appended, never rewritten). */
|
|
4
|
+
export interface OriginClearanceEvent {
|
|
5
|
+
eventId: string;
|
|
6
|
+
at: number;
|
|
7
|
+
to: "done" | "failed";
|
|
8
|
+
/** The resolver of THIS event (the opener on the normal path; a later resumer on a crash path). */
|
|
9
|
+
requestId: string;
|
|
10
|
+
/** Mechanical detail (landing slug, refusal reason) — engine-composed, bounded by the writer. */
|
|
11
|
+
detail?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface OriginClearanceRow {
|
|
14
|
+
clearanceId: string;
|
|
15
|
+
/** The entry whose marker was cleared. The re-record keeps the SAME id (§2.3-2's later-batch
|
|
16
|
+
* arm), so this key joins the clearance to the entry across its whole life. */
|
|
17
|
+
entryId: string;
|
|
18
|
+
scope: string;
|
|
19
|
+
slug: string;
|
|
20
|
+
/** The committed rev the clearance was judged against (the tombstone's CAS anchor). */
|
|
21
|
+
baseRev: string;
|
|
22
|
+
/** The cleared marker, verbatim (audit: what the host vouched away). */
|
|
23
|
+
origin: MemoryEntryOrigin;
|
|
24
|
+
/** WHO cleared — the mandatory audit attribution (never defaulted). */
|
|
25
|
+
requestId: string;
|
|
26
|
+
/** WHY — the host's stated ground (never defaulted). */
|
|
27
|
+
reason: string;
|
|
28
|
+
at: number;
|
|
29
|
+
status: "pending" | "done" | "failed";
|
|
30
|
+
/** CUSTODY: the full serialized cleared-entry file text. A crash between the tombstone batch and
|
|
31
|
+
* the re-record batch leaves this as the only copy — the pending row is the loud recovery seat
|
|
32
|
+
* (a later clearEntryOrigin call for the same entry resumes from it). */
|
|
33
|
+
entryText: string;
|
|
34
|
+
events: OriginClearanceEvent[];
|
|
35
|
+
}
|
|
36
|
+
/** Lock-free strict read of the whole account (host audit face; journal-aware, corrupt = throw). */
|
|
37
|
+
export declare function readOriginClearances(controlDir: string): OriginClearanceRow[];
|
|
38
|
+
/**
|
|
39
|
+
* WRITE-AHEAD open: append the pending row (custody included) before any store mutation. Refuses a
|
|
40
|
+
* second OPEN while a pending row for the same entry stands — the pending row is a resume seat,
|
|
41
|
+
* not a queue (the caller resumes it instead).
|
|
42
|
+
*/
|
|
43
|
+
export declare function openOriginClearance(controlDir: string, row: Omit<OriginClearanceRow, "status" | "events">): void;
|
|
44
|
+
/**
|
|
45
|
+
* Terminal event append + status flip. Unknown clearanceId is a corrupt-caller refusal.
|
|
46
|
+
*
|
|
47
|
+
* SINGLE-WINNER terminal (codex r1): a row already terminal ignores a late contradictory
|
|
48
|
+
* settlement — two concurrent resumers race the store legs (the store's own CAS decides those),
|
|
49
|
+
* and the loser's stale verdict must not overwrite the winner's audit state (a successfully
|
|
50
|
+
* cleared entry reading `failed` with contradictory events). The loser's own thrown refusal is
|
|
51
|
+
* its loud channel; a re-call then answers off the CURRENT committed state.
|
|
52
|
+
*
|
|
53
|
+
* `keepPending: true` appends the event WITHOUT flipping the status: the resume-seat form for a
|
|
54
|
+
* refused re-record leg — the tombstone has committed and the custody row is the ONLY copy of the
|
|
55
|
+
* bytes, so the row must stay resumable (a `failed` flip would strand them: the resume arm only
|
|
56
|
+
* completes pending rows).
|
|
57
|
+
*/
|
|
58
|
+
export declare function settleOriginClearance(controlDir: string, input: {
|
|
59
|
+
clearanceId: string;
|
|
60
|
+
to: "done" | "failed";
|
|
61
|
+
requestId: string;
|
|
62
|
+
eventId: string;
|
|
63
|
+
now: () => number;
|
|
64
|
+
detail?: string;
|
|
65
|
+
keepPending?: boolean;
|
|
66
|
+
}): void;
|