@sema-agent/core 7.18.0 → 7.20.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 +276 -2
- package/dist/agents/subagent.js +12 -20
- package/dist/agents/teacher.js +2 -5
- package/dist/agents/verify.js +21 -10
- package/dist/brain/anthropic.js +8 -3
- package/dist/brain/open-responses.js +12 -4
- package/dist/brain/openai.js +7 -2
- package/dist/brain/reasoning.d.ts +13 -13
- package/dist/brain/reasoning.js +2 -2
- package/dist/brain/stream-engine.d.ts +4 -0
- package/dist/brain/stream-engine.js +6 -1
- package/dist/brain/stream-shared.d.ts +3 -0
- package/dist/brain/stream-shared.js +1 -0
- package/dist/core/ask-origin.d.ts +16 -0
- package/dist/core/ask-origin.js +8 -0
- package/dist/core/auto-mode-arming.d.ts +6 -0
- package/dist/core/auto-mode-arming.js +5 -5
- package/dist/core/auto-mode-defaults.d.ts +43 -15
- package/dist/core/auto-mode-defaults.js +4 -1
- package/dist/core/auto-mode.d.ts +74 -26
- package/dist/core/auto-mode.js +80 -11
- package/dist/core/checkpoint-store.d.ts +7 -0
- package/dist/core/checkpoint-store.js +2 -2
- package/dist/core/child-clamp-seats.d.ts +79 -0
- package/dist/core/child-clamp-seats.js +29 -0
- package/dist/core/engine-notice.d.ts +89 -41
- package/dist/core/fs-write-gate-policy.d.ts +17 -9
- package/dist/core/fs-write-gate-policy.js +51 -4
- package/dist/core/gate-bounded-await.d.ts +38 -0
- package/dist/core/gate-bounded-await.js +53 -0
- package/dist/core/gate-fold.js +20 -48
- package/dist/core/gate-lanes.js +17 -9
- package/dist/core/gate-outcome.d.ts +34 -6
- package/dist/core/gate-outcome.js +7 -0
- package/dist/core/governance-codes.d.ts +16 -1
- package/dist/core/governance-codes.js +18 -0
- package/dist/core/hooks.d.ts +8 -8
- package/dist/core/memory-engine/engine.d.ts +14 -9
- package/dist/core/memory-engine/engine.js +21 -8
- package/dist/core/memory-engine/layout.d.ts +31 -0
- package/dist/core/memory-engine/layout.js +68 -3
- package/dist/core/memory-engine/types.d.ts +24 -0
- package/dist/core/memory.d.ts +19 -0
- package/dist/core/memory.js +12 -0
- package/dist/core/read-root-candidate.d.ts +12 -0
- package/dist/core/read-root-candidate.js +24 -0
- package/dist/core/remote-env.d.ts +32 -0
- package/dist/core/remote-env.js +4 -1
- package/dist/core/roles.d.ts +9 -1
- package/dist/core/roles.js +4 -0
- package/dist/core/runner/advertised-writable-dirs.d.ts +59 -1
- package/dist/core/runner/advertised-writable-dirs.js +51 -3
- package/dist/core/runner/announce-once-ledger.d.ts +15 -0
- package/dist/core/runner/announce-once-ledger.js +8 -0
- package/dist/core/runner/classifier-ladder.d.ts +154 -11
- package/dist/core/runner/classifier-ladder.js +170 -10
- package/dist/core/runner/contracts.d.ts +46 -1
- package/dist/core/runner/denial-limit-arms.d.ts +14 -1
- package/dist/core/runner/denial-limit-arms.js +7 -6
- package/dist/core/runner/env-capability-census.d.ts +30 -0
- package/dist/core/runner/env-capability-census.js +38 -0
- package/dist/core/runner/forward-sink.d.ts +32 -0
- package/dist/core/runner/forward-sink.js +41 -0
- package/dist/core/runner/gate-exit.d.ts +4 -0
- package/dist/core/runner/gate-exit.js +4 -1
- package/dist/core/runner/git-status-frame.d.ts +0 -9
- package/dist/core/runner/listing-fold.d.ts +57 -0
- package/dist/core/runner/listing-fold.js +75 -0
- package/dist/core/runner/prepare-ask-lane.js +1 -0
- package/dist/core/runner/prepare-caps-and-workflow.js +20 -31
- package/dist/core/runner/prepare-config-doors.d.ts +6 -0
- package/dist/core/runner/prepare-config-doors.js +8 -10
- package/dist/core/runner/prepare-execution-env.d.ts +1 -1
- package/dist/core/runner/prepare-execution-env.js +2 -0
- package/dist/core/runner/prepare-gate-stations.d.ts +8 -0
- package/dist/core/runner/prepare-gate-stations.js +6 -2
- package/dist/core/runner/prepare-hands-readface.d.ts +4 -0
- package/dist/core/runner/prepare-hands-readface.js +3 -5
- package/dist/core/runner/prepare-listings.d.ts +3 -0
- package/dist/core/runner/prepare-listings.js +4 -2
- package/dist/core/runner/prepare-memory.js +7 -1
- package/dist/core/runner/prepare-policy-chain.js +5 -5
- package/dist/core/runner/prepare-project-context.d.ts +5 -1
- package/dist/core/runner/prepare-project-context.js +3 -2
- package/dist/core/runner/prepare-prompt-inputs.d.ts +2 -2
- package/dist/core/runner/prepare-run-refs.d.ts +4 -2
- package/dist/core/runner/prepare-run-refs.js +5 -4
- package/dist/core/runner/prepare-task.js +8 -6
- package/dist/core/runner/resume-apply.js +3 -3
- package/dist/core/runner/run-clock-and-content.js +3 -7
- package/dist/core/runner/run-harness-handlers.js +10 -10
- package/dist/core/runner/run-leg.js +31 -16
- package/dist/core/runner/run-settle-and-teardown.js +2 -5
- package/dist/core/runner/run-turn-boundary.js +44 -25
- package/dist/core/runner/runtask.js +2 -1
- package/dist/core/runner/synthetic-tools.d.ts +41 -29
- package/dist/core/runner/synthetic-tools.js +25 -40
- package/dist/core/runner/task-root-canonical.d.ts +45 -0
- package/dist/core/runner/task-root-canonical.js +18 -0
- package/dist/core/runner/turn-attachments.d.ts +160 -53
- package/dist/core/runner/turn-attachments.js +153 -51
- package/dist/core/safe-notify.d.ts +5 -0
- package/dist/core/safe-notify.js +24 -2
- package/dist/core/sensitive-path-policy.d.ts +84 -17
- package/dist/core/sensitive-path-policy.js +37 -19
- package/dist/core/skill-listing-budget.d.ts +106 -0
- package/dist/core/skill-listing-budget.js +57 -0
- package/dist/core/skills-directory.js +17 -10
- package/dist/core/task-registry.js +2 -1
- package/dist/core/task-spec.d.ts +29 -2
- package/dist/core/tool-policy.d.ts +31 -5
- package/dist/core/tool-policy.js +45 -32
- package/dist/core/tool-spec.d.ts +37 -4
- package/dist/core/types.d.ts +1 -1
- package/dist/core/write-protect.d.ts +105 -20
- package/dist/core/write-protect.js +89 -32
- package/dist/index.d.ts +8 -7
- package/dist/index.js +6 -5
- package/dist/orchestration/workflow-primitives.js +10 -20
- package/dist/stores/file/memory-store.d.ts +5 -2
- package/dist/stores/file/memory-store.js +8 -4
- package/dist/tools/fs/bash-readonly-classifier.d.ts +40 -2
- package/dist/tools/fs/bash-readonly-classifier.js +376 -41
- package/dist/tools/fs/fs-bash.d.ts +1 -1
- package/dist/tools/fs/fs-bash.js +19 -28
- package/dist/tools/fs/fs-shared.d.ts +22 -0
- package/dist/tools/fs/fs-shared.js +30 -0
- package/dist/tools/fs/index.d.ts +1 -1
- package/dist/tools/fs/index.js +1 -1
- package/dist/tools/fs/read-deny.d.ts +4 -3
- package/dist/tools/fs/safety.d.ts +15 -0
- package/dist/tools/fs/safety.js +7 -0
- package/package.json +2 -1
- package/test/export-surface.snapshot.json +31 -5
- package/wire-changes/core@7.18.0.json +1273 -0
- package/wire-changes/core@7.19.0.json +457 -0
- package/wire-changes/core@7.20.0.json +568 -0
package/dist/agents/subagent.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { isAbsolute } from "node:path";
|
|
3
3
|
import { applyParentSpecSeat, parentSpecSeatOfCtx } from "../core/parent-spec-seat.js";
|
|
4
|
+
import { childClampSeats } from "../core/child-clamp-seats.js";
|
|
4
5
|
import { stripSpawnTurnQuestionFace } from "../core/ask-question.js";
|
|
5
6
|
import { withDelegationProvenance } from "../core/tool-policy.js";
|
|
6
7
|
import { errorResult } from "../core/tools.js";
|
|
@@ -20,6 +21,7 @@ import { SUBAGENT_PROMPT } from "../prompts/default.js";
|
|
|
20
21
|
import { hasSessionFork } from "../core/session.js";
|
|
21
22
|
import { isDurablePause, pauseFamily } from "./suspend-guard.js";
|
|
22
23
|
import { terminalProjection } from "../core/runner/terminal-projection.js";
|
|
24
|
+
import { forwardSafely } from "../core/runner/forward-sink.js";
|
|
23
25
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
24
26
|
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
25
27
|
import { boundedRedactedSummary } from "../core/untrusted-egress.js";
|
|
@@ -556,13 +558,13 @@ export function createSubagentResume(deps) {
|
|
|
556
558
|
sessionId: entry.childSessionId,
|
|
557
559
|
requireExistingSession: true,
|
|
558
560
|
...(deps.currentOnQuestion !== undefined ? { onQuestion: deps.currentOnQuestion } : {}),
|
|
559
|
-
...(
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
:
|
|
565
|
-
|
|
561
|
+
...childClampSeats({
|
|
562
|
+
handsReadOnly: entry.specSnapshot.handsReadOnly === true || deps.currentClamps?.handsReadOnly === true,
|
|
563
|
+
interactiveTools: entry.specSnapshot.interactiveTools === false || deps.currentClamps?.interactiveTools === false ? false : undefined,
|
|
564
|
+
readFace: entry.specSnapshot.readFace === "roots" || deps.currentClamps?.readFaceRoots === true ? "roots" : entry.specSnapshot.readFace,
|
|
565
|
+
writeFace: entry.specSnapshot.writeFace === "roots" || deps.currentClamps?.writeFaceRoots === true ? "roots" : entry.specSnapshot.writeFace,
|
|
566
|
+
readDenyPatterns: [...(entry.specSnapshot.readDenyPatterns ?? []), ...(deps.currentClamps?.readDenyPatterns ?? [])],
|
|
567
|
+
}),
|
|
566
568
|
...(entry.specSnapshot.oneShot === true || deps.currentClamps?.oneShot === true ? { oneShot: true } : {}),
|
|
567
569
|
...(entry.specSnapshot.memoryPersistenceCapable === false || deps.currentClamps?.memoryPersistenceCapable === false
|
|
568
570
|
? { memoryPersistenceCapable: false }
|
|
@@ -643,11 +645,7 @@ export function createSubagentResume(deps) {
|
|
|
643
645
|
? {
|
|
644
646
|
onForwardEvent: (e) => {
|
|
645
647
|
resumeStepRecorder.record(e);
|
|
646
|
-
|
|
647
|
-
entry.internalsSnapshot.onForwardEvent?.(deps.taskId !== undefined ? { ...e, bgAgentId: deps.taskId } : e);
|
|
648
|
-
}
|
|
649
|
-
catch {
|
|
650
|
-
}
|
|
648
|
+
forwardSafely(entry.internalsSnapshot, deps.taskId !== undefined ? { ...e, bgAgentId: deps.taskId } : e);
|
|
651
649
|
reviveActivityBeat(e);
|
|
652
650
|
if (reviveEmit !== undefined && e.type === "task_progress") {
|
|
653
651
|
reviveEmit({
|
|
@@ -1904,9 +1902,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
1904
1902
|
...(ctx.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...ctx.alwaysLoadTools] } : {}),
|
|
1905
1903
|
...(ctx.restoreGatedTools !== undefined ? { restoreGatedTools: ctx.restoreGatedTools === true ? true : [...ctx.restoreGatedTools] } : {}),
|
|
1906
1904
|
...(ctx.promptProfile !== undefined ? { promptProfile: ctx.promptProfile } : {}),
|
|
1907
|
-
...(ctx.
|
|
1908
|
-
...(ctx.writeFace === "roots" ? { writeFace: "roots" } : {}),
|
|
1909
|
-
...(ctx.readDenyPatterns !== undefined ? { readDenyPatterns: [...ctx.readDenyPatterns] } : {}),
|
|
1905
|
+
...childClampSeats({ handsReadOnly: ctx.handsReadOnly, interactiveTools: ctx.interactiveTools, readFace: ctx.readFace, writeFace: ctx.writeFace, readDenyPatterns: ctx.readDenyPatterns }),
|
|
1910
1906
|
enableBlockedReport: false,
|
|
1911
1907
|
limits: {
|
|
1912
1908
|
maxTurns: typeof observerDef.maxTurns === "number" && Number.isFinite(observerDef.maxTurns) && observerDef.maxTurns > 0 ? observerDef.maxTurns : 8,
|
|
@@ -2160,11 +2156,7 @@ function createSubagentToolNode(opts, depth, excluded, extraToolsBudget) {
|
|
|
2160
2156
|
...(def?.memoryPersistenceCapable !== undefined ? { memoryPersistenceCapable: def.memoryPersistenceCapable } : {}),
|
|
2161
2157
|
...(ctx.checkpointStoreForChildren !== undefined ? { checkpointStore: ctx.checkpointStoreForChildren } : {}),
|
|
2162
2158
|
...(childOnAsk !== undefined ? { onAsk: childOnAsk } : {}),
|
|
2163
|
-
...(ctx.interactiveTools
|
|
2164
|
-
...(ctx.handsReadOnly === true ? { handsReadOnly: true } : {}),
|
|
2165
|
-
...(ctx.readFace === "roots" ? { readFace: "roots" } : {}),
|
|
2166
|
-
...(ctx.writeFace === "roots" ? { writeFace: "roots" } : {}),
|
|
2167
|
-
...(ctx.readDenyPatterns !== undefined ? { readDenyPatterns: [...ctx.readDenyPatterns] } : {}),
|
|
2159
|
+
...childClampSeats({ handsReadOnly: ctx.handsReadOnly, interactiveTools: ctx.interactiveTools, readFace: ctx.readFace, writeFace: ctx.writeFace, readDenyPatterns: ctx.readDenyPatterns }),
|
|
2168
2160
|
...(ctx.excludeTools !== undefined ? { excludeTools: [...ctx.excludeTools] } : {}),
|
|
2169
2161
|
...(ctx.deferTools !== undefined ? { deferTools: [...ctx.deferTools] } : {}),
|
|
2170
2162
|
...(ctx.alwaysLoadTools !== undefined ? { alwaysLoadTools: [...ctx.alwaysLoadTools] } : {}),
|
package/dist/agents/teacher.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { MAX_STRATEGY_INJECTION_TOTAL_BYTES, MAX_STRATEGY_PROBLEM_BYTES, MAX_STRATEGY_TEXT_BYTES } from "../core/strategy-store.js";
|
|
3
3
|
import { createSafeNotifier, observeThenableRejection } from "../core/safe-notify.js";
|
|
4
|
+
import { childClampSeats } from "../core/child-clamp-seats.js";
|
|
4
5
|
import { mapNestedSuspend, isDurablePause } from "./suspend-guard.js";
|
|
5
6
|
import { isDefineToolProduct, rebindDefineToolCtx, stampDefineToolBrand } from "../core/tools.js";
|
|
6
7
|
import { delimitUntrusted, inlineUntrusted } from "../core/untrusted-text.js";
|
|
@@ -255,11 +256,7 @@ async function runTeacherCore(runner, studentSpec, teacher) {
|
|
|
255
256
|
...(studentSpec.principal !== undefined ? { principal: studentSpec.principal } : {}),
|
|
256
257
|
...(studentSpec.clientContext !== undefined ? { clientContext: studentSpec.clientContext } : {}),
|
|
257
258
|
...(studentSpec.promptProfile !== undefined ? { promptProfile: studentSpec.promptProfile } : {}),
|
|
258
|
-
...(studentSpec.handsReadOnly
|
|
259
|
-
...(studentSpec.readFace === "roots" ? { readFace: "roots" } : {}),
|
|
260
|
-
...(studentSpec.writeFace === "roots" ? { writeFace: "roots" } : {}),
|
|
261
|
-
...(studentSpec.readDenyPatterns !== undefined ? { readDenyPatterns: [...studentSpec.readDenyPatterns] } : {}),
|
|
262
|
-
...(studentSpec.interactiveTools === false ? { interactiveTools: false } : {}),
|
|
259
|
+
...childClampSeats({ handsReadOnly: studentSpec.handsReadOnly, interactiveTools: studentSpec.interactiveTools, readFace: studentSpec.readFace, writeFace: studentSpec.writeFace, readDenyPatterns: studentSpec.readDenyPatterns }),
|
|
263
260
|
...(studentSpec.excludeTools !== undefined
|
|
264
261
|
? { excludeTools: Array.isArray(studentSpec.excludeTools) ? [...studentSpec.excludeTools] : studentSpec.excludeTools }
|
|
265
262
|
: {}),
|
package/dist/agents/verify.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Type } from "typebox";
|
|
2
2
|
import { CODE_AGENT_PROMPT } from "../prompts/default.js";
|
|
3
3
|
import { releaseSession } from "./session-util.js";
|
|
4
|
+
import { childClampSeats } from "../core/child-clamp-seats.js";
|
|
4
5
|
import { mapNestedSuspend, isDurablePause, pauseFamily } from "./suspend-guard.js";
|
|
5
6
|
import { delimitUntrusted, sanitizeUntrustedText } from "../core/untrusted-text.js";
|
|
6
7
|
import { createSafeNotifier } from "../core/safe-notify.js";
|
|
@@ -136,17 +137,21 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
136
137
|
const runVerifier = async (impl, round) => {
|
|
137
138
|
refuseUnhonorableInternals(internals, "before_leg");
|
|
138
139
|
const roundEvidence = round === 1 ? config.evidence : undefined;
|
|
139
|
-
const
|
|
140
|
+
const verifierSpec = {
|
|
140
141
|
objective: verifierObjective(objective, impl, roundEvidence),
|
|
141
142
|
systemPrompt: verifierPrompt,
|
|
142
143
|
...(config.verifierModel ? { model: config.verifierModel } : { modelRole: "verifier" }),
|
|
143
144
|
roles: specBase.roles,
|
|
144
145
|
tools: verifierTools,
|
|
145
146
|
handsReadOnly: config.verifierHandsReadOnly ?? true,
|
|
146
|
-
...(foldedRootsFace ? { readFace: "roots" } : {}),
|
|
147
|
-
...(foldedRootsWriteFace ? { writeFace: "roots" } : {}),
|
|
148
|
-
...(foldedReadDeny.length > 0 ? { readDenyPatterns: [...foldedReadDeny] } : {}),
|
|
149
147
|
interactiveTools: false,
|
|
148
|
+
...childClampSeats({
|
|
149
|
+
handsReadOnly: specBase.handsReadOnly,
|
|
150
|
+
interactiveTools: specBase.interactiveTools,
|
|
151
|
+
readFace: foldedRootsFace ? "roots" : undefined,
|
|
152
|
+
writeFace: foldedRootsWriteFace ? "roots" : undefined,
|
|
153
|
+
readDenyPatterns: foldedReadDeny,
|
|
154
|
+
}),
|
|
150
155
|
outputSchema: VerdictSchema,
|
|
151
156
|
enableBlockedReport: false,
|
|
152
157
|
limits: { ...(specBase.limits?.maxWalltimeMs !== undefined ? { maxWalltimeMs: specBase.limits.maxWalltimeMs } : {}) },
|
|
@@ -167,7 +172,8 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
167
172
|
...(specBase.clientContext !== undefined ? { clientContext: { ...specBase.clientContext } } : {}),
|
|
168
173
|
...(specBase.promptProfile !== undefined ? { promptProfile: specBase.promptProfile } : {}),
|
|
169
174
|
signal: specBase.signal,
|
|
170
|
-
}
|
|
175
|
+
};
|
|
176
|
+
const v = await runner.runTask(verifierSpec, legInternals());
|
|
171
177
|
try {
|
|
172
178
|
const cost = (v.stats.costMicroUsd ?? 0) + (v.stats.nested?.costMicroUsd ?? 0);
|
|
173
179
|
return { verdict: v.structuredOutput, cost, ...(isDurablePause(v.terminal) ? { paused: { ...v, terminal: v.terminal } } : {}) };
|
|
@@ -241,14 +247,19 @@ export async function verifyCompleted(runner, result, specBase, objective, confi
|
|
|
241
247
|
if (config.costCeilingMicroUsd != null && spend >= config.costCeilingMicroUsd)
|
|
242
248
|
break;
|
|
243
249
|
refuseUnhonorableInternals(internals, "before_leg");
|
|
244
|
-
|
|
250
|
+
const fixLegSpec = {
|
|
245
251
|
...specBase,
|
|
246
252
|
sessionId: current.sessionId,
|
|
247
253
|
objective: fixObjective(outcome.findings),
|
|
248
|
-
...(
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
254
|
+
...childClampSeats({
|
|
255
|
+
handsReadOnly: specBase.handsReadOnly,
|
|
256
|
+
interactiveTools: specBase.interactiveTools,
|
|
257
|
+
readFace: foldedRootsFace ? "roots" : specBase.readFace,
|
|
258
|
+
writeFace: foldedRootsWriteFace ? "roots" : specBase.writeFace,
|
|
259
|
+
readDenyPatterns: foldedReadDeny,
|
|
260
|
+
}),
|
|
261
|
+
};
|
|
262
|
+
current = await runner.runTask(fixLegSpec, legInternals());
|
|
252
263
|
spend += (current.stats.costMicroUsd ?? 0) + (current.stats.nested?.costMicroUsd ?? 0);
|
|
253
264
|
implAccount.add(current.stats);
|
|
254
265
|
if (isDurablePause(current.terminal)) {
|
package/dist/brain/anthropic.js
CHANGED
|
@@ -2,7 +2,7 @@ import {} from "../internal/llm.js";
|
|
|
2
2
|
import { BrainError } from "./errors.js";
|
|
3
3
|
import { startsWithInputTooLongAnchor } from "./input-too-long.js";
|
|
4
4
|
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
|
|
5
|
-
import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js";
|
|
5
|
+
import { STREAM_TORN_WARNING, createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js";
|
|
6
6
|
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
7
7
|
import { emitBrainTelemetry, reportReasoningWireFacts } from "./status-sink.js";
|
|
8
8
|
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, modelSupportsVision, sendableImages } from "./media-degrade.js";
|
|
@@ -365,6 +365,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
365
365
|
const blocks = new Map();
|
|
366
366
|
const usageRaw = {};
|
|
367
367
|
let stopReason = null;
|
|
368
|
+
const terminalFrameSeen = () => stopReason != null;
|
|
368
369
|
let stopDetails;
|
|
369
370
|
let nextContentIndex = 0;
|
|
370
371
|
let malformedFrames = 0;
|
|
@@ -532,6 +533,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
532
533
|
hasSubstantiveText,
|
|
533
534
|
hasCompletedToolCall,
|
|
534
535
|
hasOnlyThinking: hasThinking && !hasSubstantiveText && !hasCompletedToolCall,
|
|
536
|
+
terminalFrameSeen: terminalFrameSeen(),
|
|
535
537
|
};
|
|
536
538
|
},
|
|
537
539
|
sealForRetry() {
|
|
@@ -560,7 +562,7 @@ export function createAnthropicBrain(config = {}) {
|
|
|
560
562
|
}
|
|
561
563
|
handleEvent(evt);
|
|
562
564
|
},
|
|
563
|
-
finalize() {
|
|
565
|
+
finalize(opts) {
|
|
564
566
|
const finalContent = [];
|
|
565
567
|
const toolCalls = [];
|
|
566
568
|
const malformed = [];
|
|
@@ -666,6 +668,8 @@ export function createAnthropicBrain(config = {}) {
|
|
|
666
668
|
emptyNoFinish ||
|
|
667
669
|
(toolError !== undefined && noUsableContent) ||
|
|
668
670
|
(truncatedEmpty && !dynamicCut && !staticReasoningCut);
|
|
671
|
+
const tornWithContent = !errored && opts?.truncated !== true && !terminalFrameSeen() && !noUsableContent && !degenerate;
|
|
672
|
+
const errorMessageOut = tornWithContent ? (errorMessage ? `${errorMessage}\n${STREAM_TORN_WARNING}` : STREAM_TORN_WARNING) : errorMessage;
|
|
669
673
|
const errorKind = degenerate
|
|
670
674
|
? "degenerate"
|
|
671
675
|
: !safetyCut && contextWindowExceeded
|
|
@@ -680,8 +684,9 @@ export function createAnthropicBrain(config = {}) {
|
|
|
680
684
|
usage: computeUsage(model, usageRaw),
|
|
681
685
|
...(Object.keys(usageRaw).length === 0 ? { usageMissing: true } : {}),
|
|
682
686
|
timestamp: Date.now(),
|
|
683
|
-
...(
|
|
687
|
+
...(errorMessageOut ? { errorMessage: errorMessageOut } : {}),
|
|
684
688
|
...(errorKind ? { errorKind } : {}),
|
|
689
|
+
...(tornWithContent ? { partialFinalized: true } : {}),
|
|
685
690
|
...(rep.cut !== undefined || rep.spared.length > 0
|
|
686
691
|
? { repetition: { ...(rep.cut !== undefined ? { cut: rep.cut } : {}), ...(rep.spared.length > 0 ? { spared: rep.spared } : {}) } }
|
|
687
692
|
: {}),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {} from "../internal/llm.js";
|
|
2
2
|
import { BrainError } from "./errors.js";
|
|
3
3
|
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
|
|
4
|
-
import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js";
|
|
4
|
+
import { STREAM_TORN_WARNING, createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js";
|
|
5
5
|
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
6
6
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
7
|
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
|
|
@@ -195,6 +195,7 @@ function responsesErrorCode(code) {
|
|
|
195
195
|
}
|
|
196
196
|
const INCOMPLETE_MAX_OUTPUT_TOKENS = "max_output_tokens";
|
|
197
197
|
const INCOMPLETE_CONTENT_FILTER = "content_filter";
|
|
198
|
+
const RESPONSE_INCOMPLETE_WARNING = 'The response above may be incomplete: the endpoint reported status "incomplete" with no reason this client recognises.';
|
|
198
199
|
function closeFunctionCall(acc) {
|
|
199
200
|
if (acc.closedTc)
|
|
200
201
|
return acc.closedTc;
|
|
@@ -302,6 +303,7 @@ export function createOpenResponsesBrain(config = {}) {
|
|
|
302
303
|
let usageRaw;
|
|
303
304
|
let responseId;
|
|
304
305
|
let terminalStatus;
|
|
306
|
+
const terminalFrameSeen = () => terminalStatus !== undefined;
|
|
305
307
|
let incompleteReason;
|
|
306
308
|
let accumRefusal = "";
|
|
307
309
|
const capabilityNotes = [];
|
|
@@ -572,6 +574,7 @@ export function createOpenResponsesBrain(config = {}) {
|
|
|
572
574
|
hasSubstantiveText,
|
|
573
575
|
hasCompletedToolCall,
|
|
574
576
|
hasOnlyThinking: hasReasoning && !hasSubstantiveText && !hasCompletedToolCall,
|
|
577
|
+
terminalFrameSeen: terminalFrameSeen(),
|
|
575
578
|
};
|
|
576
579
|
},
|
|
577
580
|
sealForRetry() {
|
|
@@ -688,7 +691,8 @@ export function createOpenResponsesBrain(config = {}) {
|
|
|
688
691
|
const staticReasoningCut = truncatedEmpty && !dynamicCut && reasoningSeen && options?.staticReasoningCutDowngrade === true;
|
|
689
692
|
const safetyCut = incompleteReason === INCOMPLETE_CONTENT_FILTER;
|
|
690
693
|
const refused = accumRefusal.trim().length > 0;
|
|
691
|
-
const
|
|
694
|
+
const incompleteUnmapped = terminalStatus === "incomplete" && incompleteReason !== INCOMPLETE_MAX_OUTPUT_TOKENS && incompleteReason !== INCOMPLETE_CONTENT_FILTER;
|
|
695
|
+
const emptyNoFinish = noUsableContent && (!terminalFrameSeen() || incompleteUnmapped) && !degenerate;
|
|
692
696
|
const maxTokensNote = sentMaxTokens !== undefined
|
|
693
697
|
? `effective max_output_tokens=${sentMaxTokens} (from ${sentMaxTokensLane === "options"
|
|
694
698
|
? "options.maxTokens"
|
|
@@ -708,7 +712,7 @@ export function createOpenResponsesBrain(config = {}) {
|
|
|
708
712
|
? `[length_empty] response truncated at max_output_tokens (${maxTokensNote}) with no answer text — the model spent the whole budget on reasoning; raise the cap, or lower the reasoning effort for this task.`
|
|
709
713
|
: `[length_empty] response truncated at max_output_tokens (${maxTokensNote}) with no answer text — raise the cap`
|
|
710
714
|
: emptyNoFinish
|
|
711
|
-
? `[stream_torn] model stream ended with no content and no terminal response event — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
|
|
715
|
+
? `[stream_torn] model stream ended with no content and ${terminalStatus === undefined ? "no terminal response event" : `a terminal status "${terminalStatus}" with no reason this client recognises (incomplete_details.reason=${incompleteReason ?? "absent"})`} — the response was lost (a torn stream or an in-band provider error frame)${malformedFrames > 0 ? ` — ${malformedFrames} unparseable data frame(s) were dropped on this stream (first sample: ${JSON.stringify(malformedSample)})` : ""}`
|
|
712
716
|
: undefined));
|
|
713
717
|
const errored = degenerate ||
|
|
714
718
|
safetyCut ||
|
|
@@ -716,6 +720,9 @@ export function createOpenResponsesBrain(config = {}) {
|
|
|
716
720
|
emptyNoFinish ||
|
|
717
721
|
(toolError !== undefined && noUsableContent) ||
|
|
718
722
|
(truncatedEmpty && !dynamicCut && !staticReasoningCut);
|
|
723
|
+
const tornWithContent = !errored && opts?.truncated !== true && (!terminalFrameSeen() || incompleteUnmapped) && !noUsableContent && !degenerate;
|
|
724
|
+
const partialWarning = terminalStatus === undefined ? STREAM_TORN_WARNING : RESPONSE_INCOMPLETE_WARNING;
|
|
725
|
+
const errorMessageOut = tornWithContent ? (errorMessage ? `${errorMessage}\n${partialWarning}` : partialWarning) : errorMessage;
|
|
719
726
|
const errorKind = degenerate
|
|
720
727
|
? "degenerate"
|
|
721
728
|
: !safetyCut && !refused && toolError === undefined && truncatedEmpty && !dynamicCut && !staticReasoningCut
|
|
@@ -732,8 +739,9 @@ export function createOpenResponsesBrain(config = {}) {
|
|
|
732
739
|
...(usageRaw === undefined ? { usageMissing: true } : {}),
|
|
733
740
|
timestamp: Date.now(),
|
|
734
741
|
...(responseId !== undefined ? { responseId } : {}),
|
|
735
|
-
...(
|
|
742
|
+
...(errorMessageOut ? { errorMessage: errorMessageOut } : {}),
|
|
736
743
|
...(errorKind ? { errorKind } : {}),
|
|
744
|
+
...(tornWithContent ? { partialFinalized: true } : {}),
|
|
737
745
|
...(rep.cut !== undefined || rep.spared.length > 0
|
|
738
746
|
? { repetition: { ...(rep.cut !== undefined ? { cut: rep.cut } : {}), ...(rep.spared.length > 0 ? { spared: rep.spared } : {}) } }
|
|
739
747
|
: {}),
|
package/dist/brain/openai.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {} from "../internal/llm.js";
|
|
2
2
|
import { repairTextToolCalls } from "./tool-call-repair.js";
|
|
3
3
|
import { DEGENERATE_MESSAGE, trimDegenerateTail } from "./repetition.js";
|
|
4
|
-
import { createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js";
|
|
4
|
+
import { STREAM_TORN_WARNING, createRepetitionPoll, parseStreamedToolArgs } from "./stream-shared.js";
|
|
5
5
|
import { mintFallbackToolCallId } from "./tool-call-id.js";
|
|
6
6
|
import { emitBrainTelemetry } from "./status-sink.js";
|
|
7
7
|
import { errorResultMediaNote, IMAGE_OMITTED_NO_VISION, imagesOmittedNoVisionNote, modelSupportsVision, sendableImages } from "./media-degrade.js";
|
|
@@ -373,6 +373,7 @@ export function createOpenAIBrain(config = {}) {
|
|
|
373
373
|
let textIndex = 0;
|
|
374
374
|
const toolAccum = new Map();
|
|
375
375
|
let finishReason = null;
|
|
376
|
+
const terminalFrameSeen = () => finishReason != null;
|
|
376
377
|
let usageRaw;
|
|
377
378
|
let malformedFrames = 0;
|
|
378
379
|
let malformedSample;
|
|
@@ -438,6 +439,7 @@ export function createOpenAIBrain(config = {}) {
|
|
|
438
439
|
hasSubstantiveText,
|
|
439
440
|
hasCompletedToolCall,
|
|
440
441
|
hasOnlyThinking: accumReasoning.length > 0 && !hasSubstantiveText && !hasCompletedToolCall,
|
|
442
|
+
terminalFrameSeen: terminalFrameSeen(),
|
|
441
443
|
};
|
|
442
444
|
},
|
|
443
445
|
sealForRetry() {
|
|
@@ -619,6 +621,8 @@ export function createOpenAIBrain(config = {}) {
|
|
|
619
621
|
emptyNoFinish ||
|
|
620
622
|
(toolError !== undefined && noUsableContent) ||
|
|
621
623
|
(truncatedEmpty && !dynamicCut);
|
|
624
|
+
const tornWithContent = !errored && opts?.truncated !== true && !terminalFrameSeen() && !noUsableContent && !degenerate;
|
|
625
|
+
const errorMessageOut = tornWithContent ? (errorMessage ? `${errorMessage}\n${STREAM_TORN_WARNING}` : STREAM_TORN_WARNING) : errorMessage;
|
|
622
626
|
const errorKind = degenerate
|
|
623
627
|
? "degenerate"
|
|
624
628
|
: !safetyCut && !refused && toolError === undefined && truncatedEmpty && !dynamicCut
|
|
@@ -631,8 +635,9 @@ export function createOpenAIBrain(config = {}) {
|
|
|
631
635
|
usage: computeUsage(model, usageRaw),
|
|
632
636
|
...(usageRaw === undefined ? { usageMissing: true } : {}),
|
|
633
637
|
timestamp: Date.now(),
|
|
634
|
-
...(
|
|
638
|
+
...(errorMessageOut ? { errorMessage: errorMessageOut } : {}),
|
|
635
639
|
...(errorKind ? { errorKind } : {}),
|
|
640
|
+
...(tornWithContent ? { partialFinalized: true } : {}),
|
|
636
641
|
...(rep.cut !== undefined || rep.spared.length > 0
|
|
637
642
|
? { repetition: { ...(rep.cut !== undefined ? { cut: rep.cut } : {}), ...(rep.spared.length > 0 ? { spared: rep.spared } : {}) } }
|
|
638
643
|
: {}),
|
|
@@ -117,25 +117,25 @@ export interface OffCapabilityModel {
|
|
|
117
117
|
* "none" tier), on an endpoint that takes an effort value at all (`supportsReasoningEffort` not
|
|
118
118
|
* false) — else `undefined`: there is nothing to send. The effort formats have no disable key, and an
|
|
119
119
|
* effort VALUE is never off (it enables reasoning at that tier), so off on those wires is spelled only
|
|
120
|
-
* when the deployment says how. The two appliers and {@link
|
|
120
|
+
* when the deployment says how. The two appliers and {@link thinkingOffSpelled} read this ONE
|
|
121
121
|
* function, so what the wire carries and what the capability table claims cannot disagree.
|
|
122
122
|
*/
|
|
123
123
|
export declare function declaredOffSpelling(model: OffCapabilityModel): string | undefined;
|
|
124
124
|
/**
|
|
125
|
-
* Whether
|
|
126
|
-
*
|
|
127
|
-
* `{type:"disabled"}
|
|
128
|
-
* `{
|
|
129
|
-
*
|
|
130
|
-
* a deployment-declared off spelling ({@link declaredOffSpelling}).
|
|
125
|
+
* Whether the wire will CARRY a disable key for this model — off is SPELLED, never assumed. Per wire: the
|
|
126
|
+
* anthropic budget form spells `{type:"disabled"}` (the ADAPTIVE form rejects it — those models think on
|
|
127
|
+
* every request); deepseek `{type:"disabled"}`, the binary enable keys at `false`, openrouter/together
|
|
128
|
+
* `reasoning:{enabled:false}` all spell it; the effort-only wires (stock openai completions, the Responses
|
|
129
|
+
* lane) spell it only through a deployment-declared off spelling ({@link declaredOffSpelling}).
|
|
131
130
|
*
|
|
132
|
-
* A
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
131
|
+
* A model whose `reasoning` is FALSY spells NOTHING: every applier returns before writing any key for it, so
|
|
132
|
+
* the request is the gateway's own default — and a default-on gateway thinks regardless (three of four live
|
|
133
|
+
* endpoints, 2026-09-15). This arm used to answer `true` ("a non-reasoning model never thinks"): the engine
|
|
134
|
+
* may size a request by what it SENT, never by what it hopes a gateway does when told nothing. What this
|
|
135
|
+
* table cannot see is a gateway that IGNORES the key it was sent — the classifier's re-ask on a no-verdict
|
|
136
|
+
* reply is the seat that learns that from the reply.
|
|
137
137
|
*/
|
|
138
|
-
export declare function
|
|
138
|
+
export declare function thinkingOffSpelled(model: OffCapabilityModel): boolean;
|
|
139
139
|
/**
|
|
140
140
|
* OPTIONAL per-request facts for {@link resolveReasoning} — what the wire's budget arm knows at
|
|
141
141
|
* request build that a per-leg eager resolution cannot: the resolved output cap and whether it is a
|
package/dist/brain/reasoning.js
CHANGED
|
@@ -30,9 +30,9 @@ export function declaredOffSpelling(model) {
|
|
|
30
30
|
const mapped = model.thinkingLevelMap?.off;
|
|
31
31
|
return typeof mapped === "string" && mapped !== "" ? mapped : undefined;
|
|
32
32
|
}
|
|
33
|
-
export function
|
|
33
|
+
export function thinkingOffSpelled(model) {
|
|
34
34
|
if (!model.reasoning)
|
|
35
|
-
return
|
|
35
|
+
return false;
|
|
36
36
|
const compat = (model.compat ?? {});
|
|
37
37
|
if (model.api === "anthropic-messages")
|
|
38
38
|
return compat.thinkingMode !== "adaptive";
|
|
@@ -194,11 +194,15 @@ export interface StreamParser {
|
|
|
194
194
|
* - `hasOnlyThinking` — thinking streamed but nothing substantive (no non-blank text, no completed
|
|
195
195
|
* tool call) → tier B (seal + whole-turn retry). A dangling tool-call accumulation alongside the
|
|
196
196
|
* thinking does not veto this (nothing was admitted; the retry's snapshot replacement drops it).
|
|
197
|
+
* - `terminalFrameSeen` — the provider TERMINATED this stream (openai `finish_reason`, anthropic
|
|
198
|
+
* `message_delta.stop_reason`, responses `response.completed`/`response.incomplete`). A FACT, not a tier:
|
|
199
|
+
* the parser reports it off the same state its own finalize reads, and the tiering owns what it means.
|
|
197
200
|
*/
|
|
198
201
|
snapshot(): {
|
|
199
202
|
hasSubstantiveText: boolean;
|
|
200
203
|
hasCompletedToolCall: boolean;
|
|
201
204
|
hasOnlyThinking: boolean;
|
|
205
|
+
terminalFrameSeen: boolean;
|
|
202
206
|
};
|
|
203
207
|
/**
|
|
204
208
|
* design/124 §0.5-2 tier B: seal the UI stream before a whole-turn retry — emit `thinking_end` for
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createAssistantMessageEventStream, } from "../internal/llm.js";
|
|
2
|
+
import { STREAM_TORN_WARNING } from "./stream-shared.js";
|
|
2
3
|
import { FLOOR_OUTPUT_TOKENS, parseContextOverflow, planOutputCapAdjustment } from "./context-overflow.js";
|
|
3
4
|
import { BrainError, classifyConnectFailure, classifyHttp, describeNetworkError, namesTheFailure, readProviderRequestId } from "./errors.js";
|
|
4
5
|
import { classifyInputTooLong } from "./input-too-long.js";
|
|
@@ -646,11 +647,15 @@ export function runStreamingBrain(args) {
|
|
|
646
647
|
return;
|
|
647
648
|
}
|
|
648
649
|
const snap = parser.snapshot();
|
|
650
|
+
if (snap.terminalFrameSeen) {
|
|
651
|
+
parser.finalize();
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
649
654
|
if (snap.hasSubstantiveText || snap.hasCompletedToolCall) {
|
|
650
655
|
midstreamWarning =
|
|
651
656
|
failure.kind === "stalled"
|
|
652
657
|
? "Response stalled mid-stream. The response above may be incomplete."
|
|
653
|
-
:
|
|
658
|
+
: STREAM_TORN_WARNING;
|
|
654
659
|
parser.finalize({ truncated: true });
|
|
655
660
|
return;
|
|
656
661
|
}
|
|
@@ -24,6 +24,9 @@ import { type RepetitionEvent } from "./repetition.js";
|
|
|
24
24
|
* means "do not emit a `toolcall_end` for this call"; callers must NOT cache that outcome — finalize retries
|
|
25
25
|
* the parse against the complete accumulation.
|
|
26
26
|
*/
|
|
27
|
+
/** The CC-verbatim warning stamped beside `partialFinalized` when a stream ended before its close — by the
|
|
28
|
+
* mid-stream promotion (the body threw) and by the parsers' own finalize (content arrived, no finish frame). */
|
|
29
|
+
export declare const STREAM_TORN_WARNING = "Connection closed mid-response. The response above may be incomplete.";
|
|
27
30
|
export declare function parseStreamedToolArgs(json: string): Record<string, unknown> | undefined;
|
|
28
31
|
/** Repetition telemetry for ONE stream — the cut event plus the allowance-spared hits. */
|
|
29
32
|
export interface RepetitionPoll {
|
|
@@ -133,6 +133,22 @@ export interface AskOriginFacts {
|
|
|
133
133
|
export declare function askOriginOf(decision: Extract<PermissionResult, {
|
|
134
134
|
action: "ask";
|
|
135
135
|
}>, facts: AskOriginFacts): AskOrigin;
|
|
136
|
+
/**
|
|
137
|
+
* Stamp the ask's origin word — and withdraw the disclosures that speak for the TIGHTEN alone when some
|
|
138
|
+
* layer above it is the reason this call is being asked about. Today that is one member: the read
|
|
139
|
+
* boundary's directory offer, whose claim holds only while the tighten is the whole reason. Every higher
|
|
140
|
+
* rung is a layer with a reason of its own — a standing `ask` row, an unreadable rule store, an org
|
|
141
|
+
* verdict, a hook, the denial-limit fallback — which no directory grant answers, so the ask would stand
|
|
142
|
+
* after the grant and the offer would have promised a clearing it cannot deliver (measured: `cat
|
|
143
|
+
* <out-of-root>` under a standing `Bash(cat:*)` ask rule offered the directory and kept asking after it).
|
|
144
|
+
* It lives at the one derivation rather than the three stamp sites: the withdrawal's input IS the origin
|
|
145
|
+
* word, so a site that stamped without it would be a second, quieter answer to one question.
|
|
146
|
+
*/
|
|
147
|
+
export declare function stampAskOrigin<T extends Extract<PermissionResult, {
|
|
148
|
+
action: "ask";
|
|
149
|
+
}>>(decision: T, facts: AskOriginFacts): T & {
|
|
150
|
+
origin: AskOrigin;
|
|
151
|
+
};
|
|
136
152
|
/**
|
|
137
153
|
* Which origins IMPLY the `requiresRealApproval` bit, and WHERE that implication is verified — the
|
|
138
154
|
* consistency table {@link askOriginOf} reads. An ask of a must-carry origin exists so that no automatic
|
package/dist/core/ask-origin.js
CHANGED
|
@@ -15,6 +15,14 @@ export function askOriginOf(decision, facts) {
|
|
|
15
15
|
}
|
|
16
16
|
return origin;
|
|
17
17
|
}
|
|
18
|
+
const TIGHTEN_ONLY_ORIGINS = new Set(["shell_gate_tighten", "safety_tighten"]);
|
|
19
|
+
export function stampAskOrigin(decision, facts) {
|
|
20
|
+
const origin = askOriginOf(decision, facts);
|
|
21
|
+
if (decision.probeReadRootCandidate === undefined || TIGHTEN_ONLY_ORIGINS.has(origin))
|
|
22
|
+
return { ...decision, origin };
|
|
23
|
+
const { probeReadRootCandidate: _withdrawn, ...kept } = decision;
|
|
24
|
+
return { ...kept, origin };
|
|
25
|
+
}
|
|
18
26
|
function originLadder(decision, facts) {
|
|
19
27
|
if (facts.contentQuestion)
|
|
20
28
|
return "content_question";
|
|
@@ -180,5 +180,11 @@ export type AutoModeArmingFold = {
|
|
|
180
180
|
* nevertheless declare the same epoch — that is a deployment telling us its settings did not move
|
|
181
181
|
* while its own data says otherwise, and picking either side would be a guess about which one is the
|
|
182
182
|
* policy in force.
|
|
183
|
+
*
|
|
184
|
+
* @contract auto_mode.arming.absent_timeout_reads_recorded_default — a parked arming's absent `timeoutMs` is
|
|
185
|
+
* read as the default in force when it was recorded (`AUTO_MODE_RECORDED_ABSENT_TIMEOUT_MS`, 15_000 ms), not
|
|
186
|
+
* the default in force when it is redeemed: persisted data is read under the contract it was written under,
|
|
187
|
+
* so a default that moves can never widen an ancestor's bound at redemption; every recipe minted since
|
|
188
|
+
* materialises the knob, so absence dates the recording.
|
|
183
189
|
*/
|
|
184
190
|
export declare function foldAutoModeArming(recorded: unknown, current: unknown): AutoModeArmingFold;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { AUTO_MODE_DEFAULT_TIMEOUT_MS, AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS, AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIES, AUTO_MODE_DEFAULTS_SENTINEL, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_DENIAL_LIMIT_DEFAULTS, } from "./auto-mode-defaults.js";
|
|
1
|
+
import { AUTO_MODE_DEFAULT_TIMEOUT_MS, AUTO_MODE_RECORDED_ABSENT_TIMEOUT_MS, AUTO_MODE_DEFAULT_WINDOW_MAX_CHARS, AUTO_MODE_DEFAULT_WINDOW_MAX_ENTRIES, AUTO_MODE_DEFAULTS_SENTINEL, AUTO_MODE_DENIAL_AUTO_DENY_DEFAULT_MS, AUTO_MODE_DENIAL_LIMIT_DEFAULTS, } from "./auto-mode-defaults.js";
|
|
2
2
|
export const AUTO_MODE_ARMING_RECIPE_VERSION = 2;
|
|
3
3
|
const isPlainRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
4
4
|
function canonicalStrings(v) {
|
|
@@ -143,7 +143,7 @@ export function autoModeArmingRecipeOf(face, bind) {
|
|
|
143
143
|
...(face.settingsDenyRules !== undefined ? { settingsDenyRules: face.settingsDenyRules } : {}),
|
|
144
144
|
...(face.sessionContext !== undefined ? { sessionContext: face.sessionContext } : {}),
|
|
145
145
|
...(face.window !== undefined ? { window: face.window } : {}),
|
|
146
|
-
|
|
146
|
+
timeoutMs: face.timeoutMs ?? AUTO_MODE_DEFAULT_TIMEOUT_MS,
|
|
147
147
|
...(face.denialLimit !== undefined ? { denialLimit: face.denialLimit } : {}),
|
|
148
148
|
...(face.settingsEpoch !== undefined ? { settingsEpoch: face.settingsEpoch } : {}),
|
|
149
149
|
...(face.crossSessionMessagesRule !== undefined ? { crossSessionMessagesRule: face.crossSessionMessagesRule } : {}),
|
|
@@ -208,8 +208,8 @@ export function foldAutoModeArming(recorded, current) {
|
|
|
208
208
|
"(the strictest decidable answer: the inherited ask flows the original chain to a human)",
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
|
-
const
|
|
212
|
-
const timeoutMs =
|
|
211
|
+
const recordedTimeoutMs = rec.timeoutMs ?? AUTO_MODE_RECORDED_ABSENT_TIMEOUT_MS;
|
|
212
|
+
const timeoutMs = Math.min(recordedTimeoutMs, cur.timeoutMs ?? AUTO_MODE_DEFAULT_TIMEOUT_MS);
|
|
213
213
|
const denialLimit = tightenDenialLimit(rec.denialLimit, cur.denialLimit);
|
|
214
214
|
const recordedDenial = tightenDenialLimit(rec.denialLimit, rec.denialLimit);
|
|
215
215
|
return {
|
|
@@ -221,7 +221,7 @@ export function foldAutoModeArming(recorded, current) {
|
|
|
221
221
|
denialLimit,
|
|
222
222
|
...(cur.settingsEpoch !== undefined ? { settingsEpoch: cur.settingsEpoch } : {}),
|
|
223
223
|
},
|
|
224
|
-
tightened: timeoutMs <
|
|
224
|
+
tightened: timeoutMs < recordedTimeoutMs ||
|
|
225
225
|
denialLimit.maxConsecutive < recordedDenial.maxConsecutive ||
|
|
226
226
|
denialLimit.maxTotal < recordedDenial.maxTotal ||
|
|
227
227
|
denialLimit.autoDenyAfterMs !== recordedDenial.autoDenyAfterMs,
|