@superdurable/dex-agents 0.4.0 → 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/README.md +506 -52
- package/dist/src/agent-context-compaction.d.ts +1 -1
- package/dist/src/agent-framework-runner.d.ts +122 -11
- package/dist/src/agent-framework-runner.js +4 -0
- package/dist/src/agent-framework-runner.js.map +1 -1
- package/dist/src/agent-local-directory-media-store.d.ts +111 -0
- package/dist/src/agent-local-directory-media-store.js +417 -0
- package/dist/src/agent-local-directory-media-store.js.map +1 -0
- package/dist/src/agent-media.d.ts +279 -0
- package/dist/src/agent-media.js +457 -0
- package/dist/src/agent-media.js.map +1 -0
- package/dist/src/agent-session-codecs.d.ts +3 -1
- package/dist/src/agent-session-codecs.js +45 -4
- package/dist/src/agent-session-codecs.js.map +1 -1
- package/dist/src/agent-session-failure.d.ts +4 -2
- package/dist/src/agent-session-failure.js +6 -2
- package/dist/src/agent-session-failure.js.map +1 -1
- package/dist/src/agent-session-flow-graph.d.ts +46 -0
- package/dist/src/agent-session-flow-graph.js +51 -0
- package/dist/src/agent-session-flow-graph.js.map +1 -1
- package/dist/src/agent-session-flow.d.ts +5 -3
- package/dist/src/agent-session-flow.js +10 -1
- package/dist/src/agent-session-flow.js.map +1 -1
- package/dist/src/agent-session-start.d.ts +1 -1
- package/dist/src/agent-session-start.js +2 -2
- package/dist/src/agent-session-start.js.map +1 -1
- package/dist/src/agent-session-steps.d.ts +2 -0
- package/dist/src/agent-session-steps.js +52 -3
- package/dist/src/agent-session-steps.js.map +1 -1
- package/dist/src/agent-session-tracer.js +1 -1
- package/dist/src/agent-session-tracer.js.map +1 -1
- package/dist/src/agent-session-transitions.d.ts +2 -0
- package/dist/src/agent-session-transitions.js +21 -1
- package/dist/src/agent-session-transitions.js.map +1 -1
- package/dist/src/agent-session-values.d.ts +76 -1
- package/dist/src/agent-session-values.js.map +1 -1
- package/dist/src/agent-session-wait-steps.js +14 -2
- package/dist/src/agent-session-wait-steps.js.map +1 -1
- package/dist/src/ai-sdk/ai-sdk-model-client.d.ts +15 -3
- package/dist/src/ai-sdk/ai-sdk-model-client.js +36 -7
- package/dist/src/ai-sdk/ai-sdk-model-client.js.map +1 -1
- package/dist/src/ai-sdk/index.d.ts +1 -1
- package/dist/src/ai-sdk/index.js +1 -1
- package/dist/src/ai-sdk/index.js.map +1 -1
- package/dist/src/{stepwise → builtin-loop}/agent-definition.d.ts +120 -5
- package/dist/src/builtin-loop/agent-definition.js.map +1 -0
- package/dist/src/builtin-loop/builtin-agent-session-flow.d.ts +69 -0
- package/dist/src/{stepwise/stepwise-agent-session-flow.js → builtin-loop/builtin-agent-session-flow.js} +15 -9
- package/dist/src/builtin-loop/builtin-agent-session-flow.js.map +1 -0
- package/dist/src/{stepwise/stepwise-agent-runner.d.ts → builtin-loop/builtin-loop-agent-runner.d.ts} +72 -14
- package/dist/src/{stepwise/stepwise-agent-runner.js → builtin-loop/builtin-loop-agent-runner.js} +284 -41
- package/dist/src/builtin-loop/builtin-loop-agent-runner.js.map +1 -0
- package/dist/src/{stepwise/stepwise-input-items.d.ts → builtin-loop/builtin-loop-input-items.d.ts} +7 -3
- package/dist/src/{stepwise/stepwise-input-items.js → builtin-loop/builtin-loop-input-items.js} +57 -10
- package/dist/src/builtin-loop/builtin-loop-input-items.js.map +1 -0
- package/dist/src/builtin-loop/function-tool.d.ts +282 -0
- package/dist/src/{stepwise → builtin-loop}/function-tool.js +63 -3
- package/dist/src/builtin-loop/function-tool.js.map +1 -0
- package/dist/src/builtin-loop/index.d.ts +8 -0
- package/dist/src/builtin-loop/index.js +13 -0
- package/dist/src/builtin-loop/index.js.map +1 -0
- package/dist/src/builtin-loop/json-schema-validation.js.map +1 -0
- package/dist/src/{stepwise → builtin-loop}/model-client.d.ts +36 -7
- package/dist/src/builtin-loop/model-client.js.map +1 -0
- package/dist/src/{stepwise → builtin-loop}/scripted-model-client.d.ts +8 -5
- package/dist/src/{stepwise → builtin-loop}/scripted-model-client.js +8 -5
- package/dist/src/builtin-loop/scripted-model-client.js.map +1 -0
- package/dist/src/{stepwise → builtin-loop}/tool-source.d.ts +2 -2
- package/dist/src/builtin-loop/tool-source.js.map +1 -0
- package/dist/src/connectors/connector-input-schema.js +1 -1
- package/dist/src/connectors/connector-input-schema.js.map +1 -1
- package/dist/src/connectors/connector-step-context.d.ts +19 -2
- package/dist/src/connectors/connector-step-context.js +61 -5
- package/dist/src/connectors/connector-step-context.js.map +1 -1
- package/dist/src/connectors/connector-tool-source.d.ts +61 -13
- package/dist/src/connectors/connector-tool-source.js +111 -24
- package/dist/src/connectors/connector-tool-source.js.map +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +3 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/mcp/index.d.ts +1 -0
- package/dist/src/mcp/index.js +1 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/mcp-dex-conventions.d.ts +57 -0
- package/dist/src/mcp/mcp-dex-conventions.js +90 -0
- package/dist/src/mcp/mcp-dex-conventions.js.map +1 -0
- package/dist/src/mcp/mcp-image-content.d.ts +20 -0
- package/dist/src/mcp/mcp-image-content.js +82 -0
- package/dist/src/mcp/mcp-image-content.js.map +1 -0
- package/dist/src/mcp/mcp-tool-source.d.ts +178 -17
- package/dist/src/mcp/mcp-tool-source.js +237 -34
- package/dist/src/mcp/mcp-tool-source.js.map +1 -1
- package/dist/src/s3/index.d.ts +1 -0
- package/dist/src/s3/index.js +9 -0
- package/dist/src/s3/index.js.map +1 -0
- package/dist/src/s3/s3-media-store.d.ts +138 -0
- package/dist/src/s3/s3-media-store.js +255 -0
- package/dist/src/s3/s3-media-store.js.map +1 -0
- package/dist/src/stepwise/index.d.ts +41 -8
- package/dist/src/stepwise/index.js +35 -5
- package/dist/src/stepwise/index.js.map +1 -1
- package/dist/src/testing/agent-session-test-client.d.ts +6 -3
- package/dist/src/testing/agent-session-test-client.js +10 -3
- package/dist/src/testing/agent-session-test-client.js.map +1 -1
- package/dist/src/testing/scripted-model-client.d.ts +1 -1
- package/dist/src/testing/scripted-model-client.js +2 -2
- package/dist/src/testing/scripted-model-client.js.map +1 -1
- package/dist/src/tool-source-router.d.ts +3 -3
- package/dist/src/tool-source-router.js +2 -2
- package/dist/src/ui-stream/agent-event-json.d.ts +2 -1
- package/dist/src/ui-stream/agent-event-json.js +3 -1
- package/dist/src/ui-stream/agent-event-json.js.map +1 -1
- package/dist/src/ui-stream/agent-ui-messages.d.ts +11 -3
- package/dist/src/ui-stream/agent-ui-messages.js +29 -5
- package/dist/src/ui-stream/agent-ui-messages.js.map +1 -1
- package/package.json +16 -3
- package/dist/src/stepwise/agent-definition.js.map +0 -1
- package/dist/src/stepwise/function-tool.d.ts +0 -130
- package/dist/src/stepwise/function-tool.js.map +0 -1
- package/dist/src/stepwise/json-schema-validation.js.map +0 -1
- package/dist/src/stepwise/model-client.js.map +0 -1
- package/dist/src/stepwise/scripted-model-client.js.map +0 -1
- package/dist/src/stepwise/stepwise-agent-runner.js.map +0 -1
- package/dist/src/stepwise/stepwise-agent-session-flow.d.ts +0 -35
- package/dist/src/stepwise/stepwise-agent-session-flow.js.map +0 -1
- package/dist/src/stepwise/stepwise-input-items.js.map +0 -1
- package/dist/src/stepwise/tool-source.js.map +0 -1
- /package/dist/src/{stepwise → builtin-loop}/agent-definition.js +0 -0
- /package/dist/src/{stepwise → builtin-loop}/json-schema-validation.d.ts +0 -0
- /package/dist/src/{stepwise → builtin-loop}/json-schema-validation.js +0 -0
- /package/dist/src/{stepwise → builtin-loop}/model-client.js +0 -0
- /package/dist/src/{stepwise → builtin-loop}/tool-source.js +0 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Copyright (c) 2026 Super Durable, Inc.
|
|
2
|
+
//
|
|
3
|
+
// Licensed under the Sustainable Use License 1.0.
|
|
4
|
+
// You may not use this file except in compliance with the License.
|
|
5
|
+
// See the LICENSE file in the repository root.
|
|
6
|
+
//
|
|
7
|
+
// SPDX-License-Identifier: LicenseRef-Sustainable-Use-1.0
|
|
8
|
+
import { ToolExecutionError } from "../agent-framework-runner.js";
|
|
9
|
+
import { ImageValidationError, maxImagesPerMessage } from "../agent-media.js";
|
|
10
|
+
import { serializeToolOutputJson } from "./mcp-resource-and-prompt-tools.js";
|
|
11
|
+
// Canonical padded base64, the same check as the Python core's.
|
|
12
|
+
const base64Pattern = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/;
|
|
13
|
+
/** Reports whether a result carries at least one `image` content block. */
|
|
14
|
+
export function hasImageContent(result) {
|
|
15
|
+
return result.content.some((block) => block.type === "image");
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Renders a failed result's text with `[image <n> omitted]` for each image block.
|
|
19
|
+
*
|
|
20
|
+
* The text becomes the record's `error`, so image bytes never reach session state.
|
|
21
|
+
*/
|
|
22
|
+
export function renderToolFailureText(result) {
|
|
23
|
+
let imageCount = 0;
|
|
24
|
+
return result.content
|
|
25
|
+
.map((block) => {
|
|
26
|
+
if (block.type === "text") {
|
|
27
|
+
return block.text;
|
|
28
|
+
}
|
|
29
|
+
if (block.type === "image") {
|
|
30
|
+
imageCount += 1;
|
|
31
|
+
return `[image ${imageCount} omitted]`;
|
|
32
|
+
}
|
|
33
|
+
return serializeToolOutputJson(block);
|
|
34
|
+
})
|
|
35
|
+
.join("\n");
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Uploads a successful result's image blocks and returns the tool output with their references.
|
|
39
|
+
*
|
|
40
|
+
* Each image block becomes `[image <n>]` in the output text, `n` counting from 1 in `images` order. The caller
|
|
41
|
+
* passes only a result with image blocks.
|
|
42
|
+
* @throws {ToolExecutionError} When the result has more than {@link maxImagesPerMessage} images, or an image is not
|
|
43
|
+
* valid base64 or not an image the store accepts.
|
|
44
|
+
*/
|
|
45
|
+
export async function storeToolResultImages(result, store, toolName) {
|
|
46
|
+
const imageCount = result.content.filter((block) => block.type === "image").length;
|
|
47
|
+
if (imageCount > maxImagesPerMessage) {
|
|
48
|
+
throw new ToolExecutionError(`MCP tool ${toolName} returned ${imageCount} images; a tool result carries at most ${maxImagesPerMessage}`);
|
|
49
|
+
}
|
|
50
|
+
const references = [];
|
|
51
|
+
const parts = [];
|
|
52
|
+
for (const block of result.content) {
|
|
53
|
+
if (block.type === "text") {
|
|
54
|
+
parts.push(block.text);
|
|
55
|
+
}
|
|
56
|
+
else if (block.type === "image") {
|
|
57
|
+
references.push(await storeImage(block, store, toolName, references.length + 1));
|
|
58
|
+
parts.push(`[image ${references.length}]`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
parts.push(serializeToolOutputJson(block));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return { output: parts.join("\n"), images: references };
|
|
65
|
+
}
|
|
66
|
+
async function storeImage(block, store, toolName, imageNumber) {
|
|
67
|
+
const data = typeof block.data === "string" && base64Pattern.test(block.data) ? Buffer.from(block.data, "base64") : undefined;
|
|
68
|
+
// Re-encoding refuses unused trailing bits, so one image has one accepted text.
|
|
69
|
+
if (data === undefined || data.toString("base64") !== block.data) {
|
|
70
|
+
throw new ToolExecutionError(`MCP tool ${toolName} returned image ${imageNumber}, which is not valid base64`);
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
return await store.putImage(new Uint8Array(data));
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (error instanceof ImageValidationError) {
|
|
77
|
+
throw new ToolExecutionError(`MCP tool ${toolName} returned image ${imageNumber}, which is not a supported image: ${error.message}`);
|
|
78
|
+
}
|
|
79
|
+
throw error;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=mcp-image-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp-image-content.js","sourceRoot":"","sources":["../../../src/mcp/mcp-image-content.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,kDAAkD;AAClD,mEAAmE;AACnE,+CAA+C;AAC/C,EAAE;AACF,0DAA0D;AAM1D,OAAO,EAAE,kBAAkB,EAA4B,MAAM,8BAA8B,CAAC;AAC5F,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAmB,MAAM,mBAAmB,CAAC;AAE/F,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAE7E,gEAAgE;AAChE,MAAM,aAAa,GAAG,kEAAkE,CAAC;AAIzF,2EAA2E;AAC3E,MAAM,UAAU,eAAe,CAAC,MAAsB;IACpD,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC;AAChE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAAsB;IAC1D,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,OAAO,MAAM,CAAC,OAAO;SAClB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QACb,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,OAAO,KAAK,CAAC,IAAI,CAAC;QACpB,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC3B,UAAU,IAAI,CAAC,CAAC;YAChB,OAAO,UAAU,UAAU,WAAW,CAAC;QACzC,CAAC;QACD,OAAO,uBAAuB,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC,CAAC;SACD,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAsB,EACtB,KAAiB,EACjB,QAAgB;IAEhB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IACnF,IAAI,UAAU,GAAG,mBAAmB,EAAE,CAAC;QACrC,MAAM,IAAI,kBAAkB,CAC1B,YAAY,QAAQ,aAAa,UAAU,0CAA0C,mBAAmB,EAAE,CAC3G,CAAC;IACJ,CAAC;IACD,MAAM,UAAU,GAA0B,EAAE,CAAC;IAC7C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACzB,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAClC,UAAU,CAAC,IAAI,CAAC,MAAM,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YACjF,KAAK,CAAC,IAAI,CAAC,UAAU,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAC1D,CAAC;AAED,KAAK,UAAU,UAAU,CACvB,KAAiB,EACjB,KAAiB,EACjB,QAAgB,EAChB,WAAmB;IAEnB,MAAM,IAAI,GACR,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnH,gFAAgF;IAChF,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,KAAK,CAAC,IAAI,EAAE,CAAC;QACjE,MAAM,IAAI,kBAAkB,CAAC,YAAY,QAAQ,mBAAmB,WAAW,6BAA6B,CAAC,CAAC;IAChH,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,QAAQ,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IACpD,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,IAAI,KAAK,YAAY,oBAAoB,EAAE,CAAC;YAC1C,MAAM,IAAI,kBAAkB,CAC1B,YAAY,QAAQ,mBAAmB,WAAW,qCAAqC,KAAK,CAAC,OAAO,EAAE,CACvG,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -1,9 +1,16 @@
|
|
|
1
|
-
import { type CallToolResult } from "@modelcontextprotocol/sdk/types.js";
|
|
1
|
+
import { McpError, type CallToolResult, type Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
2
2
|
import { type AgentEventWriter, type AgentTurnContext, type DeferredToolCall, type DeferredToolExecution, type McpToolAnnotations, type ToolExecutionResult, type ToolPolicy, type ToolTaskCancellation, type ToolTaskPoll, type ToolTaskPollOutcome, type ToolTaskStarted } from "../agent-framework-runner.js";
|
|
3
3
|
import type { ToolEffect } from "../agent-session-values.js";
|
|
4
|
-
import type { SourceToolDefinition } from "../
|
|
4
|
+
import type { SourceToolDefinition } from "../builtin-loop/tool-source.js";
|
|
5
|
+
import type { MediaStore } from "../agent-media.js";
|
|
5
6
|
import { type McpServerConfig } from "./mcp-server-config.js";
|
|
6
|
-
/**
|
|
7
|
+
/**
|
|
8
|
+
* `_meta` key that carries the session's idempotency key on every `tools/call`.
|
|
9
|
+
*
|
|
10
|
+
* The key is `<flowId>/<turnId>/<toolCallId>`: the same on every attempt of one tool call, and unique per
|
|
11
|
+
* call. A server that follows the Dex MCP conventions derives its own call ID from it, so a retried attempt
|
|
12
|
+
* is recognized as the same call.
|
|
13
|
+
*/
|
|
7
14
|
export declare const mcpIdempotencyKeyMetaKey = "io.superdurable.dex/idempotencyKey";
|
|
8
15
|
/** `AgentEvents` type for an MCP `notifications/progress` of a running tool call. */
|
|
9
16
|
export declare const mcpToolProgressEventType = "mcp.tool_progress";
|
|
@@ -19,7 +26,7 @@ export declare const defaultMcpTaskTtlMs = 86400000;
|
|
|
19
26
|
export declare const defaultMcpConnectionTimeoutMs = 30000;
|
|
20
27
|
/** Default number of connection attempts before a request reports the server unreachable. */
|
|
21
28
|
export declare const defaultMcpConnectionAttempts = 3;
|
|
22
|
-
/** Default {@link McpToolSourceOptions.sourceName}, which stored
|
|
29
|
+
/** Default {@link McpToolSourceOptions.sourceName}, which stored built-in loop calls are routed by. */
|
|
23
30
|
export declare const defaultMcpSourceName = "mcp";
|
|
24
31
|
/** Default age in milliseconds after which `listTools` lists a server again, in the background: 5 minutes. */
|
|
25
32
|
export declare const defaultMcpListingTtlMs = 300000;
|
|
@@ -50,10 +57,26 @@ export interface McpTool {
|
|
|
50
57
|
/** The server's behavior hints, or `undefined` when it sent none. */
|
|
51
58
|
readonly annotations: McpToolAnnotations | undefined;
|
|
52
59
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
60
|
+
* The effect the server declares when its configuration trusts it, else `undefined`: {@link dexEffect}
|
|
61
|
+
* when the tool declares one, otherwise the effect from {@link toolEffectFromMcpToolAnnotations}. An
|
|
62
|
+
* explicit {@link ToolPolicy} overrides it, except for a tool with a {@link dexEffect}, where
|
|
63
|
+
* {@link McpToolSource.deferredToolCall} lets the policy raise the effect but never lower it.
|
|
55
64
|
*/
|
|
56
65
|
readonly frameworkEffect: ToolEffect | undefined;
|
|
66
|
+
/**
|
|
67
|
+
* The effect the tool declares under `mcpToolEffectMetaKey` in its definition's `_meta`, when the server
|
|
68
|
+
* configuration trusts the server, else `undefined`. A tool with a `dexEffect` follows the Dex MCP
|
|
69
|
+
* conventions in the package README: a session {@link ToolPolicy} may raise its effect or its approval but
|
|
70
|
+
* never lowers its effect.
|
|
71
|
+
* @since 0.5.0
|
|
72
|
+
*/
|
|
73
|
+
readonly dexEffect?: ToolEffect | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Whether the tool declares `mcpToolApprovalMetaKey` together with a {@link dexEffect}. Always `false` or
|
|
76
|
+
* absent without a {@link dexEffect}.
|
|
77
|
+
* @since 0.5.0
|
|
78
|
+
*/
|
|
79
|
+
readonly isApprovalRecommended?: boolean;
|
|
57
80
|
/**
|
|
58
81
|
* The tool's MCP `execution.taskSupport`. A `required` tool is called as a task; see
|
|
59
82
|
* {@link McpToolSource} for who polls it.
|
|
@@ -96,7 +119,7 @@ export interface McpToolSourceOptions {
|
|
|
96
119
|
* Whether {@link McpToolSource.listTools} also offers `mcp_list_resources_and_prompts`, `mcp_read_resource`,
|
|
97
120
|
* and `mcp_get_prompt`, after the servers' tools, when at least one server is configured. Their requests are
|
|
98
121
|
* bounded by `connectionTimeoutMs`. Defaults to `false`. Enable it on one source of an agent only: two sources
|
|
99
|
-
* with it list the same names, which the
|
|
122
|
+
* with it list the same names, which the built-in loop runner refuses. See {@link McpToolSource}.
|
|
100
123
|
*/
|
|
101
124
|
readonly resourceAndPromptToolsEnabled?: boolean;
|
|
102
125
|
/**
|
|
@@ -110,11 +133,20 @@ export interface McpToolSourceOptions {
|
|
|
110
133
|
*/
|
|
111
134
|
readonly listingFailureBackoffMs?: number;
|
|
112
135
|
/**
|
|
113
|
-
* The source's stable name in a
|
|
136
|
+
* The source's stable name in a built-in loop agent's `toolSources`, which stored calls are routed by. Defaults to
|
|
114
137
|
* {@link defaultMcpSourceName}, `"mcp"`, so adding or removing a server keeps it. An agent with two `McpToolSource`
|
|
115
138
|
* instances must name at least one, because names must be unique.
|
|
116
139
|
*/
|
|
117
140
|
readonly sourceName?: string;
|
|
141
|
+
/**
|
|
142
|
+
* The store for the images of tool results. With a store, each `image` content block of a successful result is
|
|
143
|
+
* uploaded inside the tool Step and returned in {@link ToolExecutionResult.images}; the output text shows it as
|
|
144
|
+
* `[image <n>]`, counting from 1. More than `maxImagesPerMessage` images, invalid base64, or bytes the store
|
|
145
|
+
* refuses throw {@link ToolExecutionError}. Omitted, image blocks stay JSON in the output text. Pass the store the
|
|
146
|
+
* session's runner reads images from.
|
|
147
|
+
* @since 0.5.0
|
|
148
|
+
*/
|
|
149
|
+
readonly mediaStore?: MediaStore;
|
|
118
150
|
}
|
|
119
151
|
/** Per-call options of {@link McpToolSource.callTool}. */
|
|
120
152
|
export interface McpToolCallOptions {
|
|
@@ -132,6 +164,13 @@ export interface McpToolCallOptions {
|
|
|
132
164
|
readonly saveHeartbeatCheckpoint?: (checkpoint: string) => Promise<void>;
|
|
133
165
|
/** Aborts the requests, for example the tool Step's cancellation signal. */
|
|
134
166
|
readonly signal?: AbortSignal;
|
|
167
|
+
/**
|
|
168
|
+
* More `_meta` entries to send with the `tools/call` request, such as the identity the caller expects the tool
|
|
169
|
+
* to have. The {@link mcpIdempotencyKeyMetaKey} entry from {@link idempotencyKey} wins over an entry with the
|
|
170
|
+
* same key. Defaults to none.
|
|
171
|
+
* @since 0.5.0
|
|
172
|
+
*/
|
|
173
|
+
readonly requestMeta?: Readonly<Record<string, unknown>>;
|
|
135
174
|
}
|
|
136
175
|
/**
|
|
137
176
|
* A `tools/call` that reached an MCP server but returned no result.
|
|
@@ -172,6 +211,33 @@ export declare class McpToolListingError extends Error {
|
|
|
172
211
|
*/
|
|
173
212
|
constructor(serverName: string, detail: string);
|
|
174
213
|
}
|
|
214
|
+
export type ToolCallOutcome = {
|
|
215
|
+
readonly kind: "succeeded";
|
|
216
|
+
readonly output: string;
|
|
217
|
+
readonly result?: CallToolResult;
|
|
218
|
+
} | {
|
|
219
|
+
readonly kind: "failed";
|
|
220
|
+
readonly message: string;
|
|
221
|
+
} | {
|
|
222
|
+
readonly kind: "outcome_unknown";
|
|
223
|
+
readonly message: string;
|
|
224
|
+
} | {
|
|
225
|
+
readonly kind: "interrupted";
|
|
226
|
+
readonly detail: string;
|
|
227
|
+
} | {
|
|
228
|
+
readonly kind: "server_unreachable";
|
|
229
|
+
readonly detail: string;
|
|
230
|
+
} | {
|
|
231
|
+
readonly kind: "task_lost";
|
|
232
|
+
readonly detail: string;
|
|
233
|
+
} | {
|
|
234
|
+
readonly kind: "task_started";
|
|
235
|
+
readonly taskId: string;
|
|
236
|
+
readonly pollIntervalSeconds: number;
|
|
237
|
+
} | {
|
|
238
|
+
readonly kind: "task_pending";
|
|
239
|
+
readonly pollIntervalSeconds: number;
|
|
240
|
+
};
|
|
175
241
|
declare const nodeInspectSymbol: unique symbol;
|
|
176
242
|
/**
|
|
177
243
|
* Offers the tools of configured MCP servers to an agent, one Dex Step per call.
|
|
@@ -189,6 +255,14 @@ declare const nodeInspectSymbol: unique symbol;
|
|
|
189
255
|
* the session's `defaultToolEffect`, `unclassified` by default. Annotations are trusted by default. A
|
|
190
256
|
* {@link ToolPolicy} in `AgentSessionOptions.toolPolicies` keyed by {@link McpTool.name} wins.
|
|
191
257
|
*
|
|
258
|
+
* A server that follows the Dex MCP conventions declares each tool's effect and approval in its definition's
|
|
259
|
+
* `_meta` instead, under `mcpToolEffectMetaKey` and `mcpToolApprovalMetaKey`, as a connector manifest does for
|
|
260
|
+
* `ConnectorToolSource`. A session {@link ToolPolicy} for such a tool may keep or raise its safety but never
|
|
261
|
+
* lowers it: the call gets the more conservative of the two effects, and its writes run in the model's order.
|
|
262
|
+
* A result or JSON-RPC error
|
|
263
|
+
* whose `_meta` reports `mcpToolOutcomeUnknown` records `outcome_unknown` without a retry, and
|
|
264
|
+
* `mcpToolOutcomeRejected` records `failed`. See the package README, "Dex MCP conventions".
|
|
265
|
+
*
|
|
192
266
|
* The source uses `@modelcontextprotocol/sdk` 1.x, which speaks MCP up to 2025-11-25: every request
|
|
193
267
|
* opens a new connection, performs the `initialize` handshake, ends the HTTP protocol session, and
|
|
194
268
|
* closes it, so no protocol session outlives a request. Every `tools/call` carries the session's
|
|
@@ -197,11 +271,19 @@ declare const nodeInspectSymbol: unique symbol;
|
|
|
197
271
|
* Results map onto the session contract:
|
|
198
272
|
* - A result becomes the tool output: text blocks joined with newlines, other blocks and structured
|
|
199
273
|
* content as JSON.
|
|
274
|
+
* - With {@link McpToolSourceOptions.mediaStore}, a successful result's `image` blocks are uploaded and returned
|
|
275
|
+
* as {@link ToolExecutionResult.images}, and the text shows `[image <n>]` in their place. More than
|
|
276
|
+
* `maxImagesPerMessage` images, invalid base64, or bytes the store refuses throw {@link ToolExecutionError}.
|
|
277
|
+
* - A failed result's text, with or without a media store, shows `[image <n> omitted]` for each `image` block, so
|
|
278
|
+
* no image bytes reach the recorded error.
|
|
200
279
|
* - An `isError` result, a JSON-RPC error response other than an internal error, and a failed,
|
|
201
280
|
* cancelled, or `input_required` task throw {@link ToolExecutionError}, so the session records
|
|
202
281
|
* `failed` and the model sees the text. The 2025-11-25 tasks deliver input requests as server requests
|
|
203
282
|
* that must be answered on a live connection, so no durable question can carry them; the source cancels
|
|
204
283
|
* an `input_required` task.
|
|
284
|
+
* - A result or JSON-RPC error whose `_meta` reports the Dex outcome `unknown` throws
|
|
285
|
+
* {@link ToolOutcomeUnknownError}, so the session records `outcome_unknown` and never sends the call again.
|
|
286
|
+
* The outcome `rejected` throws {@link ToolExecutionError}. Either wins over `isError` and the error code.
|
|
205
287
|
* - No result after the request was sent throws {@link McpToolCallInterruptedError}; the tool Step's
|
|
206
288
|
* effect decides whether it is re-issued.
|
|
207
289
|
* - A call that was never sent, because its server is not configured or cannot be reached, throws
|
|
@@ -279,6 +361,7 @@ export declare class McpToolSource {
|
|
|
279
361
|
private readonly listingTtlMs;
|
|
280
362
|
private readonly listingFailureBackoffMs;
|
|
281
363
|
private readonly configuredSourceName;
|
|
364
|
+
private readonly mediaStore;
|
|
282
365
|
private tools;
|
|
283
366
|
private readonly resourceAndPromptTools;
|
|
284
367
|
/**
|
|
@@ -308,7 +391,7 @@ export declare class McpToolSource {
|
|
|
308
391
|
readonly refresh?: boolean;
|
|
309
392
|
}): Promise<readonly McpTool[]>;
|
|
310
393
|
/**
|
|
311
|
-
* The name a
|
|
394
|
+
* The name a built-in loop agent's `toolSources` routes stored calls by: `sourceName` as passed, or
|
|
312
395
|
* {@link defaultMcpSourceName}, `"mcp"`.
|
|
313
396
|
*/
|
|
314
397
|
get sourceName(): string;
|
|
@@ -328,7 +411,7 @@ export declare class McpToolSource {
|
|
|
328
411
|
*/
|
|
329
412
|
cachedTools(): readonly McpTool[];
|
|
330
413
|
/**
|
|
331
|
-
* Returns the tool names this source lists whatever its servers offer, so the
|
|
414
|
+
* Returns the tool names this source lists whatever its servers offer, so the built-in loop runner refuses a name
|
|
332
415
|
* collision when it is constructed instead of on every model call.
|
|
333
416
|
* @returns The resource and prompt tool names when they are enabled, else none.
|
|
334
417
|
*/
|
|
@@ -341,29 +424,71 @@ export declare class McpToolSource {
|
|
|
341
424
|
*/
|
|
342
425
|
tool(toolName: string): McpTool;
|
|
343
426
|
/**
|
|
344
|
-
* Returns a {@link ToolPolicy} for every listed tool whose
|
|
427
|
+
* Returns a {@link ToolPolicy} for every listed tool whose server declares an effect.
|
|
345
428
|
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
429
|
+
* A tool with a {@link McpTool.dexEffect} gets that effect and requires approval exactly when
|
|
430
|
+
* {@link McpTool.isApprovalRecommended}. To give a framework adapter these policies together with your own,
|
|
431
|
+
* use {@link mergeToolPolicies}. Do not spread your policies after these: yours would then replace a
|
|
432
|
+
* declaration and could lower its effect.
|
|
348
433
|
* @example
|
|
349
434
|
* ```ts
|
|
350
435
|
* await tools.listTools();
|
|
351
|
-
* const
|
|
436
|
+
* const declaredPolicies = tools.toolPolicies();
|
|
352
437
|
* ```
|
|
353
|
-
* @returns Policies by {@link McpTool.name}
|
|
438
|
+
* @returns Policies by {@link McpTool.name}: the declared approval for a tool with a
|
|
439
|
+
* {@link McpTool.dexEffect}, else the effect's default approval.
|
|
354
440
|
* @throws {RangeError} When {@link listTools} has not run.
|
|
355
441
|
*/
|
|
356
442
|
toolPolicies(): Record<string, ToolPolicy>;
|
|
443
|
+
/**
|
|
444
|
+
* Returns the application's tool policies merged with what the listed servers declare.
|
|
445
|
+
*
|
|
446
|
+
* Use it with a framework adapter that offers the MCP tools as its own tools: pass the result as the
|
|
447
|
+
* adapter's `toolPolicies`, so every call gets the policy {@link deferredToolCall} would give it.
|
|
448
|
+
*
|
|
449
|
+
* For a tool with a {@link McpTool.dexEffect}, your policy may keep or raise its safety but never lowers
|
|
450
|
+
* it. The tool gets the more conservative of `dexEffect` and your policy's effect, ranked `read`,
|
|
451
|
+
* `idempotent_write`, `non_idempotent_write`, then `unclassified`. Your `requiresApproval`, when set, wins,
|
|
452
|
+
* so `false` turns off a recommended approval. When you leave it out, the tool requires approval when
|
|
453
|
+
* {@link McpTool.isApprovalRecommended} is `true` or your policy's effect requires approval by default.
|
|
454
|
+
*
|
|
455
|
+
* For any other tool, your policy wins over its annotation effect, as before. Policies for tools that no
|
|
456
|
+
* listed server offers pass through unchanged.
|
|
457
|
+
* @example
|
|
458
|
+
* ```ts
|
|
459
|
+
* await tools.listTools();
|
|
460
|
+
* const toolPolicies = tools.mergeToolPolicies({
|
|
461
|
+
* files__delete: { effect: "non_idempotent_write" },
|
|
462
|
+
* lookup_order: { effect: "read" },
|
|
463
|
+
* });
|
|
464
|
+
* ```
|
|
465
|
+
* @param applicationPolicies - Your policies by tool name, keyed by {@link McpTool.name} for MCP tools.
|
|
466
|
+
* @returns A new record with every entry of `applicationPolicies` and of {@link toolPolicies}. A tool with
|
|
467
|
+
* a {@link McpTool.dexEffect} gets the combined policy, with `requiresApproval` set.
|
|
468
|
+
* @throws {RangeError} When {@link listTools} has not run.
|
|
469
|
+
* @since 0.5.0
|
|
470
|
+
*/
|
|
471
|
+
mergeToolPolicies(applicationPolicies: Readonly<Record<string, ToolPolicy>>): Record<string, ToolPolicy>;
|
|
357
472
|
/**
|
|
358
473
|
* Builds the {@link DeferredToolCall} for one model call of a listed MCP tool.
|
|
359
474
|
*
|
|
360
475
|
* The policy comes from `turn.resolveToolPolicy` with the tool's annotation effect, so an explicit
|
|
361
476
|
* {@link ToolPolicy} wins. The call's `toolSourceReference` names the server, the tool's MCP name, and
|
|
362
477
|
* its task support.
|
|
478
|
+
*
|
|
479
|
+
* For a tool with a {@link McpTool.dexEffect}, the session's {@link ToolPolicy} for the tool may keep or
|
|
480
|
+
* raise its safety but never lowers it. The call gets the more conservative of `dexEffect` and the policy's
|
|
481
|
+
* effect, ranked `read`, `idempotent_write`, `non_idempotent_write`, then `unclassified`. It waits for
|
|
482
|
+
* approval when the policy sets `requiresApproval`. When the policy leaves `requiresApproval` out, the call
|
|
483
|
+
* waits when {@link McpTool.isApprovalRecommended} is `true` or the policy's effect requires approval by
|
|
484
|
+
* default. Without a policy, it waits exactly when {@link McpTool.isApprovalRecommended} is `true`. A write
|
|
485
|
+
* runs in the model's order after the turn's earlier writes (`DeferredToolCall.ordered`). Changed in
|
|
486
|
+
* 0.5.0: for a tool with a `dexEffect`, a {@link ToolPolicy} may raise the effect or the approval but never
|
|
487
|
+
* lowers the effect.
|
|
363
488
|
* @param turn - The `RunModel` context.
|
|
364
489
|
* @param call - Framework tool call ID, the {@link McpTool.name} the model called, the arguments as JSON
|
|
365
490
|
* text, and optionally `listedTool`, the {@link McpTool} the listing offered for this model call, as the
|
|
366
|
-
*
|
|
491
|
+
* built-in loop runner passes it, so a refresh that finished meanwhile cannot change or remove it. Without it, the
|
|
367
492
|
* tool is looked up in the current listing.
|
|
368
493
|
* @returns The call to put in `ToolCallsRequested.toolCalls`.
|
|
369
494
|
* @throws {RangeError} When no `listedTool` is given and {@link listTools} has not run or the tool is unknown.
|
|
@@ -402,13 +527,42 @@ export declare class McpToolSource {
|
|
|
402
527
|
* A Streamable HTTP server without protocol sessions that answers with a task gets {@link ToolTaskStarted}.
|
|
403
528
|
* The source saves the task ID as the heartbeat checkpoint first; a retried attempt that reads it returns
|
|
404
529
|
* the same task without calling the tool again.
|
|
530
|
+
*
|
|
531
|
+
* Changed in 0.5.0: takes `options`, whose `requestMeta` adds entries to the `tools/call` request's `_meta`.
|
|
405
532
|
* @param call - The approved call from the tool Step.
|
|
533
|
+
* @param options - `requestMeta`: more `_meta` entries for the `tools/call` request, as in
|
|
534
|
+
* {@link McpToolCallOptions.requestMeta}. The idempotency key entry wins on a name clash. Defaults to none.
|
|
406
535
|
* @returns The tool output the model sees, or {@link ToolTaskStarted} for a task that `PollToolTask` polls.
|
|
407
536
|
* @throws {ToolExecutionError} When the arguments are not a JSON object, the tool failed, the server
|
|
408
537
|
* answered with an error or asked for input, or the call was never sent.
|
|
409
538
|
* @throws {McpToolCallInterruptedError} When the call was sent and no result came back.
|
|
410
539
|
*/
|
|
411
|
-
executeDeferredToolCall(call: DeferredToolExecution
|
|
540
|
+
executeDeferredToolCall(call: DeferredToolExecution, options?: {
|
|
541
|
+
readonly requestMeta?: Readonly<Record<string, unknown>>;
|
|
542
|
+
}): Promise<ToolExecutionResult | ToolTaskStarted>;
|
|
543
|
+
/**
|
|
544
|
+
* Returns whether {@link executeDeferredToolCall} will resume a task that an earlier attempt of the same call
|
|
545
|
+
* started and can never send a new `tools/call` for this call.
|
|
546
|
+
*
|
|
547
|
+
* It is `true` only when the call's heartbeat checkpoint names a task of the same server and tool that the
|
|
548
|
+
* session polls, and the session still polls tool tasks (`call.supportsToolTasks`). Then the call returns that
|
|
549
|
+
* task at once, without contacting the server. In every other case, including a task polled inside the tool
|
|
550
|
+
* Step, such as one bound to an MCP protocol session, the call may send `tools/call`: a task the server no longer
|
|
551
|
+
* knows is called again. Use it to skip a check that only guards a new send, such as checking a tool's current
|
|
552
|
+
* listing, so a catalog that is down or changed does not stop the recovery of a task already running.
|
|
553
|
+
*
|
|
554
|
+
* @example
|
|
555
|
+
* ```ts
|
|
556
|
+
* if (!tools.willResumeToolTaskWithoutSending(call)) {
|
|
557
|
+
* await checkCurrentListing(call.toolCall.toolName);
|
|
558
|
+
* }
|
|
559
|
+
* return tools.executeDeferredToolCall(call);
|
|
560
|
+
* ```
|
|
561
|
+
* @param call - The tool Step's call, with this source's own `toolSourceReference`.
|
|
562
|
+
* @returns `true` when the call resumes a session-polled task and sends nothing; otherwise `false`.
|
|
563
|
+
* @since 0.5.0
|
|
564
|
+
*/
|
|
565
|
+
willResumeToolTaskWithoutSending(call: DeferredToolExecution): boolean;
|
|
412
566
|
/**
|
|
413
567
|
* Polls one MCP task that {@link executeDeferredToolCall} started, once.
|
|
414
568
|
*
|
|
@@ -420,6 +574,8 @@ export declare class McpToolSource {
|
|
|
420
574
|
* server's `pollInterval` to at most a minute.
|
|
421
575
|
* @throws {ToolExecutionError} When the task failed, was cancelled, returned an `isError` result, or asked
|
|
422
576
|
* for input.
|
|
577
|
+
* @throws {ToolOutcomeUnknownError} When the task's result reported the Dex outcome `unknown`; the session
|
|
578
|
+
* records `outcome_unknown`.
|
|
423
579
|
* @throws {McpToolCallInterruptedError} When the server could not be reached, no longer knows the task, or
|
|
424
580
|
* answered a poll with an error. The task may still run, so `PollToolTask` retries and then records the
|
|
425
581
|
* call by its effect.
|
|
@@ -458,6 +614,7 @@ export declare class McpToolSource {
|
|
|
458
614
|
private runResourceAndPromptTool;
|
|
459
615
|
private callLimits;
|
|
460
616
|
private resolveTarget;
|
|
617
|
+
private toolExecutionResult;
|
|
461
618
|
private callTarget;
|
|
462
619
|
private targetFromReference;
|
|
463
620
|
private targetFromListing;
|
|
@@ -465,6 +622,10 @@ export declare class McpToolSource {
|
|
|
465
622
|
private combineServerTools;
|
|
466
623
|
private listServerTools;
|
|
467
624
|
}
|
|
625
|
+
export declare function mcpToolFromListing(server: McpServerConfig, tool: Tool): McpTool;
|
|
626
|
+
export declare function outcomeFromToolResult(result: CallToolResult): ToolCallOutcome;
|
|
627
|
+
/** Maps a JSON-RPC error answer; `undefined` leaves the outcome open, so the effect class decides. */
|
|
628
|
+
export declare function outcomeFromMcpError(error: McpError): ToolCallOutcome | undefined;
|
|
468
629
|
/** Joins text blocks; other content blocks and structured content render as `serializeToolOutputJson` text. */
|
|
469
630
|
export declare function renderToolResultText(result: CallToolResult): string;
|
|
470
631
|
export {};
|