@namzu/sdk 59.0.0 → 60.1.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 +120 -0
- package/dist/agents/QueryAgent.d.ts.map +1 -1
- package/dist/agents/QueryAgent.js +1 -0
- package/dist/agents/QueryAgent.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +16 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- 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/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +12 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/compaction/context-window.d.ts +53 -0
- package/dist/compaction/context-window.d.ts.map +1 -1
- package/dist/compaction/context-window.js +141 -2
- package/dist/compaction/context-window.js.map +1 -1
- package/dist/constants/continuation.d.ts +9 -0
- package/dist/constants/continuation.d.ts.map +1 -1
- package/dist/constants/continuation.js +9 -0
- package/dist/constants/continuation.js.map +1 -1
- package/dist/contracts/session/api.d.ts +7 -1
- package/dist/contracts/session/api.d.ts.map +1 -1
- package/dist/execution/workflow/index.d.ts +3 -1
- package/dist/execution/workflow/index.d.ts.map +1 -1
- package/dist/execution/workflow/index.js +2 -1
- package/dist/execution/workflow/index.js.map +1 -1
- package/dist/execution/workflow/journal.d.ts +24 -4
- package/dist/execution/workflow/journal.d.ts.map +1 -1
- package/dist/execution/workflow/journal.js +131 -9
- package/dist/execution/workflow/journal.js.map +1 -1
- package/dist/execution/workflow/prelude.d.ts +2 -0
- package/dist/execution/workflow/prelude.d.ts.map +1 -1
- package/dist/execution/workflow/prelude.js +49 -8
- package/dist/execution/workflow/prelude.js.map +1 -1
- package/dist/execution/workflow/resume.d.ts +59 -0
- package/dist/execution/workflow/resume.d.ts.map +1 -0
- package/dist/execution/workflow/resume.js +273 -0
- package/dist/execution/workflow/resume.js.map +1 -0
- package/dist/execution/workflow/runtime.d.ts.map +1 -1
- package/dist/execution/workflow/runtime.js +112 -8
- package/dist/execution/workflow/runtime.js.map +1 -1
- package/dist/execution/workflow/types.d.ts +105 -1
- package/dist/execution/workflow/types.d.ts.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/manager/session/turn-recorder.d.ts.map +1 -1
- package/dist/manager/session/turn-recorder.js +1 -0
- package/dist/manager/session/turn-recorder.js.map +1 -1
- package/dist/plugin/compat.d.ts +24 -0
- package/dist/plugin/compat.d.ts.map +1 -0
- package/dist/plugin/compat.js +149 -0
- package/dist/plugin/compat.js.map +1 -0
- package/dist/plugin/loader.d.ts +2 -1
- package/dist/plugin/loader.d.ts.map +1 -1
- package/dist/plugin/loader.js +6 -0
- package/dist/plugin/loader.js.map +1 -1
- package/dist/pricing/index.d.ts.map +1 -1
- package/dist/pricing/index.js +2 -1
- package/dist/pricing/index.js.map +1 -1
- package/dist/process/kill-tree.d.ts +44 -0
- package/dist/process/kill-tree.d.ts.map +1 -1
- package/dist/process/kill-tree.js +102 -4
- package/dist/process/kill-tree.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 +9 -1
- package/dist/prompt/coding-agent-doctrine.js.map +1 -1
- package/dist/provider/collect-chat-completion.d.ts.map +1 -1
- package/dist/provider/collect-chat-completion.js +4 -0
- package/dist/provider/collect-chat-completion.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +11 -0
- package/dist/provider/fallback.js.map +1 -1
- package/dist/provider/idle-timeout.d.ts.map +1 -1
- package/dist/provider/idle-timeout.js +5 -0
- package/dist/provider/idle-timeout.js.map +1 -1
- package/dist/provider/mock.d.ts +2 -0
- package/dist/provider/mock.d.ts.map +1 -1
- package/dist/provider/mock.js +16 -8
- package/dist/provider/mock.js.map +1 -1
- package/dist/provider/model-window-suffix.d.ts +18 -0
- package/dist/provider/model-window-suffix.d.ts.map +1 -0
- package/dist/provider/model-window-suffix.js +78 -0
- package/dist/provider/model-window-suffix.js.map +1 -0
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +5 -0
- package/dist/provider/retry.js.map +1 -1
- package/dist/public-runtime.d.ts +5 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -4
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +1 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.d.ts +1 -4
- package/dist/registry/command/kernel-commands.d.ts.map +1 -1
- package/dist/registry/command/kernel-commands.js +0 -28
- package/dist/registry/command/kernel-commands.js.map +1 -1
- package/dist/runtime/jobs/awaited-jobs.d.ts +6 -0
- package/dist/runtime/jobs/awaited-jobs.d.ts.map +1 -1
- package/dist/runtime/jobs/awaited-jobs.js +11 -0
- package/dist/runtime/jobs/awaited-jobs.js.map +1 -1
- package/dist/runtime/jobs/host-exit-finalizer.d.ts +20 -0
- package/dist/runtime/jobs/host-exit-finalizer.d.ts.map +1 -0
- package/dist/runtime/jobs/host-exit-finalizer.js +57 -0
- package/dist/runtime/jobs/host-exit-finalizer.js.map +1 -0
- package/dist/runtime/jobs/liveness.d.ts +67 -0
- package/dist/runtime/jobs/liveness.d.ts.map +1 -0
- package/dist/runtime/jobs/liveness.js +94 -0
- package/dist/runtime/jobs/liveness.js.map +1 -0
- 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 +104 -2
- package/dist/runtime/jobs/registry.js.map +1 -1
- package/dist/runtime/jobs/wake-budget.d.ts +35 -0
- package/dist/runtime/jobs/wake-budget.d.ts.map +1 -0
- package/dist/runtime/jobs/wake-budget.js +48 -0
- package/dist/runtime/jobs/wake-budget.js.map +1 -0
- package/dist/runtime/query/detached-calls.d.ts +65 -0
- package/dist/runtime/query/detached-calls.d.ts.map +1 -0
- package/dist/runtime/query/detached-calls.js +116 -0
- package/dist/runtime/query/detached-calls.js.map +1 -0
- package/dist/runtime/query/executor.d.ts +4 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +90 -6
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +34 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +32 -5
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/effort-clamp.d.ts +13 -0
- package/dist/runtime/query/iteration/effort-clamp.d.ts.map +1 -0
- package/dist/runtime/query/iteration/effort-clamp.js +42 -0
- package/dist/runtime/query/iteration/effort-clamp.js.map +1 -0
- package/dist/runtime/query/iteration/index.d.ts +34 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +199 -6
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/outstanding-work.d.ts.map +1 -1
- package/dist/runtime/query/iteration/outstanding-work.js +11 -3
- package/dist/runtime/query/iteration/outstanding-work.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +9 -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 +63 -1
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +6 -0
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/session-storage.d.ts.map +1 -1
- package/dist/runtime/query/session-storage.js +8 -3
- package/dist/runtime/query/session-storage.js.map +1 -1
- package/dist/runtime/query/steering.d.ts +29 -3
- package/dist/runtime/query/steering.d.ts.map +1 -1
- package/dist/runtime/query/steering.js +58 -3
- package/dist/runtime/query/steering.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 +1 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/runtime/system-events.d.ts +5 -0
- package/dist/runtime/system-events.d.ts.map +1 -1
- package/dist/runtime/system-events.js +7 -1
- package/dist/runtime/system-events.js.map +1 -1
- package/dist/sandbox/denial.d.ts +11 -0
- package/dist/sandbox/denial.d.ts.map +1 -0
- package/dist/sandbox/denial.js +61 -0
- package/dist/sandbox/denial.js.map +1 -0
- package/dist/sandbox/file-walk-program.d.ts.map +1 -1
- package/dist/sandbox/file-walk-program.js +31 -13
- package/dist/sandbox/file-walk-program.js.map +1 -1
- package/dist/sandbox/file-walk.d.ts +2 -0
- package/dist/sandbox/file-walk.d.ts.map +1 -1
- package/dist/sandbox/file-walk.js +64 -13
- package/dist/sandbox/file-walk.js.map +1 -1
- package/dist/sandbox/ignore-matcher.d.ts +30 -0
- package/dist/sandbox/ignore-matcher.d.ts.map +1 -0
- package/dist/sandbox/ignore-matcher.js +11 -0
- package/dist/sandbox/ignore-matcher.js.map +1 -0
- package/dist/sandbox/ignore-source.d.ts +26 -0
- package/dist/sandbox/ignore-source.d.ts.map +1 -0
- package/dist/sandbox/ignore-source.js +221 -0
- package/dist/sandbox/ignore-source.js.map +1 -0
- package/dist/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +11 -1
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/scheduler/completion-inbox.d.ts.map +1 -1
- package/dist/scheduler/completion-inbox.js +1 -0
- package/dist/scheduler/completion-inbox.js.map +1 -1
- package/dist/session/log-hash.d.ts.map +1 -1
- package/dist/session/log-hash.js +18 -1
- package/dist/session/log-hash.js.map +1 -1
- package/dist/store/memory/disk.d.ts.map +1 -1
- package/dist/store/memory/disk.js +1 -1
- 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 +1 -1
- package/dist/store/memory/markdown.js.map +1 -1
- package/dist/store/memory/operation-lock.d.ts +20 -4
- package/dist/store/memory/operation-lock.d.ts.map +1 -1
- package/dist/store/memory/operation-lock.js +235 -58
- package/dist/store/memory/operation-lock.js.map +1 -1
- package/dist/store/session-index/sqlite.d.ts.map +1 -1
- package/dist/store/session-index/sqlite.js +169 -16
- package/dist/store/session-index/sqlite.js.map +1 -1
- package/dist/tools/builtins/bash.d.ts.map +1 -1
- package/dist/tools/builtins/bash.js +10 -4
- package/dist/tools/builtins/bash.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts +35 -0
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +512 -67
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/glob.d.ts +1 -0
- package/dist/tools/builtins/glob.d.ts.map +1 -1
- package/dist/tools/builtins/glob.js +65 -28
- package/dist/tools/builtins/glob.js.map +1 -1
- package/dist/tools/builtins/grep.d.ts +2 -0
- package/dist/tools/builtins/grep.d.ts.map +1 -1
- package/dist/tools/builtins/grep.js +94 -40
- package/dist/tools/builtins/grep.js.map +1 -1
- package/dist/tools/builtins/job.d.ts +1 -1
- package/dist/tools/builtins/job.d.ts.map +1 -1
- package/dist/tools/builtins/job.js +34 -7
- package/dist/tools/builtins/job.js.map +1 -1
- package/dist/tools/builtins/search-support.d.ts +44 -0
- package/dist/tools/builtins/search-support.d.ts.map +1 -0
- package/dist/tools/builtins/search-support.js +89 -0
- package/dist/tools/builtins/search-support.js.map +1 -0
- package/dist/tools/builtins/wait-for-job-bounds.d.ts +2 -0
- package/dist/tools/builtins/wait-for-job-bounds.d.ts.map +1 -1
- package/dist/tools/builtins/wait-for-job-bounds.js +3 -1
- package/dist/tools/builtins/wait-for-job-bounds.js.map +1 -1
- package/dist/tools/builtins/wait-for-job.d.ts +1 -1
- package/dist/tools/builtins/wait-for-job.d.ts.map +1 -1
- package/dist/tools/builtins/wait-for-job.js +13 -2
- package/dist/tools/builtins/wait-for-job.js.map +1 -1
- package/dist/tools/command-shell.d.ts.map +1 -1
- package/dist/tools/command-shell.js +14 -5
- package/dist/tools/command-shell.js.map +1 -1
- package/dist/turn/reporter.d.ts.map +1 -1
- package/dist/turn/reporter.js +11 -0
- package/dist/turn/reporter.js.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/computer-use/index.d.ts +129 -13
- package/dist/types/computer-use/index.d.ts.map +1 -1
- package/dist/types/computer-use/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 +1 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +24 -0
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +4 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +19 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +7 -0
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/index.d.ts +1 -1
- package/dist/types/provider/index.d.ts.map +1 -1
- package/dist/types/provider/interface.d.ts +15 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +6 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/sandbox/index.d.ts +38 -1
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/session/events.d.ts +34 -0
- 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 +298 -34
- package/dist/types/session/records.d.ts.map +1 -1
- package/dist/types/session/records.js +35 -1
- package/dist/types/session/records.js.map +1 -1
- package/dist/types/session/stop-reason.d.ts +11 -0
- package/dist/types/session/stop-reason.d.ts.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 +8 -0
- package/dist/types/tool/presentation.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/agents/QueryAgent.ts +1 -0
- package/src/bridge/a2a/mapper.ts +19 -0
- package/src/bridge/acp/update.ts +6 -1
- package/src/bridge/sse/mapper.ts +13 -0
- package/src/compaction/context-window.ts +163 -2
- package/src/constants/continuation.ts +13 -0
- package/src/contracts/session/api.ts +6 -0
- package/src/execution/workflow/index.ts +14 -1
- package/src/execution/workflow/journal.ts +161 -11
- package/src/execution/workflow/prelude.ts +51 -8
- package/src/execution/workflow/resume.ts +313 -0
- package/src/execution/workflow/runtime.ts +127 -9
- package/src/execution/workflow/types.ts +109 -1
- package/src/index.ts +17 -0
- package/src/manager/session/turn-recorder.ts +1 -0
- package/src/plugin/compat.ts +173 -0
- package/src/plugin/loader.ts +7 -1
- package/src/pricing/index.ts +2 -1
- package/src/process/kill-tree.ts +106 -4
- package/src/prompt/coding-agent-doctrine.ts +9 -1
- package/src/provider/collect-chat-completion.ts +3 -0
- package/src/provider/fallback.ts +17 -0
- package/src/provider/idle-timeout.ts +9 -0
- package/src/provider/mock.ts +19 -9
- package/src/provider/model-window-suffix.ts +85 -0
- package/src/provider/retry.ts +9 -0
- package/src/public-runtime.ts +21 -2
- package/src/public-types.ts +1 -0
- package/src/registry/command/kernel-commands.ts +1 -32
- package/src/runtime/jobs/awaited-jobs.ts +11 -0
- package/src/runtime/jobs/host-exit-finalizer.ts +67 -0
- package/src/runtime/jobs/liveness.ts +144 -0
- package/src/runtime/jobs/registry.ts +114 -2
- package/src/runtime/jobs/wake-budget.ts +55 -0
- package/src/runtime/query/detached-calls.ts +135 -0
- package/src/runtime/query/executor.ts +92 -10
- package/src/runtime/query/index.ts +65 -7
- package/src/runtime/query/iteration/effort-clamp.ts +46 -0
- package/src/runtime/query/iteration/index.ts +260 -6
- package/src/runtime/query/iteration/outstanding-work.ts +12 -3
- package/src/runtime/query/iteration/phases/context.ts +9 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +73 -8
- package/src/runtime/query/iteration/stream-turn.ts +5 -0
- package/src/runtime/query/session-storage.ts +8 -3
- package/src/runtime/query/steering.ts +77 -6
- package/src/runtime/query/tooling.ts +2 -0
- package/src/runtime/system-events.ts +12 -1
- package/src/sandbox/denial.ts +79 -0
- package/src/sandbox/file-walk-program.ts +32 -13
- package/src/sandbox/file-walk.ts +75 -12
- package/src/sandbox/ignore-matcher.ts +46 -0
- package/src/sandbox/ignore-source.ts +220 -0
- package/src/sandbox/provider/local.ts +11 -1
- package/src/scheduler/completion-inbox.ts +1 -0
- package/src/session/log-hash.ts +21 -0
- package/src/store/memory/disk.ts +1 -0
- package/src/store/memory/markdown.ts +1 -0
- package/src/store/memory/operation-lock.ts +288 -57
- package/src/store/session-index/sqlite.ts +171 -16
- package/src/tools/builtins/bash.ts +11 -4
- package/src/tools/builtins/computer-use.ts +593 -68
- package/src/tools/builtins/glob.ts +79 -32
- package/src/tools/builtins/grep.ts +107 -43
- package/src/tools/builtins/job.ts +42 -12
- package/src/tools/builtins/search-support.ts +107 -0
- package/src/tools/builtins/wait-for-job-bounds.ts +5 -1
- package/src/tools/builtins/wait-for-job.ts +14 -2
- package/src/tools/command-shell.ts +13 -5
- package/src/turn/reporter.ts +12 -0
- package/src/types/agent/query.ts +2 -0
- package/src/types/computer-use/index.ts +157 -15
- package/src/types/message/index.ts +5 -3
- package/src/types/plugin/index.ts +8 -0
- package/src/types/provider/chat.ts +20 -0
- package/src/types/provider/config.ts +7 -0
- package/src/types/provider/index.ts +1 -0
- package/src/types/provider/interface.ts +20 -0
- package/src/types/provider/stream.ts +6 -0
- package/src/types/sandbox/index.ts +40 -1
- package/src/types/session/events.ts +65 -5
- package/src/types/session/records.ts +39 -2
- package/src/types/session/stop-reason.ts +11 -0
- package/src/types/tool/index.ts +30 -0
- package/src/types/tool/presentation.ts +8 -0
|
@@ -7,6 +7,8 @@ import { estimateMessageTokens } from '../../../compaction/token-estimate.js'
|
|
|
7
7
|
import {
|
|
8
8
|
AUTO_CONTINUATION_USER_MESSAGE,
|
|
9
9
|
REASONING_CUT_USER_MESSAGE,
|
|
10
|
+
REFUSED_REPLY_CALL_RESULT,
|
|
11
|
+
REFUSED_REPLY_RETRY_MESSAGE,
|
|
10
12
|
} from '../../../constants/continuation.js'
|
|
11
13
|
import { CHARS_PER_TOKEN } from '../../../constants/limits.js'
|
|
12
14
|
import {
|
|
@@ -37,6 +39,7 @@ import {
|
|
|
37
39
|
createAssistantMessage,
|
|
38
40
|
createRuntimeContextMessage,
|
|
39
41
|
createSystemMessage,
|
|
42
|
+
createToolMessage,
|
|
40
43
|
} from '../../../types/message/index.js'
|
|
41
44
|
import { classifyProviderError, readOutputOverflow } from '../../../types/provider/errors.js'
|
|
42
45
|
import type { ChatCompletionResponse } from '../../../types/provider/index.js'
|
|
@@ -71,6 +74,7 @@ import {
|
|
|
71
74
|
projectRequestRichContent,
|
|
72
75
|
} from '../request-rich-content.js'
|
|
73
76
|
import { formatSteeringNote, isOperatorUserMessage } from '../steering.js'
|
|
77
|
+
import { effortForModel } from './effort-clamp.js'
|
|
74
78
|
import { parseNativeCandidate } from './native-output.js'
|
|
75
79
|
import {
|
|
76
80
|
drainArrivedTaskCompletions,
|
|
@@ -225,7 +229,10 @@ export class IterationOrchestrator {
|
|
|
225
229
|
* serving from the member it last moved to, so that member, not the head,
|
|
226
230
|
* is the one whose reading of images counts.
|
|
227
231
|
*/
|
|
228
|
-
private servingModel(model: string): {
|
|
232
|
+
private servingModel(model: string): {
|
|
233
|
+
readonly providerId: string
|
|
234
|
+
readonly model: string
|
|
235
|
+
} {
|
|
229
236
|
const serving = this.ctx.servingMember?.()
|
|
230
237
|
return {
|
|
231
238
|
providerId: serving?.providerId ?? this.ctx.provider.id,
|
|
@@ -291,6 +298,16 @@ export class IterationOrchestrator {
|
|
|
291
298
|
private stopDeferredForOutstandingWork = false
|
|
292
299
|
/** Nudges sent since the model last finished a call without hitting its output cap. */
|
|
293
300
|
private outputContinuations = 0
|
|
301
|
+
/**
|
|
302
|
+
* The model a refused request moved to. Once set it replaces the model the
|
|
303
|
+
* step asked for, for the rest of the turn: the request that was refused
|
|
304
|
+
* would only be refused again.
|
|
305
|
+
*/
|
|
306
|
+
private refusalPinnedModel: string | undefined
|
|
307
|
+
/** The request has already been re-run on another model once this turn. */
|
|
308
|
+
private refusalFallbackUsed = false
|
|
309
|
+
/** The request has already been re-run on the same model once this turn. */
|
|
310
|
+
private refusalRetryUsed = false
|
|
294
311
|
/**
|
|
295
312
|
* `turn_end` (and, for a child, `subagent_stop`) fired when the model
|
|
296
313
|
* finished and no hook asked for more. `finalizeTurn` then does not fire
|
|
@@ -319,7 +336,11 @@ export class IterationOrchestrator {
|
|
|
319
336
|
const feedback: string[] = []
|
|
320
337
|
const ask = async (
|
|
321
338
|
event: 'turn_end' | 'subagent_stop',
|
|
322
|
-
extra: {
|
|
339
|
+
extra: {
|
|
340
|
+
parentSessionId?: SessionId
|
|
341
|
+
parentTurnId?: TurnId
|
|
342
|
+
agentType?: string
|
|
343
|
+
},
|
|
323
344
|
): Promise<void> => {
|
|
324
345
|
const results = await manager.executeHooks(
|
|
325
346
|
event,
|
|
@@ -795,7 +816,7 @@ export class IterationOrchestrator {
|
|
|
795
816
|
// Preparation inference belongs to the turn, not the main-model step.
|
|
796
817
|
usageBefore = { ...recorder.tokenUsage }
|
|
797
818
|
costBefore = { ...recorder.costInfo }
|
|
798
|
-
stepModel = step.model ?? model
|
|
819
|
+
stepModel = this.refusalPinnedModel ?? step.model ?? model
|
|
799
820
|
await selectContextModel(shaping, stepModel)
|
|
800
821
|
// Preserve post-compaction preparation/recall semantics. A changed
|
|
801
822
|
// model needs a second check against its own window; never replay
|
|
@@ -824,7 +845,7 @@ export class IterationOrchestrator {
|
|
|
824
845
|
// still call any of them by name.
|
|
825
846
|
this.ctx.toolExecutor.setStepAllowedTools(stepAllowedTools)
|
|
826
847
|
const enforceToolInputSchema = enforcedModelInputToolNames(this.ctx.tools, llmTools)
|
|
827
|
-
stepModel = step.model ?? model
|
|
848
|
+
stepModel = this.refusalPinnedModel ?? step.model ?? model
|
|
828
849
|
|
|
829
850
|
// The closing directive is appended LAST, after every piece of
|
|
830
851
|
// request-only context below, so it is the final thing the model
|
|
@@ -1051,6 +1072,25 @@ export class IterationOrchestrator {
|
|
|
1051
1072
|
model: requestedMember.model ?? stepModel,
|
|
1052
1073
|
chainIndex: requestedMember.index,
|
|
1053
1074
|
}
|
|
1075
|
+
// The turn's effort was chosen for the turn's own model. A request
|
|
1076
|
+
// moved to another model (a refusal fallback, a live pick) carries
|
|
1077
|
+
// the nearest level that model offers, for this request only.
|
|
1078
|
+
let requestEffort = turnConfig.effort
|
|
1079
|
+
if (requestEffort && stepModel !== model) {
|
|
1080
|
+
requestEffort = effortForModel(
|
|
1081
|
+
this.ctx.provider,
|
|
1082
|
+
stepModel,
|
|
1083
|
+
requestEffort,
|
|
1084
|
+
turnConfig.thinking,
|
|
1085
|
+
)
|
|
1086
|
+
if (requestEffort !== turnConfig.effort) {
|
|
1087
|
+
this.ctx.log.debug('Effort lowered to a level this model offers', {
|
|
1088
|
+
'namzu.runtime.model': stepModel,
|
|
1089
|
+
'namzu.runtime.effort_requested': turnConfig.effort,
|
|
1090
|
+
'namzu.runtime.effort_used': requestEffort,
|
|
1091
|
+
})
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1054
1094
|
const operatorInputAtDispatch = this.latestUserMessage
|
|
1055
1095
|
const latestReviewUserMessage =
|
|
1056
1096
|
(this.ctx.reviewAnswer || this.ctx.structuredOutput?.review) && operatorInputAtDispatch
|
|
@@ -1092,7 +1132,7 @@ export class IterationOrchestrator {
|
|
|
1092
1132
|
outputCapOverride ?? step.maxResponseTokens ?? turnConfig.maxResponseTokens,
|
|
1093
1133
|
cacheControl: { type: 'auto' },
|
|
1094
1134
|
...(turnConfig.thinking ? { thinking: turnConfig.thinking } : {}),
|
|
1095
|
-
...(
|
|
1135
|
+
...(requestEffort ? { effort: requestEffort } : {}),
|
|
1096
1136
|
...(!forceFinalize && turnConfig.webSearch
|
|
1097
1137
|
? { webSearch: turnConfig.webSearch }
|
|
1098
1138
|
: {}),
|
|
@@ -1247,6 +1287,34 @@ export class IterationOrchestrator {
|
|
|
1247
1287
|
...contextFigures,
|
|
1248
1288
|
})
|
|
1249
1289
|
|
|
1290
|
+
// A reply the provider declined to give is its own case. It is not
|
|
1291
|
+
// an empty completion: asking again with the same history on the
|
|
1292
|
+
// same model is refused again, at the full price of the prompt.
|
|
1293
|
+
// Recovered here, before the reply can enter the history.
|
|
1294
|
+
if (response.finishReason === 'content_filter') {
|
|
1295
|
+
iterSpan.setStatus({ code: SpanStatusCode.OK })
|
|
1296
|
+
if (iterationActivity) {
|
|
1297
|
+
this.ctx.activityStore.complete(iterationActivity.id, {
|
|
1298
|
+
content: response.message.content,
|
|
1299
|
+
hasToolCalls: false,
|
|
1300
|
+
})
|
|
1301
|
+
}
|
|
1302
|
+
const outcome = yield* this.recoverFromRefusal({
|
|
1303
|
+
response,
|
|
1304
|
+
messageId,
|
|
1305
|
+
stepModel,
|
|
1306
|
+
servedBy,
|
|
1307
|
+
forceFinalize,
|
|
1308
|
+
limitStopReason: forceFinalize ? guardResult.stopReason : undefined,
|
|
1309
|
+
iterationNum,
|
|
1310
|
+
stepStartedAt,
|
|
1311
|
+
usageBefore,
|
|
1312
|
+
costBefore,
|
|
1313
|
+
})
|
|
1314
|
+
if (outcome === 'retry') continue
|
|
1315
|
+
break
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1250
1318
|
// Durable reasoning and its adapter-private replay envelope ride
|
|
1251
1319
|
// with the turn they belong to. Trimming therefore removes both;
|
|
1252
1320
|
// retaining them gives the target adapter enough evidence to
|
|
@@ -1681,7 +1749,10 @@ export class IterationOrchestrator {
|
|
|
1681
1749
|
[NAMZU.ITERATION]: iterationNum,
|
|
1682
1750
|
'namzu.runtime.finish_reason': response.finishReason,
|
|
1683
1751
|
})
|
|
1684
|
-
closingStopReason = await this.requestFinalResponse(
|
|
1752
|
+
closingStopReason = await this.requestFinalResponse(
|
|
1753
|
+
this.refusalPinnedModel ?? model,
|
|
1754
|
+
'end_turn',
|
|
1755
|
+
)
|
|
1685
1756
|
yield* this.ctx.drainPending()
|
|
1686
1757
|
}
|
|
1687
1758
|
|
|
@@ -2873,6 +2944,179 @@ export class IterationOrchestrator {
|
|
|
2873
2944
|
})
|
|
2874
2945
|
}
|
|
2875
2946
|
|
|
2947
|
+
/**
|
|
2948
|
+
* A reply the provider declined to give.
|
|
2949
|
+
*
|
|
2950
|
+
* The refused reply never enters the history sent next, except on the
|
|
2951
|
+
* same-model retry, where the stopped partial reply stays so the model can
|
|
2952
|
+
* see what to avoid. At most one automatic retry of each kind per turn:
|
|
2953
|
+
*
|
|
2954
|
+
* 1. Another model of the same provider, when the provider names one and
|
|
2955
|
+
* the host has not turned `refusalFallback` off. The model is kept for
|
|
2956
|
+
* the rest of the turn and announced in a `model_refusal` event.
|
|
2957
|
+
* 2. The same model once more, only when the refused reply had partial
|
|
2958
|
+
* text or tool calls: unfinished calls get an error result and the
|
|
2959
|
+
* model is told the reply was stopped.
|
|
2960
|
+
* 3. Otherwise the turn settles `refused`.
|
|
2961
|
+
*/
|
|
2962
|
+
private async *recoverFromRefusal(input: {
|
|
2963
|
+
response: ChatCompletionResponse
|
|
2964
|
+
messageId: MessageId
|
|
2965
|
+
stepModel: string
|
|
2966
|
+
servedBy: StepProvenance
|
|
2967
|
+
forceFinalize: boolean
|
|
2968
|
+
/** The limit that asked for this closing step; it stays the reason the turn ended. */
|
|
2969
|
+
limitStopReason?: StopReason | undefined
|
|
2970
|
+
iterationNum: number
|
|
2971
|
+
stepStartedAt: number
|
|
2972
|
+
usageBefore: TokenUsage
|
|
2973
|
+
costBefore: CostInfo
|
|
2974
|
+
}): AsyncGenerator<SessionEvent, 'retry' | 'settled'> {
|
|
2975
|
+
const { recorder } = this.ctx
|
|
2976
|
+
const { response, servedBy, iterationNum } = input
|
|
2977
|
+
const refusal = response.refusal ?? {}
|
|
2978
|
+
const toolCalls = response.message.toolCalls ?? []
|
|
2979
|
+
const hasPartial =
|
|
2980
|
+
(response.message.content !== null && response.message.content.length > 0) ||
|
|
2981
|
+
toolCalls.length > 0
|
|
2982
|
+
|
|
2983
|
+
this.ctx.log.warn('The model declined to answer', {
|
|
2984
|
+
[NAMZU.TURN_ID]: recorder.turnId,
|
|
2985
|
+
[NAMZU.ITERATION]: iterationNum,
|
|
2986
|
+
'namzu.runtime.model': servedBy.model,
|
|
2987
|
+
'namzu.runtime.refusal_category': refusal.category,
|
|
2988
|
+
'namzu.runtime.has_partial_output': hasPartial,
|
|
2989
|
+
})
|
|
2990
|
+
|
|
2991
|
+
const announce = async (outcome: 'fallback' | 'retry' | 'declined', fallbackModel?: string) => {
|
|
2992
|
+
await this.ctx.emitEvent({
|
|
2993
|
+
type: 'model_refusal',
|
|
2994
|
+
turnId: recorder.turnId,
|
|
2995
|
+
iteration: iterationNum,
|
|
2996
|
+
model: servedBy.model,
|
|
2997
|
+
providerId: servedBy.providerId,
|
|
2998
|
+
outcome,
|
|
2999
|
+
...(fallbackModel !== undefined ? { fallbackModel } : {}),
|
|
3000
|
+
...(refusal.category ? { category: refusal.category } : {}),
|
|
3001
|
+
...(refusal.explanation ? { explanation: refusal.explanation } : {}),
|
|
3002
|
+
})
|
|
3003
|
+
}
|
|
3004
|
+
const closeIteration = async () => {
|
|
3005
|
+
this.recordStep({
|
|
3006
|
+
stepNumber: iterationNum,
|
|
3007
|
+
model: input.stepModel,
|
|
3008
|
+
servedBy,
|
|
3009
|
+
messageId: input.messageId,
|
|
3010
|
+
response,
|
|
3011
|
+
toolResults: [],
|
|
3012
|
+
toolExecutionMs: 0,
|
|
3013
|
+
startedAt: input.stepStartedAt,
|
|
3014
|
+
usageBefore: input.usageBefore,
|
|
3015
|
+
costBefore: input.costBefore,
|
|
3016
|
+
})
|
|
3017
|
+
await this.ctx.emitEvent({
|
|
3018
|
+
type: 'iteration_completed',
|
|
3019
|
+
turnId: recorder.turnId,
|
|
3020
|
+
iteration: iterationNum,
|
|
3021
|
+
hasToolCalls: false,
|
|
3022
|
+
})
|
|
3023
|
+
}
|
|
3024
|
+
|
|
3025
|
+
if (this.ctx.abortController.signal.aborted) {
|
|
3026
|
+
recorder.setStopReason('cancelled')
|
|
3027
|
+
recorder.markCancelled()
|
|
3028
|
+
await closeIteration()
|
|
3029
|
+
yield* this.ctx.drainPending()
|
|
3030
|
+
return 'settled'
|
|
3031
|
+
}
|
|
3032
|
+
|
|
3033
|
+
if (!input.forceFinalize && !this.refusalFallbackUsed && this.ctx.refusalFallback !== 'off') {
|
|
3034
|
+
const next = await this.refusalFallbackModel(input.stepModel, refusal)
|
|
3035
|
+
if (next !== undefined) {
|
|
3036
|
+
this.refusalFallbackUsed = true
|
|
3037
|
+
this.refusalPinnedModel = next
|
|
3038
|
+
await announce('fallback', next)
|
|
3039
|
+
await closeIteration()
|
|
3040
|
+
yield* this.ctx.drainPending()
|
|
3041
|
+
return 'retry'
|
|
3042
|
+
}
|
|
3043
|
+
}
|
|
3044
|
+
|
|
3045
|
+
if (!input.forceFinalize && !this.refusalRetryUsed && hasPartial) {
|
|
3046
|
+
this.refusalRetryUsed = true
|
|
3047
|
+
recorder.pushMessage(
|
|
3048
|
+
withStreamedMessageIdentity(
|
|
3049
|
+
createAssistantMessage(
|
|
3050
|
+
response.message.content,
|
|
3051
|
+
toolCalls.length > 0 ? toolCalls : undefined,
|
|
3052
|
+
response.message.reasoning,
|
|
3053
|
+
response.message.citations,
|
|
3054
|
+
{
|
|
3055
|
+
type: 'model',
|
|
3056
|
+
...servedBy,
|
|
3057
|
+
...(response.message.replayState !== undefined
|
|
3058
|
+
? { replayState: response.message.replayState }
|
|
3059
|
+
: {}),
|
|
3060
|
+
},
|
|
3061
|
+
response.message.textParts,
|
|
3062
|
+
),
|
|
3063
|
+
input.messageId,
|
|
3064
|
+
),
|
|
3065
|
+
)
|
|
3066
|
+
for (const call of toolCalls) {
|
|
3067
|
+
recorder.pushMessage(createToolMessage(REFUSED_REPLY_CALL_RESULT, call.id, true))
|
|
3068
|
+
}
|
|
3069
|
+
recorder.pushMessage(
|
|
3070
|
+
createRuntimeContextMessage(REFUSED_REPLY_RETRY_MESSAGE, 'refusal-retry'),
|
|
3071
|
+
)
|
|
3072
|
+
await announce('retry')
|
|
3073
|
+
await closeIteration()
|
|
3074
|
+
yield* this.ctx.drainPending()
|
|
3075
|
+
return 'retry'
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
recorder.setLastError(
|
|
3079
|
+
`${servedBy.model} declined to answer this message${refusal.explanation ? `: ${refusal.explanation}` : '.'}`,
|
|
3080
|
+
)
|
|
3081
|
+
// A closing step that a limit asked for keeps that limit as the reason
|
|
3082
|
+
// the turn ended; the refusal is still announced.
|
|
3083
|
+
recorder.setStopReason(input.limitStopReason ?? 'refused')
|
|
3084
|
+
await announce('declined')
|
|
3085
|
+
await closeIteration()
|
|
3086
|
+
yield* this.ctx.drainPending()
|
|
3087
|
+
return 'settled'
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
/**
|
|
3091
|
+
* Another model to try after `model` declined, or `undefined` when there is
|
|
3092
|
+
* none. Only the provider can say: it names its own recommendation or its
|
|
3093
|
+
* own declared order, checked against what this credential can serve. A
|
|
3094
|
+
* chain member declared with a model of its own cannot be swapped, because
|
|
3095
|
+
* the chain would put its own model back on the request.
|
|
3096
|
+
*/
|
|
3097
|
+
private async refusalFallbackModel(
|
|
3098
|
+
model: string,
|
|
3099
|
+
refusal: NonNullable<ChatCompletionResponse['refusal']>,
|
|
3100
|
+
): Promise<string | undefined> {
|
|
3101
|
+
if (this.ctx.servingMember?.()?.model !== undefined) return undefined
|
|
3102
|
+
const pick = this.ctx.provider.refusalFallbackFor
|
|
3103
|
+
if (!pick) return undefined
|
|
3104
|
+
try {
|
|
3105
|
+
const next = await pick.call(
|
|
3106
|
+
this.ctx.provider,
|
|
3107
|
+
model,
|
|
3108
|
+
refusal,
|
|
3109
|
+
this.ctx.abortController.signal,
|
|
3110
|
+
)
|
|
3111
|
+
return next !== undefined && next.length > 0 && next !== model ? next : undefined
|
|
3112
|
+
} catch (err) {
|
|
3113
|
+
this.ctx.log.warn('The provider could not name a model to retry a refusal on', {
|
|
3114
|
+
'exception.message': toErrorMessage(err),
|
|
3115
|
+
})
|
|
3116
|
+
return undefined
|
|
3117
|
+
}
|
|
3118
|
+
}
|
|
3119
|
+
|
|
2876
3120
|
private async requestFinalResponse(
|
|
2877
3121
|
model: string,
|
|
2878
3122
|
reason: StopReason,
|
|
@@ -2980,6 +3224,16 @@ export class IterationOrchestrator {
|
|
|
2980
3224
|
model: servedBy.model,
|
|
2981
3225
|
})
|
|
2982
3226
|
|
|
3227
|
+
// A closing request the provider declined has nothing to add, and its
|
|
3228
|
+
// empty reply must not be stored or sent again.
|
|
3229
|
+
if (response.finishReason === 'content_filter') {
|
|
3230
|
+
this.ctx.log.warn('The closing response was declined by the model', {
|
|
3231
|
+
[NAMZU.TURN_ID]: this.ctx.recorder.turnId,
|
|
3232
|
+
'namzu.runtime.model': servedBy.model,
|
|
3233
|
+
})
|
|
3234
|
+
return undefined
|
|
3235
|
+
}
|
|
3236
|
+
|
|
2983
3237
|
const finalMessageId = generateMessageId()
|
|
2984
3238
|
const assistantMsg = withStreamedMessageIdentity(
|
|
2985
3239
|
createAssistantMessage(
|
|
@@ -5,6 +5,7 @@ import type { TaskHandle } from '../../../types/agent/scheduler.js'
|
|
|
5
5
|
import { createRuntimeContextMessage } from '../../../types/message/index.js'
|
|
6
6
|
import type { SessionEvent } from '../../../types/session/index.js'
|
|
7
7
|
import { readPositiveIntEnv } from '../../../utils/env.js'
|
|
8
|
+
import { formatLateResults } from '../detached-calls.js'
|
|
8
9
|
import { formatJobNote, formatMonitorNote } from '../steering.js'
|
|
9
10
|
import type { IterationContext } from './phases/index.js'
|
|
10
11
|
|
|
@@ -165,7 +166,8 @@ export async function* holdForOutstandingWork(
|
|
|
165
166
|
): AsyncGenerator<SessionEvent, boolean> {
|
|
166
167
|
const inbox = ctx.completionInbox?.holdsTurn ? ctx.completionInbox : undefined
|
|
167
168
|
const jobs = ctx.awaitedJobs?.hasPendingWork ? ctx.awaitedJobs : undefined
|
|
168
|
-
|
|
169
|
+
const detached = ctx.detachedCalls?.hasWork ? ctx.detachedCalls : undefined
|
|
170
|
+
if (!inbox && !jobs && !detached) return false
|
|
169
171
|
|
|
170
172
|
// Read HERE rather than from `forceFinalize`, which was sampled at the
|
|
171
173
|
// top of the iteration: one that has since crossed the finalize point
|
|
@@ -179,7 +181,7 @@ export async function* holdForOutstandingWork(
|
|
|
179
181
|
// to be running. A job therefore never shortens a wait, and it never
|
|
180
182
|
// lengthens one either: where a task is outstanding too, that is how
|
|
181
183
|
// long this turn was waiting anyway.
|
|
182
|
-
let graceMs = inbox ? settleGraceMs(remainingMs) : awaitedJobGraceMs(remainingMs)
|
|
184
|
+
let graceMs = inbox || detached ? settleGraceMs(remainingMs) : awaitedJobGraceMs(remainingMs)
|
|
183
185
|
// A monitor the turn owns is held for until the monitor itself ends, which
|
|
184
186
|
// its own deadline bounds; the two-minute job ceiling would walk away from
|
|
185
187
|
// a watch with most of its time left. As with a task, this only ever
|
|
@@ -207,6 +209,7 @@ export async function* holdForOutstandingWork(
|
|
|
207
209
|
await Promise.race([
|
|
208
210
|
...(inbox ? [inbox.waitForArrival(graceMs, waiting.signal)] : []),
|
|
209
211
|
...(jobs ? [jobs.waitForArrival(graceMs, waiting.signal)] : []),
|
|
212
|
+
...(detached ? [detached.waitForArrival(graceMs, waiting.signal)] : []),
|
|
210
213
|
...(ctx.waitForInbound ? [ctx.waitForInbound(waiting.signal)] : []),
|
|
211
214
|
...(ctx.durableInbound?.wait ? [ctx.durableInbound.wait(waiting.signal)] : []),
|
|
212
215
|
])
|
|
@@ -228,8 +231,14 @@ export async function* holdForOutstandingWork(
|
|
|
228
231
|
}
|
|
229
232
|
const exited = deliverAwaitedJobExits(ctx)
|
|
230
233
|
const monitored = deliverMonitorNotices(ctx)
|
|
234
|
+
const late = ctx.detachedCalls?.take() ?? []
|
|
235
|
+
if (late.length > 0) {
|
|
236
|
+
ctx.recorder.pushMessage(createRuntimeContextMessage(formatLateResults(late), 'job-exit'))
|
|
237
|
+
}
|
|
231
238
|
const inbound = await deliverInbound()
|
|
232
|
-
if (arrived.length === 0 && !exited && !monitored && inbound === 0)
|
|
239
|
+
if (arrived.length === 0 && !exited && !monitored && late.length === 0 && inbound === 0) {
|
|
240
|
+
return false
|
|
241
|
+
}
|
|
233
242
|
await ctx.emitEvent({
|
|
234
243
|
type: 'iteration_completed',
|
|
235
244
|
turnId: ctx.recorder.turnId,
|
|
@@ -40,6 +40,7 @@ import type { TaskStore } from '../../../../types/task/index.js'
|
|
|
40
40
|
import type { Logger } from '../../../../utils/logger.js'
|
|
41
41
|
import type { AwaitedJobs } from '../../../jobs/awaited-jobs.js'
|
|
42
42
|
import type { CheckpointManager } from '../../checkpoint.js'
|
|
43
|
+
import type { DetachedCalls } from '../../detached-calls.js'
|
|
43
44
|
import type { EmitEvent } from '../../events.js'
|
|
44
45
|
import type { ToolExecutor } from '../../executor.js'
|
|
45
46
|
import type { GuardCoordinator } from '../../guard.js'
|
|
@@ -171,6 +172,12 @@ export interface IterationContext {
|
|
|
171
172
|
* and a turn whose model stopped calling tools settles without waiting.
|
|
172
173
|
*/
|
|
173
174
|
readonly awaitedJobs?: AwaitedJobs
|
|
175
|
+
/**
|
|
176
|
+
* Calls answered with a placeholder because the operator wrote while they
|
|
177
|
+
* ran. Absent means no call is ever handed back and the operator's message
|
|
178
|
+
* waits for the batch, as it always has.
|
|
179
|
+
*/
|
|
180
|
+
readonly detachedCalls?: DetachedCalls
|
|
174
181
|
readonly checkpointMgr: CheckpointManager
|
|
175
182
|
readonly planManager: PlanManager
|
|
176
183
|
|
|
@@ -217,6 +224,8 @@ export interface IterationContext {
|
|
|
217
224
|
readonly compactionConfig?: CompactionConfig
|
|
218
225
|
/** See `QueryParams.maxOutputContinuations`. */
|
|
219
226
|
readonly maxOutputContinuations?: number
|
|
227
|
+
/** See `QueryParams.refusalFallback`. */
|
|
228
|
+
readonly refusalFallback?: 'auto' | 'off'
|
|
220
229
|
|
|
221
230
|
/**
|
|
222
231
|
* What the driver said this model's context window is, resolved once.
|
|
@@ -7,10 +7,12 @@ import type {
|
|
|
7
7
|
ToolCallDeclined,
|
|
8
8
|
ToolCallSummary,
|
|
9
9
|
} from '../../../../types/hitl/index.js'
|
|
10
|
+
import { createRuntimeContextMessage } from '../../../../types/message/index.js'
|
|
10
11
|
import type { ChatCompletionResponse } from '../../../../types/provider/index.js'
|
|
11
12
|
import type { SessionEvent } from '../../../../types/session/index.js'
|
|
12
13
|
import type { ShellDialect } from '../../../../types/tool/index.js'
|
|
13
14
|
import { DECLINED_TOOL_CALL_FEEDBACK } from '../../declined.js'
|
|
15
|
+
import { type BatchDetach, DETACH_AFTER_MS, formatLateResults } from '../../detached-calls.js'
|
|
14
16
|
import type {
|
|
15
17
|
PreparedToolBatch,
|
|
16
18
|
ReviewedExecution,
|
|
@@ -65,6 +67,60 @@ function hookAskedReason(reason: string | undefined): string {
|
|
|
65
67
|
: 'a hook asked that a person approve this call'
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
/**
|
|
71
|
+
* Watch for the operator writing while this batch runs.
|
|
72
|
+
*
|
|
73
|
+
* The batch is handed back once a message has arrived and the batch has run for
|
|
74
|
+
* {@link DETACH_AFTER_MS}; a short call finishes first and nothing changes. The
|
|
75
|
+
* arrival signal is the host's `waitForOperator`, which fires for operator text
|
|
76
|
+
* only, so a host that supplies none never detaches a call and a notice from a
|
|
77
|
+
* child or a peer never does.
|
|
78
|
+
*/
|
|
79
|
+
function watchForOperator(
|
|
80
|
+
ctx: IterationContext,
|
|
81
|
+
startedAt: number,
|
|
82
|
+
): { readonly detach: BatchDetach; stop(): void } | undefined {
|
|
83
|
+
const calls = ctx.detachedCalls
|
|
84
|
+
const wakes = calls?.waitForOperator ? [calls.waitForOperator] : []
|
|
85
|
+
if (!calls || wakes.length === 0) return undefined
|
|
86
|
+
const watching = new AbortController()
|
|
87
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
88
|
+
const requested = new Promise<void>((resolve) => {
|
|
89
|
+
const settled = Promise.race(
|
|
90
|
+
wakes.map((wake) => {
|
|
91
|
+
try {
|
|
92
|
+
return wake(watching.signal)
|
|
93
|
+
} catch {
|
|
94
|
+
return new Promise<void>(() => {})
|
|
95
|
+
}
|
|
96
|
+
}),
|
|
97
|
+
)
|
|
98
|
+
settled.then(
|
|
99
|
+
() => {
|
|
100
|
+
if (watching.signal.aborted) return
|
|
101
|
+
const wait = Math.max(0, startedAt + DETACH_AFTER_MS - Date.now())
|
|
102
|
+
timer = setTimeout(resolve, wait)
|
|
103
|
+
},
|
|
104
|
+
// An aborted wait is the batch ending, which is not a message.
|
|
105
|
+
() => {},
|
|
106
|
+
)
|
|
107
|
+
})
|
|
108
|
+
return {
|
|
109
|
+
detach: { requested, calls },
|
|
110
|
+
stop() {
|
|
111
|
+
watching.abort()
|
|
112
|
+
if (timer !== undefined) clearTimeout(timer)
|
|
113
|
+
},
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The outcomes of calls that were handed back as still running and have since finished. */
|
|
118
|
+
function takeLateResults(ctx: IterationContext) {
|
|
119
|
+
const outcomes = ctx.detachedCalls?.take()
|
|
120
|
+
if (!outcomes || outcomes.length === 0) return undefined
|
|
121
|
+
return createRuntimeContextMessage(formatLateResults(outcomes), 'job-exit')
|
|
122
|
+
}
|
|
123
|
+
|
|
68
124
|
export type ToolReviewDecision = 'executed' | 'rejected' | 'stop'
|
|
69
125
|
|
|
70
126
|
/**
|
|
@@ -290,14 +346,21 @@ export async function* runToolReview(
|
|
|
290
346
|
): Promise<void> => {
|
|
291
347
|
await announceRefusals(denials)
|
|
292
348
|
const startedAt = Date.now()
|
|
293
|
-
const
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
349
|
+
const watch = watchForOperator(ctx, startedAt)
|
|
350
|
+
let batch: Awaited<ReturnType<typeof ctx.toolExecutor.executeBatch>>
|
|
351
|
+
try {
|
|
352
|
+
batch = await ctx.toolExecutor.executeBatch(
|
|
353
|
+
response,
|
|
354
|
+
withRepeatRefusals(denials),
|
|
355
|
+
undefined,
|
|
356
|
+
preparedBatch,
|
|
357
|
+
declines,
|
|
358
|
+
review,
|
|
359
|
+
watch?.detach,
|
|
360
|
+
)
|
|
361
|
+
} finally {
|
|
362
|
+
watch?.stop()
|
|
363
|
+
}
|
|
301
364
|
toolMs += Date.now() - startedAt
|
|
302
365
|
executed = batch.results
|
|
303
366
|
await announceBatch(batch.results)
|
|
@@ -369,6 +432,8 @@ export async function* runToolReview(
|
|
|
369
432
|
},
|
|
370
433
|
)
|
|
371
434
|
if (monitorNotice) ctx.recorder.pushMessage(monitorNotice)
|
|
435
|
+
const lateResults = takeLateResults(ctx)
|
|
436
|
+
if (lateResults) ctx.recorder.pushMessage(lateResults)
|
|
372
437
|
const credentialNotice = takeWithheldEnvNotice(ctx.recorder.messages, batch.observations)
|
|
373
438
|
if (credentialNotice) ctx.recorder.pushMessage(credentialNotice)
|
|
374
439
|
if (jobNoticeDelivered) ctx.onJobNoticeDelivered?.()
|
|
@@ -284,6 +284,8 @@ export async function* streamProviderTurn(
|
|
|
284
284
|
let reportedFinishReason: ChatCompletionResponse['finishReason'] | undefined
|
|
285
285
|
// Which limit a 'length' finish reached, when it was the context window.
|
|
286
286
|
let finishDetail: ChatCompletionResponse['finishDetail']
|
|
287
|
+
// What the provider said about a refusal, kept with the finish that carried it.
|
|
288
|
+
let refusal: ChatCompletionResponse['refusal']
|
|
287
289
|
let usage: ChatCompletionResponse['usage'] = {
|
|
288
290
|
promptTokens: 0,
|
|
289
291
|
completionTokens: 0,
|
|
@@ -416,6 +418,7 @@ export async function* streamProviderTurn(
|
|
|
416
418
|
finishReason = 'stop'
|
|
417
419
|
reportedFinishReason = undefined
|
|
418
420
|
finishDetail = undefined
|
|
421
|
+
refusal = undefined
|
|
419
422
|
streamedLength = 0
|
|
420
423
|
lastOutputCall = undefined
|
|
421
424
|
usage = {
|
|
@@ -766,6 +769,7 @@ export async function* streamProviderTurn(
|
|
|
766
769
|
reportedFinishReason = chunk.finishReason
|
|
767
770
|
finishDetail = chunk.finishReason === 'length' ? chunk.finishDetail : undefined
|
|
768
771
|
}
|
|
772
|
+
if (chunk.refusal) refusal = chunk.refusal
|
|
769
773
|
// Merge (per-field max), not last-write-wins: a late usage frame that
|
|
770
774
|
// omits input/cache tokens must not zero the counts seen earlier in the
|
|
771
775
|
// stream, which would under-report this turn's accumulated usage.
|
|
@@ -1061,6 +1065,7 @@ export async function* streamProviderTurn(
|
|
|
1061
1065
|
},
|
|
1062
1066
|
finishReason: effectiveFinishReason,
|
|
1063
1067
|
...(effectiveFinishReason === 'length' && finishDetail ? { finishDetail } : {}),
|
|
1068
|
+
...(effectiveFinishReason === 'content_filter' && refusal ? { refusal } : {}),
|
|
1064
1069
|
usage,
|
|
1065
1070
|
}
|
|
1066
1071
|
|
|
@@ -273,9 +273,14 @@ export function checkpointLogView(log: SessionLog): CheckpointLogView {
|
|
|
273
273
|
return {
|
|
274
274
|
async verifyThrough(scope, throughSeq, throughSha256) {
|
|
275
275
|
own(scope)
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
// Stream the walk and keep only the last pointer: the chain is
|
|
277
|
+
// verified as it is read, and a long session must not be held in
|
|
278
|
+
// memory just to look at its final entry.
|
|
279
|
+
let last: { seq: number; sha256: string } | undefined
|
|
280
|
+
for await (const entry of log.read({ mode: 'tolerant', throughSeq })) {
|
|
281
|
+
last = entry.pointer
|
|
282
|
+
}
|
|
283
|
+
return last?.seq === throughSeq && last.sha256 === throughSha256
|
|
279
284
|
},
|
|
280
285
|
async writtenDocSha256(scope, checkpointId) {
|
|
281
286
|
own(scope)
|