@namzu/sdk 51.1.0 → 52.0.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 +100 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +2 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/examples/SupervisorAgent.d.ts +42 -0
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/examples/SupervisorAgent.js +80 -4
- package/dist/agents/examples/SupervisorAgent.js.map +1 -1
- package/dist/agents/index.d.ts +1 -1
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js.map +1 -1
- package/dist/authorization/protected-paths.d.ts +36 -0
- package/dist/authorization/protected-paths.d.ts.map +1 -0
- package/dist/authorization/protected-paths.js +107 -0
- package/dist/authorization/protected-paths.js.map +1 -0
- package/dist/authorization/readonly-commands.d.ts +39 -0
- package/dist/authorization/readonly-commands.d.ts.map +1 -0
- package/dist/authorization/readonly-commands.js +797 -0
- package/dist/authorization/readonly-commands.js.map +1 -0
- package/dist/authorization/readonly-flags.d.ts +19 -0
- package/dist/authorization/readonly-flags.d.ts.map +1 -0
- package/dist/authorization/readonly-flags.js +178 -0
- package/dist/authorization/readonly-flags.js.map +1 -0
- package/dist/authorization/removal.d.ts +52 -0
- package/dist/authorization/removal.d.ts.map +1 -0
- package/dist/authorization/removal.js +283 -0
- package/dist/authorization/removal.js.map +1 -0
- package/dist/authorization/shell-paths.d.ts +66 -0
- package/dist/authorization/shell-paths.d.ts.map +1 -0
- package/dist/authorization/shell-paths.js +697 -0
- package/dist/authorization/shell-paths.js.map +1 -0
- package/dist/bridge/acp/update.d.ts.map +1 -1
- package/dist/bridge/acp/update.js +1 -0
- package/dist/bridge/acp/update.js.map +1 -1
- package/dist/compaction/context-window.d.ts +24 -0
- package/dist/compaction/context-window.d.ts.map +1 -1
- package/dist/compaction/context-window.js +34 -0
- package/dist/compaction/context-window.js.map +1 -1
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +10 -2
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/plan.d.ts +4 -0
- package/dist/compaction/plan.d.ts.map +1 -1
- package/dist/compaction/plan.js +6 -2
- package/dist/compaction/plan.js.map +1 -1
- package/dist/compaction/verifier.d.ts +11 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +19 -7
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/runtime.d.ts +55 -7
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +23 -2
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/web/guarded-fetch.d.ts.map +1 -1
- package/dist/connector/web/guarded-fetch.js +24 -0
- package/dist/connector/web/guarded-fetch.js.map +1 -1
- package/dist/connector/web/index.d.ts +1 -1
- package/dist/connector/web/index.d.ts.map +1 -1
- package/dist/connector/web/page.d.ts +59 -0
- package/dist/connector/web/page.d.ts.map +1 -0
- package/dist/connector/web/page.js +112 -0
- package/dist/connector/web/page.js.map +1 -0
- package/dist/connector/web/types.d.ts +47 -0
- package/dist/connector/web/types.d.ts.map +1 -1
- package/dist/connector/web/types.js.map +1 -1
- package/dist/constants/continuation.d.ts +8 -0
- package/dist/constants/continuation.d.ts.map +1 -1
- package/dist/constants/continuation.js +8 -0
- package/dist/constants/continuation.js.map +1 -1
- package/dist/constants/plugin/index.d.ts +1 -1
- package/dist/constants/plugin/index.d.ts.map +1 -1
- package/dist/constants/plugin/index.js +1 -1
- package/dist/constants/plugin/index.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +7 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +11 -2
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +4 -2
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +1 -1
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts +6 -0
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +98 -114
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/shell-hook.d.ts +90 -22
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +434 -41
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/public-runtime.d.ts +4 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +4 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -2
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.d.ts +21 -0
- package/dist/runtime/jobs/registry.d.ts.map +1 -1
- package/dist/runtime/jobs/registry.js +15 -2
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.d.ts +10 -0
- package/dist/runtime/query/executor/tool-call-admission.d.ts.map +1 -1
- package/dist/runtime/query/executor/tool-call-admission.js +74 -13
- package/dist/runtime/query/executor/tool-call-admission.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +41 -2
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +190 -6
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/finalize-turn.d.ts +6 -0
- package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
- package/dist/runtime/query/finalize-turn.js +1 -1
- package/dist/runtime/query/finalize-turn.js.map +1 -1
- package/dist/runtime/query/index.d.ts +29 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +15 -3
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +31 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +216 -7
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +58 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.js +91 -0
- package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +89 -7
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +10 -0
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +165 -2
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/path-escalation.d.ts +36 -0
- package/dist/runtime/query/path-escalation.d.ts.map +1 -0
- package/dist/runtime/query/path-escalation.js +184 -0
- package/dist/runtime/query/path-escalation.js.map +1 -0
- package/dist/runtime/query/plugin-hooks.d.ts.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +8 -0
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/reminders.d.ts +35 -0
- package/dist/runtime/query/reminders.d.ts.map +1 -0
- package/dist/runtime/query/reminders.js +7 -0
- package/dist/runtime/query/reminders.js.map +1 -0
- package/dist/runtime/query/resume-pending.d.ts.map +1 -1
- package/dist/runtime/query/resume-pending.js +26 -0
- package/dist/runtime/query/resume-pending.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +26 -0
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +79 -5
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/tool-output-budget.d.ts +46 -0
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +85 -1
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +1 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +5 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/session/paths.d.ts +5 -1
- package/dist/session/paths.d.ts.map +1 -1
- package/dist/session/paths.js +8 -0
- package/dist/session/paths.js.map +1 -1
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +2 -2
- package/dist/store/memory/disk.js.map +1 -1
- package/dist/store/memory/markdown.d.ts.map +1 -1
- package/dist/store/memory/markdown.js +2 -2
- package/dist/store/memory/markdown.js.map +1 -1
- package/dist/store/memory/naming.d.ts +9 -0
- package/dist/store/memory/naming.d.ts.map +1 -1
- package/dist/store/memory/naming.js +12 -0
- package/dist/store/memory/naming.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts +30 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +170 -26
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/content-fingerprint.d.ts +2 -0
- package/dist/tools/builtins/content-fingerprint.d.ts.map +1 -1
- package/dist/tools/builtins/content-fingerprint.js +4 -0
- package/dist/tools/builtins/content-fingerprint.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +60 -18
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts +8 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +152 -33
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts +1 -0
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +44 -5
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/read-render.d.ts +52 -0
- package/dist/tools/builtins/read-render.d.ts.map +1 -1
- package/dist/tools/builtins/read-render.js +143 -0
- package/dist/tools/builtins/read-render.js.map +1 -1
- package/dist/tools/builtins/web.d.ts +13 -2
- package/dist/tools/builtins/web.d.ts.map +1 -1
- package/dist/tools/builtins/web.js +225 -32
- package/dist/tools/builtins/web.js.map +1 -1
- package/dist/tools/coordinator/ask-user-question.d.ts +2 -0
- package/dist/tools/coordinator/ask-user-question.d.ts.map +1 -1
- package/dist/tools/coordinator/ask-user-question.js +256 -92
- package/dist/tools/coordinator/ask-user-question.js.map +1 -1
- package/dist/tools/coordinator/question-options.d.ts +1 -0
- package/dist/tools/coordinator/question-options.d.ts.map +1 -1
- package/dist/tools/coordinator/question-options.js +6 -1
- package/dist/tools/coordinator/question-options.js.map +1 -1
- package/dist/tools/defineTool.d.ts +4 -1
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +6 -1
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/paths.d.ts +6 -0
- package/dist/tools/paths.d.ts.map +1 -1
- package/dist/tools/paths.js +1 -1
- package/dist/tools/paths.js.map +1 -1
- package/dist/types/agent/query.d.ts +3 -0
- package/dist/types/agent/query.d.ts.map +1 -1
- package/dist/types/errors/catalog.js +1 -1
- package/dist/types/errors/catalog.js.map +1 -1
- package/dist/types/hitl/index.d.ts +104 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/message/index.d.ts +1 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +2 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +93 -19
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +3 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/errors.d.ts +19 -0
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +24 -0
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/session/events.d.ts +10 -2
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/events.js.map +1 -1
- package/dist/types/session/records.d.ts +9 -9
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +1 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/stop-reason.d.ts +5 -0
- package/dist/types/session/stop-reason.d.ts.map +1 -1
- package/dist/types/tool/index.d.ts +52 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/package.json +4 -2
- package/src/agents/QueryAgent.ts +5 -1
- package/src/agents/SupervisorAgent.ts +1 -1
- package/src/agents/examples/SupervisorAgent.ts +217 -106
- package/src/agents/index.ts +1 -1
- package/src/authorization/protected-paths.ts +110 -0
- package/src/authorization/readonly-commands.ts +771 -0
- package/src/authorization/readonly-flags.ts +196 -0
- package/src/authorization/removal.ts +326 -0
- package/src/authorization/shell-paths.ts +927 -0
- package/src/bridge/acp/update.ts +1 -0
- package/src/compaction/context-window.ts +41 -0
- package/src/compaction/manual.ts +10 -2
- package/src/compaction/plan.ts +11 -2
- package/src/compaction/verifier.ts +28 -3
- package/src/config/runtime.ts +23 -2
- package/src/connector/web/guarded-fetch.ts +26 -0
- package/src/connector/web/index.ts +3 -0
- package/src/connector/web/page.ts +130 -0
- package/src/connector/web/types.ts +47 -0
- package/src/constants/continuation.ts +10 -0
- package/src/constants/plugin/index.ts +1 -1
- package/src/manager/agent/lifecycle.ts +12 -4
- package/src/manager/session/turn-recorder.ts +10 -2
- package/src/plugin/index.ts +1 -0
- package/src/plugin/lifecycle.ts +107 -119
- package/src/plugin/shell-hook.ts +477 -43
- package/src/public-runtime.ts +8 -0
- package/src/public-types.ts +4 -0
- package/src/runtime/jobs/registry.ts +39 -2
- package/src/runtime/query/executor/tool-call-admission.ts +79 -13
- package/src/runtime/query/executor.ts +242 -8
- package/src/runtime/query/finalize-turn.ts +7 -1
- package/src/runtime/query/index.ts +45 -3
- package/src/runtime/query/iteration/index.ts +251 -12
- package/src/runtime/query/iteration/phases/compaction-breaker.ts +106 -0
- package/src/runtime/query/iteration/phases/compaction.ts +99 -12
- package/src/runtime/query/iteration/phases/context.ts +11 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +177 -2
- package/src/runtime/query/path-escalation.ts +204 -0
- package/src/runtime/query/plugin-hooks.ts +8 -0
- package/src/runtime/query/reminders.ts +45 -0
- package/src/runtime/query/resume-pending.ts +27 -0
- package/src/runtime/query/review-policy.ts +106 -5
- package/src/runtime/query/tool-output-budget.ts +115 -1
- package/src/runtime/query/tooling.ts +6 -0
- package/src/session/paths.ts +11 -1
- package/src/store/memory/disk.ts +2 -1
- package/src/store/memory/markdown.ts +2 -1
- package/src/store/memory/naming.ts +13 -0
- package/src/tools/builtins/bash.ts +203 -34
- package/src/tools/builtins/content-fingerprint.ts +5 -0
- package/src/tools/builtins/edit.ts +69 -19
- package/src/tools/builtins/grep.ts +159 -38
- package/src/tools/builtins/read-file.ts +48 -5
- package/src/tools/builtins/read-render.ts +193 -0
- package/src/tools/builtins/web.ts +258 -33
- package/src/tools/coordinator/ask-user-question.ts +309 -103
- package/src/tools/coordinator/question-options.ts +8 -1
- package/src/tools/defineTool.ts +11 -2
- package/src/tools/paths.ts +1 -1
- package/src/types/agent/query.ts +3 -0
- package/src/types/errors/catalog.ts +1 -1
- package/src/types/hitl/index.ts +104 -0
- package/src/types/message/index.ts +2 -0
- package/src/types/plugin/index.ts +101 -18
- package/src/types/provider/errors.ts +33 -0
- package/src/types/session/events.ts +10 -2
- package/src/types/session/records.ts +1 -0
- package/src/types/session/stop-reason.ts +5 -0
- package/src/types/tool/index.ts +53 -2
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
extractFromUserMessage,
|
|
5
5
|
} from '../../../compaction/extractor.js'
|
|
6
6
|
import { estimateMessageTokens } from '../../../compaction/token-estimate.js'
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
AUTO_CONTINUATION_USER_MESSAGE,
|
|
9
|
+
REASONING_CUT_USER_MESSAGE,
|
|
10
|
+
} from '../../../constants/continuation.js'
|
|
8
11
|
import { CHARS_PER_TOKEN } from '../../../constants/limits.js'
|
|
9
12
|
import {
|
|
10
13
|
DEFAULT_STRUCTURED_OUTPUT_RETRIES,
|
|
@@ -27,7 +30,7 @@ import { STRUCTURED_OUTPUT_TOOL_NAME } from '../../../tools/builtins/structuredO
|
|
|
27
30
|
import type { ToolManager } from '../../../toolsets/manager.js'
|
|
28
31
|
import type { CostInfo, TokenUsage } from '../../../types/common/index.js'
|
|
29
32
|
import { NamzuError } from '../../../types/errors/index.js'
|
|
30
|
-
import type { MessageId } from '../../../types/ids/index.js'
|
|
33
|
+
import type { MessageId, SessionId, TurnId } from '../../../types/ids/index.js'
|
|
31
34
|
import {
|
|
32
35
|
type Message,
|
|
33
36
|
type UserMessage,
|
|
@@ -35,7 +38,7 @@ import {
|
|
|
35
38
|
createRuntimeContextMessage,
|
|
36
39
|
createSystemMessage,
|
|
37
40
|
} from '../../../types/message/index.js'
|
|
38
|
-
import { classifyProviderError } from '../../../types/provider/errors.js'
|
|
41
|
+
import { classifyProviderError, readOutputOverflow } from '../../../types/provider/errors.js'
|
|
39
42
|
import type { ChatCompletionResponse } from '../../../types/provider/index.js'
|
|
40
43
|
import type { AnswerReview, AnswerReviewContext } from '../../../types/session/answer-review.js'
|
|
41
44
|
import type {
|
|
@@ -75,6 +78,7 @@ import {
|
|
|
75
78
|
} from './outstanding-work.js'
|
|
76
79
|
import { runAdvisoryPhase } from './phases/advisory.js'
|
|
77
80
|
import { runIterationCheckpoint } from './phases/checkpoint.js'
|
|
81
|
+
import { compactionBreaker } from './phases/compaction-breaker.js'
|
|
78
82
|
import {
|
|
79
83
|
activeContextWindow,
|
|
80
84
|
measureContext,
|
|
@@ -125,6 +129,11 @@ export type { ToolReviewOutcome } from './phases/index.js'
|
|
|
125
129
|
*/
|
|
126
130
|
const DEFAULT_ANSWER_REVIEW_LIMIT = 3
|
|
127
131
|
|
|
132
|
+
/** Left unused when an output cap is shrunk to fit the window (reference: 1000). */
|
|
133
|
+
const OUTPUT_CAP_MARGIN_TOKENS = 1000
|
|
134
|
+
/** A shrunk output cap below this is not worth a call (reference floor: 3000). */
|
|
135
|
+
const MIN_OUTPUT_CAP_TOKENS = 3000
|
|
136
|
+
|
|
128
137
|
// Ending a turn changes the available actions, not the strength of its evidence.
|
|
129
138
|
// Use the same standard for warning closure and empty-completion recovery.
|
|
130
139
|
const CLOSING_RESPONSE_GUIDANCE =
|
|
@@ -194,6 +203,74 @@ export class IterationOrchestrator {
|
|
|
194
203
|
* sets it.
|
|
195
204
|
*/
|
|
196
205
|
private stopDeferredForOutstandingWork = false
|
|
206
|
+
/** Nudges sent since the model last finished a call without hitting its output cap. */
|
|
207
|
+
private outputContinuations = 0
|
|
208
|
+
/**
|
|
209
|
+
* `turn_end` (and, for a child, `subagent_stop`) fired when the model
|
|
210
|
+
* finished and no hook asked for more. `finalizeTurn` then does not fire
|
|
211
|
+
* them a second time.
|
|
212
|
+
*/
|
|
213
|
+
private turnEndFired = false
|
|
214
|
+
/** A hook already kept this turn going once: it is asked again with `stop_hook_active`. */
|
|
215
|
+
private stopHookActive = false
|
|
216
|
+
get turnEndHooksFired(): boolean {
|
|
217
|
+
return this.turnEndFired
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Fire `turn_end` (and `subagent_stop` for a child) at the point the model
|
|
222
|
+
* has finished, and read the answer.
|
|
223
|
+
*
|
|
224
|
+
* A hook that answers `skip` is refusing to let the turn end: its reasons
|
|
225
|
+
* are returned, joined, as feedback for the model. A hook that asks for the
|
|
226
|
+
* turn to halt wins over any such feedback (`takeHalt`). With nothing asked,
|
|
227
|
+
* the events have fired for this turn and the finalizer does not repeat them.
|
|
228
|
+
*/
|
|
229
|
+
private async *askStopHooks(): AsyncGenerator<SessionEvent, string | undefined> {
|
|
230
|
+
const manager = this.ctx.pluginManager
|
|
231
|
+
if (!manager) return undefined
|
|
232
|
+
const recorder = this.ctx.recorder
|
|
233
|
+
const feedback: string[] = []
|
|
234
|
+
const ask = async (
|
|
235
|
+
event: 'turn_end' | 'subagent_stop',
|
|
236
|
+
extra: { parentSessionId?: SessionId; parentTurnId?: TurnId },
|
|
237
|
+
): Promise<void> => {
|
|
238
|
+
const results = await manager.executeHooks(
|
|
239
|
+
event,
|
|
240
|
+
{
|
|
241
|
+
sessionId: recorder.sessionId,
|
|
242
|
+
turnId: recorder.turnId,
|
|
243
|
+
...extra,
|
|
244
|
+
...(this.stopHookActive ? { stopHookActive: true } : {}),
|
|
245
|
+
signal: this.ctx.abortController.signal,
|
|
246
|
+
},
|
|
247
|
+
this.ctx.emitEvent,
|
|
248
|
+
)
|
|
249
|
+
applyLifecycleHookResults(event, results)
|
|
250
|
+
for (const result of results) {
|
|
251
|
+
if (result.action === 'skip') feedback.push(result.reason)
|
|
252
|
+
if ((result.action === 'continue' || result.action === 'skip') && result.halt) {
|
|
253
|
+
this.ctx.toolExecutor.requestHalt?.(result.halt.reason)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
await ask('turn_end', {})
|
|
258
|
+
yield* this.ctx.drainPending()
|
|
259
|
+
if (this.ctx.parentSessionId !== undefined) {
|
|
260
|
+
await ask('subagent_stop', {
|
|
261
|
+
parentSessionId: this.ctx.parentSessionId,
|
|
262
|
+
...(this.ctx.parentTurnId !== undefined ? { parentTurnId: this.ctx.parentTurnId } : {}),
|
|
263
|
+
})
|
|
264
|
+
yield* this.ctx.drainPending()
|
|
265
|
+
}
|
|
266
|
+
if (feedback.length === 0) {
|
|
267
|
+
this.turnEndFired = true
|
|
268
|
+
return undefined
|
|
269
|
+
}
|
|
270
|
+
this.stopHookActive = true
|
|
271
|
+
return feedback.join('\n')
|
|
272
|
+
}
|
|
273
|
+
|
|
197
274
|
/** One current input, independent of the compactable history array. */
|
|
198
275
|
private latestUserMessage: UserMessage | undefined
|
|
199
276
|
private initialDurableDelivered = false
|
|
@@ -375,6 +452,9 @@ export class IterationOrchestrator {
|
|
|
375
452
|
// obvious moves left. It is cleared by a turn that actually
|
|
376
453
|
// succeeded, which is the evidence that the turn is no longer stuck.
|
|
377
454
|
let overflowRelieved = false
|
|
455
|
+
// The output cap a provider told us fits beside this prompt. One request's
|
|
456
|
+
// worth: it is cleared by a call that went through.
|
|
457
|
+
let outputCapOverride: number | undefined
|
|
378
458
|
|
|
379
459
|
// A `finally` rather than a line at each exit, for the reason written
|
|
380
460
|
// beside `iterSpan.end()` below: this loop leaves by eight `break`s,
|
|
@@ -573,12 +653,15 @@ export class IterationOrchestrator {
|
|
|
573
653
|
yield* this.ctx.drainPending()
|
|
574
654
|
}
|
|
575
655
|
|
|
656
|
+
if (!forceFinalize) await this.deliverReminders(iterationNum)
|
|
657
|
+
|
|
576
658
|
// Re-pin the working-memory block from ground truth at the primacy
|
|
577
659
|
// edge BEFORE compaction runs (so the refreshed slot is what
|
|
578
660
|
// compaction preserves). No-op when no provider is configured.
|
|
579
661
|
await refreshWorkingMemory(this.ctx)
|
|
580
662
|
await runCompactionCheck(this.ctx)
|
|
581
663
|
yield* this.ctx.drainPending()
|
|
664
|
+
if (this.stopForCompactionBreaker()) break
|
|
582
665
|
|
|
583
666
|
// Cache discipline: keep the tools param byte-stable even on the
|
|
584
667
|
// forced-final iteration and forbid tool use via tool_choice
|
|
@@ -630,6 +713,7 @@ export class IterationOrchestrator {
|
|
|
630
713
|
if (stepModel !== contextModelBeforePreparation) await runCompactionCheck(this.ctx)
|
|
631
714
|
// Publish context edits before entering a possibly slow provider.
|
|
632
715
|
yield* this.ctx.drainPending()
|
|
716
|
+
if (this.stopForCompactionBreaker()) break
|
|
633
717
|
|
|
634
718
|
const stepAllowedTools = step.allowedTools ?? this.ctx.allowedTools
|
|
635
719
|
const deferredMcpContext = deferredMcpHints?.forNames(stepAllowedTools) ?? ''
|
|
@@ -808,7 +892,8 @@ export class IterationOrchestrator {
|
|
|
808
892
|
messages: Object.freeze(messages.map((m) => Object.freeze({ ...m }))),
|
|
809
893
|
toolNames: Object.freeze(llmTools.map((t) => t.function.name)),
|
|
810
894
|
temperature: step.temperature ?? turnConfig.temperature,
|
|
811
|
-
maxTokens:
|
|
895
|
+
maxTokens:
|
|
896
|
+
outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
|
|
812
897
|
}),
|
|
813
898
|
},
|
|
814
899
|
this.ctx.emitEvent,
|
|
@@ -881,7 +966,8 @@ export class IterationOrchestrator {
|
|
|
881
966
|
? step.toolChoice
|
|
882
967
|
: undefined,
|
|
883
968
|
temperature: step.temperature ?? turnConfig.temperature,
|
|
884
|
-
maxTokens:
|
|
969
|
+
maxTokens:
|
|
970
|
+
outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
|
|
885
971
|
cacheControl: { type: 'auto' },
|
|
886
972
|
...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
|
|
887
973
|
...(turnConfig.effort ? { effort: turnConfig.effort } : {}),
|
|
@@ -957,6 +1043,8 @@ export class IterationOrchestrator {
|
|
|
957
1043
|
// The turn went through, so the turn is not sitting on an
|
|
958
1044
|
// irreducible prompt any more. Re-arm relief for the next one.
|
|
959
1045
|
overflowRelieved = false
|
|
1046
|
+
outputCapOverride = undefined
|
|
1047
|
+
if (response.finishReason !== 'length') this.outputContinuations = 0
|
|
960
1048
|
|
|
961
1049
|
if (this.ctx.pluginManager) {
|
|
962
1050
|
const hookResults = await this.ctx.pluginManager.executeHooks(
|
|
@@ -1283,19 +1371,55 @@ export class IterationOrchestrator {
|
|
|
1283
1371
|
// it has no room left to continue into, and the
|
|
1284
1372
|
// continuation would only send a longer prompt into
|
|
1285
1373
|
// the same full window. The reply stands as it is.
|
|
1374
|
+
const cutDuringReasoning =
|
|
1375
|
+
!hasContent &&
|
|
1376
|
+
(response.message.toolCalls?.length ?? 0) === 0 &&
|
|
1377
|
+
(response.message.reasoning?.length ?? 0) > 0
|
|
1286
1378
|
if (
|
|
1287
1379
|
!forceFinalize &&
|
|
1288
1380
|
response.finishReason === 'length' &&
|
|
1289
1381
|
response.finishDetail !== 'context_window' &&
|
|
1290
|
-
hasContent
|
|
1382
|
+
(hasContent || cutDuringReasoning)
|
|
1291
1383
|
) {
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1384
|
+
// A host that opted in gets a cap on the nudges: a model that
|
|
1385
|
+
// overruns its output limit every time would otherwise spend the
|
|
1386
|
+
// whole turn on it. Past the cap the turn stops, and says why.
|
|
1387
|
+
const cap = this.ctx.maxOutputContinuations
|
|
1388
|
+
if (cap !== undefined && this.outputContinuations >= cap) {
|
|
1389
|
+
this.ctx.log.warn('LLM kept hitting max_tokens — stopping the turn', {
|
|
1390
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
1391
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
1392
|
+
'namzu.runtime.output_continuations': this.outputContinuations,
|
|
1393
|
+
})
|
|
1394
|
+
recorder.setLastError(
|
|
1395
|
+
`The reply was cut off at the output limit ${cap + 1} times in a row. Ask for a smaller piece of the work, or raise the output limit.`,
|
|
1396
|
+
)
|
|
1397
|
+
recorder.setStopReason('output_limit')
|
|
1398
|
+
await this.ctx.emitEvent({
|
|
1399
|
+
type: 'iteration_completed',
|
|
1400
|
+
turnId: recorder.turnId,
|
|
1401
|
+
iteration: iterationNum,
|
|
1402
|
+
hasToolCalls: false,
|
|
1403
|
+
})
|
|
1404
|
+
yield* this.ctx.drainPending()
|
|
1405
|
+
break
|
|
1406
|
+
}
|
|
1407
|
+
this.outputContinuations++
|
|
1408
|
+
this.ctx.log.info(
|
|
1409
|
+
cutDuringReasoning
|
|
1410
|
+
? 'LLM hit max_tokens while reasoning — nudging to answer briefly'
|
|
1411
|
+
: 'LLM hit max_tokens mid-text — auto-continuing',
|
|
1412
|
+
{
|
|
1413
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
1414
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
1415
|
+
[GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
|
|
1416
|
+
},
|
|
1417
|
+
)
|
|
1297
1418
|
recorder.pushMessage(
|
|
1298
|
-
createRuntimeContextMessage(
|
|
1419
|
+
createRuntimeContextMessage(
|
|
1420
|
+
cutDuringReasoning ? REASONING_CUT_USER_MESSAGE : AUTO_CONTINUATION_USER_MESSAGE,
|
|
1421
|
+
'auto-continuation',
|
|
1422
|
+
),
|
|
1299
1423
|
)
|
|
1300
1424
|
await this.ctx.emitEvent({
|
|
1301
1425
|
type: 'iteration_completed',
|
|
@@ -1451,6 +1575,27 @@ export class IterationOrchestrator {
|
|
|
1451
1575
|
// also settle as `end_turn`, and there the deferred predicate
|
|
1452
1576
|
// is not why the turn ended: those decided the answer
|
|
1453
1577
|
// themselves.
|
|
1578
|
+
// The model is done and has no tool calls. A `turn_end` (or
|
|
1579
|
+
// `subagent_stop`) hook may refuse to let it be: its reason goes
|
|
1580
|
+
// back to the model and the turn goes on.
|
|
1581
|
+
if (!forceFinalize && closingStopReason === undefined) {
|
|
1582
|
+
const feedback = yield* this.askStopHooks()
|
|
1583
|
+
const stoppedByHook = this.ctx.toolExecutor.takeHalt?.()
|
|
1584
|
+
if (stoppedByHook !== undefined) {
|
|
1585
|
+
recorder.setLastError(`Stopped by a hook: ${stoppedByHook}`)
|
|
1586
|
+
recorder.setStopReason('stop_condition')
|
|
1587
|
+
break
|
|
1588
|
+
}
|
|
1589
|
+
if (feedback !== undefined) {
|
|
1590
|
+
recorder.pushMessage(
|
|
1591
|
+
createRuntimeContextMessage(
|
|
1592
|
+
`A hook asked for more before this turn ends:\n${feedback}`,
|
|
1593
|
+
'hook-feedback',
|
|
1594
|
+
),
|
|
1595
|
+
)
|
|
1596
|
+
continue
|
|
1597
|
+
}
|
|
1598
|
+
}
|
|
1454
1599
|
recorder.setStopReason(
|
|
1455
1600
|
closingStopReason ??
|
|
1456
1601
|
(stopWasDeferredForOutstandingWork ? 'stop_condition' : 'end_turn'),
|
|
@@ -1495,6 +1640,28 @@ export class IterationOrchestrator {
|
|
|
1495
1640
|
return
|
|
1496
1641
|
}
|
|
1497
1642
|
|
|
1643
|
+
// A hook asked for the turn to stop once this step's calls were
|
|
1644
|
+
// answered. Every `tool_use` has its `tool_result` by now, so the
|
|
1645
|
+
// transcript is whole; what is left is to end the turn and say why.
|
|
1646
|
+
const hookHalt = this.ctx.toolExecutor.takeHalt?.()
|
|
1647
|
+
if (hookHalt !== undefined) {
|
|
1648
|
+
this.ctx.log.info('A hook stopped the turn', {
|
|
1649
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
1650
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
1651
|
+
'namzu.hook.reason': hookHalt,
|
|
1652
|
+
})
|
|
1653
|
+
recorder.setLastError(`Stopped by a hook: ${hookHalt}`)
|
|
1654
|
+
recorder.setStopReason('stop_condition')
|
|
1655
|
+
await this.ctx.emitEvent({
|
|
1656
|
+
type: 'iteration_completed',
|
|
1657
|
+
turnId: recorder.turnId,
|
|
1658
|
+
iteration: iterationNum,
|
|
1659
|
+
hasToolCalls: true,
|
|
1660
|
+
})
|
|
1661
|
+
yield* this.ctx.drainPending()
|
|
1662
|
+
break
|
|
1663
|
+
}
|
|
1664
|
+
|
|
1498
1665
|
if (reviewOutcome.decision === 'rejected') {
|
|
1499
1666
|
continue
|
|
1500
1667
|
}
|
|
@@ -1822,6 +1989,36 @@ export class IterationOrchestrator {
|
|
|
1822
1989
|
// actually shed something. A second overflow after a successful
|
|
1823
1990
|
// compaction means the prompt is irreducible, and looping on it
|
|
1824
1991
|
// would burn the budget to arrive at the same error.
|
|
1992
|
+
// The prompt fit and only the requested output did not: ask for what
|
|
1993
|
+
// room is left (less a margin) and send it again, once. A reply that
|
|
1994
|
+
// would be too small to be useful, or a cap that is not smaller than
|
|
1995
|
+
// the one already tried, is not retried: the same failure would follow.
|
|
1996
|
+
const outputOverflow =
|
|
1997
|
+
err instanceof AnswerReviewFailure ? undefined : readOutputOverflow(err)
|
|
1998
|
+
if (outputOverflow) {
|
|
1999
|
+
const room = Math.max(
|
|
2000
|
+
0,
|
|
2001
|
+
outputOverflow.contextLimit - outputOverflow.inputTokens - OUTPUT_CAP_MARGIN_TOKENS,
|
|
2002
|
+
)
|
|
2003
|
+
if (
|
|
2004
|
+
room >= MIN_OUTPUT_CAP_TOKENS &&
|
|
2005
|
+
(outputCapOverride === undefined || room < outputCapOverride)
|
|
2006
|
+
) {
|
|
2007
|
+
outputCapOverride = room
|
|
2008
|
+
this.ctx.log.info('Output cap does not fit beside the prompt — retrying with less', {
|
|
2009
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
2010
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
2011
|
+
'namzu.runtime.input_tokens': outputOverflow.inputTokens,
|
|
2012
|
+
'namzu.runtime.context_limit': outputOverflow.contextLimit,
|
|
2013
|
+
'namzu.runtime.adjusted_max_tokens': room,
|
|
2014
|
+
})
|
|
2015
|
+
if (iterationActivity) {
|
|
2016
|
+
this.ctx.activityStore.complete(iterationActivity.id)
|
|
2017
|
+
}
|
|
2018
|
+
continue
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
|
|
1825
2022
|
if (
|
|
1826
2023
|
!overflowRelieved &&
|
|
1827
2024
|
!(err instanceof AnswerReviewFailure) &&
|
|
@@ -1925,6 +2122,39 @@ export class IterationOrchestrator {
|
|
|
1925
2122
|
}
|
|
1926
2123
|
}
|
|
1927
2124
|
|
|
2125
|
+
/**
|
|
2126
|
+
* Ask the host's reminder source (`QueryParams.reminders`) what the model
|
|
2127
|
+
* should be told before this request, and append each text as a hidden
|
|
2128
|
+
* user-role message. The start of a request is a provider-valid boundary:
|
|
2129
|
+
* the history ends in the operator's message or in a finished tool batch.
|
|
2130
|
+
* A source that throws has nothing to say; it never fails the turn.
|
|
2131
|
+
*/
|
|
2132
|
+
private async deliverReminders(iteration: number): Promise<void> {
|
|
2133
|
+
const source = this.ctx.reminders
|
|
2134
|
+
if (!source) return
|
|
2135
|
+
const { recorder } = this.ctx
|
|
2136
|
+
let texts: readonly string[] | undefined
|
|
2137
|
+
try {
|
|
2138
|
+
texts = await source({
|
|
2139
|
+
sessionId: recorder.sessionId,
|
|
2140
|
+
turnId: recorder.turnId,
|
|
2141
|
+
iteration,
|
|
2142
|
+
messages: recorder.messages,
|
|
2143
|
+
})
|
|
2144
|
+
} catch (error) {
|
|
2145
|
+
this.ctx.log.warn('A reminder source threw; the request goes without its reminders', {
|
|
2146
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
2147
|
+
[NAMZU.ITERATION]: iteration,
|
|
2148
|
+
'exception.message': error instanceof Error ? error.message : String(error),
|
|
2149
|
+
})
|
|
2150
|
+
return
|
|
2151
|
+
}
|
|
2152
|
+
for (const text of texts ?? []) {
|
|
2153
|
+
if (text.trim().length === 0) continue
|
|
2154
|
+
recorder.pushMessage(createRuntimeContextMessage(text, 'reminder'))
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
|
|
1928
2158
|
private async deliverInbound(): Promise<number> {
|
|
1929
2159
|
const queued = this.ctx.inboundMessages?.() ?? []
|
|
1930
2160
|
for (const message of queued) {
|
|
@@ -2078,6 +2308,15 @@ export class IterationOrchestrator {
|
|
|
2078
2308
|
return true
|
|
2079
2309
|
}
|
|
2080
2310
|
|
|
2311
|
+
/** The compaction circuit breaker asked for the turn to stop (opt-in; see `compactionConfig.circuitBreaker`). */
|
|
2312
|
+
private stopForCompactionBreaker(): boolean {
|
|
2313
|
+
const reason = compactionBreaker(this.ctx)?.stop
|
|
2314
|
+
if (reason === undefined) return false
|
|
2315
|
+
this.ctx.recorder.setLastError(reason)
|
|
2316
|
+
this.ctx.recorder.setStopReason('error')
|
|
2317
|
+
return true
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2081
2320
|
private async recordToolStructuredFailure(
|
|
2082
2321
|
results: readonly ToolCallOutcome[],
|
|
2083
2322
|
iteration: number,
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Two ways automatic compaction gives up for the rest of a turn.
|
|
3
|
+
*
|
|
4
|
+
* - Three failures in a row (a pass that ran and shed nothing): every one of
|
|
5
|
+
* them paid for a summary or a reducer call and left the history as it was,
|
|
6
|
+
* so a fourth would do the same. Compaction is skipped from then on and the
|
|
7
|
+
* turn carries on; a pass the provider forces by rejecting the prompt is not
|
|
8
|
+
* gated.
|
|
9
|
+
* - Three compactions in a row, each followed by the context filling back up
|
|
10
|
+
* within three iterations: something in the loop (a huge file read, a tool
|
|
11
|
+
* that prints without limit) refills the window faster than any summary can
|
|
12
|
+
* empty it. The turn stops, because carrying on only buys the same wall.
|
|
13
|
+
*
|
|
14
|
+
* Off unless `compactionConfig.circuitBreaker` is true: stopping compaction,
|
|
15
|
+
* and above all stopping a turn, is not something a host had before.
|
|
16
|
+
*
|
|
17
|
+
* State is per turn, held against the turn's recorder, so two turns never
|
|
18
|
+
* share a count.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import type { IterationContext } from './context.js'
|
|
22
|
+
|
|
23
|
+
/** Consecutive failed passes after which automatic compaction stops. */
|
|
24
|
+
export const COMPACTION_FAILURE_LIMIT = 3
|
|
25
|
+
/** A compaction followed by a trigger sooner than this many iterations is a refill. */
|
|
26
|
+
export const COMPACTION_REFILL_ITERATIONS = 3
|
|
27
|
+
/** Consecutive refills that stop the turn. */
|
|
28
|
+
export const COMPACTION_REFILL_LIMIT = 3
|
|
29
|
+
|
|
30
|
+
/** One short line, shown once, when the turn stops on refills. */
|
|
31
|
+
export const COMPACTION_REFILL_MESSAGE =
|
|
32
|
+
'The context filled up again right after being compacted, three times in a row. A file or tool output is probably too big for the window: read it in smaller parts, or start fresh with /clear.'
|
|
33
|
+
/** One short line, shown once, when compaction stops after failures. */
|
|
34
|
+
export const COMPACTION_FAILURE_MESSAGE =
|
|
35
|
+
'Automatic compaction stopped after three failed attempts. Run /compact yourself, or start fresh with /clear.'
|
|
36
|
+
|
|
37
|
+
export class CompactionBreaker {
|
|
38
|
+
private failures = 0
|
|
39
|
+
private refills = 0
|
|
40
|
+
private pendingRefills = 0
|
|
41
|
+
private lastCompactedIteration: number | undefined
|
|
42
|
+
private failureNoticeSent = false
|
|
43
|
+
private stopReason: string | undefined
|
|
44
|
+
|
|
45
|
+
/** Whether three failures in a row have switched automatic compaction off. */
|
|
46
|
+
get failuresOpen(): boolean {
|
|
47
|
+
return this.failures >= COMPACTION_FAILURE_LIMIT
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Mark the failure notice sent; true the first time only. */
|
|
51
|
+
takeFailureNotice(): boolean {
|
|
52
|
+
if (this.failureNoticeSent) return false
|
|
53
|
+
this.failureNoticeSent = true
|
|
54
|
+
return true
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
recordFailure(): void {
|
|
58
|
+
this.failures++
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** A pass committed an edit; failures start over. */
|
|
62
|
+
recordRelief(): void {
|
|
63
|
+
this.failures = 0
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* A summary or reducer pass is about to run at `iteration`. True when it
|
|
68
|
+
* would be the third refill in a row, in which case it must not run.
|
|
69
|
+
*/
|
|
70
|
+
wouldTrip(iteration: number): boolean {
|
|
71
|
+
const quick =
|
|
72
|
+
this.lastCompactedIteration !== undefined &&
|
|
73
|
+
iteration - this.lastCompactedIteration < COMPACTION_REFILL_ITERATIONS
|
|
74
|
+
this.pendingRefills = quick ? this.refills + 1 : 0
|
|
75
|
+
return this.pendingRefills >= COMPACTION_REFILL_LIMIT
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** The pass that `wouldTrip` allowed completed at `iteration`. */
|
|
79
|
+
recordCompaction(iteration: number): void {
|
|
80
|
+
this.refills = this.pendingRefills
|
|
81
|
+
this.lastCompactedIteration = iteration
|
|
82
|
+
this.recordRelief()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
stopTurn(message: string): void {
|
|
86
|
+
this.stopReason ??= message
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The reason the turn must stop, when a breaker has asked for it. */
|
|
90
|
+
get stop(): string | undefined {
|
|
91
|
+
return this.stopReason
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const breakers = new WeakMap<object, CompactionBreaker>()
|
|
96
|
+
|
|
97
|
+
/** The turn's breaker, or `undefined` when the host did not opt in. */
|
|
98
|
+
export function compactionBreaker(ctx: IterationContext): CompactionBreaker | undefined {
|
|
99
|
+
if (ctx.compactionConfig?.circuitBreaker !== true) return undefined
|
|
100
|
+
let breaker = breakers.get(ctx.recorder)
|
|
101
|
+
if (!breaker) {
|
|
102
|
+
breaker = new CompactionBreaker()
|
|
103
|
+
breakers.set(ctx.recorder, breaker)
|
|
104
|
+
}
|
|
105
|
+
return breaker
|
|
106
|
+
}
|