@better-agent/core 0.1.0-beta.1
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 +3 -0
- package/dist/agent/constants.mjs +6 -0
- package/dist/agent/constants.mjs.map +1 -0
- package/dist/agent/define-agent.d.mts +29 -0
- package/dist/agent/define-agent.d.mts.map +1 -0
- package/dist/agent/define-agent.mjs +27 -0
- package/dist/agent/define-agent.mjs.map +1 -0
- package/dist/agent/index.d.mts +2 -0
- package/dist/agent/types.d.mts +216 -0
- package/dist/agent/types.d.mts.map +1 -0
- package/dist/agent/validation.mjs +64 -0
- package/dist/agent/validation.mjs.map +1 -0
- package/dist/api.d.mts +8 -0
- package/dist/api.d.mts.map +1 -0
- package/dist/api.mjs +63 -0
- package/dist/api.mjs.map +1 -0
- package/dist/app/config.mjs +43 -0
- package/dist/app/config.mjs.map +1 -0
- package/dist/app/create-app.d.mts +36 -0
- package/dist/app/create-app.d.mts.map +1 -0
- package/dist/app/create-app.mjs +132 -0
- package/dist/app/create-app.mjs.map +1 -0
- package/dist/app/registry.mjs +43 -0
- package/dist/app/registry.mjs.map +1 -0
- package/dist/app/types.d.mts +142 -0
- package/dist/app/types.d.mts.map +1 -0
- package/dist/events/constants.d.mts +49 -0
- package/dist/events/constants.d.mts.map +1 -0
- package/dist/events/constants.mjs +46 -0
- package/dist/events/constants.mjs.map +1 -0
- package/dist/events/index.d.mts +4 -0
- package/dist/events/index.mjs +3 -0
- package/dist/events/types.d.mts +289 -0
- package/dist/events/types.d.mts.map +1 -0
- package/dist/index.d.mts +23 -0
- package/dist/index.mjs +14 -0
- package/dist/internal/id.mjs +21 -0
- package/dist/internal/id.mjs.map +1 -0
- package/dist/internal/types.d.mts +11 -0
- package/dist/internal/types.d.mts.map +1 -0
- package/dist/mcp/error/mcp-client-error.d.mts +36 -0
- package/dist/mcp/error/mcp-client-error.d.mts.map +1 -0
- package/dist/mcp/error/mcp-client-error.mjs +33 -0
- package/dist/mcp/error/mcp-client-error.mjs.map +1 -0
- package/dist/mcp/index.d.mts +8 -0
- package/dist/mcp/index.mjs +9 -0
- package/dist/mcp/tool/json-rpc-message.d.mts +50 -0
- package/dist/mcp/tool/json-rpc-message.d.mts.map +1 -0
- package/dist/mcp/tool/json-rpc-message.mjs +84 -0
- package/dist/mcp/tool/json-rpc-message.mjs.map +1 -0
- package/dist/mcp/tool/mcp-client.d.mts +71 -0
- package/dist/mcp/tool/mcp-client.d.mts.map +1 -0
- package/dist/mcp/tool/mcp-client.mjs +304 -0
- package/dist/mcp/tool/mcp-client.mjs.map +1 -0
- package/dist/mcp/tool/mcp-http-transport.d.mts +62 -0
- package/dist/mcp/tool/mcp-http-transport.d.mts.map +1 -0
- package/dist/mcp/tool/mcp-http-transport.mjs +307 -0
- package/dist/mcp/tool/mcp-http-transport.mjs.map +1 -0
- package/dist/mcp/tool/mcp-tools.d.mts +20 -0
- package/dist/mcp/tool/mcp-tools.d.mts.map +1 -0
- package/dist/mcp/tool/mcp-tools.mjs +73 -0
- package/dist/mcp/tool/mcp-tools.mjs.map +1 -0
- package/dist/mcp/tool/mcp-transport.d.mts +81 -0
- package/dist/mcp/tool/mcp-transport.d.mts.map +1 -0
- package/dist/mcp/tool/mcp-transport.mjs +11 -0
- package/dist/mcp/tool/mcp-transport.mjs.map +1 -0
- package/dist/mcp/tool/types.d.mts +230 -0
- package/dist/mcp/tool/types.d.mts.map +1 -0
- package/dist/mcp/tool/types.mjs +19 -0
- package/dist/mcp/tool/types.mjs.map +1 -0
- package/dist/persistence/index.d.mts +3 -0
- package/dist/persistence/index.mjs +3 -0
- package/dist/persistence/memory.d.mts +21 -0
- package/dist/persistence/memory.d.mts.map +1 -0
- package/dist/persistence/memory.mjs +107 -0
- package/dist/persistence/memory.mjs.map +1 -0
- package/dist/persistence/types.d.mts +124 -0
- package/dist/persistence/types.d.mts.map +1 -0
- package/dist/plugins/index.d.mts +2 -0
- package/dist/plugins/runtime.d.mts +17 -0
- package/dist/plugins/runtime.d.mts.map +1 -0
- package/dist/plugins/runtime.mjs +456 -0
- package/dist/plugins/runtime.mjs.map +1 -0
- package/dist/plugins/types.d.mts +344 -0
- package/dist/plugins/types.d.mts.map +1 -0
- package/dist/providers/index.d.mts +9 -0
- package/dist/providers/index.mjs +0 -0
- package/dist/providers/types/capabilities.d.mts +153 -0
- package/dist/providers/types/capabilities.d.mts.map +1 -0
- package/dist/providers/types/content.d.mts +125 -0
- package/dist/providers/types/content.d.mts.map +1 -0
- package/dist/providers/types/conversation.d.mts +32 -0
- package/dist/providers/types/conversation.d.mts.map +1 -0
- package/dist/providers/types/index.d.mts +8 -0
- package/dist/providers/types/input.d.mts +74 -0
- package/dist/providers/types/input.d.mts.map +1 -0
- package/dist/providers/types/model.d.mts +68 -0
- package/dist/providers/types/model.d.mts.map +1 -0
- package/dist/providers/types/output.d.mts +29 -0
- package/dist/providers/types/output.d.mts.map +1 -0
- package/dist/providers/types/response.d.mts +35 -0
- package/dist/providers/types/response.d.mts.map +1 -0
- package/dist/providers/types/tool-calls.d.mts +51 -0
- package/dist/providers/types/tool-calls.d.mts.map +1 -0
- package/dist/run/agent-loop.mjs +231 -0
- package/dist/run/agent-loop.mjs.map +1 -0
- package/dist/run/event-queue.mjs +67 -0
- package/dist/run/event-queue.mjs.map +1 -0
- package/dist/run/execute-tool-calls.mjs +550 -0
- package/dist/run/execute-tool-calls.mjs.map +1 -0
- package/dist/run/execution.mjs +93 -0
- package/dist/run/execution.mjs.map +1 -0
- package/dist/run/helpers.mjs +466 -0
- package/dist/run/helpers.mjs.map +1 -0
- package/dist/run/hooks.mjs +124 -0
- package/dist/run/hooks.mjs.map +1 -0
- package/dist/run/index.d.mts +4 -0
- package/dist/run/messages.d.mts +8 -0
- package/dist/run/messages.d.mts.map +1 -0
- package/dist/run/messages.mjs +83 -0
- package/dist/run/messages.mjs.map +1 -0
- package/dist/run/model-strategy.mjs +105 -0
- package/dist/run/model-strategy.mjs.map +1 -0
- package/dist/run/output-errors.d.mts +75 -0
- package/dist/run/output-errors.d.mts.map +1 -0
- package/dist/run/pending-tools.d.mts +1 -0
- package/dist/run/pending-tools.mjs +185 -0
- package/dist/run/pending-tools.mjs.map +1 -0
- package/dist/run/registry.mjs +22 -0
- package/dist/run/registry.mjs.map +1 -0
- package/dist/run/runtime.d.mts +19 -0
- package/dist/run/runtime.d.mts.map +1 -0
- package/dist/run/runtime.mjs +491 -0
- package/dist/run/runtime.mjs.map +1 -0
- package/dist/run/stop-conditions.mjs +41 -0
- package/dist/run/stop-conditions.mjs.map +1 -0
- package/dist/run/types.d.mts +348 -0
- package/dist/run/types.d.mts.map +1 -0
- package/dist/schema/index.d.mts +2 -0
- package/dist/schema/resolve-json-schema.d.mts +12 -0
- package/dist/schema/resolve-json-schema.d.mts.map +1 -0
- package/dist/schema/resolve-json-schema.mjs +167 -0
- package/dist/schema/resolve-json-schema.mjs.map +1 -0
- package/dist/schema/types.d.mts +27 -0
- package/dist/schema/types.d.mts.map +1 -0
- package/dist/server/create-server.d.mts +21 -0
- package/dist/server/create-server.d.mts.map +1 -0
- package/dist/server/create-server.mjs +107 -0
- package/dist/server/create-server.mjs.map +1 -0
- package/dist/server/http.mjs +182 -0
- package/dist/server/http.mjs.map +1 -0
- package/dist/server/index.d.mts +3 -0
- package/dist/server/index.mjs +3 -0
- package/dist/server/routes.mjs +399 -0
- package/dist/server/routes.mjs.map +1 -0
- package/dist/server/types.d.mts +31 -0
- package/dist/server/types.d.mts.map +1 -0
- package/dist/tools/constants.d.mts +12 -0
- package/dist/tools/constants.d.mts.map +1 -0
- package/dist/tools/constants.mjs +13 -0
- package/dist/tools/constants.mjs.map +1 -0
- package/dist/tools/define-tool.d.mts +25 -0
- package/dist/tools/define-tool.d.mts.map +1 -0
- package/dist/tools/define-tool.mjs +76 -0
- package/dist/tools/define-tool.mjs.map +1 -0
- package/dist/tools/index.d.mts +5 -0
- package/dist/tools/lazy-tools.d.mts +49 -0
- package/dist/tools/lazy-tools.d.mts.map +1 -0
- package/dist/tools/lazy-tools.mjs +87 -0
- package/dist/tools/lazy-tools.mjs.map +1 -0
- package/dist/tools/resolve-tools.d.mts +12 -0
- package/dist/tools/resolve-tools.d.mts.map +1 -0
- package/dist/tools/resolve-tools.mjs +86 -0
- package/dist/tools/resolve-tools.mjs.map +1 -0
- package/dist/tools/types.d.mts +318 -0
- package/dist/tools/types.d.mts.map +1 -0
- package/dist/tools/validation.mjs +23 -0
- package/dist/tools/validation.mjs.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//#region src/run/event-queue.ts
|
|
2
|
+
/** Creates an async event queue. */
|
|
3
|
+
const createAsyncEventQueue = () => {
|
|
4
|
+
const values = [];
|
|
5
|
+
const waiters = [];
|
|
6
|
+
let closed = false;
|
|
7
|
+
let failure;
|
|
8
|
+
const settleNext = (result) => {
|
|
9
|
+
const waiter = waiters.shift();
|
|
10
|
+
if (waiter) {
|
|
11
|
+
waiter.resolve(result);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
return false;
|
|
15
|
+
};
|
|
16
|
+
const rejectAll = (error) => {
|
|
17
|
+
for (const waiter of waiters.splice(0)) waiter.reject(error);
|
|
18
|
+
};
|
|
19
|
+
const closeAll = () => {
|
|
20
|
+
for (const waiter of waiters.splice(0)) waiter.resolve({
|
|
21
|
+
value: void 0,
|
|
22
|
+
done: true
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
return {
|
|
26
|
+
push(value) {
|
|
27
|
+
if (closed || failure !== void 0) return;
|
|
28
|
+
if (settleNext({
|
|
29
|
+
value,
|
|
30
|
+
done: false
|
|
31
|
+
})) return;
|
|
32
|
+
values.push(value);
|
|
33
|
+
},
|
|
34
|
+
close() {
|
|
35
|
+
if (closed || failure !== void 0) return;
|
|
36
|
+
closed = true;
|
|
37
|
+
closeAll();
|
|
38
|
+
},
|
|
39
|
+
fail(error) {
|
|
40
|
+
if (closed || failure !== void 0) return;
|
|
41
|
+
failure = error;
|
|
42
|
+
rejectAll(error);
|
|
43
|
+
},
|
|
44
|
+
async *iterate() {
|
|
45
|
+
while (true) {
|
|
46
|
+
if (values.length > 0) {
|
|
47
|
+
yield values.shift();
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (failure !== void 0) throw failure;
|
|
51
|
+
if (closed) return;
|
|
52
|
+
const next = await new Promise((resolve, reject) => {
|
|
53
|
+
waiters.push({
|
|
54
|
+
resolve,
|
|
55
|
+
reject
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
if (next.done) return;
|
|
59
|
+
yield next.value;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
//#endregion
|
|
66
|
+
export { createAsyncEventQueue };
|
|
67
|
+
//# sourceMappingURL=event-queue.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-queue.mjs","names":[],"sources":["../../src/run/event-queue.ts"],"sourcesContent":["/** Async queue for stream events. */\nexport interface AsyncEventQueue<T> {\n push(value: T): void;\n close(): void;\n fail(error: unknown): void;\n iterate(): AsyncGenerator<T>;\n}\n\n/** Creates an async event queue. */\nexport const createAsyncEventQueue = <T>(): AsyncEventQueue<T> => {\n const values: T[] = [];\n const waiters: {\n resolve: (result: IteratorResult<T>) => void;\n reject: (error: unknown) => void;\n }[] = [];\n let closed = false;\n let failure: unknown;\n\n const settleNext = (result: IteratorResult<T>) => {\n const waiter = waiters.shift();\n if (waiter) {\n waiter.resolve(result);\n return true;\n }\n\n return false;\n };\n\n const rejectAll = (error: unknown) => {\n for (const waiter of waiters.splice(0)) {\n waiter.reject(error);\n }\n };\n\n const closeAll = () => {\n for (const waiter of waiters.splice(0)) {\n waiter.resolve({ value: undefined, done: true } as IteratorResult<T>);\n }\n };\n\n return {\n push(value) {\n if (closed || failure !== undefined) return;\n if (settleNext({ value, done: false })) return;\n values.push(value);\n },\n close() {\n if (closed || failure !== undefined) return;\n closed = true;\n closeAll();\n },\n fail(error) {\n if (closed || failure !== undefined) return;\n failure = error;\n rejectAll(error);\n },\n async *iterate() {\n while (true) {\n if (values.length > 0) {\n // biome-ignore lint/style/noNonNullAssertion: queue length was checked above\n yield values.shift()!;\n continue;\n }\n\n if (failure !== undefined) {\n throw failure;\n }\n\n if (closed) {\n return;\n }\n\n const next = await new Promise<IteratorResult<T>>((resolve, reject) => {\n waiters.push({ resolve, reject });\n });\n\n if (next.done) {\n return;\n }\n\n yield next.value;\n }\n },\n };\n};\n"],"mappings":";;AASA,MAAa,8BAAqD;CAC9D,MAAM,SAAc,EAAE;CACtB,MAAM,UAGA,EAAE;CACR,IAAI,SAAS;CACb,IAAI;CAEJ,MAAM,cAAc,WAA8B;EAC9C,MAAM,SAAS,QAAQ,OAAO;AAC9B,MAAI,QAAQ;AACR,UAAO,QAAQ,OAAO;AACtB,UAAO;;AAGX,SAAO;;CAGX,MAAM,aAAa,UAAmB;AAClC,OAAK,MAAM,UAAU,QAAQ,OAAO,EAAE,CAClC,QAAO,OAAO,MAAM;;CAI5B,MAAM,iBAAiB;AACnB,OAAK,MAAM,UAAU,QAAQ,OAAO,EAAE,CAClC,QAAO,QAAQ;GAAE,OAAO;GAAW,MAAM;GAAM,CAAsB;;AAI7E,QAAO;EACH,KAAK,OAAO;AACR,OAAI,UAAU,YAAY,OAAW;AACrC,OAAI,WAAW;IAAE;IAAO,MAAM;IAAO,CAAC,CAAE;AACxC,UAAO,KAAK,MAAM;;EAEtB,QAAQ;AACJ,OAAI,UAAU,YAAY,OAAW;AACrC,YAAS;AACT,aAAU;;EAEd,KAAK,OAAO;AACR,OAAI,UAAU,YAAY,OAAW;AACrC,aAAU;AACV,aAAU,MAAM;;EAEpB,OAAO,UAAU;AACb,UAAO,MAAM;AACT,QAAI,OAAO,SAAS,GAAG;AAEnB,WAAM,OAAO,OAAO;AACpB;;AAGJ,QAAI,YAAY,OACZ,OAAM;AAGV,QAAI,OACA;IAGJ,MAAM,OAAO,MAAM,IAAI,SAA4B,SAAS,WAAW;AACnE,aAAQ,KAAK;MAAE;MAAS;MAAQ,CAAC;MACnC;AAEF,QAAI,KAAK,KACL;AAGJ,UAAM,KAAK;;;EAGtB"}
|
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import { validateInput } from "../schema/resolve-json-schema.mjs";
|
|
2
|
+
import { Events } from "../events/constants.mjs";
|
|
3
|
+
import { BetterAgentError } from "@better-agent/shared/errors";
|
|
4
|
+
import { logger } from "@better-agent/shared/logger";
|
|
5
|
+
import { safeJsonParse } from "@better-agent/shared/utils";
|
|
6
|
+
|
|
7
|
+
//#region src/run/execute-tool-calls.ts
|
|
8
|
+
const MAX_TOOL_ERROR_REPAIR_DEPTH = 2;
|
|
9
|
+
const MAX_TOOL_ERROR_RETRY_ATTEMPTS = 3;
|
|
10
|
+
const getToolRunName = (tool) => tool.kind === "hosted" ? typeof tool.name === "string" && tool.name.length > 0 ? tool.name : typeof tool.type === "string" && tool.type.length > 0 ? tool.type : void 0 : typeof tool.name === "string" && tool.name.length > 0 ? tool.name : void 0;
|
|
11
|
+
/** Executes the next batch of tool calls. */
|
|
12
|
+
const executeToolCalls = async (params) => {
|
|
13
|
+
const results = [];
|
|
14
|
+
const throwIfAborted = () => {
|
|
15
|
+
if (params.signal.aborted) throw BetterAgentError.fromCode("ABORTED", "Tool execution was aborted.", { trace: [{ at: "core.run.executeToolCalls.aborted" }] });
|
|
16
|
+
};
|
|
17
|
+
const isAbortError = (error) => params.signal.aborted === true || error instanceof BetterAgentError && error.code === "ABORTED";
|
|
18
|
+
const waitForRetryBackoff = async (ms) => {
|
|
19
|
+
if (ms <= 0) {
|
|
20
|
+
throwIfAborted();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
await new Promise((resolve, reject) => {
|
|
24
|
+
const timer = setTimeout(() => {
|
|
25
|
+
params.signal.removeEventListener("abort", onAbort);
|
|
26
|
+
resolve();
|
|
27
|
+
}, ms);
|
|
28
|
+
const onAbort = () => {
|
|
29
|
+
clearTimeout(timer);
|
|
30
|
+
reject(BetterAgentError.fromCode("ABORTED", "Tool execution was aborted.", { trace: [{ at: "core.run.executeToolCalls.retryBackoff" }] }));
|
|
31
|
+
};
|
|
32
|
+
if (params.signal.aborted) {
|
|
33
|
+
onAbort();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
params.signal.addEventListener("abort", onAbort, { once: true });
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
const extractToolErrorMessage = (error, errorKind) => {
|
|
40
|
+
const rawMessage = error instanceof BetterAgentError || error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown tool error";
|
|
41
|
+
if (errorKind === "parse") return `Tool arguments could not be parsed as valid JSON. ${rawMessage}`.trim();
|
|
42
|
+
if (errorKind === "validation") return `Tool arguments failed schema validation. ${rawMessage}`.trim();
|
|
43
|
+
return rawMessage;
|
|
44
|
+
};
|
|
45
|
+
const createToolErrorOutcome = (args) => ({
|
|
46
|
+
result: {
|
|
47
|
+
type: "tool_error",
|
|
48
|
+
toolName: args.toolName,
|
|
49
|
+
errorKind: args.errorKind,
|
|
50
|
+
message: args.message,
|
|
51
|
+
retryable: args.retryable
|
|
52
|
+
},
|
|
53
|
+
isError: true,
|
|
54
|
+
errorKind: args.errorKind
|
|
55
|
+
});
|
|
56
|
+
const createToolErrorOutcomeFromError = (args) => createToolErrorOutcome({
|
|
57
|
+
toolName: args.toolName,
|
|
58
|
+
errorKind: args.errorKind,
|
|
59
|
+
message: args.message ?? extractToolErrorMessage(args.error, args.errorKind),
|
|
60
|
+
retryable: args.retryable ?? (args.error instanceof BetterAgentError ? args.error.retryable : void 0)
|
|
61
|
+
});
|
|
62
|
+
const emitToolCallStart = async (prepared) => {
|
|
63
|
+
await params.emit({
|
|
64
|
+
type: Events.TOOL_CALL_START,
|
|
65
|
+
runId: params.runId,
|
|
66
|
+
agentName: params.agentName,
|
|
67
|
+
parentMessageId: params.parentMessageId,
|
|
68
|
+
toolCallId: prepared.toolCall.callId,
|
|
69
|
+
toolCallName: prepared.toolCall.name,
|
|
70
|
+
toolTarget: prepared.toolTarget,
|
|
71
|
+
timestamp: Date.now()
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
const emitToolCallArgs = async (prepared) => {
|
|
75
|
+
await params.emit({
|
|
76
|
+
type: Events.TOOL_CALL_ARGS,
|
|
77
|
+
runId: params.runId,
|
|
78
|
+
agentName: params.agentName,
|
|
79
|
+
parentMessageId: params.parentMessageId,
|
|
80
|
+
toolCallId: prepared.toolCall.callId,
|
|
81
|
+
toolCallName: prepared.toolCall.name,
|
|
82
|
+
delta: prepared.toolCall.arguments,
|
|
83
|
+
toolTarget: prepared.toolTarget,
|
|
84
|
+
timestamp: Date.now()
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
const emitToolCallEnd = async (prepared) => {
|
|
88
|
+
await params.emit({
|
|
89
|
+
type: Events.TOOL_CALL_END,
|
|
90
|
+
runId: params.runId,
|
|
91
|
+
agentName: params.agentName,
|
|
92
|
+
parentMessageId: params.parentMessageId,
|
|
93
|
+
toolCallId: prepared.toolCall.callId,
|
|
94
|
+
toolCallName: prepared.toolCall.name,
|
|
95
|
+
toolTarget: prepared.toolTarget,
|
|
96
|
+
timestamp: Date.now()
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
const emitToolCallResult = async (prepared, outcome) => {
|
|
100
|
+
await params.emit({
|
|
101
|
+
type: Events.TOOL_CALL_RESULT,
|
|
102
|
+
runId: params.runId,
|
|
103
|
+
agentName: params.agentName,
|
|
104
|
+
parentMessageId: params.parentMessageId,
|
|
105
|
+
toolCallId: prepared.toolCall.callId,
|
|
106
|
+
toolCallName: prepared.toolCall.name,
|
|
107
|
+
result: outcome.result,
|
|
108
|
+
isError: outcome.isError,
|
|
109
|
+
errorKind: outcome.errorKind,
|
|
110
|
+
toolTarget: prepared.toolTarget,
|
|
111
|
+
timestamp: Date.now()
|
|
112
|
+
});
|
|
113
|
+
};
|
|
114
|
+
const resolveExecutableTool = (toolCall) => {
|
|
115
|
+
const tool = params.tools.find((candidate) => getToolRunName(candidate) === toolCall.name);
|
|
116
|
+
if (!tool) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Tool '${toolCall.name}' was requested by the model but is not available for this run.`, {
|
|
117
|
+
context: {
|
|
118
|
+
runId: params.runId,
|
|
119
|
+
agentName: params.agentName,
|
|
120
|
+
toolName: toolCall.name
|
|
121
|
+
},
|
|
122
|
+
trace: [{ at: "core.run.executeToolCalls.missingTool" }]
|
|
123
|
+
});
|
|
124
|
+
if (tool.kind === "hosted") throw BetterAgentError.fromCode("NOT_IMPLEMENTED", `Hosted tool '${getToolRunName(tool) ?? toolCall.name}' reached the in-process tool executor, but hosted tools must be executed by the provider during model invocation.`, {
|
|
125
|
+
context: {
|
|
126
|
+
runId: params.runId,
|
|
127
|
+
agentName: params.agentName,
|
|
128
|
+
toolName: getToolRunName(tool) ?? toolCall.name,
|
|
129
|
+
toolTarget: tool.kind,
|
|
130
|
+
provider: tool.provider,
|
|
131
|
+
toolType: tool.type
|
|
132
|
+
},
|
|
133
|
+
trace: [{ at: "core.run.executeToolCalls.hostedToolInvariant" }]
|
|
134
|
+
});
|
|
135
|
+
return {
|
|
136
|
+
tool,
|
|
137
|
+
toolTarget: tool.kind === "client" ? "client" : "server"
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
const resolveApprovalConfig = async (args) => {
|
|
141
|
+
const approval = args.approval;
|
|
142
|
+
if (!approval) return;
|
|
143
|
+
let resolved = {
|
|
144
|
+
required: approval.required,
|
|
145
|
+
timeoutMs: approval.timeoutMs,
|
|
146
|
+
meta: approval.meta
|
|
147
|
+
};
|
|
148
|
+
if (approval.resolve) {
|
|
149
|
+
const runtimeResolved = await approval.resolve({
|
|
150
|
+
context: params.context,
|
|
151
|
+
input: args.input,
|
|
152
|
+
runId: params.runId,
|
|
153
|
+
toolCallId: args.toolCall.callId,
|
|
154
|
+
toolName: args.tool.name,
|
|
155
|
+
toolTarget: args.toolTarget
|
|
156
|
+
});
|
|
157
|
+
resolved = {
|
|
158
|
+
required: runtimeResolved.required ?? resolved.required,
|
|
159
|
+
timeoutMs: runtimeResolved.timeoutMs ?? resolved.timeoutMs,
|
|
160
|
+
meta: runtimeResolved.meta ?? resolved.meta
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
return resolved;
|
|
164
|
+
};
|
|
165
|
+
const emitApprovalRequested = async (prepared) => {
|
|
166
|
+
await params.emit({
|
|
167
|
+
type: Events.TOOL_APPROVAL_REQUIRED,
|
|
168
|
+
runId: params.runId,
|
|
169
|
+
agentName: params.agentName,
|
|
170
|
+
parentMessageId: params.parentMessageId,
|
|
171
|
+
toolCallId: prepared.toolCall.callId,
|
|
172
|
+
toolCallName: prepared.toolCall.name,
|
|
173
|
+
toolTarget: prepared.toolTarget,
|
|
174
|
+
toolInput: prepared.validatedInput,
|
|
175
|
+
state: "requested",
|
|
176
|
+
timestamp: Date.now(),
|
|
177
|
+
meta: prepared.resolvedApproval?.meta
|
|
178
|
+
});
|
|
179
|
+
await params.emit({
|
|
180
|
+
type: Events.TOOL_APPROVAL_UPDATED,
|
|
181
|
+
runId: params.runId,
|
|
182
|
+
agentName: params.agentName,
|
|
183
|
+
parentMessageId: params.parentMessageId,
|
|
184
|
+
toolCallId: prepared.toolCall.callId,
|
|
185
|
+
toolCallName: prepared.toolCall.name,
|
|
186
|
+
toolTarget: prepared.toolTarget,
|
|
187
|
+
state: "requested",
|
|
188
|
+
toolInput: prepared.validatedInput,
|
|
189
|
+
timestamp: Date.now(),
|
|
190
|
+
meta: prepared.resolvedApproval?.meta
|
|
191
|
+
});
|
|
192
|
+
};
|
|
193
|
+
const awaitApprovalIfNeeded = async (prepared) => {
|
|
194
|
+
if (!prepared.resolvedApproval?.required) return { approved: true };
|
|
195
|
+
if (!params.pendingToolRuntime) throw BetterAgentError.fromCode("NOT_IMPLEMENTED", `Tool '${prepared.tool.name}' requires approval, but no approval runtime is configured.`, {
|
|
196
|
+
context: {
|
|
197
|
+
runId: params.runId,
|
|
198
|
+
toolName: prepared.tool.name,
|
|
199
|
+
toolCallId: prepared.toolCall.callId,
|
|
200
|
+
toolTarget: prepared.toolTarget
|
|
201
|
+
},
|
|
202
|
+
trace: [{ at: "core.run.executeTools.awaitApprovalIfNeeded" }]
|
|
203
|
+
});
|
|
204
|
+
try {
|
|
205
|
+
const decision = await params.pendingToolRuntime.awaitToolApproval({
|
|
206
|
+
runId: params.runId,
|
|
207
|
+
toolCallId: prepared.toolCall.callId,
|
|
208
|
+
toolName: prepared.tool.name,
|
|
209
|
+
timeoutMs: prepared.resolvedApproval.timeoutMs ?? params.advanced?.toolApprovalTimeoutMs,
|
|
210
|
+
signal: params.signal
|
|
211
|
+
});
|
|
212
|
+
await params.emit({
|
|
213
|
+
type: Events.TOOL_APPROVAL_UPDATED,
|
|
214
|
+
runId: params.runId,
|
|
215
|
+
agentName: params.agentName,
|
|
216
|
+
parentMessageId: params.parentMessageId,
|
|
217
|
+
toolCallId: prepared.toolCall.callId,
|
|
218
|
+
toolCallName: prepared.toolCall.name,
|
|
219
|
+
toolTarget: prepared.toolTarget,
|
|
220
|
+
state: decision.decision,
|
|
221
|
+
toolInput: prepared.validatedInput,
|
|
222
|
+
timestamp: Date.now(),
|
|
223
|
+
meta: prepared.resolvedApproval.meta,
|
|
224
|
+
note: decision.note,
|
|
225
|
+
actorId: decision.actorId
|
|
226
|
+
});
|
|
227
|
+
if (decision.decision === "denied") return decision.note !== void 0 ? {
|
|
228
|
+
approved: false,
|
|
229
|
+
note: decision.note
|
|
230
|
+
} : { approved: false };
|
|
231
|
+
return { approved: true };
|
|
232
|
+
} catch (error) {
|
|
233
|
+
if (error instanceof BetterAgentError && error.code === "TIMEOUT") await params.emit({
|
|
234
|
+
type: Events.TOOL_APPROVAL_UPDATED,
|
|
235
|
+
runId: params.runId,
|
|
236
|
+
agentName: params.agentName,
|
|
237
|
+
parentMessageId: params.parentMessageId,
|
|
238
|
+
toolCallId: prepared.toolCall.callId,
|
|
239
|
+
toolCallName: prepared.toolCall.name,
|
|
240
|
+
toolTarget: prepared.toolTarget,
|
|
241
|
+
state: "expired",
|
|
242
|
+
toolInput: prepared.validatedInput,
|
|
243
|
+
timestamp: Date.now(),
|
|
244
|
+
meta: prepared.resolvedApproval.meta
|
|
245
|
+
});
|
|
246
|
+
throw error;
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
for (const toolCall of params.toolCalls) {
|
|
250
|
+
throwIfAborted();
|
|
251
|
+
const { tool, toolTarget } = resolveExecutableTool(toolCall);
|
|
252
|
+
const resolvedToolErrorMode = tool.toolErrorMode ?? params.toolErrorMode ?? "tool_error";
|
|
253
|
+
const defaultToolErrorOutcome = (args) => {
|
|
254
|
+
if (isAbortError(args.error)) throw args.error;
|
|
255
|
+
if (resolvedToolErrorMode === "throw") throw args.error;
|
|
256
|
+
return createToolErrorOutcomeFromError({
|
|
257
|
+
toolName: toolCall.name,
|
|
258
|
+
error: args.error,
|
|
259
|
+
errorKind: args.errorKind,
|
|
260
|
+
retryable: args.retryable
|
|
261
|
+
});
|
|
262
|
+
};
|
|
263
|
+
const runToolHandler = async (input) => {
|
|
264
|
+
if (tool.kind === "server") return await tool.handler(input, {
|
|
265
|
+
signal: params.signal,
|
|
266
|
+
emit: params.emit
|
|
267
|
+
});
|
|
268
|
+
if (!params.pendingToolRuntime) throw BetterAgentError.fromCode("NOT_IMPLEMENTED", `Client tool '${tool.name}' requires a live runtime capable of accepting tool results.`, {
|
|
269
|
+
context: {
|
|
270
|
+
runId: params.runId,
|
|
271
|
+
agentName: params.agentName,
|
|
272
|
+
toolName: tool.name,
|
|
273
|
+
toolCallId: toolCall.callId
|
|
274
|
+
},
|
|
275
|
+
trace: [{ at: "core.run.executeToolCalls.awaitClientToolResult" }]
|
|
276
|
+
});
|
|
277
|
+
return await params.pendingToolRuntime.awaitClientToolResult({
|
|
278
|
+
runId: params.runId,
|
|
279
|
+
toolCallId: toolCall.callId,
|
|
280
|
+
toolName: toolCall.name,
|
|
281
|
+
timeoutMs: params.advanced?.clientToolResultTimeoutMs,
|
|
282
|
+
signal: params.signal
|
|
283
|
+
});
|
|
284
|
+
};
|
|
285
|
+
const resolveHookAction = async (context) => {
|
|
286
|
+
const toolAction = await tool.onToolError?.(context);
|
|
287
|
+
if (toolAction !== void 0) {
|
|
288
|
+
if (toolAction.action !== "skip") return toolAction;
|
|
289
|
+
} else return;
|
|
290
|
+
const agentAction = await params.onToolError?.(context);
|
|
291
|
+
if (agentAction !== void 0 && agentAction.action !== "skip") return agentAction;
|
|
292
|
+
};
|
|
293
|
+
const resolveToolError = async (args) => {
|
|
294
|
+
if (isAbortError(args.error)) throw args.error;
|
|
295
|
+
if (args.recoveryDepth >= MAX_TOOL_ERROR_REPAIR_DEPTH) return defaultToolErrorOutcome({
|
|
296
|
+
error: args.error,
|
|
297
|
+
errorKind: args.errorKind
|
|
298
|
+
});
|
|
299
|
+
const action = await resolveHookAction(args.errorKind === "parse" ? {
|
|
300
|
+
toolName: toolCall.name,
|
|
301
|
+
toolCallId: toolCall.callId,
|
|
302
|
+
error: args.error,
|
|
303
|
+
rawArguments: toolCall.arguments,
|
|
304
|
+
errorKind: "parse"
|
|
305
|
+
} : args.errorKind === "validation" ? {
|
|
306
|
+
toolName: toolCall.name,
|
|
307
|
+
toolCallId: toolCall.callId,
|
|
308
|
+
error: args.error,
|
|
309
|
+
rawArguments: toolCall.arguments,
|
|
310
|
+
input: args.input,
|
|
311
|
+
errorKind: "validation"
|
|
312
|
+
} : {
|
|
313
|
+
toolName: toolCall.name,
|
|
314
|
+
toolCallId: toolCall.callId,
|
|
315
|
+
error: args.error,
|
|
316
|
+
rawArguments: toolCall.arguments,
|
|
317
|
+
input: args.input,
|
|
318
|
+
errorKind: "execution"
|
|
319
|
+
});
|
|
320
|
+
if (action === void 0) return defaultToolErrorOutcome({
|
|
321
|
+
error: args.error,
|
|
322
|
+
errorKind: args.errorKind
|
|
323
|
+
});
|
|
324
|
+
switch (action.action) {
|
|
325
|
+
case "send_to_model": return createToolErrorOutcomeFromError({
|
|
326
|
+
toolName: toolCall.name,
|
|
327
|
+
error: args.error,
|
|
328
|
+
errorKind: args.errorKind,
|
|
329
|
+
message: action.message ?? extractToolErrorMessage(args.error, args.errorKind),
|
|
330
|
+
retryable: action.retryable
|
|
331
|
+
});
|
|
332
|
+
case "throw": throw args.error;
|
|
333
|
+
case "skip": return defaultToolErrorOutcome({
|
|
334
|
+
error: args.error,
|
|
335
|
+
errorKind: args.errorKind
|
|
336
|
+
});
|
|
337
|
+
case "repair": return await validateAndExecuteInput(action.input, args.recoveryDepth + 1);
|
|
338
|
+
case "retry": {
|
|
339
|
+
if (args.errorKind !== "execution" || args.input === void 0) {
|
|
340
|
+
logger.warn(`[better-agent] Ignoring invalid onToolError action '${action.action}' for tool '${toolCall.name}' (${args.errorKind} error).`);
|
|
341
|
+
return createToolErrorOutcomeFromError({
|
|
342
|
+
toolName: toolCall.name,
|
|
343
|
+
error: args.error,
|
|
344
|
+
errorKind: args.errorKind
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
const attempts = Math.max(1, Math.min(action.maxAttempts ?? 1, MAX_TOOL_ERROR_RETRY_ATTEMPTS));
|
|
348
|
+
let lastError = args.error;
|
|
349
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
350
|
+
throwIfAborted();
|
|
351
|
+
try {
|
|
352
|
+
return { result: await runToolHandler(args.input) };
|
|
353
|
+
} catch (retryError) {
|
|
354
|
+
if (isAbortError(retryError)) throw retryError;
|
|
355
|
+
lastError = retryError;
|
|
356
|
+
if (attempt < attempts - 1) await waitForRetryBackoff((attempt + 1) * 500);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return await resolveToolError({
|
|
360
|
+
error: lastError,
|
|
361
|
+
errorKind: "execution",
|
|
362
|
+
input: args.input,
|
|
363
|
+
recoveryDepth: args.recoveryDepth + 1
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
case "result":
|
|
367
|
+
if (args.errorKind !== "execution") {
|
|
368
|
+
logger.warn(`[better-agent] Ignoring invalid onToolError action '${action.action}' for tool '${toolCall.name}' (${args.errorKind} error).`);
|
|
369
|
+
return createToolErrorOutcomeFromError({
|
|
370
|
+
toolName: toolCall.name,
|
|
371
|
+
error: args.error,
|
|
372
|
+
errorKind: args.errorKind
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
return { result: action.value };
|
|
376
|
+
default: return defaultToolErrorOutcome({
|
|
377
|
+
error: args.error,
|
|
378
|
+
errorKind: args.errorKind
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
const executeApprovedInput = async (prepared, input, recoveryDepth) => {
|
|
383
|
+
const approval = await awaitApprovalIfNeeded(prepared);
|
|
384
|
+
if (!approval.approved) {
|
|
385
|
+
const denialReason = approval.note ? ` Reason: ${approval.note}` : "";
|
|
386
|
+
return { result: `Tool '${toolCall.name}' was denied by the operator.${denialReason}` };
|
|
387
|
+
}
|
|
388
|
+
try {
|
|
389
|
+
return { result: await runToolHandler(input) };
|
|
390
|
+
} catch (error) {
|
|
391
|
+
if (isAbortError(error)) throw error;
|
|
392
|
+
return await resolveToolError({
|
|
393
|
+
error,
|
|
394
|
+
errorKind: "execution",
|
|
395
|
+
input,
|
|
396
|
+
recoveryDepth
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
const validateAndExecuteInput = async (input, recoveryDepth) => {
|
|
401
|
+
const validatedInput = await validateInput(tool.schema, input);
|
|
402
|
+
if (validatedInput.isErr()) return await resolveToolError({
|
|
403
|
+
error: validatedInput.error.at({ at: "core.run.executeToolCalls.validateToolInput" }),
|
|
404
|
+
errorKind: "validation",
|
|
405
|
+
input,
|
|
406
|
+
recoveryDepth
|
|
407
|
+
});
|
|
408
|
+
return await executeApprovedInput({
|
|
409
|
+
toolCall,
|
|
410
|
+
tool,
|
|
411
|
+
toolTarget,
|
|
412
|
+
args: input,
|
|
413
|
+
validatedInput: validatedInput.value,
|
|
414
|
+
resolvedApproval: await resolveApprovalConfig({
|
|
415
|
+
approval: tool.approval,
|
|
416
|
+
input: validatedInput.value,
|
|
417
|
+
toolCall,
|
|
418
|
+
tool,
|
|
419
|
+
toolTarget
|
|
420
|
+
}),
|
|
421
|
+
skip: false,
|
|
422
|
+
shouldEmitToolCallEnd: false
|
|
423
|
+
}, validatedInput.value, recoveryDepth);
|
|
424
|
+
};
|
|
425
|
+
const prepareToolCall = async () => {
|
|
426
|
+
const parsed = safeJsonParse(toolCall.arguments);
|
|
427
|
+
if (parsed.isErr()) return {
|
|
428
|
+
toolCall,
|
|
429
|
+
tool,
|
|
430
|
+
toolTarget,
|
|
431
|
+
skip: false,
|
|
432
|
+
shouldEmitToolCallEnd: false,
|
|
433
|
+
parseError: BetterAgentError.wrap({
|
|
434
|
+
err: parsed.error,
|
|
435
|
+
message: `Failed to parse arguments for tool '${toolCall.name}'`,
|
|
436
|
+
opts: {
|
|
437
|
+
code: "VALIDATION_FAILED",
|
|
438
|
+
context: {
|
|
439
|
+
toolName: toolCall.name,
|
|
440
|
+
toolCallId: toolCall.callId
|
|
441
|
+
},
|
|
442
|
+
trace: [{ at: "core.run.executeToolCalls.parseToolArguments" }]
|
|
443
|
+
}
|
|
444
|
+
})
|
|
445
|
+
};
|
|
446
|
+
const beforeHook = params.pluginRuntime?.hasToolHooks === true ? await params.pluginRuntime.applyBeforeToolCall({
|
|
447
|
+
runId: params.runId,
|
|
448
|
+
agentName: params.agentName,
|
|
449
|
+
toolName: toolCall.name,
|
|
450
|
+
toolCallId: toolCall.callId,
|
|
451
|
+
args: parsed.value,
|
|
452
|
+
conversationId: params.conversationId
|
|
453
|
+
}) : { args: parsed.value };
|
|
454
|
+
if (beforeHook?.decision?.skip === true) return {
|
|
455
|
+
toolCall,
|
|
456
|
+
tool,
|
|
457
|
+
toolTarget,
|
|
458
|
+
args: beforeHook.args,
|
|
459
|
+
skip: true,
|
|
460
|
+
skipResult: beforeHook.decision.result,
|
|
461
|
+
shouldEmitToolCallEnd: true
|
|
462
|
+
};
|
|
463
|
+
const validatedInput = await validateInput(tool.schema, beforeHook.args);
|
|
464
|
+
if (validatedInput.isErr()) return {
|
|
465
|
+
toolCall,
|
|
466
|
+
tool,
|
|
467
|
+
toolTarget,
|
|
468
|
+
args: beforeHook.args,
|
|
469
|
+
skip: false,
|
|
470
|
+
shouldEmitToolCallEnd: false,
|
|
471
|
+
validationError: validatedInput.error.at({ at: "core.run.executeToolCalls.validateToolInput" })
|
|
472
|
+
};
|
|
473
|
+
const prepared = {
|
|
474
|
+
toolCall,
|
|
475
|
+
tool,
|
|
476
|
+
toolTarget,
|
|
477
|
+
args: beforeHook.args,
|
|
478
|
+
validatedInput: validatedInput.value,
|
|
479
|
+
resolvedApproval: await resolveApprovalConfig({
|
|
480
|
+
approval: tool.approval,
|
|
481
|
+
input: validatedInput.value,
|
|
482
|
+
toolCall,
|
|
483
|
+
tool,
|
|
484
|
+
toolTarget
|
|
485
|
+
}),
|
|
486
|
+
skip: false,
|
|
487
|
+
shouldEmitToolCallEnd: true
|
|
488
|
+
};
|
|
489
|
+
if (prepared.resolvedApproval?.required) await emitApprovalRequested(prepared);
|
|
490
|
+
return prepared;
|
|
491
|
+
};
|
|
492
|
+
const executePreparedTool = async (prepared) => {
|
|
493
|
+
if (prepared.parseError) return await resolveToolError({
|
|
494
|
+
error: prepared.parseError,
|
|
495
|
+
errorKind: "parse",
|
|
496
|
+
recoveryDepth: 0
|
|
497
|
+
});
|
|
498
|
+
if (prepared.skip) return { result: prepared.skipResult };
|
|
499
|
+
if (prepared.validationError) return await resolveToolError({
|
|
500
|
+
error: prepared.validationError,
|
|
501
|
+
errorKind: "validation",
|
|
502
|
+
input: prepared.args,
|
|
503
|
+
recoveryDepth: 0
|
|
504
|
+
});
|
|
505
|
+
return await executeApprovedInput(prepared, prepared.validatedInput, 0);
|
|
506
|
+
};
|
|
507
|
+
const applyAfterToolCall = async (prepared, outcome) => {
|
|
508
|
+
if (params.pluginRuntime?.hasToolHooks !== true) return outcome;
|
|
509
|
+
const afterHook = await params.pluginRuntime.applyAfterToolCall({
|
|
510
|
+
runId: params.runId,
|
|
511
|
+
agentName: params.agentName,
|
|
512
|
+
toolName: toolCall.name,
|
|
513
|
+
toolCallId: toolCall.callId,
|
|
514
|
+
args: prepared.args,
|
|
515
|
+
result: outcome.result,
|
|
516
|
+
conversationId: params.conversationId,
|
|
517
|
+
error: outcome.isError === true && typeof outcome.result === "object" && outcome.result !== null && "message" in outcome.result && typeof outcome.result.message === "string" ? outcome.result.message : void 0
|
|
518
|
+
});
|
|
519
|
+
return {
|
|
520
|
+
...outcome,
|
|
521
|
+
result: afterHook.result
|
|
522
|
+
};
|
|
523
|
+
};
|
|
524
|
+
await emitToolCallStart({
|
|
525
|
+
toolCall,
|
|
526
|
+
toolTarget
|
|
527
|
+
});
|
|
528
|
+
await emitToolCallArgs({
|
|
529
|
+
toolCall,
|
|
530
|
+
toolTarget
|
|
531
|
+
});
|
|
532
|
+
const prepared = await prepareToolCall();
|
|
533
|
+
if (prepared.shouldEmitToolCallEnd) await emitToolCallEnd(prepared);
|
|
534
|
+
const finalOutcome = await applyAfterToolCall(prepared, await executePreparedTool(prepared));
|
|
535
|
+
await emitToolCallResult(prepared, finalOutcome);
|
|
536
|
+
results.push({
|
|
537
|
+
type: "tool-call",
|
|
538
|
+
callId: toolCall.callId,
|
|
539
|
+
name: toolCall.name,
|
|
540
|
+
arguments: toolCall.arguments,
|
|
541
|
+
result: finalOutcome.result,
|
|
542
|
+
isError: finalOutcome.isError
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
return { results };
|
|
546
|
+
};
|
|
547
|
+
|
|
548
|
+
//#endregion
|
|
549
|
+
export { executeToolCalls };
|
|
550
|
+
//# sourceMappingURL=execute-tool-calls.mjs.map
|