@namzu/sdk 6.2.0 → 8.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 +677 -0
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +5 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +172 -158
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts +2 -0
- package/dist/agents/__tests__/supervisor-inbox-scope.test.d.ts.map +1 -0
- package/dist/agents/__tests__/supervisor-inbox-scope.test.js +125 -0
- package/dist/agents/__tests__/supervisor-inbox-scope.test.js.map +1 -0
- package/dist/agents/runAgent.d.ts +19 -1
- package/dist/agents/runAgent.d.ts.map +1 -1
- package/dist/agents/runAgent.js +2 -0
- package/dist/agents/runAgent.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +4 -0
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +24 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/connector/mcp/__tests__/positional-arrays.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/positional-arrays.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/positional-arrays.test.js +142 -0
- package/dist/connector/mcp/__tests__/positional-arrays.test.js.map +1 -0
- package/dist/connector/mcp/adapter.d.ts.map +1 -1
- package/dist/connector/mcp/adapter.js +123 -8
- package/dist/connector/mcp/adapter.js.map +1 -1
- package/dist/constants/agent/index.d.ts +5 -0
- package/dist/constants/agent/index.d.ts.map +1 -1
- package/dist/constants/agent/index.js +5 -0
- package/dist/constants/agent/index.js.map +1 -1
- package/dist/constants/plugin/index.d.ts +15 -0
- package/dist/constants/plugin/index.d.ts.map +1 -1
- package/dist/constants/plugin/index.js +15 -0
- package/dist/constants/plugin/index.js.map +1 -1
- package/dist/contracts/api.d.ts +1 -1
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/gateway/__tests__/completion-inbox.test.js +292 -2
- package/dist/gateway/__tests__/completion-inbox.test.js.map +1 -1
- package/dist/gateway/completion-inbox.d.ts +94 -6
- package/dist/gateway/completion-inbox.d.ts.map +1 -1
- package/dist/gateway/completion-inbox.js +235 -15
- package/dist/gateway/completion-inbox.js.map +1 -1
- package/dist/gateway/local.d.ts +11 -0
- package/dist/gateway/local.d.ts.map +1 -1
- package/dist/gateway/local.js +27 -1
- package/dist/gateway/local.js.map +1 -1
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +6 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +8 -0
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +12 -0
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/provider/thinking-support.d.ts +2 -1
- package/dist/provider/thinking-support.d.ts.map +1 -1
- package/dist/provider/thinking-support.js +14 -0
- package/dist/provider/thinking-support.js.map +1 -1
- package/dist/public-runtime.d.ts +1 -1
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +9 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +11 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js +142 -0
- package/dist/runtime/query/__tests__/completion-does-not-erase-the-answer.test.js.map +1 -0
- package/dist/runtime/query/__tests__/completion-notification.test.js +414 -32
- package/dist/runtime/query/__tests__/completion-notification.test.js.map +1 -1
- package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/context-size-on-the-wire.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js +100 -0
- package/dist/runtime/query/__tests__/context-size-on-the-wire.test.js.map +1 -0
- package/dist/runtime/query/__tests__/context.test.js +18 -0
- package/dist/runtime/query/__tests__/context.test.js.map +1 -1
- package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.js +118 -0
- package/dist/runtime/query/__tests__/effort-reaches-the-wire.test.js.map +1 -0
- package/dist/runtime/query/__tests__/tool-timeout.test.js +34 -0
- package/dist/runtime/query/__tests__/tool-timeout.test.js.map +1 -1
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +16 -1
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +11 -1
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/guard.d.ts +28 -0
- package/dist/runtime/query/guard.d.ts.map +1 -1
- package/dist/runtime/query/guard.js +31 -0
- package/dist/runtime/query/guard.js.map +1 -1
- package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts +2 -0
- package/dist/runtime/query/iteration/__tests__/settle-grace.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/__tests__/settle-grace.test.js +226 -0
- package/dist/runtime/query/iteration/__tests__/settle-grace.test.js.map +1 -0
- package/dist/runtime/query/iteration/index.d.ts +92 -0
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +818 -565
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js +95 -0
- package/dist/runtime/query/iteration/phases/__tests__/compaction-declined.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts +34 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +61 -4
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/telemetry/__tests__/model-call-span.test.js +22 -4
- package/dist/telemetry/__tests__/model-call-span.test.js.map +1 -1
- package/dist/telemetry/__tests__/span-closure.test.js +12 -5
- package/dist/telemetry/__tests__/span-closure.test.js.map +1 -1
- package/dist/tools/__tests__/untrusted-envelope.test.js +16 -0
- package/dist/tools/__tests__/untrusted-envelope.test.js.map +1 -1
- package/dist/tools/coordinator/__tests__/completion-delivery.test.js +117 -0
- package/dist/tools/coordinator/__tests__/completion-delivery.test.js.map +1 -1
- package/dist/tools/coordinator/__tests__/task-list.test.js +57 -0
- package/dist/tools/coordinator/__tests__/task-list.test.js.map +1 -1
- package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.d.ts +2 -0
- package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.js +193 -0
- package/dist/tools/coordinator/__tests__/wait-with-idle-bound.test.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +19 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +191 -71
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/tools/coordinator/wait-with-idle-bound.d.ts +66 -0
- package/dist/tools/coordinator/wait-with-idle-bound.d.ts.map +1 -0
- package/dist/tools/coordinator/wait-with-idle-bound.js +78 -0
- package/dist/tools/coordinator/wait-with-idle-bound.js.map +1 -0
- package/dist/tools/untrusted-envelope.d.ts.map +1 -1
- package/dist/tools/untrusted-envelope.js +9 -1
- package/dist/tools/untrusted-envelope.js.map +1 -1
- package/dist/types/agent/base.d.ts +16 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/gateway.d.ts +41 -0
- package/dist/types/agent/gateway.d.ts.map +1 -1
- package/dist/types/agent/lifecycle-event.d.ts +9 -1
- package/dist/types/agent/lifecycle-event.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +5 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/hitl/index.d.ts +10 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/probe/registry.d.ts +6 -0
- package/dist/types/probe/registry.d.ts.map +1 -1
- package/dist/types/provider/interface.d.ts +35 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/run/config.d.ts +25 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/entity.d.ts +16 -0
- package/dist/types/run/entity.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +75 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/prepare-step.d.ts +17 -2
- package/dist/types/run/prepare-step.d.ts.map +1 -1
- package/dist/types/verification/index.d.ts +98 -0
- package/dist/types/verification/index.d.ts.map +1 -1
- package/dist/types/verification/index.js +10 -0
- package/dist/types/verification/index.js.map +1 -1
- package/dist/utils/__tests__/abort-reason.test.d.ts +2 -0
- package/dist/utils/__tests__/abort-reason.test.d.ts.map +1 -0
- package/dist/utils/__tests__/abort-reason.test.js +48 -0
- package/dist/utils/__tests__/abort-reason.test.js.map +1 -0
- package/dist/utils/abort.d.ts +26 -0
- package/dist/utils/abort.d.ts.map +1 -1
- package/dist/utils/abort.js +34 -0
- package/dist/utils/abort.js.map +1 -1
- package/dist/verification/__tests__/argument-pattern.test.d.ts +2 -0
- package/dist/verification/__tests__/argument-pattern.test.d.ts.map +1 -0
- package/dist/verification/__tests__/argument-pattern.test.js +122 -0
- package/dist/verification/__tests__/argument-pattern.test.js.map +1 -0
- package/dist/verification/__tests__/rule-order-and-reason.test.d.ts +2 -0
- package/dist/verification/__tests__/rule-order-and-reason.test.d.ts.map +1 -0
- package/dist/verification/__tests__/rule-order-and-reason.test.js +126 -0
- package/dist/verification/__tests__/rule-order-and-reason.test.js.map +1 -0
- package/dist/verification/gate.d.ts +17 -1
- package/dist/verification/gate.d.ts.map +1 -1
- package/dist/verification/gate.js +102 -2
- package/dist/verification/gate.js.map +1 -1
- package/dist/verification/index.d.ts +1 -1
- package/dist/verification/index.d.ts.map +1 -1
- package/dist/verification/index.js +1 -1
- package/dist/verification/index.js.map +1 -1
- package/dist/verification/rules.d.ts.map +1 -1
- package/dist/verification/rules.js +27 -0
- package/dist/verification/rules.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/ReactiveAgent.ts +5 -0
- package/src/agents/SupervisorAgent.ts +175 -162
- package/src/agents/__tests__/supervisor-inbox-scope.test.ts +149 -0
- package/src/agents/runAgent.ts +22 -1
- package/src/bridge/a2a/mapper.ts +4 -0
- package/src/bridge/sse/mapper.ts +25 -0
- package/src/connector/mcp/__tests__/positional-arrays.test.ts +183 -0
- package/src/connector/mcp/adapter.ts +131 -7
- package/src/constants/agent/index.ts +5 -0
- package/src/constants/plugin/index.ts +15 -0
- package/src/contracts/api.ts +1 -0
- package/src/gateway/__tests__/completion-inbox.test.ts +348 -2
- package/src/gateway/completion-inbox.ts +248 -16
- package/src/gateway/local.ts +26 -1
- package/src/manager/agent/lifecycle.ts +6 -0
- package/src/manager/run/persistence.ts +12 -0
- package/src/provider/thinking-support.ts +19 -2
- package/src/public-runtime.ts +9 -0
- package/src/run/reporter.ts +12 -0
- package/src/runtime/query/__tests__/completion-does-not-erase-the-answer.test.ts +163 -0
- package/src/runtime/query/__tests__/completion-notification.test.ts +486 -34
- package/src/runtime/query/__tests__/context-size-on-the-wire.test.ts +122 -0
- package/src/runtime/query/__tests__/context.test.ts +24 -0
- package/src/runtime/query/__tests__/effort-reaches-the-wire.test.ts +135 -0
- package/src/runtime/query/__tests__/tool-timeout.test.ts +38 -0
- package/src/runtime/query/context.ts +16 -1
- package/src/runtime/query/executor.ts +11 -1
- package/src/runtime/query/guard.ts +32 -0
- package/src/runtime/query/iteration/__tests__/settle-grace.test.ts +265 -0
- package/src/runtime/query/iteration/index.ts +906 -635
- package/src/runtime/query/iteration/phases/__tests__/compaction-declined.test.ts +124 -0
- package/src/runtime/query/iteration/phases/compaction.ts +83 -10
- package/src/telemetry/__tests__/model-call-span.test.ts +22 -5
- package/src/telemetry/__tests__/span-closure.test.ts +12 -5
- package/src/tools/__tests__/untrusted-envelope.test.ts +23 -0
- package/src/tools/coordinator/__tests__/completion-delivery.test.ts +147 -0
- package/src/tools/coordinator/__tests__/task-list.test.ts +72 -0
- package/src/tools/coordinator/__tests__/wait-with-idle-bound.test.ts +247 -0
- package/src/tools/coordinator/index.ts +205 -78
- package/src/tools/coordinator/wait-with-idle-bound.ts +142 -0
- package/src/tools/untrusted-envelope.ts +9 -1
- package/src/types/agent/base.ts +17 -0
- package/src/types/agent/gateway.ts +42 -0
- package/src/types/agent/lifecycle-event.ts +7 -0
- package/src/types/agent/task.ts +5 -0
- package/src/types/hitl/index.ts +10 -0
- package/src/types/probe/registry.ts +6 -0
- package/src/types/provider/interface.ts +39 -0
- package/src/types/run/config.ts +26 -0
- package/src/types/run/entity.ts +17 -0
- package/src/types/run/events.ts +75 -0
- package/src/types/run/prepare-step.ts +17 -2
- package/src/types/verification/index.ts +61 -0
- package/src/utils/__tests__/abort-reason.test.ts +56 -0
- package/src/utils/abort.ts +34 -0
- package/src/verification/__tests__/argument-pattern.test.ts +158 -0
- package/src/verification/__tests__/rule-order-and-reason.test.ts +149 -0
- package/src/verification/gate.ts +106 -3
- package/src/verification/index.ts +1 -1
- package/src/verification/rules.ts +28 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { toSchemaDialect } from '../../../registry/tool/dialect.js'
|
|
4
|
+
import { renderToolSchema } from '../../../registry/tool/schema.js'
|
|
5
|
+
import type { MCPJsonSchema } from '../../../types/connector/index.js'
|
|
6
|
+
import { mcpJsonSchemaToZod } from '../adapter.js'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A bridged tool's schema makes a round trip — server JSON Schema → Zod →
|
|
10
|
+
* JSON Schema on the wire — so this file asserts what comes out the FAR end,
|
|
11
|
+
* not what the Zod type is. Two failures live at that far end and neither is
|
|
12
|
+
* visible from the Zod side:
|
|
13
|
+
*
|
|
14
|
+
* - whatever the conversion drops is dropped from what the MODEL is shown,
|
|
15
|
+
* and a positional array was being flattened to "an array of anything";
|
|
16
|
+
* - whatever it emits has to be a construct the receiving wire accepts, and
|
|
17
|
+
* a rejected tool schema fails the WHOLE request rather than degrading one
|
|
18
|
+
* tool. So a faithful conversion that cannot be sent is worse than a lossy
|
|
19
|
+
* one that can, which is why the tuple gate is narrow rather than eager.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
function wire(schema: MCPJsonSchema): Record<string, unknown> {
|
|
23
|
+
const rendered = renderToolSchema(mcpJsonSchemaToZod(schema))
|
|
24
|
+
const properties = toSchemaDialect(rendered, '2020-12').properties as Record<
|
|
25
|
+
string,
|
|
26
|
+
Record<string, unknown>
|
|
27
|
+
>
|
|
28
|
+
return properties.a as Record<string, unknown>
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const wrap = (a: Record<string, unknown>): MCPJsonSchema =>
|
|
32
|
+
({ type: 'object', properties: { a }, required: ['a'] }) as unknown as MCPJsonSchema
|
|
33
|
+
|
|
34
|
+
describe('a server that pinned its positions gets a tuple', () => {
|
|
35
|
+
it('carries the draft-07 spelling through to bounded prefixItems', () => {
|
|
36
|
+
expect(
|
|
37
|
+
wire(
|
|
38
|
+
wrap({
|
|
39
|
+
type: 'array',
|
|
40
|
+
items: [{ type: 'string' }, { type: 'number' }],
|
|
41
|
+
additionalItems: false,
|
|
42
|
+
minItems: 2,
|
|
43
|
+
maxItems: 2,
|
|
44
|
+
}),
|
|
45
|
+
),
|
|
46
|
+
).toEqual({
|
|
47
|
+
type: 'array',
|
|
48
|
+
minItems: 2,
|
|
49
|
+
maxItems: 2,
|
|
50
|
+
prefixItems: [{ type: 'string' }, { type: 'number' }],
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('reaches the identical wire shape from the 2020-12 spelling', () => {
|
|
55
|
+
// The two spellings say the same thing and a server may use either.
|
|
56
|
+
// Converging them is the point: a bridged tool should not be shown
|
|
57
|
+
// differently to the model because of which dialect its author wrote.
|
|
58
|
+
expect(
|
|
59
|
+
wire(
|
|
60
|
+
wrap({
|
|
61
|
+
type: 'array',
|
|
62
|
+
prefixItems: [{ type: 'string' }, { type: 'number' }],
|
|
63
|
+
items: false,
|
|
64
|
+
minItems: 2,
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
67
|
+
).toEqual({
|
|
68
|
+
type: 'array',
|
|
69
|
+
minItems: 2,
|
|
70
|
+
maxItems: 2,
|
|
71
|
+
prefixItems: [{ type: 'string' }, { type: 'number' }],
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
describe('a positional array the wire cannot carry keeps its shape in words', () => {
|
|
77
|
+
it('falls back when the server did not pin the length', () => {
|
|
78
|
+
// The inversion worth pinning: positional members do NOT constrain
|
|
79
|
+
// length. With no `minItems` the server is permitting a SHORTER array,
|
|
80
|
+
// and a tuple cannot express that — so an absent lower bound is a
|
|
81
|
+
// reason to fall back rather than a detail to round up.
|
|
82
|
+
const result = wire(
|
|
83
|
+
wrap({
|
|
84
|
+
type: 'array',
|
|
85
|
+
prefixItems: [{ type: 'string' }, { type: 'number' }],
|
|
86
|
+
}),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
expect(result.prefixItems).toBeUndefined()
|
|
90
|
+
expect(result.description).toContain('[0] string')
|
|
91
|
+
expect(result.description).toContain('[1] number')
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
it('appends the shape to the description rather than replacing it', () => {
|
|
95
|
+
// The case this exists for is a server that documented its argument
|
|
96
|
+
// WELL and used a positional array. Assigning the description would
|
|
97
|
+
// have deleted its sentence to make room for ours.
|
|
98
|
+
const result = wire(
|
|
99
|
+
wrap({
|
|
100
|
+
type: 'array',
|
|
101
|
+
prefixItems: [{ type: 'string' }, { type: 'number' }],
|
|
102
|
+
description: 'A coordinate pair.',
|
|
103
|
+
}),
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
expect(result.description).toContain('A coordinate pair.')
|
|
107
|
+
expect(result.description).toContain('[0] string')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
it('keeps the bounds the server did state', () => {
|
|
111
|
+
// The fallback is a ZodArray precisely so the ordinary constraint pass
|
|
112
|
+
// still carries `minItems`/`maxItems` onto it.
|
|
113
|
+
const result = wire(
|
|
114
|
+
wrap({ type: 'array', prefixItems: [{ type: 'string' }], minItems: 1, maxItems: 9 }),
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
expect(result.minItems).toBe(1)
|
|
118
|
+
expect(result.maxItems).toBe(9)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('names enums and literals in the description, not just types', () => {
|
|
122
|
+
const result = wire(
|
|
123
|
+
wrap({
|
|
124
|
+
type: 'array',
|
|
125
|
+
prefixItems: [{ enum: ['r', 'w'] }, { const: 7 }],
|
|
126
|
+
}),
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
expect(result.description).toContain('"r"|"w"')
|
|
130
|
+
expect(result.description).toContain('7')
|
|
131
|
+
})
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
describe('an ordinary list is untouched', () => {
|
|
135
|
+
it('still renders as a homogeneous array', () => {
|
|
136
|
+
expect(wire(wrap({ type: 'array', items: { type: 'string' } }))).toEqual({
|
|
137
|
+
type: 'array',
|
|
138
|
+
items: { type: 'string' },
|
|
139
|
+
})
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
describe('a deep schema cannot take the process down', () => {
|
|
144
|
+
// `MAX_CONVERSION_DEPTH` promised in its own comment that a node past the
|
|
145
|
+
// ceiling is "left permissive rather than the process being taken down by
|
|
146
|
+
// a stack overflow". That was false for arrays and for unions: the counter
|
|
147
|
+
// was never passed down the array path, and even where it WAS passed
|
|
148
|
+
// correctly — the union path — nothing compared it to anything, because
|
|
149
|
+
// the only comparison lived in the object branch a pure array or union
|
|
150
|
+
// never reaches. A remote server's tool listing is untrusted input, so
|
|
151
|
+
// this was reachable denial of service.
|
|
152
|
+
const nestArrays = (depth: number): MCPJsonSchema => {
|
|
153
|
+
let inner: Record<string, unknown> = { type: 'string' }
|
|
154
|
+
for (let i = 0; i < depth; i += 1) inner = { type: 'array', items: inner }
|
|
155
|
+
return wrap(inner)
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const nestUnions = (depth: number): MCPJsonSchema => {
|
|
159
|
+
let inner: Record<string, unknown> = { type: 'string' }
|
|
160
|
+
for (let i = 0; i < depth; i += 1) inner = { anyOf: [inner] }
|
|
161
|
+
return wrap(inner)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
it('survives a deeply nested array', () => {
|
|
165
|
+
expect(() => mcpJsonSchemaToZod(nestArrays(5_000))).not.toThrow()
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
it('survives a deeply nested union', () => {
|
|
169
|
+
expect(() => mcpJsonSchemaToZod(nestUnions(5_000))).not.toThrow()
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('still converts a shallow schema faithfully', () => {
|
|
173
|
+
// The guard must not be so eager that it flattens ordinary nesting.
|
|
174
|
+
const result = wire(
|
|
175
|
+
wrap({ type: 'array', items: { type: 'array', items: { type: 'number' } } }),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
expect(result).toEqual({
|
|
179
|
+
type: 'array',
|
|
180
|
+
items: { type: 'array', items: { type: 'number' } },
|
|
181
|
+
})
|
|
182
|
+
})
|
|
183
|
+
})
|
|
@@ -93,9 +93,20 @@ function jsonSchemaPropertyToZod(prop: unknown, depth = 0): z.ZodType {
|
|
|
93
93
|
base = base.nullable()
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
96
|
+
// Appended, not assigned. The conversion itself can produce a description
|
|
97
|
+
// — a positional array that could not be expressed as a tuple carries its
|
|
98
|
+
// shape here, because that prose is the only place the model learns it —
|
|
99
|
+
// and `.describe()` REPLACES. Overwriting would have silently deleted the
|
|
100
|
+
// note in exactly the case it exists for: a server that documented its
|
|
101
|
+
// argument well AND used a positional array.
|
|
102
|
+
const carried = base.description
|
|
103
|
+
const declared = schema.description
|
|
104
|
+
const parts = [
|
|
105
|
+
typeof declared === 'string' && declared.length > 0 ? declared : undefined,
|
|
106
|
+
carried,
|
|
107
|
+
].filter((part): part is string => typeof part === 'string' && part.length > 0)
|
|
108
|
+
if (parts.length > 0) {
|
|
109
|
+
base = base.describe(parts.join(' '))
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
if (schema.default !== undefined) {
|
|
@@ -195,6 +206,12 @@ function baseTypeToZod(schema: Record<string, unknown>, depth = 0): z.ZodType {
|
|
|
195
206
|
|
|
196
207
|
const composite = (schema.anyOf ?? schema.oneOf) as unknown[] | undefined
|
|
197
208
|
if (Array.isArray(composite) && composite.length > 0) {
|
|
209
|
+
// The ceiling has to be CHECKED here, not merely counted. `depth` was
|
|
210
|
+
// threaded correctly through this branch from the start, and a
|
|
211
|
+
// 5000-deep union still overflowed the stack — because the only
|
|
212
|
+
// comparison against `MAX_CONVERSION_DEPTH` lived in `objectToZod`,
|
|
213
|
+
// which a pure union never reaches.
|
|
214
|
+
if (depth >= MAX_CONVERSION_DEPTH) return z.unknown()
|
|
198
215
|
const members = composite.map((member) => jsonSchemaPropertyToZod(member, depth + 1))
|
|
199
216
|
return members.length === 1
|
|
200
217
|
? (members[0] as z.ZodType)
|
|
@@ -229,11 +246,37 @@ function baseTypeToZod(schema: Record<string, unknown>, depth = 0): z.ZodType {
|
|
|
229
246
|
case 'null':
|
|
230
247
|
return z.null()
|
|
231
248
|
case 'array': {
|
|
249
|
+
// Both the counter and the check. `depth` was never passed to the
|
|
250
|
+
// element conversion below, so the counter reset to zero on every
|
|
251
|
+
// array level — and even threaded it would not have helped, since
|
|
252
|
+
// nothing on this path compared it to anything. Measured before
|
|
253
|
+
// and after: a 5000-deep array schema took the process down with a
|
|
254
|
+
// stack overflow, which is a denial of service reachable from a
|
|
255
|
+
// remote server's tool listing.
|
|
256
|
+
if (depth >= MAX_CONVERSION_DEPTH) return z.array(z.unknown())
|
|
257
|
+
|
|
258
|
+
// A positional array has two spellings and a server may use
|
|
259
|
+
// either: draft-07 puts the member schemas in `items` with the
|
|
260
|
+
// tail rule in `additionalItems`, 2020-12 moved them to
|
|
261
|
+
// `prefixItems` with the tail rule in `items`.
|
|
262
|
+
const positional = Array.isArray(schema.prefixItems)
|
|
263
|
+
? (schema.prefixItems as unknown[])
|
|
264
|
+
: Array.isArray(schema.items)
|
|
265
|
+
? (schema.items as unknown[])
|
|
266
|
+
: undefined
|
|
267
|
+
if (positional) return positionalToZod(positional, schema, depth)
|
|
268
|
+
|
|
232
269
|
const items = schema.items
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
270
|
+
return z.array(
|
|
271
|
+
// A boolean `items` is a tail RULE, not an element schema — it
|
|
272
|
+
// only has meaning next to `prefixItems`, which was handled
|
|
273
|
+
// above. Reaching it here means the server closed an array
|
|
274
|
+
// that has no positions, and an unconstrained element type is
|
|
275
|
+
// the permissive reading of that.
|
|
276
|
+
items === undefined || typeof items === 'boolean'
|
|
277
|
+
? z.unknown()
|
|
278
|
+
: jsonSchemaPropertyToZod(items, depth + 1),
|
|
279
|
+
)
|
|
237
280
|
}
|
|
238
281
|
case 'object':
|
|
239
282
|
return objectToZod(schema, depth)
|
|
@@ -242,6 +285,87 @@ function baseTypeToZod(schema: Record<string, unknown>, depth = 0): z.ZodType {
|
|
|
242
285
|
}
|
|
243
286
|
}
|
|
244
287
|
|
|
288
|
+
/**
|
|
289
|
+
* How many positions we will express as a tuple.
|
|
290
|
+
*
|
|
291
|
+
* Not a correctness bound — a server may pin any arity it likes. It is a
|
|
292
|
+
* prompt-cost bound: every member renders its own schema into the tool
|
|
293
|
+
* definition the model is shown, and past a couple of dozen positions the
|
|
294
|
+
* thing being described is a data payload rather than a call signature. Past
|
|
295
|
+
* the cap the shape still reaches the model, in the description.
|
|
296
|
+
*/
|
|
297
|
+
const MAX_TUPLE_ARITY = 32
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* A positional array: a tuple when the server pinned it, a described list
|
|
301
|
+
* otherwise.
|
|
302
|
+
*
|
|
303
|
+
* This used to be `z.array(z.unknown())` unconditionally, so a server that
|
|
304
|
+
* spelled out `[string, number]` had the model told "an array of anything" —
|
|
305
|
+
* the positions, their types and their order all dropped from what the model
|
|
306
|
+
* reads, not merely from what is validated locally.
|
|
307
|
+
*
|
|
308
|
+
* The reason it is not simply converted is that the schema makes a ROUND TRIP:
|
|
309
|
+
* server JSON Schema → Zod → JSON Schema on the wire. So whatever is emitted
|
|
310
|
+
* here has to be a construct the receiving wire accepts, and a construct it
|
|
311
|
+
* rejects fails the ENTIRE request rather than degrading one tool — taking
|
|
312
|
+
* down every run that offered the toolset. A faithful conversion that cannot
|
|
313
|
+
* be sent is strictly worse than a lossy one that can.
|
|
314
|
+
*
|
|
315
|
+
* Hence the narrow gate. A tuple is emitted only where the server itself
|
|
316
|
+
* pinned the arity and closed the tail, because that renders as bounded
|
|
317
|
+
* `prefixItems` — the one positional shape measured as accepted, and the same
|
|
318
|
+
* shape a first-party builtin already ships. Everything else keeps the
|
|
319
|
+
* permissive array and gains the positional shape in its description.
|
|
320
|
+
*
|
|
321
|
+
* The subtlety worth stating, because it inverts the intuition: positional
|
|
322
|
+
* `items`/`prefixItems` does not constrain LENGTH. Without `minItems` the
|
|
323
|
+
* server is permitting a SHORTER array, and a tuple cannot express that — so
|
|
324
|
+
* an absent lower bound is a reason to fall back, not a detail to round up.
|
|
325
|
+
*/
|
|
326
|
+
function positionalToZod(
|
|
327
|
+
positional: readonly unknown[],
|
|
328
|
+
schema: Record<string, unknown>,
|
|
329
|
+
depth: number,
|
|
330
|
+
): z.ZodType {
|
|
331
|
+
// draft-07 spells the tail rule `additionalItems`; 2020-12 spells it
|
|
332
|
+
// `items`, which is only a tail rule when `prefixItems` holds the members.
|
|
333
|
+
const tail = Array.isArray(schema.items) ? schema.additionalItems : schema.items
|
|
334
|
+
|
|
335
|
+
const arity = positional.length
|
|
336
|
+
const pinnedLow = num(schema.minItems) === arity
|
|
337
|
+
const closedHigh = tail === false || num(schema.maxItems) === arity
|
|
338
|
+
|
|
339
|
+
if (arity === 0 || arity > MAX_TUPLE_ARITY || !pinnedLow || !closedHigh) {
|
|
340
|
+
// A ZodArray, deliberately: `applyConstraints` then carries the
|
|
341
|
+
// server's own `minItems`/`maxItems` onto it, so the loose case keeps
|
|
342
|
+
// whatever bounds the server did state.
|
|
343
|
+
return z.array(z.unknown()).describe(describePositional(positional))
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
const members = positional.map((member) => jsonSchemaPropertyToZod(member, depth + 1))
|
|
347
|
+
// Never `.rest()`. It renders a tail schema this wire has not been measured
|
|
348
|
+
// against, and the gate above has already established there is no tail.
|
|
349
|
+
return z.tuple(members as [z.ZodType, ...z.ZodType[]])
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/** The positional shape in prose, for the cases a tuple cannot carry. */
|
|
353
|
+
function describePositional(positional: readonly unknown[]): string {
|
|
354
|
+
const shape = positional
|
|
355
|
+
.map((member, index) => `[${index}] ${positionalTypeName(member)}`)
|
|
356
|
+
.join(', ')
|
|
357
|
+
return `Positional array — ${shape}.`
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function positionalTypeName(member: unknown): string {
|
|
361
|
+
if (typeof member !== 'object' || member === null) return 'any'
|
|
362
|
+
const schema = member as Record<string, unknown>
|
|
363
|
+
if (schema.const !== undefined) return JSON.stringify(schema.const)
|
|
364
|
+
if (Array.isArray(schema.enum)) return schema.enum.map((v) => JSON.stringify(v)).join('|')
|
|
365
|
+
const type = Array.isArray(schema.type) ? schema.type.join('|') : schema.type
|
|
366
|
+
return typeof type === 'string' ? type : 'any'
|
|
367
|
+
}
|
|
368
|
+
|
|
245
369
|
/**
|
|
246
370
|
* Flatten `allOf` members into one object schema.
|
|
247
371
|
*
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import type { AgentCapabilities } from '../../types/agent/base.js'
|
|
2
2
|
import type { AgentManagerConfig } from '../../types/agent/task.js'
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* **Nothing reads this.** No activity list is trimmed to it anywhere.
|
|
6
|
+
*
|
|
7
|
+
* @deprecated Unused. Removed in the next major.
|
|
8
|
+
*/
|
|
4
9
|
export const MAX_RECENT_ACTIVITIES = 5
|
|
5
10
|
|
|
6
11
|
export const AGENT_MANAGER_DEFAULTS: Readonly<AgentManagerConfig> = {
|
|
@@ -44,4 +44,19 @@ export const HOOK_TIMEOUT_MS = 5_000
|
|
|
44
44
|
* can sort itself after.
|
|
45
45
|
*/
|
|
46
46
|
export const DEFAULT_HOOK_PRIORITY = 100
|
|
47
|
+
/**
|
|
48
|
+
* **Nothing reads this.** Hooks run sequentially and always have.
|
|
49
|
+
*
|
|
50
|
+
* It reads as a concurrency cap that is in force, which is the misleading
|
|
51
|
+
* kind of dead: a reviewer sees a bound, assumes hook execution is batched
|
|
52
|
+
* at ten, and reasons about plugin behaviour that does not exist.
|
|
53
|
+
*
|
|
54
|
+
* Kept for one release rather than deleted, because it is reachable from the
|
|
55
|
+
* published typings. Do not "make it work" by batching hook execution —
|
|
56
|
+
* ordering is the contract hooks are written against, and parallelising them
|
|
57
|
+
* to justify a constant would change behaviour to match a number nobody
|
|
58
|
+
* chose deliberately.
|
|
59
|
+
*
|
|
60
|
+
* @deprecated Unused. Removed in the next major.
|
|
61
|
+
*/
|
|
47
62
|
export const HOOK_MAX_CONCURRENT = 10
|
package/src/contracts/api.ts
CHANGED