@namzu/sdk 62.6.1 → 62.8.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 +42 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +5 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/agents/examples/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/examples/SupervisorAgent.js +1 -0
- package/dist/agents/examples/SupervisorAgent.js.map +1 -1
- package/dist/agents/explore.d.ts.map +1 -1
- package/dist/agents/explore.js +2 -1
- package/dist/agents/explore.js.map +1 -1
- package/dist/compaction/index.d.ts +1 -1
- package/dist/compaction/index.d.ts.map +1 -1
- package/dist/compaction/index.js +1 -1
- package/dist/compaction/index.js.map +1 -1
- package/dist/compaction/model-summary.js +2 -2
- package/dist/compaction/model-summary.js.map +1 -1
- package/dist/compaction/summary.d.ts +16 -1
- package/dist/compaction/summary.d.ts.map +1 -1
- package/dist/compaction/summary.js +25 -2
- package/dist/compaction/summary.js.map +1 -1
- package/dist/config/runtime.d.ts +8 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +15 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/execution/workflow/runtime.d.ts.map +1 -1
- package/dist/execution/workflow/runtime.js +3 -0
- package/dist/execution/workflow/runtime.js.map +1 -1
- package/dist/execution/workflow/types.d.ts +8 -0
- package/dist/execution/workflow/types.d.ts.map +1 -1
- package/dist/manager/agent/child-session.d.ts +12 -0
- package/dist/manager/agent/child-session.d.ts.map +1 -1
- package/dist/manager/agent/child-session.js +29 -0
- package/dist/manager/agent/child-session.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +8 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts +13 -2
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +107 -29
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/prompt/coding-agent-doctrine.d.ts +3 -3
- package/dist/prompt/coding-agent-doctrine.d.ts.map +1 -1
- package/dist/prompt/coding-agent-doctrine.js +5 -3
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +5 -1
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/retry.d.ts +25 -0
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +28 -0
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +3 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +3 -3
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +4 -2
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +5 -2
- package/dist/public-tools.js.map +1 -1
- package/dist/runtime/query/auto-review/classifier.d.ts +7 -1
- package/dist/runtime/query/auto-review/classifier.d.ts.map +1 -1
- package/dist/runtime/query/auto-review/classifier.js +8 -4
- package/dist/runtime/query/auto-review/classifier.js.map +1 -1
- package/dist/runtime/query/auto-review/prompt.d.ts.map +1 -1
- package/dist/runtime/query/auto-review/prompt.js +2 -0
- package/dist/runtime/query/auto-review/prompt.js.map +1 -1
- package/dist/runtime/query/auto-review/script-evidence.d.ts +93 -0
- package/dist/runtime/query/auto-review/script-evidence.d.ts.map +1 -0
- package/dist/runtime/query/auto-review/script-evidence.js +566 -0
- package/dist/runtime/query/auto-review/script-evidence.js.map +1 -0
- package/dist/runtime/query/checkpoint.d.ts +13 -0
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +37 -0
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +28 -2
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +38 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +154 -13
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/finalize-turn.d.ts +7 -1
- package/dist/runtime/query/finalize-turn.d.ts.map +1 -1
- package/dist/runtime/query/finalize-turn.js +2 -0
- package/dist/runtime/query/finalize-turn.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 +7 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +32 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +158 -10
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +12 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +48 -1
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js +5 -0
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +3 -3
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/prelude-lease.d.ts.map +1 -1
- package/dist/runtime/query/prelude-lease.js +22 -0
- package/dist/runtime/query/prelude-lease.js.map +1 -1
- package/dist/runtime/query/prepare-turn.d.ts.map +1 -1
- package/dist/runtime/query/prepare-turn.js +30 -12
- package/dist/runtime/query/prepare-turn.js.map +1 -1
- package/dist/runtime/query/review-policy.d.ts.map +1 -1
- package/dist/runtime/query/review-policy.js +29 -0
- package/dist/runtime/query/review-policy.js.map +1 -1
- package/dist/runtime/query/session-storage.d.ts.map +1 -1
- package/dist/runtime/query/session-storage.js +44 -3
- package/dist/runtime/query/session-storage.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +2 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +1 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/runtime/query/turn/completion-audit.d.ts +27 -1
- package/dist/runtime/query/turn/completion-audit.d.ts.map +1 -1
- package/dist/runtime/query/turn/completion-audit.js +71 -6
- package/dist/runtime/query/turn/completion-audit.js.map +1 -1
- package/dist/runtime/query/turn/outcome-ledger.d.ts +113 -0
- package/dist/runtime/query/turn/outcome-ledger.d.ts.map +1 -0
- package/dist/runtime/query/turn/outcome-ledger.js +578 -0
- package/dist/runtime/query/turn/outcome-ledger.js.map +1 -0
- package/dist/sandbox/file-walk.d.ts +7 -1
- package/dist/sandbox/file-walk.d.ts.map +1 -1
- package/dist/sandbox/file-walk.js +30 -4
- package/dist/sandbox/file-walk.js.map +1 -1
- package/dist/sandbox/glob-segment.d.ts +8 -1
- package/dist/sandbox/glob-segment.d.ts.map +1 -1
- package/dist/sandbox/glob-segment.js +28 -9
- package/dist/sandbox/glob-segment.js.map +1 -1
- package/dist/store/checkpoint/contract.d.ts +12 -0
- package/dist/store/checkpoint/contract.d.ts.map +1 -1
- package/dist/store/checkpoint/contract.js.map +1 -1
- package/dist/store/checkpoint/disk.d.ts.map +1 -1
- package/dist/store/checkpoint/disk.js +34 -2
- package/dist/store/checkpoint/disk.js.map +1 -1
- package/dist/store/checkpoint/prune.d.ts.map +1 -1
- package/dist/store/checkpoint/prune.js +6 -0
- package/dist/store/checkpoint/prune.js.map +1 -1
- package/dist/store/evidence/disk.js +40 -3
- package/dist/store/evidence/disk.js.map +1 -1
- package/dist/store/session-log/incremental.d.ts +23 -0
- package/dist/store/session-log/incremental.d.ts.map +1 -0
- package/dist/store/session-log/incremental.js +117 -0
- package/dist/store/session-log/incremental.js.map +1 -0
- package/dist/store/session-log/index.d.ts +1 -0
- package/dist/store/session-log/index.d.ts.map +1 -1
- package/dist/store/session-log/index.js +1 -0
- package/dist/store/session-log/index.js.map +1 -1
- package/dist/store/session-log/turn-outcome.d.ts +37 -0
- package/dist/store/session-log/turn-outcome.d.ts.map +1 -0
- package/dist/store/session-log/turn-outcome.js +77 -0
- package/dist/store/session-log/turn-outcome.js.map +1 -0
- package/dist/tools/admit-facts.d.ts.map +1 -1
- package/dist/tools/admit-facts.js +1 -0
- package/dist/tools/admit-facts.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +11 -2
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/bounded-file-read.d.ts +19 -1
- package/dist/tools/builtins/bounded-file-read.d.ts.map +1 -1
- package/dist/tools/builtins/bounded-file-read.js +56 -3
- package/dist/tools/builtins/bounded-file-read.js.map +1 -1
- package/dist/tools/builtins/edit-apply.d.ts.map +1 -1
- package/dist/tools/builtins/edit-apply.js +24 -3
- package/dist/tools/builtins/edit-apply.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts +31 -17
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +92 -27
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/tools/builtins/glob.js +1 -1
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts +8 -0
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +148 -21
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/notebook-guard.d.ts +19 -0
- package/dist/tools/builtins/notebook-guard.d.ts.map +1 -0
- package/dist/tools/builtins/notebook-guard.js +75 -0
- package/dist/tools/builtins/notebook-guard.js.map +1 -0
- package/dist/tools/builtins/read-render.d.ts.map +1 -1
- package/dist/tools/builtins/read-render.js +8 -2
- package/dist/tools/builtins/read-render.js.map +1 -1
- package/dist/tools/builtins/search-support.d.ts +8 -0
- package/dist/tools/builtins/search-support.d.ts.map +1 -1
- package/dist/tools/builtins/search-support.js +66 -0
- package/dist/tools/builtins/search-support.js.map +1 -1
- package/dist/tools/builtins/write-file.d.ts.map +1 -1
- package/dist/tools/builtins/write-file.js +14 -1
- package/dist/tools/builtins/write-file.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js +25 -6
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +6 -1
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/coordinator/outcome-record.d.ts +22 -0
- package/dist/tools/coordinator/outcome-record.d.ts.map +1 -0
- package/dist/tools/coordinator/outcome-record.js +91 -0
- package/dist/tools/coordinator/outcome-record.js.map +1 -0
- package/dist/tools/defineTool.d.ts +2 -0
- package/dist/tools/defineTool.d.ts.map +1 -1
- package/dist/tools/defineTool.js +1 -0
- package/dist/tools/defineTool.js.map +1 -1
- package/dist/tools/file-read-tracker.d.ts +23 -2
- package/dist/tools/file-read-tracker.d.ts.map +1 -1
- package/dist/tools/file-read-tracker.js +37 -2
- package/dist/tools/file-read-tracker.js.map +1 -1
- package/dist/tools/untrusted-envelope.d.ts +6 -0
- package/dist/tools/untrusted-envelope.d.ts.map +1 -1
- package/dist/tools/untrusted-envelope.js +22 -1
- package/dist/tools/untrusted-envelope.js.map +1 -1
- package/dist/toolsets/manager.d.ts.map +1 -1
- package/dist/toolsets/manager.js +3 -4
- package/dist/toolsets/manager.js.map +1 -1
- package/dist/toolsets/validation-issues.d.ts +27 -0
- package/dist/toolsets/validation-issues.d.ts.map +1 -0
- package/dist/toolsets/validation-issues.js +83 -0
- package/dist/toolsets/validation-issues.js.map +1 -0
- package/dist/turn/memory-recall.d.ts +13 -0
- package/dist/turn/memory-recall.d.ts.map +1 -1
- package/dist/turn/memory-recall.js +18 -0
- package/dist/turn/memory-recall.js.map +1 -1
- package/dist/types/agent/base.d.ts +19 -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/common/index.d.ts +17 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +8 -1
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/hitl/index.d.ts +22 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/session/config.d.ts +20 -0
- package/dist/types/session/config.d.ts.map +1 -1
- package/dist/types/session/events.d.ts +3 -3
- package/dist/types/session/events.d.ts.map +1 -1
- package/dist/types/session/index.d.ts +1 -0
- package/dist/types/session/index.d.ts.map +1 -1
- package/dist/types/session/index.js +1 -0
- package/dist/types/session/index.js.map +1 -1
- package/dist/types/session/outcome.d.ts +95 -0
- package/dist/types/session/outcome.d.ts.map +1 -0
- package/dist/types/session/outcome.js +45 -0
- package/dist/types/session/outcome.js.map +1 -0
- package/dist/types/session/prepare-step.d.ts +2 -0
- package/dist/types/session/prepare-step.d.ts.map +1 -1
- package/dist/types/session/records.d.ts +167 -2
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +42 -0
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/turn.d.ts +18 -0
- package/dist/types/session/turn.d.ts.map +1 -1
- package/dist/types/session/turn.js.map +1 -1
- package/dist/types/tool/index.d.ts +30 -0
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/tool/presentation.d.ts +6 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +5 -0
- package/src/agents/examples/SupervisorAgent.ts +1 -0
- package/src/agents/explore.ts +2 -1
- package/src/compaction/index.ts +1 -0
- package/src/compaction/model-summary.ts +2 -2
- package/src/compaction/summary.ts +30 -2
- package/src/config/runtime.ts +15 -0
- package/src/execution/workflow/runtime.ts +4 -0
- package/src/execution/workflow/types.ts +9 -0
- package/src/manager/agent/child-session.ts +36 -0
- package/src/manager/agent/lifecycle.ts +9 -0
- package/src/manager/session/turn-recorder.ts +115 -30
- package/src/pricing/rates.source.json +1 -1
- package/src/prompt/coding-agent-doctrine.ts +5 -3
- package/src/provider/fallback.ts +6 -1
- package/src/provider/retry.ts +51 -0
- package/src/public-runtime.ts +10 -1
- package/src/public-tools.ts +6 -1
- package/src/runtime/query/auto-review/classifier.ts +21 -5
- package/src/runtime/query/auto-review/prompt.ts +2 -0
- package/src/runtime/query/auto-review/script-evidence.ts +618 -0
- package/src/runtime/query/checkpoint.ts +53 -1
- package/src/runtime/query/context.ts +33 -6
- package/src/runtime/query/executor.ts +183 -21
- package/src/runtime/query/finalize-turn.ts +8 -1
- package/src/runtime/query/index.ts +19 -0
- package/src/runtime/query/iteration/index.ts +167 -14
- package/src/runtime/query/iteration/phases/compaction.ts +53 -3
- package/src/runtime/query/iteration/phases/context.ts +6 -0
- package/src/runtime/query/iteration/stream-turn.ts +3 -3
- package/src/runtime/query/prelude-lease.ts +19 -0
- package/src/runtime/query/prepare-turn.ts +29 -12
- package/src/runtime/query/review-policy.ts +33 -0
- package/src/runtime/query/session-storage.ts +42 -3
- package/src/runtime/query/tooling.ts +3 -0
- package/src/runtime/query/turn/completion-audit.ts +76 -5
- package/src/runtime/query/turn/outcome-ledger.ts +633 -0
- package/src/sandbox/file-walk.ts +41 -4
- package/src/sandbox/glob-segment.ts +48 -24
- package/src/store/checkpoint/contract.ts +15 -0
- package/src/store/checkpoint/disk.ts +34 -2
- package/src/store/checkpoint/prune.ts +13 -0
- package/src/store/evidence/disk.ts +57 -2
- package/src/store/session-log/incremental.ts +147 -0
- package/src/store/session-log/index.ts +7 -0
- package/src/store/session-log/turn-outcome.ts +84 -0
- package/src/tools/admit-facts.ts +1 -0
- package/src/tools/builtins/bash.ts +12 -2
- package/src/tools/builtins/bounded-file-read.ts +59 -2
- package/src/tools/builtins/edit-apply.ts +26 -4
- package/src/tools/builtins/edit.ts +106 -41
- package/src/tools/builtins/glob.ts +1 -1
- package/src/tools/builtins/grep.ts +173 -27
- package/src/tools/builtins/notebook-guard.ts +76 -0
- package/src/tools/builtins/read-render.ts +12 -2
- package/src/tools/builtins/search-support.ts +62 -0
- package/src/tools/builtins/write-file.ts +12 -1
- package/src/tools/coordinator/agent.ts +31 -7
- package/src/tools/coordinator/index.ts +6 -1
- package/src/tools/coordinator/outcome-record.ts +93 -0
- package/src/tools/defineTool.ts +3 -0
- package/src/tools/file-read-tracker.ts +51 -2
- package/src/tools/untrusted-envelope.ts +23 -1
- package/src/toolsets/manager.ts +6 -10
- package/src/toolsets/validation-issues.ts +97 -0
- package/src/turn/memory-recall.ts +28 -0
- package/src/types/agent/base.ts +20 -0
- package/src/types/agent/query.ts +2 -0
- package/src/types/common/index.ts +25 -1
- package/src/types/hitl/index.ts +27 -0
- package/src/types/session/config.ts +20 -0
- package/src/types/session/events.ts +3 -3
- package/src/types/session/index.ts +1 -0
- package/src/types/session/outcome.ts +139 -0
- package/src/types/session/prepare-step.ts +2 -0
- package/src/types/session/records.ts +67 -1
- package/src/types/session/turn.ts +19 -0
- package/src/types/tool/index.ts +30 -0
- package/src/types/tool/presentation.ts +6 -0
|
@@ -3,6 +3,7 @@ import { type TurnRecorder, readFoldedHistory } from '../../manager/session/turn
|
|
|
3
3
|
import type { CheckpointScope, SessionCheckpointStore } from '../../store/checkpoint/index.js'
|
|
4
4
|
import type { SessionLog } from '../../store/session-log/index.js'
|
|
5
5
|
import { restoreSpilledRequestInputs } from '../../store/session-log/spilled-input.js'
|
|
6
|
+
import { readOutcomeProgress } from '../../store/session-log/turn-outcome.js'
|
|
6
7
|
import type { SerializedSpanContext } from '../../telemetry/attributes.js'
|
|
7
8
|
import { NamzuError } from '../../types/errors/index.js'
|
|
8
9
|
import type {
|
|
@@ -84,7 +85,14 @@ type CheckpointState = Pick<
|
|
|
84
85
|
| 'review'
|
|
85
86
|
| 'latestUserMessageId'
|
|
86
87
|
| 'workingState'
|
|
87
|
-
>
|
|
88
|
+
> & {
|
|
89
|
+
/**
|
|
90
|
+
* What the turn's outcome record had gathered. Not a field of the document:
|
|
91
|
+
* it is written as its own `outcome_progress_written` record beside the
|
|
92
|
+
* checkpoint, so a release that validates the document strictly reads it.
|
|
93
|
+
*/
|
|
94
|
+
readonly outcome?: Readonly<Record<string, unknown>>
|
|
95
|
+
}
|
|
88
96
|
|
|
89
97
|
/**
|
|
90
98
|
* A checkpoint read back for a resume: the document, the context it
|
|
@@ -276,6 +284,11 @@ export class CheckpointManager {
|
|
|
276
284
|
private restoredNativeAttempts = 0
|
|
277
285
|
private toolStructuredAttemptsSource?: () => number
|
|
278
286
|
private restoredToolAttempts = 0
|
|
287
|
+
private outcomeSource?: () => Readonly<Record<string, unknown>> | undefined
|
|
288
|
+
private restoredOutcomeState: { resumed: boolean; snapshot?: Readonly<Record<string, unknown>> } =
|
|
289
|
+
{
|
|
290
|
+
resumed: false,
|
|
291
|
+
}
|
|
279
292
|
private lastCreatedId?: CheckpointId
|
|
280
293
|
private traceSource?: () => SerializedSpanContext | undefined
|
|
281
294
|
private parkTtlMs?: number
|
|
@@ -329,6 +342,23 @@ export class CheckpointManager {
|
|
|
329
342
|
return this.restoredToolAttempts
|
|
330
343
|
}
|
|
331
344
|
|
|
345
|
+
/** Wire the turn's outcome record in, so every checkpoint carries what it had gathered. */
|
|
346
|
+
setOutcomeSource(source: () => Readonly<Record<string, unknown>> | undefined): void {
|
|
347
|
+
this.outcomeSource = source
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* What a resumed turn's checkpoint kept of the outcome record. `resumed` is
|
|
352
|
+
* false for a turn that began here; true with no `snapshot` for one resumed
|
|
353
|
+
* from a checkpoint that predates the record.
|
|
354
|
+
*/
|
|
355
|
+
get restoredOutcome(): {
|
|
356
|
+
readonly resumed: boolean
|
|
357
|
+
readonly snapshot?: Readonly<Record<string, unknown>>
|
|
358
|
+
} {
|
|
359
|
+
return this.restoredOutcomeState
|
|
360
|
+
}
|
|
361
|
+
|
|
332
362
|
setStructuredReviewAttemptsSource(source: () => number): void {
|
|
333
363
|
this.structuredReviewAttemptsSource = source
|
|
334
364
|
}
|
|
@@ -417,6 +447,7 @@ export class CheckpointManager {
|
|
|
417
447
|
},
|
|
418
448
|
...(latestUserMessageId ? { latestUserMessageId } : {}),
|
|
419
449
|
...withDefined('workingState', this.workingStateSource?.()),
|
|
450
|
+
...withDefined('outcome', this.outcomeSource?.() ?? this.restoredOutcomeState.snapshot),
|
|
420
451
|
}
|
|
421
452
|
}
|
|
422
453
|
|
|
@@ -449,6 +480,19 @@ export class CheckpointManager {
|
|
|
449
480
|
turnCreatedAt: this.turnCreatedAt ?? new Date(recorder.getTurn().startedAt).toISOString(),
|
|
450
481
|
createdAt: new Date().toISOString(),
|
|
451
482
|
}
|
|
483
|
+
// What the outcome record had gathered goes in its own record, ahead of the
|
|
484
|
+
// one that commits the checkpoint, and is read back by the checkpoint's id.
|
|
485
|
+
// Failing to write it is not failing to checkpoint: a resume without it
|
|
486
|
+
// reports no outcome, never a false `unchanged`.
|
|
487
|
+
if (state.outcome !== undefined) {
|
|
488
|
+
await recorder.appendIgnorableRecord({
|
|
489
|
+
type: 'outcome_progress_written',
|
|
490
|
+
turnId: this.scope.turnId,
|
|
491
|
+
ignorable: true,
|
|
492
|
+
checkpointId: document.checkpointId,
|
|
493
|
+
progress: state.outcome,
|
|
494
|
+
})
|
|
495
|
+
}
|
|
452
496
|
const receipt = await this.store.write(this.scope, document)
|
|
453
497
|
await recorder.appendRecord({
|
|
454
498
|
type: 'checkpoint_written',
|
|
@@ -654,6 +698,14 @@ export class CheckpointManager {
|
|
|
654
698
|
this.restoredNativeAttempts = document.review.nativeStructuredAttempts
|
|
655
699
|
this.restoredToolAttempts = document.review.toolStructuredAttempts ?? 0
|
|
656
700
|
this.restoredReviewAttempts = document.review.structuredAttempts
|
|
701
|
+
await this.recorder.flush()
|
|
702
|
+
const progress = await readOutcomeProgress(this.recorder.log, checkpointId).catch(
|
|
703
|
+
() => undefined,
|
|
704
|
+
)
|
|
705
|
+
this.restoredOutcomeState = {
|
|
706
|
+
resumed: true,
|
|
707
|
+
...(progress !== undefined ? { snapshot: progress } : {}),
|
|
708
|
+
}
|
|
657
709
|
this.restoredUserMessage = restored.latestUserMessage
|
|
658
710
|
this.restoredUserMessageId = restored.latestUserMessage
|
|
659
711
|
? document.latestUserMessageId
|
|
@@ -92,6 +92,34 @@ export interface TurnContext {
|
|
|
92
92
|
trackingConfig: ActivityTrackingConfig
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Forward `signal`'s abort to `controller` without keeping the controller, or
|
|
97
|
+
* anything the turn built around it, alive for the life of `signal`.
|
|
98
|
+
*
|
|
99
|
+
* The session's signal outlives every turn, and a listener on it is strongly
|
|
100
|
+
* held for as long as the signal is. A listener that closed over the turn's
|
|
101
|
+
* controller (and the configuration it was built from) kept each finished
|
|
102
|
+
* turn's whole object graph in memory, one more per turn. This one holds the
|
|
103
|
+
* controller through a `WeakRef`, lives in its own scope so the closure
|
|
104
|
+
* captures nothing else, and is removed once the controller is collected.
|
|
105
|
+
*/
|
|
106
|
+
function relayAbort(signal: AbortSignal, controller: AbortController): void {
|
|
107
|
+
const controllerRef = new WeakRef(controller)
|
|
108
|
+
const signalRef = new WeakRef(signal)
|
|
109
|
+
const relay = (): void => {
|
|
110
|
+
controllerRef.deref()?.abort(signalRef.deref()?.reason)
|
|
111
|
+
}
|
|
112
|
+
signal.addEventListener('abort', relay, { once: true })
|
|
113
|
+
relayCleanup.register(controller, { signalRef, relay })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const relayCleanup = new FinalizationRegistry<{
|
|
117
|
+
signalRef: WeakRef<AbortSignal>
|
|
118
|
+
relay: () => void
|
|
119
|
+
}>(({ signalRef, relay }) => {
|
|
120
|
+
signalRef.deref()?.removeEventListener('abort', relay)
|
|
121
|
+
})
|
|
122
|
+
|
|
95
123
|
export class TurnContextFactory {
|
|
96
124
|
/**
|
|
97
125
|
* The turn's one correlated logger, built once and handed to every
|
|
@@ -136,11 +164,7 @@ export class TurnContextFactory {
|
|
|
136
164
|
if (config.signal.aborted) {
|
|
137
165
|
abortController.abort(config.signal.reason)
|
|
138
166
|
} else {
|
|
139
|
-
config.signal
|
|
140
|
-
'abort',
|
|
141
|
-
() => abortController.abort(config.signal?.reason),
|
|
142
|
-
{ once: true },
|
|
143
|
-
)
|
|
167
|
+
relayAbort(config.signal, abortController)
|
|
144
168
|
}
|
|
145
169
|
}
|
|
146
170
|
|
|
@@ -178,7 +202,10 @@ export class TurnContextFactory {
|
|
|
178
202
|
config.enableActivityTracking,
|
|
179
203
|
)
|
|
180
204
|
const activityStore = new ActivityStore(config.turnId, trackingConfig)
|
|
181
|
-
const planManager = new PlanManager({
|
|
205
|
+
const planManager = new PlanManager({
|
|
206
|
+
sessionId: config.sessionId,
|
|
207
|
+
turnId: config.turnId,
|
|
208
|
+
})
|
|
182
209
|
const toolResultsDir =
|
|
183
210
|
config.storage.sessionDir === undefined
|
|
184
211
|
? undefined
|
|
@@ -29,7 +29,11 @@ import { resolveRetainedOutput } from '../../tools/retained-output.js'
|
|
|
29
29
|
import type { ToolManager } from '../../toolsets/manager.js'
|
|
30
30
|
import type { ToolSourceRef } from '../../toolsets/types.js'
|
|
31
31
|
import type { ToolResultGuardrailSpec } from '../../types/guardrail/index.js'
|
|
32
|
-
import type {
|
|
32
|
+
import type {
|
|
33
|
+
LaunchedScript,
|
|
34
|
+
ToolCallDeclined,
|
|
35
|
+
ToolCallEscalation,
|
|
36
|
+
} from '../../types/hitl/index.js'
|
|
33
37
|
import type { SessionId, ToolUseId, TurnId } from '../../types/ids/index.js'
|
|
34
38
|
import type { InvocationState } from '../../types/invocation/index.js'
|
|
35
39
|
import {
|
|
@@ -72,6 +76,12 @@ import { compressShellOutput } from '../../utils/shell-compress.js'
|
|
|
72
76
|
import { parseStructuredResultJson } from '../../utils/structured-result-json.js'
|
|
73
77
|
import { ensureScratchpadDirectory } from '../../utils/temp.js'
|
|
74
78
|
import { type BackgroundJobRegistry, type JobProcess, bindOwner } from '../jobs/registry.js'
|
|
79
|
+
import {
|
|
80
|
+
type ScriptEvidence,
|
|
81
|
+
analyzeLaunches,
|
|
82
|
+
changedSinceRead,
|
|
83
|
+
readLaunchedScripts,
|
|
84
|
+
} from './auto-review/script-evidence.js'
|
|
75
85
|
import { type BatchDetach, stillRunningResult } from './detached-calls.js'
|
|
76
86
|
import {
|
|
77
87
|
type ToolAdmissionHost,
|
|
@@ -378,6 +388,8 @@ export interface ToolExecutorConfig {
|
|
|
378
388
|
outsideRootAccess?: 'refuse' | 'review'
|
|
379
389
|
/** See `QueryParams.sandboxEscape`. Default `'refuse'`. */
|
|
380
390
|
sandboxEscape?: 'refuse' | 'review'
|
|
391
|
+
/** See `QueryParams.outsideRootRefusal`. */
|
|
392
|
+
outsideRootRefusal?: string
|
|
381
393
|
/**
|
|
382
394
|
* Read LIVE, not frozen at turn start.
|
|
383
395
|
*
|
|
@@ -577,6 +589,12 @@ export interface ToolCallOutcome {
|
|
|
577
589
|
/** Rich form for the model, when the tool supplied one. */
|
|
578
590
|
content?: ToolResultContent
|
|
579
591
|
isError?: boolean
|
|
592
|
+
/**
|
|
593
|
+
* What the call was about and what came of it, as typed fields; the same value
|
|
594
|
+
* `tool_completed` carries. Absent when a post-tool hook replaced the output,
|
|
595
|
+
* so a reader never takes an exit status from a result the hook rewrote.
|
|
596
|
+
*/
|
|
597
|
+
facts?: ToolFacts
|
|
580
598
|
/** Provider argument admission failure, never inferred from receipt text. */
|
|
581
599
|
inputFailure?: CompletedToolRecord['inputFailure']
|
|
582
600
|
/** A pre-tool hook deliberately skipped execution; its receipt is not an output candidate. */
|
|
@@ -687,7 +705,7 @@ export type PriorToolResults = ReadonlyMap<string, PriorToolResult>
|
|
|
687
705
|
* for administrator rights is named as that; everything else is a plain block.
|
|
688
706
|
* Read from the call's own argument, never from the refusal's wording.
|
|
689
707
|
*/
|
|
690
|
-
function refusalFacts(toolName: string, input: unknown): ToolFacts {
|
|
708
|
+
function refusalFacts(toolName: string, input: unknown, reason?: string): ToolFacts {
|
|
691
709
|
const command =
|
|
692
710
|
input && typeof input === 'object' ? (input as { command?: unknown }).command : undefined
|
|
693
711
|
const elevated = typeof command === 'string' && /(^|[\s;&|(])(sudo|doas)\b/.test(command)
|
|
@@ -695,9 +713,31 @@ function refusalFacts(toolName: string, input: unknown): ToolFacts {
|
|
|
695
713
|
kind: 'failure',
|
|
696
714
|
reason: elevated ? 'needs_administrator' : 'blocked',
|
|
697
715
|
subject: toolName,
|
|
716
|
+
...(refusalNote(reason) ? { note: refusalNote(reason) } : {}),
|
|
698
717
|
}
|
|
699
718
|
}
|
|
700
719
|
|
|
720
|
+
const REFUSAL_NOTE_MAX = 200
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* The refusal's own reason as one line for the person's view: the first
|
|
724
|
+
* non-empty line, the leading "Blocked:" label dropped, first letter up, bounded. The policy
|
|
725
|
+
* wrote it; nothing here reads the model's text.
|
|
726
|
+
*/
|
|
727
|
+
function refusalNote(reason: string | undefined): string | undefined {
|
|
728
|
+
const line = reason
|
|
729
|
+
?.split(/\r?\n/)
|
|
730
|
+
.map((part) => part.trim())
|
|
731
|
+
.find(Boolean)
|
|
732
|
+
if (!line) return undefined
|
|
733
|
+
const plain = line.replace(/^Blocked(?: by [^:]{1,60})?:\s*/i, '').trim()
|
|
734
|
+
if (!plain) return undefined
|
|
735
|
+
const sentence = `${plain.charAt(0).toUpperCase()}${plain.slice(1)}`
|
|
736
|
+
return sentence.length > REFUSAL_NOTE_MAX
|
|
737
|
+
? `${sentence.slice(0, REFUSAL_NOTE_MAX - 1)}…`
|
|
738
|
+
: sentence
|
|
739
|
+
}
|
|
740
|
+
|
|
701
741
|
export function deniedToolOutput(toolName: string, reason: string): string {
|
|
702
742
|
return `Error: Tool "${toolName}" was not executed. ${reason}`
|
|
703
743
|
}
|
|
@@ -732,6 +772,9 @@ export class ToolExecutor {
|
|
|
732
772
|
private readonly toolCallBudget?: ToolCallBudget
|
|
733
773
|
private readonly preparedBatches = new WeakSet<PreparedToolBatch>()
|
|
734
774
|
private readonly preparedBashDirectories = new WeakMap<PreparedSafetyCall, string>()
|
|
775
|
+
/** Script files a classifier read for a call, by call id, to be checked again when it runs. */
|
|
776
|
+
private readonly reviewedScripts = new Map<string, ScriptEvidence['watch']>()
|
|
777
|
+
private readonly startedAt = Date.now()
|
|
735
778
|
/** Ephemeral proof of actual execution; restored messages do not acquire it. */
|
|
736
779
|
private readonly recordedObservations = new Map<
|
|
737
780
|
string,
|
|
@@ -982,6 +1025,47 @@ export class ToolExecutor {
|
|
|
982
1025
|
)
|
|
983
1026
|
}
|
|
984
1027
|
|
|
1028
|
+
/**
|
|
1029
|
+
* The script files a shell line launches that the agent wrote or that
|
|
1030
|
+
* appeared during the session, read now for the auto-mode classifier.
|
|
1031
|
+
* Remembers what was read for `callId`, so the file can be checked again
|
|
1032
|
+
* just before the call runs.
|
|
1033
|
+
*/
|
|
1034
|
+
async launchedScripts(
|
|
1035
|
+
callId: string,
|
|
1036
|
+
line: string,
|
|
1037
|
+
dialect: ShellDialect,
|
|
1038
|
+
): Promise<readonly LaunchedScript[]> {
|
|
1039
|
+
if (this.sandboxed) {
|
|
1040
|
+
// A sandbox's files are not on this host's disk. Say so rather than
|
|
1041
|
+
// letting a launch inside one look like a launch of nothing.
|
|
1042
|
+
const analysis = analyzeLaunches(line, dialect)
|
|
1043
|
+
const named = analysis.launches.map((l) => l.path)
|
|
1044
|
+
return named.length === 0 && analysis.unresolved.length === 0
|
|
1045
|
+
? []
|
|
1046
|
+
: [
|
|
1047
|
+
{
|
|
1048
|
+
path: named[0] ?? '(launch form not resolved)',
|
|
1049
|
+
origin: 'unknown',
|
|
1050
|
+
state: 'unreadable',
|
|
1051
|
+
note: 'the command runs in a sandbox whose files are not read for this review, so what the script does is not known',
|
|
1052
|
+
},
|
|
1053
|
+
]
|
|
1054
|
+
}
|
|
1055
|
+
const evidence = await readLaunchedScripts(line, dialect, {
|
|
1056
|
+
cwd: effectiveBashWorkingDirectory(this.config.sessionId, this.config.workingDirectory, {
|
|
1057
|
+
sandboxed: false,
|
|
1058
|
+
}),
|
|
1059
|
+
startedAt: this.fileReadTracker.startedAt ?? this.startedAt,
|
|
1060
|
+
...(this.fileReadTracker.authoredFingerprint
|
|
1061
|
+
? { authoredFingerprint: (key: string) => this.fileReadTracker.authoredFingerprint?.(key) }
|
|
1062
|
+
: {}),
|
|
1063
|
+
})
|
|
1064
|
+
if (evidence.watch.length > 0) this.reviewedScripts.set(callId, evidence.watch)
|
|
1065
|
+
else this.reviewedScripts.delete(callId)
|
|
1066
|
+
return evidence.scripts
|
|
1067
|
+
}
|
|
1068
|
+
|
|
985
1069
|
/** The whole of the evidence as one text, from the same ledger. No filesystem I/O. */
|
|
986
1070
|
describeFileEvidence(messages: readonly Message[]): string | undefined {
|
|
987
1071
|
return describeVisibleFileEvidence(
|
|
@@ -1207,11 +1291,12 @@ export class ToolExecutor {
|
|
|
1207
1291
|
toolCall,
|
|
1208
1292
|
!hostModifiedCallIds?.has(toolCall.id),
|
|
1209
1293
|
)
|
|
1210
|
-
const
|
|
1294
|
+
const beforeReview = await this.refusedBeforeReview(prepared, earlierPaths)
|
|
1211
1295
|
const path = inputPathOf(prepared)
|
|
1212
1296
|
if (path !== undefined) earlierPaths.push(path)
|
|
1297
|
+
this.admitScratchpad(beforeReview)
|
|
1298
|
+
const call = await this.refusedOutsideRoot(beforeReview)
|
|
1213
1299
|
calls.set(toolCall.id, call)
|
|
1214
|
-
this.admitScratchpad(call)
|
|
1215
1300
|
const escalation = await this.escalationOrUnfinished(call)
|
|
1216
1301
|
if (escalation) escalations.set(toolCall.id, escalation)
|
|
1217
1302
|
if (await this.writesScratchpad(call, escalation)) scratchpadWrites.add(toolCall.id)
|
|
@@ -1220,6 +1305,49 @@ export class ToolExecutor {
|
|
|
1220
1305
|
return this.publishPreparedBatch(calls, escalations, scratchpadWrites)
|
|
1221
1306
|
}
|
|
1222
1307
|
|
|
1308
|
+
/**
|
|
1309
|
+
* A file tool's path outside the roots, refused with the host's own words
|
|
1310
|
+
* (`QueryParams.outsideRootRefusal`) instead of the tool's generic one.
|
|
1311
|
+
*
|
|
1312
|
+
* It takes the place of the call like any refusal found while preparing it,
|
|
1313
|
+
* so it holds in every permission mode and reaches no reviewer. Only a turn
|
|
1314
|
+
* that refuses (`outsideRootAccess: 'refuse'`) and has no sandbox is looked
|
|
1315
|
+
* at, and only a tool that declares `pathArgument`. The exemptions are the
|
|
1316
|
+
* review's: the scratch folder is a root, and so is this session's retained
|
|
1317
|
+
* output for `read`.
|
|
1318
|
+
*/
|
|
1319
|
+
private async refusedOutsideRoot(call: PreparedDirectCall): Promise<PreparedDirectCall> {
|
|
1320
|
+
const note = this.config.outsideRootRefusal
|
|
1321
|
+
if (call.kind !== 'ready' || note === undefined) return call
|
|
1322
|
+
if (this.config.outsideRootAccess !== 'refuse' || this.sandboxed) return call
|
|
1323
|
+
const tool = this.config.tools.get(call.toolName)
|
|
1324
|
+
if (tool?.pathArgument === undefined) return call
|
|
1325
|
+
if (call.input === null || typeof call.input !== 'object') return call
|
|
1326
|
+
const value = (call.input as Record<string, unknown>)[tool.pathArgument]
|
|
1327
|
+
if (typeof value !== 'string') return call
|
|
1328
|
+
try {
|
|
1329
|
+
const retained =
|
|
1330
|
+
tool.name === 'read' && (await resolveRetainedOutput(this.outputDirectory(), value))
|
|
1331
|
+
if (retained) return call
|
|
1332
|
+
const outside = await pathOutsideRoots(this.rootsNow(), value)
|
|
1333
|
+
if (outside === undefined) return call
|
|
1334
|
+
return {
|
|
1335
|
+
kind: 'synthetic',
|
|
1336
|
+
toolCall: call.toolCall,
|
|
1337
|
+
toolName: call.toolName,
|
|
1338
|
+
input: call.input,
|
|
1339
|
+
message: formatFailedToolOutput(
|
|
1340
|
+
undefined,
|
|
1341
|
+
`${outside} is outside the working directory. ${note}`,
|
|
1342
|
+
),
|
|
1343
|
+
isError: true,
|
|
1344
|
+
}
|
|
1345
|
+
} catch {
|
|
1346
|
+
this.config.abortSignal.throwIfAborted()
|
|
1347
|
+
return call
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1223
1351
|
/**
|
|
1224
1352
|
* A ready call the tool already knows cannot run, answered now instead of
|
|
1225
1353
|
* after a reviewer has been asked about it (`ToolDefinition.refuseBeforeReview`).
|
|
@@ -1461,12 +1589,13 @@ export class ToolExecutor {
|
|
|
1461
1589
|
const earlierPaths: string[] = []
|
|
1462
1590
|
for (const toolCall of response.message.toolCalls ?? []) {
|
|
1463
1591
|
if (changedCallIds.has(toolCall.id)) {
|
|
1464
|
-
const
|
|
1592
|
+
const beforeReview = await this.refusedBeforeReview(
|
|
1465
1593
|
await prepareDirectCall(this.admissionHost(), toolCall, false),
|
|
1466
1594
|
earlierPaths,
|
|
1467
1595
|
)
|
|
1596
|
+
this.admitScratchpad(beforeReview)
|
|
1597
|
+
const call = await this.refusedOutsideRoot(beforeReview)
|
|
1468
1598
|
calls.set(toolCall.id, call)
|
|
1469
|
-
this.admitScratchpad(call)
|
|
1470
1599
|
const escalation = await this.escalationOrUnfinished(call)
|
|
1471
1600
|
if (escalation) escalations.set(toolCall.id, escalation)
|
|
1472
1601
|
else escalations.delete(toolCall.id)
|
|
@@ -1730,13 +1859,25 @@ export class ToolExecutor {
|
|
|
1730
1859
|
let outcome: ToolCallOutcome
|
|
1731
1860
|
started.add(toolCall.id)
|
|
1732
1861
|
try {
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1862
|
+
const reviewedScripts = this.reviewedScripts.get(toolCall.id)
|
|
1863
|
+
this.reviewedScripts.delete(toolCall.id)
|
|
1864
|
+
const moved =
|
|
1865
|
+
reviewedScripts === undefined ? undefined : await changedSinceRead(reviewedScripts)
|
|
1866
|
+
outcome =
|
|
1867
|
+
moved !== undefined
|
|
1868
|
+
? {
|
|
1869
|
+
toolCallId: toolCall.id,
|
|
1870
|
+
toolName: toolCall.function.name,
|
|
1871
|
+
output: `Error: ${moved} changed after the safety check read it, so the command was not run. Run it again and it will be checked against the new text.`,
|
|
1872
|
+
isError: true,
|
|
1873
|
+
}
|
|
1874
|
+
: await this.executeSingle(
|
|
1875
|
+
toolCall,
|
|
1876
|
+
ctx,
|
|
1877
|
+
recordObservation,
|
|
1878
|
+
() => progress.close(),
|
|
1879
|
+
preparedCall,
|
|
1880
|
+
)
|
|
1740
1881
|
} catch (error) {
|
|
1741
1882
|
// A call already answered with the placeholder has no batch
|
|
1742
1883
|
// left to fail; its failure is its outcome.
|
|
@@ -1932,6 +2073,7 @@ export class ToolExecutor {
|
|
|
1932
2073
|
} catch (error) {
|
|
1933
2074
|
failure ??= { error }
|
|
1934
2075
|
} finally {
|
|
2076
|
+
this.reviewedScripts.delete(unit.toolCall.id)
|
|
1935
2077
|
settledUnit()
|
|
1936
2078
|
inFlight.delete(unit)
|
|
1937
2079
|
pump()
|
|
@@ -2269,7 +2411,7 @@ export class ToolExecutor {
|
|
|
2269
2411
|
toolName: name,
|
|
2270
2412
|
toolUseId: nestedId,
|
|
2271
2413
|
output: rawOutput,
|
|
2272
|
-
maxChars: this.config.
|
|
2414
|
+
maxChars: this.toolOutputCap(this.config.tools.get(name), false),
|
|
2273
2415
|
retainedPreviewChars: this.config.retainedToolPreviewChars,
|
|
2274
2416
|
spillDir: this.outputDirectory(),
|
|
2275
2417
|
recoveryRoutes: this.recoveryRoutes(),
|
|
@@ -2734,11 +2876,7 @@ export class ToolExecutor {
|
|
|
2734
2876
|
]
|
|
2735
2877
|
}
|
|
2736
2878
|
}
|
|
2737
|
-
|
|
2738
|
-
// cap only; a host that set the cap explicitly still gets it.
|
|
2739
|
-
const maxToolOutputChars =
|
|
2740
|
-
this.config.maxToolOutputChars ??
|
|
2741
|
-
(executedDefinition?.selfLimitingOutput === true ? 0 : DEFAULT_MAX_TOOL_OUTPUT_CHARS)
|
|
2879
|
+
const maxToolOutputChars = this.toolOutputCap(executedDefinition, true)
|
|
2742
2880
|
const sourceOutput = output
|
|
2743
2881
|
// This owner-bound channel follows the selected receipt after both result
|
|
2744
2882
|
// screening and post-tool replacement. ToolResult.data and raw input can
|
|
@@ -2967,6 +3105,7 @@ export class ToolExecutor {
|
|
|
2967
3105
|
? { handoff: result.handoff }
|
|
2968
3106
|
: {}),
|
|
2969
3107
|
...(revealedTools !== undefined ? { revealedTools } : {}),
|
|
3108
|
+
...(result.facts !== undefined && !postOverride ? { facts: result.facts } : {}),
|
|
2970
3109
|
}
|
|
2971
3110
|
}
|
|
2972
3111
|
|
|
@@ -3465,6 +3604,8 @@ export class ToolExecutor {
|
|
|
3465
3604
|
preparedCall?: PreparedDirectCall,
|
|
3466
3605
|
declined?: ToolCallDeclined,
|
|
3467
3606
|
): Promise<ToolCallOutcome> {
|
|
3607
|
+
// A call that does not run has no use for what a review read of its scripts.
|
|
3608
|
+
this.reviewedScripts.delete(toolCall.id)
|
|
3468
3609
|
const toolName = preparedCall?.toolName ?? toolCall.function.name
|
|
3469
3610
|
let input: unknown = preparedCall?.input ?? {}
|
|
3470
3611
|
if (!preparedCall) {
|
|
@@ -3514,7 +3655,7 @@ export class ToolExecutor {
|
|
|
3514
3655
|
...(declined ? { presentation: this.declinedPresentation(toolName, input, declined) } : {}),
|
|
3515
3656
|
// A refusal by policy says what kind it was; the person's own No is
|
|
3516
3657
|
// already carried by the presentation above.
|
|
3517
|
-
...(declined ? {} : { facts: refusalFacts(toolName, input) }),
|
|
3658
|
+
...(declined ? {} : { facts: refusalFacts(toolName, input, reason) }),
|
|
3518
3659
|
isError: true,
|
|
3519
3660
|
})
|
|
3520
3661
|
|
|
@@ -3623,6 +3764,7 @@ export class ToolExecutor {
|
|
|
3623
3764
|
toolName,
|
|
3624
3765
|
output: outcome.output,
|
|
3625
3766
|
isError: outcome.kind === 'error',
|
|
3767
|
+
...(outcome.facts ? { facts: outcome.facts } : {}),
|
|
3626
3768
|
...(outcome.inputFailure ? { inputFailure: outcome.inputFailure } : {}),
|
|
3627
3769
|
...(outcome.skipped ? { skipped: true as const } : {}),
|
|
3628
3770
|
}
|
|
@@ -3638,6 +3780,26 @@ export class ToolExecutor {
|
|
|
3638
3780
|
})
|
|
3639
3781
|
}
|
|
3640
3782
|
|
|
3783
|
+
/**
|
|
3784
|
+
* The per-result character cap for one tool: the turn's cap, tightened by
|
|
3785
|
+
* the tool's own `maxOutputChars`. A tool that bounds its own output is
|
|
3786
|
+
* exempt from the DEFAULT cap only (`exemptSelfLimiting`); a host that set
|
|
3787
|
+
* the cap explicitly still gets it. 0 means no cap.
|
|
3788
|
+
*/
|
|
3789
|
+
private toolOutputCap(
|
|
3790
|
+
definition: ToolDefinition | undefined,
|
|
3791
|
+
exemptSelfLimiting: boolean,
|
|
3792
|
+
): number {
|
|
3793
|
+
const turn =
|
|
3794
|
+
this.config.maxToolOutputChars ??
|
|
3795
|
+
(exemptSelfLimiting && definition?.selfLimitingOutput === true
|
|
3796
|
+
? 0
|
|
3797
|
+
: DEFAULT_MAX_TOOL_OUTPUT_CHARS)
|
|
3798
|
+
const own = definition?.maxOutputChars
|
|
3799
|
+
if (own === undefined || !Number.isFinite(own) || own <= 0) return turn
|
|
3800
|
+
return turn > 0 ? Math.min(turn, Math.floor(own)) : Math.floor(own)
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3641
3803
|
/**
|
|
3642
3804
|
* The host preview and model text may differ; each gets its own text
|
|
3643
3805
|
* budget. Rich bytes are measured separately and withheld whole when
|
|
@@ -3674,7 +3836,7 @@ export class ToolExecutor {
|
|
|
3674
3836
|
toolName,
|
|
3675
3837
|
toolUseId,
|
|
3676
3838
|
output: text,
|
|
3677
|
-
maxChars: this.config.
|
|
3839
|
+
maxChars: this.toolOutputCap(this.config.tools.get(toolName), false),
|
|
3678
3840
|
...(notice ? { notice } : {}),
|
|
3679
3841
|
// A tool may return different host and model text. They must
|
|
3680
3842
|
// never compete for the same exclusive spill filename.
|
|
@@ -2,7 +2,7 @@ import type { Span } from '@opentelemetry/api'
|
|
|
2
2
|
import { consolidationEntry, isConsolidated } from '../../compaction/consolidation.js'
|
|
3
3
|
import type { WorkingStateManager } from '../../compaction/manager.js'
|
|
4
4
|
import { NAMZU } from '../../constants/telemetry/index.js'
|
|
5
|
-
import type { SessionEvent, StepResult } from '../../types/session/index.js'
|
|
5
|
+
import type { SessionEvent, StepResult, TurnOutcome } from '../../types/session/index.js'
|
|
6
6
|
import { toErrorMessage } from '../../utils/error.js'
|
|
7
7
|
import type { TurnContext } from './context.js'
|
|
8
8
|
import type { EventTranslator } from './events.js'
|
|
@@ -53,6 +53,12 @@ export interface TurnFinalization {
|
|
|
53
53
|
* immediately before the record is written.
|
|
54
54
|
*/
|
|
55
55
|
readonly takeSteps: () => readonly StepResult[]
|
|
56
|
+
/**
|
|
57
|
+
* The turn's outcome record, read here for the same reason as the steps: the
|
|
58
|
+
* workspace is compared once, as late as it can be and before the record is
|
|
59
|
+
* written. Never throws; `undefined` means no record was kept.
|
|
60
|
+
*/
|
|
61
|
+
readonly takeOutcome?: () => Promise<TurnOutcome | undefined>
|
|
56
62
|
/**
|
|
57
63
|
* Whether the loop already fired `turn_end` (and `subagent_stop`) when the
|
|
58
64
|
* model finished, so a hook could keep the turn going. Fired there, it is
|
|
@@ -108,6 +114,7 @@ export async function* finalizeTurn(
|
|
|
108
114
|
// Hand the step record to the turn before it settles, so the
|
|
109
115
|
// returned `Turn` carries it.
|
|
110
116
|
ctx.recorder.setSteps(takeSteps())
|
|
117
|
+
if (finalization.takeOutcome) ctx.recorder.setOutcome(await finalization.takeOutcome())
|
|
111
118
|
|
|
112
119
|
// Gates the FINAL result, not the stream — `text_delta` already
|
|
113
120
|
// reached the host as the model produced it. A rewrite is
|
|
@@ -619,6 +619,14 @@ export interface QueryParams {
|
|
|
619
619
|
* is never, because the path is not mounted there to be reached.
|
|
620
620
|
*/
|
|
621
621
|
outsideRootAccess?: 'refuse' | 'review'
|
|
622
|
+
/**
|
|
623
|
+
* The sentence a host wants the model to read when `outsideRootAccess` is
|
|
624
|
+
* `'refuse'` and a file tool names a path outside the roots, for example
|
|
625
|
+
* which setting made the wall and how to move it. Without it the tool's own
|
|
626
|
+
* generic refusal stands. The refusal is made before any review, so no
|
|
627
|
+
* permission mode (bypass included) lifts it.
|
|
628
|
+
*/
|
|
629
|
+
outsideRootRefusal?: string
|
|
622
630
|
/**
|
|
623
631
|
* Whether a call may ask to run outside the turn's sandbox (the shipped
|
|
624
632
|
* `bash` tool's `dangerously_disable_sandbox`).
|
|
@@ -1599,6 +1607,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
1599
1607
|
: {}),
|
|
1600
1608
|
...(scratchpadDirectory ? { scratchpadDirectory } : {}),
|
|
1601
1609
|
...(params.outsideRootAccess ? { outsideRootAccess: params.outsideRootAccess } : {}),
|
|
1610
|
+
...(params.outsideRootRefusal ? { outsideRootRefusal: params.outsideRootRefusal } : {}),
|
|
1602
1611
|
...(params.sandboxEscape ? { sandboxEscape: params.sandboxEscape } : {}),
|
|
1603
1612
|
permissionMode: () => ctx.permissionMode.current,
|
|
1604
1613
|
env: turnConfig.env ?? {},
|
|
@@ -2823,6 +2832,9 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2823
2832
|
}
|
|
2824
2833
|
}
|
|
2825
2834
|
|
|
2835
|
+
// The parked batch runs here, outside the loop's own steps, so
|
|
2836
|
+
// the outcome record is told about it here.
|
|
2837
|
+
await iterationOrchestrator.outcomeBefore(pendingResume.response)
|
|
2826
2838
|
const resumedBatch = await applyPendingResume(
|
|
2827
2839
|
pendingResume,
|
|
2828
2840
|
ctx.recorder,
|
|
@@ -2831,6 +2843,11 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2831
2843
|
pendingResumeAssistantId,
|
|
2832
2844
|
)
|
|
2833
2845
|
iterationOrchestrator.setResumedToolResults(resumedBatch.results)
|
|
2846
|
+
await iterationOrchestrator.outcomeObserve(
|
|
2847
|
+
ctx.recorder.currentIteration,
|
|
2848
|
+
pendingResume.response,
|
|
2849
|
+
resumedBatch.results,
|
|
2850
|
+
)
|
|
2834
2851
|
yield* eventTranslator.drainPending()
|
|
2835
2852
|
}
|
|
2836
2853
|
|
|
@@ -2911,6 +2928,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2911
2928
|
params,
|
|
2912
2929
|
eventTranslator,
|
|
2913
2930
|
takeSteps: () => iterationOrchestrator.getSteps(),
|
|
2931
|
+
takeOutcome: () => iterationOrchestrator.takeOutcome(),
|
|
2914
2932
|
turnEndHooksFired: () => iterationOrchestrator.turnEndHooksFired,
|
|
2915
2933
|
workingStateManager,
|
|
2916
2934
|
resultAssembler,
|
|
@@ -2933,6 +2951,7 @@ export async function* query(params: QueryParams): AsyncGenerator<SessionEvent,
|
|
|
2933
2951
|
if (isTurnInProgressError(err) && !ctx.recorder.isActive) throw err
|
|
2934
2952
|
// A failed turn still spent its steps; report them.
|
|
2935
2953
|
ctx.recorder.setSteps(iterationOrchestrator.getSteps())
|
|
2954
|
+
ctx.recorder.setOutcome(await iterationOrchestrator.takeOutcome())
|
|
2936
2955
|
await executeUserInterruptHooks(err)
|
|
2937
2956
|
yield* eventTranslator.drainPending()
|
|
2938
2957
|
await executeTurnFailureHooks(err)
|