@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.
Files changed (179) hide show
  1. package/README.md +3 -0
  2. package/dist/agent/constants.mjs +6 -0
  3. package/dist/agent/constants.mjs.map +1 -0
  4. package/dist/agent/define-agent.d.mts +29 -0
  5. package/dist/agent/define-agent.d.mts.map +1 -0
  6. package/dist/agent/define-agent.mjs +27 -0
  7. package/dist/agent/define-agent.mjs.map +1 -0
  8. package/dist/agent/index.d.mts +2 -0
  9. package/dist/agent/types.d.mts +216 -0
  10. package/dist/agent/types.d.mts.map +1 -0
  11. package/dist/agent/validation.mjs +64 -0
  12. package/dist/agent/validation.mjs.map +1 -0
  13. package/dist/api.d.mts +8 -0
  14. package/dist/api.d.mts.map +1 -0
  15. package/dist/api.mjs +63 -0
  16. package/dist/api.mjs.map +1 -0
  17. package/dist/app/config.mjs +43 -0
  18. package/dist/app/config.mjs.map +1 -0
  19. package/dist/app/create-app.d.mts +36 -0
  20. package/dist/app/create-app.d.mts.map +1 -0
  21. package/dist/app/create-app.mjs +132 -0
  22. package/dist/app/create-app.mjs.map +1 -0
  23. package/dist/app/registry.mjs +43 -0
  24. package/dist/app/registry.mjs.map +1 -0
  25. package/dist/app/types.d.mts +142 -0
  26. package/dist/app/types.d.mts.map +1 -0
  27. package/dist/events/constants.d.mts +49 -0
  28. package/dist/events/constants.d.mts.map +1 -0
  29. package/dist/events/constants.mjs +46 -0
  30. package/dist/events/constants.mjs.map +1 -0
  31. package/dist/events/index.d.mts +4 -0
  32. package/dist/events/index.mjs +3 -0
  33. package/dist/events/types.d.mts +289 -0
  34. package/dist/events/types.d.mts.map +1 -0
  35. package/dist/index.d.mts +23 -0
  36. package/dist/index.mjs +14 -0
  37. package/dist/internal/id.mjs +21 -0
  38. package/dist/internal/id.mjs.map +1 -0
  39. package/dist/internal/types.d.mts +11 -0
  40. package/dist/internal/types.d.mts.map +1 -0
  41. package/dist/mcp/error/mcp-client-error.d.mts +36 -0
  42. package/dist/mcp/error/mcp-client-error.d.mts.map +1 -0
  43. package/dist/mcp/error/mcp-client-error.mjs +33 -0
  44. package/dist/mcp/error/mcp-client-error.mjs.map +1 -0
  45. package/dist/mcp/index.d.mts +8 -0
  46. package/dist/mcp/index.mjs +9 -0
  47. package/dist/mcp/tool/json-rpc-message.d.mts +50 -0
  48. package/dist/mcp/tool/json-rpc-message.d.mts.map +1 -0
  49. package/dist/mcp/tool/json-rpc-message.mjs +84 -0
  50. package/dist/mcp/tool/json-rpc-message.mjs.map +1 -0
  51. package/dist/mcp/tool/mcp-client.d.mts +71 -0
  52. package/dist/mcp/tool/mcp-client.d.mts.map +1 -0
  53. package/dist/mcp/tool/mcp-client.mjs +304 -0
  54. package/dist/mcp/tool/mcp-client.mjs.map +1 -0
  55. package/dist/mcp/tool/mcp-http-transport.d.mts +62 -0
  56. package/dist/mcp/tool/mcp-http-transport.d.mts.map +1 -0
  57. package/dist/mcp/tool/mcp-http-transport.mjs +307 -0
  58. package/dist/mcp/tool/mcp-http-transport.mjs.map +1 -0
  59. package/dist/mcp/tool/mcp-tools.d.mts +20 -0
  60. package/dist/mcp/tool/mcp-tools.d.mts.map +1 -0
  61. package/dist/mcp/tool/mcp-tools.mjs +73 -0
  62. package/dist/mcp/tool/mcp-tools.mjs.map +1 -0
  63. package/dist/mcp/tool/mcp-transport.d.mts +81 -0
  64. package/dist/mcp/tool/mcp-transport.d.mts.map +1 -0
  65. package/dist/mcp/tool/mcp-transport.mjs +11 -0
  66. package/dist/mcp/tool/mcp-transport.mjs.map +1 -0
  67. package/dist/mcp/tool/types.d.mts +230 -0
  68. package/dist/mcp/tool/types.d.mts.map +1 -0
  69. package/dist/mcp/tool/types.mjs +19 -0
  70. package/dist/mcp/tool/types.mjs.map +1 -0
  71. package/dist/persistence/index.d.mts +3 -0
  72. package/dist/persistence/index.mjs +3 -0
  73. package/dist/persistence/memory.d.mts +21 -0
  74. package/dist/persistence/memory.d.mts.map +1 -0
  75. package/dist/persistence/memory.mjs +107 -0
  76. package/dist/persistence/memory.mjs.map +1 -0
  77. package/dist/persistence/types.d.mts +124 -0
  78. package/dist/persistence/types.d.mts.map +1 -0
  79. package/dist/plugins/index.d.mts +2 -0
  80. package/dist/plugins/runtime.d.mts +17 -0
  81. package/dist/plugins/runtime.d.mts.map +1 -0
  82. package/dist/plugins/runtime.mjs +456 -0
  83. package/dist/plugins/runtime.mjs.map +1 -0
  84. package/dist/plugins/types.d.mts +344 -0
  85. package/dist/plugins/types.d.mts.map +1 -0
  86. package/dist/providers/index.d.mts +9 -0
  87. package/dist/providers/index.mjs +0 -0
  88. package/dist/providers/types/capabilities.d.mts +153 -0
  89. package/dist/providers/types/capabilities.d.mts.map +1 -0
  90. package/dist/providers/types/content.d.mts +125 -0
  91. package/dist/providers/types/content.d.mts.map +1 -0
  92. package/dist/providers/types/conversation.d.mts +32 -0
  93. package/dist/providers/types/conversation.d.mts.map +1 -0
  94. package/dist/providers/types/index.d.mts +8 -0
  95. package/dist/providers/types/input.d.mts +74 -0
  96. package/dist/providers/types/input.d.mts.map +1 -0
  97. package/dist/providers/types/model.d.mts +68 -0
  98. package/dist/providers/types/model.d.mts.map +1 -0
  99. package/dist/providers/types/output.d.mts +29 -0
  100. package/dist/providers/types/output.d.mts.map +1 -0
  101. package/dist/providers/types/response.d.mts +35 -0
  102. package/dist/providers/types/response.d.mts.map +1 -0
  103. package/dist/providers/types/tool-calls.d.mts +51 -0
  104. package/dist/providers/types/tool-calls.d.mts.map +1 -0
  105. package/dist/run/agent-loop.mjs +231 -0
  106. package/dist/run/agent-loop.mjs.map +1 -0
  107. package/dist/run/event-queue.mjs +67 -0
  108. package/dist/run/event-queue.mjs.map +1 -0
  109. package/dist/run/execute-tool-calls.mjs +550 -0
  110. package/dist/run/execute-tool-calls.mjs.map +1 -0
  111. package/dist/run/execution.mjs +93 -0
  112. package/dist/run/execution.mjs.map +1 -0
  113. package/dist/run/helpers.mjs +466 -0
  114. package/dist/run/helpers.mjs.map +1 -0
  115. package/dist/run/hooks.mjs +124 -0
  116. package/dist/run/hooks.mjs.map +1 -0
  117. package/dist/run/index.d.mts +4 -0
  118. package/dist/run/messages.d.mts +8 -0
  119. package/dist/run/messages.d.mts.map +1 -0
  120. package/dist/run/messages.mjs +83 -0
  121. package/dist/run/messages.mjs.map +1 -0
  122. package/dist/run/model-strategy.mjs +105 -0
  123. package/dist/run/model-strategy.mjs.map +1 -0
  124. package/dist/run/output-errors.d.mts +75 -0
  125. package/dist/run/output-errors.d.mts.map +1 -0
  126. package/dist/run/pending-tools.d.mts +1 -0
  127. package/dist/run/pending-tools.mjs +185 -0
  128. package/dist/run/pending-tools.mjs.map +1 -0
  129. package/dist/run/registry.mjs +22 -0
  130. package/dist/run/registry.mjs.map +1 -0
  131. package/dist/run/runtime.d.mts +19 -0
  132. package/dist/run/runtime.d.mts.map +1 -0
  133. package/dist/run/runtime.mjs +491 -0
  134. package/dist/run/runtime.mjs.map +1 -0
  135. package/dist/run/stop-conditions.mjs +41 -0
  136. package/dist/run/stop-conditions.mjs.map +1 -0
  137. package/dist/run/types.d.mts +348 -0
  138. package/dist/run/types.d.mts.map +1 -0
  139. package/dist/schema/index.d.mts +2 -0
  140. package/dist/schema/resolve-json-schema.d.mts +12 -0
  141. package/dist/schema/resolve-json-schema.d.mts.map +1 -0
  142. package/dist/schema/resolve-json-schema.mjs +167 -0
  143. package/dist/schema/resolve-json-schema.mjs.map +1 -0
  144. package/dist/schema/types.d.mts +27 -0
  145. package/dist/schema/types.d.mts.map +1 -0
  146. package/dist/server/create-server.d.mts +21 -0
  147. package/dist/server/create-server.d.mts.map +1 -0
  148. package/dist/server/create-server.mjs +107 -0
  149. package/dist/server/create-server.mjs.map +1 -0
  150. package/dist/server/http.mjs +182 -0
  151. package/dist/server/http.mjs.map +1 -0
  152. package/dist/server/index.d.mts +3 -0
  153. package/dist/server/index.mjs +3 -0
  154. package/dist/server/routes.mjs +399 -0
  155. package/dist/server/routes.mjs.map +1 -0
  156. package/dist/server/types.d.mts +31 -0
  157. package/dist/server/types.d.mts.map +1 -0
  158. package/dist/tools/constants.d.mts +12 -0
  159. package/dist/tools/constants.d.mts.map +1 -0
  160. package/dist/tools/constants.mjs +13 -0
  161. package/dist/tools/constants.mjs.map +1 -0
  162. package/dist/tools/define-tool.d.mts +25 -0
  163. package/dist/tools/define-tool.d.mts.map +1 -0
  164. package/dist/tools/define-tool.mjs +76 -0
  165. package/dist/tools/define-tool.mjs.map +1 -0
  166. package/dist/tools/index.d.mts +5 -0
  167. package/dist/tools/lazy-tools.d.mts +49 -0
  168. package/dist/tools/lazy-tools.d.mts.map +1 -0
  169. package/dist/tools/lazy-tools.mjs +87 -0
  170. package/dist/tools/lazy-tools.mjs.map +1 -0
  171. package/dist/tools/resolve-tools.d.mts +12 -0
  172. package/dist/tools/resolve-tools.d.mts.map +1 -0
  173. package/dist/tools/resolve-tools.mjs +86 -0
  174. package/dist/tools/resolve-tools.mjs.map +1 -0
  175. package/dist/tools/types.d.mts +318 -0
  176. package/dist/tools/types.d.mts.map +1 -0
  177. package/dist/tools/validation.mjs +23 -0
  178. package/dist/tools/validation.mjs.map +1 -0
  179. 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