@namzu/sdk 0.4.4 → 0.5.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 +241 -0
- package/dist/advisory/executor.d.ts.map +1 -1
- package/dist/advisory/executor.js +3 -2
- package/dist/advisory/executor.js.map +1 -1
- package/dist/advisory/executor.test.js +36 -14
- package/dist/advisory/executor.test.js.map +1 -1
- package/dist/agents/ReactiveAgent.d.ts.map +1 -1
- package/dist/agents/ReactiveAgent.js +1 -0
- package/dist/agents/ReactiveAgent.js.map +1 -1
- package/dist/agents/RouterAgent.d.ts.map +1 -1
- package/dist/agents/RouterAgent.js +3 -2
- package/dist/agents/RouterAgent.js.map +1 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +2 -0
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +23 -9
- package/dist/bridge/a2a/mapper.js.map +1 -1
- package/dist/bridge/a2a/mapper.test.js +35 -9
- package/dist/bridge/a2a/mapper.test.js.map +1 -1
- package/dist/bridge/sse/mapper.d.ts.map +1 -1
- package/dist/bridge/sse/mapper.js +60 -8
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/bridge/sse/mapper.test.js +123 -16
- package/dist/bridge/sse/mapper.test.js.map +1 -1
- package/dist/compaction/verifier.d.ts.map +1 -1
- package/dist/compaction/verifier.js +3 -2
- package/dist/compaction/verifier.js.map +1 -1
- package/dist/config/runtime.d.ts +14 -14
- package/dist/config/runtime.js +1 -1
- package/dist/config/runtime.js.map +1 -1
- package/dist/contracts/api.d.ts +1 -1
- package/dist/contracts/api.d.ts.map +1 -1
- package/dist/contracts/schemas.js +1 -1
- package/dist/contracts/schemas.js.map +1 -1
- package/dist/gateway/local.d.ts +1 -1
- package/dist/gateway/local.d.ts.map +1 -1
- package/dist/gateway/local.js +1 -0
- package/dist/gateway/local.js.map +1 -1
- package/dist/manager/agent/__tests__/lifecycle.test.js +2 -2
- package/dist/provider/collect.d.ts +25 -0
- package/dist/provider/collect.d.ts.map +1 -0
- package/dist/provider/collect.js +82 -0
- package/dist/provider/collect.js.map +1 -0
- package/dist/provider/collect.test.d.ts +22 -0
- package/dist/provider/collect.test.d.ts.map +1 -0
- package/dist/provider/collect.test.js +123 -0
- package/dist/provider/collect.test.js.map +1 -0
- package/dist/provider/instrumentation.d.ts.map +1 -1
- package/dist/provider/instrumentation.js +10 -43
- package/dist/provider/instrumentation.js.map +1 -1
- package/dist/provider/instrumentation.test.d.ts +15 -0
- package/dist/provider/instrumentation.test.d.ts.map +1 -1
- package/dist/provider/instrumentation.test.js +73 -87
- package/dist/provider/instrumentation.test.js.map +1 -1
- package/dist/provider/mock.d.ts +1 -2
- package/dist/provider/mock.d.ts.map +1 -1
- package/dist/provider/mock.js +2 -5
- package/dist/provider/mock.js.map +1 -1
- package/dist/public-runtime.d.ts +1 -0
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +5 -0
- package/dist/public-runtime.js.map +1 -1
- package/dist/run/LimitChecker.test.d.ts +2 -0
- package/dist/run/LimitChecker.test.d.ts.map +1 -0
- package/dist/run/LimitChecker.test.js +26 -0
- package/dist/run/LimitChecker.test.js.map +1 -0
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +10 -6
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/query/__tests__/prompt.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/prompt.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/prompt.test.js +35 -0
- package/dist/runtime/query/__tests__/prompt.test.js.map +1 -0
- package/dist/runtime/query/context-cache.d.ts +2 -0
- package/dist/runtime/query/context-cache.d.ts.map +1 -1
- package/dist/runtime/query/context-cache.js +3 -0
- package/dist/runtime/query/context-cache.js.map +1 -1
- package/dist/runtime/query/events.d.ts +2 -0
- package/dist/runtime/query/events.d.ts.map +1 -1
- package/dist/runtime/query/events.js +48 -1
- package/dist/runtime/query/events.js.map +1 -1
- package/dist/runtime/query/executor.d.ts.map +1 -1
- package/dist/runtime/query/executor.js +55 -5
- package/dist/runtime/query/executor.js.map +1 -1
- package/dist/runtime/query/index.d.ts +2 -1
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +2 -0
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +245 -13
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +2 -0
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/prompt.d.ts +2 -0
- package/dist/runtime/query/prompt.d.ts.map +1 -1
- package/dist/runtime/query/prompt.js +35 -13
- package/dist/runtime/query/prompt.js.map +1 -1
- package/dist/session/__tests__/integration/e2e-spawn.test.js +2 -2
- package/dist/session/__tests__/integration/event-stream-ordering.test.d.ts +1 -1
- package/dist/session/__tests__/integration/event-stream-ordering.test.js +7 -7
- package/dist/streaming/coalesce.d.ts +28 -0
- package/dist/streaming/coalesce.d.ts.map +1 -0
- package/dist/streaming/coalesce.js +75 -0
- package/dist/streaming/coalesce.js.map +1 -0
- package/dist/streaming/coalesce.test.d.ts +19 -0
- package/dist/streaming/coalesce.test.d.ts.map +1 -0
- package/dist/streaming/coalesce.test.js +120 -0
- package/dist/streaming/coalesce.test.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +2 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +1 -0
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/types/agent/base.d.ts +7 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/gateway.d.ts +2 -1
- package/dist/types/agent/gateway.d.ts.map +1 -1
- package/dist/types/ids/index.d.ts +10 -0
- package/dist/types/ids/index.d.ts.map +1 -1
- package/dist/types/ids/index.js.map +1 -1
- package/dist/types/provider/interface.d.ts +26 -2
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/provider/stream.d.ts +18 -0
- package/dist/types/provider/stream.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +58 -8
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js +23 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/schema-version.d.ts +7 -1
- package/dist/types/run/schema-version.d.ts.map +1 -1
- package/dist/types/run/schema-version.js +7 -1
- package/dist/types/run/schema-version.js.map +1 -1
- package/dist/types/run/stop-reason.d.ts +9 -0
- package/dist/types/run/stop-reason.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/advisory/executor.test.ts +37 -15
- package/src/advisory/executor.ts +10 -7
- package/src/agents/ReactiveAgent.ts +1 -0
- package/src/agents/RouterAgent.ts +9 -6
- package/src/agents/SupervisorAgent.ts +2 -0
- package/src/bridge/a2a/mapper.test.ts +35 -9
- package/src/bridge/a2a/mapper.ts +23 -9
- package/src/bridge/sse/mapper.test.ts +152 -24
- package/src/bridge/sse/mapper.ts +66 -9
- package/src/compaction/verifier.ts +9 -6
- package/src/config/runtime.ts +1 -1
- package/src/contracts/api.ts +7 -0
- package/src/contracts/schemas.ts +1 -1
- package/src/gateway/local.ts +3 -2
- package/src/manager/agent/__tests__/lifecycle.test.ts +2 -2
- package/src/provider/collect.test.ts +142 -0
- package/src/provider/collect.ts +85 -0
- package/src/provider/instrumentation.test.ts +81 -100
- package/src/provider/instrumentation.ts +11 -53
- package/src/provider/mock.ts +2 -6
- package/src/public-runtime.ts +6 -0
- package/src/run/LimitChecker.test.ts +32 -0
- package/src/run/reporter.ts +10 -7
- package/src/runtime/query/__tests__/prompt.test.ts +38 -0
- package/src/runtime/query/context-cache.ts +5 -0
- package/src/runtime/query/events.ts +52 -1
- package/src/runtime/query/executor.ts +54 -5
- package/src/runtime/query/index.ts +5 -1
- package/src/runtime/query/iteration/index.ts +301 -26
- package/src/runtime/query/iteration/phases/compaction.ts +2 -0
- package/src/runtime/query/prompt.ts +45 -17
- package/src/session/__tests__/integration/e2e-spawn.test.ts +2 -2
- package/src/session/__tests__/integration/event-stream-ordering.test.ts +7 -7
- package/src/streaming/coalesce.test.ts +132 -0
- package/src/streaming/coalesce.ts +89 -0
- package/src/tools/coordinator/index.ts +3 -0
- package/src/types/agent/base.ts +9 -0
- package/src/types/agent/gateway.ts +3 -1
- package/src/types/ids/index.ts +10 -0
- package/src/types/provider/interface.ts +28 -3
- package/src/types/provider/stream.ts +18 -0
- package/src/types/run/events.ts +105 -9
- package/src/types/run/schema-version.ts +7 -1
- package/src/types/run/stop-reason.ts +17 -0
|
@@ -67,39 +67,43 @@ describe('mapRunToStreamEvent — mapped variants', () => {
|
|
|
67
67
|
expect(b).toEqual({ wire: 'iteration.completed', data: { run_id: RID, iteration: 2 } })
|
|
68
68
|
})
|
|
69
69
|
|
|
70
|
-
it('
|
|
71
|
-
const
|
|
72
|
-
{ type: 'llm_response', runId: RID, content: 'hi', hasToolCalls: false },
|
|
73
|
-
RID,
|
|
74
|
-
)
|
|
75
|
-
expect(r).toEqual({
|
|
76
|
-
wire: 'message.delta',
|
|
77
|
-
data: { run_id: RID, content: 'hi', has_tool_calls: false },
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
it('llm_response with null content → content: null', () => {
|
|
82
|
-
const r = mapRunToStreamEvent(
|
|
83
|
-
{ type: 'llm_response', runId: RID, content: null, hasToolCalls: true },
|
|
84
|
-
RID,
|
|
85
|
-
)
|
|
86
|
-
expect(r?.data).toMatchObject({ content: null, has_tool_calls: true })
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('tool_executing / tool_completed carry tool_name + input/result', () => {
|
|
70
|
+
it('tool_executing / tool_completed carry tool_use_id, tool_name, input/result, is_error', () => {
|
|
71
|
+
const TUID = 'toolu_x'
|
|
90
72
|
const exec = mapRunToStreamEvent(
|
|
91
|
-
{
|
|
73
|
+
{
|
|
74
|
+
type: 'tool_executing',
|
|
75
|
+
runId: RID,
|
|
76
|
+
toolUseId: TUID,
|
|
77
|
+
toolName: 'read_file',
|
|
78
|
+
input: { path: '/a' },
|
|
79
|
+
},
|
|
92
80
|
RID,
|
|
93
81
|
)
|
|
94
82
|
expect(exec?.wire).toBe('tool.executing')
|
|
95
|
-
expect(exec?.data).toMatchObject({
|
|
83
|
+
expect(exec?.data).toMatchObject({
|
|
84
|
+
tool_use_id: TUID,
|
|
85
|
+
tool_name: 'read_file',
|
|
86
|
+
input: { path: '/a' },
|
|
87
|
+
})
|
|
96
88
|
|
|
97
89
|
const done = mapRunToStreamEvent(
|
|
98
|
-
{
|
|
90
|
+
{
|
|
91
|
+
type: 'tool_completed',
|
|
92
|
+
runId: RID,
|
|
93
|
+
toolUseId: TUID,
|
|
94
|
+
toolName: 'read_file',
|
|
95
|
+
result: 'ok',
|
|
96
|
+
isError: false,
|
|
97
|
+
},
|
|
99
98
|
RID,
|
|
100
99
|
)
|
|
101
100
|
expect(done?.wire).toBe('tool.completed')
|
|
102
|
-
expect(done?.data).toMatchObject({
|
|
101
|
+
expect(done?.data).toMatchObject({
|
|
102
|
+
tool_use_id: TUID,
|
|
103
|
+
tool_name: 'read_file',
|
|
104
|
+
result: 'ok',
|
|
105
|
+
is_error: false,
|
|
106
|
+
})
|
|
103
107
|
})
|
|
104
108
|
|
|
105
109
|
it('tool_review_requested / tool_review_completed carry review fields', () => {
|
|
@@ -410,6 +414,130 @@ describe('mapRunToStreamEvent — explicit null set', () => {
|
|
|
410
414
|
})
|
|
411
415
|
})
|
|
412
416
|
|
|
417
|
+
describe('mapRunToStreamEvent — v3 message and tool-input lifecycle', () => {
|
|
418
|
+
const MID = 'msg_1' as `msg_${string}`
|
|
419
|
+
const TUID = 'toolu_a'
|
|
420
|
+
|
|
421
|
+
it('message_started → message.created', () => {
|
|
422
|
+
const r = mapRunToStreamEvent(
|
|
423
|
+
{ type: 'message_started', runId: RID, iteration: 0, messageId: MID },
|
|
424
|
+
RID,
|
|
425
|
+
)
|
|
426
|
+
expect(r?.wire).toBe('message.created')
|
|
427
|
+
expect(r?.data).toMatchObject({ run_id: RID, iteration: 0, message_id: MID })
|
|
428
|
+
})
|
|
429
|
+
|
|
430
|
+
it('text_delta → message.delta carries raw text fragment', () => {
|
|
431
|
+
const r = mapRunToStreamEvent(
|
|
432
|
+
{
|
|
433
|
+
type: 'text_delta',
|
|
434
|
+
runId: RID,
|
|
435
|
+
iteration: 0,
|
|
436
|
+
messageId: MID,
|
|
437
|
+
text: 'hel',
|
|
438
|
+
},
|
|
439
|
+
RID,
|
|
440
|
+
)
|
|
441
|
+
expect(r?.wire).toBe('message.delta')
|
|
442
|
+
expect(r?.data).toMatchObject({ message_id: MID, text: 'hel' })
|
|
443
|
+
})
|
|
444
|
+
|
|
445
|
+
it('message_completed → message.completed carries stop reason and usage', () => {
|
|
446
|
+
const usage = {
|
|
447
|
+
promptTokens: 10,
|
|
448
|
+
completionTokens: 5,
|
|
449
|
+
totalTokens: 15,
|
|
450
|
+
cachedTokens: 0,
|
|
451
|
+
cacheWriteTokens: 0,
|
|
452
|
+
}
|
|
453
|
+
const r = mapRunToStreamEvent(
|
|
454
|
+
{
|
|
455
|
+
type: 'message_completed',
|
|
456
|
+
runId: RID,
|
|
457
|
+
iteration: 0,
|
|
458
|
+
messageId: MID,
|
|
459
|
+
stopReason: 'end_turn',
|
|
460
|
+
usage,
|
|
461
|
+
},
|
|
462
|
+
RID,
|
|
463
|
+
)
|
|
464
|
+
expect(r?.wire).toBe('message.completed')
|
|
465
|
+
expect(r?.data).toMatchObject({
|
|
466
|
+
message_id: MID,
|
|
467
|
+
stop_reason: 'end_turn',
|
|
468
|
+
usage,
|
|
469
|
+
})
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
it('message_completed without usage → usage: null (defensive against dropped message_stop)', () => {
|
|
473
|
+
const r = mapRunToStreamEvent(
|
|
474
|
+
{
|
|
475
|
+
type: 'message_completed',
|
|
476
|
+
runId: RID,
|
|
477
|
+
iteration: 0,
|
|
478
|
+
messageId: MID,
|
|
479
|
+
stopReason: 'tool_use',
|
|
480
|
+
},
|
|
481
|
+
RID,
|
|
482
|
+
)
|
|
483
|
+
expect(r?.data).toMatchObject({ usage: null })
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
it('tool_input_started → tool.input_started carries toolUseId + toolName', () => {
|
|
487
|
+
const r = mapRunToStreamEvent(
|
|
488
|
+
{
|
|
489
|
+
type: 'tool_input_started',
|
|
490
|
+
runId: RID,
|
|
491
|
+
iteration: 0,
|
|
492
|
+
messageId: MID,
|
|
493
|
+
toolUseId: TUID,
|
|
494
|
+
toolName: 'Read',
|
|
495
|
+
},
|
|
496
|
+
RID,
|
|
497
|
+
)
|
|
498
|
+
expect(r?.wire).toBe('tool.input_started')
|
|
499
|
+
expect(r?.data).toMatchObject({
|
|
500
|
+
tool_use_id: TUID,
|
|
501
|
+
tool_name: 'Read',
|
|
502
|
+
message_id: MID,
|
|
503
|
+
})
|
|
504
|
+
})
|
|
505
|
+
|
|
506
|
+
it('tool_input_delta → tool.input_delta carries raw partial JSON fragment', () => {
|
|
507
|
+
const r = mapRunToStreamEvent(
|
|
508
|
+
{
|
|
509
|
+
type: 'tool_input_delta',
|
|
510
|
+
runId: RID,
|
|
511
|
+
toolUseId: TUID,
|
|
512
|
+
partialJson: '{"file_path":"',
|
|
513
|
+
},
|
|
514
|
+
RID,
|
|
515
|
+
)
|
|
516
|
+
expect(r?.wire).toBe('tool.input_delta')
|
|
517
|
+
expect(r?.data).toMatchObject({
|
|
518
|
+
tool_use_id: TUID,
|
|
519
|
+
partial_json: '{"file_path":"',
|
|
520
|
+
})
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
it('tool_input_completed → tool.input_completed carries parsed input object', () => {
|
|
524
|
+
const r = mapRunToStreamEvent(
|
|
525
|
+
{
|
|
526
|
+
type: 'tool_input_completed',
|
|
527
|
+
runId: RID,
|
|
528
|
+
toolUseId: TUID,
|
|
529
|
+
input: { file_path: '/etc/passwd' },
|
|
530
|
+
},
|
|
531
|
+
RID,
|
|
532
|
+
)
|
|
533
|
+
expect(r?.wire).toBe('tool.input_completed')
|
|
534
|
+
expect(r?.data).toMatchObject({
|
|
535
|
+
tool_use_id: TUID,
|
|
536
|
+
input: { file_path: '/etc/passwd' },
|
|
537
|
+
})
|
|
538
|
+
})
|
|
539
|
+
})
|
|
540
|
+
|
|
413
541
|
describe('mapSessionToStreamEvent (deprecated alias)', () => {
|
|
414
542
|
it('is the same function reference as mapRunToStreamEvent', () => {
|
|
415
543
|
// Identity check is deterministic. toEqual on paired calls
|
package/src/bridge/sse/mapper.ts
CHANGED
|
@@ -33,19 +33,11 @@ const MAPPING: {
|
|
|
33
33
|
transform: (e, runId) => ({ run_id: runId, iteration: e.iteration }),
|
|
34
34
|
},
|
|
35
35
|
|
|
36
|
-
llm_response: {
|
|
37
|
-
wire: 'message.delta',
|
|
38
|
-
transform: (e, runId) => ({
|
|
39
|
-
run_id: runId,
|
|
40
|
-
content: e.content ?? null,
|
|
41
|
-
has_tool_calls: !!e.hasToolCalls,
|
|
42
|
-
}),
|
|
43
|
-
},
|
|
44
|
-
|
|
45
36
|
tool_executing: {
|
|
46
37
|
wire: 'tool.executing',
|
|
47
38
|
transform: (e, runId) => ({
|
|
48
39
|
run_id: runId,
|
|
40
|
+
tool_use_id: e.toolUseId,
|
|
49
41
|
tool_name: e.toolName,
|
|
50
42
|
input: e.input,
|
|
51
43
|
}),
|
|
@@ -55,8 +47,10 @@ const MAPPING: {
|
|
|
55
47
|
wire: 'tool.completed',
|
|
56
48
|
transform: (e, runId) => ({
|
|
57
49
|
run_id: runId,
|
|
50
|
+
tool_use_id: e.toolUseId,
|
|
58
51
|
tool_name: e.toolName,
|
|
59
52
|
result: e.result,
|
|
53
|
+
is_error: e.isError,
|
|
60
54
|
}),
|
|
61
55
|
},
|
|
62
56
|
|
|
@@ -279,6 +273,69 @@ const MAPPING: {
|
|
|
279
273
|
subsession_spawned: null,
|
|
280
274
|
subsession_messaged: null,
|
|
281
275
|
subsession_idled: null,
|
|
276
|
+
|
|
277
|
+
// v3 message + tool-input lifecycle (ses_001-tool-stream-events). Additive
|
|
278
|
+
// today; the orchestrator does not yet emit these. Phase 4 of the
|
|
279
|
+
// migration switches the orchestrator over and removes `llm_response`
|
|
280
|
+
// from this map.
|
|
281
|
+
message_started: {
|
|
282
|
+
wire: 'message.created',
|
|
283
|
+
transform: (e, runId) => ({
|
|
284
|
+
run_id: runId,
|
|
285
|
+
iteration: e.iteration,
|
|
286
|
+
message_id: e.messageId,
|
|
287
|
+
}),
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
text_delta: {
|
|
291
|
+
wire: 'message.delta',
|
|
292
|
+
transform: (e, runId) => ({
|
|
293
|
+
run_id: runId,
|
|
294
|
+
iteration: e.iteration,
|
|
295
|
+
message_id: e.messageId,
|
|
296
|
+
text: e.text,
|
|
297
|
+
}),
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
message_completed: {
|
|
301
|
+
wire: 'message.completed',
|
|
302
|
+
transform: (e, runId) => ({
|
|
303
|
+
run_id: runId,
|
|
304
|
+
iteration: e.iteration,
|
|
305
|
+
message_id: e.messageId,
|
|
306
|
+
stop_reason: e.stopReason,
|
|
307
|
+
usage: e.usage ?? null,
|
|
308
|
+
}),
|
|
309
|
+
},
|
|
310
|
+
|
|
311
|
+
tool_input_started: {
|
|
312
|
+
wire: 'tool.input_started',
|
|
313
|
+
transform: (e, runId) => ({
|
|
314
|
+
run_id: runId,
|
|
315
|
+
iteration: e.iteration,
|
|
316
|
+
message_id: e.messageId,
|
|
317
|
+
tool_use_id: e.toolUseId,
|
|
318
|
+
tool_name: e.toolName,
|
|
319
|
+
}),
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
tool_input_delta: {
|
|
323
|
+
wire: 'tool.input_delta',
|
|
324
|
+
transform: (e, runId) => ({
|
|
325
|
+
run_id: runId,
|
|
326
|
+
tool_use_id: e.toolUseId,
|
|
327
|
+
partial_json: e.partialJson,
|
|
328
|
+
}),
|
|
329
|
+
},
|
|
330
|
+
|
|
331
|
+
tool_input_completed: {
|
|
332
|
+
wire: 'tool.input_completed',
|
|
333
|
+
transform: (e, runId) => ({
|
|
334
|
+
run_id: runId,
|
|
335
|
+
tool_use_id: e.toolUseId,
|
|
336
|
+
input: e.input,
|
|
337
|
+
}),
|
|
338
|
+
},
|
|
282
339
|
}
|
|
283
340
|
|
|
284
341
|
export function mapRunToStreamEvent(event: RunEvent, runId: RunId): MappedStreamEvent | null {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CompactionConfig } from '../config/runtime.js'
|
|
2
|
+
import { collect } from '../provider/collect.js'
|
|
2
3
|
import type { Message } from '../types/message/index.js'
|
|
3
4
|
import type { LLMProvider } from '../types/provider/interface.js'
|
|
4
5
|
import type { WorkingStateManager } from './manager.js'
|
|
@@ -74,12 +75,14 @@ export async function buildVerifiedSummary(
|
|
|
74
75
|
},
|
|
75
76
|
]
|
|
76
77
|
|
|
77
|
-
const response = await
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
const response = await collect(
|
|
79
|
+
provider.chatStream({
|
|
80
|
+
model: '',
|
|
81
|
+
messages: verificationMessages,
|
|
82
|
+
maxTokens: config.llmVerificationMaxTokens,
|
|
83
|
+
temperature: 0,
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
83
86
|
|
|
84
87
|
const responseText = response.message.content?.trim() ?? ''
|
|
85
88
|
|
package/src/config/runtime.ts
CHANGED
|
@@ -63,7 +63,7 @@ export type PluginRuntimeConfig = z.infer<typeof PluginRuntimeConfigSchema>
|
|
|
63
63
|
export const RuntimeConfigSchema = z.object({
|
|
64
64
|
model: z.string().default('qwen/qwen3.6-plus:free'),
|
|
65
65
|
temperature: z.number().min(0).max(2).default(0.3),
|
|
66
|
-
tokenBudget: z.number().
|
|
66
|
+
tokenBudget: z.number().nonnegative().default(100_000),
|
|
67
67
|
maxResponseTokens: z.number().positive().default(8192),
|
|
68
68
|
timeoutMs: z.number().positive().default(600_000),
|
|
69
69
|
maxIterations: z.number().positive().default(200),
|
package/src/contracts/api.ts
CHANGED
|
@@ -145,6 +145,13 @@ export type StreamEventType =
|
|
|
145
145
|
| 'tool.executing'
|
|
146
146
|
| 'tool.completed'
|
|
147
147
|
| 'tool.error'
|
|
148
|
+
// v3 tool input lifecycle (ses_001-tool-stream-events). Additive; phase 4
|
|
149
|
+
// of the migration removes `tool.error` and folds the boolean into
|
|
150
|
+
// `tool.completed`. Until then both surfaces are wire-supported so
|
|
151
|
+
// adapters can roll forward independently.
|
|
152
|
+
| 'tool.input_started'
|
|
153
|
+
| 'tool.input_delta'
|
|
154
|
+
| 'tool.input_completed'
|
|
148
155
|
| 'token.usage'
|
|
149
156
|
| 'message.created'
|
|
150
157
|
| 'message.delta'
|
package/src/contracts/schemas.ts
CHANGED
|
@@ -8,7 +8,7 @@ export const RunConfigSchema = z
|
|
|
8
8
|
.object({
|
|
9
9
|
model: z.string().min(1).optional(),
|
|
10
10
|
temperature: z.number().min(0).max(2).optional(),
|
|
11
|
-
tokenBudget: z.number().int().
|
|
11
|
+
tokenBudget: z.number().int().nonnegative().optional(),
|
|
12
12
|
maxResponseTokens: z.number().int().positive().optional(),
|
|
13
13
|
timeoutMs: z.number().int().positive().max(3_600_000).optional(),
|
|
14
14
|
permissionMode: z.enum(['plan', 'auto']).optional(),
|
package/src/gateway/local.ts
CHANGED
|
@@ -14,7 +14,7 @@ export class LocalTaskGateway implements TaskGateway {
|
|
|
14
14
|
private listener: RunEventListener | undefined
|
|
15
15
|
private trackedTaskIds: Set<TaskId> = new Set()
|
|
16
16
|
|
|
17
|
-
private parentInput?: Pick<AgentInput, 'taskStore' | 'runtimeToolOverrides'>
|
|
17
|
+
private parentInput?: Pick<AgentInput, 'taskStore' | 'runtimeToolOverrides' | 'runtimeContext'>
|
|
18
18
|
|
|
19
19
|
private completionListeners: Set<(handle: TaskHandle) => void> = new Set()
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ export class LocalTaskGateway implements TaskGateway {
|
|
|
22
22
|
agentManager: AgentManagerContract,
|
|
23
23
|
taskContext: AgentTaskContext,
|
|
24
24
|
listener?: RunEventListener,
|
|
25
|
-
parentInput?: Pick<AgentInput, 'taskStore' | 'runtimeToolOverrides'>,
|
|
25
|
+
parentInput?: Pick<AgentInput, 'taskStore' | 'runtimeToolOverrides' | 'runtimeContext'>,
|
|
26
26
|
) {
|
|
27
27
|
this.agentManager = agentManager
|
|
28
28
|
this.taskContext = taskContext
|
|
@@ -39,6 +39,7 @@ export class LocalTaskGateway implements TaskGateway {
|
|
|
39
39
|
workingDirectory: options.workingDirectory,
|
|
40
40
|
taskStore: this.parentInput?.taskStore,
|
|
41
41
|
runtimeToolOverrides: this.parentInput?.runtimeToolOverrides,
|
|
42
|
+
runtimeContext: options.runtimeContext ?? this.parentInput?.runtimeContext,
|
|
42
43
|
},
|
|
43
44
|
// Phase 6: spawn scope propagates from the gateway's task context.
|
|
44
45
|
// The caller built it at SupervisorAgent boundary (§12.1).
|
|
@@ -258,14 +258,14 @@ describe('AgentManager.sendMessage — Phase 6 SubSession spawn', () => {
|
|
|
258
258
|
expect(spawned.lineage.parentSessionId).toBe(harness.parentSession.id)
|
|
259
259
|
expect(spawned.lineage.rootSessionId).toBe(harness.parentSession.id)
|
|
260
260
|
expect(spawned.lineage.depth).toBe(1)
|
|
261
|
-
expect(spawned.schemaVersion).toBe(
|
|
261
|
+
expect(spawned.schemaVersion).toBe(3)
|
|
262
262
|
}
|
|
263
263
|
|
|
264
264
|
const idled = events.find((e) => e.type === 'subsession_idled')
|
|
265
265
|
expect(idled).toBeDefined()
|
|
266
266
|
if (idled && 'lineage' in idled) {
|
|
267
267
|
expect(idled.lineage.depth).toBe(1)
|
|
268
|
-
expect(idled.schemaVersion).toBe(
|
|
268
|
+
expect(idled.schemaVersion).toBe(3)
|
|
269
269
|
}
|
|
270
270
|
})
|
|
271
271
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Behavioural contract for `collect()` (ses_001-tool-stream-events phase 1A):
|
|
3
|
+
*
|
|
4
|
+
* - Drains a `StreamChunk` async iterable into a single
|
|
5
|
+
* `ChatCompletionResponse` matching the legacy `provider.chat()` shape.
|
|
6
|
+
* - Concatenates `delta.content` in arrival order; null when no text
|
|
7
|
+
* chunks ever arrive.
|
|
8
|
+
* - Buckets tool-call argument fragments by `index`; emits `toolCalls`
|
|
9
|
+
* sorted by index. `id` and `function.name` come from the first chunk
|
|
10
|
+
* that supplies them; `function.arguments` is the concatenation of all
|
|
11
|
+
* `arguments` fragments for that index.
|
|
12
|
+
* - Latest-wins for `finishReason` and `usage`; defaults
|
|
13
|
+
* `finishReason: 'stop'` and zero usage if the provider omits them
|
|
14
|
+
* (defensive — see anthropics/anthropic-sdk-typescript#842).
|
|
15
|
+
* - Throws if any chunk surfaces a `chunk.error`.
|
|
16
|
+
*
|
|
17
|
+
* Phase 2 swaps every internal `provider.chat()` call site for
|
|
18
|
+
* `collect(provider.chatStream())`; the response shape parity guarded
|
|
19
|
+
* here is what makes that swap safe.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { describe, expect, it } from 'vitest'
|
|
23
|
+
|
|
24
|
+
import type { StreamChunk } from '../types/provider/stream.js'
|
|
25
|
+
|
|
26
|
+
import { collect } from './collect.js'
|
|
27
|
+
|
|
28
|
+
async function* fromArray(chunks: StreamChunk[]): AsyncIterable<StreamChunk> {
|
|
29
|
+
for (const chunk of chunks) yield chunk
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('collect()', () => {
|
|
33
|
+
it('aggregates text-only stream into single content string', async () => {
|
|
34
|
+
const result = await collect(
|
|
35
|
+
fromArray([
|
|
36
|
+
{ id: 'm1', delta: { content: 'hel' } },
|
|
37
|
+
{ id: 'm1', delta: { content: 'lo' } },
|
|
38
|
+
{ id: 'm1', delta: { content: ' world' } },
|
|
39
|
+
{ id: 'm1', delta: {}, finishReason: 'stop' },
|
|
40
|
+
]),
|
|
41
|
+
)
|
|
42
|
+
expect(result.message.content).toBe('hello world')
|
|
43
|
+
expect(result.message.toolCalls).toBeUndefined()
|
|
44
|
+
expect(result.finishReason).toBe('stop')
|
|
45
|
+
expect(result.id).toBe('m1')
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
it('returns content: null when no text chunks arrive', async () => {
|
|
49
|
+
const result = await collect(fromArray([{ id: 'm', delta: {}, finishReason: 'stop' }]))
|
|
50
|
+
expect(result.message.content).toBeNull()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('buckets parallel tool calls by index, preserves order', async () => {
|
|
54
|
+
const result = await collect(
|
|
55
|
+
fromArray([
|
|
56
|
+
{
|
|
57
|
+
id: 'm',
|
|
58
|
+
delta: {
|
|
59
|
+
toolCalls: [
|
|
60
|
+
{ index: 0, id: 'toolu_a', function: { name: 'Read' } },
|
|
61
|
+
{ index: 1, id: 'toolu_b', function: { name: 'WebSearch' } },
|
|
62
|
+
],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'm',
|
|
67
|
+
delta: {
|
|
68
|
+
toolCalls: [{ index: 1, function: { arguments: '{"query":"x"}' } }],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
id: 'm',
|
|
73
|
+
delta: {
|
|
74
|
+
toolCalls: [
|
|
75
|
+
{ index: 0, function: { arguments: '{"file_path":' } },
|
|
76
|
+
{ index: 0, function: { arguments: '"/a"}' } },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
{ id: 'm', delta: {}, finishReason: 'tool_calls' },
|
|
81
|
+
]),
|
|
82
|
+
)
|
|
83
|
+
expect(result.message.toolCalls).toEqual([
|
|
84
|
+
{
|
|
85
|
+
id: 'toolu_a',
|
|
86
|
+
type: 'function',
|
|
87
|
+
function: { name: 'Read', arguments: '{"file_path":"/a"}' },
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'toolu_b',
|
|
91
|
+
type: 'function',
|
|
92
|
+
function: { name: 'WebSearch', arguments: '{"query":"x"}' },
|
|
93
|
+
},
|
|
94
|
+
])
|
|
95
|
+
expect(result.finishReason).toBe('tool_calls')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('latest finishReason and usage win', async () => {
|
|
99
|
+
const result = await collect(
|
|
100
|
+
fromArray([
|
|
101
|
+
{ id: 'm', delta: {}, finishReason: 'stop' },
|
|
102
|
+
{
|
|
103
|
+
id: 'm',
|
|
104
|
+
delta: {},
|
|
105
|
+
finishReason: 'length',
|
|
106
|
+
usage: {
|
|
107
|
+
promptTokens: 100,
|
|
108
|
+
completionTokens: 50,
|
|
109
|
+
totalTokens: 150,
|
|
110
|
+
cachedTokens: 0,
|
|
111
|
+
cacheWriteTokens: 0,
|
|
112
|
+
},
|
|
113
|
+
},
|
|
114
|
+
]),
|
|
115
|
+
)
|
|
116
|
+
expect(result.finishReason).toBe('length')
|
|
117
|
+
expect(result.usage.totalTokens).toBe(150)
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
it('defaults finishReason to stop and usage to zero when provider omits them', async () => {
|
|
121
|
+
const result = await collect(fromArray([{ id: 'm', delta: { content: 'hi' } }]))
|
|
122
|
+
expect(result.finishReason).toBe('stop')
|
|
123
|
+
expect(result.usage).toEqual({
|
|
124
|
+
promptTokens: 0,
|
|
125
|
+
completionTokens: 0,
|
|
126
|
+
totalTokens: 0,
|
|
127
|
+
cachedTokens: 0,
|
|
128
|
+
cacheWriteTokens: 0,
|
|
129
|
+
})
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('throws on chunk.error', async () => {
|
|
133
|
+
await expect(
|
|
134
|
+
collect(
|
|
135
|
+
fromArray([
|
|
136
|
+
{ id: 'm', delta: { content: 'hi' } },
|
|
137
|
+
{ id: 'm', delta: {}, error: 'rate limited' },
|
|
138
|
+
]),
|
|
139
|
+
),
|
|
140
|
+
).rejects.toThrow('rate limited')
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { ChatCompletionResponse } from '../types/provider/chat.js'
|
|
2
|
+
import type { StreamChunk } from '../types/provider/stream.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Drains a {@link StreamChunk} async iterable into the equivalent
|
|
6
|
+
* non-streaming {@link ChatCompletionResponse}.
|
|
7
|
+
*
|
|
8
|
+
* Phase 2 of ses_001-tool-stream-events removes `LLMProvider.chat()`; the
|
|
9
|
+
* four internal callers that genuinely need the aggregated view (advisory
|
|
10
|
+
* executor, RouterAgent's deterministic routing decision, compaction's
|
|
11
|
+
* verifier, the instrumentation wrapper) replace `provider.chat(p)` with
|
|
12
|
+
* `collect(provider.chatStream(p))`.
|
|
13
|
+
*
|
|
14
|
+
* Behaviour matches the pre-removal `chat()` contract:
|
|
15
|
+
* - text content is concatenated in delta order;
|
|
16
|
+
* - tool calls are bucketed by `index` into the existing
|
|
17
|
+
* `Array<{ id, function: { name, arguments } }>` shape;
|
|
18
|
+
* - usage and finishReason fall back to safe defaults when the provider
|
|
19
|
+
* omits them (defensive — see anthropics/anthropic-sdk-typescript#842
|
|
20
|
+
* where `message_stop` is occasionally dropped on connection close).
|
|
21
|
+
*
|
|
22
|
+
* The orchestrator does NOT call this helper — it consumes the stream
|
|
23
|
+
* directly so it can emit per-delta `RunEvent`s.
|
|
24
|
+
*/
|
|
25
|
+
export async function collect(stream: AsyncIterable<StreamChunk>): Promise<ChatCompletionResponse> {
|
|
26
|
+
let id = ''
|
|
27
|
+
const model = ''
|
|
28
|
+
let content = ''
|
|
29
|
+
let finishReason: ChatCompletionResponse['finishReason'] = 'stop'
|
|
30
|
+
let usage: ChatCompletionResponse['usage'] = {
|
|
31
|
+
promptTokens: 0,
|
|
32
|
+
completionTokens: 0,
|
|
33
|
+
totalTokens: 0,
|
|
34
|
+
cachedTokens: 0,
|
|
35
|
+
cacheWriteTokens: 0,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const toolBuckets = new Map<number, { id: string; name: string; argsBuf: string }>()
|
|
39
|
+
|
|
40
|
+
for await (const chunk of stream) {
|
|
41
|
+
if (chunk.error) {
|
|
42
|
+
throw new Error(chunk.error)
|
|
43
|
+
}
|
|
44
|
+
if (!id && chunk.id) id = chunk.id
|
|
45
|
+
|
|
46
|
+
if (chunk.delta.content) {
|
|
47
|
+
content += chunk.delta.content
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const tc of chunk.delta.toolCalls ?? []) {
|
|
51
|
+
const bucket = toolBuckets.get(tc.index) ?? {
|
|
52
|
+
id: '',
|
|
53
|
+
name: '',
|
|
54
|
+
argsBuf: '',
|
|
55
|
+
}
|
|
56
|
+
if (tc.id) bucket.id = tc.id
|
|
57
|
+
if (tc.function?.name) bucket.name = tc.function.name
|
|
58
|
+
if (tc.function?.arguments) bucket.argsBuf += tc.function.arguments
|
|
59
|
+
toolBuckets.set(tc.index, bucket)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (chunk.finishReason) finishReason = chunk.finishReason
|
|
63
|
+
if (chunk.usage) usage = chunk.usage
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const toolCalls = [...toolBuckets.entries()]
|
|
67
|
+
.sort(([a], [b]) => a - b)
|
|
68
|
+
.map(([, b]) => ({
|
|
69
|
+
id: b.id,
|
|
70
|
+
type: 'function' as const,
|
|
71
|
+
function: { name: b.name, arguments: b.argsBuf },
|
|
72
|
+
}))
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
id,
|
|
76
|
+
model,
|
|
77
|
+
message: {
|
|
78
|
+
role: 'assistant',
|
|
79
|
+
content: content.length > 0 ? content : null,
|
|
80
|
+
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
|
|
81
|
+
},
|
|
82
|
+
finishReason,
|
|
83
|
+
usage,
|
|
84
|
+
}
|
|
85
|
+
}
|