@sema-agent/core 7.30.1 → 7.31.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 +242 -0
- package/dist/agents/cross-session-judge.d.ts +4 -12
- package/dist/agents/cross-session-judge.js +4 -11
- package/dist/agents/peer-session-drain.js +0 -5
- package/dist/agents/send-message-tool.js +1 -0
- package/dist/agents/subagent.d.ts +1 -0
- package/dist/agents/subagent.js +1 -0
- package/dist/core/ask-origin.d.ts +6 -0
- package/dist/core/ask-origin.js +10 -0
- package/dist/core/checkpoint-store.d.ts +14 -0
- package/dist/core/checkpoint-store.js +1 -0
- package/dist/core/engine-notice.d.ts +4 -0
- package/dist/core/governance-codes.d.ts +1 -1
- package/dist/core/governance-codes.js +3 -0
- package/dist/core/memory-engine/data-plane.d.ts +3 -1
- package/dist/core/memory-engine/data-plane.js +1 -1
- package/dist/core/memory-engine/delegation-settlement.d.ts +44 -3
- package/dist/core/memory-engine/delegation-settlement.js +38 -3
- package/dist/core/memory-engine/engine.d.ts +123 -26
- package/dist/core/memory-engine/engine.js +426 -149
- package/dist/core/memory-engine/entry-scan.d.ts +23 -0
- package/dist/core/memory-engine/entry-scan.js +55 -0
- package/dist/core/memory-engine/file-backend.d.ts +16 -23
- package/dist/core/memory-engine/file-backend.js +122 -99
- package/dist/core/memory-engine/layer-observations.d.ts +109 -0
- package/dist/core/memory-engine/layer-observations.js +148 -0
- package/dist/core/memory-engine/layout.d.ts +11 -0
- package/dist/core/memory-engine/layout.js +20 -4
- package/dist/core/memory-engine/types.d.ts +26 -2
- package/dist/core/parent-spec-seat.d.ts +4 -1
- package/dist/core/parent-spec-seat.js +2 -0
- package/dist/core/park-selfcheck.js +1 -0
- package/dist/core/runner/announce-once-ledger.d.ts +26 -0
- package/dist/core/runner/announce-once-ledger.js +37 -0
- package/dist/core/runner/contracts.d.ts +22 -5
- package/dist/core/runner/denial-limit-arms.d.ts +2 -2
- package/dist/core/runner/prepare-acquire-reconcile.d.ts +0 -4
- package/dist/core/runner/prepare-acquire-reconcile.js +1 -3
- package/dist/core/runner/prepare-file-history.d.ts +13 -15
- package/dist/core/runner/prepare-file-history.js +57 -43
- package/dist/core/runner/prepare-hands-readface.d.ts +2 -2
- package/dist/core/runner/prepare-listings.d.ts +3 -1
- package/dist/core/runner/prepare-listings.js +2 -2
- package/dist/core/runner/prepare-memory.js +16 -5
- package/dist/core/runner/prepare-park-ask.js +27 -16
- package/dist/core/runner/prepare-prompt-inputs.d.ts +8 -1
- package/dist/core/runner/prepare-prompt-inputs.js +5 -3
- package/dist/core/runner/prepare-task.js +7 -5
- package/dist/core/runner/prepare-wiring-manifest.d.ts +1 -0
- package/dist/core/runner/prepare-wiring-manifest.js +2 -1
- package/dist/core/runner/run-attachment-seats.js +1 -1
- package/dist/core/runner/run-leg.js +61 -52
- package/dist/core/runner/run-settle-and-teardown.js +4 -0
- package/dist/core/runner/run-turn-boundary.js +6 -6
- package/dist/core/runner/turn-attachments.d.ts +6 -6
- package/dist/core/runner-deps.d.ts +3 -1
- package/dist/core/secret-env.d.ts +11 -0
- package/dist/core/secret-env.js +20 -0
- package/dist/core/store-contracts/checkpoint-store-contract.d.ts +2 -0
- package/dist/core/store-contracts/checkpoint-store-contract.js +22 -0
- package/dist/core/task-result.d.ts +1 -1
- package/dist/core/task-spec.d.ts +17 -8
- package/dist/core/tool-spec.d.ts +1 -1
- package/dist/core/wiring-manifest.d.ts +10 -0
- package/dist/core/wiring-manifest.js +7 -1
- package/dist/engine/execution-env/node-execution-env.d.ts +6 -0
- package/dist/engine/execution-env/node-execution-env.js +15 -17
- package/dist/engine/harness/agent-harness.js +7 -5
- package/dist/engine/lsp/node-lsp-manager.d.ts +4 -0
- package/dist/engine/lsp/node-lsp-manager.js +10 -4
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +7 -1
- package/wire-changes/core@7.31.0.json +705 -0
|
@@ -153,6 +153,19 @@ export interface UnattributedMember {
|
|
|
153
153
|
export interface SessionAccountRow {
|
|
154
154
|
sessionId: string;
|
|
155
155
|
openedAt: number;
|
|
156
|
+
/** The scope this session writes (its write home) — the layer it is a live writer of until
|
|
157
|
+
* `terminatedAt` ({@link openLayerWriters}). Absent on rows written before the field. */
|
|
158
|
+
writeScope?: string;
|
|
159
|
+
/** The session's leg ENDED here: its terminal harvest returned (whatever it admitted — a refused,
|
|
160
|
+
* zero-admission or budget-deferred pass ends the leg all the same), or the host valve closed the
|
|
161
|
+
* row. A lifecycle fact, independent of `closedAt` (every full harvest closes the account — a
|
|
162
|
+
* checkpoint harvest too — while the session may keep writing); cleared when a resume re-opens
|
|
163
|
+
* the row. */
|
|
164
|
+
terminatedAt?: number;
|
|
165
|
+
/** The LEG that holds the row now: minted by the materialize that opened (or re-opened) it and
|
|
166
|
+
* carried on that mount's handle. A close or a leg end made for another leg — an earlier leg
|
|
167
|
+
* whose harvest finished after a resume re-opened the row — is not this leg's and is not written. */
|
|
168
|
+
leg?: string;
|
|
156
169
|
/** The STICKY unattributed set, frozen at materialize (§3.6 归属判据): pre-existing, uncommitted
|
|
157
170
|
* files seen while a FOREIGN dangling open row stood. Membership never shrinks on later writes
|
|
158
171
|
* (the sticky law); it clears only when the row is re-opened with a fresh classification (the
|
|
@@ -175,6 +188,20 @@ export declare function convertV1SidecarKeys(controlDir: string): void;
|
|
|
175
188
|
/** The dangling-trigger read: open rows (and unadjudicated closes) belonging to OTHER sessions.
|
|
176
189
|
* These are what arm the unattributed residue classification at a session's materialize. */
|
|
177
190
|
export declare function foreignDanglingSessionAccounts(controlDir: string, selfSessionId: string): SessionAccountRow[];
|
|
191
|
+
/** The live writers of the store's layers, as the account knows them: every row other than the one
|
|
192
|
+
* held by `selfLeg` (the asking mount's own leg — another leg of the same session is another
|
|
193
|
+
* writer) whose session's leg has not ended (no `terminatedAt` — running, or ended without
|
|
194
|
+
* a terminal close, e.g. a crash), by its write scope. A row written before the write scope was
|
|
195
|
+
* recorded (the engine then closed the account at every full harvest, so its close cannot say
|
|
196
|
+
* whether the leg ended) reads as before: live while never closed, counted in `unscoped` — a
|
|
197
|
+
* writer of an unknown layer. Sessions that open no row (no session id, provenance "off", an
|
|
198
|
+
* adoption-restricted mount) are not visible here. */
|
|
199
|
+
export declare function openLayerWriters(controlDir: string, selfLeg: string | undefined): {
|
|
200
|
+
scopes: Set<string>;
|
|
201
|
+
unscoped: number;
|
|
202
|
+
};
|
|
203
|
+
/** The leg that holds `sessionId`'s account row (undefined ⇔ no row, or a row without one). */
|
|
204
|
+
export declare function sessionAccountLeg(controlDir: string, sessionId: string): string | undefined;
|
|
178
205
|
/** The membership question the harvest asks per file: is `(scope, rel)` in the session's sticky
|
|
179
206
|
* set? A session carrying members the conversion could not attribute answers yes for every seat. */
|
|
180
207
|
export interface SessionUnattributedSet {
|
|
@@ -189,6 +216,8 @@ export declare function openSessionAccount(controlDir: string, input: {
|
|
|
189
216
|
sessionId: string;
|
|
190
217
|
now: () => number;
|
|
191
218
|
unattributed: readonly UnattributedMember[];
|
|
219
|
+
writeScope?: string;
|
|
220
|
+
leg?: string;
|
|
192
221
|
}): void;
|
|
193
222
|
/** The current session's sticky unattributed set (the harvest's residue-arm input). */
|
|
194
223
|
export declare function sessionUnattributedSet(controlDir: string, sessionId: string): SessionUnattributedSet;
|
|
@@ -203,13 +232,25 @@ export declare function sessionUnattributedSet(controlDir: string, sessionId: st
|
|
|
203
232
|
* condition and does not hold the close, but the caller DISCLOSES any standing deferral beside it
|
|
204
233
|
* (a close over id-less deferred files must not read as a clean full pass).
|
|
205
234
|
*
|
|
206
|
-
* Returns
|
|
235
|
+
* Returns what happened ({@link SessionAccountWrite}): `"absent"` (a session that never opened an
|
|
207
236
|
* account — e.g. an adoption-restricted materialize — has no close, and the caller's disclosure
|
|
208
|
-
* must not claim one). */
|
|
237
|
+
* must not claim one), or `"stale"` when `leg` names a mount that no longer holds the row. */
|
|
209
238
|
export declare function closeSessionAccount(controlDir: string, input: {
|
|
210
239
|
sessionId: string;
|
|
211
240
|
now: () => number;
|
|
212
|
-
|
|
241
|
+
leg?: string;
|
|
242
|
+
}): SessionAccountWrite;
|
|
243
|
+
/** What a close / leg end did: wrote the row, found none, or found the row held by ANOTHER leg (the
|
|
244
|
+
* caller's leg is over — a resume re-opened the row — so nothing was written). */
|
|
245
|
+
export type SessionAccountWrite = "written" | "absent" | "stale";
|
|
246
|
+
/** Record that the session's leg ended ({@link SessionAccountRow.terminatedAt}). Touches nothing
|
|
247
|
+
* else — `closedAt` stays whatever the harvests made it. `leg` (the caller's mount) must hold the
|
|
248
|
+
* row: a leg that is over does not end the one that re-opened it. */
|
|
249
|
+
export declare function terminateSessionAccount(controlDir: string, input: {
|
|
250
|
+
sessionId: string;
|
|
251
|
+
now: () => number;
|
|
252
|
+
leg?: string;
|
|
253
|
+
}): SessionAccountWrite;
|
|
213
254
|
/** The host valve for a dangling row (advisory; §3.6 — dangling rows never auto-expire). The
|
|
214
255
|
* close is `closed-unadjudicated` (r7-3): it stops the row dangling but the residue arm keeps
|
|
215
256
|
* firing until a full-domain harvest closes normally. `requestId` required (audit, #123). */
|
|
@@ -242,6 +242,8 @@ function malformedAccountRow(row) {
|
|
|
242
242
|
!Number.isFinite(row.openedAt) ||
|
|
243
243
|
!Array.isArray(row.unattributed) ||
|
|
244
244
|
(row.closedAt !== undefined && (typeof row.closedAt !== "number" || !Number.isFinite(row.closedAt))) ||
|
|
245
|
+
(row.terminatedAt !== undefined && (typeof row.terminatedAt !== "number" || !Number.isFinite(row.terminatedAt))) ||
|
|
246
|
+
(row.leg !== undefined && !reqStr(row.leg)) ||
|
|
245
247
|
(row.unadjudicated !== undefined && row.unadjudicated !== true));
|
|
246
248
|
}
|
|
247
249
|
function isUnattributedMember(x) {
|
|
@@ -273,6 +275,8 @@ function coerceSessionAccounts(raw) {
|
|
|
273
275
|
bad();
|
|
274
276
|
if (row.unattributable !== undefined && (!Array.isArray(row.unattributable) || row.unattributable.some((x) => typeof x !== "string")))
|
|
275
277
|
bad();
|
|
278
|
+
if (row.writeScope !== undefined && !reqStr(row.writeScope))
|
|
279
|
+
bad();
|
|
276
280
|
}
|
|
277
281
|
}
|
|
278
282
|
return rec.version === 1 ? { version: 1, rows: rows } : { version: 2, rows: rows };
|
|
@@ -320,6 +324,13 @@ export function foreignDanglingSessionAccounts(controlDir, selfSessionId) {
|
|
|
320
324
|
const rec = readSessionAccountsConverted(controlDir);
|
|
321
325
|
return rec.rows.filter((r) => r.sessionId !== selfSessionId && (r.closedAt === undefined || r.unadjudicated === true));
|
|
322
326
|
}
|
|
327
|
+
export function openLayerWriters(controlDir, selfLeg) {
|
|
328
|
+
const open = readSessionAccountsConverted(controlDir).rows.filter((r) => (selfLeg === undefined || r.leg !== selfLeg) && (r.writeScope !== undefined ? r.terminatedAt === undefined : r.closedAt === undefined));
|
|
329
|
+
return { scopes: new Set(open.flatMap((r) => (r.writeScope !== undefined ? [r.writeScope] : []))), unscoped: open.filter((r) => r.writeScope === undefined).length };
|
|
330
|
+
}
|
|
331
|
+
export function sessionAccountLeg(controlDir, sessionId) {
|
|
332
|
+
return readSessionAccountsConverted(controlDir).rows.find((r) => r.sessionId === sessionId)?.leg;
|
|
333
|
+
}
|
|
323
334
|
export function emptyUnattributedSet() {
|
|
324
335
|
return { has: () => false, empty: true };
|
|
325
336
|
}
|
|
@@ -330,8 +341,17 @@ export function openSessionAccount(controlDir, input) {
|
|
|
330
341
|
lockedStrictUpdate(controlDir, SESSION_ACCOUNTS_FILE, "memory session-account ledger", coerceSessionAccounts, (read) => {
|
|
331
342
|
const { rec } = convertSessionAccounts(read, controlDir);
|
|
332
343
|
const row = rec.rows.find((r) => r.sessionId === input.sessionId);
|
|
344
|
+
if (row !== undefined && input.writeScope !== undefined)
|
|
345
|
+
row.writeScope = input.writeScope;
|
|
346
|
+
if (row !== undefined) {
|
|
347
|
+
delete row.terminatedAt;
|
|
348
|
+
if (input.leg !== undefined)
|
|
349
|
+
row.leg = input.leg;
|
|
350
|
+
else
|
|
351
|
+
delete row.leg;
|
|
352
|
+
}
|
|
333
353
|
if (row === undefined) {
|
|
334
|
-
rec.rows.push({ sessionId: input.sessionId, openedAt: input.now(), unattributed: [...input.unattributed] });
|
|
354
|
+
rec.rows.push({ sessionId: input.sessionId, openedAt: input.now(), ...(input.writeScope !== undefined ? { writeScope: input.writeScope } : {}), ...(input.leg !== undefined ? { leg: input.leg } : {}), unattributed: [...input.unattributed] });
|
|
335
355
|
}
|
|
336
356
|
else if (row.closedAt !== undefined && row.unadjudicated === undefined) {
|
|
337
357
|
row.openedAt = input.now();
|
|
@@ -363,12 +383,26 @@ export function closeSessionAccount(controlDir, input) {
|
|
|
363
383
|
const { rec } = convertSessionAccounts(read, controlDir);
|
|
364
384
|
const row = rec.rows.find((r) => r.sessionId === input.sessionId);
|
|
365
385
|
if (row === undefined)
|
|
366
|
-
return { result:
|
|
386
|
+
return { result: "absent" };
|
|
387
|
+
if (input.leg !== undefined && row.leg !== input.leg)
|
|
388
|
+
return { result: "stale" };
|
|
367
389
|
row.closedAt = input.now();
|
|
368
390
|
delete row.unadjudicated;
|
|
369
391
|
for (const r of rec.rows)
|
|
370
392
|
delete r.unadjudicated;
|
|
371
|
-
return { next: rec, result:
|
|
393
|
+
return { next: rec, result: "written" };
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
export function terminateSessionAccount(controlDir, input) {
|
|
397
|
+
return lockedStrictUpdate(controlDir, SESSION_ACCOUNTS_FILE, "memory session-account ledger", coerceSessionAccounts, (read) => {
|
|
398
|
+
const { rec } = convertSessionAccounts(read, controlDir);
|
|
399
|
+
const row = rec.rows.find((r) => r.sessionId === input.sessionId);
|
|
400
|
+
if (row === undefined)
|
|
401
|
+
return { result: "absent" };
|
|
402
|
+
if (input.leg !== undefined && row.leg !== input.leg)
|
|
403
|
+
return { result: "stale" };
|
|
404
|
+
row.terminatedAt = input.now();
|
|
405
|
+
return { next: rec, result: "written" };
|
|
372
406
|
});
|
|
373
407
|
}
|
|
374
408
|
export function resolveSessionAccountRecord(controlDir, input) {
|
|
@@ -387,6 +421,7 @@ export function resolveSessionAccountRecord(controlDir, input) {
|
|
|
387
421
|
}
|
|
388
422
|
row.closedAt = input.now();
|
|
389
423
|
row.unadjudicated = true;
|
|
424
|
+
row.terminatedAt = row.closedAt;
|
|
390
425
|
return { next: rec, result: undefined };
|
|
391
426
|
});
|
|
392
427
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EngineNotice } from "../types.js";
|
|
2
2
|
import { type ConsolidationGateRead, type ConsolidationGateRow, type ConsolidationPlanFile, type ConsolidationProposal, type ConsolidationRunStopReason, type MemoryConsolidationOptions } from "./consolidation.js";
|
|
3
|
-
import { foreignDanglingSessionAccounts, type EffectiveSettlement, type HoldRow } from "./delegation-settlement.js";
|
|
3
|
+
import { foreignDanglingSessionAccounts, type SessionAccountWrite, type EffectiveSettlement, type HoldRow } from "./delegation-settlement.js";
|
|
4
4
|
import { type OriginClearanceRow } from "./origin-clearance.js";
|
|
5
5
|
import { type CommittedBinding, type EraseMemoryEntriesInput, type MemoryErasureAttestation, type TransferEvidence } from "./file-backend.js";
|
|
6
6
|
import { type MemoryExportBundle, type MemoryImportReport } from "./export-bundle.js";
|
|
@@ -155,7 +155,8 @@ export declare const MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA = "Any guidance in
|
|
|
155
155
|
* about the state itself, and a model asked "remember X" under that silence answers with a success
|
|
156
156
|
* receipt for a save that never happens (the confabulated-receipt shape). This section states the
|
|
157
157
|
* state instead. Mounted only where "you cannot save" is provably TRUE: by `inject()` for a
|
|
158
|
-
* read-only layering (writeScope null ⇒
|
|
158
|
+
* read-only layering (writeScope null ⇒ the harvest admits nothing and the file-tool gate refuses the
|
|
159
|
+
* whole memory domain), and by the runner for handsReadOnly (the
|
|
159
160
|
* shell rides the read-only band), for a declared-unavailable session (`memoryPersistenceCapable:
|
|
160
161
|
* false` — where the runner's write gate also refuses the file channel, keeping the engine-refusal
|
|
161
162
|
* sentence true), and for a write-less roster the persistence inference cannot vouch for. A merely
|
|
@@ -310,7 +311,7 @@ export interface MemoryEngineOptions {
|
|
|
310
311
|
/** M7 — max files PROCESSED per harvest (default {@link DEFAULT_HARVEST_FILE_BUDGET}). */
|
|
311
312
|
harvestFileBudget?: number;
|
|
312
313
|
/**
|
|
313
|
-
* Where this engine's advisory incidents go.
|
|
314
|
+
* Where this engine's advisory incidents go. Codes that ride it include:
|
|
314
315
|
* - `memory.partition_split` / `memory.partition_adopt_failed` — the mount-time partition-key probe,
|
|
315
316
|
* when this engine derives its OWN control dir (no `controlDir`, backend pins none). Same seat and
|
|
316
317
|
* same reason as the file backend's option: the probe promises "never silent", and on this
|
|
@@ -319,6 +320,9 @@ export interface MemoryEngineOptions {
|
|
|
319
320
|
* The queue is the DISCLOSURE lane for out-of-session memory events, and its own failures used to
|
|
320
321
|
* be swallowed at four call sites: a host whose control plane went unwritable kept seeing healthy
|
|
321
322
|
* sessions while every gate/external notice was dropped on the floor.
|
|
323
|
+
* - `memory.observation_settlement_unavailable` — once per engine, at a zero-copy materialize over a
|
|
324
|
+
* backend without the committed-snapshot face: changes observed in read-only layers are recorded
|
|
325
|
+
* undecided and cannot settle on it.
|
|
322
326
|
* Absent ⇒ advisory-silent; neither a mount nor a harvest ever fails over this seat.
|
|
323
327
|
*/
|
|
324
328
|
onIncident?: MemoryPartitionIncidentSink;
|
|
@@ -343,8 +347,8 @@ export interface MemoryInjection {
|
|
|
343
347
|
/** CC `# Memory` section (system-authority instruction — §0.3 逐字 surface). Empty for a read-only
|
|
344
348
|
* layering (no write channel to instruct). */
|
|
345
349
|
instruction: string;
|
|
346
|
-
/** Present exactly when this is a read-only LAYERING (writeScope null — the
|
|
347
|
-
*
|
|
350
|
+
/** Present exactly when this is a read-only LAYERING (writeScope null — the harvest admits
|
|
351
|
+
* nothing from this session, whatever the roster writes): the {@link MEMORY_READONLY_NOTICE}
|
|
348
352
|
* section stating that memory cannot be saved to, so the model declines "remember X" instead of
|
|
349
353
|
* issuing a confabulated success receipt. NOT set for `writeToolMounted:false` over a writable
|
|
350
354
|
* scope — there only the instruction-named tool is absent, other roster tools can still write the
|
|
@@ -843,6 +847,7 @@ export declare class MemoryEngine {
|
|
|
843
847
|
private readonly provenance;
|
|
844
848
|
/** {@link MemoryEngineOptions.onIncident} — the advisory disclosure seat (partition + announce codes). */
|
|
845
849
|
private readonly onIncident;
|
|
850
|
+
private unsettleableDisclosed;
|
|
846
851
|
/** A backend-pinned directory (FileBackend zero-copy detection). */
|
|
847
852
|
private readonly backendPinnedRoot?;
|
|
848
853
|
constructor(opts: MemoryEngineOptions);
|
|
@@ -858,6 +863,94 @@ export declare class MemoryEngine {
|
|
|
858
863
|
get provenanceMode(): "off" | "carry";
|
|
859
864
|
/** {@link MemoryEngineOptions.consolidation}, screened — undefined ⇔ the protocol is OFF. */
|
|
860
865
|
private readonly consolidation?;
|
|
866
|
+
/**
|
|
867
|
+
* Record that this mount's leg of `sessionId` ended — its terminal harvest returned or threw, or
|
|
868
|
+
* the run that mounted it will never harvest (a prepare that failed after the mount). Writer
|
|
869
|
+
* liveness (`SessionAccountRow.terminatedAt`) is the session's lifecycle, so this is the one mint
|
|
870
|
+
* of it besides the host valve. Only the leg the handle opened is ended (`"stale"`: a resume
|
|
871
|
+
* re-opened the row, and the later leg is still live). A record that cannot be written leaves the
|
|
872
|
+
* row reading live (restores of its layer withheld) and is disclosed, never silent.
|
|
873
|
+
*/
|
|
874
|
+
endSessionLeg(handle: MemorySessionHandle, sessionId: string): SessionAccountWrite | undefined;
|
|
875
|
+
private endLeg;
|
|
876
|
+
/** design/336 §3.6 (slice 2) — the session-account incident channel (`memory.session_account_failed`):
|
|
877
|
+
* a degraded residue classification is DISCLOSED, never quiet (the behavioral fail-closed half —
|
|
878
|
+
* harvest refusing on `handle.sessionAccountFailed` — is enforced at the write boundary). */
|
|
879
|
+
private discloseSessionAccountIncident;
|
|
880
|
+
/** Read-only layer integrity, DETECTION half — run on every harvest path, before any exit. A
|
|
881
|
+
* read-only layer is this session's POLICY (the file-tool gate refuses writes into it; nothing on
|
|
882
|
+
* disk enforces it), so a layer file that differs from what this session was served is a
|
|
883
|
+
* `readonly_layer` rejection whatever the arm — unless its bytes EQUAL the committed copy: then a
|
|
884
|
+
* session that may write this layer committed them while this one ran (the same transactional
|
|
885
|
+
* proof the restricted committed view reads), and there is nothing to reject or put back.
|
|
886
|
+
* Returns what the RESTORE half may put back ({@link restoreReadOnlyLayers}): only a session that
|
|
887
|
+
* may commit ever restores, and only once the harvest's fail-closed preflights (journal recovery
|
|
888
|
+
* included) passed — a session that admits nothing (read-only layering, capture opt-out,
|
|
889
|
+
* persistence declared unavailable, an indeterminate capture state) only detects and reports: it
|
|
890
|
+
* never adjudicates a shared layer on disk, and the boundary audit of that arm names the same
|
|
891
|
+
* divergence on the inbound channel. */
|
|
892
|
+
private detectReadOnlyLayerChanges;
|
|
893
|
+
/** The committed copy of `id` read WITHOUT an adopting sync: a File backend's ordinary `getByIds`
|
|
894
|
+
* adopts pending disk edits across every registered scope, which a session that admits nothing
|
|
895
|
+
* must never trigger — its committed view (`restrictedAdoptionView`, audit off) is read instead.
|
|
896
|
+
* Zero-copy reads the committed shadow already, and a backend without that view has no read-side
|
|
897
|
+
* adoption channel (its plain reads serve committed rows). */
|
|
898
|
+
private committedContentUnadopted;
|
|
899
|
+
/** Read-only layer integrity, RESTORE half (zero-copy hazard: the changed file IS the backend's
|
|
900
|
+
* storage, and left in place a later read-side sync would adopt it as the layer's baseline). It
|
|
901
|
+
* acts on the committed account AS IT STANDS NOW — after the preflights recovered any interrupted
|
|
902
|
+
* transaction — never on what detection read before them: the file and its committed copy are
|
|
903
|
+
* read again, and bytes that now equal the committed copy were a commit (the early rejection was
|
|
904
|
+
* judged against a stale account and is withdrawn; nothing is written). A restore that could not
|
|
905
|
+
* land is a warning, never a silent skip. */
|
|
906
|
+
private restoreReadOnlyLayers;
|
|
907
|
+
/** Read-only layer integrity, OBSERVATION half — run on every harvest path with the detection
|
|
908
|
+
* half. Every file of a layer this session holds read-only whose bytes changed in the window
|
|
909
|
+
* (against the materialize-time census) is a candidate; one that no commit backs at its seat is
|
|
910
|
+
* recorded in the session-observation table, which every door into the committed account
|
|
911
|
+
* consults: the bytes are never adopted, whoever reads next. A file this session was not served
|
|
912
|
+
* (a new file, with or without an entry id) is rejected here; a served entry's change is the
|
|
913
|
+
* detection half's rejection. The commit question is answered only by a committed account that
|
|
914
|
+
* can be read whole — a pending transaction journal (a writer mid-commit, or one that died there)
|
|
915
|
+
* makes it undecidable, never "not committed": those candidates are recorded UNDECIDED (never
|
|
916
|
+
* adopted meanwhile) and returned; a writing session settles every undecided row once its
|
|
917
|
+
* preflight has recovered the journal (the restore half's rule). Zero-copy only (the census is taken there): that is where the
|
|
918
|
+
* observed plane IS the store the read side adopts from. A record that cannot be written is a
|
|
919
|
+
* warning — the next session may then adopt the bytes, which must not happen silently. */
|
|
920
|
+
private observeReadOnlyLayers;
|
|
921
|
+
/** Write an observation batch AS JUDGED (a retry writes the same rows — same baseline, same
|
|
922
|
+
* observation time — never a re-judgment against a later account). Only a landed batch claims
|
|
923
|
+
* anything: its rejections now, its undecided rows' disclosure at the conclusion. A failure is
|
|
924
|
+
* kept, and said only once no retry follows ({@link concludeObservations}). */
|
|
925
|
+
private landObservations;
|
|
926
|
+
/** Where the harvest leaves the observation half for good: a batch that never landed is said (once,
|
|
927
|
+
* with its last failure); a landed batch's undecided rows are named. */
|
|
928
|
+
private concludeObservations;
|
|
929
|
+
/** Candidates the committed account could not answer for are named, never silently dropped. */
|
|
930
|
+
private discloseUndecidedObservations;
|
|
931
|
+
/** The settlement of undecided observation rows — run by a session that may write, right after its
|
|
932
|
+
* preflight recovered any pending journal, each row by its own key: removed when the committed
|
|
933
|
+
* account backs its bytes at its seat (they are committed content), otherwise pinned to the
|
|
934
|
+
* committed rev read here — an ordinary row, retired like any other when that rev moves; a row the
|
|
935
|
+
* account still cannot answer for stays undecided.
|
|
936
|
+
* This session's own undecided candidates that settle
|
|
937
|
+
* as unexpected writes and were not served become its rejections; ones still undecidable are
|
|
938
|
+
* named. */
|
|
939
|
+
private settleUndecidedObservations;
|
|
940
|
+
/** Whether the read doors refuse this content of `entryId` now: the observation table as it stands,
|
|
941
|
+
* against the committed rev read now. Undefined when either cannot be read. */
|
|
942
|
+
private refusedNow;
|
|
943
|
+
/** Once per engine, at a zero-copy materialize: a backend without `committedSnapshotOf` (a wrapper
|
|
944
|
+
* that does not pass it through) records every read-only-layer change undecided, and none settles. */
|
|
945
|
+
private discloseUnsettleableObservations;
|
|
946
|
+
/** The committed row of `id`, read side-effect-free from the store's audit face: rev plus binding,
|
|
947
|
+
* undefined when the account has no row, `"undecidable"` when the account cannot be read whole
|
|
948
|
+
* (a pending journal, a fault, no audit face) — never a guess from a copy that carries no seat. */
|
|
949
|
+
private committedRowOf;
|
|
950
|
+
/** A caught memory-write failure as a report / incident line: its text, plus the remedy when the
|
|
951
|
+
* file system refused it on permissions ({@link withPermissionRemedy} — the one spelling every
|
|
952
|
+
* write seat surfaces, so no seat renders a permission refusal as a bare errno). */
|
|
953
|
+
private writeFailureText;
|
|
861
954
|
/**
|
|
862
955
|
* The ONE delivery point for a failed announcement-queue operation (`memory.announce_failed`, ruled
|
|
863
956
|
* 2026-08-03). Every enqueue/drain call site stays fail-OPEN — a broken queue must never fail a
|
|
@@ -869,10 +962,6 @@ export declare class MemoryEngine {
|
|
|
869
962
|
* failure produces no second-order disclosure — the alternative (announcing the announce failure)
|
|
870
963
|
* would recurse through the very lane that just failed.
|
|
871
964
|
*/
|
|
872
|
-
/** design/336 §3.6 (slice 2) — the session-account incident channel (`memory.session_account_failed`):
|
|
873
|
-
* a degraded residue classification is DISCLOSED, never quiet (the behavioral fail-closed half —
|
|
874
|
-
* harvest refusing on `handle.sessionAccountFailed` — is enforced at the write boundary). */
|
|
875
|
-
private discloseSessionAccountIncident;
|
|
876
965
|
private discloseAnnounceFailure;
|
|
877
966
|
/**
|
|
878
967
|
* Record retrieval hits (entries a memory tool actually RETURNED to the model) into this engine's
|
|
@@ -1089,6 +1178,7 @@ export declare class MemoryEngine {
|
|
|
1089
1178
|
* `residuals.indexUncleared`, never silently absorbed into a clean residual set.
|
|
1090
1179
|
*/
|
|
1091
1180
|
eraseMemoryEntries(input: EraseMemoryEntriesInput): Promise<MemoryErasureAttestation>;
|
|
1181
|
+
private eraseMemoryEntriesCore;
|
|
1092
1182
|
/**
|
|
1093
1183
|
* design/178 v2-b (rescan hardening) — the erase-time DERIVED-INDEX sweep, both lanes. MEMORY.md
|
|
1094
1184
|
* is the engine's model-visible distillation of entry frontmatter (name + description — routinely
|
|
@@ -1199,6 +1289,7 @@ export declare class MemoryEngine {
|
|
|
1199
1289
|
sessionId?: string;
|
|
1200
1290
|
shadowNames?: ReadonlyMap<string, string>;
|
|
1201
1291
|
}): Promise<MemorySessionHandle>;
|
|
1292
|
+
private materializeCore;
|
|
1202
1293
|
inject(handle: MemorySessionHandle, opts?: {
|
|
1203
1294
|
writeToolMounted?: boolean;
|
|
1204
1295
|
reminderMark?: string;
|
|
@@ -1228,6 +1319,10 @@ export declare class MemoryEngine {
|
|
|
1228
1319
|
* S2-B (O-F7/C-F3): the announcement ENQUEUE rides the harvest tail INSIDE the engine — every
|
|
1229
1320
|
* caller (checkpoint mint / task terminal) is covered without run-loop wiring, and a refused
|
|
1230
1321
|
* harvest (incident) announces too. The enqueue is fail-open: a broken queue never fails a harvest.
|
|
1322
|
+
* `opts.phase` is the CALLER's word for where the session stands: `"terminal"` ⇔ its leg ends with
|
|
1323
|
+
* this harvest, which records the leg's end on the session account once the harvest returns or
|
|
1324
|
+
* throws — whatever it admitted (`SessionAccountRow.terminatedAt`); anything else, absent
|
|
1325
|
+
* included, leaves the writer live.
|
|
1231
1326
|
*/
|
|
1232
1327
|
harvest(handle: MemorySessionHandle, opts?: {
|
|
1233
1328
|
polluted?: {
|
|
@@ -1239,6 +1334,7 @@ export declare class MemoryEngine {
|
|
|
1239
1334
|
reason: string;
|
|
1240
1335
|
cause?: "capture_optout_inherited";
|
|
1241
1336
|
};
|
|
1337
|
+
phase?: "checkpoint" | "terminal";
|
|
1242
1338
|
}): Promise<HarvestReport>;
|
|
1243
1339
|
private harvestCore;
|
|
1244
1340
|
/**
|
|
@@ -1272,7 +1368,8 @@ export declare class MemoryEngine {
|
|
|
1272
1368
|
resolveHold(holdId: string, action: "release" | "discard", requestId: string): void;
|
|
1273
1369
|
/** §3.6 — the session-account valve: close a dangling foreign row WITHOUT adjudication (the
|
|
1274
1370
|
* residue arm keeps firing until the next full-domain harvest closes normally — a valve close
|
|
1275
|
-
* must not launder the window it covers).
|
|
1371
|
+
* must not launder the window it covers). It also ends the session's leg (no live writer of its
|
|
1372
|
+
* layer any more). Audited (requestId required). */
|
|
1276
1373
|
resolveSessionAccount(sessionId: string, requestId: string): void;
|
|
1277
1374
|
/** The engine's control-plane directory (the sidecar home: pollution markers, lineage/challenge
|
|
1278
1375
|
* ledgers, and the design/336 settlement/hold/session accounts). Exposed so a host (and the
|
|
@@ -1687,11 +1784,12 @@ export declare class MemoryEngine {
|
|
|
1687
1784
|
/** Persist the canonical projection back to the session file (id minting / frontmatter completion).
|
|
1688
1785
|
* C-F6 (S2-0): called ONLY after the backend transaction committed — never ahead of the journal
|
|
1689
1786
|
* commit point. Skips the write when the disk already holds the canonical bytes (zero-copy: the
|
|
1690
|
-
* journal's own execute step wrote them, making this an idempotent no-op). Returns
|
|
1691
|
-
* disk now holds the canonical bytes
|
|
1692
|
-
*
|
|
1693
|
-
*
|
|
1694
|
-
*
|
|
1787
|
+
* journal's own execute step wrote them, making this an idempotent no-op). Returns `undefined`
|
|
1788
|
+
* when the disk now holds the canonical bytes, else the failure's text: the caller reports it
|
|
1789
|
+
* (the ordinary completion family stays recoverable — the backend commit is authoritative and the
|
|
1790
|
+
* next materialize re-projects), and the id-COMPLETION family additionally keeps the #366
|
|
1791
|
+
* projection-debt row standing (an id-less plane file must re-bind to its committed id at the
|
|
1792
|
+
* next harvest, never re-admit as a duplicate). */
|
|
1695
1793
|
private writeBackProjection;
|
|
1696
1794
|
/**
|
|
1697
1795
|
* #366 — repair an id-less-but-committed seat IN PLACE (the failed write-back's retry): write the
|
|
@@ -1699,7 +1797,8 @@ export declare class MemoryEngine {
|
|
|
1699
1797
|
* already proven the seat carries no unadmitted edit (rev equality against its baseline), so the
|
|
1700
1798
|
* overwrite is content-preserving — it restores the id line, the completed frontmatter and any
|
|
1701
1799
|
* carried marker, nothing else. A refused read/write leaves the row standing (retried next pass;
|
|
1702
|
-
* the next materialize's projection loop repairs and settles it too)
|
|
1800
|
+
* the next materialize's projection loop repairs and settles it too); a refused WRITE is said on
|
|
1801
|
+
* `warnings`, so a seat that keeps failing is not silently retried forever.
|
|
1703
1802
|
*/
|
|
1704
1803
|
private repairProjectionSeat;
|
|
1705
1804
|
/**
|
|
@@ -1713,16 +1812,14 @@ export declare class MemoryEngine {
|
|
|
1713
1812
|
* `getByIds` there would sync-adopt mid-harvest.
|
|
1714
1813
|
*/
|
|
1715
1814
|
private debtCommittedProjection;
|
|
1716
|
-
/**
|
|
1717
|
-
* no other scope's directory can
|
|
1718
|
-
*
|
|
1719
|
-
*
|
|
1720
|
-
*
|
|
1721
|
-
*
|
|
1722
|
-
*
|
|
1723
|
-
|
|
1724
|
-
* per-call behaviour (each call states everything it skipped). */
|
|
1725
|
-
private chmodScopeTree;
|
|
1815
|
+
/** The layer census: one walk of a scope's home per materialize, looking and never changing
|
|
1816
|
+
* anything. The tree is the scope's own home and nothing else — no other scope's directory can
|
|
1817
|
+
* lie under it. What it owes is DISCLOSURE: a link or an unreadable path inside a memory layer is
|
|
1818
|
+
* an anomaly the host should hear about, and a read-only layer has no other channel for it (the
|
|
1819
|
+
* harvest scan, which reports links for the writable scope, does not walk read-only layers at
|
|
1820
|
+
* all). Links are never followed (lstat): a linked directory would carry the walk out of the
|
|
1821
|
+
* tree. */
|
|
1822
|
+
private censusScopeTree;
|
|
1726
1823
|
}
|
|
1727
1824
|
/** Render drained announcements as ONE engine-owned system-reminder block (the lead/frame is
|
|
1728
1825
|
* trusted; item identifiers were neutralized at enqueue — see {@link MemoryAnnouncement.items}). */
|