@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
|
@@ -25,6 +25,7 @@ import { EmergencySaveManager } from '../../manager/run/emergency.js'
|
|
|
25
25
|
import type { RunPersistence } from '../../manager/run/persistence.js'
|
|
26
26
|
import { resolveModelPricing } from '../../pricing/index.js'
|
|
27
27
|
import { resolveProviderCapabilities } from '../../provider/capabilities.js'
|
|
28
|
+
import { isCallerAbortError } from '../../provider/errors.js'
|
|
28
29
|
import {
|
|
29
30
|
type ProviderChainMember,
|
|
30
31
|
type ServingMember,
|
|
@@ -77,6 +78,7 @@ import type { AgentPersona } from '../../types/persona/index.js'
|
|
|
77
78
|
import type { LLMProvider } from '../../types/provider/index.js'
|
|
78
79
|
import type { TaskRouterConfig } from '../../types/router/index.js'
|
|
79
80
|
import type { ReviewAnswer } from '../../types/run/answer-review.js'
|
|
81
|
+
import { cancelCauseOf } from '../../types/run/cancel-cause.js'
|
|
80
82
|
import type { CheckpointStore, FencingToken } from '../../types/run/checkpoint-store.js'
|
|
81
83
|
import type { RunEventCursor, RunEventReplay } from '../../types/run/event-cursor.js'
|
|
82
84
|
import { resolveRunEventReplay } from '../../types/run/event-cursor.js'
|
|
@@ -302,6 +304,11 @@ export interface QueryParams {
|
|
|
302
304
|
* store REFUSES rather than dropping the attachment.
|
|
303
305
|
*/
|
|
304
306
|
attachmentStore?: import('../../store/attachment/index.js').AttachmentStore
|
|
307
|
+
/**
|
|
308
|
+
* Maximum wall-clock time for resolving the run's stored attachments.
|
|
309
|
+
* Defaults to one minute; `0` retains the prior unbounded wait.
|
|
310
|
+
*/
|
|
311
|
+
attachmentResolveTimeoutMs?: number
|
|
305
312
|
|
|
306
313
|
/**
|
|
307
314
|
* How this run reaches the web.
|
|
@@ -1192,6 +1199,7 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1192
1199
|
resolvedInitialMessages = [
|
|
1193
1200
|
...(await resolveAttachments(seeded, params.attachmentStore, {
|
|
1194
1201
|
signal: params.signal,
|
|
1202
|
+
timeoutMs: params.attachmentResolveTimeoutMs,
|
|
1195
1203
|
})),
|
|
1196
1204
|
]
|
|
1197
1205
|
params.signal?.throwIfAborted()
|
|
@@ -1361,6 +1369,40 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1361
1369
|
eventTranslator.wireActivityStore(ctx.activityStore, ctx.runId)
|
|
1362
1370
|
eventTranslator.wirePlanManager(ctx.planManager, ctx.runId)
|
|
1363
1371
|
eventTranslator.setGeneration(params.claimFence)
|
|
1372
|
+
let interruptHooksStarted = false
|
|
1373
|
+
const executeUserInterruptHooks = async (terminalError: unknown): Promise<void> => {
|
|
1374
|
+
if (
|
|
1375
|
+
interruptHooksStarted ||
|
|
1376
|
+
!params.pluginManager ||
|
|
1377
|
+
!isCallerAbortError(terminalError, ctx.abortController.signal) ||
|
|
1378
|
+
params.parentRunId !== undefined ||
|
|
1379
|
+
(params.depth ?? 0) !== 0 ||
|
|
1380
|
+
cancelCauseOf(ctx.abortController.signal.reason) !== 'user'
|
|
1381
|
+
) {
|
|
1382
|
+
return
|
|
1383
|
+
}
|
|
1384
|
+
|
|
1385
|
+
interruptHooksStarted = true
|
|
1386
|
+
try {
|
|
1387
|
+
// Deliberately omit the already-aborted run signal. The lifecycle
|
|
1388
|
+
// manager still supplies each handler its own deadline signal, while
|
|
1389
|
+
// `run_interrupt`'s observational fan-out prevents one result from
|
|
1390
|
+
// suppressing the cleanup hooks that follow it.
|
|
1391
|
+
await params.pluginManager.executeHooks(
|
|
1392
|
+
'run_interrupt',
|
|
1393
|
+
{ runId: ctx.runId, cancelCause: 'user' },
|
|
1394
|
+
eventTranslator.emitEvent,
|
|
1395
|
+
)
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
// Cancellation is the terminal authority. A hook event sink or an
|
|
1398
|
+
// unexpected manager failure is reported, but cannot turn Stop into a
|
|
1399
|
+
// failed run or prevent the durable cancellation verdict.
|
|
1400
|
+
ctx.log.error('Run interrupt hooks did not settle cleanly', {
|
|
1401
|
+
[NAMZU.RUN_ID]: ctx.runId,
|
|
1402
|
+
...errorAttributes(error),
|
|
1403
|
+
})
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1364
1406
|
|
|
1365
1407
|
if (attachmentResolutionCancelled) {
|
|
1366
1408
|
// Attachment materialization happens before RunContext exists. Once it
|
|
@@ -1369,6 +1411,9 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1369
1411
|
// and settle through the ordinary cancellation classifier. Prompt
|
|
1370
1412
|
// contributions/cache, host callbacks, tools, plugins, sandbox, guardrails,
|
|
1371
1413
|
// advisors, and providers are all authority-bearing work and stay out.
|
|
1414
|
+
// The dedicated root interrupt notification is the sole plugin exception:
|
|
1415
|
+
// it runs after cancellation under its own deadline and cannot regain model
|
|
1416
|
+
// or tool authority.
|
|
1372
1417
|
if (params.resumeFromCheckpoint && !selectedResumeState) {
|
|
1373
1418
|
// The canonical resume surface hands query the checkpoint state it
|
|
1374
1419
|
// already selected. A raw resume query has no such snapshot; after
|
|
@@ -1454,6 +1499,8 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1454
1499
|
const terminalError = ctx.abortController.signal.aborted
|
|
1455
1500
|
? ctx.abortController.signal.reason
|
|
1456
1501
|
: error
|
|
1502
|
+
await executeUserInterruptHooks(terminalError)
|
|
1503
|
+
yield* eventTranslator.drainPending()
|
|
1457
1504
|
yield* cancelledAssembler.handleError(terminalError, rootSpan)
|
|
1458
1505
|
} finally {
|
|
1459
1506
|
rootSpan.end()
|
|
@@ -1614,6 +1661,14 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1614
1661
|
const questionParks = params.questionParks ?? new QuestionParkBinding()
|
|
1615
1662
|
const pendingAnswers = params.pendingAnswers ?? new PendingAnswers()
|
|
1616
1663
|
|
|
1664
|
+
// One gate instance owns both model-issued calls and calls dispatched by
|
|
1665
|
+
// another tool. Constructing it only inside the iteration review left the
|
|
1666
|
+
// nested registry path outside the operator's policy entirely.
|
|
1667
|
+
const gateConfig = params.authorizationGate
|
|
1668
|
+
const verificationGate = gateConfig?.enabled
|
|
1669
|
+
? new AuthorizationGate(gateConfig, ctx.log)
|
|
1670
|
+
: undefined
|
|
1671
|
+
|
|
1617
1672
|
// is null only when the run has no disk layout (tests,
|
|
1618
1673
|
// in-memory hosts); the budget then degrades to middle-elision.
|
|
1619
1674
|
const runDirForTools = ctx.runMgr.getRunDir()
|
|
@@ -1654,6 +1709,8 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1654
1709
|
// `read`/`grep` without a new affordance.
|
|
1655
1710
|
...(toolOutputDir ? { toolOutputDir } : {}),
|
|
1656
1711
|
...(params.repairToolCall ? { repairToolCall: params.repairToolCall } : {}),
|
|
1712
|
+
...(verificationGate ? { authorizationGate: verificationGate } : {}),
|
|
1713
|
+
recordAudit: (input) => ctx.runMgr.recordAudit(input),
|
|
1657
1714
|
// The durable pause, reachable from any tool rather than from the
|
|
1658
1715
|
// four kernel-owned points that used to own it. Built here from
|
|
1659
1716
|
// the machinery the run already holds; the recorder binds a few
|
|
@@ -1800,12 +1857,6 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
1800
1857
|
}
|
|
1801
1858
|
}
|
|
1802
1859
|
|
|
1803
|
-
const gateConfig = params.authorizationGate
|
|
1804
|
-
|
|
1805
|
-
const verificationGate = gateConfig?.enabled
|
|
1806
|
-
? new AuthorizationGate(gateConfig, ctx.log)
|
|
1807
|
-
: undefined
|
|
1808
|
-
|
|
1809
1860
|
const iterationOrchestrator = new IterationOrchestrator({
|
|
1810
1861
|
provider: resilientProvider,
|
|
1811
1862
|
servingMember: () => serving.current,
|
|
@@ -2575,6 +2626,8 @@ export async function* query(params: QueryParams): AsyncGenerator<RunEvent, Run>
|
|
|
2575
2626
|
} catch (err) {
|
|
2576
2627
|
// A failed run still spent its steps; report them.
|
|
2577
2628
|
ctx.runMgr.setSteps(iterationOrchestrator.getSteps())
|
|
2629
|
+
await executeUserInterruptHooks(err)
|
|
2630
|
+
yield* eventTranslator.drainPending()
|
|
2578
2631
|
yield* resultAssembler.handleError(err, rootSpan)
|
|
2579
2632
|
} finally {
|
|
2580
2633
|
// Release the process's termination path as soon as this run is
|
|
@@ -22,8 +22,8 @@ import type { CostInfo, TokenUsage } from '../../../types/common/index.js'
|
|
|
22
22
|
import type { MessageId } from '../../../types/ids/index.js'
|
|
23
23
|
import {
|
|
24
24
|
createAssistantMessage,
|
|
25
|
+
createRuntimeContextMessage,
|
|
25
26
|
createSystemMessage,
|
|
26
|
-
createUserMessage,
|
|
27
27
|
} from '../../../types/message/index.js'
|
|
28
28
|
import type { ToolChoice } from '../../../types/provider/chat.js'
|
|
29
29
|
import { classifyProviderError } from '../../../types/provider/errors.js'
|
|
@@ -47,6 +47,8 @@ import type { ToolCallOutcome } from '../executor.js'
|
|
|
47
47
|
import { applyLifecycleHookResults } from '../plugin-hooks.js'
|
|
48
48
|
import {
|
|
49
49
|
DEFAULT_MAX_REQUEST_RICH_CONTENT_BYTES,
|
|
50
|
+
type RequestImageIdentity,
|
|
51
|
+
markProviderRejectedImage,
|
|
50
52
|
projectRequestRichContent,
|
|
51
53
|
} from '../request-rich-content.js'
|
|
52
54
|
import { formatSteeringNote } from '../steering.js'
|
|
@@ -57,6 +59,7 @@ import type { IterationContext } from './phases/index.js'
|
|
|
57
59
|
import { runPlanGate } from './phases/plan.js'
|
|
58
60
|
import { runToolReview } from './phases/tool-review.js'
|
|
59
61
|
import { refreshWorkingMemory } from './phases/working-memory.js'
|
|
62
|
+
import { streamWithProviderRejectedImageRecovery } from './provider-rejected-image.js'
|
|
60
63
|
import { streamProviderTurn } from './stream-turn.js'
|
|
61
64
|
|
|
62
65
|
export type { IterationContext } from './phases/index.js'
|
|
@@ -394,8 +397,9 @@ export class IterationOrchestrator {
|
|
|
394
397
|
const baseMessages = forceFinalize
|
|
395
398
|
? [
|
|
396
399
|
...runMgr.messages,
|
|
397
|
-
|
|
400
|
+
createRuntimeContextMessage(
|
|
398
401
|
'[SYSTEM] You are approaching your resource limits. Provide your final, comprehensive response now based on everything you have gathered so far. Do not request any more tool calls.',
|
|
402
|
+
'limit-finalization',
|
|
399
403
|
),
|
|
400
404
|
]
|
|
401
405
|
: runMgr.messages
|
|
@@ -589,6 +593,9 @@ export class IterationOrchestrator {
|
|
|
589
593
|
this.ctx.log,
|
|
590
594
|
iterSpan,
|
|
591
595
|
stepMessageId,
|
|
596
|
+
{
|
|
597
|
+
onAccepted: (identity) => this.acceptProviderRejectedImage(identity),
|
|
598
|
+
},
|
|
592
599
|
)
|
|
593
600
|
stepResponse = response
|
|
594
601
|
|
|
@@ -880,7 +887,9 @@ export class IterationOrchestrator {
|
|
|
880
887
|
[NAMZU.ITERATION]: iterationNum,
|
|
881
888
|
[GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
|
|
882
889
|
})
|
|
883
|
-
runMgr.pushMessage(
|
|
890
|
+
runMgr.pushMessage(
|
|
891
|
+
createRuntimeContextMessage(AUTO_CONTINUATION_USER_MESSAGE, 'auto-continuation'),
|
|
892
|
+
)
|
|
884
893
|
await this.ctx.emitEvent({
|
|
885
894
|
type: 'iteration_completed',
|
|
886
895
|
runId: runMgr.id,
|
|
@@ -912,7 +921,9 @@ export class IterationOrchestrator {
|
|
|
912
921
|
'namzu.retry.attempt': attempt,
|
|
913
922
|
'namzu.runtime.limit': limit,
|
|
914
923
|
})
|
|
915
|
-
runMgr.pushMessage(
|
|
924
|
+
runMgr.pushMessage(
|
|
925
|
+
createRuntimeContextMessage(STRUCTURED_OUTPUT_REPROMPT, 'structured-output'),
|
|
926
|
+
)
|
|
916
927
|
await this.ctx.emitEvent({
|
|
917
928
|
type: 'iteration_completed',
|
|
918
929
|
runId: runMgr.id,
|
|
@@ -955,7 +966,7 @@ export class IterationOrchestrator {
|
|
|
955
966
|
'namzu.retry.attempt': attempt,
|
|
956
967
|
'namzu.runtime.limit': limit,
|
|
957
968
|
})
|
|
958
|
-
runMgr.pushMessage(
|
|
969
|
+
runMgr.pushMessage(createRuntimeContextMessage(review.feedback, 'answer-review'))
|
|
959
970
|
await this.ctx.emitEvent({
|
|
960
971
|
type: 'iteration_completed',
|
|
961
972
|
runId: runMgr.id,
|
|
@@ -1188,7 +1199,9 @@ export class IterationOrchestrator {
|
|
|
1188
1199
|
[NAMZU.ITERATION]: iterationNum,
|
|
1189
1200
|
'namzu.runtime.tasks': unheard.map((h) => h.taskId),
|
|
1190
1201
|
})
|
|
1191
|
-
runMgr.pushMessage(
|
|
1202
|
+
runMgr.pushMessage(
|
|
1203
|
+
createRuntimeContextMessage(formatCompletionNotification(unheard), 'task-completion'),
|
|
1204
|
+
)
|
|
1192
1205
|
}
|
|
1193
1206
|
|
|
1194
1207
|
// The same seam, for the two channels that could accept text
|
|
@@ -1388,7 +1401,9 @@ export class IterationOrchestrator {
|
|
|
1388
1401
|
const arrived = this.ctx.completionInbox.drain()
|
|
1389
1402
|
if (arrived.length === 0) return false
|
|
1390
1403
|
|
|
1391
|
-
this.ctx.runMgr.pushMessage(
|
|
1404
|
+
this.ctx.runMgr.pushMessage(
|
|
1405
|
+
createRuntimeContextMessage(formatCompletionNotification(arrived), 'task-completion'),
|
|
1406
|
+
)
|
|
1392
1407
|
await this.ctx.emitEvent({
|
|
1393
1408
|
type: 'iteration_completed',
|
|
1394
1409
|
runId: this.ctx.runMgr.id,
|
|
@@ -1467,7 +1482,9 @@ export class IterationOrchestrator {
|
|
|
1467
1482
|
[NAMZU.RUN_ID]: this.ctx.runMgr.id,
|
|
1468
1483
|
'namzu.runtime.tasks': unheard.map((h) => h.taskId),
|
|
1469
1484
|
})
|
|
1470
|
-
this.ctx.runMgr.pushMessage(
|
|
1485
|
+
this.ctx.runMgr.pushMessage(
|
|
1486
|
+
createRuntimeContextMessage(formatCompletionNotification(unheard), 'task-completion'),
|
|
1487
|
+
)
|
|
1471
1488
|
}
|
|
1472
1489
|
|
|
1473
1490
|
/**
|
|
@@ -1633,7 +1650,9 @@ export class IterationOrchestrator {
|
|
|
1633
1650
|
// guidance from a turn that had no result to attach it to.
|
|
1634
1651
|
const stranded = this.ctx.steering?.drain()
|
|
1635
1652
|
if (stranded) {
|
|
1636
|
-
this.ctx.runMgr.pushMessage(
|
|
1653
|
+
this.ctx.runMgr.pushMessage(
|
|
1654
|
+
createRuntimeContextMessage(formatSteeringNote(stranded), 'steering'),
|
|
1655
|
+
)
|
|
1637
1656
|
}
|
|
1638
1657
|
|
|
1639
1658
|
return queued.length + (stranded ? 1 : 0)
|
|
@@ -1922,6 +1941,23 @@ export class IterationOrchestrator {
|
|
|
1922
1941
|
.some((t) => t.state !== 'completed' && t.state !== 'failed' && t.state !== 'canceled')
|
|
1923
1942
|
}
|
|
1924
1943
|
|
|
1944
|
+
private async acceptProviderRejectedImage(identity: RequestImageIdentity): Promise<void> {
|
|
1945
|
+
const repaired = markProviderRejectedImage(this.ctx.runMgr.messages, identity)
|
|
1946
|
+
if (repaired.count === 0) {
|
|
1947
|
+
throw new Error('Provider-rejected image recovery could not find its durable source image')
|
|
1948
|
+
}
|
|
1949
|
+
this.ctx.runMgr.replaceMessages(repaired.messages)
|
|
1950
|
+
await this.ctx.emitEvent?.({
|
|
1951
|
+
type: 'message_history_repaired',
|
|
1952
|
+
runId: this.ctx.runMgr.id,
|
|
1953
|
+
source: 'provider-rejected-image',
|
|
1954
|
+
duplicateToolResultsRemoved: 0,
|
|
1955
|
+
orphanedToolResultsRemoved: 0,
|
|
1956
|
+
syntheticToolResultsInserted: 0,
|
|
1957
|
+
providerRejectedImagesSuppressed: repaired.count,
|
|
1958
|
+
})
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1925
1961
|
private async requestFinalResponse(model: string, reason: StopReason): Promise<void> {
|
|
1926
1962
|
const lastAssistant = [...this.ctx.runMgr.messages]
|
|
1927
1963
|
.reverse()
|
|
@@ -1941,8 +1977,9 @@ export class IterationOrchestrator {
|
|
|
1941
1977
|
try {
|
|
1942
1978
|
const finalHistory = [
|
|
1943
1979
|
...this.ctx.runMgr.messages,
|
|
1944
|
-
|
|
1980
|
+
createRuntimeContextMessage(
|
|
1945
1981
|
`[SYSTEM] Run is ending due to ${reason}. You MUST provide a final response now summarizing all your findings and work so far. Do not use any tools.`,
|
|
1982
|
+
'limit-finalization',
|
|
1946
1983
|
),
|
|
1947
1984
|
]
|
|
1948
1985
|
const finalMessages = projectRequestRichContent(
|
|
@@ -1964,26 +2001,29 @@ export class IterationOrchestrator {
|
|
|
1964
2001
|
model: requestedMember.model ?? model,
|
|
1965
2002
|
chainIndex: requestedMember.index,
|
|
1966
2003
|
}
|
|
2004
|
+
const finalParams = {
|
|
2005
|
+
model,
|
|
2006
|
+
providerRoute: requestedRoute,
|
|
2007
|
+
messages: finalMessages,
|
|
2008
|
+
tools: finalTools.length > 0 ? finalTools : undefined,
|
|
2009
|
+
...(finalEnforced ? { enforceToolInputSchema: finalEnforced } : {}),
|
|
2010
|
+
toolChoice: finalTools.length > 0 ? 'none' : undefined,
|
|
2011
|
+
temperature: this.ctx.runConfig.temperature,
|
|
2012
|
+
maxTokens: this.ctx.runConfig.maxResponseTokens,
|
|
2013
|
+
cacheControl: { type: 'auto' },
|
|
2014
|
+
...(this.ctx.runConfig.thinking ? { thinking: this.ctx.runConfig.thinking } : {}),
|
|
2015
|
+
// This turn is a hand-maintained duplicate of the one above, which
|
|
2016
|
+
// is exactly the shape a field goes missing from — so it is tested
|
|
2017
|
+
// separately rather than assumed to have been kept in step.
|
|
2018
|
+
...(this.ctx.runConfig.effort ? { effort: this.ctx.runConfig.effort } : {}),
|
|
2019
|
+
// Cancellable too: a Stop during the closing summary must not
|
|
2020
|
+
// stream to completion.
|
|
2021
|
+
signal: this.ctx.abortController.signal,
|
|
2022
|
+
} satisfies import('../../../types/provider/index.js').ChatCompletionParams
|
|
1967
2023
|
const response = await collectChatCompletion(
|
|
1968
|
-
this.ctx.provider
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
messages: finalMessages,
|
|
1972
|
-
tools: finalTools.length > 0 ? finalTools : undefined,
|
|
1973
|
-
...(finalEnforced ? { enforceToolInputSchema: finalEnforced } : {}),
|
|
1974
|
-
toolChoice: finalTools.length > 0 ? 'none' : undefined,
|
|
1975
|
-
temperature: this.ctx.runConfig.temperature,
|
|
1976
|
-
maxTokens: this.ctx.runConfig.maxResponseTokens,
|
|
1977
|
-
cacheControl: { type: 'auto' },
|
|
1978
|
-
...(this.ctx.runConfig.thinking ? { thinking: this.ctx.runConfig.thinking } : {}),
|
|
1979
|
-
// This turn is a hand-maintained duplicate of the one above, which
|
|
1980
|
-
// is exactly the shape a field goes missing from — so it is tested
|
|
1981
|
-
// separately rather than assumed to have been kept in step.
|
|
1982
|
-
...(this.ctx.runConfig.effort ? { effort: this.ctx.runConfig.effort } : {}),
|
|
1983
|
-
// Cancellable too: a Stop during the closing summary must not
|
|
1984
|
-
// stream to completion.
|
|
1985
|
-
signal: this.ctx.abortController.signal,
|
|
1986
|
-
}),
|
|
2024
|
+
streamWithProviderRejectedImageRecovery(this.ctx.provider, finalParams, (identity) =>
|
|
2025
|
+
this.acceptProviderRejectedImage(identity),
|
|
2026
|
+
),
|
|
1987
2027
|
)
|
|
1988
2028
|
|
|
1989
2029
|
const servingMember = this.ctx.servingMember?.() ?? requestedMember
|
|
@@ -2,7 +2,7 @@ import { serializeState } from '../../../../compaction/serializer.js'
|
|
|
2
2
|
import { NAMZU } from '../../../../constants/telemetry/index.js'
|
|
3
3
|
import type { AdvisoryRequest, TriggerEvaluationState } from '../../../../types/advisory/index.js'
|
|
4
4
|
import { toolResultToText } from '../../../../types/message/content.js'
|
|
5
|
-
import {
|
|
5
|
+
import { createRuntimeContextMessage } from '../../../../types/message/index.js'
|
|
6
6
|
import type { ChatCompletionResponse } from '../../../../types/provider/index.js'
|
|
7
7
|
import { toErrorMessage } from '../../../../utils/error.js'
|
|
8
8
|
import type { IterationContext } from './context.js'
|
|
@@ -169,7 +169,7 @@ export async function runAdvisoryPhase(
|
|
|
169
169
|
|
|
170
170
|
sections.push('</advisory-result>')
|
|
171
171
|
|
|
172
|
-
ctx.runMgr.pushMessage(
|
|
172
|
+
ctx.runMgr.pushMessage(createRuntimeContextMessage(sections.join('\n'), 'advisory'))
|
|
173
173
|
|
|
174
174
|
ctx.log.info('Advisory phase completed', {
|
|
175
175
|
[NAMZU.RUN_ID]: ctx.runMgr.id,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { AuthorizationGate } from '../../../../authorization/index.js'
|
|
2
|
+
import type { ToolCallSummary } from '../../../../types/hitl/index.js'
|
|
2
3
|
import type { ChatCompletionResponse } from '../../../../types/provider/index.js'
|
|
3
4
|
import type { RunEvent } from '../../../../types/run/index.js'
|
|
4
|
-
import type { ToolCallDenials } from '../../executor.js'
|
|
5
|
+
import type { PreparedToolBatch, ToolCallDenials } from '../../executor.js'
|
|
5
6
|
import {
|
|
6
7
|
awaitProjectInstructionCallback,
|
|
7
8
|
replaceProjectInstructionSnapshot,
|
|
@@ -64,28 +65,42 @@ export async function* runToolReview(
|
|
|
64
65
|
return finish('executed')
|
|
65
66
|
}
|
|
66
67
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
68
|
+
const prepareForReview = async (): Promise<PreparedToolBatch | undefined> => {
|
|
69
|
+
const prepare = ctx.toolExecutor.prepareBatchForReview
|
|
70
|
+
return typeof prepare === 'function' ? prepare.call(ctx.toolExecutor, response) : undefined
|
|
71
|
+
}
|
|
72
|
+
let preparedBatch = await prepareForReview()
|
|
73
|
+
const summariesFor = (prepared: PreparedToolBatch | undefined): ToolCallSummary[] => {
|
|
74
|
+
const calls =
|
|
75
|
+
prepared?.reviewCalls ??
|
|
76
|
+
toolCalls.map((tc) => {
|
|
77
|
+
let input: unknown
|
|
78
|
+
try {
|
|
79
|
+
input = JSON.parse(tc.function.arguments)
|
|
80
|
+
} catch {
|
|
81
|
+
input = tc.function.arguments
|
|
82
|
+
}
|
|
83
|
+
return { id: tc.id, name: tc.function.name, input }
|
|
84
|
+
})
|
|
85
|
+
return calls.map((tc) => {
|
|
86
|
+
const tool = ctx.tools.get(tc.name)
|
|
87
|
+
const isDestructive = tool?.isDestructive ? tool.isDestructive(tc.input) : false
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
id: tc.id,
|
|
91
|
+
name: tc.name,
|
|
92
|
+
input: tc.input,
|
|
93
|
+
isDestructive,
|
|
94
|
+
authorization: { decision: 'review' },
|
|
95
|
+
}
|
|
96
|
+
})
|
|
97
|
+
}
|
|
98
|
+
let toolCallSummaries = summariesFor(preparedBatch)
|
|
84
99
|
|
|
85
100
|
/** Executes the batch, answering every call, and appends the results. */
|
|
86
101
|
const settle = async (denials?: ToolCallDenials): Promise<void> => {
|
|
87
102
|
const startedAt = Date.now()
|
|
88
|
-
const batch = await ctx.toolExecutor.executeBatch(response, denials)
|
|
103
|
+
const batch = await ctx.toolExecutor.executeBatch(response, denials, undefined, preparedBatch)
|
|
89
104
|
toolMs += Date.now() - startedAt
|
|
90
105
|
executed = batch.results
|
|
91
106
|
// Recorded AFTER execution and never before it: this advises, it does
|
|
@@ -130,6 +145,12 @@ export async function* runToolReview(
|
|
|
130
145
|
}
|
|
131
146
|
}
|
|
132
147
|
|
|
148
|
+
if (toolCallSummaries.length === 0) {
|
|
149
|
+
await settle()
|
|
150
|
+
yield* ctx.drainPending()
|
|
151
|
+
return finish('executed')
|
|
152
|
+
}
|
|
153
|
+
|
|
133
154
|
/** Every call denied for the same reason (human rejection, gate stop). */
|
|
134
155
|
const denyAll = (reason: string): ToolCallDenials =>
|
|
135
156
|
new Map(toolCalls.map((tc) => [tc.id, reason]))
|
|
@@ -159,10 +180,16 @@ export async function* runToolReview(
|
|
|
159
180
|
toolDef: ctx.tools.get(tc.name),
|
|
160
181
|
}),
|
|
161
182
|
}))
|
|
183
|
+
for (const { toolCall, gateResult } of gateResults) {
|
|
184
|
+
toolCall.authorization = {
|
|
185
|
+
decision: gateResult.decision,
|
|
186
|
+
...(gateResult.reason ? { reason: gateResult.reason } : {}),
|
|
187
|
+
}
|
|
188
|
+
}
|
|
162
189
|
|
|
163
190
|
for (const gr of gateResults) {
|
|
164
191
|
if (gr.gateResult.decision === 'deny') {
|
|
165
|
-
const reason = `Blocked by the
|
|
192
|
+
const reason = `Blocked by the authorization gate: ${gr.gateResult.reason}`
|
|
166
193
|
gateDenied.set(gr.toolCall.id, reason)
|
|
167
194
|
// A gate denial is a refusal — first-class in the audit trail, never
|
|
168
195
|
// an absent record (LOG-14, design §5). Written here, once per
|
|
@@ -180,7 +207,7 @@ export async function* runToolReview(
|
|
|
180
207
|
const allDenied = gateResults.every((gr) => gr.gateResult.decision === 'deny')
|
|
181
208
|
|
|
182
209
|
if (allAllowed) {
|
|
183
|
-
ctx.log.debug('
|
|
210
|
+
ctx.log.debug('Authorization gate: all tool calls pre-approved', {
|
|
184
211
|
'namzu.tool.names': gateResults.map((gr) => gr.toolCall.name),
|
|
185
212
|
})
|
|
186
213
|
await settle()
|
|
@@ -189,7 +216,7 @@ export async function* runToolReview(
|
|
|
189
216
|
}
|
|
190
217
|
|
|
191
218
|
if (allDenied) {
|
|
192
|
-
ctx.log.debug('
|
|
219
|
+
ctx.log.debug('Authorization gate: all tool calls denied', {
|
|
193
220
|
'namzu.tool.names': gateResults.map((gr) => gr.toolCall.name),
|
|
194
221
|
})
|
|
195
222
|
await settle(gateDenied)
|
|
@@ -197,7 +224,7 @@ export async function* runToolReview(
|
|
|
197
224
|
return finish('rejected')
|
|
198
225
|
}
|
|
199
226
|
|
|
200
|
-
ctx.log.debug('
|
|
227
|
+
ctx.log.debug('Authorization gate: mixed decisions, proceeding to review', {
|
|
201
228
|
'namzu.runtime.decisions': gateResults.map((gr) => ({
|
|
202
229
|
tool: gr.toolCall.name,
|
|
203
230
|
decision: gr.gateResult.decision,
|
|
@@ -267,6 +294,7 @@ export async function* runToolReview(
|
|
|
267
294
|
|
|
268
295
|
// Gate denials are the floor; per-call human denials add to them.
|
|
269
296
|
const denials = new Map(gateDenied)
|
|
297
|
+
const modifiedCallIds = new Set<string>()
|
|
270
298
|
|
|
271
299
|
for (const mod of reviewDecision.modifications) {
|
|
272
300
|
if (mod.action === 'modify' && mod.modifiedInput !== undefined) {
|
|
@@ -274,6 +302,7 @@ export async function* runToolReview(
|
|
|
274
302
|
// A modification cannot resurrect a gate-denied call.
|
|
275
303
|
if (tc && !denials.has(tc.id)) {
|
|
276
304
|
tc.function.arguments = JSON.stringify(mod.modifiedInput)
|
|
305
|
+
modifiedCallIds.add(tc.id)
|
|
277
306
|
}
|
|
278
307
|
}
|
|
279
308
|
if (mod.action === 'deny' && !denials.has(mod.toolCallId)) {
|
|
@@ -281,6 +310,41 @@ export async function* runToolReview(
|
|
|
281
310
|
}
|
|
282
311
|
}
|
|
283
312
|
|
|
313
|
+
// A human modification changes the raw call after the first preparation.
|
|
314
|
+
// Decode it once again, then require policy to explicitly allow the new
|
|
315
|
+
// executable value. A second nested review would be ambiguous: the human
|
|
316
|
+
// edited raw JSON, not an unseen schema transform of it.
|
|
317
|
+
if (modifiedCallIds.size > 0) {
|
|
318
|
+
const reprepare = ctx.toolExecutor.reprepareBatchForReview
|
|
319
|
+
preparedBatch =
|
|
320
|
+
preparedBatch && typeof reprepare === 'function'
|
|
321
|
+
? await reprepare.call(ctx.toolExecutor, response, preparedBatch, modifiedCallIds)
|
|
322
|
+
: await prepareForReview()
|
|
323
|
+
toolCallSummaries = summariesFor(preparedBatch)
|
|
324
|
+
}
|
|
325
|
+
if (ctx.verificationGate && modifiedCallIds.size > 0) {
|
|
326
|
+
for (const summary of toolCallSummaries) {
|
|
327
|
+
if (!modifiedCallIds.has(summary.id)) continue
|
|
328
|
+
if (denials.has(summary.id)) continue
|
|
329
|
+
const gateResult = ctx.verificationGate.evaluate({
|
|
330
|
+
toolName: summary.name,
|
|
331
|
+
toolInput: summary.input,
|
|
332
|
+
toolDef: ctx.tools.get(summary.name),
|
|
333
|
+
})
|
|
334
|
+
if (gateResult.decision === 'allow') continue
|
|
335
|
+
const reason =
|
|
336
|
+
gateResult.decision === 'deny'
|
|
337
|
+
? `Blocked by the authorization gate after the tool input was modified: ${gateResult.reason}`
|
|
338
|
+
: `Blocked by the authorization gate after the tool input was modified: the prepared value requires a new explicit approval. ${gateResult.reason}`
|
|
339
|
+
denials.set(summary.id, reason)
|
|
340
|
+
await ctx.runMgr.recordAudit({
|
|
341
|
+
what: { action: 'tool_call', tool: summary.name },
|
|
342
|
+
outcome: 'refused',
|
|
343
|
+
reason,
|
|
344
|
+
})
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
284
348
|
const everythingDenied = denials.size === toolCalls.length
|
|
285
349
|
await settle(denials)
|
|
286
350
|
yield* ctx.drainPending()
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { isProviderRequestError } from '../../../provider/errors.js'
|
|
2
|
+
import type {
|
|
3
|
+
ChatCompletionParams,
|
|
4
|
+
LLMProvider,
|
|
5
|
+
StreamChunk,
|
|
6
|
+
} from '../../../types/provider/index.js'
|
|
7
|
+
import {
|
|
8
|
+
type RequestImageIdentity,
|
|
9
|
+
findSingleRequestImage,
|
|
10
|
+
projectRequestWithoutImage,
|
|
11
|
+
} from '../request-rich-content.js'
|
|
12
|
+
|
|
13
|
+
const IMAGE_REJECTION_PATTERNS: readonly RegExp[] = [
|
|
14
|
+
/\binvalid[_ -]?image\b/i,
|
|
15
|
+
/\bimage\b.{0,100}\b(?:cannot|could not|failed to)\s+(?:be\s+)?(?:decoded|processed|loaded|parsed|read)\b/i,
|
|
16
|
+
/\b(?:cannot|could not|failed to)\s+(?:decode|process|load|parse|read)\b.{0,100}\bimage\b/i,
|
|
17
|
+
/\bimage\b.{0,100}\b(?:malformed|corrupt|unsupported format)\b/i,
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
type ImageRejectionConfidence = 'server-confirmed' | 'heuristic'
|
|
21
|
+
|
|
22
|
+
function classifyProviderRejectedImageError(error: unknown): ImageRejectionConfidence | null {
|
|
23
|
+
if (!isProviderRequestError(error) || error.kind !== 'bad_request') return null
|
|
24
|
+
if (error.status === 400 && error.providerCode === 'invalid_image') return 'server-confirmed'
|
|
25
|
+
const detail = error.detail ?? error.message
|
|
26
|
+
return IMAGE_REJECTION_PATTERNS.some((pattern) => pattern.test(detail)) ? 'heuristic' : null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isProviderRejectedImageError(error: unknown): boolean {
|
|
30
|
+
return classifyProviderRejectedImageError(error) !== null
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isAcceptedChunk(chunk: StreamChunk): boolean {
|
|
34
|
+
if (chunk.retry !== undefined || chunk.fallback !== undefined || chunk.error !== undefined) {
|
|
35
|
+
return false
|
|
36
|
+
}
|
|
37
|
+
return Boolean(
|
|
38
|
+
chunk.delta.content ||
|
|
39
|
+
chunk.delta.toolCalls?.length ||
|
|
40
|
+
chunk.delta.toolCallEnd ||
|
|
41
|
+
chunk.delta.reasoning ||
|
|
42
|
+
chunk.delta.citation ||
|
|
43
|
+
chunk.finishReason !== undefined ||
|
|
44
|
+
chunk.usage !== undefined ||
|
|
45
|
+
chunk.replayState !== undefined,
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function throwIfAborted(signal: AbortSignal | undefined): void {
|
|
50
|
+
if (signal?.aborted) throw signal.reason
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Retry one provider-invalid request without its only distinct image.
|
|
55
|
+
*
|
|
56
|
+
* The callback is the durable commit point for an HTTP 400 carrying the exact
|
|
57
|
+
* provider code `invalid_image`. It runs only after the retry has produced an
|
|
58
|
+
* accepted chunk or reached a clean EOF. A legacy phrase can still recover the
|
|
59
|
+
* current request, but cannot assert durable server proof.
|
|
60
|
+
*/
|
|
61
|
+
export async function* streamWithProviderRejectedImageRecovery(
|
|
62
|
+
provider: LLMProvider,
|
|
63
|
+
params: ChatCompletionParams,
|
|
64
|
+
onAccepted: (identity: RequestImageIdentity) => Promise<void>,
|
|
65
|
+
): AsyncIterable<StreamChunk> {
|
|
66
|
+
const candidate = findSingleRequestImage(params.messages)
|
|
67
|
+
if (candidate === null) {
|
|
68
|
+
yield* provider.chatStream(params)
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
let produced = false
|
|
73
|
+
let confidence: ImageRejectionConfidence | null = null
|
|
74
|
+
try {
|
|
75
|
+
for await (const chunk of provider.chatStream(params)) {
|
|
76
|
+
if (isAcceptedChunk(chunk)) produced = true
|
|
77
|
+
yield chunk
|
|
78
|
+
}
|
|
79
|
+
return
|
|
80
|
+
} catch (error) {
|
|
81
|
+
throwIfAborted(params.signal)
|
|
82
|
+
confidence = classifyProviderRejectedImageError(error)
|
|
83
|
+
if (produced || confidence === null) throw error
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
throwIfAborted(params.signal)
|
|
87
|
+
const retryParams: ChatCompletionParams = {
|
|
88
|
+
...params,
|
|
89
|
+
messages: projectRequestWithoutImage(params.messages, candidate),
|
|
90
|
+
}
|
|
91
|
+
let accepted = false
|
|
92
|
+
let retryReportedError = false
|
|
93
|
+
for await (const chunk of provider.chatStream(retryParams)) {
|
|
94
|
+
if (chunk.error !== undefined) retryReportedError = true
|
|
95
|
+
if (!accepted && isAcceptedChunk(chunk)) {
|
|
96
|
+
throwIfAborted(params.signal)
|
|
97
|
+
if (confidence === 'server-confirmed') await onAccepted(candidate)
|
|
98
|
+
accepted = true
|
|
99
|
+
}
|
|
100
|
+
yield chunk
|
|
101
|
+
}
|
|
102
|
+
if (!accepted && !retryReportedError) {
|
|
103
|
+
throwIfAborted(params.signal)
|
|
104
|
+
if (confidence === 'server-confirmed') await onAccepted(candidate)
|
|
105
|
+
}
|
|
106
|
+
}
|