@namzu/sdk 32.0.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 +127 -0
- 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/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/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/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/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 +2 -2
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +2 -2
- package/dist/public-runtime.js.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/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 +626 -161
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +46 -4
- package/dist/runtime/query/index.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/request-rich-content.d.ts.map +1 -1
- package/dist/runtime/query/request-rich-content.js +33 -13
- package/dist/runtime/query/request-rich-content.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/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/run-code.d.ts.map +1 -1
- package/dist/tools/builtins/run-code.js +9 -11
- 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/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 +1 -1
- package/dist/types/message/index.d.ts.map +1 -1
- package/dist/types/message/index.js +2 -1
- 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/interface.d.ts +14 -0
- package/dist/types/provider/interface.d.ts.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 +77 -6
- 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/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/execution/base.ts +124 -7
- 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/plugin/lifecycle.ts +9 -3
- package/src/process/kill-tree.ts +11 -13
- 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 +7 -1
- package/src/registry/tool/execute.ts +230 -64
- package/src/run/command-gate.ts +12 -0
- 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 +769 -163
- package/src/runtime/query/index.ts +53 -6
- package/src/runtime/query/iteration/phases/tool-review.ts +87 -23
- package/src/runtime/query/request-rich-content.ts +39 -14
- 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/tools/builtins/computer-use.ts +45 -2
- package/src/tools/builtins/run-code.ts +9 -11
- package/src/tools/builtins/skill.ts +325 -7
- package/src/tools/coordinator/agent.ts +108 -33
- 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 +3 -2
- package/src/types/plugin/index.ts +15 -0
- package/src/types/provider/interface.ts +18 -0
- package/src/types/sandbox/index.ts +15 -10
- package/src/types/tool/index.ts +85 -6
- package/src/types/workspace/ref.ts +5 -0
- package/src/utils/abort.ts +18 -0
|
@@ -7,10 +7,12 @@ import { isTrustedReadOnly } from '../../tools/trusted-read-only.js'
|
|
|
7
7
|
import type { ToolResultGuardrailSpec } from '../../types/guardrail/index.js'
|
|
8
8
|
import type {
|
|
9
9
|
LLMToolSchema,
|
|
10
|
+
PreparedToolExecution,
|
|
10
11
|
ToolAvailability,
|
|
11
12
|
ToolContext,
|
|
12
13
|
ToolDefinition,
|
|
13
14
|
ToolExecutionResult,
|
|
15
|
+
ToolPreparationResult,
|
|
14
16
|
ToolRegistryConfig,
|
|
15
17
|
ToolTierConfig,
|
|
16
18
|
} from '../../types/tool/index.js'
|
|
@@ -87,6 +89,82 @@ export function assertToolName(name: string): void {
|
|
|
87
89
|
)
|
|
88
90
|
}
|
|
89
91
|
|
|
92
|
+
function clonePreparedInput(value: unknown, freeze: boolean, path = '$'): unknown {
|
|
93
|
+
const active = new WeakSet<object>()
|
|
94
|
+
const copies = new WeakMap<object, object>()
|
|
95
|
+
|
|
96
|
+
const clone = (candidate: unknown, at: string): unknown => {
|
|
97
|
+
if (candidate === null || typeof candidate === 'string' || typeof candidate === 'boolean') {
|
|
98
|
+
return candidate
|
|
99
|
+
}
|
|
100
|
+
if (typeof candidate === 'number') {
|
|
101
|
+
if (!Number.isFinite(candidate) || Object.is(candidate, -0)) {
|
|
102
|
+
throw new TypeError(`${at} must be a finite JSON number`)
|
|
103
|
+
}
|
|
104
|
+
return candidate
|
|
105
|
+
}
|
|
106
|
+
if (typeof candidate !== 'object') {
|
|
107
|
+
throw new TypeError(`${at} must be a JSON value, not ${typeof candidate}`)
|
|
108
|
+
}
|
|
109
|
+
if (active.has(candidate)) throw new TypeError(`${at} contains a cycle`)
|
|
110
|
+
const existing = copies.get(candidate)
|
|
111
|
+
if (existing) return existing
|
|
112
|
+
|
|
113
|
+
active.add(candidate)
|
|
114
|
+
try {
|
|
115
|
+
if (Array.isArray(candidate)) {
|
|
116
|
+
const extra = Reflect.ownKeys(candidate).filter(
|
|
117
|
+
(key) => key !== 'length' && (typeof key !== 'string' || !/^(0|[1-9]\d*)$/.test(key)),
|
|
118
|
+
)
|
|
119
|
+
if (extra.length > 0) throw new TypeError(`${at} has non-JSON array properties`)
|
|
120
|
+
const result: unknown[] = new Array(candidate.length)
|
|
121
|
+
copies.set(candidate, result)
|
|
122
|
+
for (let index = 0; index < candidate.length; index++) {
|
|
123
|
+
const descriptor = Object.getOwnPropertyDescriptor(candidate, String(index))
|
|
124
|
+
if (!descriptor) throw new TypeError(`${at}[${index}] is a sparse array hole`)
|
|
125
|
+
if (!descriptor.enumerable || !('value' in descriptor)) {
|
|
126
|
+
throw new TypeError(`${at}[${index}] must be an enumerable data property`)
|
|
127
|
+
}
|
|
128
|
+
result[index] = clone(descriptor.value, `${at}[${index}]`)
|
|
129
|
+
}
|
|
130
|
+
if (freeze) Object.freeze(result)
|
|
131
|
+
return result
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const prototype = Object.getPrototypeOf(candidate)
|
|
135
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
136
|
+
throw new TypeError(`${at} must be a plain JSON object`)
|
|
137
|
+
}
|
|
138
|
+
// JSON does not preserve prototypes. Canonicalize both admitted plain
|
|
139
|
+
// shapes to an ordinary object so a disk checkpoint round-trip compares
|
|
140
|
+
// equal to a fresh preparation of the same semantic value. Properties
|
|
141
|
+
// are still defined explicitly, so a literal "__proto__" key remains
|
|
142
|
+
// data rather than changing this object's prototype.
|
|
143
|
+
const result: Record<string, unknown> = {}
|
|
144
|
+
copies.set(candidate, result)
|
|
145
|
+
for (const key of Reflect.ownKeys(candidate)) {
|
|
146
|
+
if (typeof key !== 'string') throw new TypeError(`${at} has a symbol property`)
|
|
147
|
+
const descriptor = Object.getOwnPropertyDescriptor(candidate, key)
|
|
148
|
+
if (!descriptor?.enumerable || !('value' in descriptor)) {
|
|
149
|
+
throw new TypeError(`${at}.${key} must be an enumerable data property`)
|
|
150
|
+
}
|
|
151
|
+
Object.defineProperty(result, key, {
|
|
152
|
+
value: clone(descriptor.value, `${at}.${key}`),
|
|
153
|
+
enumerable: true,
|
|
154
|
+
writable: !freeze,
|
|
155
|
+
configurable: !freeze,
|
|
156
|
+
})
|
|
157
|
+
}
|
|
158
|
+
if (freeze) Object.freeze(result)
|
|
159
|
+
return result
|
|
160
|
+
} finally {
|
|
161
|
+
active.delete(candidate)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return clone(value, path)
|
|
166
|
+
}
|
|
167
|
+
|
|
90
168
|
/**
|
|
91
169
|
* Two sources contributed the same tool name and neither may take it.
|
|
92
170
|
*
|
|
@@ -136,9 +214,17 @@ export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
|
|
|
136
214
|
private availability: Map<string, ToolAvailability> = new Map()
|
|
137
215
|
private tierConfig?: ToolTierConfig
|
|
138
216
|
private resultGuardrails?: readonly ToolResultGuardrailSpec[]
|
|
217
|
+
private readonly preparations = new WeakMap<
|
|
218
|
+
PreparedToolExecution,
|
|
219
|
+
{ readonly tool: ToolDefinition; readonly input: unknown }
|
|
220
|
+
>()
|
|
139
221
|
|
|
140
222
|
constructor(config?: ToolRegistryConfig) {
|
|
141
|
-
super({
|
|
223
|
+
super({
|
|
224
|
+
componentName: 'ToolRegistry',
|
|
225
|
+
idField: 'name',
|
|
226
|
+
logger: config?.logger,
|
|
227
|
+
})
|
|
142
228
|
this.tierConfig = config?.tierConfig
|
|
143
229
|
this.resultGuardrails = config?.resultGuardrails
|
|
144
230
|
}
|
|
@@ -401,10 +487,106 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
401
487
|
return this.getByAvailability(['active'], toolNames)
|
|
402
488
|
}
|
|
403
489
|
|
|
490
|
+
prepareExecution(toolName: string, rawInput: unknown): ToolPreparationResult {
|
|
491
|
+
const tool = this.getOrThrow(toolName)
|
|
492
|
+
const parseResult = tool.inputSchema.safeParse(rawInput)
|
|
493
|
+
if (!parseResult.success) {
|
|
494
|
+
return {
|
|
495
|
+
success: false,
|
|
496
|
+
result: this.validationFailure(tool, rawInput, parseResult.error),
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
let retainedInput: unknown
|
|
501
|
+
let reviewInput: unknown
|
|
502
|
+
try {
|
|
503
|
+
retainedInput = clonePreparedInput(parseResult.data, false)
|
|
504
|
+
reviewInput = clonePreparedInput(retainedInput, true)
|
|
505
|
+
} catch (err) {
|
|
506
|
+
const message = `Tool "${toolName}" produced an input that cannot be safely prepared for review and execution: ${toErrorMessage(err)}`
|
|
507
|
+
this.log.error('Prepared tool input could not be detached for review and execution', {
|
|
508
|
+
'namzu.tool.name': toolName,
|
|
509
|
+
'exception.message': toErrorMessage(err),
|
|
510
|
+
})
|
|
511
|
+
return {
|
|
512
|
+
success: false,
|
|
513
|
+
result: { success: false, output: '', error: message },
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const prepared = Object.freeze({ toolName, input: reviewInput })
|
|
518
|
+
this.preparations.set(prepared, { tool, input: retainedInput })
|
|
519
|
+
return { success: true, prepared }
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
async executePrepared(
|
|
523
|
+
prepared: PreparedToolExecution,
|
|
524
|
+
context: ToolContext,
|
|
525
|
+
): Promise<ToolExecutionResult> {
|
|
526
|
+
const retained = this.preparations.get(prepared)
|
|
527
|
+
if (!retained) {
|
|
528
|
+
return {
|
|
529
|
+
success: false,
|
|
530
|
+
output: '',
|
|
531
|
+
error: `Tool "${prepared.toolName}" preparation is not owned by this registry or is no longer valid.`,
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (this.get(prepared.toolName) !== retained.tool) {
|
|
535
|
+
return {
|
|
536
|
+
success: false,
|
|
537
|
+
output: '',
|
|
538
|
+
error: `Tool "${prepared.toolName}" changed after its input was reviewed; prepare the call again.`,
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return this.executeRetained(prepared.toolName, retained.tool, retained.input, context)
|
|
542
|
+
}
|
|
543
|
+
|
|
404
544
|
async execute(
|
|
405
545
|
toolName: string,
|
|
406
546
|
rawInput: unknown,
|
|
407
547
|
context: ToolContext,
|
|
548
|
+
): Promise<ToolExecutionResult> {
|
|
549
|
+
let preparation: ToolPreparationResult
|
|
550
|
+
try {
|
|
551
|
+
preparation = this.prepareExecution(toolName, rawInput)
|
|
552
|
+
} catch (err) {
|
|
553
|
+
return this.rejectPreparationWithClosedSpan(toolName, context, err)
|
|
554
|
+
}
|
|
555
|
+
if (!preparation.success) return preparation.result
|
|
556
|
+
return this.executePrepared(preparation.prepared, context)
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
private rejectPreparationWithClosedSpan(
|
|
560
|
+
toolName: string,
|
|
561
|
+
context: ToolContext,
|
|
562
|
+
err: unknown,
|
|
563
|
+
): Promise<never> {
|
|
564
|
+
const tracer = getTracer()
|
|
565
|
+
const parentCtx = context.parentSpan
|
|
566
|
+
? trace.setSpan(otelContext.active(), context.parentSpan)
|
|
567
|
+
: otelContext.active()
|
|
568
|
+
return tracer.startActiveSpan(toolSpanName(toolName), {}, parentCtx, async (span) => {
|
|
569
|
+
try {
|
|
570
|
+
span.setAttributes({
|
|
571
|
+
[GENAI.TOOL_NAME]: toolName,
|
|
572
|
+
[GENAI.TOOL_TYPE]: 'function',
|
|
573
|
+
...(context.toolUseId !== undefined ? { [GENAI.TOOL_CALL_ID]: context.toolUseId } : {}),
|
|
574
|
+
[NAMZU.TOOL_SUCCESS]: false,
|
|
575
|
+
[NAMZU.TOOL_ERROR]: toErrorMessage(err),
|
|
576
|
+
})
|
|
577
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: toErrorMessage(err) })
|
|
578
|
+
throw err
|
|
579
|
+
} finally {
|
|
580
|
+
span.end()
|
|
581
|
+
}
|
|
582
|
+
})
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
private async executeRetained(
|
|
586
|
+
toolName: string,
|
|
587
|
+
tool: ToolDefinition,
|
|
588
|
+
finalInput: unknown,
|
|
589
|
+
context: ToolContext,
|
|
408
590
|
): Promise<ToolExecutionResult> {
|
|
409
591
|
const tracer = getTracer()
|
|
410
592
|
|
|
@@ -437,8 +619,6 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
437
619
|
...(context.toolUseId !== undefined ? { [GENAI.TOOL_CALL_ID]: context.toolUseId } : {}),
|
|
438
620
|
})
|
|
439
621
|
|
|
440
|
-
const tool = this.getOrThrow(toolName)
|
|
441
|
-
|
|
442
622
|
const availability = this.getAvailability(toolName)
|
|
443
623
|
if (availability !== 'active') {
|
|
444
624
|
const msg = `Tool "${toolName}" is ${availability} and cannot be executed`
|
|
@@ -492,7 +672,7 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
492
672
|
|
|
493
673
|
const mode = context.permissionContext?.mode ?? 'auto'
|
|
494
674
|
if (mode === 'plan') {
|
|
495
|
-
const isReadOnly = isTrustedReadOnly(tool,
|
|
675
|
+
const isReadOnly = isTrustedReadOnly(tool, finalInput)
|
|
496
676
|
if (!isReadOnly) {
|
|
497
677
|
const msg = `plan mode: non-read-only tool "${toolName}" blocked`
|
|
498
678
|
span.setAttributes({
|
|
@@ -510,63 +690,6 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
510
690
|
}
|
|
511
691
|
}
|
|
512
692
|
|
|
513
|
-
const parseResult = tool.inputSchema.safeParse(rawInput)
|
|
514
|
-
if (!parseResult.success) {
|
|
515
|
-
const errorMessage = parseResult.error.issues
|
|
516
|
-
.map((i) => `${i.path.join('.')}: ${i.message}`)
|
|
517
|
-
.join('; ')
|
|
518
|
-
|
|
519
|
-
// Distinguish "model sent an empty/no-arg call" from
|
|
520
|
-
// "model sent partial args" — the first is most often a
|
|
521
|
-
// streaming hiccup or a definition-test ping (a provider
|
|
522
|
-
// occasionally pings tool surfaces with `{}` while the
|
|
523
|
-
// schema is still loading), the second is a genuine
|
|
524
|
-
// programming mistake by the model. The model self-
|
|
525
|
-
// corrects MUCH more reliably when the error tells it
|
|
526
|
-
// (a) which fields are required, (b) their types, and
|
|
527
|
-
// (c) a minimal example call. Without these hints the
|
|
528
|
-
// downstream UI just shows a red "Failed" row and the
|
|
529
|
-
// model rarely retries with the right args.
|
|
530
|
-
const isEmptyInput =
|
|
531
|
-
rawInput === null ||
|
|
532
|
-
rawInput === undefined ||
|
|
533
|
-
(typeof rawInput === 'object' &&
|
|
534
|
-
!Array.isArray(rawInput) &&
|
|
535
|
-
Object.keys(rawInput as Record<string, unknown>).length === 0)
|
|
536
|
-
|
|
537
|
-
const requiredHint = describeRequiredInput(tool.inputSchema)
|
|
538
|
-
// A conditional schema's required shape cannot be reconstructed
|
|
539
|
-
// from JSON Schema's top-level `required`, so the author gets to
|
|
540
|
-
// say what a valid retry looks like.
|
|
541
|
-
const recoveryHint = tool.validationErrorHint?.trim()
|
|
542
|
-
? ` ${tool.validationErrorHint.trim()}`
|
|
543
|
-
: ''
|
|
544
|
-
|
|
545
|
-
const enrichedMessage = isEmptyInput
|
|
546
|
-
? `Tool "${toolName}" was called with no arguments. ${requiredHint}${recoveryHint} Retry the call with the required parameters populated.`
|
|
547
|
-
: `Validation failed for "${toolName}": ${errorMessage}. ${requiredHint}${recoveryHint}`
|
|
548
|
-
|
|
549
|
-
this.log.error('Tool input validation failed', {
|
|
550
|
-
'namzu.tool.name': toolName,
|
|
551
|
-
'namzu.registry.errors': errorMessage,
|
|
552
|
-
'namzu.registry.empty': isEmptyInput,
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
span.setAttributes({
|
|
556
|
-
[NAMZU.TOOL_SUCCESS]: false,
|
|
557
|
-
[NAMZU.TOOL_ERROR]: `Validation: ${errorMessage}`,
|
|
558
|
-
})
|
|
559
|
-
span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage })
|
|
560
|
-
|
|
561
|
-
return {
|
|
562
|
-
success: false,
|
|
563
|
-
output: '',
|
|
564
|
-
error: enrichedMessage,
|
|
565
|
-
}
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
const finalInput = parseResult.data
|
|
569
|
-
|
|
570
693
|
try {
|
|
571
694
|
this.log.debug('Executing tool', { 'namzu.tool.name': toolName })
|
|
572
695
|
const startedAt = Date.now()
|
|
@@ -604,7 +727,10 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
604
727
|
)
|
|
605
728
|
if (!result.success && result.error) {
|
|
606
729
|
span.setAttribute(NAMZU.TOOL_ERROR, result.error)
|
|
607
|
-
span.setStatus({
|
|
730
|
+
span.setStatus({
|
|
731
|
+
code: SpanStatusCode.ERROR,
|
|
732
|
+
message: result.error,
|
|
733
|
+
})
|
|
608
734
|
} else {
|
|
609
735
|
span.setStatus({ code: SpanStatusCode.OK })
|
|
610
736
|
}
|
|
@@ -622,7 +748,10 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
622
748
|
[NAMZU.TOOL_SUCCESS]: false,
|
|
623
749
|
[NAMZU.TOOL_ERROR]: err.message,
|
|
624
750
|
})
|
|
625
|
-
span.setStatus({
|
|
751
|
+
span.setStatus({
|
|
752
|
+
code: SpanStatusCode.ERROR,
|
|
753
|
+
message: err.message,
|
|
754
|
+
})
|
|
626
755
|
throw err
|
|
627
756
|
}
|
|
628
757
|
const errorMessage = toErrorMessage(err)
|
|
@@ -635,7 +764,10 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
635
764
|
[NAMZU.TOOL_SUCCESS]: false,
|
|
636
765
|
[NAMZU.TOOL_ERROR]: errorMessage,
|
|
637
766
|
})
|
|
638
|
-
span.setStatus({
|
|
767
|
+
span.setStatus({
|
|
768
|
+
code: SpanStatusCode.ERROR,
|
|
769
|
+
message: errorMessage,
|
|
770
|
+
})
|
|
639
771
|
span.recordException(err instanceof Error ? err : new Error(errorMessage))
|
|
640
772
|
|
|
641
773
|
return {
|
|
@@ -655,6 +787,40 @@ Executable tool names, descriptions, and JSON input schemas are attached through
|
|
|
655
787
|
})
|
|
656
788
|
}
|
|
657
789
|
|
|
790
|
+
private validationFailure(
|
|
791
|
+
tool: ToolDefinition,
|
|
792
|
+
rawInput: unknown,
|
|
793
|
+
error: {
|
|
794
|
+
readonly issues: readonly {
|
|
795
|
+
readonly path: readonly PropertyKey[]
|
|
796
|
+
readonly message: string
|
|
797
|
+
}[]
|
|
798
|
+
},
|
|
799
|
+
): ToolExecutionResult {
|
|
800
|
+
const errorMessage = error.issues
|
|
801
|
+
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
802
|
+
.join('; ')
|
|
803
|
+
const isEmptyInput =
|
|
804
|
+
rawInput === null ||
|
|
805
|
+
rawInput === undefined ||
|
|
806
|
+
(typeof rawInput === 'object' &&
|
|
807
|
+
!Array.isArray(rawInput) &&
|
|
808
|
+
Object.keys(rawInput as Record<string, unknown>).length === 0)
|
|
809
|
+
const requiredHint = describeRequiredInput(tool.inputSchema)
|
|
810
|
+
const recoveryHint = tool.validationErrorHint?.trim()
|
|
811
|
+
? ` ${tool.validationErrorHint.trim()}`
|
|
812
|
+
: ''
|
|
813
|
+
const enrichedMessage = isEmptyInput
|
|
814
|
+
? `Tool "${tool.name}" was called with no arguments. ${requiredHint}${recoveryHint} Retry the call with the required parameters populated.`
|
|
815
|
+
: `Validation failed for "${tool.name}": ${errorMessage}. ${requiredHint}${recoveryHint}`
|
|
816
|
+
this.log.error('Tool input validation failed', {
|
|
817
|
+
'namzu.tool.name': tool.name,
|
|
818
|
+
'namzu.registry.errors': errorMessage,
|
|
819
|
+
'namzu.registry.empty': isEmptyInput,
|
|
820
|
+
})
|
|
821
|
+
return { success: false, output: '', error: enrichedMessage }
|
|
822
|
+
}
|
|
823
|
+
|
|
658
824
|
private getByAvailability(states: ToolAvailability[], filter?: string[]): ToolDefinition[] {
|
|
659
825
|
const candidates = filter ? filter.map((n) => this.getOrThrow(n)) : this.getAll()
|
|
660
826
|
return candidates.filter((t) => states.includes(this.getAvailability(t.name)))
|
package/src/run/command-gate.ts
CHANGED
|
@@ -130,6 +130,17 @@ function failureFeedback(
|
|
|
130
130
|
maxOutputChars: number,
|
|
131
131
|
): string {
|
|
132
132
|
const output = clipOutput(`${result.stdout}\n${result.stderr}`, maxOutputChars)
|
|
133
|
+
const truncatedStreams = [
|
|
134
|
+
result.stdoutTruncated === true ? 'stdout' : undefined,
|
|
135
|
+
result.stderrTruncated === true ? 'stderr' : undefined,
|
|
136
|
+
].filter((stream): stream is string => stream !== undefined)
|
|
137
|
+
const truncationWarning =
|
|
138
|
+
truncatedStreams.length === 0
|
|
139
|
+
? []
|
|
140
|
+
: [
|
|
141
|
+
'',
|
|
142
|
+
`Retained output is incomplete: ${truncatedStreams.join(' and ')} ${truncatedStreams.length === 1 ? 'was' : 'were'} truncated by the execution context. Re-run with a narrower command or filter to recover the missing diagnostics.`,
|
|
143
|
+
]
|
|
133
144
|
return [
|
|
134
145
|
`The answer was not accepted: \`${command}\` failed (attempt ${attempt}, exit ${result.exitCode}).`,
|
|
135
146
|
'',
|
|
@@ -137,6 +148,7 @@ function failureFeedback(
|
|
|
137
148
|
'```',
|
|
138
149
|
output || '(no output)',
|
|
139
150
|
'```',
|
|
151
|
+
...truncationWarning,
|
|
140
152
|
'',
|
|
141
153
|
'Fix the cause and then finish. Do not restate the failure back to me; change the code so the command passes.',
|
|
142
154
|
].join('\n')
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
* told to edit something it had already edited.
|
|
42
42
|
*
|
|
43
43
|
* So: a non-zero exit from any git invocation, a repository with no commits,
|
|
44
|
-
* a timeout, or output past the size
|
|
45
|
-
* partial hash. A truncated diff that
|
|
46
|
-
* outcome available here, because two
|
|
47
|
-
* same point collide.
|
|
44
|
+
* a timeout, an executor-reported truncated stream, or output past the size
|
|
45
|
+
* cap all produce `null` rather than a partial hash. A truncated diff that
|
|
46
|
+
* hashed successfully would be the worst outcome available here, because two
|
|
47
|
+
* different workspaces truncated at the same point collide.
|
|
48
48
|
*/
|
|
49
49
|
|
|
50
50
|
import { createHash } from 'node:crypto'
|
|
@@ -156,6 +156,7 @@ export async function fingerprintWorkspace(
|
|
|
156
156
|
// repository" and "no commits yet". All three mean the same thing to
|
|
157
157
|
// this function: it has no basis for a comparison.
|
|
158
158
|
if (result.exitCode !== 0) return null
|
|
159
|
+
if (result.stdoutTruncated === true || result.stderrTruncated === true) return null
|
|
159
160
|
if (Buffer.byteLength(result.stdout, 'utf8') > maxBytes) return null
|
|
160
161
|
return result.stdout
|
|
161
162
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { createMockBidiProvider } from './mock.js'
|
|
2
2
|
export type { MockBidiScript, MockBidiSession } from './mock.js'
|
|
3
|
-
export { startBidiRun } from './session.js'
|
|
3
|
+
export { BidiSessionCloseTimeoutError, startBidiRun } from './session.js'
|
|
4
4
|
export type { BidiRun, BidiRunParams } from './session.js'
|