@namzu/sdk 57.0.0 → 58.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 +62 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +3 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/file-definitions.d.ts +35 -4
- package/dist/agents/file-definitions.d.ts.map +1 -1
- package/dist/agents/file-definitions.js +98 -8
- package/dist/agents/file-definitions.js.map +1 -1
- package/dist/authorization/protected-paths.d.ts +9 -0
- package/dist/authorization/protected-paths.d.ts.map +1 -1
- package/dist/authorization/protected-paths.js +29 -3
- package/dist/authorization/protected-paths.js.map +1 -1
- package/dist/compaction/index.d.ts +1 -0
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/manual.d.ts +7 -0
- package/dist/compaction/manual.d.ts.map +1 -1
- package/dist/compaction/manual.js +31 -12
- package/dist/compaction/manual.js.map +1 -1
- package/dist/compaction/model-summary.d.ts +101 -0
- package/dist/compaction/model-summary.d.ts.map +1 -0
- package/dist/compaction/model-summary.js +333 -0
- package/dist/compaction/model-summary.js.map +1 -0
- package/dist/compaction/verifier.d.ts +6 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +3 -3
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/runtime.d.ts +62 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +16 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/mcp/client.d.ts +8 -0
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +75 -2
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/constants/mcp/index.d.ts +2 -0
- package/dist/constants/mcp/index.d.ts.map +1 -1
- package/dist/constants/mcp/index.js +2 -0
- package/dist/constants/mcp/index.js.map +1 -1
- package/dist/constants/plugin/index.d.ts +2 -0
- package/dist/constants/plugin/index.d.ts.map +1 -1
- package/dist/constants/plugin/index.js +2 -0
- package/dist/constants/plugin/index.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +25 -4
- 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 +2 -1
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/persona/assembler.d.ts.map +1 -1
- package/dist/persona/assembler.js +2 -2
- package/dist/persona/assembler.js.map +1 -1
- package/dist/plugin/index.d.ts +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/lifecycle.d.ts +8 -1
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +9 -2
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/shell-hook-http.d.ts +39 -0
- package/dist/plugin/shell-hook-http.d.ts.map +1 -0
- package/dist/plugin/shell-hook-http.js +163 -0
- package/dist/plugin/shell-hook-http.js.map +1 -0
- package/dist/plugin/shell-hook.d.ts +93 -11
- package/dist/plugin/shell-hook.d.ts.map +1 -1
- package/dist/plugin/shell-hook.js +223 -39
- package/dist/plugin/shell-hook.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +2 -1
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/public-runtime.d.ts +3 -2
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +2 -2
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +1 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +2 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +1 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/runtime/query/executor.d.ts +4 -0
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +47 -9
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +8 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +29 -1
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +6 -2
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts +18 -8
- package/dist/runtime/query/iteration/phases/compaction-breaker.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction-breaker.js +33 -9
- package/dist/runtime/query/iteration/phases/compaction-breaker.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +52 -5
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +5 -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 +70 -3
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/path-escalation.d.ts +2 -0
- package/dist/runtime/query/path-escalation.d.ts.map +1 -1
- package/dist/runtime/query/path-escalation.js +7 -4
- package/dist/runtime/query/path-escalation.js.map +1 -1
- package/dist/runtime/query/plugin-hooks.js +2 -2
- package/dist/runtime/query/plugin-hooks.js.map +1 -1
- package/dist/runtime/query/prepare-turn.d.ts +2 -0
- package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
- package/dist/runtime/query/prepare-turn.js +25 -0
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts +3 -2
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +32 -13
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/scheduler/local.js +1 -1
- package/dist/scheduler/local.js.map +1 -1
- package/dist/session/retention/archive.js +1 -1
- package/dist/session/retention/archive.js.map +1 -1
- package/dist/session/workspace/driver.d.ts +6 -0
- package/dist/session/workspace/driver.d.ts.map +1 -1
- package/dist/session/workspace/git-worktree.d.ts.map +1 -1
- package/dist/session/workspace/git-worktree.js +28 -0
- package/dist/session/workspace/git-worktree.js.map +1 -1
- package/dist/skills/index.d.ts +1 -1
- package/dist/skills/index.d.ts.map +1 -1
- package/dist/skills/index.js +1 -1
- package/dist/skills/index.js.map +1 -1
- package/dist/skills/loader.d.ts +7 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +53 -0
- package/dist/skills/loader.js.map +1 -1
- package/dist/skills/registry.d.ts +1 -1
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +2 -1
- package/dist/skills/registry.js.map +1 -1
- package/dist/tools/builtins/bash-cwd.d.ts +52 -0
- package/dist/tools/builtins/bash-cwd.d.ts.map +1 -0
- package/dist/tools/builtins/bash-cwd.js +127 -0
- package/dist/tools/builtins/bash-cwd.js.map +1 -0
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +41 -6
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/bounded-file-read.d.ts +10 -0
- package/dist/tools/builtins/bounded-file-read.d.ts.map +1 -0
- package/dist/tools/builtins/bounded-file-read.js +67 -0
- package/dist/tools/builtins/bounded-file-read.js.map +1 -0
- package/dist/tools/builtins/grep.d.ts +6 -1
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +187 -24
- 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 +113 -28
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/read-media.d.ts +68 -0
- package/dist/tools/builtins/read-media.d.ts.map +1 -0
- package/dist/tools/builtins/read-media.js +397 -0
- package/dist/tools/builtins/read-media.js.map +1 -0
- package/dist/tools/builtins/search-tools.js +1 -1
- package/dist/tools/builtins/search-tools.js.map +1 -1
- package/dist/tools/builtins/skill.d.ts +4 -2
- package/dist/tools/builtins/skill.d.ts.map +1 -1
- package/dist/tools/builtins/skill.js +61 -2
- package/dist/tools/builtins/skill.js.map +1 -1
- package/dist/tools/builtins/sleep-guard.d.ts +15 -0
- package/dist/tools/builtins/sleep-guard.d.ts.map +1 -0
- package/dist/tools/builtins/sleep-guard.js +33 -0
- package/dist/tools/builtins/sleep-guard.js.map +1 -0
- package/dist/tools/builtins/view-image.d.ts +23 -1
- package/dist/tools/builtins/view-image.d.ts.map +1 -1
- package/dist/tools/builtins/view-image.js +66 -58
- package/dist/tools/builtins/view-image.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts.map +1 -1
- package/dist/tools/file-read-tracker.js +31 -0
- package/dist/tools/file-read-tracker.js.map +1 -1
- package/dist/toolsets/manager.js +3 -3
- package/dist/toolsets/manager.js.map +1 -1
- package/dist/types/agent/base.d.ts +8 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/query.d.ts +2 -0
- package/dist/types/agent/query.d.ts.map +1 -1
- package/dist/types/agent/scheduler.d.ts +8 -0
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +6 -2
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js.map +1 -1
- package/dist/types/connector/mcp.d.ts +46 -0
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/hitl/index.d.ts +1 -1
- package/dist/types/plugin/index.d.ts +74 -7
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +12 -1
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/session/sub-session.d.ts +6 -2
- package/dist/types/session/sub-session.d.ts.map +1 -1
- package/dist/types/skills/index.d.ts +36 -0
- package/dist/types/skills/index.d.ts.map +1 -1
- package/dist/types/skills/index.js +9 -0
- package/dist/types/skills/index.js.map +1 -1
- package/dist/types/tool/index.d.ts +37 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/workspace/ref.d.ts +2 -0
- package/dist/types/workspace/ref.d.ts.map +1 -1
- package/dist/utils/frontmatter.d.ts +12 -0
- package/dist/utils/frontmatter.d.ts.map +1 -1
- package/dist/utils/frontmatter.js +6 -0
- package/dist/utils/frontmatter.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +3 -0
- package/src/agents/file-definitions.ts +126 -6
- package/src/authorization/protected-paths.ts +30 -2
- package/src/compaction/index.ts +1 -0
- package/src/compaction/manual.ts +68 -30
- package/src/compaction/model-summary.ts +412 -0
- package/src/compaction/verifier.ts +3 -3
- package/src/config/runtime.ts +16 -0
- package/src/connector/mcp/client.ts +87 -1
- package/src/constants/mcp/index.ts +3 -0
- package/src/constants/plugin/index.ts +2 -0
- package/src/manager/agent/lifecycle.ts +24 -5
- package/src/manager/session/turn-recorder.ts +2 -1
- package/src/persona/assembler.ts +2 -2
- package/src/plugin/index.ts +5 -0
- package/src/plugin/lifecycle.ts +16 -2
- package/src/plugin/shell-hook-http.ts +181 -0
- package/src/plugin/shell-hook.ts +386 -89
- package/src/prompt/coding-agent-doctrine.ts +2 -1
- package/src/public-runtime.ts +6 -0
- package/src/public-tools.ts +2 -0
- package/src/public-types.ts +5 -0
- package/src/runtime/query/executor.ts +54 -10
- package/src/runtime/query/index.ts +44 -0
- package/src/runtime/query/iteration/index.ts +11 -3
- package/src/runtime/query/iteration/phases/compaction-breaker.ts +38 -9
- package/src/runtime/query/iteration/phases/compaction.ts +67 -5
- package/src/runtime/query/iteration/phases/context.ts +6 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +74 -3
- package/src/runtime/query/path-escalation.ts +11 -5
- package/src/runtime/query/plugin-hooks.ts +2 -2
- package/src/runtime/query/prepare-turn.ts +33 -0
- package/src/runtime/query/review-policy.ts +45 -16
- package/src/scheduler/local.ts +1 -1
- package/src/session/retention/archive.ts +1 -1
- package/src/session/workspace/driver.ts +6 -0
- package/src/session/workspace/git-worktree.ts +31 -0
- package/src/skills/index.ts +6 -1
- package/src/skills/loader.ts +54 -0
- package/src/skills/registry.ts +7 -6
- package/src/tools/builtins/bash-cwd.ts +156 -0
- package/src/tools/builtins/bash.ts +79 -34
- package/src/tools/builtins/bounded-file-read.ts +80 -0
- package/src/tools/builtins/grep.ts +198 -28
- package/src/tools/builtins/read-file.ts +151 -30
- package/src/tools/builtins/read-media.ts +483 -0
- package/src/tools/builtins/search-tools.ts +1 -1
- package/src/tools/builtins/skill.ts +91 -2
- package/src/tools/builtins/sleep-guard.ts +31 -0
- package/src/tools/builtins/view-image.ts +74 -62
- package/src/tools/file-read-tracker.ts +30 -0
- package/src/toolsets/manager.ts +3 -3
- package/src/types/agent/base.ts +9 -0
- package/src/types/agent/query.ts +2 -0
- package/src/types/agent/scheduler.ts +9 -0
- package/src/types/agent/task.ts +6 -2
- package/src/types/connector/mcp.ts +48 -0
- package/src/types/hitl/index.ts +1 -1
- package/src/types/plugin/index.ts +102 -16
- package/src/types/session/sub-session.ts +6 -2
- package/src/types/skills/index.ts +48 -0
- package/src/types/tool/index.ts +38 -0
- package/src/types/workspace/ref.ts +2 -0
- package/src/utils/frontmatter.ts +21 -0
|
@@ -79,7 +79,8 @@ export const CODING_AGENT_WORKING_DOCTRINE = `## How you work
|
|
|
79
79
|
*/
|
|
80
80
|
export const CODING_AGENT_DELEGATION_DOCTRINE = `### Planning and delegating
|
|
81
81
|
- For work of three or more steps — several files, several distinct stages, anything you would write a checklist for — open a task list with \`task_create\` and keep it current with \`task_update\`, marking each step done when it is done rather than at the end. Skip it for a single inspect-edit-test cycle or a two-step job: the list is for the user to follow, and a list of one or two items tells them nothing.
|
|
82
|
-
- When the \`Agent\` tool is available, delegate independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts. Brief each sub-agent with four things: the objective; what is out of bounds; which tools and sources to use; and the shape of the reply you want, such as a short summary with a file path for anything long.
|
|
82
|
+
- When the \`Agent\` tool is available, delegate independent work through it and give each delegation a short, specific description; the user watches those descriptions, not the prompts. Brief each sub-agent with four things: the objective; what is out of bounds; which tools and sources to use; and the shape of the reply you want, such as a short summary with a file path for anything long. A fresh sub-agent starts with none of your context and returns what you ask for.
|
|
83
|
+
- When the \`Agent\` tool lists a \`fork\` type, use \`subagent_type: "fork"\` when a side task would fill your context with output you will not need again, such as an open-ended survey. A fork starts from this conversation under your own instructions, so its prompt is only the directive: what to do and where its scope ends, without the background. Never write or guess what a fork will find before its report arrives. A fork cannot start forks of its own.
|
|
83
84
|
- When delegated agents finish, tell the user what each one found in your next message. Their results reach you and not the user, so a result you do not pass on stays unseen.
|
|
84
85
|
- Delegate lookups — where something is defined, which files reference it, how a module works — with \`subagent_type: "explore"\`: it has reading and searching tools only and never interrupts the user for permission. Reserve the default sub-agent for work that changes files or runs commands.`
|
|
85
86
|
|
package/src/public-runtime.ts
CHANGED
|
@@ -485,6 +485,7 @@ export {
|
|
|
485
485
|
loadSkill,
|
|
486
486
|
resolveSkillChain,
|
|
487
487
|
SKILL_FRONTMATTER_KEYS,
|
|
488
|
+
SKILL_FRONTMATTER_TOLERATED_KEYS,
|
|
488
489
|
SkillCollisionError,
|
|
489
490
|
SkillRegistry,
|
|
490
491
|
} from './skills/index.js'
|
|
@@ -1323,6 +1324,10 @@ export { isWorkingMemoryMessage } from './runtime/query/iteration/phases/working
|
|
|
1323
1324
|
// `any` — and the first host to try (`@namzu/cli`'s `/compact`) did exactly
|
|
1324
1325
|
// that before this line existed.
|
|
1325
1326
|
export type { CompactNowInput, CompactionResult } from './compaction/manual.js'
|
|
1327
|
+
export type {
|
|
1328
|
+
CompactionSummaryRoute,
|
|
1329
|
+
CompactionSummaryRouter,
|
|
1330
|
+
} from './compaction/model-summary.js'
|
|
1326
1331
|
|
|
1327
1332
|
export {
|
|
1328
1333
|
buildVerifiedSummary,
|
|
@@ -1517,6 +1522,7 @@ export {
|
|
|
1517
1522
|
SKILL_INVOCATION_DEFAULT,
|
|
1518
1523
|
isInvocableBy,
|
|
1519
1524
|
skillInvocation,
|
|
1525
|
+
skillModelDescription,
|
|
1520
1526
|
} from './types/skills/index.js'
|
|
1521
1527
|
|
|
1522
1528
|
// Reaching the web. Fetching ships guarded because its rules are about the
|
package/src/public-tools.ts
CHANGED
|
@@ -79,6 +79,8 @@ export {
|
|
|
79
79
|
backgroundTasksDisabled,
|
|
80
80
|
execHostShell,
|
|
81
81
|
} from './tools/builtins/bash.js'
|
|
82
|
+
// Authorization and execution must agree after a session carries its shell cwd.
|
|
83
|
+
export { effectiveBashWorkingDirectory } from './tools/builtins/bash-cwd.js'
|
|
82
84
|
export {
|
|
83
85
|
CODE_ANALYSIS_TOOL_NAME,
|
|
84
86
|
CodeAnalysisTool,
|
package/src/public-types.ts
CHANGED
|
@@ -231,8 +231,13 @@ export type {
|
|
|
231
231
|
SkippedAgentFile,
|
|
232
232
|
} from './agents/file-definitions.js'
|
|
233
233
|
export type {
|
|
234
|
+
HttpHookEntry,
|
|
235
|
+
PromptHookEntry,
|
|
234
236
|
ShellHookEntry,
|
|
235
237
|
ShellHookEvent,
|
|
238
|
+
ShellHookHandler,
|
|
239
|
+
ShellHookJudge,
|
|
240
|
+
ShellHookOnFailure,
|
|
236
241
|
ShellHookInput,
|
|
237
242
|
ShellHookOptions,
|
|
238
243
|
ShellHookOutcome,
|
|
@@ -14,6 +14,11 @@ import { ProbeVetoError } from '../../probe/errors.js'
|
|
|
14
14
|
import { probe as defaultProbeRegistry } from '../../probe/registry.js'
|
|
15
15
|
import type { ProbeEnforcement } from '../../probe/registry.js'
|
|
16
16
|
import type { ActivityStore } from '../../store/activity/memory.js'
|
|
17
|
+
import {
|
|
18
|
+
effectiveBashWorkingDirectory,
|
|
19
|
+
pinBashWorkingDirectory,
|
|
20
|
+
} from '../../tools/builtins/bash-cwd.js'
|
|
21
|
+
import { BashTool } from '../../tools/builtins/bash.js'
|
|
17
22
|
import { fingerprintContent } from '../../tools/builtins/content-fingerprint.js'
|
|
18
23
|
import { sandboxShellSpawn, withoutBashStartup } from '../../tools/command-shell.js'
|
|
19
24
|
import { isAlwaysDestructive } from '../../tools/defineTool.js'
|
|
@@ -99,6 +104,9 @@ import {
|
|
|
99
104
|
export type { EmitEvent } from './events.js'
|
|
100
105
|
import type { EmitEvent, SessionEventDraft } from './events.js'
|
|
101
106
|
|
|
107
|
+
/** Input and identity needed by the shared direct/nested safety checks. */
|
|
108
|
+
type PreparedSafetyCall = Pick<PreparedDirectCall, 'kind' | 'toolName' | 'input'>
|
|
109
|
+
|
|
102
110
|
export type PreparedDirectCall =
|
|
103
111
|
| {
|
|
104
112
|
readonly kind: 'ready'
|
|
@@ -703,10 +711,12 @@ export class ToolExecutor {
|
|
|
703
711
|
private log: Logger
|
|
704
712
|
private workingStateManager?: WorkingStateManager
|
|
705
713
|
private historyView?: () => readonly Message[]
|
|
714
|
+
private systemPromptView?: () => string | undefined
|
|
706
715
|
private probes: ProbeEnforcement
|
|
707
716
|
private parentSpan?: Span
|
|
708
717
|
private readonly toolCallBudget?: ToolCallBudget
|
|
709
718
|
private readonly preparedBatches = new WeakSet<PreparedToolBatch>()
|
|
719
|
+
private readonly preparedBashDirectories = new WeakMap<PreparedSafetyCall, string>()
|
|
710
720
|
/** Ephemeral proof of actual execution; restored messages do not acquire it. */
|
|
711
721
|
private readonly recordedObservations = new Map<
|
|
712
722
|
string,
|
|
@@ -761,6 +771,11 @@ export class ToolExecutor {
|
|
|
761
771
|
this.historyView = view
|
|
762
772
|
}
|
|
763
773
|
|
|
774
|
+
/** The system prompt assembled for the current iteration, for `ToolContext.conversation`. */
|
|
775
|
+
setSystemPromptView(view: () => string | undefined): void {
|
|
776
|
+
this.systemPromptView = view
|
|
777
|
+
}
|
|
778
|
+
|
|
764
779
|
private receiptVisible = (callId: string, renderedFingerprint: string): boolean => {
|
|
765
780
|
const history = this.historyView?.()
|
|
766
781
|
if (!history) return false
|
|
@@ -1174,7 +1189,7 @@ export class ToolExecutor {
|
|
|
1174
1189
|
* safety checks did not run is the one a person must look at.
|
|
1175
1190
|
*/
|
|
1176
1191
|
private async escalationOrUnfinished(
|
|
1177
|
-
call:
|
|
1192
|
+
call: PreparedSafetyCall,
|
|
1178
1193
|
): Promise<ToolCallEscalation | undefined> {
|
|
1179
1194
|
try {
|
|
1180
1195
|
return await this.escalationOf(call)
|
|
@@ -1206,12 +1221,19 @@ export class ToolExecutor {
|
|
|
1206
1221
|
* roots is not mounted there to be approved. The escape is looked at only
|
|
1207
1222
|
* WITH one, since without one there is nothing to escape.
|
|
1208
1223
|
*/
|
|
1209
|
-
private async escalationOf(call:
|
|
1224
|
+
private async escalationOf(call: PreparedSafetyCall): Promise<ToolCallEscalation | undefined> {
|
|
1210
1225
|
if (call.kind === 'synthetic') return undefined
|
|
1211
1226
|
const tool = this.config.tools.get(call.toolName)
|
|
1212
1227
|
if (!tool || call.input === null || typeof call.input !== 'object') return undefined
|
|
1213
1228
|
const input = call.input as Record<string, unknown>
|
|
1214
1229
|
const sandboxed = this.sandboxed
|
|
1230
|
+
const shellCwd =
|
|
1231
|
+
tool.execute === BashTool.execute
|
|
1232
|
+
? effectiveBashWorkingDirectory(this.config.sessionId, this.config.workingDirectory, {
|
|
1233
|
+
sandboxed,
|
|
1234
|
+
})
|
|
1235
|
+
: undefined
|
|
1236
|
+
if (shellCwd !== undefined) this.preparedBashDirectories.set(call, shellCwd)
|
|
1215
1237
|
let outsidePaths: string[] | undefined
|
|
1216
1238
|
if (
|
|
1217
1239
|
this.config.outsideRootAccess === 'review' &&
|
|
@@ -1234,7 +1256,7 @@ export class ToolExecutor {
|
|
|
1234
1256
|
tool.sandboxEscapeArgument !== undefined &&
|
|
1235
1257
|
input[tool.sandboxEscapeArgument] === true
|
|
1236
1258
|
const unknownProgram = this.unknownProgramOf(tool, input, sandboxed)
|
|
1237
|
-
const reaches = await this.pathEscalationsOf(tool, input, sandboxed)
|
|
1259
|
+
const reaches = await this.pathEscalationsOf(tool, input, sandboxed, shellCwd)
|
|
1238
1260
|
if (reaches.outsidePaths.length > 0) {
|
|
1239
1261
|
outsidePaths = [...new Set([...(outsidePaths ?? []), ...reaches.outsidePaths])]
|
|
1240
1262
|
}
|
|
@@ -1275,6 +1297,7 @@ export class ToolExecutor {
|
|
|
1275
1297
|
>,
|
|
1276
1298
|
input: Record<string, unknown>,
|
|
1277
1299
|
sandboxed: boolean,
|
|
1300
|
+
shellCwd?: string,
|
|
1278
1301
|
): Promise<{
|
|
1279
1302
|
outsidePaths: readonly string[]
|
|
1280
1303
|
protectedPaths: readonly string[]
|
|
@@ -1297,6 +1320,7 @@ export class ToolExecutor {
|
|
|
1297
1320
|
const dialect = tool.commandDialect?.({ sandboxed }) ?? 'sh'
|
|
1298
1321
|
const found = await shellEscalations(line, dialect, {
|
|
1299
1322
|
roots,
|
|
1323
|
+
...(shellCwd !== undefined ? { cwd: shellCwd } : {}),
|
|
1300
1324
|
reviewOutside: this.config.outsideRootAccess === 'review' && !sandboxed,
|
|
1301
1325
|
})
|
|
1302
1326
|
protectedPaths.push(...found.protectedPaths)
|
|
@@ -1592,6 +1616,10 @@ export class ToolExecutor {
|
|
|
1592
1616
|
...(this.config.toolPause ? { requestPause: this.config.toolPause(toolCall.id) } : {}),
|
|
1593
1617
|
report: (message: string, fraction?: number) => progress.report(message, fraction),
|
|
1594
1618
|
}
|
|
1619
|
+
if (preparedCall) {
|
|
1620
|
+
const cwd = this.preparedBashDirectories.get(preparedCall)
|
|
1621
|
+
if (cwd !== undefined) pinBashWorkingDirectory(ctx, cwd)
|
|
1622
|
+
}
|
|
1595
1623
|
const run = async () => {
|
|
1596
1624
|
try {
|
|
1597
1625
|
results[i] = await this.executeSingle(
|
|
@@ -1843,7 +1871,10 @@ export class ToolExecutor {
|
|
|
1843
1871
|
signal.throwIfAborted()
|
|
1844
1872
|
// Preparation detaches input synchronously before its first await.
|
|
1845
1873
|
// Admission must not add an earlier yield where the caller can mutate it.
|
|
1846
|
-
const preparedCall =
|
|
1874
|
+
const preparedCall = {
|
|
1875
|
+
...(await this.prepareNestedCall(name, input, signal)),
|
|
1876
|
+
toolName: name,
|
|
1877
|
+
}
|
|
1847
1878
|
signal.throwIfAborted()
|
|
1848
1879
|
if (this.toolCallBudget) {
|
|
1849
1880
|
const refusal = await this.toolCallBudget.admit(1, 'nested', signal)
|
|
@@ -1926,12 +1957,15 @@ export class ToolExecutor {
|
|
|
1926
1957
|
} catch {
|
|
1927
1958
|
needsApproval = true
|
|
1928
1959
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
:
|
|
1933
|
-
|
|
1934
|
-
|
|
1960
|
+
const nestedEscalation = await this.escalationOrUnfinished(preparedCall)
|
|
1961
|
+
if (nestedEscalation || needsApproval || (gateResult && gateResult.decision !== 'allow')) {
|
|
1962
|
+
const reason = nestedEscalation
|
|
1963
|
+
? 'Blocked: this nested call reaches a protected path, removal floor, outside-root boundary or another safety escalation. Call it directly so it can be reviewed.'
|
|
1964
|
+
: needsApproval
|
|
1965
|
+
? `Blocked: "${name}" requires the person's approval for every call, and an approval cannot be requested from inside another tool. Call it directly.`
|
|
1966
|
+
: gateResult?.decision === 'deny'
|
|
1967
|
+
? `Blocked by the authorization gate: ${gateResult.reason}`
|
|
1968
|
+
: `Blocked by the authorization gate: this nested call requires an explicit allow rule because an operator review cannot be opened from inside another tool. ${gateResult?.reason}`
|
|
1935
1969
|
const output = deniedToolOutput(name, reason)
|
|
1936
1970
|
// Same fail-closed durability rule as a direct gate denial: if the
|
|
1937
1971
|
// configured session log cannot record the refusal, do not quietly carry
|
|
@@ -2004,6 +2038,8 @@ export class ToolExecutor {
|
|
|
2004
2038
|
report: (message: string, fraction?: number) => progress.report(message, fraction),
|
|
2005
2039
|
}
|
|
2006
2040
|
|
|
2041
|
+
const nestedCwd = this.preparedBashDirectories.get(preparedCall)
|
|
2042
|
+
if (nestedCwd !== undefined) pinBashWorkingDirectory(childContext, nestedCwd)
|
|
2007
2043
|
await this.emitEvent({
|
|
2008
2044
|
type: 'tool_executing',
|
|
2009
2045
|
turnId: this.config.turnId,
|
|
@@ -2213,6 +2249,14 @@ export class ToolExecutor {
|
|
|
2213
2249
|
sandbox: this.config.sandbox,
|
|
2214
2250
|
fileReadTracker: this.fileReadTracker,
|
|
2215
2251
|
receiptVisible: this.receiptVisible,
|
|
2252
|
+
...(this.historyView
|
|
2253
|
+
? {
|
|
2254
|
+
conversation: {
|
|
2255
|
+
messages: () => this.historyView?.() ?? [],
|
|
2256
|
+
systemPrompt: () => this.systemPromptView?.(),
|
|
2257
|
+
},
|
|
2258
|
+
}
|
|
2259
|
+
: {}),
|
|
2216
2260
|
...(this.config.codeAnalysis ? { codeAnalysis: this.config.codeAnalysis } : {}),
|
|
2217
2261
|
// Bound to this turn, once. Binding here rather than passing the
|
|
2218
2262
|
// owner from the tool is what makes the scoping structural: there
|
|
@@ -10,6 +10,7 @@ import { SkillGrantSet } from '../../authorization/skill-grant.js'
|
|
|
10
10
|
import { repairToolMessageHistory, toolHistoryRepairChanged } from '../../compaction/dangling.js'
|
|
11
11
|
import { extractFromUserMessage } from '../../compaction/extractor.js'
|
|
12
12
|
import { WorkingStateManager } from '../../compaction/manager.js'
|
|
13
|
+
import type { CompactionSummaryRouter } from '../../compaction/model-summary.js'
|
|
13
14
|
import type { ContextReducer } from '../../compaction/reducer.js'
|
|
14
15
|
import { serializeState as serializeWorkingState } from '../../compaction/serializer.js'
|
|
15
16
|
import { restoreWorkingState, snapshotWorkingState } from '../../compaction/wire.js'
|
|
@@ -552,6 +553,8 @@ export interface QueryParams {
|
|
|
552
553
|
allowedTools?: string[]
|
|
553
554
|
agentId: string
|
|
554
555
|
agentName: string
|
|
556
|
+
/** Application-owned agent kind for lifecycle hooks; defaults to agentName. */
|
|
557
|
+
agentType?: string
|
|
555
558
|
workingDirectory?: string
|
|
556
559
|
/**
|
|
557
560
|
* Directories besides the working directory the file tools may reach,
|
|
@@ -821,6 +824,12 @@ export interface QueryParams {
|
|
|
821
824
|
*/
|
|
822
825
|
postCompactionContext?: () => string | undefined | Promise<string | undefined>
|
|
823
826
|
|
|
827
|
+
/**
|
|
828
|
+
* Names the model that writes a compaction summary. Asked once per pass;
|
|
829
|
+
* resolving nothing, or a call that fails, keeps the extracted summary.
|
|
830
|
+
*/
|
|
831
|
+
compactionSummaryRoute?: CompactionSummaryRouter
|
|
832
|
+
|
|
824
833
|
/**
|
|
825
834
|
* Where this conversation's durable state lives.
|
|
826
835
|
*
|
|
@@ -1064,6 +1073,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1064
1073
|
approvalPolicy,
|
|
1065
1074
|
eventTranslator,
|
|
1066
1075
|
executeUserInterruptHooks,
|
|
1076
|
+
executeTurnFailureHooks,
|
|
1067
1077
|
pendingHistoryRepairs,
|
|
1068
1078
|
initialMessages,
|
|
1069
1079
|
historyIds,
|
|
@@ -1841,6 +1851,9 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1841
1851
|
...(params.postCompactionContext
|
|
1842
1852
|
? { postCompactionContext: params.postCompactionContext }
|
|
1843
1853
|
: {}),
|
|
1854
|
+
...(params.compactionSummaryRoute
|
|
1855
|
+
? { compactionSummaryRoute: params.compactionSummaryRoute }
|
|
1856
|
+
: {}),
|
|
1844
1857
|
...(providerContextWindow !== undefined ? { providerContextWindow } : {}),
|
|
1845
1858
|
resolveModelContextWindow: async (model) => {
|
|
1846
1859
|
if (!modelContextWindows.has(model)) {
|
|
@@ -1867,6 +1880,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1867
1880
|
pluginManager: params.pluginManager,
|
|
1868
1881
|
...(params.parentSessionId !== undefined ? { parentSessionId: params.parentSessionId } : {}),
|
|
1869
1882
|
...(params.parentTurnId !== undefined ? { parentTurnId: params.parentTurnId } : {}),
|
|
1883
|
+
agentType: params.agentType ?? params.agentName,
|
|
1870
1884
|
})
|
|
1871
1885
|
|
|
1872
1886
|
const tracer = getTracer()
|
|
@@ -2031,6 +2045,33 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2031
2045
|
'namzu.runtime.resume_from_checkpoint': params.resumeFromCheckpoint ?? null,
|
|
2032
2046
|
})
|
|
2033
2047
|
|
|
2048
|
+
// A child session is starting: its hooks may add context for the
|
|
2049
|
+
// child's turn, before the child's first prompt is seen.
|
|
2050
|
+
if (params.pluginManager && params.parentSessionId !== undefined) {
|
|
2051
|
+
const startResults = await params.pluginManager.executeHooks(
|
|
2052
|
+
'subagent_start',
|
|
2053
|
+
{
|
|
2054
|
+
turnId: ctx.turnId,
|
|
2055
|
+
sessionId: params.sessionId,
|
|
2056
|
+
parentSessionId: params.parentSessionId,
|
|
2057
|
+
...(params.parentTurnId !== undefined ? { parentTurnId: params.parentTurnId } : {}),
|
|
2058
|
+
agentType: params.agentType ?? params.agentName,
|
|
2059
|
+
signal: ctx.abortController.signal,
|
|
2060
|
+
},
|
|
2061
|
+
eventTranslator.emitEvent,
|
|
2062
|
+
)
|
|
2063
|
+
const startAnnotations = applyLifecycleHookResults('subagent_start', startResults)
|
|
2064
|
+
if (startAnnotations.length > 0) {
|
|
2065
|
+
promptContributions.replace({
|
|
2066
|
+
id: 'hooks:subagent_start',
|
|
2067
|
+
placement: 'context',
|
|
2068
|
+
render: () =>
|
|
2069
|
+
`Context from the operator's hooks:\n\n${startAnnotations.join('\n\n')}`,
|
|
2070
|
+
})
|
|
2071
|
+
}
|
|
2072
|
+
yield* eventTranslator.drainPending()
|
|
2073
|
+
}
|
|
2074
|
+
|
|
2034
2075
|
// The operator's prompt, before the model sees it. A hook may
|
|
2035
2076
|
// refuse it — the turn ends here, before a prompt is built — or add
|
|
2036
2077
|
// to what the model is told; the addition rides the prompt as a
|
|
@@ -2351,6 +2392,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2351
2392
|
? `${segments.static}\n\n---\n\n${segments.dynamic}`
|
|
2352
2393
|
: segments.static
|
|
2353
2394
|
|
|
2395
|
+
toolExecutor.setSystemPromptView(() => assembledPrompt)
|
|
2354
2396
|
ctx.recorder.markRunning()
|
|
2355
2397
|
// A resumed turn already began; `turn_resuming` above continued it.
|
|
2356
2398
|
if (!params.resumeFromCheckpoint) {
|
|
@@ -2788,6 +2830,8 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2788
2830
|
ctx.recorder.setSteps(iterationOrchestrator.getSteps())
|
|
2789
2831
|
await executeUserInterruptHooks(err)
|
|
2790
2832
|
yield* eventTranslator.drainPending()
|
|
2833
|
+
await executeTurnFailureHooks(err)
|
|
2834
|
+
yield* eventTranslator.drainPending()
|
|
2791
2835
|
yield* resultAssembler.handleError(err, rootSpan)
|
|
2792
2836
|
} finally {
|
|
2793
2837
|
// A pending acquisition is withdrawn and awaited first, so a handle
|
|
@@ -79,7 +79,7 @@ import {
|
|
|
79
79
|
} from './outstanding-work.js'
|
|
80
80
|
import { runAdvisoryPhase } from './phases/advisory.js'
|
|
81
81
|
import { runIterationCheckpoint } from './phases/checkpoint.js'
|
|
82
|
-
import { compactionBreaker } from './phases/compaction-breaker.js'
|
|
82
|
+
import { compactionBreaker, estimateRefillTokens } from './phases/compaction-breaker.js'
|
|
83
83
|
import {
|
|
84
84
|
AUTOMATIC_COMPACTION_FAILED,
|
|
85
85
|
OVERFLOW_RELIEF_LIMIT,
|
|
@@ -319,7 +319,7 @@ export class IterationOrchestrator {
|
|
|
319
319
|
const feedback: string[] = []
|
|
320
320
|
const ask = async (
|
|
321
321
|
event: 'turn_end' | 'subagent_stop',
|
|
322
|
-
extra: { parentSessionId?: SessionId; parentTurnId?: TurnId },
|
|
322
|
+
extra: { parentSessionId?: SessionId; parentTurnId?: TurnId; agentType?: string },
|
|
323
323
|
): Promise<void> => {
|
|
324
324
|
const results = await manager.executeHooks(
|
|
325
325
|
event,
|
|
@@ -346,6 +346,7 @@ export class IterationOrchestrator {
|
|
|
346
346
|
await ask('subagent_stop', {
|
|
347
347
|
parentSessionId: this.ctx.parentSessionId,
|
|
348
348
|
...(this.ctx.parentTurnId !== undefined ? { parentTurnId: this.ctx.parentTurnId } : {}),
|
|
349
|
+
...(this.ctx.agentType !== undefined ? { agentType: this.ctx.agentType } : {}),
|
|
349
350
|
})
|
|
350
351
|
yield* this.ctx.drainPending()
|
|
351
352
|
}
|
|
@@ -2176,6 +2177,7 @@ export class IterationOrchestrator {
|
|
|
2176
2177
|
}
|
|
2177
2178
|
// A rung that sheds too little to change the verdict is not worth a
|
|
2178
2179
|
// provider call; climb to the next one at once.
|
|
2180
|
+
const tokensBeforeRelief = estimateRefillTokens(recorder.messages)
|
|
2179
2181
|
let shed = false
|
|
2180
2182
|
while (!shed && overflowReliefs < OVERFLOW_RELIEF_LIMIT) {
|
|
2181
2183
|
overflowReliefs++
|
|
@@ -2185,7 +2187,13 @@ export class IterationOrchestrator {
|
|
|
2185
2187
|
// Clearing results is a compaction too: only summaries and reducers
|
|
2186
2188
|
// recorded themselves, so a clear-only relief was invisible to the
|
|
2187
2189
|
// breaker.
|
|
2188
|
-
|
|
2190
|
+
// Record the whole ladder once, including any insufficient first
|
|
2191
|
+
// rung, against the same estimator the refill check uses.
|
|
2192
|
+
compactionBreaker(this.ctx)?.recordCompaction(
|
|
2193
|
+
iterationNum,
|
|
2194
|
+
tokensBeforeRelief,
|
|
2195
|
+
estimateRefillTokens(recorder.messages),
|
|
2196
|
+
)
|
|
2189
2197
|
this.ctx.log.info('Retrying the turn after relieving a context overflow', {
|
|
2190
2198
|
[NAMZU.TURN_ID]: recorder.turnId,
|
|
2191
2199
|
[NAMZU.ITERATION]: iterationNum,
|
|
@@ -7,10 +7,10 @@
|
|
|
7
7
|
* and the turn carries on, silently as far as the person is concerned (a log
|
|
8
8
|
* line and a `breaker_open` event, no sentence); a pass the provider forces
|
|
9
9
|
* by rejecting the prompt is not gated, so the reactive ladder still runs.
|
|
10
|
-
* - Three compactions in a row, each followed by the
|
|
11
|
-
* within three iterations: something in the loop (a huge file
|
|
12
|
-
* that prints without limit) refills the window faster than
|
|
13
|
-
* empty it. The turn stops, because carrying on
|
|
10
|
+
* - Three compactions in a row, each followed by the history regaining all
|
|
11
|
+
* space shed within three iterations: something in the loop (a huge file
|
|
12
|
+
* read, a tool that prints without limit) refills the window faster than a
|
|
13
|
+
* summary can empty it. The turn stops, because carrying on buys the same wall.
|
|
14
14
|
*
|
|
15
15
|
* Off unless `compactionConfig.circuitBreaker` is true: stopping compaction,
|
|
16
16
|
* and above all stopping a turn, is not something a host had before.
|
|
@@ -19,11 +19,31 @@
|
|
|
19
19
|
* share a count.
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
|
+
import { estimateMessageTokens } from '../../../../compaction/token-estimate.js'
|
|
23
|
+
import { type Message, isTurnContextEntry } from '../../../../types/message/index.js'
|
|
24
|
+
import { isReminderMessage } from '../../reminders.js'
|
|
22
25
|
import type { IterationContext } from './context.js'
|
|
23
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Conversation growth for the refill guard. Compaction sheds host reminders
|
|
29
|
+
* and recorded context so the host can announce them again; their return is
|
|
30
|
+
* not evidence of a tool refilling the window. Budgeting still includes them.
|
|
31
|
+
* Use provenance, not text that an operator or tool could happen to quote.
|
|
32
|
+
*/
|
|
33
|
+
export function estimateRefillTokens(messages: readonly Message[]): number {
|
|
34
|
+
return messages.reduce(
|
|
35
|
+
(total, message) =>
|
|
36
|
+
total +
|
|
37
|
+
(isReminderMessage(message) || isTurnContextEntry(message)
|
|
38
|
+
? 0
|
|
39
|
+
: estimateMessageTokens(message)),
|
|
40
|
+
0,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
24
44
|
/** Consecutive failed passes after which automatic compaction stops. */
|
|
25
45
|
export const COMPACTION_FAILURE_LIMIT = 3
|
|
26
|
-
/** A
|
|
46
|
+
/** A full refill sooner than this many iterations counts toward the limit. */
|
|
27
47
|
export const COMPACTION_REFILL_ITERATIONS = 3
|
|
28
48
|
/** Consecutive refills that stop the turn. */
|
|
29
49
|
export const COMPACTION_REFILL_LIMIT = 3
|
|
@@ -41,6 +61,8 @@ export class CompactionBreaker {
|
|
|
41
61
|
private refills = 0
|
|
42
62
|
private pendingRefills = 0
|
|
43
63
|
private lastCompactedIteration: number | undefined
|
|
64
|
+
private lastTokensBefore = 0
|
|
65
|
+
private lastTokensAfter = 0
|
|
44
66
|
private failureNoticeSent = false
|
|
45
67
|
private stopReason: string | undefined
|
|
46
68
|
|
|
@@ -69,18 +91,25 @@ export class CompactionBreaker {
|
|
|
69
91
|
* A summary or reducer pass is about to run at `iteration`. True when it
|
|
70
92
|
* would be the third refill in a row, in which case it must not run.
|
|
71
93
|
*/
|
|
72
|
-
wouldTrip(iteration: number): boolean {
|
|
94
|
+
wouldTrip(iteration: number, tokens: number): boolean {
|
|
95
|
+
// Iteration distance alone confuses continued pressure with a refill:
|
|
96
|
+
// a small reduction may leave the prompt above its trigger. Compare the
|
|
97
|
+
// same history estimator on both sides, never provider usage against an
|
|
98
|
+
// estimate. Count only when new context has replaced ALL the space shed.
|
|
99
|
+
const refilled = this.lastTokensAfter < this.lastTokensBefore && tokens >= this.lastTokensBefore
|
|
73
100
|
const quick =
|
|
74
101
|
this.lastCompactedIteration !== undefined &&
|
|
75
102
|
iteration - this.lastCompactedIteration < COMPACTION_REFILL_ITERATIONS
|
|
76
|
-
this.pendingRefills = quick ? this.refills + 1 : 0
|
|
103
|
+
this.pendingRefills = quick && refilled ? this.refills + 1 : 0
|
|
77
104
|
return this.pendingRefills >= COMPACTION_REFILL_LIMIT
|
|
78
105
|
}
|
|
79
106
|
|
|
80
|
-
/** The pass
|
|
81
|
-
recordCompaction(iteration: number): void {
|
|
107
|
+
/** The allowed pass completed; both sizes must use the same history estimator. */
|
|
108
|
+
recordCompaction(iteration: number, tokensBefore: number, tokensAfter: number): void {
|
|
82
109
|
this.refills = this.pendingRefills
|
|
83
110
|
this.lastCompactedIteration = iteration
|
|
111
|
+
this.lastTokensBefore = tokensBefore
|
|
112
|
+
this.lastTokensAfter = tokensAfter
|
|
84
113
|
this.recordRelief()
|
|
85
114
|
}
|
|
86
115
|
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
resolveTriggerThreshold,
|
|
4
4
|
} from '../../../../compaction/context-window.js'
|
|
5
5
|
import { findDanglingMessages } from '../../../../compaction/dangling.js'
|
|
6
|
+
import { replaceableSummaries, summarizeWithModel } from '../../../../compaction/model-summary.js'
|
|
6
7
|
import {
|
|
7
8
|
type CompactionPlan,
|
|
8
9
|
DEFAULT_SOFT_TARGET,
|
|
@@ -28,7 +29,11 @@ import { invariants } from '../../../../invariants/index.js'
|
|
|
28
29
|
import { resolveTaskModel } from '../../../../model-router/task-router.js'
|
|
29
30
|
import type { Message } from '../../../../types/message/index.js'
|
|
30
31
|
import { applyLifecycleHookResults } from '../../plugin-hooks.js'
|
|
31
|
-
import {
|
|
32
|
+
import {
|
|
33
|
+
COMPACTION_REFILL_MESSAGE,
|
|
34
|
+
compactionBreaker,
|
|
35
|
+
estimateRefillTokens,
|
|
36
|
+
} from './compaction-breaker.js'
|
|
32
37
|
import type { IterationContext } from './context.js'
|
|
33
38
|
import { isWorkingMemoryMessage } from './working-memory.js'
|
|
34
39
|
|
|
@@ -442,7 +447,10 @@ export async function stopOnRefill(ctx: IterationContext): Promise<boolean> {
|
|
|
442
447
|
if (!breaker) return false
|
|
443
448
|
// Already announced: the turn is on its way out; do not say it twice.
|
|
444
449
|
if (breaker.stop !== undefined) return true
|
|
445
|
-
if (
|
|
450
|
+
if (
|
|
451
|
+
!breaker.wouldTrip(ctx.recorder.currentIteration, estimateRefillTokens(ctx.recorder.messages))
|
|
452
|
+
)
|
|
453
|
+
return false
|
|
446
454
|
ctx.log.warn('Compaction keeps refilling — stopping the turn', {
|
|
447
455
|
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
448
456
|
[NAMZU.ITERATION]: ctx.recorder.currentIteration,
|
|
@@ -508,6 +516,7 @@ async function applyReducer(
|
|
|
508
516
|
const messages = ctx.recorder.messages
|
|
509
517
|
const before = messages.length
|
|
510
518
|
const beforeChars = totalChars(messages)
|
|
519
|
+
const beforeTokens = estimateRefillTokens(messages)
|
|
511
520
|
|
|
512
521
|
let next: readonly Message[] | undefined
|
|
513
522
|
try {
|
|
@@ -594,7 +603,11 @@ async function applyReducer(
|
|
|
594
603
|
reduction.contextWindowTokens,
|
|
595
604
|
measurement.windowSource,
|
|
596
605
|
)
|
|
597
|
-
compactionBreaker(ctx)?.recordCompaction(
|
|
606
|
+
compactionBreaker(ctx)?.recordCompaction(
|
|
607
|
+
ctx.recorder.currentIteration,
|
|
608
|
+
beforeTokens,
|
|
609
|
+
estimateRefillTokens(messages),
|
|
610
|
+
)
|
|
598
611
|
}
|
|
599
612
|
|
|
600
613
|
/**
|
|
@@ -960,7 +973,51 @@ async function runCompactionCheckInner(
|
|
|
960
973
|
|
|
961
974
|
let compactedContent: string
|
|
962
975
|
|
|
963
|
-
|
|
976
|
+
// A model the host set aside writes the summary when it can. Its own cost
|
|
977
|
+
// goes on the turn's ledger at the price of the model that answered. When
|
|
978
|
+
// there is no such model, or its call does not give a summary, the
|
|
979
|
+
// extracted state below is used, with its verifier step where that applies.
|
|
980
|
+
let modelRoute: { providerId: string; model: string } | undefined
|
|
981
|
+
const modelWritten = !ctx.compactionSummaryRoute
|
|
982
|
+
? undefined
|
|
983
|
+
: await summarizeWithModel({
|
|
984
|
+
manager,
|
|
985
|
+
olderMessages,
|
|
986
|
+
previousSummaries: replaceableSummaries(systemMessages),
|
|
987
|
+
config,
|
|
988
|
+
resolveRoute: async (signal) => {
|
|
989
|
+
const route = await ctx.compactionSummaryRoute?.(signal)
|
|
990
|
+
if (route) {
|
|
991
|
+
modelRoute = {
|
|
992
|
+
providerId: route.providerId ?? ctx.recorder.servingProviderId,
|
|
993
|
+
model: route.model,
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
return route
|
|
997
|
+
},
|
|
998
|
+
onUsage: (usage) => {
|
|
999
|
+
if (modelRoute) ctx.recorder.accumulateUsage(usage, modelRoute)
|
|
1000
|
+
},
|
|
1001
|
+
signal: ctx.abortController.signal,
|
|
1002
|
+
onFallback: (reason, error) => {
|
|
1003
|
+
if (reason === 'no_route') return
|
|
1004
|
+
if (error !== undefined) noteCompactionError(ctx, error)
|
|
1005
|
+
ctx.log.warn(
|
|
1006
|
+
'The model-written compaction summary was not used — using the extracted state',
|
|
1007
|
+
{
|
|
1008
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
1009
|
+
'namzu.runtime.reason': reason,
|
|
1010
|
+
...(error !== undefined
|
|
1011
|
+
? { 'exception.message': error instanceof Error ? error.message : String(error) }
|
|
1012
|
+
: {}),
|
|
1013
|
+
},
|
|
1014
|
+
)
|
|
1015
|
+
},
|
|
1016
|
+
})
|
|
1017
|
+
|
|
1018
|
+
if (modelWritten !== undefined) {
|
|
1019
|
+
compactedContent = modelWritten
|
|
1020
|
+
} else if (config.llmVerification && manager.slotCount() < config.richStateThreshold) {
|
|
964
1021
|
// Named once and used twice on purpose. The model the summariser is
|
|
965
1022
|
// asked for is also the model its tokens are priced at, and computing
|
|
966
1023
|
// the two separately is how they drift — a router that sends compaction
|
|
@@ -1077,6 +1134,7 @@ async function runCompactionCheckInner(
|
|
|
1077
1134
|
|
|
1078
1135
|
const live = ctx.recorder.messages
|
|
1079
1136
|
const oldCount = live.length
|
|
1137
|
+
const beforeTokens = estimateRefillTokens(live)
|
|
1080
1138
|
// Retained history can survive in full while a summary adds another copy
|
|
1081
1139
|
// of its facts. Even an unretained span may be smaller than the cumulative
|
|
1082
1140
|
// summary. Compare both candidates with the same estimator, rather than
|
|
@@ -1151,5 +1209,9 @@ async function runCompactionCheckInner(
|
|
|
1151
1209
|
reachedResetThreshold: reachedReset,
|
|
1152
1210
|
})
|
|
1153
1211
|
await emitContextUsageSnapshot(ctx, newEstimate, budget, window.source)
|
|
1154
|
-
compactionBreaker(ctx)?.recordCompaction(
|
|
1212
|
+
compactionBreaker(ctx)?.recordCompaction(
|
|
1213
|
+
ctx.recorder.currentIteration,
|
|
1214
|
+
beforeTokens,
|
|
1215
|
+
estimateRefillTokens(live),
|
|
1216
|
+
)
|
|
1155
1217
|
}
|
|
@@ -2,6 +2,7 @@ import type { AdvisoryContext } from '../../../../advisory/context.js'
|
|
|
2
2
|
import type { SkillGrantSet } from '../../../../authorization/skill-grant.js'
|
|
3
3
|
import type { AgentBus } from '../../../../bus/index.js'
|
|
4
4
|
import type { WorkingStateManager } from '../../../../compaction/manager.js'
|
|
5
|
+
import type { CompactionSummaryRouter } from '../../../../compaction/model-summary.js'
|
|
5
6
|
import type { ContextReducer } from '../../../../compaction/reducer.js'
|
|
6
7
|
import type { CompactionConfig } from '../../../../config/runtime.js'
|
|
7
8
|
import { NAMZU } from '../../../../constants/telemetry/index.js'
|
|
@@ -259,6 +260,9 @@ export interface IterationContext {
|
|
|
259
260
|
/** Host text appended to the restored context after a compaction. */
|
|
260
261
|
readonly postCompactionContext?: () => string | undefined | Promise<string | undefined>
|
|
261
262
|
|
|
263
|
+
/** Where a model-written compaction summary is asked for; see {@link CompactionSummaryRouter}. */
|
|
264
|
+
readonly compactionSummaryRoute?: CompactionSummaryRouter
|
|
265
|
+
|
|
262
266
|
readonly workingStateManager?: WorkingStateManager
|
|
263
267
|
|
|
264
268
|
/**
|
|
@@ -280,6 +284,8 @@ export interface IterationContext {
|
|
|
280
284
|
/** The delegating session, when this turn is a child's: `subagent_stop` names it. */
|
|
281
285
|
readonly parentSessionId?: import('../../../../types/ids/index.js').SessionId
|
|
282
286
|
readonly parentTurnId?: import('../../../../types/ids/index.js').TurnId
|
|
287
|
+
/** The agent this turn runs, named to `subagent_stop` hooks of a child. */
|
|
288
|
+
readonly agentType?: string
|
|
283
289
|
|
|
284
290
|
/**
|
|
285
291
|
* Override for {@link PARK_RECORD_DELAY_MS}. Internal; tests set `0` to
|