@namzu/sdk 5.0.0 → 5.2.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 +463 -0
- package/README.md +2 -2
- package/dist/agents/__tests__/run-agent-forwarding.test.d.ts +2 -0
- package/dist/agents/__tests__/run-agent-forwarding.test.d.ts.map +1 -0
- package/dist/agents/__tests__/run-agent-forwarding.test.js +76 -0
- package/dist/agents/__tests__/run-agent-forwarding.test.js.map +1 -0
- package/dist/agents/__tests__/run-completed-stop-reason.test.d.ts +2 -0
- package/dist/agents/__tests__/run-completed-stop-reason.test.d.ts.map +1 -0
- package/dist/agents/__tests__/run-completed-stop-reason.test.js +50 -0
- package/dist/agents/__tests__/run-completed-stop-reason.test.js.map +1 -0
- package/dist/agents/index.d.ts +1 -0
- package/dist/agents/index.d.ts.map +1 -1
- package/dist/agents/index.js +4 -0
- package/dist/agents/index.js.map +1 -1
- package/dist/agents/runAgent.d.ts +34 -0
- package/dist/agents/runAgent.d.ts.map +1 -1
- package/dist/agents/runAgent.js +5 -3
- package/dist/agents/runAgent.js.map +1 -1
- package/dist/config/runtime.d.ts +30 -30
- package/dist/constants/tools/__tests__/dangerous-patterns.test.d.ts +2 -0
- package/dist/constants/tools/__tests__/dangerous-patterns.test.d.ts.map +1 -0
- package/dist/constants/tools/__tests__/dangerous-patterns.test.js +54 -0
- package/dist/constants/tools/__tests__/dangerous-patterns.test.js.map +1 -0
- package/dist/constants/tools/index.d.ts.map +1 -1
- package/dist/constants/tools/index.js +28 -1
- package/dist/constants/tools/index.js.map +1 -1
- package/dist/contracts/a2a.d.ts +44 -44
- package/dist/directory/__tests__/derive.test.d.ts +2 -0
- package/dist/directory/__tests__/derive.test.d.ts.map +1 -0
- package/dist/directory/__tests__/derive.test.js +111 -0
- package/dist/directory/__tests__/derive.test.js.map +1 -0
- package/dist/directory/__tests__/load-diagnostics.test.d.ts +2 -0
- package/dist/directory/__tests__/load-diagnostics.test.d.ts.map +1 -0
- package/dist/directory/__tests__/load-diagnostics.test.js +106 -0
- package/dist/directory/__tests__/load-diagnostics.test.js.map +1 -0
- package/dist/directory/__tests__/load-directory.test.d.ts +2 -0
- package/dist/directory/__tests__/load-directory.test.d.ts.map +1 -0
- package/dist/directory/__tests__/load-directory.test.js +196 -0
- package/dist/directory/__tests__/load-directory.test.js.map +1 -0
- package/dist/directory/__tests__/subagents.test.d.ts +2 -0
- package/dist/directory/__tests__/subagents.test.d.ts.map +1 -0
- package/dist/directory/__tests__/subagents.test.js +117 -0
- package/dist/directory/__tests__/subagents.test.js.map +1 -0
- package/dist/directory/__tests__/supervisor-budget.test.d.ts +2 -0
- package/dist/directory/__tests__/supervisor-budget.test.d.ts.map +1 -0
- package/dist/directory/__tests__/supervisor-budget.test.js +97 -0
- package/dist/directory/__tests__/supervisor-budget.test.js.map +1 -0
- package/dist/directory/derive-supervisor.d.ts +58 -0
- package/dist/directory/derive-supervisor.d.ts.map +1 -0
- package/dist/directory/derive-supervisor.js +88 -0
- package/dist/directory/derive-supervisor.js.map +1 -0
- package/dist/directory/derive.d.ts +12 -0
- package/dist/directory/derive.d.ts.map +1 -0
- package/dist/directory/derive.js +58 -0
- package/dist/directory/derive.js.map +1 -0
- package/dist/directory/index.d.ts +7 -0
- package/dist/directory/index.d.ts.map +1 -0
- package/dist/directory/index.js +5 -0
- package/dist/directory/index.js.map +1 -0
- package/dist/directory/load.d.ts +14 -0
- package/dist/directory/load.d.ts.map +1 -0
- package/dist/directory/load.js +488 -0
- package/dist/directory/load.js.map +1 -0
- package/dist/directory/scan.d.ts +36 -0
- package/dist/directory/scan.d.ts.map +1 -0
- package/dist/directory/scan.js +136 -0
- package/dist/directory/scan.js.map +1 -0
- package/dist/directory/types.d.ts +215 -0
- package/dist/directory/types.d.ts.map +1 -0
- package/dist/directory/types.js +8 -0
- package/dist/directory/types.js.map +1 -0
- package/dist/plugin/__tests__/path-containment.test.d.ts +2 -0
- package/dist/plugin/__tests__/path-containment.test.d.ts.map +1 -0
- package/dist/plugin/__tests__/path-containment.test.js +91 -0
- package/dist/plugin/__tests__/path-containment.test.js.map +1 -0
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +3 -3
- package/dist/plugin/lifecycle.js.map +1 -1
- package/dist/plugin/loader.d.ts.map +1 -1
- package/dist/plugin/loader.js +12 -2
- package/dist/plugin/loader.js.map +1 -1
- package/dist/provider/__tests__/model-version.test.d.ts +2 -0
- package/dist/provider/__tests__/model-version.test.d.ts.map +1 -0
- package/dist/provider/__tests__/model-version.test.js +80 -0
- package/dist/provider/__tests__/model-version.test.js.map +1 -0
- package/dist/provider/__tests__/strict-schema.test.d.ts +2 -0
- package/dist/provider/__tests__/strict-schema.test.d.ts.map +1 -0
- package/dist/provider/__tests__/strict-schema.test.js +109 -0
- package/dist/provider/__tests__/strict-schema.test.js.map +1 -0
- package/dist/provider/__tests__/typed-error-classification.test.d.ts +2 -0
- package/dist/provider/__tests__/typed-error-classification.test.d.ts.map +1 -0
- package/dist/provider/__tests__/typed-error-classification.test.js +123 -0
- package/dist/provider/__tests__/typed-error-classification.test.js.map +1 -0
- package/dist/provider/model-version.d.ts +60 -0
- package/dist/provider/model-version.d.ts.map +1 -0
- package/dist/provider/model-version.js +64 -0
- package/dist/provider/model-version.js.map +1 -0
- package/dist/provider/retry.d.ts.map +1 -1
- package/dist/provider/retry.js +23 -7
- package/dist/provider/retry.js.map +1 -1
- package/dist/provider/strict-schema.d.ts +49 -0
- package/dist/provider/strict-schema.d.ts.map +1 -0
- package/dist/provider/strict-schema.js +112 -0
- package/dist/provider/strict-schema.js.map +1 -0
- package/dist/public-runtime.d.ts +6 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +21 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/public-tools.d.ts +1 -0
- package/dist/public-tools.d.ts.map +1 -1
- package/dist/public-tools.js +4 -0
- package/dist/public-tools.js.map +1 -1
- package/dist/public-types.d.ts +2 -0
- package/dist/public-types.d.ts.map +1 -1
- package/dist/registry/tool/__tests__/strict-registration.test.d.ts +2 -0
- package/dist/registry/tool/__tests__/strict-registration.test.d.ts.map +1 -0
- package/dist/registry/tool/__tests__/strict-registration.test.js +82 -0
- package/dist/registry/tool/__tests__/strict-registration.test.js.map +1 -0
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +16 -0
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/runtime/query/__tests__/stream-recovery.test.js +5 -0
- package/dist/runtime/query/__tests__/stream-recovery.test.js.map +1 -1
- package/dist/runtime/query/result.d.ts.map +1 -1
- package/dist/runtime/query/result.js +5 -0
- package/dist/runtime/query/result.js.map +1 -1
- package/dist/tools/builtins/__tests__/edit-insert-reachable.test.js +7 -1
- package/dist/tools/builtins/__tests__/edit-insert-reachable.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/edit.test.js +1 -1
- package/dist/tools/builtins/__tests__/edit.test.js.map +1 -1
- package/dist/tools/builtins/edit.d.ts.map +1 -1
- package/dist/tools/builtins/edit.js +20 -2
- package/dist/tools/builtins/edit.js.map +1 -1
- package/dist/types/plugin/index.d.ts +4 -4
- package/dist/types/provider/errors.d.ts +0 -17
- package/dist/types/provider/errors.d.ts.map +1 -1
- package/dist/types/provider/errors.js +60 -0
- package/dist/types/provider/errors.js.map +1 -1
- package/dist/types/run/events.d.ts +17 -2
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/verification/index.d.ts +6 -6
- package/package.json +1 -1
- package/src/agents/__tests__/run-agent-forwarding.test.ts +85 -0
- package/src/agents/__tests__/run-completed-stop-reason.test.ts +59 -0
- package/src/agents/index.ts +8 -0
- package/src/agents/runAgent.ts +30 -4
- package/src/constants/tools/__tests__/dangerous-patterns.test.ts +62 -0
- package/src/constants/tools/index.ts +28 -1
- package/src/directory/__tests__/derive.test.ts +135 -0
- package/src/directory/__tests__/load-diagnostics.test.ts +126 -0
- package/src/directory/__tests__/load-directory.test.ts +243 -0
- package/src/directory/__tests__/subagents.test.ts +155 -0
- package/src/directory/__tests__/supervisor-budget.test.ts +117 -0
- package/src/directory/derive-supervisor.ts +149 -0
- package/src/directory/derive.ts +70 -0
- package/src/directory/index.ts +27 -0
- package/src/directory/load.ts +556 -0
- package/src/directory/scan.ts +163 -0
- package/src/directory/types.ts +254 -0
- package/src/plugin/__tests__/path-containment.test.ts +107 -0
- package/src/plugin/lifecycle.ts +3 -3
- package/src/plugin/loader.ts +12 -2
- package/src/provider/__tests__/model-version.test.ts +93 -0
- package/src/provider/__tests__/strict-schema.test.ts +133 -0
- package/src/provider/__tests__/typed-error-classification.test.ts +143 -0
- package/src/provider/model-version.ts +98 -0
- package/src/provider/retry.ts +23 -7
- package/src/provider/strict-schema.ts +125 -0
- package/src/public-runtime.ts +31 -0
- package/src/public-tools.ts +4 -0
- package/src/public-types.ts +8 -0
- package/src/registry/tool/__tests__/strict-registration.test.ts +115 -0
- package/src/registry/tool/execute.ts +16 -0
- package/src/runtime/query/__tests__/stream-recovery.test.ts +5 -0
- package/src/runtime/query/result.ts +5 -0
- package/src/tools/builtins/__tests__/edit-insert-reachable.test.ts +8 -2
- package/src/tools/builtins/__tests__/edit.test.ts +1 -1
- package/src/tools/builtins/edit.ts +20 -2
- package/src/types/provider/errors.ts +71 -0
- package/src/types/run/events.ts +15 -2
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { z } from 'zod'
|
|
3
|
+
|
|
4
|
+
import type { ToolDefinition } from '../../../types/tool/index.js'
|
|
5
|
+
import { ToolRegistry } from '../execute.js'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* A tool that asks for constrained generation and hands over a schema the
|
|
9
|
+
* constrained dialect cannot express is wrong at the moment it is DECLARED,
|
|
10
|
+
* whichever model it later meets.
|
|
11
|
+
*
|
|
12
|
+
* The first attempt at this check lived in a provider driver. That caught the
|
|
13
|
+
* bug — but per request, in one of the two drivers that mark tools strict, and
|
|
14
|
+
* only once something actually ran. The registry already refused
|
|
15
|
+
* `enforceModelInput` without a `modelInputSchema`, with a comment stating the
|
|
16
|
+
* principle exactly: "Refusing at registration puts the error where the author
|
|
17
|
+
* can fix it rather than at the first request." The rule was written down; the
|
|
18
|
+
* check was in the wrong place.
|
|
19
|
+
*
|
|
20
|
+
* So the pair is here now. One asks whether a model schema EXISTS, the other
|
|
21
|
+
* whether it can carry the guarantee the tool just requested.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
function tool(overrides: Partial<ToolDefinition>): ToolDefinition {
|
|
25
|
+
return {
|
|
26
|
+
name: 'sample',
|
|
27
|
+
description: 'a tool',
|
|
28
|
+
inputSchema: z.object({}),
|
|
29
|
+
execute: async () => ({ success: true, output: 'ok' }),
|
|
30
|
+
...overrides,
|
|
31
|
+
} as ToolDefinition
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
describe('a tool cannot register a schema its own guarantee cannot carry', () => {
|
|
35
|
+
it('refuses a keyword outside the strict subset, naming the path', () => {
|
|
36
|
+
const registry = new ToolRegistry()
|
|
37
|
+
|
|
38
|
+
expect(() =>
|
|
39
|
+
registry.register(
|
|
40
|
+
tool({
|
|
41
|
+
name: 'edit',
|
|
42
|
+
enforceModelInput: true,
|
|
43
|
+
modelInputSchema: {
|
|
44
|
+
type: 'object',
|
|
45
|
+
properties: { insertLine: { oneOf: [{ type: 'integer' }, { const: 'end' }] } },
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
),
|
|
49
|
+
).toThrow(/edit\.properties\.insertLine\.oneOf/)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
it('names the remedy, not just the offence', () => {
|
|
53
|
+
const registry = new ToolRegistry()
|
|
54
|
+
|
|
55
|
+
expect(() =>
|
|
56
|
+
registry.register(
|
|
57
|
+
tool({ enforceModelInput: true, modelInputSchema: { properties: { n: { minimum: 0 } } } }),
|
|
58
|
+
),
|
|
59
|
+
).toThrow(/enforce at execution/)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('admits the same union spelled the way the subset accepts', () => {
|
|
63
|
+
const registry = new ToolRegistry()
|
|
64
|
+
|
|
65
|
+
expect(() =>
|
|
66
|
+
registry.register(
|
|
67
|
+
tool({
|
|
68
|
+
enforceModelInput: true,
|
|
69
|
+
modelInputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: { insertLine: { anyOf: [{ type: 'integer' }, { const: 'end' }] } },
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
},
|
|
74
|
+
}),
|
|
75
|
+
),
|
|
76
|
+
).not.toThrow()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
it('leaves a tool that never asked for the guarantee alone', () => {
|
|
80
|
+
// Without `enforceModelInput` nothing is marked strict, so the schema is
|
|
81
|
+
// sent as ordinary JSON Schema and `oneOf` is perfectly legal there.
|
|
82
|
+
// Refusing it would break working setups for no reason.
|
|
83
|
+
const registry = new ToolRegistry()
|
|
84
|
+
|
|
85
|
+
expect(() =>
|
|
86
|
+
registry.register(
|
|
87
|
+
tool({ modelInputSchema: { properties: { a: { oneOf: [{ type: 'string' }] } } } }),
|
|
88
|
+
),
|
|
89
|
+
).not.toThrow()
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
it('still refuses enforcement with no model schema at all', () => {
|
|
93
|
+
// The check this one was added beside. Kept in the same file so a
|
|
94
|
+
// future edit sees both halves of the pair together.
|
|
95
|
+
const registry = new ToolRegistry()
|
|
96
|
+
|
|
97
|
+
expect(() => registry.register(tool({ enforceModelInput: true }))).toThrow(
|
|
98
|
+
/does not define modelInputSchema/,
|
|
99
|
+
)
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('refuses through every registration shape', () => {
|
|
103
|
+
// `register` has three overloads and only one of them was exercised
|
|
104
|
+
// above; a check on the wrong one would look like coverage.
|
|
105
|
+
const bad = tool({
|
|
106
|
+
name: 'bad',
|
|
107
|
+
enforceModelInput: true,
|
|
108
|
+
modelInputSchema: { properties: { a: { oneOf: [] } } },
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
expect(() => new ToolRegistry().register(bad)).toThrow(/oneOf/)
|
|
112
|
+
expect(() => new ToolRegistry().register('bad', bad)).toThrow(/oneOf/)
|
|
113
|
+
expect(() => new ToolRegistry().register([bad])).toThrow(/oneOf/)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SpanStatusCode, context as otelContext, trace } from '@opentelemetry/api'
|
|
2
|
+
import { assertStrictSchema } from '../../provider/strict-schema.js'
|
|
2
3
|
import { GENAI, NAMZU, toolSpanName } from '../../telemetry/attributes.js'
|
|
3
4
|
import { recordToolCall } from '../../telemetry/metrics.js'
|
|
4
5
|
import { getTracer } from '../../telemetry/runtime-accessors.js'
|
|
@@ -177,6 +178,21 @@ export class ToolRegistry extends ManagedRegistry<ToolDefinition> {
|
|
|
177
178
|
`Tool "${id}" enables enforceModelInput but does not define modelInputSchema. Constrained input generation requires an explicit provider-safe model schema.`,
|
|
178
179
|
)
|
|
179
180
|
}
|
|
181
|
+
// …and the schema has to be one a constrained decoder can actually be
|
|
182
|
+
// given. The check above asks whether a model schema EXISTS; this asks
|
|
183
|
+
// whether it can carry the guarantee the tool just requested.
|
|
184
|
+
//
|
|
185
|
+
// Both belong here for the reason the comment above already states.
|
|
186
|
+
// Strict validation runs against a SUBSET of JSON Schema, and a keyword
|
|
187
|
+
// outside it is not degraded — the request is rejected whole, so one
|
|
188
|
+
// unexpressible field takes down every other tool in the call. The
|
|
189
|
+
// first version of this check lived in a provider driver, which meant
|
|
190
|
+
// it fired per request, in one driver, long after the author had moved
|
|
191
|
+
// on. A tool that asks for a guarantee its own schema cannot carry is
|
|
192
|
+
// wrong at the moment it is declared, whichever model it later meets.
|
|
193
|
+
if (tool.enforceModelInput) {
|
|
194
|
+
assertStrictSchema(id, tool.modelInputSchema)
|
|
195
|
+
}
|
|
180
196
|
if (tool.tier && this.tierConfig) {
|
|
181
197
|
const validIds = this.tierConfig.tiers.map((t) => t.id)
|
|
182
198
|
if (!validIds.includes(tool.tier)) {
|
|
@@ -182,6 +182,11 @@ describe('query stream recovery', () => {
|
|
|
182
182
|
const run = await drainQuery(
|
|
183
183
|
{
|
|
184
184
|
provider: new ClassifiedFailureProvider(),
|
|
185
|
+
// Retry off: this pins METADATA at the boundary, and a throttle
|
|
186
|
+
// is genuinely retryable — leaving retry on would spend the run's
|
|
187
|
+
// whole timeout backing off and settle it as a timeout instead,
|
|
188
|
+
// testing the retry policy rather than the thing named here.
|
|
189
|
+
retry: { maxRetries: 0 },
|
|
185
190
|
tools: new ToolRegistry(),
|
|
186
191
|
runConfig: {
|
|
187
192
|
model: 'mock-model',
|
|
@@ -47,6 +47,11 @@ export class ResultAssembler {
|
|
|
47
47
|
type: 'run_completed',
|
|
48
48
|
runId: runMgr.id,
|
|
49
49
|
result: runMgr.getRun().result ?? '',
|
|
50
|
+
// Read AFTER `markCompleted`, which is where a run that was stopped
|
|
51
|
+
// mid-flight has its reason settled. Carried on the event so a
|
|
52
|
+
// consumer can tell "answered" from "ran out of budget" without
|
|
53
|
+
// holding the `Run`.
|
|
54
|
+
...(runMgr.getRun().stopReason ? { stopReason: runMgr.getRun().stopReason } : {}),
|
|
50
55
|
})
|
|
51
56
|
yield* drainPending()
|
|
52
57
|
|
|
@@ -70,12 +70,18 @@ describe('the model can emit the idiom the description recommends', () => {
|
|
|
70
70
|
})
|
|
71
71
|
|
|
72
72
|
it('admits only "end" as a string, so a synonym cannot be generated', () => {
|
|
73
|
-
const insert = (schema().properties as Record<string, {
|
|
73
|
+
const insert = (schema().properties as Record<string, { anyOf?: unknown[] }>).insertLine
|
|
74
74
|
|
|
75
75
|
// The schema is where the synonym problem is solved for a provider that
|
|
76
76
|
// constrains: `"EOF"` is not emittable because `"end"` is the only
|
|
77
77
|
// string the union admits.
|
|
78
|
-
|
|
78
|
+
//
|
|
79
|
+
// `anyOf`, not `oneOf`: strict tool use validates against a subset of
|
|
80
|
+
// JSON Schema that excludes `oneOf`, and the vendor rejects the whole
|
|
81
|
+
// request rather than one field — so the spelling here is load-bearing,
|
|
82
|
+
// not stylistic. The two are equivalent for disjoint branches.
|
|
83
|
+
// `minimum` is gone for the same reason; the execution schema keeps it.
|
|
84
|
+
expect(insert?.anyOf).toEqual([{ type: 'integer' }, { const: 'end' }])
|
|
79
85
|
})
|
|
80
86
|
})
|
|
81
87
|
|
|
@@ -39,7 +39,7 @@ describe('EditTool', () => {
|
|
|
39
39
|
'Exact replacement text. May be empty to delete old_string. Keep under 12000 characters.',
|
|
40
40
|
},
|
|
41
41
|
insertLine: {
|
|
42
|
-
|
|
42
|
+
anyOf: [{ type: 'integer' }, { const: 'end' }],
|
|
43
43
|
description:
|
|
44
44
|
'Insert instead of replacing. The new_string goes after this 1-indexed line; 0 inserts before the first line; "end" appends. Omit for a find-and-replace.',
|
|
45
45
|
},
|
|
@@ -102,12 +102,30 @@ const modelInputSchema: Record<string, unknown> = {
|
|
|
102
102
|
},
|
|
103
103
|
insertLine: {
|
|
104
104
|
// The union the execution schema already accepts, stated so a
|
|
105
|
-
// constrained decoder can emit it.
|
|
105
|
+
// constrained decoder can emit it. Stating it as a union of an
|
|
106
106
|
// integer and the literal `"end"` also makes the synonym problem
|
|
107
107
|
// structurally impossible: `"EOF"`, `"append"` and `"last"` are
|
|
108
108
|
// not emittable, because `"end"` is the only string the schema
|
|
109
109
|
// admits.
|
|
110
|
-
|
|
110
|
+
//
|
|
111
|
+
// `anyOf`, NOT `oneOf`. Strict tool use validates against a SUBSET
|
|
112
|
+
// of JSON Schema, and `oneOf` is not in it — the vendor rejects the
|
|
113
|
+
// whole request with `tools.N.custom: Schema type 'oneOf' is not
|
|
114
|
+
// supported`, so the tool never mounts and the turn dies before a
|
|
115
|
+
// single token. Measured against the live API: strict + `oneOf` is
|
|
116
|
+
// a 400, strict + `anyOf` is accepted, and non-strict + `oneOf` is
|
|
117
|
+
// accepted — which is why nothing caught it. Both halves were
|
|
118
|
+
// individually fine; only their combination fails, and strict is on
|
|
119
|
+
// for every model at or above the gate.
|
|
120
|
+
//
|
|
121
|
+
// The two branches are disjoint, so `anyOf` and `oneOf` mean the
|
|
122
|
+
// same thing here — nothing is loosened.
|
|
123
|
+
//
|
|
124
|
+
// `minimum` is gone for the same reason: numeric constraints are
|
|
125
|
+
// outside the strict subset too. The bound is not lost — the
|
|
126
|
+
// execution schema still enforces it, which is where a value that
|
|
127
|
+
// crosses a boundary should be checked anyway.
|
|
128
|
+
anyOf: [{ type: 'integer' }, { const: 'end' }],
|
|
111
129
|
description:
|
|
112
130
|
'Insert instead of replacing. The new_string goes after this 1-indexed line; 0 inserts before the first line; "end" appends. Omit for a find-and-replace.',
|
|
113
131
|
},
|
|
@@ -362,6 +362,62 @@ function codeFromStructure(err: unknown): ProviderErrorCode | undefined {
|
|
|
362
362
|
*
|
|
363
363
|
* Aborts are passed through untouched — see {@link isAbortError}.
|
|
364
364
|
*/
|
|
365
|
+
/**
|
|
366
|
+
* Every `ProviderErrorKind`, mapped to what the runtime acts on.
|
|
367
|
+
*
|
|
368
|
+
* Read structurally rather than with `instanceof`: a driver in one package
|
|
369
|
+
* throws this and the runtime in another reads it, and two copies of the SDK
|
|
370
|
+
* in one process make `instanceof` unreliable — the same reason
|
|
371
|
+
* `isProviderRequestError` exists.
|
|
372
|
+
*
|
|
373
|
+
* `retryable` here is about whether resending the SAME request could succeed.
|
|
374
|
+
* `context_overflow` is correctly false — an identical prompt overflows
|
|
375
|
+
* identically — and it maps to `context_length_exceeded` so the loop can reach
|
|
376
|
+
* for compaction, which is a different remedy than a retry.
|
|
377
|
+
*/
|
|
378
|
+
const KIND_TO_CODE: Readonly<Record<string, { code: ProviderErrorCode; retryable: boolean }>> = {
|
|
379
|
+
throttle: { code: 'rate_limit', retryable: true },
|
|
380
|
+
network: { code: 'network', retryable: true },
|
|
381
|
+
server: { code: 'server_error', retryable: true },
|
|
382
|
+
auth: { code: 'auth', retryable: false },
|
|
383
|
+
context_overflow: { code: 'context_length_exceeded', retryable: false },
|
|
384
|
+
bad_request: { code: 'invalid_request', retryable: false },
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function classifyFromProviderRequestError(
|
|
388
|
+
err: unknown,
|
|
389
|
+
providerId: string | undefined,
|
|
390
|
+
now: number,
|
|
391
|
+
): ProviderError | undefined {
|
|
392
|
+
if (!(err instanceof Error) || err.name !== 'ProviderRequestError') return undefined
|
|
393
|
+
const candidate = err as Error & {
|
|
394
|
+
kind?: unknown
|
|
395
|
+
providerId?: unknown
|
|
396
|
+
status?: unknown
|
|
397
|
+
retryAfterMs?: unknown
|
|
398
|
+
}
|
|
399
|
+
if (typeof candidate.kind !== 'string') return undefined
|
|
400
|
+
const mapped = KIND_TO_CODE[candidate.kind]
|
|
401
|
+
if (!mapped) return undefined
|
|
402
|
+
|
|
403
|
+
const retryAfterMs =
|
|
404
|
+
typeof candidate.retryAfterMs === 'number' ? candidate.retryAfterMs : readRetryAfterMs(err, now)
|
|
405
|
+
|
|
406
|
+
return new ProviderError({
|
|
407
|
+
code: mapped.code,
|
|
408
|
+
message: err.message,
|
|
409
|
+
retryable: mapped.retryable,
|
|
410
|
+
cause: err,
|
|
411
|
+
...(typeof candidate.providerId === 'string'
|
|
412
|
+
? { providerId: candidate.providerId }
|
|
413
|
+
: providerId !== undefined
|
|
414
|
+
? { providerId }
|
|
415
|
+
: {}),
|
|
416
|
+
...(typeof candidate.status === 'number' ? { status: candidate.status } : {}),
|
|
417
|
+
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
365
421
|
export function classifyProviderError(
|
|
366
422
|
err: unknown,
|
|
367
423
|
providerId?: string,
|
|
@@ -369,6 +425,21 @@ export function classifyProviderError(
|
|
|
369
425
|
): ProviderError {
|
|
370
426
|
if (isProviderError(err)) return err
|
|
371
427
|
|
|
428
|
+
// A driver that already classified its own failure is read FIRST, and by
|
|
429
|
+
// its `kind` — the field it set on purpose.
|
|
430
|
+
//
|
|
431
|
+
// Without this the classifier fell through to the status heuristics, where
|
|
432
|
+
// a `ProviderRequestError` carrying `kind: 'context_overflow'` and a 400
|
|
433
|
+
// became `invalid_request`, non-retryable. Three of the six kinds landed
|
|
434
|
+
// wrong that way, and the consequences were not cosmetic: the loop's
|
|
435
|
+
// overflow branch tests for `context_length_exceeded`, so compaction relief
|
|
436
|
+
// — the one provider failure this kernel can actually do something about —
|
|
437
|
+
// was unreachable for exactly the drivers that had diagnosed it correctly.
|
|
438
|
+
// A driver that classified its own error came out worse than one that did
|
|
439
|
+
// not, which is the opposite of the incentive the type was created for.
|
|
440
|
+
const fromKind = classifyFromProviderRequestError(err, providerId, now)
|
|
441
|
+
if (fromKind) return fromKind
|
|
442
|
+
|
|
372
443
|
const message = err instanceof Error ? err.message : String(err)
|
|
373
444
|
const status = readStatus(err)
|
|
374
445
|
const errno = readErrnoCode(err)
|
package/src/types/run/events.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { PlanStep } from '../plan/index.js'
|
|
|
16
16
|
import type { PluginHookEvent, PluginHookResult } from '../plugin/index.js'
|
|
17
17
|
import type { TaskStatus } from '../task/index.js'
|
|
18
18
|
import type { Lineage } from './lineage.js'
|
|
19
|
-
import type { MessageStopReason } from './stop-reason.js'
|
|
19
|
+
import type { MessageStopReason, StopReason } from './stop-reason.js'
|
|
20
20
|
import type {
|
|
21
21
|
SubsessionIdledEvent,
|
|
22
22
|
SubsessionMessagedEvent,
|
|
@@ -252,7 +252,20 @@ type CoreRunEvent =
|
|
|
252
252
|
guardrail?: string
|
|
253
253
|
reason?: string
|
|
254
254
|
}
|
|
255
|
-
|
|
255
|
+
/**
|
|
256
|
+
* The run reached its end without throwing.
|
|
257
|
+
*
|
|
258
|
+
* `completed` is not `succeeded`. A run stopped by its token budget, its
|
|
259
|
+
* timeout, its iteration cap, a cancellation or a blocking output guardrail
|
|
260
|
+
* all arrive here — `run_failed` is emitted only from the throw path — so a
|
|
261
|
+
* consumer that treated this event as success reported one for a run whose
|
|
262
|
+
* answer was refused. Measured: `max_iterations` produces
|
|
263
|
+
* `status: 'completed'` with `result` holding whatever partial text existed.
|
|
264
|
+
*
|
|
265
|
+
* `stopReason` is what separates them, and it is on the event because the
|
|
266
|
+
* alternative is asking every consumer to hold the `Run` as well.
|
|
267
|
+
*/
|
|
268
|
+
| { type: 'run_completed'; runId: RunId; result: string; stopReason?: StopReason }
|
|
256
269
|
/**
|
|
257
270
|
* The run failed.
|
|
258
271
|
*
|