@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
package/dist/agents/subagent.js
CHANGED
|
@@ -37,10 +37,27 @@ function delegatedCostField(stats) {
|
|
|
37
37
|
const total = rollupDelegatedCost(stats, stats.nested);
|
|
38
38
|
return total !== undefined ? { costMicroUsd: total } : {};
|
|
39
39
|
}
|
|
40
|
+
export function delegatedGranularityFields(stats, elapsedMs) {
|
|
41
|
+
return { ...(stats.toolCalls !== undefined ? { toolUses: stats.toolCalls } : {}), durationMs: elapsedMs };
|
|
42
|
+
}
|
|
43
|
+
function providerFaultField(child) {
|
|
44
|
+
if (child.apiFailure === undefined)
|
|
45
|
+
return {};
|
|
46
|
+
return {
|
|
47
|
+
apiFailure: {
|
|
48
|
+
...(typeof child.apiFailure.status === "number" ? { status: child.apiFailure.status } : {}),
|
|
49
|
+
...(typeof child.apiFailure.requestId === "string" ? { requestId: child.apiFailure.requestId } : {}),
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function delegatedGranularityFieldsWire(stats, elapsedMs) {
|
|
54
|
+
return { ...(stats.toolCalls !== undefined ? { tool_uses: stats.toolCalls } : {}), duration_ms: elapsedMs };
|
|
55
|
+
}
|
|
40
56
|
export { RETAIN_DEFAULT_TTL_MS, RETAIN_DEFAULT_MAX };
|
|
41
57
|
import { SUBAGENT_RESUME_CAP, SubagentRetainLedger, getOrCreateSessionRetainLedger, ensureSessionReapHook, createResumePrompt, } from "./retain-ledger.js";
|
|
42
58
|
import { createPeerInboundChainRef, createPeerSelfRef } from "./peer-admission.js";
|
|
43
59
|
import { recordRosterSpawn } from "./roster-store.js";
|
|
60
|
+
import { LAUNCH_RECEIPT_OWN_WORDS_CLAUSE, launchReceiptNoQuoteClause } from "./launch-receipt-contract.js";
|
|
44
61
|
import { ObserverDigestTap, ObserverPairing, createObserverReportToolSpec, markObserverTaskId, unmarkObserverTaskId, isObserverTaskId, ObserverResumeStateError, ObserverStoppedByUserError, observerFramingPrompt, observerSlug, resolveObserverDeclaration, } from "./observer.js";
|
|
45
62
|
import { SubagentStepRecorder } from "./subagent-steps.js";
|
|
46
63
|
const BG_AGENT_RESULT_MAX = 4_000;
|
|
@@ -326,6 +343,7 @@ function resumabilityClaim(f) {
|
|
|
326
343
|
const BG_AGENT_COLLATERAL_REAP_REASON = "its parent run ended";
|
|
327
344
|
const RESERVED_AGENT_NAMES = new Set([OUTPUT_TOOL_NAME, TOOL_SEARCH_NAME, OFFLOAD_TOOL_NAME, REPORT_BLOCKED_TOOL_NAME, DEFAULT_SUBAGENT_TOOL_NAME]);
|
|
328
345
|
export const REPORT_FIELD_MAX = 300;
|
|
346
|
+
const ROSTER_ECHO_MAX = 400;
|
|
329
347
|
function configError(message, code) {
|
|
330
348
|
const e = new Error(message);
|
|
331
349
|
e.code = code;
|
|
@@ -364,6 +382,7 @@ export function completedAgentCard(child, extras) {
|
|
|
364
382
|
...(child.blockedReason !== undefined ? { blockedReason: child.blockedReason } : {}),
|
|
365
383
|
...(child.errorMessage !== undefined ? { errorMessage: child.errorMessage } : {}),
|
|
366
384
|
...(child.errorCode !== undefined ? { errorCode: child.errorCode } : {}),
|
|
385
|
+
...providerFaultField(child),
|
|
367
386
|
...(child.retryAfterMs !== undefined ? { retryAfterMs: child.retryAfterMs } : {}),
|
|
368
387
|
...(child.degraded !== undefined ? { degraded: child.degraded } : {}),
|
|
369
388
|
...(extras.handbackWarning !== undefined ? { handbackWarning: extras.handbackWarning } : {}),
|
|
@@ -711,9 +730,10 @@ export function createSubagentResume(deps) {
|
|
|
711
730
|
const completionIdRevive = deps.registry?.getCompletionId(deps.taskId ?? "");
|
|
712
731
|
const reviveName = deps.rowDescription ?? `sub-agent ${marker}`;
|
|
713
732
|
const failReasonRevive = status === "failed" ? child.errorMessage : undefined;
|
|
733
|
+
const reviveElapsedMs = Date.now() - reviveStartedAt;
|
|
714
734
|
const reviveTerminalSummary = failReasonRevive !== undefined
|
|
715
|
-
? `Agent "${reviveName}" (resumed) failed: ${failReasonRevive}${ccElapsedTag(
|
|
716
|
-
: `Agent "${reviveName}" (resumed) ${status === "killed" ? "stopped" : child.status === "completed" ? "finished" : String(child.status)}${ccElapsedTag(
|
|
735
|
+
? `Agent "${reviveName}" (resumed) failed: ${failReasonRevive}${ccElapsedTag(reviveElapsedMs)}`.slice(0, 300) + errorKindClause(errClassRevive)
|
|
736
|
+
: `Agent "${reviveName}" (resumed) ${status === "killed" ? "stopped" : child.status === "completed" ? "finished" : String(child.status)}${ccElapsedTag(reviveElapsedMs)}`;
|
|
717
737
|
const reviveDurableProbe = deps.registry !== undefined && deps.taskId !== undefined ? deps.registry.durableAgentRowProbe(deps.taskId) : undefined;
|
|
718
738
|
const reviveDurableRow = reviveDurableProbe !== undefined ? await reviveDurableProbe().catch(() => false) : false;
|
|
719
739
|
const resumableRevive = resumabilityClaim({
|
|
@@ -739,7 +759,7 @@ export function createSubagentResume(deps) {
|
|
|
739
759
|
summary: reviveTerminalSummary,
|
|
740
760
|
...resumeResidual(),
|
|
741
761
|
resumable: resumableRevive,
|
|
742
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
762
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats), ...delegatedGranularityFields(child.stats, reviveElapsedMs) },
|
|
743
763
|
...(completionIdRevive !== undefined ? { completionId: completionIdRevive } : {}),
|
|
744
764
|
});
|
|
745
765
|
const resumeFrame = {
|
|
@@ -753,11 +773,12 @@ export function createSubagentResume(deps) {
|
|
|
753
773
|
summary: reviveTerminalSummary,
|
|
754
774
|
...(failReasonRevive !== undefined ? { error: failReasonRevive.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
755
775
|
...(status === "failed" && errCodeRevive !== undefined ? { errorCode: errCodeRevive } : {}),
|
|
776
|
+
...providerFaultField(child),
|
|
756
777
|
...(resumeHandbackResult ? { result: notifyResultField(resumeHandbackResult) } : {}),
|
|
757
778
|
...(status === "killed" && child.result ? { partial: true } : {}),
|
|
758
779
|
...resumeResidual(),
|
|
759
780
|
resumable: resumableRevive,
|
|
760
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
781
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats), ...delegatedGranularityFieldsWire(child.stats, reviveElapsedMs) },
|
|
761
782
|
...(completionIdRevive !== undefined ? { completionId: completionIdRevive } : {}),
|
|
762
783
|
};
|
|
763
784
|
const gateOwner = ownsTerminalFacesRevive && status === "completed" && deps.notify !== undefined && deps.registry !== undefined && ledger.get(deps.parentToolCallId) === entry
|
|
@@ -1206,7 +1227,7 @@ export function forkWorktreeTranslationNote(parentCwd, worktreeDir) {
|
|
|
1206
1227
|
export function asyncLaunchedReceipt(p) {
|
|
1207
1228
|
const noteLines = (p.notes ?? []).filter((n) => n !== undefined && n !== "");
|
|
1208
1229
|
const oneShotBlocked = p.notify && p.oneShot === true;
|
|
1209
|
-
return (`Async agent launched successfully. (
|
|
1230
|
+
return (`Async agent launched successfully. (${launchReceiptNoQuoteClause(", including the task_id below,")})\n` +
|
|
1210
1231
|
`task_id: ${p.taskId} (internal ID - do not mention to user. Use SendMessage with to: '${p.taskId}', summary: '<5-10 word recap>' to continue this agent.)\n` +
|
|
1211
1232
|
(oneShotBlocked
|
|
1212
1233
|
? `${p.workingLine} This is a ONE-SHOT submission — there is no later turn for a background notification to land in, so do NOT end your turn expecting one. Actively wait instead: TaskOutput(task_id: "${p.taskId}", block: true). If it is still running after the wait, wait again (bounded) rather than ending the turn, or write out your best available answer now if you are near your own time budget.\n`
|
|
@@ -1215,10 +1236,18 @@ export function asyncLaunchedReceipt(p) {
|
|
|
1215
1236
|
: `${p.workingLine} Its result is NOT pushed automatically — retrieve progress and results with TaskOutput(task_id) where mounted.\n`) +
|
|
1216
1237
|
noteLines.map((n) => `${n}\n`).join("") +
|
|
1217
1238
|
(oneShotBlocked
|
|
1218
|
-
?
|
|
1239
|
+
? `${LAUNCH_RECEIPT_OWN_WORDS_CLAUSE} Do not assume a later message will deliver the result: this submission ends after this turn, so retrieve it now with the blocking TaskOutput wait above before writing your final answer.`
|
|
1219
1240
|
: p.notify
|
|
1220
|
-
?
|
|
1221
|
-
:
|
|
1241
|
+
? `${LAUNCH_RECEIPT_OWN_WORDS_CLAUSE} Agent results will arrive in a subsequent message. If the user asks for progress, say the agent is still running.`
|
|
1242
|
+
: `${LAUNCH_RECEIPT_OWN_WORDS_CLAUSE} The result will NOT arrive on its own — retrieve it with TaskOutput(task_id) where mounted before relying on it.`));
|
|
1243
|
+
}
|
|
1244
|
+
function runnerModelCatalog(runner) {
|
|
1245
|
+
try {
|
|
1246
|
+
return runner?.agentCatalog?.models;
|
|
1247
|
+
}
|
|
1248
|
+
catch {
|
|
1249
|
+
return undefined;
|
|
1250
|
+
}
|
|
1222
1251
|
}
|
|
1223
1252
|
export function createSubagentTool(opts) {
|
|
1224
1253
|
const catalog = opts.runner.agentCatalog;
|
|
@@ -1227,8 +1256,7 @@ export function createSubagentTool(opts) {
|
|
|
1227
1256
|
if (opts.builtinAgents === undefined && catalog?.builtinAgents !== undefined) {
|
|
1228
1257
|
opts = { ...opts, builtinAgents: catalog.builtinAgents };
|
|
1229
1258
|
}
|
|
1230
|
-
|
|
1231
|
-
opts = { ...opts, models: catalog.models };
|
|
1259
|
+
const assemblyModels = opts.models ?? catalog?.models;
|
|
1232
1260
|
if (opts.agents?.length) {
|
|
1233
1261
|
opts = {
|
|
1234
1262
|
...opts,
|
|
@@ -1252,7 +1280,7 @@ export function createSubagentTool(opts) {
|
|
|
1252
1280
|
}
|
|
1253
1281
|
if (typeof a.model === "string") {
|
|
1254
1282
|
try {
|
|
1255
|
-
resolveModel(a.model,
|
|
1283
|
+
resolveModel(a.model, assemblyModels);
|
|
1256
1284
|
}
|
|
1257
1285
|
catch {
|
|
1258
1286
|
throw configError(`createSubagentTool: agent "${a.name}" references unknown model "${a.model}" (pass \`models\` to resolve string refs).`, "config.agent.unknown_model");
|
|
@@ -1308,7 +1336,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1308
1336
|
...(forkOffered ? [FORK_SUBAGENT_TYPE] : []),
|
|
1309
1337
|
...(generalPurposeShadowed ? [] : [GENERAL_PURPOSE_SUBAGENT_TYPE]),
|
|
1310
1338
|
];
|
|
1311
|
-
const
|
|
1339
|
+
const activeModels = () => opts.models ?? runnerModelCatalog(opts.runner);
|
|
1340
|
+
const mountModels = activeModels();
|
|
1341
|
+
const rosterNames = mountModels ? Object.keys(mountModels) : undefined;
|
|
1312
1342
|
const agentListing = [
|
|
1313
1343
|
...(generalPurposeShadowed
|
|
1314
1344
|
? []
|
|
@@ -1424,7 +1454,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1424
1454
|
})),
|
|
1425
1455
|
model: Type.Optional(Type.String({
|
|
1426
1456
|
description: rosterNames !== undefined && rosterNames.length > 0
|
|
1427
|
-
? `Optional model override for this agent. Takes precedence over the agent definition's model. If omitted, uses the agent definition's model, or inherits from the parent. Ignored for subagent_type: "${FORK_SUBAGENT_TYPE}" — forks always inherit the parent model.`
|
|
1457
|
+
? `Optional model override for this agent. Takes precedence over the agent definition's model. If omitted, uses the agent definition's model, or inherits from the parent. A value outside this deployment's model catalog is REFUSED — the call does not fall back silently. Ignored for subagent_type: "${FORK_SUBAGENT_TYPE}" — forks always inherit the parent model.`
|
|
1428
1458
|
: `Optional model override request. No model catalog is configured here, so a requested name cannot be resolved — the agent runs its definition's model (or inherits from the parent) and the result notes the override was not applied. Ignored for subagent_type: "${FORK_SUBAGENT_TYPE}".`,
|
|
1429
1459
|
})),
|
|
1430
1460
|
isolation: Type.Optional(Type.Literal("worktree", {
|
|
@@ -1560,23 +1590,75 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1560
1590
|
};
|
|
1561
1591
|
}
|
|
1562
1592
|
}
|
|
1593
|
+
const modelObjectForm = typeof a.model === "object" &&
|
|
1594
|
+
a.model !== null &&
|
|
1595
|
+
!Array.isArray(a.model) &&
|
|
1596
|
+
typeof a.model.id === "string" &&
|
|
1597
|
+
typeof a.model.api === "string";
|
|
1598
|
+
if (a.model !== undefined && typeof a.model !== "string" && !modelObjectForm) {
|
|
1599
|
+
const typeDesc = Array.isArray(a.model) ? "an array" : a.model === null ? "null" : typeof a.model === "object" ? "an object" : `a ${typeof a.model}`;
|
|
1600
|
+
return {
|
|
1601
|
+
isError: true,
|
|
1602
|
+
content: `Sub-agent not started: \`model\` must be a model name (string) from this deployment's catalog — got ${typeDesc}. Omit the parameter to run the agent type's own model.`,
|
|
1603
|
+
details: { error: "model.invalid" },
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
if (typeof a.model === "string" && a.model.trim() === "" && !wantsFork && reviveClaim === undefined) {
|
|
1607
|
+
return {
|
|
1608
|
+
isError: true,
|
|
1609
|
+
content: "Sub-agent not started: `model` was empty. Name a model from this deployment's catalog, or omit the parameter to run the agent type's own model.",
|
|
1610
|
+
details: { error: "model.empty" },
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1563
1613
|
const requestedModel = typeof a.model === "string" && a.model.trim() !== "" ? a.model.trim() : undefined;
|
|
1564
1614
|
let perCallModel;
|
|
1565
1615
|
let modelNote;
|
|
1566
|
-
|
|
1616
|
+
const judgedModels = activeModels();
|
|
1617
|
+
const rosterKeys = judgedModels !== undefined ? Object.keys(judgedModels) : [];
|
|
1618
|
+
if (modelObjectForm) {
|
|
1567
1619
|
if (wantsFork) {
|
|
1568
|
-
modelNote = `note: model "${
|
|
1620
|
+
modelNote = `note: model "${inlineUntrusted(a.model.id, 80)}" was ignored — a fork always runs on the caller's model.`;
|
|
1569
1621
|
}
|
|
1570
|
-
else
|
|
1571
|
-
|
|
1622
|
+
else {
|
|
1623
|
+
perCallModel = a.model;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
if (typeof a.model === "string" && requestedModel === undefined) {
|
|
1627
|
+
modelNote = wantsFork
|
|
1628
|
+
? `note: model "" was ignored — a fork always runs on the caller's model.`
|
|
1629
|
+
: `note: the durable record's model was blank and was NOT applied — the revived agent ran on its default (inherited) model.`;
|
|
1630
|
+
}
|
|
1631
|
+
else if (requestedModel !== undefined) {
|
|
1632
|
+
if (wantsFork) {
|
|
1633
|
+
modelNote = `note: model "${inlineUntrusted(requestedModel, 80)}" was ignored — a fork always runs on the caller's model.`;
|
|
1634
|
+
}
|
|
1635
|
+
else if (rosterKeys.length === 0) {
|
|
1636
|
+
modelNote = `note: model "${inlineUntrusted(requestedModel, 80)}" was NOT applied — this delegation tool has no model roster configured; the sub-agent ran on its default (inherited) model.`;
|
|
1572
1637
|
}
|
|
1573
1638
|
else {
|
|
1574
1639
|
try {
|
|
1575
|
-
resolveModel(requestedModel,
|
|
1576
|
-
perCallModel = requestedModel;
|
|
1640
|
+
perCallModel = resolveModel(requestedModel, judgedModels);
|
|
1577
1641
|
}
|
|
1578
1642
|
catch {
|
|
1579
|
-
|
|
1643
|
+
if (reviveClaim !== undefined) {
|
|
1644
|
+
modelNote = `note: recorded model "${inlineUntrusted(requestedModel, 80)}" is not on the current model roster and was NOT applied — the revived agent ran on its default (inherited) model.`;
|
|
1645
|
+
}
|
|
1646
|
+
else {
|
|
1647
|
+
const shown = [];
|
|
1648
|
+
let budget = ROSTER_ECHO_MAX;
|
|
1649
|
+
for (const k of rosterKeys) {
|
|
1650
|
+
if (budget - k.length < 0)
|
|
1651
|
+
break;
|
|
1652
|
+
shown.push(k);
|
|
1653
|
+
budget -= k.length + 2;
|
|
1654
|
+
}
|
|
1655
|
+
const rosterEcho = shown.length === rosterKeys.length ? shown.join(", ") : `${shown.join(", ")}, … (${rosterKeys.length - shown.length} more)`;
|
|
1656
|
+
return {
|
|
1657
|
+
isError: true,
|
|
1658
|
+
content: `Sub-agent not started: unknown model "${inlineUntrusted(requestedModel, 80)}". Available model values: ${inlineUntrusted(rosterEcho, ROSTER_ECHO_MAX + 32)}. Omit \`model\` to run the agent type's own model, or inherit the caller's.`,
|
|
1659
|
+
details: { error: "model.unknown" },
|
|
1660
|
+
};
|
|
1661
|
+
}
|
|
1580
1662
|
}
|
|
1581
1663
|
}
|
|
1582
1664
|
}
|
|
@@ -1690,9 +1772,39 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1690
1772
|
const childTools = def
|
|
1691
1773
|
? resolveToolSubset(childPool, def.allowTools, def.denyTools)
|
|
1692
1774
|
: resolveToolSubset(childPool, opts.allowTools, opts.denyTools);
|
|
1693
|
-
|
|
1775
|
+
let childModel = wantsFork
|
|
1694
1776
|
? ctx.model ?? opts.model
|
|
1695
1777
|
: perCallModel ?? (def ? (def.model ?? ctx.model ?? opts.model) : opts.model);
|
|
1778
|
+
if (reviveClaim !== undefined && typeof childModel === "string") {
|
|
1779
|
+
const resolvable = (ref) => {
|
|
1780
|
+
try {
|
|
1781
|
+
resolveModel(ref, runnerModelCatalog(opts.runner));
|
|
1782
|
+
return true;
|
|
1783
|
+
}
|
|
1784
|
+
catch {
|
|
1785
|
+
return false;
|
|
1786
|
+
}
|
|
1787
|
+
};
|
|
1788
|
+
if (!resolvable(childModel)) {
|
|
1789
|
+
const dead = childModel;
|
|
1790
|
+
const rest = def !== undefined ? [ctx.model, opts.model] : [opts.model];
|
|
1791
|
+
childModel = rest.find((c) => c !== undefined && (typeof c !== "string" || resolvable(c)));
|
|
1792
|
+
if (modelNote === undefined) {
|
|
1793
|
+
modelNote = `note: model "${inlineUntrusted(dead, 80)}" from this agent type's configuration is not on the current model roster and was NOT applied — the revived agent ran on its default (inherited) model.`;
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
const spawnModel = typeof childModel === "string"
|
|
1798
|
+
? (() => {
|
|
1799
|
+
try {
|
|
1800
|
+
return resolveModel(childModel, judgedModels).id;
|
|
1801
|
+
}
|
|
1802
|
+
catch {
|
|
1803
|
+
return resolveModelDisplayLabel(childModel);
|
|
1804
|
+
}
|
|
1805
|
+
})()
|
|
1806
|
+
: childModel?.id;
|
|
1807
|
+
const rowModelKey = perCallModel !== undefined ? requestedModel ?? perCallModel.id : typeof childModel === "string" ? childModel : childModel?.id;
|
|
1696
1808
|
const childSystemPrompt = def?.systemPrompt ?? opts.systemPrompt;
|
|
1697
1809
|
const childDefaultPersona = childSystemPrompt === undefined && !wantsFork ? SUBAGENT_PROMPT : undefined;
|
|
1698
1810
|
const defMaxTurns = typeof def?.maxTurns === "number" && Number.isFinite(def.maxTurns) && def.maxTurns > 0 ? def.maxTurns : undefined;
|
|
@@ -1936,6 +2048,9 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1936
2048
|
const provenanceRequest = ctx.delegationProvenanceForChildren?.();
|
|
1937
2049
|
const childProvenanceRef = provenanceRequest !== undefined ? { current: newDelegationProvenanceAggregate() } : undefined;
|
|
1938
2050
|
const childAttestation = (status) => childProvenanceRef !== undefined ? reduceDelegationAttestation(childProvenanceRef.current, { completed: status === "completed" }) : undefined;
|
|
2051
|
+
const liveCaptureFloor = reviveClaim === undefined
|
|
2052
|
+
? { optedOut: (await ctx.memoryCaptureOptedOut) === true, indeterminate: (await ctx.memoryCaptureIndeterminate) === true }
|
|
2053
|
+
: undefined;
|
|
1939
2054
|
const childInternals = {
|
|
1940
2055
|
...(inheritedManifestScope ? { inheritedManifestScope } : {}),
|
|
1941
2056
|
...(childProvenanceRef !== undefined && provenanceRequest !== undefined ? { delegationProvenance: { ref: childProvenanceRef, contentSafety: provenanceRequest } } : {}),
|
|
@@ -1954,8 +2069,8 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1954
2069
|
...(ctx.interactionPosture !== undefined ? { parentInteractionPosture: ctx.interactionPosture } : {}),
|
|
1955
2070
|
...(reviveClaim === undefined
|
|
1956
2071
|
? {
|
|
1957
|
-
...(
|
|
1958
|
-
...(
|
|
2072
|
+
...(liveCaptureFloor?.optedOut === true ? { memoryCaptureFloor: true } : {}),
|
|
2073
|
+
...(liveCaptureFloor?.optedOut !== true && liveCaptureFloor?.indeterminate === true ? { memoryCaptureFloorIndeterminate: true } : {}),
|
|
1959
2074
|
...(ctx.memoryCaptureControlDir !== undefined ? { memoryCaptureQueryDir: ctx.memoryCaptureControlDir } : {}),
|
|
1960
2075
|
...(ctx.memoryCaptureAncestors !== undefined ? { memoryCaptureAncestors: ctx.memoryCaptureAncestors } : {}),
|
|
1961
2076
|
}
|
|
@@ -2342,7 +2457,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2342
2457
|
...(ctx.taskId !== undefined && ctx.taskId !== ctx.sessionId ? { parentTaskId: ctx.taskId } : {}),
|
|
2343
2458
|
...(ctx.sessionId !== undefined ? { parentSessionId: ctx.sessionId } : {}),
|
|
2344
2459
|
...((ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}),
|
|
2345
|
-
...(
|
|
2460
|
+
...(rowModelKey !== undefined ? { model: rowModelKey } : {}),
|
|
2346
2461
|
deliveryChannel: "attaching",
|
|
2347
2462
|
toolUseId: ctx.toolCallId,
|
|
2348
2463
|
...(bg.agentStore !== undefined ? { store: bg.agentStore } : {}),
|
|
@@ -2403,7 +2518,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2403
2518
|
}, { priority: "next" }), "subagent.reapTerminalNotify");
|
|
2404
2519
|
});
|
|
2405
2520
|
if (agentName !== undefined) {
|
|
2406
|
-
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, sessionId: forkedId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...(
|
|
2521
|
+
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, sessionId: forkedId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...(rowModelKey !== undefined ? { model: rowModelKey } : {}), ...(modelFallback !== undefined ? { modelFallback } : {}), ...((ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: Date.now() }, (err) => opts.onObserverError?.(err, { site: "roster.recordSpawn" }));
|
|
2407
2522
|
}
|
|
2408
2523
|
const bgSink = ctx.onBackgroundChildEvent;
|
|
2409
2524
|
const sinkEmit = (event) => {
|
|
@@ -2422,6 +2537,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2422
2537
|
...(bgScope !== undefined ? { scope: bgScope } : {}),
|
|
2423
2538
|
description: shortDesc,
|
|
2424
2539
|
agentType: spawnAgentType,
|
|
2540
|
+
...(spawnModel !== undefined ? { model: spawnModel } : {}),
|
|
2425
2541
|
...(bgForkCycleSeq !== undefined ? { seq: bgForkCycleSeq } : {}),
|
|
2426
2542
|
...(agentName !== undefined ? { name: agentName } : {}),
|
|
2427
2543
|
sessionId: forkedId,
|
|
@@ -2451,6 +2567,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2451
2567
|
parentToolCallId: ctx.toolCallId,
|
|
2452
2568
|
progressTaskId: forkedId,
|
|
2453
2569
|
agentType: spawnAgentType,
|
|
2570
|
+
...(spawnModel !== undefined ? { model: spawnModel } : {}),
|
|
2454
2571
|
...(bgForkCycleSeq !== undefined ? { seq: bgForkCycleSeq } : {}),
|
|
2455
2572
|
...(currentAction !== undefined ? { currentAction } : {}),
|
|
2456
2573
|
...(currentTool !== undefined ? { currentTool } : {}),
|
|
@@ -2485,6 +2602,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2485
2602
|
progressTaskId: e.taskId,
|
|
2486
2603
|
...(e.parentTaskId !== undefined ? { progressParentTaskId: e.parentTaskId } : {}),
|
|
2487
2604
|
agentType: spawnAgentType,
|
|
2605
|
+
...(spawnModel !== undefined ? { model: spawnModel } : {}),
|
|
2488
2606
|
...(e.name !== undefined ? { name: e.name } : {}),
|
|
2489
2607
|
...(currentAction !== undefined ? { currentAction } : {}),
|
|
2490
2608
|
...(currentTool !== undefined ? { currentTool } : {}),
|
|
@@ -2562,9 +2680,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2562
2680
|
const resumableFork = false;
|
|
2563
2681
|
const forkTranscriptId = child.sessionId ?? forkedId;
|
|
2564
2682
|
const failReasonFork = settledBg === "failed" ? child.errorMessage : undefined;
|
|
2683
|
+
const forkElapsedMs = Date.now() - forkBgStartedAt;
|
|
2565
2684
|
const forkTerminalSummary = failReasonFork !== undefined
|
|
2566
|
-
? `Agent "${shortDesc}" failed: ${failReasonFork}${ccElapsedTag(
|
|
2567
|
-
: `${ccCompletionText(shortDesc, settledBg, String(child.status),
|
|
2685
|
+
? `Agent "${shortDesc}" failed: ${failReasonFork}${ccElapsedTag(forkElapsedMs)}`.slice(0, 300) + errorKindClause(errClassFork)
|
|
2686
|
+
: `${ccCompletionText(shortDesc, settledBg, String(child.status), forkElapsedMs)}`;
|
|
2568
2687
|
const ownsTerminalFacesFork = bg.registry.claimAgentTerminalNotify(taskId);
|
|
2569
2688
|
if (ownsTerminalFacesFork)
|
|
2570
2689
|
sinkEmit({
|
|
@@ -2580,7 +2699,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2580
2699
|
summary: forkTerminalSummary,
|
|
2581
2700
|
...residualFork,
|
|
2582
2701
|
resumable: resumableFork,
|
|
2583
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
2702
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats), ...delegatedGranularityFields(child.stats, forkElapsedMs) },
|
|
2584
2703
|
...(completionIdFork !== undefined ? { completionId: completionIdFork } : {}),
|
|
2585
2704
|
});
|
|
2586
2705
|
try {
|
|
@@ -2594,12 +2713,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2594
2713
|
summary: forkTerminalSummary,
|
|
2595
2714
|
...(failReasonFork !== undefined ? { error: failReasonFork.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
2596
2715
|
...(settledBg === "failed" && errCodeFork !== undefined ? { errorCode: errCodeFork } : {}),
|
|
2716
|
+
...providerFaultField(child),
|
|
2597
2717
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
2598
2718
|
...(forkBgResult ? { result: notifyResultField(forkBgResult) } : {}),
|
|
2599
2719
|
...(settledBg === "killed" && child.result ? { partial: true } : {}),
|
|
2600
2720
|
...residualFork,
|
|
2601
2721
|
resumable: resumableFork,
|
|
2602
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
2722
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats), ...delegatedGranularityFieldsWire(child.stats, forkElapsedMs) },
|
|
2603
2723
|
...(completionIdFork !== undefined ? { completionId: completionIdFork } : {}),
|
|
2604
2724
|
}, { priority: "next" });
|
|
2605
2725
|
}
|
|
@@ -2912,7 +3032,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2912
3032
|
...(ctx.taskId !== undefined && ctx.taskId !== ctx.sessionId ? { parentTaskId: ctx.taskId } : {}),
|
|
2913
3033
|
...(ctx.sessionId !== undefined ? { parentSessionId: ctx.sessionId } : {}),
|
|
2914
3034
|
...((ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}),
|
|
2915
|
-
...(
|
|
3035
|
+
...(rowModelKey !== undefined ? { model: rowModelKey } : {}),
|
|
2916
3036
|
deliveryChannel: "attaching",
|
|
2917
3037
|
toolUseId: ctx.toolCallId,
|
|
2918
3038
|
...(bg.agentStore !== undefined ? { store: bg.agentStore } : {}),
|
|
@@ -2936,7 +3056,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2936
3056
|
chargeDelegationEntryHandle(bg.registry, capLedgerKey, bg.agentStore, reviveRow === undefined ? taskId : undefined);
|
|
2937
3057
|
childInternals.peerSelfRef?.addAxis("h", taskId);
|
|
2938
3058
|
if (agentName !== undefined) {
|
|
2939
|
-
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...(
|
|
3059
|
+
recordRosterSpawn(ctx.roster, { name: agentName, agentId: taskId, toolUseId: ctx.toolCallId, owner: bgOwner, scope: bgScope, ...(reviveRow !== undefined ? (reviveRow.model !== undefined ? { model: reviveRow.model } : {}) : rowModelKey !== undefined ? { model: rowModelKey } : {}), ...(modelFallback !== undefined ? { modelFallback } : {}), ...(reviveRow !== undefined ? ((reviveRow.rootSessionId ?? reviveRow.parentSessionId) !== undefined ? { rootSessionId: reviveRow.rootSessionId ?? reviveRow.parentSessionId } : {}) : (ctx.rootSessionId ?? ctx.sessionId) !== undefined ? { rootSessionId: ctx.rootSessionId ?? ctx.sessionId } : {}), ...(sessionScopedBg ? { sessionScoped: true } : {}), createdAt: reviveRow?.spawnedAt ?? Date.now() }, (err) => opts.onObserverError?.(err, { site: "roster.recordSpawn" }));
|
|
2940
3060
|
}
|
|
2941
3061
|
const bgSink = ctx.onBackgroundChildEvent;
|
|
2942
3062
|
const sinkEmit = (event) => {
|
|
@@ -2992,6 +3112,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2992
3112
|
...(bgScope !== undefined ? { scope: bgScope } : {}),
|
|
2993
3113
|
description: reviveRow !== undefined ? `${shortDesc} (revived)` : shortDesc,
|
|
2994
3114
|
agentType: spawnAgentType,
|
|
3115
|
+
...(spawnModel !== undefined ? { model: spawnModel } : {}),
|
|
2995
3116
|
...(bgCycleSeq !== undefined ? { seq: bgCycleSeq } : {}),
|
|
2996
3117
|
...(agentName !== undefined ? { name: agentName } : {}),
|
|
2997
3118
|
sessionId: bgChildSessionId,
|
|
@@ -3065,6 +3186,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3065
3186
|
parentToolCallId: ctx.toolCallId,
|
|
3066
3187
|
progressTaskId: bgChildSessionId,
|
|
3067
3188
|
agentType: spawnAgentType,
|
|
3189
|
+
...(spawnModel !== undefined ? { model: spawnModel } : {}),
|
|
3068
3190
|
...(bgCycleSeq !== undefined ? { seq: bgCycleSeq } : {}),
|
|
3069
3191
|
...(currentAction !== undefined ? { currentAction } : {}),
|
|
3070
3192
|
...(currentTool !== undefined ? { currentTool } : {}),
|
|
@@ -3111,6 +3233,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3111
3233
|
progressTaskId: e.taskId,
|
|
3112
3234
|
...(e.parentTaskId !== undefined ? { progressParentTaskId: e.parentTaskId } : {}),
|
|
3113
3235
|
agentType: spawnAgentType,
|
|
3236
|
+
...(spawnModel !== undefined ? { model: spawnModel } : {}),
|
|
3114
3237
|
...(e.name !== undefined ? { name: e.name } : {}),
|
|
3115
3238
|
...(currentAction !== undefined ? { currentAction } : {}),
|
|
3116
3239
|
...(currentTool !== undefined ? { currentTool } : {}),
|
|
@@ -3399,9 +3522,10 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3399
3522
|
named: agentName !== undefined,
|
|
3400
3523
|
});
|
|
3401
3524
|
const failReasonBg = settled === "failed" ? unparkedPauseReason ?? child.errorMessage : undefined;
|
|
3525
|
+
const bgElapsedMs = Date.now() - bgStartedAt;
|
|
3402
3526
|
const bgTerminalSummary = failReasonBg !== undefined
|
|
3403
|
-
? `Agent "${shortDesc}" failed: ${failReasonBg}${ccElapsedTag(
|
|
3404
|
-
: `${ccCompletionText(shortDesc, settled, String(child.status),
|
|
3527
|
+
? `Agent "${shortDesc}" failed: ${failReasonBg}${ccElapsedTag(bgElapsedMs)}${observerNote}`.slice(0, 300) + errorKindClause(errClassBg)
|
|
3528
|
+
: `${ccCompletionText(shortDesc, settled, String(child.status), bgElapsedMs)}${observerNote}`;
|
|
3405
3529
|
const ownsTerminalFaces = bg.registry.claimAgentTerminalNotify(taskId);
|
|
3406
3530
|
if (ownsTerminalFaces)
|
|
3407
3531
|
sinkEmit({
|
|
@@ -3416,7 +3540,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3416
3540
|
summary: bgTerminalSummary,
|
|
3417
3541
|
...residual,
|
|
3418
3542
|
resumable: resumableBg,
|
|
3419
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
3543
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats), ...delegatedGranularityFields(child.stats, bgElapsedMs) },
|
|
3420
3544
|
...(completionIdBg !== undefined ? { completionId: completionIdBg } : {}),
|
|
3421
3545
|
});
|
|
3422
3546
|
const completionFrame = {
|
|
@@ -3429,12 +3553,13 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
3429
3553
|
summary: bgTerminalSummary,
|
|
3430
3554
|
...(failReasonBg !== undefined ? { error: failReasonBg.slice(0, REPORT_FIELD_MAX) } : {}),
|
|
3431
3555
|
...(settled === "failed" && errCodeBg !== undefined ? { errorCode: errCodeBg } : {}),
|
|
3556
|
+
...providerFaultField(child),
|
|
3432
3557
|
...(child.sessionId ? { sessionId: child.sessionId } : {}),
|
|
3433
3558
|
...(bgHandbackResult ? { result: notifyResultField(bgHandbackResult) } : {}),
|
|
3434
3559
|
...(settled === "killed" && child.result ? { partial: true } : {}),
|
|
3435
3560
|
...residual,
|
|
3436
3561
|
resumable: resumableBg,
|
|
3437
|
-
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats) },
|
|
3562
|
+
usage: { tokens: child.stats.tokens, turns: child.stats.turns, ...delegatedCostField(child.stats), ...delegatedGranularityFieldsWire(child.stats, bgElapsedMs) },
|
|
3438
3563
|
...(completionIdBg !== undefined ? { completionId: completionIdBg } : {}),
|
|
3439
3564
|
};
|
|
3440
3565
|
const gateOwner = ownsTerminalFaces && settled === "completed" && notify !== undefined ? bgRetain?.childSessionId : undefined;
|
|
@@ -166,6 +166,14 @@ export function validateFileHistoryExport(data) {
|
|
|
166
166
|
return bad(`tracked row ${t.relOrAbsPath} carries a non-integer version number`);
|
|
167
167
|
if (v.blobHash !== null && (typeof v.blobHash !== "string" || !/^[0-9a-f]{64}$/.test(v.blobHash)))
|
|
168
168
|
return bad(`tracked row ${t.relOrAbsPath} carries a blob reference that is not a sha256 digest`);
|
|
169
|
+
if (v.mode !== undefined) {
|
|
170
|
+
if (typeof v.mode !== "number" || !Number.isInteger(v.mode) || v.mode < 0 || v.mode > 0o7777) {
|
|
171
|
+
return bad(`tracked row ${t.relOrAbsPath} carries a mode outside the permission-bit domain [0, 0o7777] — refused, never coerced`);
|
|
172
|
+
}
|
|
173
|
+
if ((v.mode & 0o6000) !== 0) {
|
|
174
|
+
return bad(`tracked row ${t.relOrAbsPath} carries a set-ID mode (0o${v.mode.toString(8)}) — a restore replaying setuid/setgid bits would mint a privilege-escalation primitive; refused`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
169
177
|
}
|
|
170
178
|
if (new Set(t.versions.map((v) => v.version)).size !== t.versions.length)
|
|
171
179
|
return bad(`tracked row ${t.relOrAbsPath} carries the same version number twice — a chain whose versions are ambiguous cannot be published`);
|
|
@@ -284,13 +292,15 @@ export async function applyFileRestoreViaEnv(env, absPath, target, opts) {
|
|
|
284
292
|
return { outcome: "identical" };
|
|
285
293
|
if (modeOnly === "applied")
|
|
286
294
|
return { outcome: "applied" };
|
|
295
|
+
if (modeOnly === "applied_unpreserved")
|
|
296
|
+
return { outcome: "applied", modeNotPreserved: true };
|
|
287
297
|
return { outcome: "identical", modeNotPreserved: true };
|
|
288
298
|
}
|
|
289
299
|
const w = await writeRestoredBytes(env, absPath, target.bytes, signal);
|
|
290
300
|
if (!w.ok)
|
|
291
301
|
return { outcome: w.refused ? "refused" : "failed", reason: `write: ${w.code}` };
|
|
292
302
|
const modeAfter = await applyRecordedMode(env, absPath, opts, undefined);
|
|
293
|
-
return { outcome: "applied", ...(modeAfter === "unpreserved" ? { modeNotPreserved: true } : {}) };
|
|
303
|
+
return { outcome: "applied", ...(modeAfter === "unpreserved" || modeAfter === "applied_unpreserved" ? { modeNotPreserved: true } : {}) };
|
|
294
304
|
}
|
|
295
305
|
async function restorePreflight(env, absPath, opts) {
|
|
296
306
|
const info = await env.fileInfo(absPath, opts.signal);
|
|
@@ -337,7 +347,10 @@ export async function previewFileDelta(env, absPath, targetHash, targetBytes, ta
|
|
|
337
347
|
return undefined;
|
|
338
348
|
const info = await env.fileInfo(absPath);
|
|
339
349
|
const currentMode = info.ok ? info.value.mode : undefined;
|
|
340
|
-
|
|
350
|
+
const replayMode = (targetMode & 0o6000) !== 0 ? targetMode & 0o1777 : targetMode;
|
|
351
|
+
return modeProvenDifferent(targetMode, currentMode) && modeProvenDifferent(replayMode, currentMode)
|
|
352
|
+
? { insertions: 0, deletions: 0 }
|
|
353
|
+
: undefined;
|
|
341
354
|
}
|
|
342
355
|
return countRestoreLineDiff(curBytes, targetBytes);
|
|
343
356
|
}
|
|
@@ -347,6 +360,15 @@ async function applyRecordedMode(env, absPath, opts, currentMode) {
|
|
|
347
360
|
return "unchanged";
|
|
348
361
|
if (currentMode === (want & 0o7777))
|
|
349
362
|
return "unchanged";
|
|
363
|
+
if ((want & 0o6000) !== 0) {
|
|
364
|
+
const stripped = want & 0o1777;
|
|
365
|
+
if (currentMode === stripped)
|
|
366
|
+
return "unpreserved";
|
|
367
|
+
if (env.setFileMode === undefined)
|
|
368
|
+
return "unpreserved";
|
|
369
|
+
const r = await env.setFileMode(absPath, stripped, opts.signal);
|
|
370
|
+
return r.ok ? "applied_unpreserved" : "unpreserved";
|
|
371
|
+
}
|
|
350
372
|
if (env.setFileMode === undefined)
|
|
351
373
|
return "unpreserved";
|
|
352
374
|
const r = await env.setFileMode(absPath, want & 0o7777, opts.signal);
|
|
@@ -796,6 +796,27 @@ export declare function memoryConsolidationRefusedNotice(input: {
|
|
|
796
796
|
reason?: string;
|
|
797
797
|
occurrenceId?: string;
|
|
798
798
|
}): EngineNotice;
|
|
799
|
+
/**
|
|
800
|
+
* design/383 §S-7 (#511 件1) — {@link MemoryEngine.sessionMemoryStatus}'s answer. Every key is
|
|
801
|
+
* optional; ABSENCE means the fact's source could not be read (a fault never coins a `false`/`0`
|
|
802
|
+
* stand-in) — see the method doc for each key's exact absence law.
|
|
803
|
+
*/
|
|
804
|
+
export interface SessionMemoryStatus {
|
|
805
|
+
/** TRUE = a standing capture opt-out record; FALSE = store readable, no record (capture on).
|
|
806
|
+
* ABSENT = the record store faulted — indeterminate (`optOutSource: "fault"` accompanies). */
|
|
807
|
+
captureOptedOut?: boolean;
|
|
808
|
+
/** Committed entries carrying this session's lineage contribution. Absent = ledger unreadable. */
|
|
809
|
+
committedCount?: number;
|
|
810
|
+
/** Of those, entries already folded into consolidation products (lineage × `distilled.inputs`).
|
|
811
|
+
* Absent = ledger / scope enumeration / product read unreadable. */
|
|
812
|
+
foldedCount?: number;
|
|
813
|
+
/** WHY `captureOptedOut` reads as it does: `"record"` = a standing one-way record;
|
|
814
|
+
* `"fault"` = the store faulted and the capture state is INDETERMINATE (no boolean is coined). */
|
|
815
|
+
optOutSource?: "record" | "fault";
|
|
816
|
+
/** Newest lineage `lastAt` for this session (ms epoch) — rides the same single ledger read as
|
|
817
|
+
* `committedCount`. Absent = ledger unreadable, or no committed contribution exists at all. */
|
|
818
|
+
lastCaptureAt?: number;
|
|
819
|
+
}
|
|
799
820
|
export declare class MemoryEngine {
|
|
800
821
|
private readonly backend;
|
|
801
822
|
private readonly memoryDir;
|
|
@@ -910,13 +931,17 @@ export declare class MemoryEngine {
|
|
|
910
931
|
* first cross-process resume, and "私密 only until the next resume" is a promise this engine
|
|
911
932
|
* refuses to imply — the deliberate divergence from the pollution marker's best-effort arm.
|
|
912
933
|
*/
|
|
913
|
-
markSessionCaptureOptOut(sessionId: string, reason: string): SessionCaptureOptOutMarkOutcome
|
|
934
|
+
markSessionCaptureOptOut(sessionId: string, reason: string): SessionCaptureOptOutMarkOutcome | Promise<SessionCaptureOptOutMarkOutcome>;
|
|
914
935
|
/** The session's capture opt-out record (in-process first, then the durable store) — undefined =
|
|
915
936
|
* capture is on. Side-effect-free OBSERVER face; a THROWING store read degrades to the
|
|
916
937
|
* in-process answer here. Every consumer whose answer decides whether bytes COMMIT must use
|
|
917
938
|
* {@link sessionCaptureOptOutOrFault} instead — this face cannot distinguish "no record" from
|
|
918
|
-
* "store outage", and on that distinction the fail direction flips.
|
|
919
|
-
|
|
939
|
+
* "store outage", and on that distinction the fail direction flips.
|
|
940
|
+
* DUAL FORM (#511 件2, the whole capture-face family — this one, `sessionCaptureOptOutOrFault`,
|
|
941
|
+
* `markSessionCaptureOptOut`, `listCaptureOptOutSessions`): over a sync store the answer is the
|
|
942
|
+
* same synchronous value as always; over a Promise-form {@link SessionCaptureRecordStore} the
|
|
943
|
+
* face answers a Promise of the identical shape. `await` is always correct on either arm. */
|
|
944
|
+
sessionCaptureOptOut(sessionId: string): SessionCaptureOptOutRecord | undefined | Promise<SessionCaptureOptOutRecord | undefined>;
|
|
920
945
|
/** {@link sessionCaptureOptOut} with the FAULT axis preserved (codex review, 亲核 adopted):
|
|
921
946
|
* `fault: true` ⇔ the durable store THREW — the record state is INDETERMINATE, which the
|
|
922
947
|
* commit-deciding consumers (harvest opening read, pre-commit re-read, the runner's read
|
|
@@ -926,11 +951,14 @@ export declare class MemoryEngine {
|
|
|
926
951
|
sessionCaptureOptOutOrFault(sessionId: string): {
|
|
927
952
|
record?: SessionCaptureOptOutRecord;
|
|
928
953
|
fault: boolean;
|
|
929
|
-
}
|
|
954
|
+
} | Promise<{
|
|
955
|
+
record?: SessionCaptureOptOutRecord;
|
|
956
|
+
fault: boolean;
|
|
957
|
+
}>;
|
|
930
958
|
/** Every capture-opted-out session id (durable roster ∪ in-process marks). THROWS on an
|
|
931
959
|
* enumeration failure — the one consumer (the consolidation eligibility arm, §2.4) is
|
|
932
960
|
* fail-closed by design: "roster unknown" must refuse the run, never read as "no one opted out". */
|
|
933
|
-
listCaptureOptOutSessions(): Set<string
|
|
961
|
+
listCaptureOptOutSessions(): Set<string> | Promise<Set<string>>;
|
|
934
962
|
/**
|
|
935
963
|
* design/383 §2.3 — the mid-session flip's BOUNDARY-ISOLATION SWEEP over the write plane: files
|
|
936
964
|
* under the writable root that this session window added or changed (vs the materialize baseline)
|
|
@@ -1445,6 +1473,35 @@ export declare class MemoryEngine {
|
|
|
1445
1473
|
* registry THROWS fail-closed — the same reason an unsupported one never reads as empty.
|
|
1446
1474
|
*/
|
|
1447
1475
|
listMemoryScopes(): Promise<MemoryScopeEnumeration>;
|
|
1476
|
+
/**
|
|
1477
|
+
* design/383 §S-7 (#511 件1) — the HOST's per-session memory-status read face: the data supply
|
|
1478
|
+
* for the "memory capture is off / N committed, M folded" disclosure family and the
|
|
1479
|
+
* resume-visibility answer (state face, not event face — notice dedup semantics are untouched).
|
|
1480
|
+
* A server projects it onto a wire endpoint as a PURE derivation of these keys.
|
|
1481
|
+
*
|
|
1482
|
+
* EVERY key is optional and its ABSENCE means "that fact's source could not be read" — a fault
|
|
1483
|
+
* never coins a `false`/`0` stand-in (the §3.1 fault-transit law):
|
|
1484
|
+
* - `captureOptedOut` — the capture opt-out state. Present `true` (a standing record — with
|
|
1485
|
+
* `optOutSource: "record"`) or present `false` (the store answered and no one-way record
|
|
1486
|
+
* exists — genuine "capture on"). ABSENT ⇔ the record store faulted: the capture state is
|
|
1487
|
+
* INDETERMINATE and `optOutSource: "fault"` says so (the 383 片2/3 `captureIndeterminate`
|
|
1488
|
+
* axis, projected — never a boolean).
|
|
1489
|
+
* - `committedCount` — how many committed entries carry this session's lineage contribution
|
|
1490
|
+
* (the lineage ledger's committed set). Absent ⇔ the ledger is unreadable.
|
|
1491
|
+
* - `foldedCount` — of those, how many appear among the `distilled.inputs` of committed
|
|
1492
|
+
* consolidation products (the lineage × distilled.inputs intersection — "already folded into
|
|
1493
|
+
* long-term memory"). Absent ⇔ the ledger, the scope enumeration (a backend without
|
|
1494
|
+
* `listScopes` cannot name the header universe), or the product read is unreadable.
|
|
1495
|
+
* - `lastCaptureAt` — the newest `lastAt` over this session's lineage contributions (ms epoch).
|
|
1496
|
+
* CHEAP by construction — it rides the SAME single ledger read as `committedCount`, so it is
|
|
1497
|
+
* carried rather than dropped; absent ⇔ the ledger is unreadable OR the session has no
|
|
1498
|
+
* committed contribution at all (there is no such moment to name — the one key whose absence
|
|
1499
|
+
* also covers "no fact exists", stated here so consumers need not guess).
|
|
1500
|
+
*
|
|
1501
|
+
* Side-effect-free committed reads throughout (capture face + lineage sidecar + audit-face
|
|
1502
|
+
* headers/getByIds); never throws — an unreadable source is an absent key, which IS the answer.
|
|
1503
|
+
*/
|
|
1504
|
+
sessionMemoryStatus(sessionId: string): Promise<SessionMemoryStatus>;
|
|
1448
1505
|
/** The committed, side-effect-free audit read: the adoption-restricted committed view when the
|
|
1449
1506
|
* backend offers one (zero-copy File — ledger+shadow, no disk adoption), else the non-adopting
|
|
1450
1507
|
* retrieval view (copy-out File), else the backend itself (Pg/TiDB shapes — naturally committed
|