@namzu/sdk 3.2.0 → 3.3.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 +44 -0
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +6 -0
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/supervisor-hands-down-hitl.test.d.ts +2 -0
- package/dist/agents/__tests__/supervisor-hands-down-hitl.test.d.ts.map +1 -0
- package/dist/agents/__tests__/supervisor-hands-down-hitl.test.js +112 -0
- package/dist/agents/__tests__/supervisor-hands-down-hitl.test.js.map +1 -0
- package/dist/connector/index.d.ts +1 -0
- package/dist/connector/index.d.ts.map +1 -1
- package/dist/connector/index.js +1 -0
- package/dist/connector/index.js.map +1 -1
- package/dist/connector/mcp/__tests__/prompt-as-tool.test.d.ts +2 -0
- package/dist/connector/mcp/__tests__/prompt-as-tool.test.d.ts.map +1 -0
- package/dist/connector/mcp/__tests__/prompt-as-tool.test.js +122 -0
- package/dist/connector/mcp/__tests__/prompt-as-tool.test.js.map +1 -0
- package/dist/connector/mcp/discovery.d.ts +15 -1
- package/dist/connector/mcp/discovery.d.ts.map +1 -1
- package/dist/connector/mcp/discovery.js +39 -1
- package/dist/connector/mcp/discovery.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +2 -1
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +2 -1
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/policy.d.ts +17 -0
- package/dist/connector/mcp/policy.d.ts.map +1 -1
- package/dist/connector/mcp/policy.js +18 -7
- package/dist/connector/mcp/policy.js.map +1 -1
- package/dist/connector/mcp/prompt-adapter.d.ts +39 -0
- package/dist/connector/mcp/prompt-adapter.d.ts.map +1 -0
- package/dist/connector/mcp/prompt-adapter.js +113 -0
- package/dist/connector/mcp/prompt-adapter.js.map +1 -0
- package/dist/manager/agent/__tests__/hitl-across-spawn.test.d.ts +2 -0
- package/dist/manager/agent/__tests__/hitl-across-spawn.test.d.ts.map +1 -0
- package/dist/manager/agent/__tests__/hitl-across-spawn.test.js +226 -0
- package/dist/manager/agent/__tests__/hitl-across-spawn.test.js.map +1 -0
- package/dist/manager/agent/lifecycle.d.ts.map +1 -1
- package/dist/manager/agent/lifecycle.js +13 -0
- package/dist/manager/agent/lifecycle.js.map +1 -1
- package/dist/plugin/__tests__/mcp-admission.test.js +37 -2
- package/dist/plugin/__tests__/mcp-admission.test.js.map +1 -1
- package/dist/plugin/lifecycle.d.ts.map +1 -1
- package/dist/plugin/lifecycle.js +10 -0
- package/dist/plugin/lifecycle.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 +1 -1
- package/dist/public-runtime.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +1 -46
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/telemetry/__tests__/model-call-span.test.js +13 -8
- package/dist/telemetry/__tests__/model-call-span.test.js.map +1 -1
- package/dist/types/agent/base.d.ts +26 -0
- package/dist/types/agent/base.d.ts.map +1 -1
- package/dist/types/agent/task.d.ts +17 -0
- package/dist/types/agent/task.d.ts.map +1 -1
- package/dist/types/agent/task.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/SupervisorAgent.ts +6 -0
- package/src/agents/__tests__/supervisor-hands-down-hitl.test.ts +131 -0
- package/src/connector/index.ts +1 -0
- package/src/connector/mcp/__tests__/prompt-as-tool.test.ts +157 -0
- package/src/connector/mcp/discovery.ts +48 -1
- package/src/connector/mcp/index.ts +2 -1
- package/src/connector/mcp/policy.ts +23 -8
- package/src/connector/mcp/prompt-adapter.ts +139 -0
- package/src/manager/agent/__tests__/hitl-across-spawn.test.ts +273 -0
- package/src/manager/agent/lifecycle.ts +12 -0
- package/src/plugin/__tests__/mcp-admission.test.ts +46 -2
- package/src/plugin/lifecycle.ts +11 -0
- package/src/public-runtime.ts +2 -0
- package/src/runtime/query/iteration/index.ts +0 -48
- package/src/telemetry/__tests__/model-call-span.test.ts +13 -7
- package/src/types/agent/base.ts +27 -0
- package/src/types/agent/task.ts +18 -0
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
GENAI,
|
|
12
12
|
NAMZU,
|
|
13
13
|
agentIterationSpanName,
|
|
14
|
-
chatSpanName,
|
|
15
14
|
parentContext,
|
|
16
15
|
} from '../../../telemetry/attributes.js'
|
|
17
16
|
import { getTracer } from '../../../telemetry/runtime-accessors.js'
|
|
@@ -154,9 +153,6 @@ export class IterationOrchestrator {
|
|
|
154
153
|
{},
|
|
155
154
|
parentContext(this.ctx.rootSpan),
|
|
156
155
|
)
|
|
157
|
-
// Declared out here so the iteration's own finally can close it on
|
|
158
|
-
// any path that does not reach its success branch.
|
|
159
|
-
let chatSpan: Span | undefined
|
|
160
156
|
try {
|
|
161
157
|
// Tool spans for this turn belong under this iteration. Inside
|
|
162
158
|
// the try rather than before it: a throw from any of these left
|
|
@@ -282,27 +278,6 @@ export class IterationOrchestrator {
|
|
|
282
278
|
// aggregated `ChatCompletionResponse` for the legacy
|
|
283
279
|
// downstream paths (assistantMsg construction, working
|
|
284
280
|
// state extraction, telemetry attribute stamping).
|
|
285
|
-
// The model call gets its own span. There was none at all —
|
|
286
|
-
// `chatSpanName` shipped with zero call sites — so a run's traces
|
|
287
|
-
// carried no LLM latency whatsoever, and the one thing anybody
|
|
288
|
-
// opens a trace to find (which turn was slow, and why) was the
|
|
289
|
-
// one thing not in it.
|
|
290
|
-
chatSpan = tracer.startSpan(chatSpanName(stepModel), {}, parentContext(iterSpan))
|
|
291
|
-
chatSpan.setAttributes({
|
|
292
|
-
[GENAI.OPERATION_NAME]: 'chat',
|
|
293
|
-
[GENAI.SYSTEM]: this.ctx.provider.id,
|
|
294
|
-
[GENAI.REQUEST_MODEL]: stepModel,
|
|
295
|
-
...((step.temperature ?? runConfig.temperature) !== undefined
|
|
296
|
-
? { [GENAI.REQUEST_TEMPERATURE]: (step.temperature ?? runConfig.temperature) as number }
|
|
297
|
-
: {}),
|
|
298
|
-
...((step.maxResponseTokens ?? runConfig.maxResponseTokens) !== undefined
|
|
299
|
-
? {
|
|
300
|
-
[GENAI.REQUEST_MAX_TOKENS]: (step.maxResponseTokens ??
|
|
301
|
-
runConfig.maxResponseTokens) as number,
|
|
302
|
-
}
|
|
303
|
-
: {}),
|
|
304
|
-
})
|
|
305
|
-
|
|
306
281
|
const { response, messageId } = yield* streamProviderTurn(
|
|
307
282
|
this.ctx.provider,
|
|
308
283
|
{
|
|
@@ -339,27 +314,6 @@ export class IterationOrchestrator {
|
|
|
339
314
|
iterSpan,
|
|
340
315
|
)
|
|
341
316
|
|
|
342
|
-
// Stamped on the call that produced them. The token counts also
|
|
343
|
-
// stay on the iteration span below, where they have always been:
|
|
344
|
-
// moving them would silently break whatever reads them today,
|
|
345
|
-
// and one turn per iteration makes the two agree.
|
|
346
|
-
chatSpan.setAttributes({
|
|
347
|
-
[GENAI.RESPONSE_MODEL]: response.model || stepModel,
|
|
348
|
-
[GENAI.RESPONSE_ID]: response.id,
|
|
349
|
-
[GENAI.USAGE_INPUT_TOKENS]: response.usage.promptTokens,
|
|
350
|
-
[GENAI.USAGE_OUTPUT_TOKENS]: response.usage.completionTokens,
|
|
351
|
-
// An array, per the semantic convention: one call can finish
|
|
352
|
-
// several ways when a provider returns more than one choice.
|
|
353
|
-
[GENAI.RESPONSE_FINISH_REASONS]: [response.finishReason ?? 'stop'],
|
|
354
|
-
[NAMZU.CACHE_READ_TOKENS]: response.usage.cachedTokens ?? 0,
|
|
355
|
-
[NAMZU.CACHE_WRITE_TOKENS]: response.usage.cacheWriteTokens ?? 0,
|
|
356
|
-
})
|
|
357
|
-
chatSpan.setStatus({ code: SpanStatusCode.OK })
|
|
358
|
-
chatSpan.end()
|
|
359
|
-
// Closed here for an accurate duration, and cleared so the
|
|
360
|
-
// iteration finally does not close it a second time.
|
|
361
|
-
chatSpan = undefined
|
|
362
|
-
|
|
363
317
|
// Main-loop turn: also records the prompt size compaction reads.
|
|
364
318
|
runMgr.recordTurnUsage(response.usage)
|
|
365
319
|
|
|
@@ -794,8 +748,6 @@ export class IterationOrchestrator {
|
|
|
794
748
|
iterSpan.recordException(err instanceof Error ? err : new Error(String(err)))
|
|
795
749
|
throw err
|
|
796
750
|
} finally {
|
|
797
|
-
// A model call that threw never reached its own close above.
|
|
798
|
-
chatSpan?.end()
|
|
799
751
|
// The only place the iteration span ends. It used to be ended at each of
|
|
800
752
|
// seventeen exits, which is a rule every future edit has to
|
|
801
753
|
// remember; a generator abandoned by its consumer never reached
|
|
@@ -109,10 +109,14 @@ describe('the model call has a span of its own', () => {
|
|
|
109
109
|
it('opens one named for the model', async () => {
|
|
110
110
|
await runOnce([{ text: 'done' }])
|
|
111
111
|
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
|
|
115
|
-
|
|
112
|
+
// EXACTLY one. This was written as `toHaveLength(1)`, failed with 2,
|
|
113
|
+
// and was relaxed to `>= 1` under a plausible-sounding explanation
|
|
114
|
+
// about forced-final turns. The 2 was real: a second span with the
|
|
115
|
+
// same name and the same parent had been added beside the one
|
|
116
|
+
// `stream-turn.ts` already opened, so a naive sum double-counted both
|
|
117
|
+
// latency and tokens. Relaxing the assertion is what let that ship.
|
|
118
|
+
expect(chatSpans()).toHaveLength(1)
|
|
119
|
+
expect(chatSpans()[0]?.name).toBe('chat mock-model')
|
|
116
120
|
})
|
|
117
121
|
|
|
118
122
|
it('closes it', async () => {
|
|
@@ -168,11 +172,13 @@ describe('the model call has a span of its own', () => {
|
|
|
168
172
|
expect(attrs).toHaveProperty('namzu.cache.write_tokens')
|
|
169
173
|
})
|
|
170
174
|
|
|
171
|
-
it('opens one per
|
|
175
|
+
it('opens exactly one per model call, however many turns a run takes', async () => {
|
|
172
176
|
await runOnce([{ text: 'first' }, { text: 'second' }])
|
|
173
177
|
|
|
174
|
-
//
|
|
175
|
-
|
|
178
|
+
// The count has to track model calls, not runs and not iterations —
|
|
179
|
+
// which is the only way a duplicate is visible at all.
|
|
180
|
+
const calls = spans.filter((s) => s.name.includes('iteration')).length
|
|
181
|
+
expect(chatSpans().length).toBeLessThanOrEqual(calls)
|
|
176
182
|
})
|
|
177
183
|
|
|
178
184
|
it('nests under the iteration span rather than emitting as a root', async () => {
|
package/src/types/agent/base.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AgentStatus, CostInfo, TokenUsage } from '../common/index.js'
|
|
2
|
+
import type { ResumeHandler } from '../hitl/index.js'
|
|
2
3
|
import type { RunId, SessionId, TenantId } from '../ids/index.js'
|
|
3
4
|
import type { InvocationState } from '../invocation/index.js'
|
|
4
5
|
import type { Message } from '../message/index.js'
|
|
@@ -78,6 +79,32 @@ export interface BaseAgentConfig {
|
|
|
78
79
|
|
|
79
80
|
/** Span a delegated run hangs off. Absent for a top-level run. */
|
|
80
81
|
parentSpan?: import('@opentelemetry/api').Span
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Where this agent takes a decision it cannot make alone — a tool that
|
|
85
|
+
* needs approval, a question for a human, a plan to sign off.
|
|
86
|
+
*
|
|
87
|
+
* Declared HERE, on the base config, rather than only on the agent
|
|
88
|
+
* shapes that happened to want it. `AgentManager` builds a child as a
|
|
89
|
+
* `BaseAgentConfig` and `SendMessageOptions.configOverrides` is a
|
|
90
|
+
* `Partial` of it, so a field further down the hierarchy is one a
|
|
91
|
+
* spawn cannot express AT THE TYPE LEVEL — and that is what happened:
|
|
92
|
+
* every delegated child fell through to the SDK's `autoApproveHandler`
|
|
93
|
+
* however carefully its parent had been wired.
|
|
94
|
+
*
|
|
95
|
+
* What that cost is narrower than "no gate in children" and worth
|
|
96
|
+
* stating exactly. A `VerificationGate` DENY still bites inside a
|
|
97
|
+
* child, because denials are threaded into the executor and no later
|
|
98
|
+
* approval releases them. What was lost is the REVIEW tier: every call
|
|
99
|
+
* the gate left undecided went to the resume handler, and for a child
|
|
100
|
+
* that handler auto-approved. So a host running "ask before acting"
|
|
101
|
+
* had a human review `write` at the top level and never see the same
|
|
102
|
+
* `write` issued one hop down.
|
|
103
|
+
*
|
|
104
|
+
* Absent still means auto-approve, so a host that never wired one is
|
|
105
|
+
* unaffected.
|
|
106
|
+
*/
|
|
107
|
+
resumeHandler?: ResumeHandler
|
|
81
108
|
}
|
|
82
109
|
|
|
83
110
|
export type RuntimeToolOverrides = Record<string, ToolAvailability | 'disabled'>
|
package/src/types/agent/task.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ActorRef } from '../../types/session/actor.js'
|
|
2
2
|
import type { WorkspaceBackendKind } from '../../types/workspace/ref.js'
|
|
3
3
|
import type { TokenUsage } from '../common/index.js'
|
|
4
|
+
import type { ResumeHandler } from '../hitl/index.js'
|
|
4
5
|
import type { RunId, SessionId, TaskId, TenantId } from '../ids/index.js'
|
|
5
6
|
import type { Message } from '../message/index.js'
|
|
6
7
|
import type { RunEventListener } from '../run/events.js'
|
|
@@ -41,6 +42,23 @@ export interface AgentTaskContext {
|
|
|
41
42
|
|
|
42
43
|
factoryOptions?: AgentFactoryOptions
|
|
43
44
|
|
|
45
|
+
/**
|
|
46
|
+
* The parent's channel to whoever can answer a decision it cannot make
|
|
47
|
+
* alone, handed down so a child asks the same person.
|
|
48
|
+
*
|
|
49
|
+
* Passed as the function itself, which works because delegation is
|
|
50
|
+
* in-process: `LocalTaskGateway` is the only `TaskGateway` in the tree.
|
|
51
|
+
* A gateway that dispatched across a process boundary could not carry a
|
|
52
|
+
* closure and would have to proxy the request onto the parent's event
|
|
53
|
+
* stream and route the answer back by request id — the upward half of
|
|
54
|
+
* which already exists, since `wrapChildListener` stamps lineage on
|
|
55
|
+
* every child event the parent sees.
|
|
56
|
+
*
|
|
57
|
+
* Absent means the child auto-approves, exactly as every child did
|
|
58
|
+
* before this existed.
|
|
59
|
+
*/
|
|
60
|
+
resumeHandler?: ResumeHandler
|
|
61
|
+
|
|
44
62
|
/** Isolation boundary. Required per session-hierarchy.md §12.1. */
|
|
45
63
|
tenantId: TenantId
|
|
46
64
|
|