@sema-agent/core 7.0.0 → 7.0.2
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 +32 -0
- package/dist/agents/launch-receipt-contract.d.ts +34 -0
- package/dist/agents/launch-receipt-contract.js +5 -0
- package/dist/agents/subagent.d.ts +134 -2
- package/dist/agents/subagent.js +160 -35
- package/dist/core/file-history-store.js +24 -2
- package/dist/core/memory-engine/engine.d.ts +62 -5
- package/dist/core/memory-engine/engine.js +90 -19
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/layout.d.ts +11 -3
- package/dist/core/roles.d.ts +6 -1
- package/dist/core/roles.js +10 -1
- package/dist/core/runner/prepare-memory.js +29 -22
- package/dist/core/runner/prepare-task.d.ts +3 -2
- package/dist/core/runner/prepare-task.js +13 -8
- package/dist/core/runner/runtask.js +2 -0
- package/dist/core/task-notification.d.ts +20 -0
- package/dist/core/types.d.ts +84 -3
- package/dist/core/wiring-manifest.d.ts +18 -1
- package/dist/index.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +9 -1
- package/dist/orchestration/run-workflow-tool.js +13 -8
- package/dist/orchestration/workflow.d.ts +9 -1
- package/dist/orchestration/workflow.js +22 -14
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +3 -1
|
@@ -457,37 +457,47 @@ export class MemoryEngine {
|
|
|
457
457
|
return "unpersisted";
|
|
458
458
|
}
|
|
459
459
|
}
|
|
460
|
-
let
|
|
460
|
+
let raw;
|
|
461
461
|
try {
|
|
462
|
-
|
|
462
|
+
raw = this.captureRecords.mark(sessionId, record);
|
|
463
463
|
}
|
|
464
464
|
catch {
|
|
465
|
-
|
|
465
|
+
raw = "unpersisted";
|
|
466
466
|
}
|
|
467
|
-
|
|
468
|
-
this.captureOptOutSessions.
|
|
469
|
-
|
|
467
|
+
const settle = (outcome) => {
|
|
468
|
+
if (outcome !== "unpersisted" && !this.captureOptOutSessions.has(sessionId))
|
|
469
|
+
this.captureOptOutSessions.set(sessionId, record);
|
|
470
|
+
return outcome;
|
|
471
|
+
};
|
|
472
|
+
return raw instanceof Promise ? raw.then(settle, () => "unpersisted") : settle(raw);
|
|
470
473
|
}
|
|
471
474
|
sessionCaptureOptOut(sessionId) {
|
|
472
|
-
|
|
475
|
+
const r = this.sessionCaptureOptOutOrFault(sessionId);
|
|
476
|
+
return r instanceof Promise ? r.then((s) => s.record) : r.record;
|
|
473
477
|
}
|
|
474
478
|
sessionCaptureOptOutOrFault(sessionId) {
|
|
475
479
|
const inProcess = this.captureOptOutSessions.get(sessionId);
|
|
476
480
|
if (inProcess !== undefined)
|
|
477
481
|
return { record: inProcess, fault: false };
|
|
482
|
+
let raw;
|
|
478
483
|
try {
|
|
479
|
-
|
|
480
|
-
return record !== undefined ? { record, fault: false } : { fault: false };
|
|
484
|
+
raw = this.captureRecords.read(sessionId);
|
|
481
485
|
}
|
|
482
486
|
catch {
|
|
483
487
|
return { fault: true };
|
|
484
488
|
}
|
|
489
|
+
const fold = (record) => record !== undefined ? { record, fault: false } : { fault: false };
|
|
490
|
+
return raw instanceof Promise ? raw.then(fold, () => ({ fault: true })) : fold(raw);
|
|
485
491
|
}
|
|
486
492
|
listCaptureOptOutSessions() {
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
out.
|
|
490
|
-
|
|
493
|
+
const raw = this.captureRecords.list();
|
|
494
|
+
const fold = (rows) => {
|
|
495
|
+
const out = new Set(Object.keys(rows));
|
|
496
|
+
for (const id of this.captureOptOutSessions.keys())
|
|
497
|
+
out.add(id);
|
|
498
|
+
return out;
|
|
499
|
+
};
|
|
500
|
+
return raw instanceof Promise ? raw.then(fold) : fold(raw);
|
|
491
501
|
}
|
|
492
502
|
async sweepSessionCaptureResidue(handle) {
|
|
493
503
|
const out = { swept: [], restored: [], failures: [] };
|
|
@@ -1546,7 +1556,7 @@ export class MemoryEngine {
|
|
|
1546
1556
|
warnings: [],
|
|
1547
1557
|
};
|
|
1548
1558
|
const writeScope = handle.writeScope;
|
|
1549
|
-
const captureRead = opts?.sessionId !== undefined ? this.sessionCaptureOptOutOrFault(opts.sessionId) : { fault: false };
|
|
1559
|
+
const captureRead = opts?.sessionId !== undefined ? await this.sessionCaptureOptOutOrFault(opts.sessionId) : { fault: false };
|
|
1550
1560
|
const captureOptOut = captureRead.record;
|
|
1551
1561
|
if (writeScope === null || opts?.admitNothing !== undefined || captureOptOut !== undefined || captureRead.fault) {
|
|
1552
1562
|
try {
|
|
@@ -2194,7 +2204,7 @@ export class MemoryEngine {
|
|
|
2194
2204
|
return report;
|
|
2195
2205
|
}
|
|
2196
2206
|
}
|
|
2197
|
-
const preCommitRead = lineageSessionId !== undefined ? this.sessionCaptureOptOutOrFault(lineageSessionId) : { fault: false, record: undefined };
|
|
2207
|
+
const preCommitRead = lineageSessionId !== undefined ? await this.sessionCaptureOptOutOrFault(lineageSessionId) : { fault: false, record: undefined };
|
|
2198
2208
|
if (preCommitRead.record !== undefined || preCommitRead.fault) {
|
|
2199
2209
|
if (lineageArmed) {
|
|
2200
2210
|
try {
|
|
@@ -2796,7 +2806,7 @@ export class MemoryEngine {
|
|
|
2796
2806
|
return false;
|
|
2797
2807
|
return frozenToken === undefined || held.token === frozenToken;
|
|
2798
2808
|
}
|
|
2799
|
-
consolidationEligibility(headers) {
|
|
2809
|
+
async consolidationEligibility(headers) {
|
|
2800
2810
|
let exclusions;
|
|
2801
2811
|
try {
|
|
2802
2812
|
exclusions = this.readChallengeExclusions();
|
|
@@ -2823,7 +2833,7 @@ export class MemoryEngine {
|
|
|
2823
2833
|
}
|
|
2824
2834
|
let optOutSessions;
|
|
2825
2835
|
try {
|
|
2826
|
-
optOutSessions = this.listCaptureOptOutSessions();
|
|
2836
|
+
optOutSessions = await this.listCaptureOptOutSessions();
|
|
2827
2837
|
}
|
|
2828
2838
|
catch (err) {
|
|
2829
2839
|
throw new ConsolidationRefusedError("memory.consolidation_governance_unreadable", `memory consolidation refused: the capture opt-out roster cannot be trusted (fail-closed): ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -2874,7 +2884,7 @@ export class MemoryEngine {
|
|
|
2874
2884
|
}
|
|
2875
2885
|
const face = this.committedAuditFace();
|
|
2876
2886
|
const headers = await face.listHeaders([scope]);
|
|
2877
|
-
const { eligible } = this.consolidationEligibility(headers);
|
|
2887
|
+
const { eligible } = await this.consolidationEligibility(headers);
|
|
2878
2888
|
const fingerprint = opts.full === true ? undefined : row?.fingerprint;
|
|
2879
2889
|
const candidateIds = [];
|
|
2880
2890
|
for (const [id, h] of eligible) {
|
|
@@ -3040,7 +3050,7 @@ export class MemoryEngine {
|
|
|
3040
3050
|
const at = this.now();
|
|
3041
3051
|
const face = this.committedAuditFace();
|
|
3042
3052
|
const headers = await face.listHeaders([scope]);
|
|
3043
|
-
const { exclusions, superseded } = this.consolidationEligibility(headers);
|
|
3053
|
+
const { exclusions, superseded } = await this.consolidationEligibility(headers);
|
|
3044
3054
|
const headerById = new Map(headers.map((h) => [h.id, h]));
|
|
3045
3055
|
const wholeReasons = [];
|
|
3046
3056
|
for (let i = 0; i < proposal.products.length; i++) {
|
|
@@ -3921,6 +3931,67 @@ export class MemoryEngine {
|
|
|
3921
3931
|
}
|
|
3922
3932
|
return await b.listScopes();
|
|
3923
3933
|
}
|
|
3934
|
+
async sessionMemoryStatus(sessionId) {
|
|
3935
|
+
const out = {};
|
|
3936
|
+
const capture = await this.sessionCaptureOptOutOrFault(sessionId);
|
|
3937
|
+
if (capture.fault) {
|
|
3938
|
+
out.optOutSource = "fault";
|
|
3939
|
+
}
|
|
3940
|
+
else if (capture.record !== undefined) {
|
|
3941
|
+
out.captureOptedOut = true;
|
|
3942
|
+
out.optOutSource = "record";
|
|
3943
|
+
}
|
|
3944
|
+
else {
|
|
3945
|
+
out.captureOptedOut = false;
|
|
3946
|
+
}
|
|
3947
|
+
let mine;
|
|
3948
|
+
try {
|
|
3949
|
+
const committed = readLineageRecord(this.controlDir).committed;
|
|
3950
|
+
const ids = new Set();
|
|
3951
|
+
let lastAt;
|
|
3952
|
+
for (const [entryId, sessions] of Object.entries(committed)) {
|
|
3953
|
+
const c = sessions[sessionId];
|
|
3954
|
+
if (c === undefined)
|
|
3955
|
+
continue;
|
|
3956
|
+
ids.add(entryId);
|
|
3957
|
+
if (lastAt === undefined || c.lastAt > lastAt)
|
|
3958
|
+
lastAt = c.lastAt;
|
|
3959
|
+
}
|
|
3960
|
+
mine = ids;
|
|
3961
|
+
out.committedCount = ids.size;
|
|
3962
|
+
if (lastAt !== undefined)
|
|
3963
|
+
out.lastCaptureAt = lastAt;
|
|
3964
|
+
}
|
|
3965
|
+
catch {
|
|
3966
|
+
}
|
|
3967
|
+
if (mine !== undefined) {
|
|
3968
|
+
if (mine.size === 0) {
|
|
3969
|
+
out.foldedCount = 0;
|
|
3970
|
+
}
|
|
3971
|
+
else {
|
|
3972
|
+
try {
|
|
3973
|
+
const enumeration = await this.listMemoryScopes();
|
|
3974
|
+
if (enumeration.supported) {
|
|
3975
|
+
const face = this.committedAuditFace();
|
|
3976
|
+
const headers = await face.listHeaders(enumeration.scopes);
|
|
3977
|
+
const productIds = headers.filter((h) => h.distilled !== undefined).map((h) => h.id);
|
|
3978
|
+
const folded = new Set();
|
|
3979
|
+
if (productIds.length > 0) {
|
|
3980
|
+
for (const p of await face.getByIds(productIds)) {
|
|
3981
|
+
for (const row of p.frontmatter.distilled?.inputs ?? [])
|
|
3982
|
+
if (mine.has(row.id))
|
|
3983
|
+
folded.add(row.id);
|
|
3984
|
+
}
|
|
3985
|
+
}
|
|
3986
|
+
out.foldedCount = folded.size;
|
|
3987
|
+
}
|
|
3988
|
+
}
|
|
3989
|
+
catch {
|
|
3990
|
+
}
|
|
3991
|
+
}
|
|
3992
|
+
}
|
|
3993
|
+
return out;
|
|
3994
|
+
}
|
|
3924
3995
|
committedAuditFace() {
|
|
3925
3996
|
const b = this.backend;
|
|
3926
3997
|
return b.restrictedAdoptionView?.({ audit: false }) ?? b.retrievalView?.() ?? this.backend;
|
|
@@ -1,4 +1,4 @@
|
|
|
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, memoryConsolidationRecommendedNotice, memoryConsolidationCommittedNotice, memoryConsolidationConflictNotice, memoryConsolidationIncompleteNotice, memoryConsolidationRefusedNotice, memoryConsolidationWithheldNotice, MEMORY_CAPTURE_OPTOUT_NOTICE, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, type ConsolidationCommitReceipt, type ConsolidationReconcileReport, type ConsolidationResolveReceipt, type ConsolidationPlanSummary, type ConsolidationPlanFoldEvidence, } 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, type MemoryEngineOptions, type MemoryInjection, type EntryProvenanceAccount, memoryConsolidationRecommendedNotice, memoryConsolidationCommittedNotice, memoryConsolidationConflictNotice, memoryConsolidationIncompleteNotice, memoryConsolidationRefusedNotice, memoryConsolidationWithheldNotice, MEMORY_CAPTURE_OPTOUT_NOTICE, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, type ConsolidationCommitReceipt, type ConsolidationReconcileReport, type ConsolidationResolveReceipt, type ConsolidationPlanSummary, type ConsolidationPlanFoldEvidence, type SessionMemoryStatus, } from "./engine.js";
|
|
2
2
|
export { MEMORY_SEARCH_TOOL_NAME, MEMORY_GET_TOOL_NAME, MEMORY_INDEX_TOOL_NAME, MEMORY_ENGINE_TOOL_NAMES, type MemorySearchDetails, type MemorySearchHit, type CleanMemorySearchHit, type ExposedMemorySearchHit, type MemoryGetDetails, type MemoryIndexDetails, type MemoryIndexRow, type CleanMemoryIndexRow, type ExposedMemoryIndexRow, } from "./tools.js";
|
|
3
3
|
export { MEMORY_EXPOSURE_BANNER, MEMORY_EXPOSURE_HANDLE_TAG, MEMORY_PROVENANCE_RECALL_SENTENCE, MEMORY_PROVENANCE_SEARCH_SENTENCE, memoryExposureIndexRow, parseMemoryExposureIndexRow, } from "./provenance-wording.js";
|
|
4
4
|
export { scanMemoryWrite, scanMemoryFileName, scanRemediation, MEMORY_FILENAME_SEGMENT_RE } from "./scan.js";
|
|
@@ -407,11 +407,19 @@ export declare function listSessionCaptureOptOut(controlDir: string): Record<str
|
|
|
407
407
|
* (`controlDir` + `sessionId` coordinates that outlive any live object — see the settlement handle
|
|
408
408
|
* doc in prepare-task.ts), and a live store object cannot ride that lane; its migration needs its
|
|
409
409
|
* own seam design and is registered here rather than half-built.
|
|
410
|
+
*
|
|
411
|
+
* DUAL FORM (#511 件2, additive): each leg may answer `T` OR `Promise<T>` — a SQL/remote carrier is
|
|
412
|
+
* async by nature and could not implement the sync-only trio at all. Every core consumption point
|
|
413
|
+
* awaits (or `.then`-chains where a sync public seat must stay sync-transparent), so a sync store's
|
|
414
|
+
* behavior is unchanged and an async store's Promise legs keep the exact same contract per leg:
|
|
415
|
+
* a rejected `mark` promise is held to the `"unpersisted"` refusal arm, a rejected `read` promise
|
|
416
|
+
* is the INDETERMINATE fault axis, a rejected `list` promise propagates as the enumeration failure.
|
|
417
|
+
* The File trio below keeps its synchronous signatures byte-identical.
|
|
410
418
|
*/
|
|
411
419
|
export interface SessionCaptureRecordStore {
|
|
412
|
-
mark(sessionId: string, record: SessionCaptureOptOutRecord): SessionCaptureOptOutMarkOutcome
|
|
413
|
-
read(sessionId: string): SessionCaptureOptOutRecord | undefined
|
|
414
|
-
list(): Record<string, SessionCaptureOptOutRecord
|
|
420
|
+
mark(sessionId: string, record: SessionCaptureOptOutRecord): SessionCaptureOptOutMarkOutcome | Promise<SessionCaptureOptOutMarkOutcome>;
|
|
421
|
+
read(sessionId: string): SessionCaptureOptOutRecord | undefined | Promise<SessionCaptureOptOutRecord | undefined>;
|
|
422
|
+
list(): Record<string, SessionCaptureOptOutRecord> | Promise<Record<string, SessionCaptureOptOutRecord>>;
|
|
415
423
|
}
|
|
416
424
|
/** The core-default {@link SessionCaptureRecordStore}: the file trio over `controlDir` (single-
|
|
417
425
|
* process / single-host deployments keep today's carrier byte-identical). */
|
package/dist/core/roles.d.ts
CHANGED
|
@@ -22,7 +22,12 @@ export declare const CC_MODEL_TIER_ALIASES: Readonly<Record<string, string>>;
|
|
|
22
22
|
* - an existing catalog key of the same name WINS (deployment SHADOW semantics, mirroring agents):
|
|
23
23
|
* a deployment that already ships a model literally named "pro" keeps it untouched;
|
|
24
24
|
* - a binding may itself be a catalog name or a Model object (resolved through `resolveModel`);
|
|
25
|
-
* an unknown binding name throws at construction (config error surfaces at boot, not first use)
|
|
25
|
+
* an unknown binding name throws at construction (config error surfaces at boot, not first use);
|
|
26
|
+
* - `"best"` (dynamic head-of-chain alias, CC `$L` parity) resolves to the FIRST tier in
|
|
27
|
+
* {@link DEFAULT_TIER_ORDER} that carries a DIRECT binding — "the strongest model this deployment
|
|
28
|
+
* actually bound", not a fixed tier — under the same SHADOW rule (a deployment catalog key named
|
|
29
|
+
* `best` wins); with no bound known-order tier (or no tiers at all) the key stays absent and
|
|
30
|
+
* `resolveModel("best")` refuses like any unknown ref.
|
|
26
31
|
*/
|
|
27
32
|
/** the DISPLAY face of a string model ref: a CC tier ALIAS (haiku/sonnet/…) resolves to its
|
|
28
33
|
* sema tier name (lite/flash/…) for labels — the alias verbatim reads as a strong claim about a
|
package/dist/core/roles.js
CHANGED
|
@@ -3,7 +3,7 @@ export function resolveModel(ref, models) {
|
|
|
3
3
|
if (typeof ref !== "string") {
|
|
4
4
|
return ref;
|
|
5
5
|
}
|
|
6
|
-
const m = models
|
|
6
|
+
const m = models !== undefined && Object.hasOwn(models, ref) ? models[ref] : undefined;
|
|
7
7
|
if (!m) {
|
|
8
8
|
throw new Error(`Unknown model ref "${ref}". Provide it in RunnerDeps.models or pass a Model object.`);
|
|
9
9
|
}
|
|
@@ -59,6 +59,15 @@ export function expandTiers(models, tiers) {
|
|
|
59
59
|
if (m)
|
|
60
60
|
out[alias] = m;
|
|
61
61
|
}
|
|
62
|
+
if (!out["best"]) {
|
|
63
|
+
for (const name of DEFAULT_TIER_ORDER) {
|
|
64
|
+
const m = bound(name);
|
|
65
|
+
if (m) {
|
|
66
|
+
out["best"] = m;
|
|
67
|
+
break;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
62
71
|
return out;
|
|
63
72
|
}
|
|
64
73
|
export function parseModelMention(text, allowedNames) {
|
|
@@ -174,8 +174,8 @@ export async function prepareMemory(input) {
|
|
|
174
174
|
};
|
|
175
175
|
const onEngineIncident = (err) => deps.onError?.(err, { phase: "memory", sessionId });
|
|
176
176
|
const adoptionRestricted = input.memoryPersistenceDeclared === false;
|
|
177
|
-
const armCaptureOptOut = (eng) => {
|
|
178
|
-
const standingRead = eng.sessionCaptureOptOutOrFault(sessionId);
|
|
177
|
+
const armCaptureOptOut = async (eng) => {
|
|
178
|
+
const standingRead = await eng.sessionCaptureOptOutOrFault(sessionId);
|
|
179
179
|
if (standingRead.fault) {
|
|
180
180
|
captureIndeterminate = true;
|
|
181
181
|
deps.onError?.(new Error(`the capture opt-out record store is unreadable at prepare — this session's capture state is indeterminate, and every memory commit boundary (read-side adoption included) suppresses fail-closed until the store answers.`), { phase: "memory", sessionId });
|
|
@@ -199,7 +199,7 @@ export async function prepareMemory(input) {
|
|
|
199
199
|
let anyFault = standingRead.fault;
|
|
200
200
|
let found = false;
|
|
201
201
|
for (const [ancestorId, ancestorDir] of rows) {
|
|
202
|
-
const samePlane = eng.sessionCaptureOptOutOrFault(ancestorId);
|
|
202
|
+
const samePlane = await eng.sessionCaptureOptOutOrFault(ancestorId);
|
|
203
203
|
if (samePlane.record !== undefined) {
|
|
204
204
|
found = true;
|
|
205
205
|
break;
|
|
@@ -208,7 +208,7 @@ export async function prepareMemory(input) {
|
|
|
208
208
|
anyFault = true;
|
|
209
209
|
if (ancestorDir !== undefined && ancestorDir !== eng.controlPlaneDir) {
|
|
210
210
|
try {
|
|
211
|
-
const crossPlane = deps.memoryCaptureRecordStore !== undefined ? deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
211
|
+
const crossPlane = deps.memoryCaptureRecordStore !== undefined ? await deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
212
212
|
if (crossPlane !== undefined) {
|
|
213
213
|
found = true;
|
|
214
214
|
break;
|
|
@@ -230,7 +230,7 @@ export async function prepareMemory(input) {
|
|
|
230
230
|
}
|
|
231
231
|
let forkInherited = false;
|
|
232
232
|
if (!floored && !captureDeclared && standing === undefined && input.captureForkOrigin !== undefined) {
|
|
233
|
-
const src = eng.sessionCaptureOptOutOrFault(input.captureForkOrigin);
|
|
233
|
+
const src = await eng.sessionCaptureOptOutOrFault(input.captureForkOrigin);
|
|
234
234
|
if (src.record !== undefined)
|
|
235
235
|
forkInherited = true;
|
|
236
236
|
else if (src.fault && !captureIndeterminate) {
|
|
@@ -246,7 +246,7 @@ export async function prepareMemory(input) {
|
|
|
246
246
|
throw captureOptOutDeniedError(verdict.detail);
|
|
247
247
|
}
|
|
248
248
|
if (standing === undefined) {
|
|
249
|
-
const outcome = eng.markSessionCaptureOptOut(sessionId, captureDeclared ? `declared on TaskSpec.memory.capture` : forkInherited ? `inherited capture opt-out from the forked source session` : `inherited capture opt-out floor from the spawning session`);
|
|
249
|
+
const outcome = await eng.markSessionCaptureOptOut(sessionId, captureDeclared ? `declared on TaskSpec.memory.capture` : forkInherited ? `inherited capture opt-out from the forked source session` : `inherited capture opt-out floor from the spawning session`);
|
|
250
250
|
if (outcome === "unpersisted") {
|
|
251
251
|
deliverEngineNotice(deps.onNotice, memoryCaptureOptOutUnpersistedNotice({ sessionId, ingress: "declaration" }));
|
|
252
252
|
throw captureOptOutUnpersistedError(sessionId, "declaration");
|
|
@@ -287,14 +287,14 @@ export async function prepareMemory(input) {
|
|
|
287
287
|
let injectFn;
|
|
288
288
|
let harvestBoth;
|
|
289
289
|
let toolPlanes;
|
|
290
|
-
const admitNothingOptsNow = () => {
|
|
290
|
+
const admitNothingOptsNow = async () => {
|
|
291
291
|
if (input.memoryPersistenceDeclared === false) {
|
|
292
292
|
return { admitNothing: { reason: "harvest admitted nothing: memory persistence is declared unavailable for this session (memoryPersistenceCapable: false)" } };
|
|
293
293
|
}
|
|
294
294
|
if (captureLineageIndeterminate) {
|
|
295
295
|
return { admitNothing: { reason: `harvest admitted nothing: this session's fork-lineage metadata is unreadable — whether it continues an opted-out source cannot be established, so commits are suppressed fail-closed` } };
|
|
296
296
|
}
|
|
297
|
-
const own = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
297
|
+
const own = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : await writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
298
298
|
if (own.record !== undefined) {
|
|
299
299
|
return { admitNothing: { reason: `harvest admitted nothing: this session declared memory capture opt-out (memory.capture: "off")` } };
|
|
300
300
|
}
|
|
@@ -309,11 +309,11 @@ export async function prepareMemory(input) {
|
|
|
309
309
|
if (input.captureForkOrigin !== undefined && !ancestors.has(input.captureForkOrigin))
|
|
310
310
|
ancestors.set(input.captureForkOrigin, undefined);
|
|
311
311
|
for (const [ancestorId, ancestorDir] of ancestors) {
|
|
312
|
-
const samePlane = writeEngine.sessionCaptureOptOutOrFault(ancestorId);
|
|
312
|
+
const samePlane = await writeEngine.sessionCaptureOptOutOrFault(ancestorId);
|
|
313
313
|
let crossPlane;
|
|
314
314
|
if (ancestorDir !== undefined && ancestorDir !== writeEngine.controlPlaneDir) {
|
|
315
315
|
try {
|
|
316
|
-
crossPlane = deps.memoryCaptureRecordStore !== undefined ? deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
316
|
+
crossPlane = deps.memoryCaptureRecordStore !== undefined ? await deps.memoryCaptureRecordStore({ controlDir: ancestorDir }).read(ancestorId) : readSessionCaptureOptOut(ancestorDir, ancestorId);
|
|
317
317
|
}
|
|
318
318
|
catch {
|
|
319
319
|
return { admitNothing: { reason: `harvest admitted nothing: a spawning session's capture record store is unreadable — the floor state is indeterminate and commits are suppressed fail-closed` } };
|
|
@@ -362,7 +362,7 @@ export async function prepareMemory(input) {
|
|
|
362
362
|
const personal = createPersonalEngine(personalBackendChosen);
|
|
363
363
|
const personalEngine = personal.engine;
|
|
364
364
|
const p = planes;
|
|
365
|
-
armCaptureOptOut(p.writePlane === "personal" ? personalEngine : projectEngine);
|
|
365
|
+
await armCaptureOptOut(p.writePlane === "personal" ? personalEngine : projectEngine);
|
|
366
366
|
const projectHandle = await projectEngine.materialize(p.project, p.writePlane === "project" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
367
367
|
materializedResidue.push(...planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null));
|
|
368
368
|
const personalHandle = await personalEngine.materialize(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
@@ -393,7 +393,7 @@ export async function prepareMemory(input) {
|
|
|
393
393
|
harvestBoth = async () => {
|
|
394
394
|
const writeFirst = writeIsPersonal ? [personalEngine, personalHandle] : [projectEngine, projectHandle];
|
|
395
395
|
const readOther = writeIsPersonal ? [projectEngine, projectHandle] : [personalEngine, personalHandle];
|
|
396
|
-
const writeReport = await writeFirst[0].harvest(writeFirst[1], { ...pollutedOpts(writeFirst[0]), sessionId, ...admitNothingOptsNow() });
|
|
396
|
+
const writeReport = await writeFirst[0].harvest(writeFirst[1], { ...pollutedOpts(writeFirst[0]), sessionId, ...(await admitNothingOptsNow()) });
|
|
397
397
|
let readReport;
|
|
398
398
|
let readFailure;
|
|
399
399
|
try {
|
|
@@ -411,14 +411,14 @@ export async function prepareMemory(input) {
|
|
|
411
411
|
else if (personalOnly) {
|
|
412
412
|
const personal = createPersonalEngine(choosePersonalBackend());
|
|
413
413
|
const personalEngine = personal.engine;
|
|
414
|
-
armCaptureOptOut(personalEngine);
|
|
414
|
+
await armCaptureOptOut(personalEngine);
|
|
415
415
|
const handle = await personalEngine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
416
416
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
417
417
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
418
418
|
writeEngine = personalEngine;
|
|
419
419
|
writeHandle = handle;
|
|
420
420
|
injectFn = () => personalEngine.inject(handle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark });
|
|
421
|
-
harvestBoth = () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...admitNothingOptsNow() });
|
|
421
|
+
harvestBoth = async () => personalEngine.harvest(handle, { ...pollutedOpts(personalEngine), sessionId, ...(await admitNothingOptsNow()) });
|
|
422
422
|
toolPlanes = [
|
|
423
423
|
{
|
|
424
424
|
backend: retrievalBackend(personal.backend, adoptionRestricted || captureOptedOut || captureIndeterminate || memorySpec.writeScope === null),
|
|
@@ -439,14 +439,14 @@ export async function prepareMemory(input) {
|
|
|
439
439
|
...captureStoreOpt(backend, identityKey !== undefined ? deriveProjectControlDir(engineRoot, identityKey) : deriveRepoControlPlaneDir(engineRoot, repoRoot)),
|
|
440
440
|
...(input.deps.memoryConsolidation !== undefined ? { consolidation: input.deps.memoryConsolidation } : {}),
|
|
441
441
|
});
|
|
442
|
-
armCaptureOptOut(engine);
|
|
442
|
+
await armCaptureOptOut(engine);
|
|
443
443
|
const handle = await engine.materialize(memorySpec.scopes, memorySpec.writeScope, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
|
|
444
444
|
materializedResidue.push(...planeScopes(memorySpec.scopes, memorySpec.writeScope));
|
|
445
445
|
memoryMarkedEntriesServed = handle.markedEntriesPresent === true;
|
|
446
446
|
writeEngine = engine;
|
|
447
447
|
writeHandle = handle;
|
|
448
448
|
injectFn = () => engine.inject(handle, { writeToolMounted: input.writeToolsMounted, reminderMark: input.reminderMark });
|
|
449
|
-
harvestBoth = () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...admitNothingOptsNow() });
|
|
449
|
+
harvestBoth = async () => engine.harvest(handle, { ...pollutedOpts(engine), sessionId, ...(await admitNothingOptsNow()) });
|
|
450
450
|
toolPlanes = [
|
|
451
451
|
{
|
|
452
452
|
backend: retrievalBackend(backend, adoptionRestricted || captureOptedOut || captureIndeterminate || memorySpec.writeScope === null),
|
|
@@ -457,7 +457,7 @@ export async function prepareMemory(input) {
|
|
|
457
457
|
},
|
|
458
458
|
];
|
|
459
459
|
}
|
|
460
|
-
memoryWriteGateRef.current = (w) => {
|
|
460
|
+
memoryWriteGateRef.current = async (w) => {
|
|
461
461
|
if (readOnlyEngine !== undefined && readOnlyHandle !== undefined) {
|
|
462
462
|
const ro = readOnlyEngine.gateWrite(readOnlyHandle, w.key, w.content);
|
|
463
463
|
if (!ro.ok)
|
|
@@ -477,7 +477,7 @@ export async function prepareMemory(input) {
|
|
|
477
477
|
{
|
|
478
478
|
const root = writeHandle.writableRoot;
|
|
479
479
|
if (w.key === root || w.key.startsWith(`${root}${sep}`)) {
|
|
480
|
-
const state = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
480
|
+
const state = captureOptedOut ? { record: { at: 0, reason: "in-run state" }, fault: false } : await writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
481
481
|
if (state.record !== undefined || state.fault) {
|
|
482
482
|
return {
|
|
483
483
|
ok: false,
|
|
@@ -555,12 +555,19 @@ export async function prepareMemory(input) {
|
|
|
555
555
|
inject: injectFn,
|
|
556
556
|
harvest: harvestSafe,
|
|
557
557
|
captureOptOut: {
|
|
558
|
-
optedOut: () =>
|
|
558
|
+
optedOut: () => {
|
|
559
|
+
if (captureOptedOut)
|
|
560
|
+
return true;
|
|
561
|
+
const r = writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
562
|
+
return r instanceof Promise ? r.then((s) => s.record !== undefined) : r.record !== undefined;
|
|
563
|
+
},
|
|
559
564
|
indeterminate: () => {
|
|
560
565
|
if (captureOptedOut)
|
|
561
566
|
return false;
|
|
562
|
-
const
|
|
563
|
-
return
|
|
567
|
+
const r = writeEngine.sessionCaptureOptOutOrFault(sessionId);
|
|
568
|
+
return r instanceof Promise
|
|
569
|
+
? r.then((s) => s.record === undefined && (captureIndeterminate || s.fault))
|
|
570
|
+
: r.record === undefined && (captureIndeterminate || r.fault);
|
|
564
571
|
},
|
|
565
572
|
flip: async (reason) => {
|
|
566
573
|
const entitlement = await resolveCaptureEntitlementFresh();
|
|
@@ -576,7 +583,7 @@ export async function prepareMemory(input) {
|
|
|
576
583
|
throw e;
|
|
577
584
|
}
|
|
578
585
|
const trimmed = reason?.trim();
|
|
579
|
-
const outcome = writeEngine.markSessionCaptureOptOut(sessionId, trimmed !== undefined && trimmed !== "" ? `host flip verb: ${trimmed.slice(0, 200)}` : "host flip verb");
|
|
586
|
+
const outcome = await writeEngine.markSessionCaptureOptOut(sessionId, trimmed !== undefined && trimmed !== "" ? `host flip verb: ${trimmed.slice(0, 200)}` : "host flip verb");
|
|
580
587
|
if (outcome === "unpersisted") {
|
|
581
588
|
deliverEngineNotice(deps.onNotice, memoryCaptureOptOutUnpersistedNotice({ sessionId, ingress: "flip-verb" }));
|
|
582
589
|
throw captureOptOutUnpersistedError(sessionId, "flip-verb");
|
|
@@ -550,8 +550,9 @@ export interface Prepared {
|
|
|
550
550
|
* the memory session mounted.
|
|
551
551
|
*/
|
|
552
552
|
captureOptOut?: {
|
|
553
|
-
|
|
554
|
-
|
|
553
|
+
/** Dual form: a sync capture store answers synchronously; a Promise-form store answers a Promise. `await` is correct on either arm. */
|
|
554
|
+
optedOut: () => boolean | Promise<boolean>;
|
|
555
|
+
indeterminate: () => boolean | Promise<boolean>;
|
|
555
556
|
flip: (reason?: string) => Promise<{
|
|
556
557
|
outcome: "created" | "existed";
|
|
557
558
|
}>;
|
|
@@ -894,6 +894,15 @@ function explicitlyDeferredMemoryTrio(mounted, roster, deferNames) {
|
|
|
894
894
|
function memoryGroupRetractionSet(builtinDeferPairNames, engineTrioInPlay) {
|
|
895
895
|
return new Set([...builtinDeferPairNames, ...(engineTrioInPlay ? MEMORY_ENGINE_TOOL_NAMES : [])]);
|
|
896
896
|
}
|
|
897
|
+
function assembleParentCaptureState(o, i, ctl, ancestors) {
|
|
898
|
+
const build = (optedOut, indeterminate) => ({
|
|
899
|
+
optedOut: optedOut === true,
|
|
900
|
+
indeterminate: indeterminate === true,
|
|
901
|
+
...(ctl !== undefined ? { controlDir: ctl } : {}),
|
|
902
|
+
ancestors,
|
|
903
|
+
});
|
|
904
|
+
return o instanceof Promise || i instanceof Promise ? Promise.all([o, i]).then(([ov, iv]) => build(ov, iv)) : build(o, i);
|
|
905
|
+
}
|
|
897
906
|
async function spliceSessionOverlayRows(overlay, sessionId, persisted, tracer, hostTaskId) {
|
|
898
907
|
if (overlay === undefined)
|
|
899
908
|
return persisted;
|
|
@@ -1501,10 +1510,10 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1501
1510
|
...(spec.envFacts !== undefined ? { envFacts: { ...spec.envFacts } } : {}),
|
|
1502
1511
|
...(spec.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: spec.memoryPersistenceCapable } : {}),
|
|
1503
1512
|
get memoryCaptureOptedOut() {
|
|
1504
|
-
return memoryEngineSession?.captureOptOut?.optedOut()
|
|
1513
|
+
return memoryEngineSession?.captureOptOut?.optedOut() ?? false;
|
|
1505
1514
|
},
|
|
1506
1515
|
get memoryCaptureIndeterminate() {
|
|
1507
|
-
return memoryEngineSession?.captureOptOut?.indeterminate()
|
|
1516
|
+
return memoryEngineSession?.captureOptOut?.indeterminate() ?? false;
|
|
1508
1517
|
},
|
|
1509
1518
|
get memoryCaptureControlDir() {
|
|
1510
1519
|
return memoryEngineSession?.engine.controlPlaneDir;
|
|
@@ -1731,12 +1740,8 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
|
|
|
1731
1740
|
...(resolvedInteractionPosture !== undefined ? { parentInteractionPosture: resolvedInteractionPosture } : {}),
|
|
1732
1741
|
parentMemoryCaptureState: () => {
|
|
1733
1742
|
const ctl = memoryEngineSession?.engine.controlPlaneDir;
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
indeterminate: memoryEngineSession?.captureOptOut?.indeterminate() === true,
|
|
1737
|
-
...(ctl !== undefined ? { controlDir: ctl } : {}),
|
|
1738
|
-
ancestors: [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }],
|
|
1739
|
-
};
|
|
1743
|
+
const co = memoryEngineSession?.captureOptOut;
|
|
1744
|
+
return assembleParentCaptureState(co?.optedOut() ?? false, co?.indeterminate() ?? false, ctl, [...(internals?.memoryCaptureAncestors ?? []), { sessionId, ...(ctl !== undefined ? { controlDir: ctl } : {}) }]);
|
|
1740
1745
|
},
|
|
1741
1746
|
autoModeReview: () => (autoModeDecider !== undefined ? { decider: autoModeDecider } : undefined),
|
|
1742
1747
|
workflowDepth: internals?.workflowDepth,
|
|
@@ -1410,6 +1410,7 @@ function makeHarnessHandlers(prepared, stats, rs, deps) {
|
|
|
1410
1410
|
...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
|
|
1411
1411
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
1412
1412
|
...(subagentName ? { name: subagentName } : {}),
|
|
1413
|
+
model: prepared.model.id,
|
|
1413
1414
|
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
|
|
1414
1415
|
status: "running",
|
|
1415
1416
|
...ident(),
|
|
@@ -4223,6 +4224,7 @@ export class Runner {
|
|
|
4223
4224
|
...(internals?.cycleSeq !== undefined ? { seq: internals.cycleSeq } : {}),
|
|
4224
4225
|
...(internals?.parentTaskId !== undefined ? { parentTaskId: internals.parentTaskId } : {}),
|
|
4225
4226
|
...(subagentName ? { name: subagentName } : {}),
|
|
4227
|
+
model: prepared.model.id,
|
|
4226
4228
|
usage: { totalTokens: stats.tokens, toolUses: stats.toolCalls, durationMs: Date.now() - rs.telemetry.taskStart },
|
|
4227
4229
|
status: result.status === "completed" ? "completed" : "failed",
|
|
4228
4230
|
...ident(),
|
|
@@ -66,6 +66,19 @@ export interface TaskNotificationPayload {
|
|
|
66
66
|
* `TaskResult.errorCode` taxonomy (brain `[code]` prefixes via extractErrorCode, `limit.*`,
|
|
67
67
|
* `budget.*`, …). Engine-minted, never model text. Absent when the failure carried no code. */
|
|
68
68
|
errorCode?: string;
|
|
69
|
+
/** Terminal, delegation lanes: the settling run's PROVIDER-BOUNDARY fault assertion, mirrored from
|
|
70
|
+
* its `TaskResult.apiFailure`. Its PRESENCE is the claim — "this failure came from the transport or
|
|
71
|
+
* the provider, not from this deployment refusing to send, a limit, or unusable model output" — and
|
|
72
|
+
* `errorCode` beside it names WHICH terminal; the two answer different questions and neither implies
|
|
73
|
+
* the other. Members are whatever the failing attempt stated about itself (`status` when the provider
|
|
74
|
+
* answered with one, `requestId` when it named one), so an EMPTY object is meaningful: "provider
|
|
75
|
+
* fault, unlabelled", which is not the same statement as absence. Absent on every non-provider
|
|
76
|
+
* terminal, on every completed run, and on the non-delegation lanes (bash/monitor), whose failures do
|
|
77
|
+
* not pass through a model provider at all. */
|
|
78
|
+
apiFailure?: {
|
|
79
|
+
status?: number;
|
|
80
|
+
requestId?: string;
|
|
81
|
+
};
|
|
69
82
|
/** Structured exit code of a background command's terminal notification (bash lane): the process
|
|
70
83
|
* exited on its own with this code. Lets consumers branch on success/failure without parsing the
|
|
71
84
|
* summary wording. Absent when the process never exited by itself (killed / spawn-failed lanes). */
|
|
@@ -77,6 +90,13 @@ export interface TaskNotificationPayload {
|
|
|
77
90
|
* every other notification (a completed result is never flagged). */
|
|
78
91
|
partial?: boolean;
|
|
79
92
|
output_file?: string;
|
|
93
|
+
/** Whatever the settling lane knows about its own spend, JSON-rendered verbatim into one `<usage>`
|
|
94
|
+
* tag. Deliberately untyped — the lanes report different quantities and nothing here should force
|
|
95
|
+
* one to fabricate a figure it does not have. What IS contracted is the VOCABULARY: a name that two
|
|
96
|
+
* lanes both publish must mean the same thing on both. The delegation lanes
|
|
97
|
+
* (`background_agent`, its fork/revive cycles) and the workflow lane therefore agree on
|
|
98
|
+
* `tokens`/`turns`/`costMicroUsd`/`tool_uses`/`duration_ms`; the workflow lane's fan-out counters
|
|
99
|
+
* (`agent_count`, `agents_done`, …) and resume counters have no delegation analog and are its own. */
|
|
80
100
|
usage?: unknown;
|
|
81
101
|
/** CC `<diagnostics>` parity: ENGINE-MINTED teaching text for the "result is empty/unexpected —
|
|
82
102
|
* now what" moment (per-agent read route / journal coordinate / resume command). Producers must mint it
|