@sema-agent/core 5.62.0 → 5.64.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 +96 -0
- package/dist/agents/cascade.d.ts +5 -1
- package/dist/agents/cascade.js +6 -1
- package/dist/agents/subagent.d.ts +12 -2
- package/dist/agents/subagent.js +4 -2
- package/dist/agents/verify.d.ts +5 -1
- package/dist/agents/verify.js +5 -2
- package/dist/core/auto-compaction.d.ts +6 -4
- package/dist/core/auto-compaction.js +3 -0
- package/dist/core/checkpoint-store.d.ts +5 -1
- package/dist/core/context-edit.d.ts +36 -29
- package/dist/core/context-edit.js +3 -3
- package/dist/core/fs-write-gate-policy.d.ts +21 -0
- package/dist/core/fs-write-gate-policy.js +14 -3
- package/dist/core/hooks.d.ts +8 -5
- package/dist/core/memory-engine/engine.d.ts +11 -0
- package/dist/core/memory-engine/engine.js +29 -3
- package/dist/core/memory-engine/index.d.ts +1 -1
- package/dist/core/memory-engine/origin-clearance.d.ts +28 -0
- package/dist/core/remote-env.d.ts +34 -2
- package/dist/core/runner/prepare-config-doors.d.ts +2 -2
- package/dist/core/runner/prepare-config-doors.js +11 -8
- package/dist/core/runner/prepare-task.d.ts +87 -5
- package/dist/core/runner/prepare-task.js +174 -92
- package/dist/core/runner/prepare-workspace-restore.js +13 -0
- package/dist/core/runner/runtask.js +203 -152
- package/dist/core/trace.d.ts +5 -4
- package/dist/core/types.d.ts +38 -20
- package/dist/core/usage-window-store.d.ts +44 -12
- package/dist/core/usage-window-store.js +11 -3
- package/dist/core/workflow-run-store-contract.js +17 -0
- package/dist/core/workflow-run-store.d.ts +22 -1
- package/dist/core/workflow-run-store.js +1 -0
- package/dist/engine/harness/agent-harness.d.ts +8 -3
- package/dist/engine/harness/agent-harness.js +29 -9
- package/dist/engine/harness/types.d.ts +127 -3
- package/dist/engine/loop/agent-loop.js +47 -4
- package/dist/engine/loop/types.d.ts +67 -6
- package/dist/index.d.ts +2 -2
- package/dist/internal/harness-types.d.ts +1 -1
- package/dist/orchestration/run-workflow-tool.d.ts +6 -0
- package/dist/orchestration/run-workflow-tool.js +1 -0
- package/dist/orchestration/workflow-types.d.ts +48 -1
- package/dist/orchestration/workflow-types.js +12 -4
- package/dist/orchestration/workflow.d.ts +18 -1
- package/dist/orchestration/workflow.js +45 -19
- package/dist/prompts/default.js +1 -1
- package/dist/tools/fs/bash-readonly-classifier.d.ts +44 -1
- package/dist/tools/fs/bash-readonly-classifier.js +132 -5
- package/dist/tools/fs/fs-bash.js +9 -2
- package/dist/tools/fs/fs-write.js +19 -8
- package/dist/tools/fs/index.d.ts +1 -0
- package/dist/tools/fs/index.js +1 -0
- package/package.json +1 -1
- package/test/export-surface.snapshot.json +7 -1
|
@@ -3030,15 +3030,119 @@ export class Runner {
|
|
|
3030
3030
|
return messages;
|
|
3031
3031
|
});
|
|
3032
3032
|
}
|
|
3033
|
+
const runForcedCompactionPass = async (lane, turnSignal) => {
|
|
3034
|
+
if (!(spec.compaction?.enabled ?? true))
|
|
3035
|
+
return false;
|
|
3036
|
+
if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES)
|
|
3037
|
+
return false;
|
|
3038
|
+
const passSignal = turnSignal !== undefined ? AbortSignal.any([prepared.abortController.signal, turnSignal]) : prepared.abortController.signal;
|
|
3039
|
+
try {
|
|
3040
|
+
const comp = await maybeCompact({
|
|
3041
|
+
session: prepared.session,
|
|
3042
|
+
epochDeclaredSections: prepared.epochDeclaredSections,
|
|
3043
|
+
...centerAdoptionOption(prepared),
|
|
3044
|
+
model: prepared.harness.getModel(),
|
|
3045
|
+
compactionModel: prepared.compModel,
|
|
3046
|
+
...forkContextOption(prepared, true),
|
|
3047
|
+
brain: compactionBrain,
|
|
3048
|
+
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
3049
|
+
thinking: prepared.thinking,
|
|
3050
|
+
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
3051
|
+
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
3052
|
+
signal: passSignal,
|
|
3053
|
+
minTokens: 0,
|
|
3054
|
+
force: true,
|
|
3055
|
+
overheadTokens: prepared.promptOverheadTokens,
|
|
3056
|
+
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
3057
|
+
onInputTruncated: emitInputTruncated(rs.telemetry.tracer, rs.telemetry.taskId),
|
|
3058
|
+
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
3059
|
+
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
3060
|
+
...this.seamCCompactionOptions(prepared),
|
|
3061
|
+
...gitRestateOption(prepared),
|
|
3062
|
+
...windowSafetyOptions(prepared.harness.getModel()),
|
|
3063
|
+
...this.compactionHookOptions(spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: passSignal }),
|
|
3064
|
+
});
|
|
3065
|
+
if (comp.compacted) {
|
|
3066
|
+
compactionBreaker.failures = 0;
|
|
3067
|
+
this.recordCompactionReuse(prepared, comp);
|
|
3068
|
+
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
3069
|
+
const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
|
|
3070
|
+
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
3071
|
+
}
|
|
3072
|
+
queue.push({
|
|
3073
|
+
type: "compacted",
|
|
3074
|
+
trigger: "forced",
|
|
3075
|
+
tokensBefore: comp.tokensBefore ?? 0,
|
|
3076
|
+
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
3077
|
+
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
3078
|
+
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
3079
|
+
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
3080
|
+
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
3081
|
+
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
3082
|
+
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
3083
|
+
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
3084
|
+
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
3085
|
+
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
3086
|
+
...ident(),
|
|
3087
|
+
});
|
|
3088
|
+
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
3089
|
+
const pd = comp.phaseDurations;
|
|
3090
|
+
const pdDur = comp.durationMs;
|
|
3091
|
+
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
|
|
3092
|
+
}
|
|
3093
|
+
prepared.cacheBreakDetector?.notifyCompaction();
|
|
3094
|
+
if (rs.attach.attachState !== undefined) {
|
|
3095
|
+
rs.attach.attachState.postCompactPending = true;
|
|
3096
|
+
rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
else {
|
|
3100
|
+
if (comp.noop) {
|
|
3101
|
+
compactionBreaker.failures = 0;
|
|
3102
|
+
}
|
|
3103
|
+
else {
|
|
3104
|
+
compactionBreaker.failures += 1;
|
|
3105
|
+
}
|
|
3106
|
+
const declineReason = lane === "rejection" ? "prompt-too-long recovery pass did not land" : "guard-chain forced compaction pass did not land";
|
|
3107
|
+
if (!comp.noop) {
|
|
3108
|
+
this.deps.onError?.(new Error(declineReason), { phase: "compaction", sessionId: prepared.sessionId });
|
|
3109
|
+
}
|
|
3110
|
+
queue.push({
|
|
3111
|
+
type: "compaction_outcome",
|
|
3112
|
+
outcome: comp.noop ? "noop" : "failed",
|
|
3113
|
+
trigger: "forced",
|
|
3114
|
+
reason: declineReason,
|
|
3115
|
+
...ident(),
|
|
3116
|
+
});
|
|
3117
|
+
}
|
|
3118
|
+
return comp.compacted === true;
|
|
3119
|
+
}
|
|
3120
|
+
catch (err) {
|
|
3121
|
+
if (turnSignal?.aborted === true && !prepared.abortController.signal.aborted) {
|
|
3122
|
+
const interruptReason = lane === "rejection"
|
|
3123
|
+
? "prompt-too-long recovery pass cut short by a turn interrupt"
|
|
3124
|
+
: "guard-chain forced compaction cut short by a turn interrupt";
|
|
3125
|
+
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: interruptReason, ...ident() });
|
|
3126
|
+
return false;
|
|
3127
|
+
}
|
|
3128
|
+
compactionBreaker.failures += 1;
|
|
3129
|
+
this.deps.onError?.(err instanceof Error ? err : new Error(String(err)), { phase: "compaction", sessionId: prepared.sessionId });
|
|
3130
|
+
const msg = String(err instanceof Error ? err.message : err);
|
|
3131
|
+
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
|
|
3132
|
+
return false;
|
|
3133
|
+
}
|
|
3134
|
+
};
|
|
3033
3135
|
prepared.harness.setLoopRecovery({
|
|
3034
3136
|
truncatedOutput: {},
|
|
3035
3137
|
malformedToolUse: {},
|
|
3036
3138
|
thinkingOnly: {},
|
|
3037
3139
|
degenerateOutput: { detect: (m) => isDegenerateCutMessage(m) },
|
|
3038
3140
|
promptTooLong: {
|
|
3039
|
-
recover: async (attempt) => {
|
|
3141
|
+
recover: async (attempt, turnSignal) => {
|
|
3040
3142
|
if (prepared.abortController.signal.aborted || prepared.suspendRef.token !== undefined)
|
|
3041
3143
|
return false;
|
|
3144
|
+
if (turnSignal?.aborted === true)
|
|
3145
|
+
return false;
|
|
3042
3146
|
if (prepared.microCompact.clearOnRejection && attempt === 1) {
|
|
3043
3147
|
const proj = prepared.microCompact.projectionRef.current;
|
|
3044
3148
|
const compactionAvailable = (spec.compaction?.enabled ?? true) && compactionBreaker.failures < MAX_CONSECUTIVE_COMPACTION_FAILURES;
|
|
@@ -3075,85 +3179,23 @@ export class Runner {
|
|
|
3075
3179
|
return true;
|
|
3076
3180
|
}
|
|
3077
3181
|
}
|
|
3078
|
-
|
|
3079
|
-
return false;
|
|
3080
|
-
if (compactionBreaker.failures >= MAX_CONSECUTIVE_COMPACTION_FAILURES)
|
|
3081
|
-
return false;
|
|
3082
|
-
try {
|
|
3083
|
-
const comp = await maybeCompact({
|
|
3084
|
-
session: prepared.session,
|
|
3085
|
-
epochDeclaredSections: prepared.epochDeclaredSections,
|
|
3086
|
-
...centerAdoptionOption(prepared),
|
|
3087
|
-
model: prepared.harness.getModel(),
|
|
3088
|
-
compactionModel: prepared.compModel,
|
|
3089
|
-
...forkContextOption(prepared, true),
|
|
3090
|
-
brain: compactionBrain,
|
|
3091
|
-
getApiKeyAndHeaders: spec.getApiKeyAndHeaders,
|
|
3092
|
-
thinking: prepared.thinking,
|
|
3093
|
-
settings: { ...DEFAULT_COMPACTION_SETTINGS, ...spec.compaction },
|
|
3094
|
-
customInstructions: spec.compaction?.instructions ?? DEFAULT_COMPACTION_INSTRUCTIONS,
|
|
3095
|
-
signal: prepared.abortController.signal,
|
|
3096
|
-
minTokens: 0,
|
|
3097
|
-
force: true,
|
|
3098
|
-
overheadTokens: prepared.promptOverheadTokens,
|
|
3099
|
-
...(prepared.activeTools.size > 0 ? { activeTools: [...prepared.activeTools] } : {}),
|
|
3100
|
-
onInputTruncated: emitInputTruncated(rs.telemetry.tracer, rs.telemetry.taskId),
|
|
3101
|
-
workingFileAttachments: buildWorkingFileAttachments(spec, prepared),
|
|
3102
|
-
...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
|
|
3103
|
-
...this.seamCCompactionOptions(prepared),
|
|
3104
|
-
...gitRestateOption(prepared),
|
|
3105
|
-
...windowSafetyOptions(prepared.harness.getModel()),
|
|
3106
|
-
...this.compactionHookOptions(spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal }),
|
|
3107
|
-
});
|
|
3108
|
-
if (comp.compacted) {
|
|
3109
|
-
compactionBreaker.failures = 0;
|
|
3110
|
-
this.recordCompactionReuse(prepared, comp);
|
|
3111
|
-
if ((comp.freedTokens ?? 0) >= COMPACTION_FREED_EPSILON) {
|
|
3112
|
-
const postSize = comp.postTriggerTokens ?? Math.max(0, (comp.triggerTokens ?? comp.tokensBefore ?? 0) - (comp.freedTokens ?? 0));
|
|
3113
|
-
rs.counters.compactionFloor = Math.ceil(postSize * COMPACTION_REGROWTH_FACTOR);
|
|
3114
|
-
}
|
|
3115
|
-
queue.push({
|
|
3116
|
-
type: "compacted",
|
|
3117
|
-
trigger: "forced",
|
|
3118
|
-
tokensBefore: comp.tokensBefore ?? 0,
|
|
3119
|
-
...(comp.postTriggerTokens !== undefined ? { tokensAfter: comp.postTriggerTokens } : {}),
|
|
3120
|
-
...(comp.triggerTokens !== undefined ? { triggerTokensBefore: comp.triggerTokens } : {}),
|
|
3121
|
-
...(comp.durationMs !== undefined ? { durationMs: comp.durationMs } : {}),
|
|
3122
|
-
...(comp.phaseDurations !== undefined ? { phaseDurations: comp.phaseDurations } : {}),
|
|
3123
|
-
...(comp.firstKeptEntryId !== undefined ? { preserved_segment: { firstKeptEntryId: comp.firstKeptEntryId } } : {}),
|
|
3124
|
-
...(comp.attachedFiles !== undefined ? { attachedFiles: comp.attachedFiles } : {}),
|
|
3125
|
-
...(comp.modelFallback ? { modelFallback: true } : {}),
|
|
3126
|
-
...(comp.fallbackReason !== undefined ? { fallbackReason: comp.fallbackReason } : {}),
|
|
3127
|
-
...(comp.clampedRatio !== undefined ? { clampedRatio: comp.clampedRatio } : {}),
|
|
3128
|
-
...(comp.clampReason !== undefined ? { clampReason: comp.clampReason } : {}),
|
|
3129
|
-
...ident(),
|
|
3130
|
-
});
|
|
3131
|
-
if (comp.phaseDurations !== undefined && comp.durationMs !== undefined) {
|
|
3132
|
-
const pd = comp.phaseDurations;
|
|
3133
|
-
const pdDur = comp.durationMs;
|
|
3134
|
-
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.phase_timings", version: 1, taskId: rs.telemetry.taskId, ...pd, durationMs: pdDur, ts: Date.now() }));
|
|
3135
|
-
}
|
|
3136
|
-
prepared.cacheBreakDetector?.notifyCompaction();
|
|
3137
|
-
if (rs.attach.attachState !== undefined) {
|
|
3138
|
-
rs.attach.attachState.postCompactPending = true;
|
|
3139
|
-
rebaseCadenceWindows(rs.attach.attachState, rs.counters.cadenceTurns);
|
|
3140
|
-
}
|
|
3141
|
-
}
|
|
3142
|
-
else {
|
|
3143
|
-
compactionBreaker.failures += 1;
|
|
3144
|
-
queue.push({ type: "compaction_outcome", outcome: comp.noop ? "noop" : "failed", trigger: "forced", reason: "prompt-too-long recovery pass did not land", ...ident() });
|
|
3145
|
-
}
|
|
3146
|
-
return comp.compacted === true;
|
|
3147
|
-
}
|
|
3148
|
-
catch (err) {
|
|
3149
|
-
compactionBreaker.failures += 1;
|
|
3150
|
-
const msg = String(err instanceof Error ? err.message : err);
|
|
3151
|
-
queue.push({ type: "compaction_outcome", outcome: "failed", trigger: "forced", reason: msg.length > 512 ? `${msg.slice(0, 512)}…` : msg, ...ident() });
|
|
3152
|
-
return false;
|
|
3153
|
-
}
|
|
3182
|
+
return runForcedCompactionPass("rejection", turnSignal);
|
|
3154
3183
|
},
|
|
3155
3184
|
},
|
|
3156
3185
|
});
|
|
3186
|
+
prepared.microCompact.inTurnCompactionRef.current = async (turnSignal, anchoredEstimate) => {
|
|
3187
|
+
if (prepared.abortController.signal.aborted || prepared.suspendRef.token !== undefined)
|
|
3188
|
+
return false;
|
|
3189
|
+
if (turnSignal?.aborted === true)
|
|
3190
|
+
return false;
|
|
3191
|
+
if (anchoredEstimate !== undefined && rs.counters.compactionFloor > 0 && anchoredEstimate < rs.counters.compactionFloor) {
|
|
3192
|
+
const floor = rs.counters.compactionFloor;
|
|
3193
|
+
emitTrace(rs.telemetry.tracer, () => ({ kind: "compaction.suppressed", version: 1, taskId: rs.telemetry.taskId, estTokens: anchoredEstimate, floor, ts: Date.now() }));
|
|
3194
|
+
queue.push({ type: "compaction_outcome", outcome: "suppressed", trigger: "forced", ...ident() });
|
|
3195
|
+
return false;
|
|
3196
|
+
}
|
|
3197
|
+
return runForcedCompactionPass("guard", turnSignal);
|
|
3198
|
+
};
|
|
3157
3199
|
const rapidRefill = createRapidRefillState();
|
|
3158
3200
|
const drainManualCompact = (outcome) => {
|
|
3159
3201
|
manualCompactRef.requested = false;
|
|
@@ -4271,7 +4313,8 @@ export class Runner {
|
|
|
4271
4313
|
if (outcome === null || typeof outcome !== "object") {
|
|
4272
4314
|
throw new CheckpointError("checkpoint.invalid_outcome", `resume outcome must be an object naming its gate (got ${outcome === null ? "null" : typeof outcome})`);
|
|
4273
4315
|
}
|
|
4274
|
-
const
|
|
4316
|
+
const config = typeof taskConfig === "object" && taskConfig !== null ? { ...taskConfig } : taskConfig;
|
|
4317
|
+
const store = resolveCheckpointStore(config, this.deps);
|
|
4275
4318
|
if (!store) {
|
|
4276
4319
|
throw new CheckpointError("checkpoint.not_found", "no CheckpointStore wired — cannot resume (set RunnerDeps.checkpointStore or taskConfig.checkpointStore)");
|
|
4277
4320
|
}
|
|
@@ -4298,7 +4341,7 @@ export class Runner {
|
|
|
4298
4341
|
let wakeMessage;
|
|
4299
4342
|
let wakeHookContext;
|
|
4300
4343
|
const outcomeGate = outcome.gate;
|
|
4301
|
-
const resumeSignal =
|
|
4344
|
+
const resumeSignal = config.signal;
|
|
4302
4345
|
let suppliedMessage;
|
|
4303
4346
|
if (outcomeGate === "wake") {
|
|
4304
4347
|
suppliedMessage = outcome.message;
|
|
@@ -4336,62 +4379,6 @@ export class Runner {
|
|
|
4336
4379
|
}
|
|
4337
4380
|
if (suppliedMessage !== undefined) {
|
|
4338
4381
|
wakeMessage = validatePendingSteer(suppliedMessage);
|
|
4339
|
-
{
|
|
4340
|
-
const resumeScreenHooks = taskConfig.hooks ?? this.deps.hooks;
|
|
4341
|
-
const screen = resumeScreenHooks?.userPromptSubmit;
|
|
4342
|
-
if (screen !== undefined && resumeSignal?.aborted !== true) {
|
|
4343
|
-
const screenedMessage = wakeMessage;
|
|
4344
|
-
const identity = mintHookInvocationIdentity({
|
|
4345
|
-
sessionId: cp.sessionId,
|
|
4346
|
-
taskId: taskConfig.taskId ?? cp.sessionId,
|
|
4347
|
-
legKind: "resume",
|
|
4348
|
-
isDelegatedChild: effectiveDelegationFacts(internals, cp.state.isDelegatedChild).isDelegatedChild,
|
|
4349
|
-
...(internals?.insideFork === true ? { insideFork: true } : {}),
|
|
4350
|
-
...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
|
|
4351
|
-
...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
|
|
4352
|
-
});
|
|
4353
|
-
const screenTimeoutMs = resolveHookTimeoutMs(resumeScreenHooks?.timeoutMs, (badErr) => {
|
|
4354
|
-
try {
|
|
4355
|
-
this.deps.onError?.(badErr instanceof Error ? badErr : new Error(String(badErr)), { phase: "hook", sessionId: cp.sessionId });
|
|
4356
|
-
}
|
|
4357
|
-
catch {
|
|
4358
|
-
}
|
|
4359
|
-
}, resumeScreenHooks);
|
|
4360
|
-
let decision;
|
|
4361
|
-
try {
|
|
4362
|
-
const seat = await runHookSeat("userPromptSubmit", { timeoutMs: screenTimeoutMs, ...(resumeSignal !== undefined ? { signal: resumeSignal } : {}), abortEnds: true }, (sig) => screen(screenedMessage.text, {
|
|
4363
|
-
identity,
|
|
4364
|
-
signal: sig,
|
|
4365
|
-
source: "resume_message",
|
|
4366
|
-
...(screenedMessage.inputId !== undefined ? { inputId: screenedMessage.inputId } : {}),
|
|
4367
|
-
...(screenedMessage.actor !== undefined ? { actor: snapshotActorAssertion(screenedMessage.actor) } : {}),
|
|
4368
|
-
}));
|
|
4369
|
-
if (seat.expired) {
|
|
4370
|
-
throw new CheckpointError("steering.blocked_by_hook", seat.cause === "timeout"
|
|
4371
|
-
? `the deployment's userPromptSubmit hook did not answer within its ${screenTimeoutMs}ms bound while screening this wake message; the resume was refused (fail-closed) and the checkpoint stays pending`
|
|
4372
|
-
: `the resume was cancelled while the deployment's userPromptSubmit hook was still screening this wake message; the checkpoint stays pending`);
|
|
4373
|
-
}
|
|
4374
|
-
decision = seat.value;
|
|
4375
|
-
}
|
|
4376
|
-
catch (hookErr) {
|
|
4377
|
-
if (hookErr instanceof CheckpointError)
|
|
4378
|
-
throw hookErr;
|
|
4379
|
-
const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
|
|
4380
|
-
try {
|
|
4381
|
-
this.deps.onError?.(err, { phase: "hook", sessionId: cp.sessionId });
|
|
4382
|
-
}
|
|
4383
|
-
catch {
|
|
4384
|
-
}
|
|
4385
|
-
throw new CheckpointError("steering.blocked_by_hook", `the deployment's userPromptSubmit hook crashed while screening this wake message (${inlineUntrusted(err.message)}); the resume was refused (fail-closed) and the checkpoint stays pending`);
|
|
4386
|
-
}
|
|
4387
|
-
if (decision?.block !== undefined && decision.block !== "") {
|
|
4388
|
-
throw new CheckpointError("steering.blocked_by_hook", `the deployment's userPromptSubmit hook blocked this wake message: ${inlineUntrusted(decision.block)}`);
|
|
4389
|
-
}
|
|
4390
|
-
if (decision?.additionalContext !== undefined && decision.additionalContext !== "") {
|
|
4391
|
-
wakeHookContext = decision.additionalContext;
|
|
4392
|
-
}
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
4382
|
}
|
|
4396
4383
|
else if (readPendingSteerQueue(cp.state).length === 0) {
|
|
4397
4384
|
throw new CheckpointError("wake.nothing_to_deliver", "cannot wake: no message was supplied and the checkpoint holds no parked pendingSteer — an empty " +
|
|
@@ -4583,7 +4570,7 @@ export class Runner {
|
|
|
4583
4570
|
: "resume carried a content-ask `answer` on a `deny` — a denial injects a refusal, never an answer; refusing rather than dropping it silently", { field: "answer" });
|
|
4584
4571
|
}
|
|
4585
4572
|
if (contentAskCallId !== undefined && plainPolicyOutcome.decision === "allow" && suppliedAnswer === undefined) {
|
|
4586
|
-
if (!isLiveQuestionFace(
|
|
4573
|
+
if (!isLiveQuestionFace(config.onQuestion ?? this.deps.onQuestion)) {
|
|
4587
4574
|
throw new CheckpointError("checkpoint.invalid_outcome", "resume approved a content-ask (the reserved question tool) without an `answer`, and this resume has no live answering face — " +
|
|
4588
4575
|
'executing the question against nothing would hand the model a fabricated "no human is available" default while consuming the approval; ' +
|
|
4589
4576
|
"re-resume with the operator's answer on the outcome (or deny it), the checkpoint stays pending", { field: "answer" });
|
|
@@ -4694,13 +4681,14 @@ export class Runner {
|
|
|
4694
4681
|
},
|
|
4695
4682
|
}
|
|
4696
4683
|
: internals;
|
|
4697
|
-
if (this.deps.lockedConfig?.keys?.includes("toolPolicy") === true &&
|
|
4684
|
+
if (this.deps.lockedConfig?.keys?.includes("toolPolicy") === true && config.basePolicyForResumeEdit !== undefined) {
|
|
4698
4685
|
throw new CheckpointError("checkpoint.invalid_outcome", "TaskSpec.basePolicyForResumeEdit is administratively locked by this deployment (locked key \"toolPolicy\") — a task-supplied " +
|
|
4699
4686
|
"resume-edit policy is refused pre-CAS (the checkpoint stays pending); remove the field or change the deployment's lock configuration");
|
|
4700
4687
|
}
|
|
4688
|
+
let recheckGovernanceWindow;
|
|
4701
4689
|
{
|
|
4702
4690
|
const rowPrincipal = cp.principal || undefined;
|
|
4703
|
-
const suppliedPrincipal =
|
|
4691
|
+
const suppliedPrincipal = config.principal || undefined;
|
|
4704
4692
|
if (rowPrincipal !== undefined && suppliedPrincipal !== undefined && suppliedPrincipal !== rowPrincipal) {
|
|
4705
4693
|
throw new CheckpointError("resume.principal_mismatch", `the resume config carries principal ${JSON.stringify(suppliedPrincipal)} but this checkpoint was suspended under principal ${JSON.stringify(rowPrincipal)} — ` +
|
|
4706
4694
|
"running the resumed leg under a different identity would move its usage-ledger bucket, scope derivation and attribution; " +
|
|
@@ -4709,16 +4697,79 @@ export class Runner {
|
|
|
4709
4697
|
const owesDelivery = outcomeGate !== "resource_limit" ||
|
|
4710
4698
|
readPendingSteerQueue(cp.state).length > 0 ||
|
|
4711
4699
|
(cp.state.runningBackgroundTasks?.length ?? 0) > 0;
|
|
4712
|
-
|
|
4700
|
+
const usageWindowStore = this.deps.usageWindowStore;
|
|
4701
|
+
if (owesDelivery && usageWindowStore !== undefined) {
|
|
4713
4702
|
const preCasWindows = resolveUsageWindows(this.deps.usageWindows);
|
|
4714
4703
|
if (preCasWindows !== undefined && preCasWindows.length > 0) {
|
|
4715
4704
|
const ledgerKey = (suppliedPrincipal ?? rowPrincipal) || GLOBAL_USAGE_KEY;
|
|
4716
|
-
const
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4705
|
+
const assertWindowOpen = async () => {
|
|
4706
|
+
const wait = usageRetryAfterMs(await usageWindowStore.read(ledgerKey, preCasWindows, Date.now()), preCasWindows);
|
|
4707
|
+
if (wait !== undefined) {
|
|
4708
|
+
throw new CheckpointError("resume.usage_window_exhausted", `a deployment usage window for ledger key ${JSON.stringify(ledgerKey)} is exhausted, and this checkpoint still owes a delivery a re-mint cannot carry — ` +
|
|
4709
|
+
`refused pre-CAS (nothing consumed, nothing unpinned): the same token and the same decision are redeemable once the window frees, in ${String(wait)}ms`, { retryAfterMs: wait });
|
|
4710
|
+
}
|
|
4711
|
+
};
|
|
4712
|
+
recheckGovernanceWindow = assertWindowOpen;
|
|
4713
|
+
await assertWindowOpen();
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
if (outcomeGate === "wake" && wakeMessage !== undefined) {
|
|
4718
|
+
const resumeScreenHooks = config.hooks ?? this.deps.hooks;
|
|
4719
|
+
const screen = resumeScreenHooks?.userPromptSubmit;
|
|
4720
|
+
if (screen !== undefined && resumeSignal?.aborted !== true) {
|
|
4721
|
+
const screenedMessage = wakeMessage;
|
|
4722
|
+
const identity = mintHookInvocationIdentity({
|
|
4723
|
+
sessionId: cp.sessionId,
|
|
4724
|
+
taskId: config.taskId ?? cp.sessionId,
|
|
4725
|
+
legKind: "resume",
|
|
4726
|
+
isDelegatedChild: effectiveDelegationFacts(internals, cp.state.isDelegatedChild).isDelegatedChild,
|
|
4727
|
+
...(internals?.insideFork === true ? { insideFork: true } : {}),
|
|
4728
|
+
...(internals?.agentName !== undefined ? { agentName: internals.agentName } : {}),
|
|
4729
|
+
...(internals?.parentToolCallId !== undefined ? { parentToolCallId: internals.parentToolCallId } : {}),
|
|
4730
|
+
});
|
|
4731
|
+
const screenTimeoutMs = resolveHookTimeoutMs(resumeScreenHooks?.timeoutMs, (badErr) => {
|
|
4732
|
+
try {
|
|
4733
|
+
this.deps.onError?.(badErr instanceof Error ? badErr : new Error(String(badErr)), { phase: "hook", sessionId: cp.sessionId });
|
|
4720
4734
|
}
|
|
4735
|
+
catch {
|
|
4736
|
+
}
|
|
4737
|
+
}, resumeScreenHooks);
|
|
4738
|
+
let decision;
|
|
4739
|
+
try {
|
|
4740
|
+
const seat = await runHookSeat("userPromptSubmit", { timeoutMs: screenTimeoutMs, ...(resumeSignal !== undefined ? { signal: resumeSignal } : {}), abortEnds: true }, (sig) => screen(screenedMessage.text, {
|
|
4741
|
+
identity,
|
|
4742
|
+
signal: sig,
|
|
4743
|
+
source: "resume_message",
|
|
4744
|
+
...(screenedMessage.inputId !== undefined ? { inputId: screenedMessage.inputId } : {}),
|
|
4745
|
+
...(screenedMessage.actor !== undefined ? { actor: snapshotActorAssertion(screenedMessage.actor) } : {}),
|
|
4746
|
+
}));
|
|
4747
|
+
if (seat.expired) {
|
|
4748
|
+
throw new CheckpointError("steering.blocked_by_hook", seat.cause === "timeout"
|
|
4749
|
+
? `the deployment's userPromptSubmit hook did not answer within its ${screenTimeoutMs}ms bound while screening this wake message; the resume was refused (fail-closed) and the checkpoint stays pending`
|
|
4750
|
+
: `the resume was cancelled while the deployment's userPromptSubmit hook was still screening this wake message; the checkpoint stays pending`);
|
|
4751
|
+
}
|
|
4752
|
+
decision = seat.value;
|
|
4753
|
+
}
|
|
4754
|
+
catch (hookErr) {
|
|
4755
|
+
if (hookErr instanceof CheckpointError)
|
|
4756
|
+
throw hookErr;
|
|
4757
|
+
const err = hookErr instanceof Error ? hookErr : new Error(String(hookErr));
|
|
4758
|
+
try {
|
|
4759
|
+
this.deps.onError?.(err, { phase: "hook", sessionId: cp.sessionId });
|
|
4760
|
+
}
|
|
4761
|
+
catch {
|
|
4762
|
+
}
|
|
4763
|
+
throw new CheckpointError("steering.blocked_by_hook", `the deployment's userPromptSubmit hook crashed while screening this wake message (${inlineUntrusted(err.message)}); the resume was refused (fail-closed) and the checkpoint stays pending`);
|
|
4764
|
+
}
|
|
4765
|
+
if (decision?.block !== undefined && decision.block !== "") {
|
|
4766
|
+
throw new CheckpointError("steering.blocked_by_hook", `the deployment's userPromptSubmit hook blocked this wake message: ${inlineUntrusted(decision.block)}`);
|
|
4767
|
+
}
|
|
4768
|
+
if (decision?.additionalContext !== undefined && decision.additionalContext !== "") {
|
|
4769
|
+
wakeHookContext = decision.additionalContext;
|
|
4721
4770
|
}
|
|
4771
|
+
if (recheckGovernanceWindow !== undefined)
|
|
4772
|
+
await recheckGovernanceWindow();
|
|
4722
4773
|
}
|
|
4723
4774
|
}
|
|
4724
4775
|
if (plainPolicyOutcome !== undefined &&
|
|
@@ -4739,7 +4790,7 @@ export class Runner {
|
|
|
4739
4790
|
budget: editBudget,
|
|
4740
4791
|
...(cp.state.handsCwd !== undefined ? { cwd: cp.state.handsCwd } : {}),
|
|
4741
4792
|
};
|
|
4742
|
-
const editSignal =
|
|
4793
|
+
const editSignal = config.signal;
|
|
4743
4794
|
const raceEditAbort = async (p) => {
|
|
4744
4795
|
if (editSignal === undefined)
|
|
4745
4796
|
return p;
|
|
@@ -4778,7 +4829,7 @@ export class Runner {
|
|
|
4778
4829
|
const toolPolicyLocked = this.deps.lockedConfig?.keys?.includes("toolPolicy") === true;
|
|
4779
4830
|
const editBasePolicy = toolPolicyLocked
|
|
4780
4831
|
? (this.deps.basePolicyForResumeEdit ?? this.deps.toolPolicy)
|
|
4781
|
-
: (
|
|
4832
|
+
: (config.basePolicyForResumeEdit ?? this.deps.basePolicyForResumeEdit ?? config.toolPolicy ?? this.deps.toolPolicy);
|
|
4782
4833
|
if (editBasePolicy !== undefined) {
|
|
4783
4834
|
let rechecked;
|
|
4784
4835
|
try {
|
|
@@ -4798,7 +4849,7 @@ export class Runner {
|
|
|
4798
4849
|
`refused pre-CAS (the checkpoint stays pending); re-submit the edited action so the rewrite is adjudicated and approved synchronously`);
|
|
4799
4850
|
}
|
|
4800
4851
|
}
|
|
4801
|
-
const resumeHooks =
|
|
4852
|
+
const resumeHooks = config.hooks ?? this.deps.hooks;
|
|
4802
4853
|
if (resumeHooks?.preToolUse !== undefined && resumeHooks.preToolUseObservational === true) {
|
|
4803
4854
|
try {
|
|
4804
4855
|
await raceEditAbort(Promise.resolve(resumeHooks.preToolUse(cp.pendingAction.toolName, cloneObserverInput(editedArgs), {
|
|
@@ -4833,15 +4884,15 @@ export class Runner {
|
|
|
4833
4884
|
consumeFlipDone = true;
|
|
4834
4885
|
}
|
|
4835
4886
|
const answerFace = plainPolicyOutcome !== undefined && redeemedAnswer !== undefined
|
|
4836
|
-
? answerFaceForRedeemedCall(redeemedAnswer, plainPolicyOutcome.boundCallId,
|
|
4887
|
+
? answerFaceForRedeemedCall(redeemedAnswer, plainPolicyOutcome.boundCallId, config.onQuestion ?? this.deps.onQuestion, boundInputHashOf(cp.pendingAction.args?.questions))
|
|
4837
4888
|
: undefined;
|
|
4838
4889
|
const spec = {
|
|
4839
|
-
...
|
|
4840
|
-
...((
|
|
4890
|
+
...config,
|
|
4891
|
+
...((config.principal || undefined) === undefined && (cp.principal || undefined) !== undefined ? { principal: cp.principal } : {}),
|
|
4841
4892
|
objective: "",
|
|
4842
4893
|
sessionId: cp.sessionId,
|
|
4843
4894
|
requireExistingSession: true,
|
|
4844
|
-
preemptSignal:
|
|
4895
|
+
preemptSignal: config.preemptSignal?.aborted ? undefined : config.preemptSignal,
|
|
4845
4896
|
...(answerFace !== undefined ? { onQuestion: answerFace } : {}),
|
|
4846
4897
|
};
|
|
4847
4898
|
const reopenFn = store.reopen?.bind(store);
|
|
@@ -4853,10 +4904,10 @@ export class Runner {
|
|
|
4853
4904
|
}
|
|
4854
4905
|
}
|
|
4855
4906
|
: undefined;
|
|
4856
|
-
if (
|
|
4907
|
+
if (resumeSignal?.aborted === true && consumeFlipDone) {
|
|
4857
4908
|
throw new CheckpointError("checkpoint.resume_aborted", "the resume was aborted after its row was already claimed — the checkpoint stays consumed and the caller's rollback settles the outcome-unknown terminal (reopening here would strand a pending approval no redemption path can reach)");
|
|
4858
4909
|
}
|
|
4859
|
-
if (
|
|
4910
|
+
if (resumeSignal?.aborted === true) {
|
|
4860
4911
|
let reopenOutcome = "refused";
|
|
4861
4912
|
if (onEnvRestoreFailed !== undefined) {
|
|
4862
4913
|
try {
|
package/dist/core/trace.d.ts
CHANGED
|
@@ -734,10 +734,11 @@ export type TraceEvent = {
|
|
|
734
734
|
* occurrences were replaced with markers in one pass. `trigger` names the arm: `"frontier"`
|
|
735
735
|
* = the proactive request-build pass (anchored estimate crossed the edit budget),
|
|
736
736
|
* `"refusal"` = the MC-R rejection-recovery arm (provider said input-too-long), `"blocking"`
|
|
737
|
-
* = the slice-3
|
|
738
|
-
*
|
|
739
|
-
* `microCompact.machine: "cc"
|
|
740
|
-
* the
|
|
737
|
+
* = the slice-3 guard-chain arm A (the machine's one pre-guard shot when the frontier pass
|
|
738
|
+
* is off, `microCompact.machine: "off"`). SCOPE (X1): emitted only by the cc machine — the
|
|
739
|
+
* `"frontier"` arm requires `microCompact.machine: "cc"` (the default since the slice-3
|
|
740
|
+
* flip), the `"refusal"` arm requires `microCompact.clearOnRejection` (also default-on);
|
|
741
|
+
* the legacy OPT-OUT machine clears silently exactly as pre-374 (its clears are ledger-less
|
|
741
742
|
* and re-fire per request, so a frame there would re-count the same occurrences and break
|
|
742
743
|
* this frame's cardinality clause). Cardinality: exactly ONE frame per firing pass — a retry
|
|
743
744
|
* chain re-sending an already-cleared view emits none. A `"refusal"` frame also marks one
|
package/dist/core/types.d.ts
CHANGED
|
@@ -552,6 +552,13 @@ export interface ToolExecuteContext {
|
|
|
552
552
|
* fixed point `ctx.rootSessionId ?? ctx.sessionId` at every spawn). Absent for a top-level run
|
|
553
553
|
* (its OWN sessionId is the root). */
|
|
554
554
|
rootSessionId?: string;
|
|
555
|
+
/** design/380 O1② (C12) — the spawning run's EXPLICIT placement fixed point
|
|
556
|
+
* (`RunInternals.placementRoot`), present only when its internals carried one (cascade rungs /
|
|
557
|
+
* verification legs after the first, or a caller-declared root). A delegation tool threads it
|
|
558
|
+
* VERBATIM into child RunInternals so every descendant keeps the ladder/gate placement; absent ⇒
|
|
559
|
+
* the `rootSessionId` chain above IS the placement root through prepare's mint middle segment
|
|
560
|
+
* (`placementRoot ?? rootSessionId ?? sessionId`) and nothing extra is threaded. */
|
|
561
|
+
placementRoot?: string;
|
|
555
562
|
/**
|
|
556
563
|
* design/151 §7 S3c — TRUSTED tier-3 revival claim (same trusted-internals seat as
|
|
557
564
|
* {@link parentTaskId}; never a model argument). Filled ONLY by the auto-mounted SendMessage's
|
|
@@ -2658,9 +2665,12 @@ export interface TaskSpec {
|
|
|
2658
2665
|
* Scope caveat (design/141 examples 批实测): `instructions` rides the WHOLE-TURN summarization
|
|
2659
2666
|
* request only; a split-turn cut point (mid-turn prefix summarization) uses the engine-owned
|
|
2660
2667
|
* turn-prefix prompt and does not carry it (same A2 boundary as `RunnerDeps.summaryProvider`).
|
|
2661
|
-
* `withinTask: false` keeps end-of-task compaction but disables the within-task
|
|
2662
|
-
* trigger — the escape hatch for the §25 (A) control-flow change; the system
|
|
2663
|
-
* claiming mid-task summarization (§6.3 honesty).
|
|
2668
|
+
* `withinTask: false` keeps end-of-task compaction but disables the ROUTINE within-task
|
|
2669
|
+
* (turn-boundary) trigger — the escape hatch for the §25 (A) control-flow change; the system
|
|
2670
|
+
* prompt then stops claiming ROUTINE mid-task summarization (§6.3 honesty). It does NOT silence
|
|
2671
|
+
* the recovery-class forced lanes (design/374 slice 3): a prompt-too-long recovery or the guard
|
|
2672
|
+
* chain's arm B may still legitimately compact WITHIN the task — error/pressure recovery is not
|
|
2673
|
+
* a routine boundary pass, so a within-task compaction under this flag is contract-conforming.
|
|
2664
2674
|
* `attachWorkingFiles` (LONGRUN-2; **default ON since 2026-07-03** — CC 198 hard-codes its
|
|
2665
2675
|
* post-compact file restore, and LONGRUN-2 measured ≈2.3 extra read round-trips per compaction
|
|
2666
2676
|
* without it): after each compaction, re-read the task's most recently READ files (CC
|
|
@@ -5929,9 +5939,12 @@ export interface RunnerDeps {
|
|
|
5929
5939
|
* `TaskSpec.limits`, which is the allowance ONE task asked for — an operator granting "N tokens per 5
|
|
5930
5940
|
* hours" cannot express it as a task limit, because nothing stops the next task from asking again.
|
|
5931
5941
|
*
|
|
5932
|
-
* A window carries a TOKEN ceiling
|
|
5933
|
-
* USD — the `TaskLimits.maxCostUsd` quantity one governance level up)
|
|
5934
|
-
*
|
|
5942
|
+
* A window carries a TOKEN ceiling (`UsageWindow.maxTokens`), a MONEY ceiling (`UsageWindow.maxCostUsd`,
|
|
5943
|
+
* absolute USD — the `TaskLimits.maxCostUsd` quantity one governance level up), or both: each is
|
|
5944
|
+
* optional on its own and at least one is required, so a deployment governed by spend alone declares
|
|
5945
|
+
* only `maxCostUsd` (a window with neither ceiling is refused, `config.usage_window_invalid`). The two
|
|
5946
|
+
* are independent and every declared one binds. A $ ceiling requires a PRICED run: a task whose model
|
|
5947
|
+
* has neither a {@link pricing}
|
|
5935
5948
|
* entry nor a `Model.cost` declaration is refused at the door (`config.usage_window_unpriced`) rather
|
|
5936
5949
|
* than charged the fabricated 0 an unpriced run would otherwise file into an operator's ceiling.
|
|
5937
5950
|
*
|
|
@@ -6345,22 +6358,26 @@ export interface RunnerDeps {
|
|
|
6345
6358
|
*/
|
|
6346
6359
|
toolResultThresholdChars?: number;
|
|
6347
6360
|
/**
|
|
6348
|
-
* design/374 — microCompact machine-alignment knobs
|
|
6349
|
-
*
|
|
6350
|
-
*
|
|
6361
|
+
* design/374 — microCompact machine-alignment knobs. Since the slice-3 default flip BOTH knobs
|
|
6362
|
+
* default ON (`machine: "cc"`, `clearOnRejection: true`); the pre-374 behavior is the explicit
|
|
6363
|
+
* opt-out `{ machine: "legacy", clearOnRejection: false }`.
|
|
6351
6364
|
*
|
|
6352
6365
|
* - `machine`: which stale-tool-result clearing machine the request pipeline runs —
|
|
6353
|
-
* `"
|
|
6354
|
-
*
|
|
6355
|
-
*
|
|
6356
|
-
*
|
|
6357
|
-
*
|
|
6358
|
-
*
|
|
6366
|
+
* `"cc"` (default: the CC 2.1.223 rejection-leg form — keep 5, ≥20k minimum-savings gate, one
|
|
6367
|
+
* deep clear beyond the keep window, CC marker bytes), `"legacy"` (the historical keep-3 /
|
|
6368
|
+
* clear-to-budget machine, kept as the compatibility opt-out; it also keeps the pre-374
|
|
6369
|
+
* backstop order — guard trim as the ordinary second line), or `"off"` (no proactive frontier
|
|
6370
|
+
* clearing at all — the D-2 off switch; the unified machine then gets its ONE shot at the
|
|
6371
|
+
* blocking point instead, the slice-3 arm A, trace trigger `"blocking"`). The pre-flip P-form
|
|
6372
|
+
* warning on {@link import("./context-edit.js").ContextEditMachine} is resolved: the slice-3
|
|
6373
|
+
* fallback re-ordering shipped with this default (blocking-point re-run → in-turn forced
|
|
6374
|
+
* compaction behind the adopt seam → trim demoted to the disaster-only last resort), so an
|
|
6375
|
+
* under-20k refusal no longer falls straight into a message-dropping trim.
|
|
6359
6376
|
* - `clearOnRejection` (MC-R, slice 2): on a provider input-too-long rejection, run ONE cheap
|
|
6360
6377
|
* deterministic clear over the rejected projection (same cc machine, savings ≥20k or nothing)
|
|
6361
|
-
* and retry inside the turn BEFORE the forced-compaction recovery. Default
|
|
6362
|
-
*
|
|
6363
|
-
*
|
|
6378
|
+
* and retry inside the turn BEFORE the forced-compaction recovery. Default true (flipped with
|
|
6379
|
+
* the machine default in slice 3; X2's dark-landing clause is spent). Independent of
|
|
6380
|
+
* `machine` — an enabled MC-R always clears in the cc form (the rejection arm has no
|
|
6364
6381
|
* budget coordinate for the legacy incremental form to stop at). BUDGET ACCOUNTING (design/374
|
|
6365
6382
|
* §3.2.1, stated here because it is otherwise invisible to a deployment): a successful MC-R
|
|
6366
6383
|
* clear-and-retry SPENDS one attempt of the shared prompt-too-long recovery budget (default 2
|
|
@@ -6372,10 +6389,11 @@ export interface RunnerDeps {
|
|
|
6372
6389
|
* A declaration outside the closed vocabulary (a `machine` string not in the union, a
|
|
6373
6390
|
* non-boolean `clearOnRejection` — JSON/env-derived config the type cannot guard) refuses the
|
|
6374
6391
|
* whole prepare loudly (`code: "config.microcompact_invalid"`, no silent re-default): folding it
|
|
6375
|
-
* would run the
|
|
6392
|
+
* would silently run the DEFAULT machine while the deployment believes its declaration took
|
|
6393
|
+
* effect.
|
|
6376
6394
|
*/
|
|
6377
6395
|
microCompact?: {
|
|
6378
|
-
machine?: "legacy" | "cc";
|
|
6396
|
+
machine?: "off" | "legacy" | "cc";
|
|
6379
6397
|
clearOnRejection?: boolean;
|
|
6380
6398
|
};
|
|
6381
6399
|
/**
|