@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
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
resolveContextWindow,
|
|
3
|
+
resolveTriggerThreshold,
|
|
4
|
+
} from '../../../../compaction/context-window.js'
|
|
2
5
|
import { findDanglingMessages } from '../../../../compaction/dangling.js'
|
|
3
6
|
import {
|
|
4
7
|
type CompactionPlan,
|
|
@@ -19,6 +22,11 @@ import { invariants } from '../../../../invariants/index.js'
|
|
|
19
22
|
import { resolveTaskModel } from '../../../../model-router/task-router.js'
|
|
20
23
|
import type { Message } from '../../../../types/message/index.js'
|
|
21
24
|
import { applyLifecycleHookResults } from '../../plugin-hooks.js'
|
|
25
|
+
import {
|
|
26
|
+
COMPACTION_FAILURE_MESSAGE,
|
|
27
|
+
COMPACTION_REFILL_MESSAGE,
|
|
28
|
+
compactionBreaker,
|
|
29
|
+
} from './compaction-breaker.js'
|
|
22
30
|
import type { IterationContext } from './context.js'
|
|
23
31
|
import { isWorkingMemoryMessage } from './working-memory.js'
|
|
24
32
|
|
|
@@ -169,6 +177,7 @@ async function commitToolResultClear(
|
|
|
169
177
|
ctx.recorder.clearLastPromptTokens?.()
|
|
170
178
|
await emitToolResultClear(ctx, plan)
|
|
171
179
|
await emitContextUsageSnapshot(ctx, estimateTokens(ctx), contextWindowTokens, windowSource)
|
|
180
|
+
compactionBreaker(ctx)?.recordRelief()
|
|
172
181
|
}
|
|
173
182
|
|
|
174
183
|
/**
|
|
@@ -312,6 +321,7 @@ async function declined(
|
|
|
312
321
|
messages: number,
|
|
313
322
|
error?: string,
|
|
314
323
|
): Promise<void> {
|
|
324
|
+
compactionBreaker(ctx)?.recordFailure()
|
|
315
325
|
await ctx.emitEvent?.({
|
|
316
326
|
type: 'compaction_failed',
|
|
317
327
|
turnId: ctx.recorder.turnId,
|
|
@@ -322,6 +332,32 @@ async function declined(
|
|
|
322
332
|
})
|
|
323
333
|
}
|
|
324
334
|
|
|
335
|
+
/**
|
|
336
|
+
* The third refill in a row: stop the turn instead of compacting again.
|
|
337
|
+
* Returns true when this pass must not run. Only with `circuitBreaker`.
|
|
338
|
+
*/
|
|
339
|
+
async function stopOnRefill(ctx: IterationContext): Promise<boolean> {
|
|
340
|
+
const breaker = compactionBreaker(ctx)
|
|
341
|
+
if (!breaker) return false
|
|
342
|
+
// Already announced: the turn is on its way out; do not say it twice.
|
|
343
|
+
if (breaker.stop !== undefined) return true
|
|
344
|
+
if (!breaker.wouldTrip(ctx.recorder.currentIteration)) return false
|
|
345
|
+
ctx.log.warn('Compaction keeps refilling — stopping the turn', {
|
|
346
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
347
|
+
[NAMZU.ITERATION]: ctx.recorder.currentIteration,
|
|
348
|
+
})
|
|
349
|
+
breaker.stopTurn(COMPACTION_REFILL_MESSAGE)
|
|
350
|
+
await ctx.emitEvent?.({
|
|
351
|
+
type: 'compaction_failed',
|
|
352
|
+
turnId: ctx.recorder.turnId,
|
|
353
|
+
iteration: ctx.recorder.currentIteration,
|
|
354
|
+
cause: 'refilling',
|
|
355
|
+
messages: ctx.recorder.messages.length,
|
|
356
|
+
error: COMPACTION_REFILL_MESSAGE,
|
|
357
|
+
})
|
|
358
|
+
return true
|
|
359
|
+
}
|
|
360
|
+
|
|
325
361
|
/**
|
|
326
362
|
* Ctx for `compaction:no-split-tool-pair`, registered just below: the
|
|
327
363
|
* candidate history a reducer just returned, not yet installed. Passed on
|
|
@@ -456,6 +492,7 @@ async function applyReducer(
|
|
|
456
492
|
reduction.contextWindowTokens,
|
|
457
493
|
measurement.windowSource,
|
|
458
494
|
)
|
|
495
|
+
compactionBreaker(ctx)?.recordCompaction(ctx.recorder.currentIteration)
|
|
459
496
|
}
|
|
460
497
|
|
|
461
498
|
/**
|
|
@@ -498,9 +535,10 @@ async function recordShed(
|
|
|
498
535
|
|
|
499
536
|
/**
|
|
500
537
|
* A pass, once the check decided to run one, is bracketed by the
|
|
501
|
-
* `pre_compact` and `post_compact` hooks.
|
|
502
|
-
*
|
|
503
|
-
*
|
|
538
|
+
* `pre_compact` and `post_compact` hooks. A hook sees the numbers and may
|
|
539
|
+
* cancel a pass the threshold asked for (`skip`), and cannot stop one the
|
|
540
|
+
* provider forced, because a context the provider will reject is not a thing
|
|
541
|
+
* a hook may insist on.
|
|
504
542
|
*/
|
|
505
543
|
interface CompactionPass {
|
|
506
544
|
attempted: boolean
|
|
@@ -573,11 +611,31 @@ async function runCompactionCheckInner(
|
|
|
573
611
|
// The salience strategy starts holding the context at `softTarget`,
|
|
574
612
|
// well before the summary trigger; every other strategy waits for it.
|
|
575
613
|
const salience = config.strategy === 'salience'
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
|
|
614
|
+
const maxOutputTokens = ctx.providerCapabilities?.maxOutputTokens
|
|
615
|
+
const trigger = resolveTriggerThreshold(config.triggerThreshold, budget, maxOutputTokens)
|
|
616
|
+
const startAt = salience ? Math.min(config.softTarget ?? DEFAULT_SOFT_TARGET, trigger) : trigger
|
|
579
617
|
if (!options?.force && usage < startAt) return
|
|
580
618
|
|
|
619
|
+
// Three failed passes in a row: stop trying for the rest of the turn, and
|
|
620
|
+
// say so once. A pass the provider forced is never gated.
|
|
621
|
+
const breaker = compactionBreaker(ctx)
|
|
622
|
+
if (breaker?.failuresOpen && !options?.force) {
|
|
623
|
+
if (breaker.takeFailureNotice()) {
|
|
624
|
+
ctx.log.warn('Compaction failed three times in a row — switched off for this turn', {
|
|
625
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
626
|
+
})
|
|
627
|
+
await ctx.emitEvent?.({
|
|
628
|
+
type: 'compaction_failed',
|
|
629
|
+
turnId: ctx.recorder.turnId,
|
|
630
|
+
iteration: ctx.recorder.currentIteration,
|
|
631
|
+
cause: 'breaker_open',
|
|
632
|
+
messages: ctx.recorder.messages.length,
|
|
633
|
+
error: COMPACTION_FAILURE_MESSAGE,
|
|
634
|
+
})
|
|
635
|
+
}
|
|
636
|
+
return
|
|
637
|
+
}
|
|
638
|
+
|
|
581
639
|
pass.attempted = true
|
|
582
640
|
pass.tokensBefore = estimatedTokens
|
|
583
641
|
pass.contextWindowTokens = budget
|
|
@@ -598,6 +656,25 @@ async function runCompactionCheckInner(
|
|
|
598
656
|
ctx.emitEvent,
|
|
599
657
|
)
|
|
600
658
|
applyLifecycleHookResults('pre_compact', results)
|
|
659
|
+
const refusal = results.find((result) => result.action === 'skip')
|
|
660
|
+
if (refusal?.action === 'skip') {
|
|
661
|
+
// A threshold pass is the context's own housekeeping and a hook may
|
|
662
|
+
// postpone it. A forced pass runs because the provider rejected the
|
|
663
|
+
// prompt: a context it will reject is not something a hook may insist
|
|
664
|
+
// on, so the refusal is reported and the pass runs.
|
|
665
|
+
if (!options?.force) {
|
|
666
|
+
pass.attempted = false
|
|
667
|
+
ctx.log.info('A pre_compact hook cancelled the compaction pass', {
|
|
668
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
669
|
+
'namzu.hook.reason': refusal.reason,
|
|
670
|
+
})
|
|
671
|
+
return
|
|
672
|
+
}
|
|
673
|
+
ctx.log.warn('A pre_compact hook asked to cancel a compaction the provider forced; it ran', {
|
|
674
|
+
[NAMZU.TURN_ID]: ctx.recorder.turnId,
|
|
675
|
+
'namzu.hook.reason': refusal.reason,
|
|
676
|
+
})
|
|
677
|
+
}
|
|
601
678
|
}
|
|
602
679
|
|
|
603
680
|
// A reducer, when the turn has one, OWNS reduction — the structured pass
|
|
@@ -609,6 +686,7 @@ async function runCompactionCheckInner(
|
|
|
609
686
|
ctx.contextReducer ??
|
|
610
687
|
(config.strategy === 'sliding-window' ? createSlidingWindowReducer() : undefined)
|
|
611
688
|
if (reducer) {
|
|
689
|
+
if (!options?.force && (await stopOnRefill(ctx))) return
|
|
612
690
|
await applyReducer(
|
|
613
691
|
ctx,
|
|
614
692
|
reducer,
|
|
@@ -635,7 +713,7 @@ async function runCompactionCheckInner(
|
|
|
635
713
|
'namzu.runtime.window': budget,
|
|
636
714
|
'namzu.runtime.window_source': window.source,
|
|
637
715
|
'namzu.runtime.usage': Math.round(usage * 100),
|
|
638
|
-
'namzu.runtime.trigger_threshold':
|
|
716
|
+
'namzu.runtime.trigger_threshold': trigger,
|
|
639
717
|
'namzu.runtime.slot_count': manager.slotCount(),
|
|
640
718
|
})
|
|
641
719
|
|
|
@@ -668,6 +746,7 @@ async function runCompactionCheckInner(
|
|
|
668
746
|
messages: ctx.recorder.messages,
|
|
669
747
|
config,
|
|
670
748
|
contextWindowTokens: budget,
|
|
749
|
+
...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
|
|
671
750
|
estimatedTokens,
|
|
672
751
|
...(openTasks ? { openTasks } : {}),
|
|
673
752
|
})
|
|
@@ -676,10 +755,7 @@ async function runCompactionCheckInner(
|
|
|
676
755
|
}
|
|
677
756
|
// Under the trigger, the working set was the whole pass: the summary
|
|
678
757
|
// path is for the trigger, not for the soft target.
|
|
679
|
-
if (
|
|
680
|
-
!options?.force &&
|
|
681
|
-
(estimatedTokens - working.reclaimedTokens) / budget < config.triggerThreshold
|
|
682
|
-
) {
|
|
758
|
+
if (!options?.force && (estimatedTokens - working.reclaimedTokens) / budget < trigger) {
|
|
683
759
|
return
|
|
684
760
|
}
|
|
685
761
|
}
|
|
@@ -687,6 +763,7 @@ async function runCompactionCheckInner(
|
|
|
687
763
|
messages: ctx.recorder.messages,
|
|
688
764
|
config,
|
|
689
765
|
contextWindowTokens: budget,
|
|
766
|
+
...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
|
|
690
767
|
estimatedTokens,
|
|
691
768
|
...(options?.force ? { force: true } : {}),
|
|
692
769
|
})
|
|
@@ -712,6 +789,7 @@ async function runCompactionCheckInner(
|
|
|
712
789
|
messages,
|
|
713
790
|
config,
|
|
714
791
|
contextWindowTokens: budget,
|
|
792
|
+
...(maxOutputTokens !== undefined ? { maxOutputTokens } : {}),
|
|
715
793
|
estimatedTokens,
|
|
716
794
|
skipToolResultClear: true,
|
|
717
795
|
...(options?.force ? { force: true } : {}),
|
|
@@ -754,6 +832,14 @@ async function runCompactionCheckInner(
|
|
|
754
832
|
return
|
|
755
833
|
}
|
|
756
834
|
|
|
835
|
+
if (!options?.force && (await stopOnRefill(ctx))) {
|
|
836
|
+
// A clear staged for this pass is still a useful edit.
|
|
837
|
+
if (stagedClear) {
|
|
838
|
+
await commitToolResultClear(ctx, stagedClear, budget, window.source, pass.reason)
|
|
839
|
+
}
|
|
840
|
+
return
|
|
841
|
+
}
|
|
842
|
+
|
|
757
843
|
const { systemMessages, olderMessages, recentMessages, keepStart } = plan
|
|
758
844
|
|
|
759
845
|
let compactedContent: string
|
|
@@ -910,4 +996,5 @@ async function runCompactionCheckInner(
|
|
|
910
996
|
reachedResetThreshold: reachedReset,
|
|
911
997
|
})
|
|
912
998
|
await emitContextUsageSnapshot(ctx, newEstimate, budget, window.source)
|
|
999
|
+
compactionBreaker(ctx)?.recordCompaction(ctx.recorder.currentIteration)
|
|
913
1000
|
}
|
|
@@ -199,6 +199,8 @@ export interface IterationContext {
|
|
|
199
199
|
readonly taskRouter?: TaskRouterConfig
|
|
200
200
|
|
|
201
201
|
readonly compactionConfig?: CompactionConfig
|
|
202
|
+
/** See `QueryParams.maxOutputContinuations`. */
|
|
203
|
+
readonly maxOutputContinuations?: number
|
|
202
204
|
|
|
203
205
|
/**
|
|
204
206
|
* What the driver said this model's context window is, resolved once.
|
|
@@ -225,6 +227,11 @@ export interface IterationContext {
|
|
|
225
227
|
* after tool results and before the next turn.
|
|
226
228
|
*/
|
|
227
229
|
readonly inboundMessages?: () => readonly import('../../../../types/message/index.js').Message[]
|
|
230
|
+
/**
|
|
231
|
+
* Hidden reminders for each model request; see `ReminderSource`. Absent:
|
|
232
|
+
* the turn gets none, as before.
|
|
233
|
+
*/
|
|
234
|
+
readonly reminders?: import('../../reminders.js').ReminderSource
|
|
228
235
|
/** Known topic-queue arrivals appended after the restored checkpoint history. */
|
|
229
236
|
readonly resumedInput?: readonly import('../../../../types/message/index.js').Message[]
|
|
230
237
|
/** Observes pending input without draining it; abort releases the waiter. */
|
|
@@ -252,6 +259,10 @@ export interface IterationContext {
|
|
|
252
259
|
|
|
253
260
|
readonly pluginManager?: import('../../../../plugin/lifecycle.js').PluginLifecycleManager
|
|
254
261
|
|
|
262
|
+
/** The delegating session, when this turn is a child's: `subagent_stop` names it. */
|
|
263
|
+
readonly parentSessionId?: import('../../../../types/ids/index.js').SessionId
|
|
264
|
+
readonly parentTurnId?: import('../../../../types/ids/index.js').TurnId
|
|
265
|
+
|
|
255
266
|
/**
|
|
256
267
|
* Override for {@link PARK_RECORD_DELAY_MS}. Internal; tests set `0` to
|
|
257
268
|
* observe a recorded park without waiting out the real threshold.
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
replaceProjectInstructionSnapshot,
|
|
13
13
|
} from '../../project-instructions.js'
|
|
14
14
|
import { attachRepeatNotice } from '../../repeat-call.js'
|
|
15
|
+
import { isReviewExempt } from '../../review-policy.js'
|
|
15
16
|
import { attachNotice, attachSteering, formatJobNote } from '../../steering.js'
|
|
16
17
|
import { type IterationContext, awaitDecisionDurably } from './context.js'
|
|
17
18
|
|
|
@@ -25,11 +26,23 @@ function escalationReason(escalation: ToolCallSummary['escalation']): string {
|
|
|
25
26
|
return `this call's program cannot be verified ahead of time (${escalation.unknownProgram})`
|
|
26
27
|
}
|
|
27
28
|
if (escalation?.sandboxEscape) return "this call asks to run outside the turn's sandbox"
|
|
29
|
+
if (escalation?.dangerousRemoval !== undefined) {
|
|
30
|
+
return `this call removes something catastrophic (${escalation.dangerousRemoval})`
|
|
31
|
+
}
|
|
32
|
+
if (escalation?.protectedPaths?.length) {
|
|
33
|
+
return `this call writes a protected ${escalation.protectedPaths.length === 1 ? 'path' : 'paths'} (${escalation.protectedPaths.join(', ')})`
|
|
34
|
+
}
|
|
28
35
|
if (escalation?.outsidePaths?.length)
|
|
29
36
|
return `this call reaches ${escalation.outsidePaths.length === 1 ? 'a path' : 'paths'} outside the turn's boundary`
|
|
30
37
|
return "this call reaches past the turn's boundary, which a rule cannot approve on its own"
|
|
31
38
|
}
|
|
32
39
|
|
|
40
|
+
function hookAskedReason(reason: string | undefined): string {
|
|
41
|
+
return reason
|
|
42
|
+
? `a hook asked that a person approve this call: ${reason}`
|
|
43
|
+
: 'a hook asked that a person approve this call'
|
|
44
|
+
}
|
|
45
|
+
|
|
33
46
|
export type ToolReviewDecision = 'executed' | 'rejected' | 'stop'
|
|
34
47
|
|
|
35
48
|
/**
|
|
@@ -124,15 +137,27 @@ export async function* runToolReview(
|
|
|
124
137
|
const isDestructive = tool?.isDestructive ? tool.isDestructive(tc.input) : false
|
|
125
138
|
const requiresApproval = tool?.requiresApproval ? tool.requiresApproval(tc.input) : false
|
|
126
139
|
const escalation = 'escalation' in tc ? tc.escalation : undefined
|
|
140
|
+
const hookPermission = 'hookPermission' in tc ? tc.hookPermission : undefined
|
|
127
141
|
|
|
128
142
|
return {
|
|
129
143
|
id: tc.id,
|
|
130
144
|
name: tc.name,
|
|
131
145
|
input: tc.input,
|
|
132
146
|
isDestructive,
|
|
133
|
-
|
|
147
|
+
// A hook that asked for a person is an explicit review: a call the
|
|
148
|
+
// tool declares read-only, or one a rule would allow, is still put
|
|
149
|
+
// to one.
|
|
150
|
+
authorization:
|
|
151
|
+
hookPermission?.decision === 'ask'
|
|
152
|
+
? {
|
|
153
|
+
decision: 'review' as const,
|
|
154
|
+
reason: hookAskedReason(hookPermission.reason),
|
|
155
|
+
explicitReview: true as const,
|
|
156
|
+
}
|
|
157
|
+
: { decision: 'review' as const },
|
|
134
158
|
...(requiresApproval ? { requiresApproval: true as const } : {}),
|
|
135
159
|
...(escalation ? { escalation } : {}),
|
|
160
|
+
...(hookPermission ? { hookPermission } : {}),
|
|
136
161
|
}
|
|
137
162
|
})
|
|
138
163
|
}
|
|
@@ -355,6 +380,24 @@ export async function* runToolReview(
|
|
|
355
380
|
reason,
|
|
356
381
|
})
|
|
357
382
|
}
|
|
383
|
+
for (const path of tc.escalation?.protectedPaths ?? []) {
|
|
384
|
+
await ctx.recorder.recordAudit({
|
|
385
|
+
what: { action: 'protected_path_write', tool: tc.name, resource: path },
|
|
386
|
+
outcome: 'refused',
|
|
387
|
+
reason,
|
|
388
|
+
})
|
|
389
|
+
}
|
|
390
|
+
if (tc.escalation?.dangerousRemoval !== undefined) {
|
|
391
|
+
await ctx.recorder.recordAudit({
|
|
392
|
+
what: {
|
|
393
|
+
action: 'dangerous_removal',
|
|
394
|
+
tool: tc.name,
|
|
395
|
+
resource: tc.escalation.dangerousRemoval,
|
|
396
|
+
},
|
|
397
|
+
outcome: 'refused',
|
|
398
|
+
reason,
|
|
399
|
+
})
|
|
400
|
+
}
|
|
358
401
|
}
|
|
359
402
|
|
|
360
403
|
// Gate-denied ids survive the whole function: a later human approval
|
|
@@ -419,11 +462,21 @@ export async function* runToolReview(
|
|
|
419
462
|
reason: `${gr.gateResult.reason}; but the tool itself declares this call always needs a person's approval, which a rule cannot waive`,
|
|
420
463
|
}
|
|
421
464
|
}
|
|
465
|
+
// A hook that asked for a person can only ADD a review, like the two
|
|
466
|
+
// overrides above: a `deny` stays a denial.
|
|
467
|
+
const hookAsked = gr.toolCall.hookPermission?.decision === 'ask'
|
|
468
|
+
if (hookAsked && gr.gateResult.decision === 'allow') {
|
|
469
|
+
gr.gateResult = {
|
|
470
|
+
decision: 'review',
|
|
471
|
+
matchedRule: gr.gateResult.matchedRule,
|
|
472
|
+
reason: `${gr.gateResult.reason}; but ${hookAskedReason(gr.toolCall.hookPermission?.reason)}`,
|
|
473
|
+
}
|
|
474
|
+
}
|
|
422
475
|
const { toolCall, gateResult } = gr
|
|
423
476
|
toolCall.authorization = {
|
|
424
477
|
decision: gateResult.decision,
|
|
425
478
|
...(gateResult.reason ? { reason: gateResult.reason } : {}),
|
|
426
|
-
...(gateResult.decision === 'review' && gateResult.matchedRule
|
|
479
|
+
...(gateResult.decision === 'review' && (gateResult.matchedRule || hookAsked)
|
|
427
480
|
? { explicitReview: true as const }
|
|
428
481
|
: {}),
|
|
429
482
|
}
|
|
@@ -498,6 +551,27 @@ export async function* runToolReview(
|
|
|
498
551
|
}
|
|
499
552
|
}
|
|
500
553
|
|
|
554
|
+
// A `pre_tool_use` hook's `allow`, marked on the calls it covers on the
|
|
555
|
+
// same terms as a skill's grant above and honoured by the review policy,
|
|
556
|
+
// never decided here. A deny, an explicit review (a rule's, or a hook's own
|
|
557
|
+
// `ask`), a destructive call, a path outside the roots, a sandbox escape and
|
|
558
|
+
// a tool's own `requiresApproval` all leave the call unmarked, so it is
|
|
559
|
+
// reviewed as though no hook had spoken.
|
|
560
|
+
/** Whether a hook's `allow` may stand in for a person on this call. */
|
|
561
|
+
const hookMayAllow = (tc: ToolCallSummary): boolean =>
|
|
562
|
+
!gateDenied.has(tc.id) &&
|
|
563
|
+
tc.authorization?.decision !== 'deny' &&
|
|
564
|
+
!tc.authorization?.explicitReview &&
|
|
565
|
+
!tc.isDestructive &&
|
|
566
|
+
tc.escalation === undefined &&
|
|
567
|
+
!tc.requiresApproval
|
|
568
|
+
for (const tc of toolCallSummaries) {
|
|
569
|
+
if (tc.hookPermission?.decision !== 'allow' || !hookMayAllow(tc)) continue
|
|
570
|
+
tc.hookGrant = {
|
|
571
|
+
...(tc.hookPermission.reason ? { reason: tc.hookPermission.reason } : {}),
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
501
575
|
// Already approved, at a scope the approver chose — and nothing the
|
|
502
576
|
// operator's policy denied, because `gateDenied` is checked first.
|
|
503
577
|
// Re-asking about a call somebody has already said yes to is how an
|
|
@@ -525,6 +599,76 @@ export async function* runToolReview(
|
|
|
525
599
|
return finish('executed')
|
|
526
600
|
}
|
|
527
601
|
|
|
602
|
+
// A person is about to be asked. `permission_request` hooks may answer
|
|
603
|
+
// first, per call: `skip` refuses it (and always wins), `allow` approves it
|
|
604
|
+
// on the same terms as a `pre_tool_use` allow. A read the review policy
|
|
605
|
+
// would not have asked about is not a question and is not offered.
|
|
606
|
+
if (ctx.pluginManager) {
|
|
607
|
+
const questions = toolCallSummaries.filter(
|
|
608
|
+
(tc) =>
|
|
609
|
+
!gateDenied.has(tc.id) &&
|
|
610
|
+
tc.authorization?.decision !== 'deny' &&
|
|
611
|
+
tc.authorization?.decision !== 'allow' &&
|
|
612
|
+
(tc.authorization?.explicitReview === true ||
|
|
613
|
+
tc.escalation !== undefined ||
|
|
614
|
+
tc.requiresApproval === true ||
|
|
615
|
+
tc.isDestructive ||
|
|
616
|
+
!isReviewExempt(ctx.tools, tc.name, tc.input)),
|
|
617
|
+
)
|
|
618
|
+
for (const tc of questions) {
|
|
619
|
+
const answers = await ctx.pluginManager.executeHooks(
|
|
620
|
+
'permission_request',
|
|
621
|
+
{
|
|
622
|
+
sessionId: ctx.recorder.sessionId,
|
|
623
|
+
turnId: ctx.recorder.turnId,
|
|
624
|
+
toolName: tc.name,
|
|
625
|
+
toolInput: tc.input,
|
|
626
|
+
signal: ctx.abortController.signal,
|
|
627
|
+
},
|
|
628
|
+
ctx.emitEvent,
|
|
629
|
+
)
|
|
630
|
+
// The hooks answered side by side: a refusal from any of them stands,
|
|
631
|
+
// whatever the others allowed.
|
|
632
|
+
const refusal = answers.find((answer) => answer.action === 'skip')
|
|
633
|
+
for (const answer of answers) {
|
|
634
|
+
if ((answer.action === 'continue' || answer.action === 'skip') && answer.halt) {
|
|
635
|
+
ctx.toolExecutor.requestHalt?.(answer.halt.reason)
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (refusal?.action === 'skip') {
|
|
639
|
+
const reason = `Blocked by a permission_request hook: ${refusal.reason}`
|
|
640
|
+
gateDenied.set(tc.id, reason)
|
|
641
|
+
tc.authorization = { decision: 'deny', reason }
|
|
642
|
+
await ctx.recorder.recordAudit({
|
|
643
|
+
what: { action: 'tool_call', tool: tc.name },
|
|
644
|
+
outcome: 'refused',
|
|
645
|
+
reason,
|
|
646
|
+
})
|
|
647
|
+
continue
|
|
648
|
+
}
|
|
649
|
+
for (const answer of answers) {
|
|
650
|
+
if (
|
|
651
|
+
answer.action === 'continue' &&
|
|
652
|
+
answer.permission?.decision === 'allow' &&
|
|
653
|
+
hookMayAllow(tc)
|
|
654
|
+
) {
|
|
655
|
+
tc.hookGrant = {
|
|
656
|
+
...(answer.permission.reason ? { reason: answer.permission.reason } : {}),
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (toolCallSummaries.every((tc) => gateDenied.has(tc.id))) {
|
|
662
|
+
for (const tc of escalated()) {
|
|
663
|
+
const reason = gateDenied.get(tc.id)
|
|
664
|
+
if (reason !== undefined) await recordRefusedEscalation(tc, reason)
|
|
665
|
+
}
|
|
666
|
+
yield* settle(gateDenied)
|
|
667
|
+
yield* ctx.drainPending()
|
|
668
|
+
return finish('rejected')
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
|
|
528
672
|
const reviewCheckpoint = await ctx.checkpointMgr.create(ctx.recorder, iterationNum)
|
|
529
673
|
|
|
530
674
|
await ctx.emitEvent({
|
|
@@ -607,6 +751,24 @@ export async function* runToolReview(
|
|
|
607
751
|
reason: "the turn's review approved this call",
|
|
608
752
|
})
|
|
609
753
|
}
|
|
754
|
+
for (const path of tc.escalation?.protectedPaths ?? []) {
|
|
755
|
+
await ctx.recorder.recordAudit({
|
|
756
|
+
what: { action: 'protected_path_write', tool: tc.name, resource: path },
|
|
757
|
+
outcome: 'approved',
|
|
758
|
+
reason: "the turn's review approved this call",
|
|
759
|
+
})
|
|
760
|
+
}
|
|
761
|
+
if (tc.escalation?.dangerousRemoval !== undefined) {
|
|
762
|
+
await ctx.recorder.recordAudit({
|
|
763
|
+
what: {
|
|
764
|
+
action: 'dangerous_removal',
|
|
765
|
+
tool: tc.name,
|
|
766
|
+
resource: tc.escalation.dangerousRemoval,
|
|
767
|
+
},
|
|
768
|
+
outcome: 'approved',
|
|
769
|
+
reason: "the turn's review approved this call",
|
|
770
|
+
})
|
|
771
|
+
}
|
|
610
772
|
}
|
|
611
773
|
}
|
|
612
774
|
|
|
@@ -762,6 +924,19 @@ export async function* runToolReview(
|
|
|
762
924
|
}
|
|
763
925
|
}
|
|
764
926
|
|
|
927
|
+
// The same for a call a hook's `allow` approved.
|
|
928
|
+
if (reviewDecision.action === 'approve_tools' && reviewDecision.hookGranted) {
|
|
929
|
+
const listed = new Set(reviewDecision.hookGranted)
|
|
930
|
+
for (const tc of toolCallSummaries) {
|
|
931
|
+
if (!listed.has(tc.id) || !tc.hookGrant || gateDenied.has(tc.id)) continue
|
|
932
|
+
await ctx.recorder.recordAudit({
|
|
933
|
+
what: { action: 'tool_call', tool: tc.name },
|
|
934
|
+
outcome: 'approved',
|
|
935
|
+
reason: `pre-approved by a pre_tool_use hook${tc.hookGrant.reason ? ` (${tc.hookGrant.reason})` : ''}; nobody was asked`,
|
|
936
|
+
})
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
765
940
|
await ctx.emitEvent({
|
|
766
941
|
type: 'tool_review_completed',
|
|
767
942
|
turnId: ctx.recorder.turnId,
|