@namzu/sdk 31.1.0 → 33.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +188 -0
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +4 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +4 -0
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/runAgent.d.ts +4 -0
- package/dist/agents/runAgent.d.ts.map +1 -1
- package/dist/agents/runAgent.js +4 -0
- package/dist/agents/runAgent.js.map +1 -1
- package/dist/authorization/gate.d.ts +1 -1
- package/dist/authorization/gate.js +1 -1
- package/dist/connector/index.d.ts +1 -1
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +1 -1
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +71 -16
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/connector/mcp/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +3 -2
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/http-redirect.d.ts +17 -0
- package/dist/connector/mcp/http-redirect.d.ts.map +1 -0
- package/dist/connector/mcp/http-redirect.js +31 -0
- package/dist/connector/mcp/http-redirect.js.map +1 -0
- package/dist/connector/mcp/http-sse.d.ts.map +1 -1
- package/dist/connector/mcp/http-sse.js +5 -0
- package/dist/connector/mcp/http-sse.js.map +1 -1
- package/dist/connector/mcp/image-admission.d.ts +16 -0
- package/dist/connector/mcp/image-admission.d.ts.map +1 -0
- package/dist/connector/mcp/image-admission.js +288 -0
- package/dist/connector/mcp/image-admission.js.map +1 -0
- package/dist/connector/mcp/stdio.d.ts +13 -0
- package/dist/connector/mcp/stdio.d.ts.map +1 -1
- package/dist/connector/mcp/stdio.js +66 -23
- package/dist/connector/mcp/stdio.js.map +1 -1
- package/dist/connector/mcp/streamable-http.d.ts.map +1 -1
- package/dist/connector/mcp/streamable-http.js +3 -0
- package/dist/connector/mcp/streamable-http.js.map +1 -1
- package/dist/constants/tools/index.js +1 -1
- package/dist/constants/tools/index.js.map +1 -1
- package/dist/directory/derive-supervisor.d.ts.map +1 -1
- package/dist/directory/derive-supervisor.js +5 -0
- package/dist/directory/derive-supervisor.js.map +1 -1
- package/dist/directory/derive.d.ts.map +1 -1
- package/dist/directory/derive.js +5 -0
- package/dist/directory/derive.js.map +1 -1
- package/dist/directory/load.d.ts.map +1 -1
- package/dist/directory/load.js +1 -0
- package/dist/directory/load.js.map +1 -1
- package/dist/directory/types.d.ts +2 -0
- package/dist/directory/types.d.ts.map +1 -1
- package/dist/execution/base.d.ts +13 -0
- package/dist/execution/base.d.ts.map +1 -1
- package/dist/execution/base.js +103 -8
- package/dist/execution/base.js.map +1 -1
- package/dist/execution/code-runtime/types.d.ts +11 -1
- package/dist/execution/code-runtime/types.d.ts.map +1 -1
- package/dist/execution/code-runtime/types.js.map +1 -1
- package/dist/execution/code-runtime/worker.d.ts.map +1 -1
- package/dist/execution/code-runtime/worker.js +67 -9
- package/dist/execution/code-runtime/worker.js.map +1 -1
- package/dist/execution/errors.d.ts +12 -0
- package/dist/execution/errors.d.ts.map +1 -0
- package/dist/execution/errors.js +21 -0
- package/dist/execution/errors.js.map +1 -0
- package/dist/execution/factory.d.ts.map +1 -1
- package/dist/execution/factory.js +1 -0
- package/dist/execution/factory.js.map +1 -1
- package/dist/execution/hybrid.d.ts +5 -0
- package/dist/execution/hybrid.d.ts.map +1 -1
- package/dist/execution/hybrid.js +36 -16
- package/dist/execution/hybrid.js.map +1 -1
- package/dist/execution/index.d.ts +1 -0
- package/dist/execution/index.d.ts.map +1 -1
- package/dist/execution/index.js +1 -0
- package/dist/execution/index.js.map +1 -1
- package/dist/execution/local.d.ts +13 -0
- package/dist/execution/local.d.ts.map +1 -1
- package/dist/execution/local.js +145 -19
- package/dist/execution/local.js.map +1 -1
- package/dist/execution/output.d.ts +27 -0
- package/dist/execution/output.d.ts.map +1 -0
- package/dist/execution/output.js +105 -0
- package/dist/execution/output.js.map +1 -0
- package/dist/execution/remote.d.ts +26 -2
- package/dist/execution/remote.d.ts.map +1 -1
- package/dist/execution/remote.js +86 -15
- package/dist/execution/remote.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +1 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +9 -3
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/process/kill-tree.d.ts +11 -13
- package/dist/process/kill-tree.d.ts.map +1 -1
- package/dist/process/kill-tree.js +11 -13
- package/dist/process/kill-tree.js.map +1 -1
- package/dist/provider/errors.d.ts +1 -0
- package/dist/provider/errors.d.ts.map +1 -1
- package/dist/provider/errors.js +39 -1
- package/dist/provider/errors.js.map +1 -1
- package/dist/provider/fallback.d.ts.map +1 -1
- package/dist/provider/fallback.js +31 -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/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 +7 -5
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +9 -4
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-types.d.ts +2 -1
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/tool/execute.d.ts +7 -1
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +201 -54
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/run/command-gate.d.ts.map +1 -1
- package/dist/run/command-gate.js +11 -0
- package/dist/run/command-gate.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +6 -1
- package/dist/run/reporter.js.map +1 -1
- package/dist/run/workspace-fingerprint.d.ts +4 -4
- package/dist/run/workspace-fingerprint.d.ts.map +1 -1
- package/dist/run/workspace-fingerprint.js +6 -4
- package/dist/run/workspace-fingerprint.js.map +1 -1
- package/dist/runtime/bidi/index.d.ts +1 -1
- package/dist/runtime/bidi/index.d.ts.map +1 -1
- package/dist/runtime/bidi/index.js +1 -1
- package/dist/runtime/bidi/index.js.map +1 -1
- package/dist/runtime/bidi/session.d.ts +19 -0
- package/dist/runtime/bidi/session.d.ts.map +1 -1
- package/dist/runtime/bidi/session.js +150 -26
- package/dist/runtime/bidi/session.js.map +1 -1
- package/dist/runtime/query/executor.d.ts +43 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +824 -174
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +5 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +47 -4
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +1 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +34 -14
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/advisory.js +2 -2
- package/dist/runtime/query/iteration/phases/advisory.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 +84 -22
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts +13 -0
- package/dist/runtime/query/iteration/provider-rejected-image.d.ts.map +1 -0
- package/dist/runtime/query/iteration/provider-rejected-image.js +91 -0
- package/dist/runtime/query/iteration/provider-rejected-image.js.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts +4 -1
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -1
- package/dist/runtime/query/iteration/stream-turn.js +7 -3
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -1
- package/dist/runtime/query/request-rich-content.d.ts +13 -0
- package/dist/runtime/query/request-rich-content.d.ts.map +1 -1
- package/dist/runtime/query/request-rich-content.js +173 -17
- package/dist/runtime/query/request-rich-content.js.map +1 -1
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js +1 -0
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/runtime/query/resume-pending.d.ts +5 -1
- package/dist/runtime/query/resume-pending.d.ts.map +1 -1
- package/dist/runtime/query/resume-pending.js +66 -1
- package/dist/runtime/query/resume-pending.js.map +1 -1
- package/dist/runtime/query/tool-output-budget.d.ts.map +1 -1
- package/dist/runtime/query/tool-output-budget.js +45 -16
- package/dist/runtime/query/tool-output-budget.js.map +1 -1
- package/dist/runtime/query/tooling.d.ts +6 -0
- package/dist/runtime/query/tooling.d.ts.map +1 -1
- package/dist/runtime/query/tooling.js +4 -0
- package/dist/runtime/query/tooling.js.map +1 -1
- package/dist/sandbox/provider/local.d.ts.map +1 -1
- package/dist/sandbox/provider/local.js +193 -41
- package/dist/sandbox/provider/local.js.map +1 -1
- package/dist/scheduler/delegating.d.ts +2 -1
- package/dist/scheduler/delegating.d.ts.map +1 -1
- package/dist/scheduler/delegating.js +6 -5
- package/dist/scheduler/delegating.js.map +1 -1
- package/dist/scheduler/local.d.ts +2 -1
- package/dist/scheduler/local.d.ts.map +1 -1
- package/dist/scheduler/local.js +2 -2
- package/dist/scheduler/local.js.map +1 -1
- package/dist/session/workspace/driver.d.ts +12 -4
- package/dist/session/workspace/driver.d.ts.map +1 -1
- package/dist/session/workspace/git-worktree.d.ts +17 -3
- package/dist/session/workspace/git-worktree.d.ts.map +1 -1
- package/dist/session/workspace/git-worktree.js +153 -34
- package/dist/session/workspace/git-worktree.js.map +1 -1
- package/dist/skills/registry.d.ts +15 -0
- package/dist/skills/registry.d.ts.map +1 -1
- package/dist/skills/registry.js +33 -0
- package/dist/skills/registry.js.map +1 -1
- package/dist/store/attachment/index.d.ts +24 -2
- package/dist/store/attachment/index.d.ts.map +1 -1
- package/dist/store/attachment/index.js +52 -5
- package/dist/store/attachment/index.js.map +1 -1
- package/dist/tools/builtins/computer-use.d.ts.map +1 -1
- package/dist/tools/builtins/computer-use.js +39 -2
- package/dist/tools/builtins/computer-use.js.map +1 -1
- package/dist/tools/builtins/lsp.d.ts +1 -1
- package/dist/tools/builtins/run-code.d.ts.map +1 -1
- package/dist/tools/builtins/run-code.js +24 -16
- package/dist/tools/builtins/run-code.js.map +1 -1
- package/dist/tools/builtins/skill.d.ts +2 -1
- package/dist/tools/builtins/skill.d.ts.map +1 -1
- package/dist/tools/builtins/skill.js +250 -6
- package/dist/tools/builtins/skill.js.map +1 -1
- package/dist/tools/coordinator/agent.d.ts.map +1 -1
- package/dist/tools/coordinator/agent.js +100 -30
- package/dist/tools/coordinator/agent.js.map +1 -1
- package/dist/types/agent/base.d.ts +4 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/factory.d.ts +1 -0
- package/dist/types/agent/factory.d.ts.map +1 -1
- package/dist/types/agent/manager.d.ts +1 -1
- package/dist/types/agent/manager.d.ts.map +1 -1
- package/dist/types/agent/scheduler.d.ts +10 -1
- package/dist/types/agent/scheduler.d.ts.map +1 -1
- package/dist/types/authorization/index.d.ts +12 -12
- package/dist/types/bidi/index.d.ts +19 -2
- package/dist/types/bidi/index.d.ts.map +1 -1
- package/dist/types/computer-use/index.d.ts +18 -0
- package/dist/types/computer-use/index.d.ts.map +1 -1
- package/dist/types/connector/execution-context.d.ts +3 -1
- package/dist/types/connector/execution-context.d.ts.map +1 -1
- package/dist/types/execution/index.d.ts +43 -1
- package/dist/types/execution/index.d.ts.map +1 -1
- package/dist/types/hitl/index.d.ts +11 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/message/index.d.ts +31 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +43 -0
- package/dist/types/message/index.js.map +1 -1
- package/dist/types/plugin/index.d.ts +14 -1
- package/dist/types/plugin/index.d.ts.map +1 -1
- package/dist/types/plugin/index.js +1 -0
- package/dist/types/plugin/index.js.map +1 -1
- package/dist/types/provider/error.d.ts +4 -1
- package/dist/types/provider/error.d.ts.map +1 -1
- package/dist/types/provider/interface.d.ts +14 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +10 -2
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/sandbox/index.d.ts +15 -10
- package/dist/types/sandbox/index.d.ts.map +1 -1
- package/dist/types/sandbox/index.js.map +1 -1
- package/dist/types/tool/index.d.ts +129 -12
- package/dist/types/tool/index.d.ts.map +1 -1
- package/dist/types/tool/index.js.map +1 -1
- package/dist/types/workspace/ref.d.ts +5 -0
- package/dist/types/workspace/ref.d.ts.map +1 -1
- package/dist/utils/abort.d.ts +8 -0
- package/dist/utils/abort.d.ts.map +1 -1
- package/dist/utils/abort.js +16 -0
- package/dist/utils/abort.js.map +1 -1
- package/package.json +4 -4
- package/src/agents/ReactiveAgent.ts +4 -0
- package/src/agents/SupervisorAgent.ts +4 -0
- package/src/agents/runAgent.ts +8 -0
- package/src/authorization/gate.ts +1 -1
- package/src/connector/index.ts +2 -0
- package/src/connector/mcp/adapter.ts +82 -21
- package/src/connector/mcp/client.ts +2 -1
- package/src/connector/mcp/http-redirect.ts +32 -0
- package/src/connector/mcp/http-sse.ts +5 -0
- package/src/connector/mcp/image-admission.ts +272 -0
- package/src/connector/mcp/stdio.ts +63 -21
- package/src/connector/mcp/streamable-http.ts +3 -0
- package/src/constants/tools/index.ts +1 -1
- package/src/directory/derive-supervisor.ts +5 -0
- package/src/directory/derive.ts +5 -0
- package/src/directory/load.ts +1 -0
- package/src/directory/types.ts +2 -0
- package/src/execution/base.ts +124 -7
- package/src/execution/code-runtime/types.ts +15 -1
- package/src/execution/code-runtime/worker.ts +72 -9
- package/src/execution/errors.ts +24 -0
- package/src/execution/factory.ts +1 -0
- package/src/execution/hybrid.ts +47 -16
- package/src/execution/index.ts +1 -0
- package/src/execution/local.ts +164 -19
- package/src/execution/output.ts +104 -0
- package/src/execution/remote.ts +122 -17
- package/src/manager/agent/lifecycle.ts +1 -0
- package/src/plugin/lifecycle.ts +9 -3
- package/src/process/kill-tree.ts +11 -13
- package/src/provider/errors.ts +40 -1
- package/src/provider/fallback.ts +39 -0
- package/src/provider/idle-timeout.ts +8 -0
- package/src/provider/retry.ts +8 -0
- package/src/public-runtime.ts +23 -2
- package/src/public-types.ts +11 -0
- package/src/registry/tool/execute.ts +230 -64
- package/src/run/command-gate.ts +12 -0
- package/src/run/reporter.ts +7 -1
- package/src/run/workspace-fingerprint.ts +5 -4
- package/src/runtime/bidi/index.ts +1 -1
- package/src/runtime/bidi/session.ts +177 -28
- package/src/runtime/query/executor.ts +987 -172
- package/src/runtime/query/index.ts +59 -6
- package/src/runtime/query/iteration/index.ts +69 -29
- package/src/runtime/query/iteration/phases/advisory.ts +2 -2
- package/src/runtime/query/iteration/phases/tool-review.ts +87 -23
- package/src/runtime/query/iteration/provider-rejected-image.ts +106 -0
- package/src/runtime/query/iteration/stream-turn.ts +12 -2
- package/src/runtime/query/request-rich-content.ts +213 -15
- package/src/runtime/query/result.ts +1 -0
- package/src/runtime/query/resume-pending.ts +83 -1
- package/src/runtime/query/tool-output-budget.ts +52 -17
- package/src/runtime/query/tooling.ts +10 -0
- package/src/sandbox/provider/local.ts +213 -45
- package/src/scheduler/delegating.ts +6 -5
- package/src/scheduler/local.ts +3 -2
- package/src/session/workspace/driver.ts +12 -4
- package/src/session/workspace/git-worktree.ts +173 -34
- package/src/skills/registry.ts +47 -0
- package/src/store/attachment/index.ts +100 -15
- package/src/tools/builtins/computer-use.ts +45 -2
- package/src/tools/builtins/run-code.ts +24 -16
- package/src/tools/builtins/skill.ts +325 -7
- package/src/tools/coordinator/agent.ts +108 -33
- package/src/types/agent/base.ts +4 -0
- package/src/types/agent/factory.ts +1 -0
- package/src/types/agent/manager.ts +1 -1
- package/src/types/agent/scheduler.ts +10 -1
- package/src/types/bidi/index.ts +19 -2
- package/src/types/computer-use/index.ts +19 -0
- package/src/types/connector/execution-context.ts +3 -0
- package/src/types/execution/index.ts +46 -1
- package/src/types/hitl/index.ts +11 -0
- package/src/types/message/index.ts +78 -1
- package/src/types/plugin/index.ts +15 -0
- package/src/types/provider/error.ts +4 -1
- package/src/types/provider/interface.ts +18 -0
- package/src/types/run/events.ts +18 -4
- package/src/types/sandbox/index.ts +15 -10
- package/src/types/tool/index.ts +159 -15
- package/src/types/workspace/ref.ts +5 -0
- package/src/utils/abort.ts +18 -0
|
@@ -22,6 +22,8 @@ import type { MessageStopReason } from '../../../types/run/stop-reason.js'
|
|
|
22
22
|
import { generateMessageId } from '../../../utils/id.js'
|
|
23
23
|
import type { Logger } from '../../../utils/logger.js'
|
|
24
24
|
import type { EmitEvent } from '../events.js'
|
|
25
|
+
import type { RequestImageIdentity } from '../request-rich-content.js'
|
|
26
|
+
import { streamWithProviderRejectedImageRecovery } from './provider-rejected-image.js'
|
|
25
27
|
|
|
26
28
|
/**
|
|
27
29
|
* Map a provider's coarse `finishReason` plus the orchestrator's
|
|
@@ -147,6 +149,9 @@ export async function* streamProviderTurn(
|
|
|
147
149
|
* Optional, so a caller with no use for the id is unchanged.
|
|
148
150
|
*/
|
|
149
151
|
announceAs?: import('../../../types/ids/index.js').MessageId,
|
|
152
|
+
imageRecovery?: {
|
|
153
|
+
readonly onAccepted: (identity: RequestImageIdentity) => Promise<void>
|
|
154
|
+
},
|
|
150
155
|
): AsyncGenerator<RunEvent, StreamingTurnResult> {
|
|
151
156
|
// The `chat {model}` span the GenAI conventions require. There was none:
|
|
152
157
|
// `chatSpanName` existed with zero call sites, so a trace carried no LLM
|
|
@@ -224,10 +229,15 @@ export async function* streamProviderTurn(
|
|
|
224
229
|
let streamError: string | undefined
|
|
225
230
|
let streamCause: unknown
|
|
226
231
|
|
|
227
|
-
const
|
|
232
|
+
const streamParams = {
|
|
228
233
|
...params,
|
|
229
234
|
stream: true,
|
|
230
|
-
}
|
|
235
|
+
} satisfies import('../../../types/provider/index.js').ChatCompletionParams
|
|
236
|
+
const stream = (
|
|
237
|
+
imageRecovery
|
|
238
|
+
? streamWithProviderRejectedImageRecovery(provider, streamParams, imageRecovery.onAccepted)
|
|
239
|
+
: provider.chatStream(streamParams)
|
|
240
|
+
) as AsyncIterable<StreamChunk>
|
|
231
241
|
|
|
232
242
|
// Drive the stream manually so each `.next()` can be RACED against the run
|
|
233
243
|
// abort: a Stop tears the in-flight model request down (the provider got
|
|
@@ -34,6 +34,12 @@ interface RichOccurrence {
|
|
|
34
34
|
readonly source: 'user' | 'tool'
|
|
35
35
|
readonly kind: RichKind
|
|
36
36
|
readonly bytes: number
|
|
37
|
+
readonly reason: 'budget' | 'provider-rejected' | 'invalid-image'
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface RequestImageIdentity {
|
|
41
|
+
readonly data: string
|
|
42
|
+
readonly mediaType: string
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
const userMarker = (kind: RichKind): string =>
|
|
@@ -42,6 +48,18 @@ const userMarker = (kind: RichKind): string =>
|
|
|
42
48
|
const toolMarker = (kind: RichKind): string =>
|
|
43
49
|
`[${kind} omitted from this model request to keep the accumulated rich-content payload within its configured size limit; call the producing tool again if it is still needed.]`
|
|
44
50
|
|
|
51
|
+
const rejectedUserImageMarker = (): string =>
|
|
52
|
+
'[image omitted from this model request because the provider rejected this image; attach a corrected image in a new message if it is still needed.]'
|
|
53
|
+
|
|
54
|
+
const rejectedToolImageMarker = (): string =>
|
|
55
|
+
'[image omitted from this model request because the provider rejected this image; call the producing tool again after correcting its image source if it is still needed.]'
|
|
56
|
+
|
|
57
|
+
const invalidUserImageMarker = (): string =>
|
|
58
|
+
'[image omitted from this model request because its encoded bytes are not a complete supported raster matching the declared media type; attach a corrected image in a new message if it is still needed.]'
|
|
59
|
+
|
|
60
|
+
const invalidToolImageMarker = (): string =>
|
|
61
|
+
'[image omitted from this model request because the producing tool returned encoded bytes that are not a complete supported raster matching the declared media type; call the tool again after correcting its image source if it is still needed.]'
|
|
62
|
+
|
|
45
63
|
function assertInlineAttachment(
|
|
46
64
|
attachment: MessageAttachment,
|
|
47
65
|
): asserts attachment is Exclude<MessageAttachment, { readonly type: 'stored' }> {
|
|
@@ -65,6 +83,13 @@ function collectRichOccurrences(messages: readonly Message[]): RichOccurrence[]
|
|
|
65
83
|
source: 'user',
|
|
66
84
|
kind: attachment.type === 'document' ? 'document' : 'image',
|
|
67
85
|
bytes: attachment.data.length,
|
|
86
|
+
reason:
|
|
87
|
+
attachment.type !== 'document' && attachment.modelOmission?.reason === 'invalid-image'
|
|
88
|
+
? 'invalid-image'
|
|
89
|
+
: attachment.type !== 'document' &&
|
|
90
|
+
attachment.modelOmission?.reason === 'provider-rejected'
|
|
91
|
+
? 'provider-rejected'
|
|
92
|
+
: 'budget',
|
|
68
93
|
})
|
|
69
94
|
}
|
|
70
95
|
continue
|
|
@@ -79,6 +104,12 @@ function collectRichOccurrences(messages: readonly Message[]): RichOccurrence[]
|
|
|
79
104
|
source: 'tool',
|
|
80
105
|
kind: block.type,
|
|
81
106
|
bytes: block.data.length,
|
|
107
|
+
reason:
|
|
108
|
+
block.type === 'image' && block.modelOmission?.reason === 'invalid-image'
|
|
109
|
+
? 'invalid-image'
|
|
110
|
+
: block.type === 'image' && block.modelOmission?.reason === 'provider-rejected'
|
|
111
|
+
? 'provider-rejected'
|
|
112
|
+
: 'budget',
|
|
82
113
|
})
|
|
83
114
|
}
|
|
84
115
|
}
|
|
@@ -86,10 +117,21 @@ function collectRichOccurrences(messages: readonly Message[]): RichOccurrence[]
|
|
|
86
117
|
return occurrences
|
|
87
118
|
}
|
|
88
119
|
|
|
89
|
-
function
|
|
90
|
-
if (
|
|
91
|
-
const
|
|
92
|
-
return content.length > 0 ? `${content}\n\n${
|
|
120
|
+
function appendTextMarkers(content: string, markers: readonly string[]): string {
|
|
121
|
+
if (markers.length === 0) return content
|
|
122
|
+
const joined = markers.join('\n\n')
|
|
123
|
+
return content.length > 0 ? `${content}\n\n${joined}` : joined
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function appendMarkers(content: string, occurrences: readonly RichOccurrence[]): string {
|
|
127
|
+
const markers = occurrences.map((occurrence) =>
|
|
128
|
+
occurrence.reason === 'provider-rejected'
|
|
129
|
+
? rejectedUserImageMarker()
|
|
130
|
+
: occurrence.reason === 'invalid-image'
|
|
131
|
+
? invalidUserImageMarker()
|
|
132
|
+
: userMarker(occurrence.kind),
|
|
133
|
+
)
|
|
134
|
+
return appendTextMarkers(content, markers)
|
|
93
135
|
}
|
|
94
136
|
|
|
95
137
|
/**
|
|
@@ -103,15 +145,10 @@ function appendMarkers(content: string, kinds: readonly RichKind[]): string {
|
|
|
103
145
|
*/
|
|
104
146
|
export function projectRequestRichContent(messages: Message[], maxBytes: number): Message[] {
|
|
105
147
|
const occurrences = collectRichOccurrences(messages)
|
|
106
|
-
if (
|
|
107
|
-
|
|
108
|
-
let total = occurrences.reduce((sum, occurrence) => sum + occurrence.bytes, 0)
|
|
109
|
-
if (total <= maxBytes) return messages
|
|
148
|
+
if (occurrences.length === 0) return messages
|
|
110
149
|
|
|
111
150
|
const omitted = new Map<number, Map<number, RichOccurrence>>()
|
|
112
|
-
|
|
113
|
-
if (total <= maxBytes) break
|
|
114
|
-
total -= occurrence.bytes
|
|
151
|
+
const omit = (occurrence: RichOccurrence): void => {
|
|
115
152
|
let byItem = omitted.get(occurrence.messageIndex)
|
|
116
153
|
if (!byItem) {
|
|
117
154
|
byItem = new Map()
|
|
@@ -120,6 +157,26 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
|
|
|
120
157
|
byItem.set(occurrence.itemIndex, occurrence)
|
|
121
158
|
}
|
|
122
159
|
|
|
160
|
+
for (const occurrence of occurrences) {
|
|
161
|
+
if (occurrence.reason === 'provider-rejected' || occurrence.reason === 'invalid-image') {
|
|
162
|
+
omit(occurrence)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
let total = occurrences.reduce(
|
|
167
|
+
(sum, occurrence) => sum + (occurrence.reason === 'budget' ? occurrence.bytes : 0),
|
|
168
|
+
0,
|
|
169
|
+
)
|
|
170
|
+
if (maxBytes > 0 && total > maxBytes) {
|
|
171
|
+
for (const occurrence of occurrences) {
|
|
172
|
+
if (total <= maxBytes) break
|
|
173
|
+
if (occurrence.reason !== 'budget') continue
|
|
174
|
+
total -= occurrence.bytes
|
|
175
|
+
omit(occurrence)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (omitted.size === 0) return messages
|
|
179
|
+
|
|
123
180
|
return messages.map((message, messageIndex) => {
|
|
124
181
|
const byItem = omitted.get(messageIndex)
|
|
125
182
|
if (!byItem) return message
|
|
@@ -127,13 +184,12 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
|
|
|
127
184
|
if (message.role === 'user') {
|
|
128
185
|
const { attachments = [], ...withoutAttachments } = message
|
|
129
186
|
const kept = attachments.filter((_attachment, itemIndex) => !byItem.has(itemIndex))
|
|
130
|
-
const
|
|
187
|
+
const omittedOccurrences = [...byItem.values()]
|
|
131
188
|
.filter((occurrence) => occurrence.source === 'user')
|
|
132
189
|
.sort((a, b) => a.itemIndex - b.itemIndex)
|
|
133
|
-
.map((occurrence) => occurrence.kind)
|
|
134
190
|
return {
|
|
135
191
|
...withoutAttachments,
|
|
136
|
-
content: appendMarkers(message.content,
|
|
192
|
+
content: appendMarkers(message.content, omittedOccurrences),
|
|
137
193
|
...(kept.length > 0 ? { attachments: kept } : {}),
|
|
138
194
|
}
|
|
139
195
|
}
|
|
@@ -142,7 +198,15 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
|
|
|
142
198
|
const content: ToolResultBlock[] = message.content.map((block, itemIndex) => {
|
|
143
199
|
const occurrence = byItem.get(itemIndex)
|
|
144
200
|
return occurrence?.source === 'tool'
|
|
145
|
-
? {
|
|
201
|
+
? {
|
|
202
|
+
type: 'text',
|
|
203
|
+
text:
|
|
204
|
+
occurrence.reason === 'provider-rejected'
|
|
205
|
+
? rejectedToolImageMarker()
|
|
206
|
+
: occurrence.reason === 'invalid-image'
|
|
207
|
+
? invalidToolImageMarker()
|
|
208
|
+
: toolMarker(occurrence.kind),
|
|
209
|
+
}
|
|
146
210
|
: block
|
|
147
211
|
})
|
|
148
212
|
return { ...message, content }
|
|
@@ -151,3 +215,137 @@ export function projectRequestRichContent(messages: Message[], maxBytes: number)
|
|
|
151
215
|
return message
|
|
152
216
|
})
|
|
153
217
|
}
|
|
218
|
+
|
|
219
|
+
function sameImage(
|
|
220
|
+
image: { readonly data: string; readonly mediaType: string },
|
|
221
|
+
identity: RequestImageIdentity,
|
|
222
|
+
): boolean {
|
|
223
|
+
return image.data === identity.data && image.mediaType === identity.mediaType
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** Find the one distinct image in the exact provider-bound request, if one exists. */
|
|
227
|
+
export function findSingleRequestImage(messages: readonly Message[]): RequestImageIdentity | null {
|
|
228
|
+
let candidate: RequestImageIdentity | null = null
|
|
229
|
+
for (const message of messages) {
|
|
230
|
+
if (message.role === 'user') {
|
|
231
|
+
for (const attachment of message.attachments ?? []) {
|
|
232
|
+
if (attachment.type === 'stored') {
|
|
233
|
+
throw new TypeError(
|
|
234
|
+
'Cannot inspect an unresolved stored attachment at the provider boundary',
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
if (attachment.type === 'document') continue
|
|
238
|
+
if (candidate === null) {
|
|
239
|
+
candidate = {
|
|
240
|
+
data: attachment.data,
|
|
241
|
+
mediaType: attachment.mediaType,
|
|
242
|
+
}
|
|
243
|
+
} else if (!sameImage(attachment, candidate)) {
|
|
244
|
+
return null
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
continue
|
|
248
|
+
}
|
|
249
|
+
if (message.role !== 'tool' || !Array.isArray(message.content)) continue
|
|
250
|
+
for (const block of message.content) {
|
|
251
|
+
if (block.type !== 'image') continue
|
|
252
|
+
if (candidate === null) {
|
|
253
|
+
candidate = { data: block.data, mediaType: block.mediaType }
|
|
254
|
+
} else if (!sameImage(block, candidate)) {
|
|
255
|
+
return null
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
return candidate
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/** Build the one-off retry request without changing durable conversation state. */
|
|
263
|
+
export function projectRequestWithoutImage(
|
|
264
|
+
messages: Message[],
|
|
265
|
+
identity: RequestImageIdentity,
|
|
266
|
+
): Message[] {
|
|
267
|
+
return messages.map((message) => {
|
|
268
|
+
if (message.role === 'user') {
|
|
269
|
+
const attachments = message.attachments ?? []
|
|
270
|
+
const rejected = attachments.filter(
|
|
271
|
+
(attachment) =>
|
|
272
|
+
attachment.type !== 'stored' &&
|
|
273
|
+
attachment.type !== 'document' &&
|
|
274
|
+
sameImage(attachment, identity),
|
|
275
|
+
)
|
|
276
|
+
if (rejected.length === 0) return message
|
|
277
|
+
const kept = attachments.filter(
|
|
278
|
+
(attachment) =>
|
|
279
|
+
attachment.type === 'stored' ||
|
|
280
|
+
attachment.type === 'document' ||
|
|
281
|
+
!sameImage(attachment, identity),
|
|
282
|
+
)
|
|
283
|
+
const { attachments: _attachments, ...withoutAttachments } = message
|
|
284
|
+
return {
|
|
285
|
+
...withoutAttachments,
|
|
286
|
+
content: appendTextMarkers(
|
|
287
|
+
message.content,
|
|
288
|
+
rejected.map(() => rejectedUserImageMarker()),
|
|
289
|
+
),
|
|
290
|
+
...(kept.length > 0 ? { attachments: kept } : {}),
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (message.role !== 'tool' || !Array.isArray(message.content)) return message
|
|
294
|
+
let changed = false
|
|
295
|
+
const content: ToolResultBlock[] = message.content.map((block) => {
|
|
296
|
+
if (block.type !== 'image' || !sameImage(block, identity)) return block
|
|
297
|
+
changed = true
|
|
298
|
+
return { type: 'text', text: rejectedToolImageMarker() }
|
|
299
|
+
})
|
|
300
|
+
return changed ? { ...message, content } : message
|
|
301
|
+
})
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** Persist a successful recovery while retaining exact image bytes. */
|
|
305
|
+
export function markProviderRejectedImage(
|
|
306
|
+
messages: readonly Message[],
|
|
307
|
+
identity: RequestImageIdentity,
|
|
308
|
+
): { readonly messages: Message[]; readonly count: number } {
|
|
309
|
+
let count = 0
|
|
310
|
+
const marked = messages.map((message) => {
|
|
311
|
+
if (message.role === 'user') {
|
|
312
|
+
let changed = false
|
|
313
|
+
const attachments = message.attachments?.map((attachment) => {
|
|
314
|
+
if (
|
|
315
|
+
attachment.type === 'stored' ||
|
|
316
|
+
attachment.type === 'document' ||
|
|
317
|
+
!sameImage(attachment, identity) ||
|
|
318
|
+
attachment.modelOmission?.reason === 'provider-rejected'
|
|
319
|
+
) {
|
|
320
|
+
return attachment
|
|
321
|
+
}
|
|
322
|
+
changed = true
|
|
323
|
+
count += 1
|
|
324
|
+
return {
|
|
325
|
+
...attachment,
|
|
326
|
+
modelOmission: { reason: 'provider-rejected' as const },
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
return changed ? { ...message, attachments } : message
|
|
330
|
+
}
|
|
331
|
+
if (message.role !== 'tool' || !Array.isArray(message.content)) return message
|
|
332
|
+
let changed = false
|
|
333
|
+
const content: ToolResultBlock[] = message.content.map((block) => {
|
|
334
|
+
if (
|
|
335
|
+
block.type !== 'image' ||
|
|
336
|
+
!sameImage(block, identity) ||
|
|
337
|
+
block.modelOmission?.reason === 'provider-rejected'
|
|
338
|
+
) {
|
|
339
|
+
return block
|
|
340
|
+
}
|
|
341
|
+
changed = true
|
|
342
|
+
count += 1
|
|
343
|
+
return {
|
|
344
|
+
...block,
|
|
345
|
+
modelOmission: { reason: 'provider-rejected' as const },
|
|
346
|
+
}
|
|
347
|
+
})
|
|
348
|
+
return changed ? { ...message, content } : message
|
|
349
|
+
})
|
|
350
|
+
return { messages: marked, count }
|
|
351
|
+
}
|
|
@@ -183,6 +183,7 @@ export class ResultAssembler {
|
|
|
183
183
|
? {
|
|
184
184
|
kind: err.kind,
|
|
185
185
|
providerId: err.providerId,
|
|
186
|
+
...(err.providerCode !== undefined ? { providerCode: err.providerCode } : {}),
|
|
186
187
|
...(err.status !== undefined ? { status: err.status } : {}),
|
|
187
188
|
...(err.retryAfterMs !== undefined ? { retryAfterMs: err.retryAfterMs } : {}),
|
|
188
189
|
// The provider's own sentence, already truncated and scrubbed
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { isDeepStrictEqual } from 'node:util'
|
|
2
|
+
|
|
1
3
|
import type { RunPersistence } from '../../manager/run/persistence.js'
|
|
2
4
|
import type {
|
|
3
5
|
CheckpointId,
|
|
4
6
|
HITLResumeDecision,
|
|
5
7
|
IterationCheckpoint,
|
|
8
|
+
ToolCallSummary,
|
|
6
9
|
} from '../../types/hitl/index.js'
|
|
7
10
|
import type { AssistantMessage, Message, ToolCall } from '../../types/message/index.js'
|
|
8
11
|
import type { ChatCompletionResponse } from '../../types/provider/index.js'
|
|
@@ -38,6 +41,10 @@ export interface PendingResumePlan {
|
|
|
38
41
|
readonly response: ChatCompletionResponse
|
|
39
42
|
/** Per-call refusals derived from the decision. */
|
|
40
43
|
readonly denials: ToolCallDenials
|
|
44
|
+
/** Exact projections and gate decisions persisted with a tool review. */
|
|
45
|
+
readonly reviewedCalls?: readonly ToolCallSummary[]
|
|
46
|
+
/** Calls whose raw input the human replaced in the durable decision. */
|
|
47
|
+
readonly modifiedCallIds?: ReadonlySet<string>
|
|
41
48
|
/**
|
|
42
49
|
* Answers to deliver to tools that parked on a question, keyed by the
|
|
43
50
|
* asking call's id. Present only on a question resume.
|
|
@@ -117,6 +124,8 @@ export function planPendingResume(
|
|
|
117
124
|
assistant,
|
|
118
125
|
response: synthesizeResponse(assistant),
|
|
119
126
|
denials,
|
|
127
|
+
reviewedCalls: pending.request.toolCalls,
|
|
128
|
+
modifiedCallIds: modifiedCallIds(decision),
|
|
120
129
|
}
|
|
121
130
|
}
|
|
122
131
|
|
|
@@ -246,13 +255,86 @@ export async function applyPendingResume(
|
|
|
246
255
|
executor: ToolExecutor,
|
|
247
256
|
prior?: PriorToolResults,
|
|
248
257
|
): Promise<void> {
|
|
258
|
+
const denials = new Map(plan.denials)
|
|
259
|
+
const reviewedById = new Map(plan.reviewedCalls?.map((call) => [call.id, call]))
|
|
260
|
+
|
|
261
|
+
// A gate denial belongs to the run, not to the process that first evaluated
|
|
262
|
+
// it. Restore it before preparation so denied calls do not even reach a
|
|
263
|
+
// pre-tool hook after restart.
|
|
264
|
+
for (const call of plan.reviewedCalls ?? []) {
|
|
265
|
+
if (call.authorization?.decision !== 'deny' || denials.has(call.id)) continue
|
|
266
|
+
denials.set(
|
|
267
|
+
call.id,
|
|
268
|
+
`Blocked by the authorization gate: ${call.authorization.reason ?? 'the persisted operator policy denied this call'}`,
|
|
269
|
+
)
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const callsToPrepare = (plan.response.message.toolCalls ?? []).filter(
|
|
273
|
+
(call) => !prior?.has(call.id) && !denials.has(call.id),
|
|
274
|
+
)
|
|
275
|
+
const responseToPrepare: ChatCompletionResponse = {
|
|
276
|
+
...plan.response,
|
|
277
|
+
message: { ...plan.response.message, toolCalls: callsToPrepare },
|
|
278
|
+
}
|
|
279
|
+
const preparedBatch = await executor.prepareBatchForReview(responseToPrepare)
|
|
280
|
+
|
|
281
|
+
for (const call of preparedBatch.reviewCalls) {
|
|
282
|
+
const reviewed = reviewedById.get(call.id)
|
|
283
|
+
const wasModified = plan.modifiedCallIds?.has(call.id) === true
|
|
284
|
+
if (reviewed && !wasModified) {
|
|
285
|
+
const unchanged = reviewed.name === call.name && isDeepStrictEqual(reviewed.input, call.input)
|
|
286
|
+
if (!unchanged) {
|
|
287
|
+
const reason =
|
|
288
|
+
'The tool input changed after its durable review; the earlier approval cannot be reused.'
|
|
289
|
+
denials.set(call.id, reason)
|
|
290
|
+
await runMgr.recordAudit({
|
|
291
|
+
what: { action: 'tool_call', tool: call.name },
|
|
292
|
+
outcome: 'refused',
|
|
293
|
+
reason,
|
|
294
|
+
})
|
|
295
|
+
continue
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const current = executor.evaluatePreparedAuthorization(call.name, call.input)
|
|
300
|
+
let reason: string | undefined
|
|
301
|
+
if (current?.decision === 'deny') {
|
|
302
|
+
reason = `Blocked by the authorization gate after resume: ${current.reason}`
|
|
303
|
+
} else if (current?.decision === 'review' && (wasModified || !reviewed)) {
|
|
304
|
+
reason = wasModified
|
|
305
|
+
? `Blocked by the authorization gate after resume: the modified prepared value requires a new explicit approval. ${current.reason}`
|
|
306
|
+
: `Blocked by the authorization gate after resume: this recovered call requires operator review. ${current.reason}`
|
|
307
|
+
} else if (!current && reviewed && reviewed.authorization === undefined) {
|
|
308
|
+
reason =
|
|
309
|
+
'The durable review predates bound authorization metadata; review this call again before execution.'
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (reason) {
|
|
313
|
+
denials.set(call.id, reason)
|
|
314
|
+
await runMgr.recordAudit({
|
|
315
|
+
what: { action: 'tool_call', tool: call.name },
|
|
316
|
+
outcome: 'refused',
|
|
317
|
+
reason,
|
|
318
|
+
})
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
249
322
|
runMgr.pushMessage(plan.assistant)
|
|
250
|
-
const batch = await executor.executeBatch(plan.response,
|
|
323
|
+
const batch = await executor.executeBatch(plan.response, denials, prior, preparedBatch)
|
|
251
324
|
for (const msg of batch.messages) {
|
|
252
325
|
runMgr.pushMessage(msg)
|
|
253
326
|
}
|
|
254
327
|
}
|
|
255
328
|
|
|
329
|
+
function modifiedCallIds(decision: HITLResumeDecision): ReadonlySet<string> {
|
|
330
|
+
if (decision.action !== 'modify_tools') return new Set()
|
|
331
|
+
return new Set(
|
|
332
|
+
decision.modifications
|
|
333
|
+
.filter((modification) => modification.action === 'modify')
|
|
334
|
+
.map((modification) => modification.toolCallId),
|
|
335
|
+
)
|
|
336
|
+
}
|
|
337
|
+
|
|
256
338
|
/**
|
|
257
339
|
* Results the run already produced for calls in `toolCalls`.
|
|
258
340
|
*
|
|
@@ -37,6 +37,57 @@ export const SPILL_MARKER = 'The full output was written to:'
|
|
|
37
37
|
*/
|
|
38
38
|
const HEAD_SHARE = 0.75
|
|
39
39
|
|
|
40
|
+
function safeHead(text: string, maxChars: number): string {
|
|
41
|
+
let end = Math.min(text.length, Math.max(0, maxChars))
|
|
42
|
+
if (end > 0) {
|
|
43
|
+
const last = text.charCodeAt(end - 1)
|
|
44
|
+
if (last >= 0xd800 && last <= 0xdbff) end--
|
|
45
|
+
}
|
|
46
|
+
return text.slice(0, end)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function safeTail(text: string, maxChars: number): string {
|
|
50
|
+
let start = Math.max(0, text.length - Math.max(0, maxChars))
|
|
51
|
+
if (start < text.length) {
|
|
52
|
+
const first = text.charCodeAt(start)
|
|
53
|
+
if (first >= 0xdc00 && first <= 0xdfff) start++
|
|
54
|
+
}
|
|
55
|
+
return text.slice(start)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Fit a recoverable head+tail preview inside the actual model-visible cap.
|
|
60
|
+
*
|
|
61
|
+
* The old implementation allocated the entire cap to source text and then
|
|
62
|
+
* appended its diagnostic and recovery instructions. A configured 1,000
|
|
63
|
+
* character cap therefore emitted 1,200–1,300 characters; at very small
|
|
64
|
+
* limits the explanation could be several times larger than the limit.
|
|
65
|
+
*/
|
|
66
|
+
function boundedPreview(
|
|
67
|
+
output: string,
|
|
68
|
+
maxChars: number,
|
|
69
|
+
toolName: string,
|
|
70
|
+
recovery: string,
|
|
71
|
+
): string {
|
|
72
|
+
const detailedMiddle = [
|
|
73
|
+
'',
|
|
74
|
+
`[... characters omitted — "${toolName}" returned ${output.length.toLocaleString()} characters, over the ${maxChars.toLocaleString()}-character budget ...]`,
|
|
75
|
+
recovery,
|
|
76
|
+
'',
|
|
77
|
+
].join('\n')
|
|
78
|
+
const compactMiddle = '\n[... omitted ...]\n'
|
|
79
|
+
const middle = detailedMiddle.length < maxChars ? detailedMiddle : compactMiddle
|
|
80
|
+
|
|
81
|
+
// A host may deliberately set a tiny positive cap. At that point no
|
|
82
|
+
// truthful recovery sentence fits; the hard bound still wins.
|
|
83
|
+
if (middle.length >= maxChars) return safeHead(middle, maxChars)
|
|
84
|
+
|
|
85
|
+
const sourceChars = maxChars - middle.length
|
|
86
|
+
const headChars = Math.max(1, Math.floor(sourceChars * HEAD_SHARE))
|
|
87
|
+
const tailChars = Math.max(0, sourceChars - headChars)
|
|
88
|
+
return `${safeHead(output, headChars)}${middle}${safeTail(output, tailChars)}`
|
|
89
|
+
}
|
|
90
|
+
|
|
40
91
|
export interface ToolOutputBudgetResult {
|
|
41
92
|
/** What the model sees. */
|
|
42
93
|
readonly output: string
|
|
@@ -127,15 +178,6 @@ export function applyToolOutputBudget(opts: ApplyToolOutputBudgetOptions): ToolO
|
|
|
127
178
|
return { output, originalLength, truncated: false }
|
|
128
179
|
}
|
|
129
180
|
|
|
130
|
-
// Both slices are derived from `maxChars` so the preview honours the
|
|
131
|
-
// budget it is enforcing. `slice(-0)` returns the WHOLE string, so the
|
|
132
|
-
// zero-tail case must be branched, not computed.
|
|
133
|
-
const headChars = Math.max(1, Math.floor(maxChars * HEAD_SHARE))
|
|
134
|
-
const tailChars = Math.max(0, maxChars - headChars)
|
|
135
|
-
const head = output.slice(0, headChars)
|
|
136
|
-
const tail = tailChars > 0 ? output.slice(-tailChars) : ''
|
|
137
|
-
const omitted = originalLength - head.length - tail.length
|
|
138
|
-
|
|
139
181
|
const spillPath = opts.spillDir
|
|
140
182
|
? spill(opts.spillDir, opts.toolUseId, output, opts.onError)
|
|
141
183
|
: undefined
|
|
@@ -148,14 +190,7 @@ export function applyToolOutputBudget(opts: ApplyToolOutputBudgetOptions): ToolO
|
|
|
148
190
|
: 'The full output was not retained. Re-run with a narrower query, a line range, or a filter.'
|
|
149
191
|
|
|
150
192
|
return {
|
|
151
|
-
output:
|
|
152
|
-
head,
|
|
153
|
-
'',
|
|
154
|
-
`[... ${omitted.toLocaleString()} characters omitted — "${opts.toolName}" returned ${originalLength.toLocaleString()} characters, over the ${maxChars.toLocaleString()}-character budget ...]`,
|
|
155
|
-
recovery,
|
|
156
|
-
'',
|
|
157
|
-
tail,
|
|
158
|
-
].join('\n'),
|
|
193
|
+
output: boundedPreview(output, maxChars, opts.toolName, recovery),
|
|
159
194
|
originalLength,
|
|
160
195
|
truncated: true,
|
|
161
196
|
...(spillPath ? { spillPath } : {}),
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import type { AuthorizationGate } from '../../authorization/gate.js'
|
|
1
2
|
import type { PluginLifecycleManager } from '../../plugin/lifecycle.js'
|
|
2
3
|
import type { ActivityStore } from '../../store/activity/memory.js'
|
|
3
4
|
import type { RunId } from '../../types/ids/index.js'
|
|
4
5
|
import type { InvocationState } from '../../types/invocation/index.js'
|
|
5
6
|
import type { PermissionMode } from '../../types/permission/index.js'
|
|
7
|
+
import type { AuditEventInput } from '../../types/run/audit.js'
|
|
6
8
|
import type { RunEvent } from '../../types/run/index.js'
|
|
7
9
|
import type {
|
|
8
10
|
RequestToolPause,
|
|
@@ -41,6 +43,10 @@ export interface ToolingBootstrapConfig {
|
|
|
41
43
|
maxToolContentBytes?: number
|
|
42
44
|
toolOutputDir?: string
|
|
43
45
|
repairToolCall?: RepairToolCall
|
|
46
|
+
/** Operator authorization shared with the direct-call review path. */
|
|
47
|
+
authorizationGate?: AuthorizationGate
|
|
48
|
+
/** Durable refusal recorder for nested authorization decisions. */
|
|
49
|
+
recordAudit?: (input: AuditEventInput) => Promise<unknown>
|
|
44
50
|
/** Builds the durable-pause seam for one tool call; see ToolContext.requestPause. */
|
|
45
51
|
toolPause?: (toolUseId: string) => RequestToolPause
|
|
46
52
|
}
|
|
@@ -81,6 +87,10 @@ export class ToolingBootstrap {
|
|
|
81
87
|
: {}),
|
|
82
88
|
...(config.toolOutputDir !== undefined ? { toolOutputDir: config.toolOutputDir } : {}),
|
|
83
89
|
...(config.repairToolCall !== undefined ? { repairToolCall: config.repairToolCall } : {}),
|
|
90
|
+
...(config.authorizationGate !== undefined
|
|
91
|
+
? { authorizationGate: config.authorizationGate }
|
|
92
|
+
: {}),
|
|
93
|
+
...(config.recordAudit !== undefined ? { recordAudit: config.recordAudit } : {}),
|
|
84
94
|
...(config.toolPause !== undefined ? { toolPause: config.toolPause } : {}),
|
|
85
95
|
},
|
|
86
96
|
activityStore,
|