@namzu/sdk 1.0.0 → 1.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 +253 -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 +22 -1
- package/dist/agents/SupervisorAgent.d.ts.map +1 -1
- package/dist/agents/SupervisorAgent.js +47 -14
- package/dist/agents/SupervisorAgent.js.map +1 -1
- package/dist/agents/__tests__/supervisor-ledger.test.d.ts +2 -0
- package/dist/agents/__tests__/supervisor-ledger.test.d.ts.map +1 -0
- package/dist/agents/__tests__/supervisor-ledger.test.js +131 -0
- package/dist/agents/__tests__/supervisor-ledger.test.js.map +1 -0
- package/dist/bridge/a2a/mapper.d.ts.map +1 -1
- package/dist/bridge/a2a/mapper.js +2 -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 +4 -0
- package/dist/bridge/sse/mapper.js.map +1 -1
- package/dist/config/runtime.d.ts +24 -0
- package/dist/config/runtime.d.ts.map +1 -1
- package/dist/config/runtime.js +9 -0
- package/dist/config/runtime.js.map +1 -1
- package/dist/connector/builtins/webhook.test.js +17 -0
- package/dist/connector/builtins/webhook.test.js.map +1 -1
- 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/client.d.ts.map +1 -1
- package/dist/connector/mcp/client.js +4 -0
- package/dist/connector/mcp/client.js.map +1 -1
- package/dist/connector/mcp/index.d.ts +1 -0
- package/dist/connector/mcp/index.d.ts.map +1 -1
- package/dist/connector/mcp/index.js +1 -0
- package/dist/connector/mcp/index.js.map +1 -1
- package/dist/connector/mcp/streamable-http.d.ts +22 -0
- package/dist/connector/mcp/streamable-http.d.ts.map +1 -0
- package/dist/connector/mcp/streamable-http.js +128 -0
- package/dist/connector/mcp/streamable-http.js.map +1 -0
- package/dist/connector/mcp/streamable-http.test.d.ts +2 -0
- package/dist/connector/mcp/streamable-http.test.d.ts.map +1 -0
- package/dist/connector/mcp/streamable-http.test.js +132 -0
- package/dist/connector/mcp/streamable-http.test.js.map +1 -0
- package/dist/{runtime/query → constants}/continuation.d.ts +5 -0
- package/dist/constants/continuation.d.ts.map +1 -0
- package/dist/{runtime/query → constants}/continuation.js +5 -0
- package/dist/constants/continuation.js.map +1 -0
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +1 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/manager/run/persistence.d.ts +11 -0
- package/dist/manager/run/persistence.d.ts.map +1 -1
- package/dist/manager/run/persistence.js +30 -1
- package/dist/manager/run/persistence.js.map +1 -1
- package/dist/persona/assembler.d.ts.map +1 -1
- package/dist/persona/assembler.js +18 -9
- package/dist/persona/assembler.js.map +1 -1
- package/dist/provider/capabilities.d.ts +27 -0
- package/dist/provider/capabilities.d.ts.map +1 -0
- package/dist/provider/capabilities.js +29 -0
- package/dist/provider/capabilities.js.map +1 -0
- package/dist/provider/collect.d.ts.map +1 -1
- package/dist/provider/collect.js +4 -1
- package/dist/provider/collect.js.map +1 -1
- package/dist/provider/index.d.ts +2 -0
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +1 -0
- package/dist/provider/index.js.map +1 -1
- package/dist/public-runtime.d.ts +4 -3
- package/dist/public-runtime.d.ts.map +1 -1
- package/dist/public-runtime.js +3 -3
- package/dist/public-runtime.js.map +1 -1
- package/dist/registry/tool/execute.d.ts +27 -0
- package/dist/registry/tool/execute.d.ts.map +1 -1
- package/dist/registry/tool/execute.js +124 -21
- package/dist/registry/tool/execute.js.map +1 -1
- package/dist/registry/tool/execute.test.d.ts +9 -6
- package/dist/registry/tool/execute.test.d.ts.map +1 -1
- package/dist/registry/tool/execute.test.js +72 -10
- package/dist/registry/tool/execute.test.js.map +1 -1
- package/dist/registry/toolset/catalog.test.js +62 -0
- package/dist/registry/toolset/catalog.test.js.map +1 -1
- package/dist/run/reporter.d.ts.map +1 -1
- package/dist/run/reporter.js +8 -0
- package/dist/run/reporter.js.map +1 -1
- package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.js +184 -0
- package/dist/runtime/query/__tests__/capability-negotiation.test.js.map +1 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.js +259 -0
- package/dist/runtime/query/__tests__/checkpoint-store.test.js.map +1 -0
- package/dist/runtime/query/__tests__/checkpoint.test.js +9 -3
- package/dist/runtime/query/__tests__/checkpoint.test.js.map +1 -1
- package/dist/runtime/query/__tests__/deferred-tools.test.js +43 -5
- package/dist/runtime/query/__tests__/deferred-tools.test.js.map +1 -1
- package/dist/runtime/query/__tests__/long-document-flow.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.js +154 -0
- package/dist/runtime/query/__tests__/long-document-flow.test.js.map +1 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts +2 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.d.ts.map +1 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js +110 -0
- package/dist/runtime/query/__tests__/orphan-task-turn-end.test.js.map +1 -0
- package/dist/runtime/query/__tests__/prompt.test.js +15 -11
- package/dist/runtime/query/__tests__/prompt.test.js.map +1 -1
- package/dist/runtime/query/checkpoint.d.ts +16 -2
- package/dist/runtime/query/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/checkpoint.js +21 -7
- package/dist/runtime/query/checkpoint.js.map +1 -1
- package/dist/runtime/query/context.d.ts +7 -0
- package/dist/runtime/query/context.d.ts.map +1 -1
- package/dist/runtime/query/context.js +1 -0
- package/dist/runtime/query/context.js.map +1 -1
- package/dist/runtime/query/index.d.ts +29 -0
- package/dist/runtime/query/index.d.ts.map +1 -1
- package/dist/runtime/query/index.js +98 -8
- package/dist/runtime/query/index.js.map +1 -1
- package/dist/runtime/query/iteration/index.d.ts +3 -55
- package/dist/runtime/query/iteration/index.d.ts.map +1 -1
- package/dist/runtime/query/iteration/index.js +260 -656
- package/dist/runtime/query/iteration/index.js.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/checkpoint.js +24 -2
- package/dist/runtime/query/iteration/phases/checkpoint.js.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.d.ts +8 -0
- package/dist/runtime/query/iteration/phases/compaction.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/compaction.js +84 -3
- package/dist/runtime/query/iteration/phases/compaction.js.map +1 -1
- package/dist/runtime/query/iteration/phases/context.d.ts +13 -2
- package/dist/runtime/query/iteration/phases/context.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/context.js +53 -0
- package/dist/runtime/query/iteration/phases/context.js.map +1 -1
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js +73 -0
- package/dist/runtime/query/iteration/phases/hitl-abort-race.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts +2 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js +120 -0
- package/dist/runtime/query/iteration/phases/hitl-answer-question.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/tool-review.d.ts +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.d.ts.map +1 -1
- package/dist/runtime/query/iteration/phases/tool-review.js +7 -2
- package/dist/runtime/query/iteration/phases/tool-review.js.map +1 -1
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts +19 -0
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js +177 -0
- package/dist/runtime/query/iteration/phases/working-memory-compaction.test.js.map +1 -0
- package/dist/runtime/query/iteration/phases/working-memory.d.ts +31 -0
- package/dist/runtime/query/iteration/phases/working-memory.d.ts.map +1 -0
- package/dist/runtime/query/iteration/phases/working-memory.js +81 -0
- package/dist/runtime/query/iteration/phases/working-memory.js.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts +35 -0
- package/dist/runtime/query/iteration/stream-turn.d.ts.map +1 -0
- package/dist/runtime/query/iteration/stream-turn.js +341 -0
- package/dist/runtime/query/iteration/stream-turn.js.map +1 -0
- package/dist/runtime/query/replay/list.d.ts +17 -1
- package/dist/runtime/query/replay/list.d.ts.map +1 -1
- package/dist/runtime/query/replay/list.js +21 -4
- package/dist/runtime/query/replay/list.js.map +1 -1
- package/dist/runtime/query/replay/prepare.d.ts +10 -0
- package/dist/runtime/query/replay/prepare.d.ts.map +1 -1
- package/dist/runtime/query/replay/prepare.js +20 -4
- package/dist/runtime/query/replay/prepare.js.map +1 -1
- package/dist/session/workspace/__tests__/shared-run.test.js +1 -1
- package/dist/session/workspace/__tests__/shared-run.test.js.map +1 -1
- package/dist/session/workspace/shared-run.js +1 -0
- package/dist/session/workspace/shared-run.js.map +1 -1
- package/dist/skills/loader.test.js +8 -8
- package/dist/skills/loader.test.js.map +1 -1
- package/dist/store/index.d.ts +1 -0
- package/dist/store/index.d.ts.map +1 -1
- package/dist/store/index.js +1 -0
- package/dist/store/index.js.map +1 -1
- package/dist/store/run/checkpoint-disk.d.ts +28 -0
- package/dist/store/run/checkpoint-disk.d.ts.map +1 -0
- package/dist/store/run/checkpoint-disk.js +55 -0
- package/dist/store/run/checkpoint-disk.js.map +1 -0
- package/dist/streaming/coalesce.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/payload-budget.test.js +48 -0
- package/dist/tools/builtins/__tests__/payload-budget.test.js.map +1 -1
- package/dist/tools/builtins/__tests__/read-file.test.js +9 -0
- package/dist/tools/builtins/__tests__/read-file.test.js.map +1 -1
- package/dist/tools/builtins/read-file.d.ts.map +1 -1
- package/dist/tools/builtins/read-file.js +50 -2
- package/dist/tools/builtins/read-file.js.map +1 -1
- package/dist/tools/builtins/search-tools.d.ts.map +1 -1
- package/dist/tools/builtins/search-tools.js +32 -7
- package/dist/tools/builtins/search-tools.js.map +1 -1
- package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts +2 -0
- package/dist/tools/coordinator/__tests__/approve-plan.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/approve-plan.test.js +114 -0
- package/dist/tools/coordinator/__tests__/approve-plan.test.js.map +1 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts +2 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.d.ts.map +1 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.js +309 -0
- package/dist/tools/coordinator/__tests__/ask-user-question.test.js.map +1 -0
- package/dist/tools/coordinator/index.d.ts +8 -0
- package/dist/tools/coordinator/index.d.ts.map +1 -1
- package/dist/tools/coordinator/index.js +195 -15
- package/dist/tools/coordinator/index.js.map +1 -1
- package/dist/types/agent/index.d.ts +1 -0
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/index.js +1 -0
- package/dist/types/agent/index.js.map +1 -1
- package/dist/types/agent/reactive.d.ts +15 -0
- package/dist/types/agent/reactive.d.ts.map +1 -1
- package/dist/types/agent/supervisor.d.ts +18 -0
- package/dist/types/agent/supervisor.d.ts.map +1 -1
- package/dist/types/agent/working-memory.d.ts +29 -0
- package/dist/types/agent/working-memory.d.ts.map +1 -0
- package/dist/types/agent/working-memory.js +2 -0
- package/dist/types/agent/working-memory.js.map +1 -0
- package/dist/types/common/index.d.ts +10 -0
- package/dist/types/common/index.d.ts.map +1 -1
- package/dist/types/common/index.js +18 -0
- package/dist/types/common/index.js.map +1 -1
- package/dist/types/common/usage-merge.test.d.ts +2 -0
- package/dist/types/common/usage-merge.test.d.ts.map +1 -0
- package/dist/types/common/usage-merge.test.js +48 -0
- package/dist/types/common/usage-merge.test.js.map +1 -0
- package/dist/types/connector/mcp.d.ts +9 -2
- package/dist/types/connector/mcp.d.ts.map +1 -1
- package/dist/types/hitl/index.d.ts +42 -0
- package/dist/types/hitl/index.d.ts.map +1 -1
- package/dist/types/hitl/index.js +11 -0
- package/dist/types/hitl/index.js.map +1 -1
- package/dist/types/provider/chat.d.ts +9 -0
- package/dist/types/provider/chat.d.ts.map +1 -1
- package/dist/types/provider/config.d.ts +18 -0
- package/dist/types/provider/config.d.ts.map +1 -1
- package/dist/types/provider/interface.d.ts +10 -0
- package/dist/types/provider/interface.d.ts.map +1 -1
- package/dist/types/run/checkpoint-store.d.ts +64 -0
- package/dist/types/run/checkpoint-store.d.ts.map +1 -0
- package/dist/types/run/checkpoint-store.js +12 -0
- package/dist/types/run/checkpoint-store.js.map +1 -0
- package/dist/types/run/config.d.ts +27 -0
- package/dist/types/run/config.d.ts.map +1 -1
- package/dist/types/run/events.d.ts +6 -0
- package/dist/types/run/events.d.ts.map +1 -1
- package/dist/types/run/events.js.map +1 -1
- package/dist/types/run/index.d.ts +1 -0
- package/dist/types/run/index.d.ts.map +1 -1
- package/dist/types/run/index.js +1 -0
- package/dist/types/run/index.js.map +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +25 -4
- package/dist/version.js.map +1 -1
- package/package.json +1 -1
- package/src/agents/ReactiveAgent.ts +5 -0
- package/src/agents/SupervisorAgent.ts +56 -16
- package/src/agents/__tests__/supervisor-ledger.test.ts +185 -0
- package/src/bridge/a2a/mapper.ts +3 -0
- package/src/bridge/sse/mapper.ts +5 -0
- package/src/config/runtime.ts +9 -0
- package/src/connector/builtins/webhook.test.ts +21 -0
- package/src/connector/index.ts +1 -0
- package/src/connector/mcp/client.ts +4 -0
- package/src/connector/mcp/index.ts +1 -0
- package/src/connector/mcp/streamable-http.test.ts +169 -0
- package/src/connector/mcp/streamable-http.ts +152 -0
- package/src/{runtime/query → constants}/continuation.ts +5 -0
- package/src/constants/index.ts +1 -0
- package/src/manager/run/persistence.ts +34 -1
- package/src/persona/assembler.ts +18 -6
- package/src/provider/capabilities.ts +47 -0
- package/src/provider/collect.ts +4 -1
- package/src/provider/index.ts +5 -0
- package/src/public-runtime.ts +5 -0
- package/src/registry/tool/execute.test.ts +85 -10
- package/src/registry/tool/execute.ts +124 -21
- package/src/registry/toolset/catalog.test.ts +68 -0
- package/src/run/reporter.ts +9 -0
- package/src/runtime/query/__tests__/capability-negotiation.test.ts +248 -0
- package/src/runtime/query/__tests__/checkpoint-store.test.ts +322 -0
- package/src/runtime/query/__tests__/checkpoint.test.ts +15 -6
- package/src/runtime/query/__tests__/deferred-tools.test.ts +52 -5
- package/src/runtime/query/__tests__/long-document-flow.test.ts +180 -0
- package/src/runtime/query/__tests__/orphan-task-turn-end.test.ts +127 -0
- package/src/runtime/query/__tests__/prompt.test.ts +15 -11
- package/src/runtime/query/checkpoint.ts +23 -9
- package/src/runtime/query/context.ts +9 -0
- package/src/runtime/query/index.ts +148 -29
- package/src/runtime/query/iteration/index.ts +316 -831
- package/src/runtime/query/iteration/phases/checkpoint.ts +32 -2
- package/src/runtime/query/iteration/phases/compaction.ts +89 -3
- package/src/runtime/query/iteration/phases/context.ts +59 -3
- package/src/runtime/query/iteration/phases/hitl-abort-race.test.ts +86 -0
- package/src/runtime/query/iteration/phases/hitl-answer-question.test.ts +139 -0
- package/src/runtime/query/iteration/phases/tool-review.ts +7 -3
- package/src/runtime/query/iteration/phases/working-memory-compaction.test.ts +215 -0
- package/src/runtime/query/iteration/phases/working-memory.ts +86 -0
- package/src/runtime/query/iteration/stream-turn.ts +396 -0
- package/src/runtime/query/replay/list.ts +38 -4
- package/src/runtime/query/replay/prepare.ts +37 -4
- package/src/session/workspace/__tests__/shared-run.test.ts +1 -1
- package/src/session/workspace/shared-run.ts +1 -0
- package/src/skills/loader.test.ts +8 -8
- package/src/store/index.ts +1 -0
- package/src/store/run/checkpoint-disk.ts +67 -0
- package/src/streaming/coalesce.test.ts +3 -3
- package/src/tools/builtins/__tests__/payload-budget.test.ts +63 -0
- package/src/tools/builtins/__tests__/read-file.test.ts +12 -0
- package/src/tools/builtins/read-file.ts +50 -2
- package/src/tools/builtins/search-tools.ts +38 -7
- package/src/tools/coordinator/__tests__/approve-plan.test.ts +139 -0
- package/src/tools/coordinator/__tests__/ask-user-question.test.ts +370 -0
- package/src/tools/coordinator/index.ts +227 -17
- package/src/types/agent/index.ts +1 -0
- package/src/types/agent/reactive.ts +17 -0
- package/src/types/agent/supervisor.ts +20 -0
- package/src/types/agent/working-memory.ts +29 -0
- package/src/types/common/index.ts +19 -0
- package/src/types/common/usage-merge.test.ts +53 -0
- package/src/types/connector/mcp.ts +14 -2
- package/src/types/hitl/index.ts +52 -1
- package/src/types/provider/chat.ts +10 -0
- package/src/types/provider/config.ts +18 -0
- package/src/types/provider/interface.ts +11 -0
- package/src/types/run/checkpoint-store.ts +72 -0
- package/src/types/run/config.ts +30 -0
- package/src/types/run/events.ts +13 -0
- package/src/types/run/index.ts +1 -0
- package/src/version.ts +26 -4
- package/dist/runtime/query/continuation.d.ts.map +0 -1
- package/dist/runtime/query/continuation.js.map +0 -1
|
@@ -4,21 +4,63 @@ import { ToolRegistry } from '../registry/tool/execute.js'
|
|
|
4
4
|
import { drainQuery } from '../runtime/query/index.js'
|
|
5
5
|
import type { LaunchedTaskMeta } from '../runtime/query/iteration/phases/context.js'
|
|
6
6
|
import { buildCoordinatorTools } from '../tools/coordinator/index.js'
|
|
7
|
-
import type { TaskGateway } from '../types/agent/gateway.js'
|
|
7
|
+
import type { TaskGateway, TaskHandle } from '../types/agent/gateway.js'
|
|
8
8
|
import type {
|
|
9
9
|
AgentInput,
|
|
10
10
|
AgentMetadata,
|
|
11
|
+
AgentTaskResult,
|
|
11
12
|
SupervisorAgentConfig,
|
|
12
13
|
SupervisorAgentResult,
|
|
13
14
|
} from '../types/agent/index.js'
|
|
14
15
|
import type { AgentTaskContext } from '../types/agent/task.js'
|
|
15
|
-
import type { AgentId, TaskId } from '../types/ids/index.js'
|
|
16
|
+
import type { AgentId, RunId, TaskId } from '../types/ids/index.js'
|
|
16
17
|
import { deriveChildState } from '../types/invocation/index.js'
|
|
17
18
|
import type { RunEventListener } from '../types/run/index.js'
|
|
18
19
|
import type { ActorRef } from '../types/session/actor.js'
|
|
19
20
|
import { ZERO_COST } from '../utils/cost.js'
|
|
20
21
|
import { AbstractAgent } from './AbstractAgent.js'
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Build the authoritative per-task ledger from the gateway's task handles.
|
|
25
|
+
*
|
|
26
|
+
* A handle carries a `result` only when its worker actually produced one. A
|
|
27
|
+
* handle with NO result never produced a verifiable outcome, so it MUST NOT be
|
|
28
|
+
* synthesized as a success: the synthesized status is always the terminal
|
|
29
|
+
* `'failed'`, regardless of the handle's reported `state` (which may itself be
|
|
30
|
+
* `'completed'`).
|
|
31
|
+
*
|
|
32
|
+
* The earlier implementation cast `handle.state` onto the synthesized result's
|
|
33
|
+
* status, letting a worker that ended without a result count toward
|
|
34
|
+
* `completedTasks`. That produced fabricated "done" workers with empty outputs
|
|
35
|
+
* (cowork task 02c5cf2b): the supervisor reported "3 workers done, 40KB
|
|
36
|
+
* reports" when the workers never started. Real workers (those with a present
|
|
37
|
+
* `result`) are unaffected — their `result` is preserved verbatim.
|
|
38
|
+
*/
|
|
39
|
+
export function synthesizeTaskResults(
|
|
40
|
+
taskHandles: readonly TaskHandle[],
|
|
41
|
+
runId: RunId,
|
|
42
|
+
now: number = Date.now(),
|
|
43
|
+
): AgentTaskResult[] {
|
|
44
|
+
return taskHandles.map((handle, index) => ({
|
|
45
|
+
agentId: handle.agentId,
|
|
46
|
+
result: handle.result ?? {
|
|
47
|
+
runId,
|
|
48
|
+
status: 'failed' as const,
|
|
49
|
+
usage: { ...EMPTY_TOKEN_USAGE },
|
|
50
|
+
cost: { ...ZERO_COST },
|
|
51
|
+
iterations: 0,
|
|
52
|
+
durationMs: now - handle.createdAt,
|
|
53
|
+
messages: [],
|
|
54
|
+
},
|
|
55
|
+
taskIndex: index,
|
|
56
|
+
}))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Count only the task results that genuinely completed. */
|
|
60
|
+
export function countCompletedTasks(taskResults: readonly AgentTaskResult[]): number {
|
|
61
|
+
return taskResults.filter((t) => t.result.status === 'completed').length
|
|
62
|
+
}
|
|
63
|
+
|
|
22
64
|
export class SupervisorAgent extends AbstractAgent<SupervisorAgentConfig, SupervisorAgentResult> {
|
|
23
65
|
readonly type = 'supervisor' as const
|
|
24
66
|
|
|
@@ -110,6 +152,10 @@ export class SupervisorAgent extends AbstractAgent<SupervisorAgentConfig, Superv
|
|
|
110
152
|
onTaskLaunched: (agentTaskId, meta) => {
|
|
111
153
|
launchedTasks.set(agentTaskId, meta)
|
|
112
154
|
},
|
|
155
|
+
// With a resume handler present the coordinator surface gains
|
|
156
|
+
// ask_user_question — the model can park the run on a question
|
|
157
|
+
// routed through the same HITL channel as plan approvals.
|
|
158
|
+
...(config.resumeHandler ? { resumeHandler: config.resumeHandler } : {}),
|
|
113
159
|
})
|
|
114
160
|
|
|
115
161
|
const tools = new ToolRegistry()
|
|
@@ -171,26 +217,20 @@ export class SupervisorAgent extends AbstractAgent<SupervisorAgentConfig, Superv
|
|
|
171
217
|
...(config.resumeHandler ? { resumeHandler: config.resumeHandler } : {}),
|
|
172
218
|
...(config.verificationGate ? { verificationGate: config.verificationGate } : {}),
|
|
173
219
|
...(config.sandboxProvider ? { sandboxProvider: config.sandboxProvider } : {}),
|
|
220
|
+
// Working-memory / compaction seam (optional; absent => unchanged
|
|
221
|
+
// run path, byte-identical for every existing consumer).
|
|
222
|
+
...(config.compactionConfig ? { compactionConfig: config.compactionConfig } : {}),
|
|
223
|
+
...(config.workingMemoryProvider
|
|
224
|
+
? { workingMemoryProvider: config.workingMemoryProvider }
|
|
225
|
+
: {}),
|
|
174
226
|
},
|
|
175
227
|
listener,
|
|
176
228
|
)
|
|
177
229
|
|
|
178
230
|
const taskHandles = gateway.listTasks()
|
|
179
|
-
const taskResults = taskHandles
|
|
180
|
-
agentId: handle.agentId,
|
|
181
|
-
result: handle.result ?? {
|
|
182
|
-
runId,
|
|
183
|
-
status: handle.state as 'completed' | 'failed' | 'cancelled',
|
|
184
|
-
usage: { ...EMPTY_TOKEN_USAGE },
|
|
185
|
-
cost: { ...ZERO_COST },
|
|
186
|
-
iterations: 0,
|
|
187
|
-
durationMs: Date.now() - handle.createdAt,
|
|
188
|
-
messages: [],
|
|
189
|
-
},
|
|
190
|
-
taskIndex: index,
|
|
191
|
-
}))
|
|
231
|
+
const taskResults = synthesizeTaskResults(taskHandles, runId)
|
|
192
232
|
|
|
193
|
-
const completedTasks = taskResults
|
|
233
|
+
const completedTasks = countCompletedTasks(taskResults)
|
|
194
234
|
|
|
195
235
|
return {
|
|
196
236
|
runId: run.id,
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import type { TaskHandle } from '../../types/agent/gateway.js'
|
|
3
|
+
import type { AgentTaskResult, BaseAgentResult } from '../../types/agent/index.js'
|
|
4
|
+
import type { RunId, TaskId } from '../../types/ids/index.js'
|
|
5
|
+
import { countCompletedTasks, synthesizeTaskResults } from '../SupervisorAgent.js'
|
|
6
|
+
|
|
7
|
+
const RUN_ID = 'run-supervisor' as RunId
|
|
8
|
+
const NOW = 1_000_000
|
|
9
|
+
|
|
10
|
+
function at(results: AgentTaskResult[], index: number): AgentTaskResult {
|
|
11
|
+
const entry = results[index]
|
|
12
|
+
if (!entry) {
|
|
13
|
+
throw new Error(`expected a task result at index ${index}, got ${results.length} results`)
|
|
14
|
+
}
|
|
15
|
+
return entry
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function handle(overrides: Partial<TaskHandle> & Pick<TaskHandle, 'state'>): TaskHandle {
|
|
19
|
+
return {
|
|
20
|
+
taskId: 'task-1' as TaskId,
|
|
21
|
+
agentId: 'worker-1',
|
|
22
|
+
createdAt: NOW - 5_000,
|
|
23
|
+
...overrides,
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function completedResult(): BaseAgentResult {
|
|
28
|
+
return {
|
|
29
|
+
runId: 'run-worker' as RunId,
|
|
30
|
+
status: 'completed',
|
|
31
|
+
usage: {
|
|
32
|
+
promptTokens: 10,
|
|
33
|
+
completionTokens: 20,
|
|
34
|
+
totalTokens: 30,
|
|
35
|
+
cachedTokens: 0,
|
|
36
|
+
cacheWriteTokens: 0,
|
|
37
|
+
},
|
|
38
|
+
cost: {
|
|
39
|
+
inputCostPer1M: 0,
|
|
40
|
+
outputCostPer1M: 0,
|
|
41
|
+
totalCost: 0,
|
|
42
|
+
cacheDiscount: 0,
|
|
43
|
+
},
|
|
44
|
+
iterations: 3,
|
|
45
|
+
durationMs: 4_200,
|
|
46
|
+
messages: [],
|
|
47
|
+
result: 'real worker output',
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe('supervisor ledger truthfulness', () => {
|
|
52
|
+
describe('synthesizeTaskResults', () => {
|
|
53
|
+
it('synthesizes a FAILED result when a handle has no result, regardless of state', () => {
|
|
54
|
+
// A worker that ended with no result is NOT a success even when the
|
|
55
|
+
// handle reports a terminal 'completed' state — this is the exact
|
|
56
|
+
// fabrication that made the supervisor claim "3 workers done" with
|
|
57
|
+
// empty outputs (cowork task 02c5cf2b).
|
|
58
|
+
const results = synthesizeTaskResults(
|
|
59
|
+
[handle({ state: 'completed', result: undefined })],
|
|
60
|
+
RUN_ID,
|
|
61
|
+
NOW,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
expect(results).toHaveLength(1)
|
|
65
|
+
expect(at(results, 0).result.status).toBe('failed')
|
|
66
|
+
expect(at(results, 0).result.status).not.toBe('completed')
|
|
67
|
+
expect(at(results, 0).result.runId).toBe(RUN_ID)
|
|
68
|
+
expect(at(results, 0).result.durationMs).toBe(NOW - (NOW - 5_000))
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
it('synthesizes a FAILED result for a genuinely failed handle', () => {
|
|
72
|
+
const results = synthesizeTaskResults(
|
|
73
|
+
[handle({ state: 'failed', result: undefined })],
|
|
74
|
+
RUN_ID,
|
|
75
|
+
NOW,
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
expect(at(results, 0).result.status).toBe('failed')
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('synthesizes a FAILED result for a canceled handle with no result', () => {
|
|
82
|
+
const results = synthesizeTaskResults(
|
|
83
|
+
[handle({ state: 'canceled', result: undefined })],
|
|
84
|
+
RUN_ID,
|
|
85
|
+
NOW,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
expect(at(results, 0).result.status).toBe('failed')
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('preserves a present completed result verbatim (real workers unaffected)', () => {
|
|
92
|
+
const real = completedResult()
|
|
93
|
+
const results = synthesizeTaskResults(
|
|
94
|
+
[handle({ state: 'completed', result: real })],
|
|
95
|
+
RUN_ID,
|
|
96
|
+
NOW,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
expect(at(results, 0).result).toBe(real)
|
|
100
|
+
expect(at(results, 0).result.status).toBe('completed')
|
|
101
|
+
expect(at(results, 0).result.result).toBe('real worker output')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('preserves a present failed result verbatim', () => {
|
|
105
|
+
const failed: BaseAgentResult = { ...completedResult(), status: 'failed' }
|
|
106
|
+
const results = synthesizeTaskResults(
|
|
107
|
+
[handle({ state: 'failed', result: failed })],
|
|
108
|
+
RUN_ID,
|
|
109
|
+
NOW,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
expect(at(results, 0).result).toBe(failed)
|
|
113
|
+
expect(at(results, 0).result.status).toBe('failed')
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe('countCompletedTasks', () => {
|
|
118
|
+
it('excludes an absent-result handle from completedTasks', () => {
|
|
119
|
+
const taskResults = synthesizeTaskResults(
|
|
120
|
+
[
|
|
121
|
+
handle({
|
|
122
|
+
taskId: 'task-a' as TaskId,
|
|
123
|
+
agentId: 'a',
|
|
124
|
+
state: 'completed',
|
|
125
|
+
result: undefined,
|
|
126
|
+
}),
|
|
127
|
+
handle({
|
|
128
|
+
taskId: 'task-b' as TaskId,
|
|
129
|
+
agentId: 'b',
|
|
130
|
+
state: 'completed',
|
|
131
|
+
result: completedResult(),
|
|
132
|
+
}),
|
|
133
|
+
],
|
|
134
|
+
RUN_ID,
|
|
135
|
+
NOW,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
// Two handles, both with handle.state === 'completed', but only the
|
|
139
|
+
// one that actually produced a result counts.
|
|
140
|
+
expect(taskResults).toHaveLength(2)
|
|
141
|
+
expect(countCompletedTasks(taskResults)).toBe(1)
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('counts every present completed result', () => {
|
|
145
|
+
const taskResults = synthesizeTaskResults(
|
|
146
|
+
[
|
|
147
|
+
handle({
|
|
148
|
+
taskId: 'task-a' as TaskId,
|
|
149
|
+
agentId: 'a',
|
|
150
|
+
state: 'completed',
|
|
151
|
+
result: completedResult(),
|
|
152
|
+
}),
|
|
153
|
+
handle({
|
|
154
|
+
taskId: 'task-b' as TaskId,
|
|
155
|
+
agentId: 'b',
|
|
156
|
+
state: 'completed',
|
|
157
|
+
result: completedResult(),
|
|
158
|
+
}),
|
|
159
|
+
],
|
|
160
|
+
RUN_ID,
|
|
161
|
+
NOW,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
expect(countCompletedTasks(taskResults)).toBe(2)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
it('reports zero completed when no worker produced a result', () => {
|
|
168
|
+
const taskResults = synthesizeTaskResults(
|
|
169
|
+
[
|
|
170
|
+
handle({
|
|
171
|
+
taskId: 'task-a' as TaskId,
|
|
172
|
+
agentId: 'a',
|
|
173
|
+
state: 'completed',
|
|
174
|
+
result: undefined,
|
|
175
|
+
}),
|
|
176
|
+
handle({ taskId: 'task-b' as TaskId, agentId: 'b', state: 'running', result: undefined }),
|
|
177
|
+
],
|
|
178
|
+
RUN_ID,
|
|
179
|
+
NOW,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
expect(countCompletedTasks(taskResults)).toBe(0)
|
|
183
|
+
})
|
|
184
|
+
})
|
|
185
|
+
})
|
package/src/bridge/a2a/mapper.ts
CHANGED
|
@@ -60,6 +60,9 @@ const MAPPING: {
|
|
|
60
60
|
parts: [{ kind: 'text', text: e.error }],
|
|
61
61
|
}),
|
|
62
62
|
|
|
63
|
+
// Capability degradation is host-facing diagnostics, not A2A task state.
|
|
64
|
+
capability_warning: null,
|
|
65
|
+
|
|
63
66
|
iteration_started: (e, ctx) =>
|
|
64
67
|
statusEvent(e.runId, 'running', false, ctx, {
|
|
65
68
|
role: 'agent',
|
package/src/bridge/sse/mapper.ts
CHANGED
|
@@ -165,6 +165,11 @@ const MAPPING: {
|
|
|
165
165
|
run_completed: null,
|
|
166
166
|
run_failed: null,
|
|
167
167
|
|
|
168
|
+
// Not mapped to a wire event yet — hosts consume `capability_warning`
|
|
169
|
+
// from the RunEvent stream directly; promoting it to the SSE contract
|
|
170
|
+
// needs a StreamEventType addition first.
|
|
171
|
+
capability_warning: null,
|
|
172
|
+
|
|
168
173
|
agent_pending: {
|
|
169
174
|
wire: 'agent.pending',
|
|
170
175
|
transform: (e, runId) => ({
|
package/src/config/runtime.ts
CHANGED
|
@@ -18,6 +18,15 @@ export const TaskRouterConfigSchema = z
|
|
|
18
18
|
|
|
19
19
|
export const CompactionConfigSchema = z.object({
|
|
20
20
|
strategy: z.enum(['structured', 'sliding-window', 'disabled']).default('structured'),
|
|
21
|
+
/**
|
|
22
|
+
* Optional model context-window size (tokens) the compaction trigger
|
|
23
|
+
* measures the CURRENT window against. NO default: when omitted the
|
|
24
|
+
* trigger falls back to the run-level cumulative `tokenBudget` exactly as
|
|
25
|
+
* before, so existing consumers are byte-identical. Hosts that keep
|
|
26
|
+
* `tokenBudget` unlimited (0) set this so compaction fires on
|
|
27
|
+
* window-pressure instead of being a silent no-op.
|
|
28
|
+
*/
|
|
29
|
+
contextWindowTokens: z.number().positive().optional(),
|
|
21
30
|
triggerThreshold: z.number().min(0).max(1).default(0.7),
|
|
22
31
|
resetThreshold: z.number().min(0).max(1).default(0.4),
|
|
23
32
|
keepRecentMessages: z.number().positive().default(4),
|
|
@@ -126,6 +126,27 @@ describe('WebhookConnector', () => {
|
|
|
126
126
|
expect((await c.execute('send', { payload: {} })).success).toBe(false)
|
|
127
127
|
})
|
|
128
128
|
|
|
129
|
+
it('healthCheck returns false when the HEAD fetch throws', async () => {
|
|
130
|
+
const c = new WebhookConnector()
|
|
131
|
+
await c.connect({ url: 'https://hook.example.com' })
|
|
132
|
+
fetchMock.mockRejectedValueOnce(new Error('network down'))
|
|
133
|
+
expect(await c.healthCheck()).toBe(false)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
it('reads a text body when the response is not JSON', async () => {
|
|
137
|
+
const c = new WebhookConnector()
|
|
138
|
+
await c.connect({ url: 'https://hook.example.com' })
|
|
139
|
+
fetchMock.mockResolvedValueOnce(
|
|
140
|
+
makeResponse({
|
|
141
|
+
status: 200,
|
|
142
|
+
headers: { 'content-type': 'text/plain' },
|
|
143
|
+
body: 'plain-text-ack',
|
|
144
|
+
}),
|
|
145
|
+
)
|
|
146
|
+
const result = await c.execute('send', { payload: { k: 'v' } })
|
|
147
|
+
expect((result.output as { body: unknown }).body).toBe('plain-text-ack')
|
|
148
|
+
})
|
|
149
|
+
|
|
129
150
|
it('metadata.deliveredAt is a recent timestamp', async () => {
|
|
130
151
|
const before = Date.now()
|
|
131
152
|
const c = new WebhookConnector()
|
package/src/connector/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ import { generateMCPClientId } from '../../utils/id.js'
|
|
|
19
19
|
import { type Logger, getRootLogger } from '../../utils/logger.js'
|
|
20
20
|
import { HttpSseTransport } from './http-sse.js'
|
|
21
21
|
import { StdioTransport } from './stdio.js'
|
|
22
|
+
import { StreamableHttpTransport } from './streamable-http.js'
|
|
22
23
|
|
|
23
24
|
import { MCP_PROTOCOL_VERSION } from '../../constants/mcp/index.js'
|
|
24
25
|
import { VERSION } from '../../version.js'
|
|
@@ -175,6 +176,9 @@ export class MCPClient {
|
|
|
175
176
|
return new StdioTransport(config)
|
|
176
177
|
case 'http-sse':
|
|
177
178
|
return new HttpSseTransport(config)
|
|
179
|
+
case 'streamable_http':
|
|
180
|
+
case 'streamable-http':
|
|
181
|
+
return new StreamableHttpTransport(config)
|
|
178
182
|
default:
|
|
179
183
|
throw new Error(`Unsupported MCP transport type: ${(config as MCPTransportUnion).type}`)
|
|
180
184
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import type { MCPJsonRpcMessage } from '../../types/connector/index.js'
|
|
4
|
+
import { MCPClient } from './client.js'
|
|
5
|
+
|
|
6
|
+
afterEach(() => {
|
|
7
|
+
vi.unstubAllGlobals()
|
|
8
|
+
})
|
|
9
|
+
|
|
10
|
+
describe('Streamable HTTP MCP transport', () => {
|
|
11
|
+
it('connects through MCPClient, preserves session headers, and parses SSE responses', async () => {
|
|
12
|
+
const fetchMock = vi.fn<typeof fetch>()
|
|
13
|
+
fetchMock
|
|
14
|
+
.mockResolvedValueOnce(
|
|
15
|
+
jsonResponse(
|
|
16
|
+
{
|
|
17
|
+
jsonrpc: '2.0',
|
|
18
|
+
id: 1,
|
|
19
|
+
result: {
|
|
20
|
+
protocolVersion: '2024-11-05',
|
|
21
|
+
capabilities: { tools: { listChanged: true } },
|
|
22
|
+
serverInfo: { name: 'github' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{ 'mcp-session-id': 'sid_123' },
|
|
26
|
+
),
|
|
27
|
+
)
|
|
28
|
+
.mockResolvedValueOnce(new Response('', { status: 202 }))
|
|
29
|
+
.mockResolvedValueOnce(
|
|
30
|
+
sseResponse([
|
|
31
|
+
{ jsonrpc: '2.0', method: 'notifications/progress', params: { pct: 50 } },
|
|
32
|
+
{
|
|
33
|
+
jsonrpc: '2.0',
|
|
34
|
+
id: 2,
|
|
35
|
+
result: {
|
|
36
|
+
tools: [
|
|
37
|
+
{
|
|
38
|
+
name: 'search_repositories',
|
|
39
|
+
description: 'Search repositories',
|
|
40
|
+
inputSchema: { type: 'object' },
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
]),
|
|
46
|
+
)
|
|
47
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
48
|
+
|
|
49
|
+
const client = new MCPClient({
|
|
50
|
+
serverName: 'github',
|
|
51
|
+
transport: {
|
|
52
|
+
type: 'streamable_http',
|
|
53
|
+
url: 'https://mcp.example.test/mcp',
|
|
54
|
+
headers: { Authorization: 'Bearer token' },
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
const notifications: string[] = []
|
|
58
|
+
client.onNotification((method) => notifications.push(method))
|
|
59
|
+
|
|
60
|
+
await client.connect()
|
|
61
|
+
const tools = await client.listTools()
|
|
62
|
+
|
|
63
|
+
expect(tools.map((tool) => tool.name)).toEqual(['search_repositories'])
|
|
64
|
+
expect(notifications).toEqual(['notifications/progress'])
|
|
65
|
+
expect(fetchMock).toHaveBeenCalledTimes(3)
|
|
66
|
+
|
|
67
|
+
const initialize = requestAt(fetchMock, 0)
|
|
68
|
+
expect(initialize.input).toBe('https://mcp.example.test/mcp')
|
|
69
|
+
expect(initialize.body.method).toBe('initialize')
|
|
70
|
+
expect(initialize.headers.Authorization).toBe('Bearer token')
|
|
71
|
+
expect(initialize.headers.Accept).toBe('application/json, text/event-stream')
|
|
72
|
+
|
|
73
|
+
const initialized = requestAt(fetchMock, 1)
|
|
74
|
+
expect(initialized.body.method).toBe('notifications/initialized')
|
|
75
|
+
expect(initialized.headers['Mcp-Session-Id']).toBe('sid_123')
|
|
76
|
+
|
|
77
|
+
const listTools = requestAt(fetchMock, 2)
|
|
78
|
+
expect(listTools.body.method).toBe('tools/list')
|
|
79
|
+
expect(listTools.headers['Mcp-Session-Id']).toBe('sid_123')
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('supports the streamable-http alias without non-standard hint headers', async () => {
|
|
83
|
+
const fetchMock = vi.fn<typeof fetch>()
|
|
84
|
+
fetchMock
|
|
85
|
+
.mockResolvedValueOnce(
|
|
86
|
+
jsonResponse(
|
|
87
|
+
{
|
|
88
|
+
jsonrpc: '2.0',
|
|
89
|
+
id: 1,
|
|
90
|
+
result: {
|
|
91
|
+
protocolVersion: '2024-11-05',
|
|
92
|
+
capabilities: { tools: {} },
|
|
93
|
+
serverInfo: { name: 'linear' },
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
{ 'Mcp-Session-Id': 'sid_alias' },
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
.mockResolvedValueOnce(new Response(null, { status: 204 }))
|
|
100
|
+
.mockResolvedValueOnce(
|
|
101
|
+
jsonResponse({
|
|
102
|
+
jsonrpc: '2.0',
|
|
103
|
+
id: 2,
|
|
104
|
+
result: { content: [{ type: 'text', text: 'ok' }] },
|
|
105
|
+
}),
|
|
106
|
+
)
|
|
107
|
+
vi.stubGlobal('fetch', fetchMock)
|
|
108
|
+
|
|
109
|
+
const client = new MCPClient({
|
|
110
|
+
serverName: 'linear',
|
|
111
|
+
transport: {
|
|
112
|
+
type: 'streamable-http',
|
|
113
|
+
url: 'https://mcp.example.test/linear',
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
await client.connect()
|
|
118
|
+
const result = await client.callTool('create_issue', { title: 'Bug' })
|
|
119
|
+
|
|
120
|
+
expect(result.content).toEqual([{ type: 'text', text: 'ok' }])
|
|
121
|
+
const callTool = requestAt(fetchMock, 2)
|
|
122
|
+
expect(callTool.body.method).toBe('tools/call')
|
|
123
|
+
expect(callTool.headers).toEqual({
|
|
124
|
+
'Content-Type': 'application/json',
|
|
125
|
+
Accept: 'application/json, text/event-stream',
|
|
126
|
+
'Mcp-Session-Id': 'sid_alias',
|
|
127
|
+
})
|
|
128
|
+
})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
function jsonResponse(body: MCPJsonRpcMessage, headers?: Record<string, string>): Response {
|
|
132
|
+
return new Response(JSON.stringify(body), {
|
|
133
|
+
status: 200,
|
|
134
|
+
headers: {
|
|
135
|
+
'content-type': 'application/json',
|
|
136
|
+
...headers,
|
|
137
|
+
},
|
|
138
|
+
})
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function sseResponse(messages: MCPJsonRpcMessage[]): Response {
|
|
142
|
+
return new Response(messages.map((message) => `data: ${JSON.stringify(message)}\n\n`).join(''), {
|
|
143
|
+
status: 200,
|
|
144
|
+
headers: {
|
|
145
|
+
'content-type': 'text/event-stream',
|
|
146
|
+
},
|
|
147
|
+
})
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function requestAt(
|
|
151
|
+
fetchMock: ReturnType<typeof vi.fn<typeof fetch>>,
|
|
152
|
+
index: number,
|
|
153
|
+
): {
|
|
154
|
+
input: Parameters<typeof fetch>[0]
|
|
155
|
+
headers: Record<string, string>
|
|
156
|
+
body: MCPJsonRpcMessage
|
|
157
|
+
} {
|
|
158
|
+
const call = fetchMock.mock.calls[index]
|
|
159
|
+
if (!call) {
|
|
160
|
+
throw new Error(`No fetch call at index ${index}`)
|
|
161
|
+
}
|
|
162
|
+
const [input, init] = call
|
|
163
|
+
if (!init) {
|
|
164
|
+
throw new Error(`Fetch call at index ${index} had no init`)
|
|
165
|
+
}
|
|
166
|
+
const headers = init.headers as Record<string, string>
|
|
167
|
+
const body = JSON.parse(String(init.body)) as MCPJsonRpcMessage
|
|
168
|
+
return { input, headers, body }
|
|
169
|
+
}
|