@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,399 @@
1
+ import { defineRoute } from "../api.mjs";
2
+ import { invalidRequest, jsonErrorResponse, jsonResponse, noContentResponse, parseAfterFromRequest, requestsEventStream, toSseResponse, toValidationIssue } from "./http.mjs";
3
+ import { BetterAgentError } from "@better-agent/shared/errors";
4
+ import Ajv from "ajv";
5
+ import { isPlainRecord, safeJsonParse } from "@better-agent/shared/utils";
6
+
7
+ //#region src/server/routes.ts
8
+ const ajv = new Ajv({
9
+ allErrors: true,
10
+ allowUnionTypes: true
11
+ });
12
+ const runSchema = {
13
+ type: "object",
14
+ required: ["input"],
15
+ properties: {
16
+ input: { oneOf: [
17
+ { type: "string" },
18
+ { type: "array" },
19
+ { type: "object" }
20
+ ] },
21
+ context: true,
22
+ output: {
23
+ type: "object",
24
+ additionalProperties: true,
25
+ properties: {
26
+ schema: { type: "object" },
27
+ name: { type: "string" },
28
+ strict: { type: "boolean" }
29
+ },
30
+ required: ["schema"]
31
+ },
32
+ modalities: {
33
+ type: "array",
34
+ minItems: 1,
35
+ items: { enum: [
36
+ "text",
37
+ "image",
38
+ "video",
39
+ "audio",
40
+ "file",
41
+ "embedding"
42
+ ] }
43
+ },
44
+ modelOptions: { type: "object" },
45
+ conversationId: {
46
+ type: "string",
47
+ pattern: "\\S"
48
+ },
49
+ conversationReplay: { anyOf: [{
50
+ type: "object",
51
+ additionalProperties: false,
52
+ properties: { omitUnsupportedParts: { type: "boolean" } }
53
+ }, {
54
+ type: "object",
55
+ additionalProperties: false,
56
+ properties: {
57
+ omitUnsupportedParts: { type: "boolean" },
58
+ prepareInput: { type: "null" }
59
+ }
60
+ }] },
61
+ replaceHistory: { type: "boolean" },
62
+ maxSteps: {
63
+ type: "integer",
64
+ minimum: 1
65
+ },
66
+ advanced: {
67
+ type: "object",
68
+ additionalProperties: false,
69
+ properties: {
70
+ clientToolResultTimeoutMs: {
71
+ type: "number",
72
+ exclusiveMinimum: 0
73
+ },
74
+ toolApprovalTimeoutMs: {
75
+ type: "number",
76
+ exclusiveMinimum: 0
77
+ }
78
+ }
79
+ }
80
+ },
81
+ additionalProperties: true
82
+ };
83
+ const toolResultSchema = {
84
+ type: "object",
85
+ required: [
86
+ "runId",
87
+ "toolCallId",
88
+ "status"
89
+ ],
90
+ properties: {
91
+ runId: {
92
+ type: "string",
93
+ pattern: "\\S"
94
+ },
95
+ toolCallId: {
96
+ type: "string",
97
+ pattern: "\\S"
98
+ },
99
+ status: { enum: ["success", "error"] },
100
+ result: true,
101
+ error: { type: "string" }
102
+ },
103
+ additionalProperties: true,
104
+ allOf: [{
105
+ if: { properties: { status: { const: "success" } } },
106
+ then: {
107
+ required: ["result"],
108
+ not: { required: ["error"] }
109
+ }
110
+ }, {
111
+ if: { properties: { status: { const: "error" } } },
112
+ then: {
113
+ required: ["error"],
114
+ not: { required: ["result"] }
115
+ }
116
+ }]
117
+ };
118
+ const toolApprovalSchema = {
119
+ type: "object",
120
+ required: [
121
+ "runId",
122
+ "toolCallId",
123
+ "decision"
124
+ ],
125
+ properties: {
126
+ runId: {
127
+ type: "string",
128
+ pattern: "\\S"
129
+ },
130
+ toolCallId: {
131
+ type: "string",
132
+ pattern: "\\S"
133
+ },
134
+ decision: { enum: ["approved", "denied"] },
135
+ note: { type: "string" },
136
+ actorId: { type: "string" }
137
+ },
138
+ additionalProperties: true
139
+ };
140
+ const validateRunRequestBody = ajv.compile(runSchema);
141
+ const validateToolResultBody = ajv.compile(toolResultSchema);
142
+ const validateToolApprovalBody = ajv.compile(toolApprovalSchema);
143
+ const isFiniteNumber = (value) => typeof value === "number" && Number.isFinite(value);
144
+ const isStoredStreamEvent = (event) => {
145
+ if (!isPlainRecord(event)) return false;
146
+ return typeof event.type === "string" && isFiniteNumber(event.timestamp) && isFiniteNumber(event.seq);
147
+ };
148
+ /**
149
+ * Creates the built-in server route table.
150
+ */
151
+ const createServerRoutes = (deps) => {
152
+ const routes = [
153
+ defineRoute({
154
+ method: "POST",
155
+ pattern: "/:name/run",
156
+ async handler(ctx) {
157
+ if (!(ctx.request.headers.get("content-type") ?? "").toLowerCase().includes("application/json")) return new Response(JSON.stringify({
158
+ code: "UNSUPPORTED_MEDIA_TYPE",
159
+ message: "Content-Type must include application/json.",
160
+ status: 415
161
+ }), {
162
+ status: 415,
163
+ headers: { "content-type": "application/json" }
164
+ });
165
+ const text = await ctx.request.text();
166
+ if (text.length === 0) return invalidRequest([toValidationIssue("Request body must be valid JSON.")]);
167
+ const parsed = safeJsonParse(text);
168
+ if (parsed.isErr() || !isPlainRecord(parsed.value)) return invalidRequest([toValidationIssue("Request body must be a JSON object.")]);
169
+ if (!validateRunRequestBody(parsed.value)) return invalidRequest(validateRunRequestBody.errors?.map((error) => toValidationIssue(error.message ?? "Request validation failed.", error.instancePath || "/")) ?? []);
170
+ const runOptions = {
171
+ input: parsed.value.input,
172
+ signal: ctx.request.signal,
173
+ ...parsed.value.context !== void 0 ? { context: parsed.value.context } : {},
174
+ ...parsed.value.output !== void 0 ? { output: parsed.value.output } : {},
175
+ ...parsed.value.modalities !== void 0 ? { modalities: parsed.value.modalities } : {},
176
+ ...parsed.value.modelOptions !== void 0 ? { modelOptions: parsed.value.modelOptions } : {},
177
+ ...parsed.value.conversationId !== void 0 ? { conversationId: parsed.value.conversationId } : {},
178
+ ...parsed.value.conversationReplay !== void 0 ? { conversationReplay: parsed.value.conversationReplay } : {},
179
+ ...parsed.value.replaceHistory !== void 0 ? { replaceHistory: parsed.value.replaceHistory } : {},
180
+ ...parsed.value.maxSteps !== void 0 ? { maxSteps: parsed.value.maxSteps } : {},
181
+ ...parsed.value.advanced !== void 0 ? { advanced: parsed.value.advanced } : {}
182
+ };
183
+ if (!requestsEventStream(ctx.request)) return jsonResponse(await deps.runtime.run(ctx.params.name, runOptions));
184
+ const streamed = deps.runtime.stream(ctx.params.name, runOptions);
185
+ streamed.result.catch(() => {});
186
+ return toSseResponse({
187
+ events: streamed.events,
188
+ runId: streamed.runId,
189
+ streamId: streamed.runId,
190
+ signal: ctx.request.signal,
191
+ heartbeatMs: deps.sseHeartbeatMs,
192
+ onDisconnect: deps.onRequestDisconnect === "continue" ? void 0 : async () => {
193
+ await deps.runtime.abortRun(streamed.runId);
194
+ }
195
+ });
196
+ }
197
+ }),
198
+ defineRoute({
199
+ method: "GET",
200
+ pattern: "/:name/conversations/:id",
201
+ async handler(ctx) {
202
+ const rawId = ctx.params.id;
203
+ const id = typeof rawId === "string" ? (() => {
204
+ try {
205
+ return decodeURIComponent(rawId);
206
+ } catch {
207
+ return rawId;
208
+ }
209
+ })() : rawId;
210
+ if (!id || id.trim().length === 0) return invalidRequest([toValidationIssue("Conversation id is required.", "/id")]);
211
+ if (!deps.runtime.loadConversation) return jsonErrorResponse({
212
+ code: "NOT_IMPLEMENTED",
213
+ message: "Conversation loading is not implemented by this runtime.",
214
+ status: 501
215
+ });
216
+ const result = await deps.runtime.loadConversation(ctx.params.name, id);
217
+ if (!result) return noContentResponse();
218
+ return jsonResponse(result);
219
+ }
220
+ }),
221
+ defineRoute({
222
+ method: "GET",
223
+ pattern: "/:name/stream-events/resume",
224
+ async handler(ctx) {
225
+ const streamId = ctx.query.get("streamId");
226
+ if (!streamId) return invalidRequest([toValidationIssue("streamId is required.", "/streamId")]);
227
+ const events = await deps.runtime.resumeStream({
228
+ streamId,
229
+ afterSeq: parseAfterFromRequest(ctx.request)
230
+ });
231
+ if (!events) return noContentResponse();
232
+ return toSseResponse({
233
+ events: (async function* () {
234
+ for await (const event of events) {
235
+ if (!isStoredStreamEvent(event)) throw BetterAgentError.fromCode("VALIDATION_FAILED", "Stored stream event is invalid.", {
236
+ context: { streamId },
237
+ trace: [{ at: "core.server.resumeStreamEvents.validateEvent" }]
238
+ });
239
+ yield event;
240
+ }
241
+ })(),
242
+ streamId,
243
+ useEventIds: true,
244
+ signal: ctx.request.signal,
245
+ heartbeatMs: deps.sseHeartbeatMs
246
+ });
247
+ }
248
+ }),
249
+ defineRoute({
250
+ method: "GET",
251
+ pattern: "/:name/conversations/:id/resume",
252
+ async handler(ctx) {
253
+ const rawId = ctx.params.id;
254
+ const id = typeof rawId === "string" ? (() => {
255
+ try {
256
+ return decodeURIComponent(rawId);
257
+ } catch {
258
+ return rawId;
259
+ }
260
+ })() : rawId;
261
+ if (!id || id.trim().length === 0) return invalidRequest([toValidationIssue("Conversation id is required.", "/id")]);
262
+ const events = await deps.runtime.resumeConversation(ctx.params.name, {
263
+ conversationId: id,
264
+ afterSeq: parseAfterFromRequest(ctx.request)
265
+ });
266
+ if (!events) return noContentResponse();
267
+ return toSseResponse({
268
+ events: (async function* () {
269
+ for await (const event of events) {
270
+ if (!isStoredStreamEvent(event)) throw BetterAgentError.fromCode("VALIDATION_FAILED", "Stored stream event is invalid.", {
271
+ context: { streamId: id },
272
+ trace: [{ at: "core.server.resumeStreamEvents.validateEvent" }]
273
+ });
274
+ yield event;
275
+ }
276
+ })(),
277
+ signal: ctx.request.signal,
278
+ useEventIds: true,
279
+ heartbeatMs: deps.sseHeartbeatMs
280
+ });
281
+ }
282
+ }),
283
+ defineRoute({
284
+ method: "POST",
285
+ pattern: "/:name/runs/:runId/abort",
286
+ async handler(ctx) {
287
+ const runId = ctx.params.runId;
288
+ if (!runId || runId.trim().length === 0) return invalidRequest([toValidationIssue("runId is required.", "/runId")]);
289
+ await deps.runtime.abortRun(runId);
290
+ return noContentResponse();
291
+ }
292
+ }),
293
+ defineRoute({
294
+ method: "POST",
295
+ pattern: "/:name/run/tool-result",
296
+ async handler(ctx) {
297
+ if (!(ctx.request.headers.get("content-type") ?? "").toLowerCase().includes("application/json")) return new Response(JSON.stringify({
298
+ code: "UNSUPPORTED_MEDIA_TYPE",
299
+ message: "Content-Type must include application/json.",
300
+ status: 415
301
+ }), {
302
+ status: 415,
303
+ headers: { "content-type": "application/json" }
304
+ });
305
+ const text = await ctx.request.text();
306
+ if (text.length === 0) return invalidRequest([toValidationIssue("Request body must be valid JSON.")]);
307
+ const parsed = safeJsonParse(text);
308
+ if (parsed.isErr() || !isPlainRecord(parsed.value)) return invalidRequest([toValidationIssue("Request body must be a JSON object.")]);
309
+ if (!validateToolResultBody(parsed.value)) return invalidRequest(validateToolResultBody.errors?.map((error) => toValidationIssue(error.message ?? "Request validation failed.", error.instancePath || "/")) ?? []);
310
+ if (!await deps.runtime.submitToolResult(parsed.value)) return jsonErrorResponse({
311
+ code: "NOT_FOUND",
312
+ message: "No pending client tool call found for runId/toolCallId.",
313
+ status: 404
314
+ });
315
+ return noContentResponse();
316
+ }
317
+ }),
318
+ defineRoute({
319
+ method: "POST",
320
+ pattern: "/:name/run/tool-approval",
321
+ async handler(ctx) {
322
+ if (!(ctx.request.headers.get("content-type") ?? "").toLowerCase().includes("application/json")) return new Response(JSON.stringify({
323
+ code: "UNSUPPORTED_MEDIA_TYPE",
324
+ message: "Content-Type must include application/json.",
325
+ status: 415
326
+ }), {
327
+ status: 415,
328
+ headers: { "content-type": "application/json" }
329
+ });
330
+ const text = await ctx.request.text();
331
+ if (text.length === 0) return invalidRequest([toValidationIssue("Request body must be valid JSON.")]);
332
+ const parsed = safeJsonParse(text);
333
+ if (parsed.isErr() || !isPlainRecord(parsed.value)) return invalidRequest([toValidationIssue("Request body must be a JSON object.")]);
334
+ if (!validateToolApprovalBody(parsed.value)) return invalidRequest(validateToolApprovalBody.errors?.map((error) => toValidationIssue(error.message ?? "Request validation failed.", error.instancePath || "/")) ?? []);
335
+ if (!await deps.runtime.submitToolApproval(parsed.value)) return jsonErrorResponse({
336
+ code: "NOT_FOUND",
337
+ message: "No pending tool approval found for runId/toolCallId.",
338
+ status: 404
339
+ });
340
+ return noContentResponse();
341
+ }
342
+ })
343
+ ];
344
+ const seenRouteKeys = new Set(routes.map((route) => `${route.method} ${route.pattern}`));
345
+ for (const endpoint of deps.pluginRuntime?.endpoints ?? []) {
346
+ const routeKey = `${endpoint.method} ${endpoint.path}`;
347
+ if (seenRouteKeys.has(routeKey)) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Plugin endpoint '${routeKey}' conflicts with an existing server route.`, {
348
+ context: {
349
+ pluginId: endpoint.pluginId,
350
+ method: endpoint.method,
351
+ path: endpoint.path
352
+ },
353
+ trace: [{ at: "core.server.createServer.pluginEndpointConflict" }]
354
+ });
355
+ seenRouteKeys.add(routeKey);
356
+ routes.push(defineRoute({
357
+ method: endpoint.method,
358
+ pattern: endpoint.path,
359
+ ...endpoint.public === true ? { public: true } : {},
360
+ async handler(ctx) {
361
+ return endpoint.handler({
362
+ request: ctx.request,
363
+ params: ctx.params,
364
+ query: ctx.query
365
+ });
366
+ }
367
+ }));
368
+ }
369
+ return routes;
370
+ };
371
+ /**
372
+ * Runs plugin guards for one matched built-in route.
373
+ */
374
+ const runPluginGuards = async (params) => {
375
+ const { pluginRuntime, match, request } = params;
376
+ if (!pluginRuntime?.hasGuards) return null;
377
+ const guardMode = match.route.pattern === "/:name/conversations/:id" ? "load_conversation" : match.route.pattern === "/:name/stream-events/resume" ? "resume_stream" : match.route.pattern === "/:name/conversations/:id/resume" ? "resume_conversation" : match.route.pattern === "/:name/runs/:runId/abort" ? "abort_run" : match.route.pattern === "/:name/run" ? requestsEventStream(request) ? "stream" : "run" : null;
378
+ if (!guardMode || typeof match.params.name !== "string") return null;
379
+ let input = {};
380
+ const contentType = request.headers.get("content-type") ?? "";
381
+ if (request.method === "POST" && contentType.toLowerCase().includes("application/json")) {
382
+ const text = await request.clone().text();
383
+ if (text.length > 0) {
384
+ const parsed = safeJsonParse(text);
385
+ if (!parsed.isErr() && isPlainRecord(parsed.value)) input = parsed.value;
386
+ }
387
+ }
388
+ return pluginRuntime.dispatchRun({
389
+ mode: guardMode,
390
+ agentName: match.params.name,
391
+ input,
392
+ request,
393
+ plugins: pluginRuntime.plugins
394
+ });
395
+ };
396
+
397
+ //#endregion
398
+ export { createServerRoutes, runPluginGuards };
399
+ //# sourceMappingURL=routes.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes.mjs","names":[],"sources":["../../src/server/routes.ts"],"sourcesContent":["import { BetterAgentError } from \"@better-agent/shared/errors\";\nimport { isPlainRecord, safeJsonParse } from \"@better-agent/shared/utils\";\nimport Ajv from \"ajv\";\nimport { type RouteEntry, defineRoute } from \"../api\";\nimport type { StreamEvent } from \"../persistence\";\nimport type { PluginGuardMode, PluginRuntime } from \"../plugins\";\nimport type { Modality } from \"../providers\";\nimport type { BetterAgentRuntime } from \"../run\";\nimport {\n invalidRequest,\n jsonErrorResponse,\n jsonResponse,\n noContentResponse,\n parseAfterFromRequest,\n requestsEventStream,\n toSseResponse,\n toValidationIssue,\n} from \"./http\";\nimport type { ToolApprovalBody, ToolResultBody } from \"./types\";\n\ninterface ServerRouteDeps {\n runtime: BetterAgentRuntime;\n pluginRuntime: PluginRuntime | null;\n sseHeartbeatMs?: number;\n onRequestDisconnect?: \"abort\" | \"continue\";\n}\n\nconst ajv = new Ajv({ allErrors: true, allowUnionTypes: true });\nconst modalities: Modality[] = [\"text\", \"image\", \"video\", \"audio\", \"file\", \"embedding\"];\n\nconst runSchema = {\n type: \"object\",\n required: [\"input\"],\n properties: {\n input: { oneOf: [{ type: \"string\" }, { type: \"array\" }, { type: \"object\" }] },\n context: true,\n output: {\n type: \"object\",\n additionalProperties: true,\n properties: {\n schema: { type: \"object\" },\n name: { type: \"string\" },\n strict: { type: \"boolean\" },\n },\n required: [\"schema\"],\n },\n modalities: {\n type: \"array\",\n minItems: 1,\n items: { enum: modalities },\n },\n modelOptions: { type: \"object\" },\n conversationId: { type: \"string\", pattern: \"\\\\S\" },\n conversationReplay: {\n anyOf: [\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n omitUnsupportedParts: { type: \"boolean\" },\n },\n },\n {\n type: \"object\",\n additionalProperties: false,\n properties: {\n omitUnsupportedParts: { type: \"boolean\" },\n prepareInput: { type: \"null\" },\n },\n },\n ],\n },\n replaceHistory: { type: \"boolean\" },\n maxSteps: { type: \"integer\", minimum: 1 },\n advanced: {\n type: \"object\",\n additionalProperties: false,\n properties: {\n clientToolResultTimeoutMs: { type: \"number\", exclusiveMinimum: 0 },\n toolApprovalTimeoutMs: { type: \"number\", exclusiveMinimum: 0 },\n },\n },\n },\n additionalProperties: true,\n} as const;\n\nconst toolResultSchema = {\n type: \"object\",\n required: [\"runId\", \"toolCallId\", \"status\"],\n properties: {\n runId: { type: \"string\", pattern: \"\\\\S\" },\n toolCallId: { type: \"string\", pattern: \"\\\\S\" },\n status: { enum: [\"success\", \"error\"] },\n result: true,\n error: { type: \"string\" },\n },\n additionalProperties: true,\n allOf: [\n {\n if: { properties: { status: { const: \"success\" } } },\n // biome-ignore lint/suspicious/noThenProperty: JSON Schema conditional keyword\n then: {\n required: [\"result\"],\n not: { required: [\"error\"] },\n },\n },\n {\n if: { properties: { status: { const: \"error\" } } },\n // biome-ignore lint/suspicious/noThenProperty: JSON Schema conditional keyword\n then: {\n required: [\"error\"],\n not: { required: [\"result\"] },\n },\n },\n ],\n} as const;\n\nconst toolApprovalSchema = {\n type: \"object\",\n required: [\"runId\", \"toolCallId\", \"decision\"],\n properties: {\n runId: { type: \"string\", pattern: \"\\\\S\" },\n toolCallId: { type: \"string\", pattern: \"\\\\S\" },\n decision: { enum: [\"approved\", \"denied\"] },\n note: { type: \"string\" },\n actorId: { type: \"string\" },\n },\n additionalProperties: true,\n} as const;\n\nconst validateRunRequestBody = ajv.compile(runSchema);\nconst validateToolResultBody = ajv.compile(toolResultSchema);\nconst validateToolApprovalBody = ajv.compile(toolApprovalSchema);\n\nconst isFiniteNumber = (value: unknown): value is number =>\n typeof value === \"number\" && Number.isFinite(value);\n\n// Guard persisted events before re-emitting them as SSE payloads.\nconst isStoredStreamEvent = (event: unknown): event is StreamEvent => {\n if (!isPlainRecord(event)) {\n return false;\n }\n\n return (\n typeof event.type === \"string\" &&\n isFiniteNumber(event.timestamp) &&\n isFiniteNumber(event.seq)\n );\n};\n\n/**\n * Creates the built-in server route table.\n */\nexport const createServerRoutes = (deps: ServerRouteDeps): RouteEntry[] => {\n const routes: RouteEntry[] = [\n defineRoute({\n method: \"POST\",\n pattern: \"/:name/run\",\n async handler(ctx) {\n const contentType = ctx.request.headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"application/json\")) {\n return new Response(\n JSON.stringify({\n code: \"UNSUPPORTED_MEDIA_TYPE\",\n message: \"Content-Type must include application/json.\",\n status: 415,\n }),\n {\n status: 415,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n }\n\n const text = await ctx.request.text();\n if (text.length === 0) {\n return invalidRequest([toValidationIssue(\"Request body must be valid JSON.\")]);\n }\n\n const parsed = safeJsonParse(text);\n if (parsed.isErr() || !isPlainRecord(parsed.value)) {\n return invalidRequest([\n toValidationIssue(\"Request body must be a JSON object.\"),\n ]);\n }\n\n if (!validateRunRequestBody(parsed.value)) {\n return invalidRequest(\n validateRunRequestBody.errors?.map((error) =>\n toValidationIssue(\n error.message ?? \"Request validation failed.\",\n error.instancePath || \"/\",\n ),\n ) ?? [],\n );\n }\n\n const runOptions = {\n input: parsed.value.input,\n signal: ctx.request.signal,\n ...(parsed.value.context !== undefined\n ? { context: parsed.value.context }\n : {}),\n ...(parsed.value.output !== undefined ? { output: parsed.value.output } : {}),\n ...(parsed.value.modalities !== undefined\n ? { modalities: parsed.value.modalities }\n : {}),\n ...(parsed.value.modelOptions !== undefined\n ? { modelOptions: parsed.value.modelOptions }\n : {}),\n ...(parsed.value.conversationId !== undefined\n ? { conversationId: parsed.value.conversationId }\n : {}),\n ...(parsed.value.conversationReplay !== undefined\n ? { conversationReplay: parsed.value.conversationReplay }\n : {}),\n ...(parsed.value.replaceHistory !== undefined\n ? { replaceHistory: parsed.value.replaceHistory }\n : {}),\n ...(parsed.value.maxSteps !== undefined\n ? { maxSteps: parsed.value.maxSteps }\n : {}),\n ...(parsed.value.advanced !== undefined\n ? { advanced: parsed.value.advanced }\n : {}),\n };\n if (!requestsEventStream(ctx.request)) {\n return jsonResponse(\n await deps.runtime.run(ctx.params.name, runOptions as never),\n );\n }\n\n const streamed = deps.runtime.stream(ctx.params.name, runOptions as never);\n void streamed.result.catch(() => {});\n return toSseResponse({\n events: streamed.events,\n runId: streamed.runId,\n streamId: streamed.runId,\n signal: ctx.request.signal,\n heartbeatMs: deps.sseHeartbeatMs,\n onDisconnect:\n deps.onRequestDisconnect === \"continue\"\n ? undefined\n : async () => {\n await deps.runtime.abortRun(streamed.runId);\n },\n });\n },\n }),\n defineRoute({\n method: \"GET\",\n pattern: \"/:name/conversations/:id\",\n async handler(ctx) {\n const rawId = ctx.params.id;\n const id =\n typeof rawId === \"string\"\n ? (() => {\n try {\n return decodeURIComponent(rawId);\n } catch {\n return rawId;\n }\n })()\n : rawId;\n if (!id || id.trim().length === 0) {\n return invalidRequest([\n toValidationIssue(\"Conversation id is required.\", \"/id\"),\n ]);\n }\n if (!deps.runtime.loadConversation) {\n return jsonErrorResponse({\n code: \"NOT_IMPLEMENTED\",\n message: \"Conversation loading is not implemented by this runtime.\",\n status: 501,\n });\n }\n\n const result = await deps.runtime.loadConversation(ctx.params.name, id);\n if (!result) {\n return noContentResponse();\n }\n\n return jsonResponse(result);\n },\n }),\n defineRoute({\n method: \"GET\",\n pattern: \"/:name/stream-events/resume\",\n async handler(ctx) {\n const streamId = ctx.query.get(\"streamId\");\n if (!streamId) {\n return invalidRequest([\n toValidationIssue(\"streamId is required.\", \"/streamId\"),\n ]);\n }\n\n const events = await deps.runtime.resumeStream({\n streamId,\n afterSeq: parseAfterFromRequest(ctx.request),\n });\n if (!events) {\n return noContentResponse();\n }\n\n return toSseResponse({\n events: (async function* () {\n for await (const event of events) {\n if (!isStoredStreamEvent(event)) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n \"Stored stream event is invalid.\",\n {\n context: { streamId },\n trace: [\n {\n at: \"core.server.resumeStreamEvents.validateEvent\",\n },\n ],\n },\n );\n }\n\n yield event as StreamEvent;\n }\n })(),\n streamId,\n useEventIds: true,\n signal: ctx.request.signal,\n heartbeatMs: deps.sseHeartbeatMs,\n });\n },\n }),\n defineRoute({\n method: \"GET\",\n pattern: \"/:name/conversations/:id/resume\",\n async handler(ctx) {\n const rawId = ctx.params.id;\n const id =\n typeof rawId === \"string\"\n ? (() => {\n try {\n return decodeURIComponent(rawId);\n } catch {\n return rawId;\n }\n })()\n : rawId;\n if (!id || id.trim().length === 0) {\n return invalidRequest([\n toValidationIssue(\"Conversation id is required.\", \"/id\"),\n ]);\n }\n\n const events = await deps.runtime.resumeConversation(ctx.params.name, {\n conversationId: id,\n afterSeq: parseAfterFromRequest(ctx.request),\n });\n if (!events) {\n return noContentResponse();\n }\n\n return toSseResponse({\n events: (async function* () {\n for await (const event of events) {\n if (!isStoredStreamEvent(event)) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n \"Stored stream event is invalid.\",\n {\n context: { streamId: id },\n trace: [\n {\n at: \"core.server.resumeStreamEvents.validateEvent\",\n },\n ],\n },\n );\n }\n\n yield event as StreamEvent;\n }\n })(),\n signal: ctx.request.signal,\n useEventIds: true,\n heartbeatMs: deps.sseHeartbeatMs,\n });\n },\n }),\n defineRoute({\n method: \"POST\",\n pattern: \"/:name/runs/:runId/abort\",\n async handler(ctx) {\n const runId = ctx.params.runId;\n if (!runId || runId.trim().length === 0) {\n return invalidRequest([toValidationIssue(\"runId is required.\", \"/runId\")]);\n }\n\n await deps.runtime.abortRun(runId);\n return noContentResponse();\n },\n }),\n defineRoute({\n method: \"POST\",\n pattern: \"/:name/run/tool-result\",\n async handler(ctx) {\n const contentType = ctx.request.headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"application/json\")) {\n return new Response(\n JSON.stringify({\n code: \"UNSUPPORTED_MEDIA_TYPE\",\n message: \"Content-Type must include application/json.\",\n status: 415,\n }),\n {\n status: 415,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n }\n\n const text = await ctx.request.text();\n if (text.length === 0) {\n return invalidRequest([toValidationIssue(\"Request body must be valid JSON.\")]);\n }\n\n const parsed = safeJsonParse(text);\n if (parsed.isErr() || !isPlainRecord(parsed.value)) {\n return invalidRequest([\n toValidationIssue(\"Request body must be a JSON object.\"),\n ]);\n }\n\n if (!validateToolResultBody(parsed.value)) {\n return invalidRequest(\n validateToolResultBody.errors?.map((error) =>\n toValidationIssue(\n error.message ?? \"Request validation failed.\",\n error.instancePath || \"/\",\n ),\n ) ?? [],\n );\n }\n\n const accepted = await deps.runtime.submitToolResult(\n parsed.value as ToolResultBody,\n );\n if (!accepted) {\n return jsonErrorResponse({\n code: \"NOT_FOUND\",\n message: \"No pending client tool call found for runId/toolCallId.\",\n status: 404,\n });\n }\n\n return noContentResponse();\n },\n }),\n defineRoute({\n method: \"POST\",\n pattern: \"/:name/run/tool-approval\",\n async handler(ctx) {\n const contentType = ctx.request.headers.get(\"content-type\") ?? \"\";\n if (!contentType.toLowerCase().includes(\"application/json\")) {\n return new Response(\n JSON.stringify({\n code: \"UNSUPPORTED_MEDIA_TYPE\",\n message: \"Content-Type must include application/json.\",\n status: 415,\n }),\n {\n status: 415,\n headers: { \"content-type\": \"application/json\" },\n },\n );\n }\n\n const text = await ctx.request.text();\n if (text.length === 0) {\n return invalidRequest([toValidationIssue(\"Request body must be valid JSON.\")]);\n }\n\n const parsed = safeJsonParse(text);\n if (parsed.isErr() || !isPlainRecord(parsed.value)) {\n return invalidRequest([\n toValidationIssue(\"Request body must be a JSON object.\"),\n ]);\n }\n\n if (!validateToolApprovalBody(parsed.value)) {\n return invalidRequest(\n validateToolApprovalBody.errors?.map((error) =>\n toValidationIssue(\n error.message ?? \"Request validation failed.\",\n error.instancePath || \"/\",\n ),\n ) ?? [],\n );\n }\n\n const accepted = await deps.runtime.submitToolApproval(\n parsed.value as ToolApprovalBody,\n );\n if (!accepted) {\n return jsonErrorResponse({\n code: \"NOT_FOUND\",\n message: \"No pending tool approval found for runId/toolCallId.\",\n status: 404,\n });\n }\n\n return noContentResponse();\n },\n }),\n ];\n\n const seenRouteKeys = new Set(routes.map((route) => `${route.method} ${route.pattern}`));\n\n for (const endpoint of deps.pluginRuntime?.endpoints ?? []) {\n const routeKey = `${endpoint.method} ${endpoint.path}`;\n if (seenRouteKeys.has(routeKey)) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n `Plugin endpoint '${routeKey}' conflicts with an existing server route.`,\n {\n context: {\n pluginId: endpoint.pluginId,\n method: endpoint.method,\n path: endpoint.path,\n },\n trace: [{ at: \"core.server.createServer.pluginEndpointConflict\" }],\n },\n );\n }\n\n seenRouteKeys.add(routeKey);\n routes.push(\n defineRoute({\n method: endpoint.method,\n pattern: endpoint.path,\n ...(endpoint.public === true ? { public: true } : {}),\n async handler(ctx) {\n return endpoint.handler({\n request: ctx.request,\n params: ctx.params,\n query: ctx.query,\n });\n },\n }),\n );\n }\n\n return routes;\n};\n\n/**\n * Runs plugin guards for one matched built-in route.\n */\nexport const runPluginGuards = async (params: {\n pluginRuntime: PluginRuntime | null;\n match: { route: RouteEntry; params: Record<string, string> };\n request: Request;\n}): Promise<Response | null> => {\n const { pluginRuntime, match, request } = params;\n if (!pluginRuntime?.hasGuards) {\n return null;\n }\n\n const guardMode: PluginGuardMode | null =\n match.route.pattern === \"/:name/conversations/:id\"\n ? \"load_conversation\"\n : match.route.pattern === \"/:name/stream-events/resume\"\n ? \"resume_stream\"\n : match.route.pattern === \"/:name/conversations/:id/resume\"\n ? \"resume_conversation\"\n : match.route.pattern === \"/:name/runs/:runId/abort\"\n ? \"abort_run\"\n : match.route.pattern === \"/:name/run\"\n ? requestsEventStream(request)\n ? \"stream\"\n : \"run\"\n : null;\n\n if (!guardMode || typeof match.params.name !== \"string\") {\n return null;\n }\n\n let input: Record<string, unknown> = {};\n const contentType = request.headers.get(\"content-type\") ?? \"\";\n const shouldParseJsonBody =\n request.method === \"POST\" && contentType.toLowerCase().includes(\"application/json\");\n\n if (shouldParseJsonBody) {\n const text = await request.clone().text();\n if (text.length > 0) {\n const parsed = safeJsonParse(text);\n if (!parsed.isErr() && isPlainRecord(parsed.value)) {\n input = parsed.value;\n }\n }\n }\n\n return pluginRuntime.dispatchRun({\n mode: guardMode,\n agentName: match.params.name,\n input,\n request,\n plugins: pluginRuntime.plugins,\n });\n};\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { PluginRuntime } from "../plugins/types.mjs";
2
+ import { BetterAgentRuntime } from "../run/types.mjs";
3
+ //#region src/server/types.d.ts
4
+ /**
5
+ * Server configuration.
6
+ */
7
+ interface CreateServerConfig {
8
+ /** Runtime used to execute agent operations. */
9
+ runtime: BetterAgentRuntime;
10
+ /** Optional plugin runtime used for guards and plugin routes. */
11
+ pluginRuntime?: PluginRuntime | null;
12
+ /** Optional bearer token required for non-public routes. */
13
+ secret?: string;
14
+ /** Base path for all routes, for example `"/api"`. */
15
+ basePath?: string;
16
+ /** Advanced server controls. */
17
+ advanced?: {
18
+ /** Heartbeat interval for SSE responses. */sseHeartbeatMs?: number; /** How streaming runs react when the client disconnects. */
19
+ onRequestDisconnect?: "abort" | "continue";
20
+ };
21
+ }
22
+ /**
23
+ * Built-in server instance.
24
+ */
25
+ interface BetterAgentServer {
26
+ /** Handles one incoming HTTP request. */
27
+ handle(request: Request): Promise<Response>;
28
+ }
29
+ //#endregion
30
+ export { BetterAgentServer, CreateServerConfig };
31
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/server/types.ts"],"mappings":";;;;;;UAaiB,kBAAA;EAAA;EAEb,OAAA,EAAS,kBAAA;;EAET,aAAA,GAAgB,aAAA;EAFhB;EAIA,MAAA;EAFA;EAIA,QAAA;EAFA;EAIA,QAAA;IAAA,4CAEI,cAAA,WAEA;IAAA,mBAAA;EAAA;AAAA;;;;UAOS,iBAAA;EAEa;EAA1B,MAAA,CAAO,OAAA,EAAS,OAAA,GAAU,OAAA,CAAQ,QAAA;AAAA"}
@@ -0,0 +1,12 @@
1
+ //#region src/tools/constants.d.ts
2
+ /**
3
+ * Symbol used to attach an optional per-run cleanup hook to a tool.
4
+ */
5
+ declare const TOOL_CLEANUP: unique symbol;
6
+ /**
7
+ * Symbol used to store the resolved JSON Schema for a tool.
8
+ */
9
+ declare const TOOL_JSON_SCHEMA: unique symbol;
10
+ //#endregion
11
+ export { TOOL_CLEANUP, TOOL_JSON_SCHEMA };
12
+ //# sourceMappingURL=constants.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.mts","names":[],"sources":["../../src/tools/constants.ts"],"mappings":";;AAGA;;cAAa,YAAA;;;AAKb;cAAa,gBAAA"}
@@ -0,0 +1,13 @@
1
+ //#region src/tools/constants.ts
2
+ /**
3
+ * Symbol used to attach an optional per-run cleanup hook to a tool.
4
+ */
5
+ const TOOL_CLEANUP = Symbol.for("better-agent.tool.cleanup");
6
+ /**
7
+ * Symbol used to store the resolved JSON Schema for a tool.
8
+ */
9
+ const TOOL_JSON_SCHEMA = Symbol.for("better-agent.tool.json_schema");
10
+
11
+ //#endregion
12
+ export { TOOL_CLEANUP, TOOL_JSON_SCHEMA };
13
+ //# sourceMappingURL=constants.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.mjs","names":[],"sources":["../../src/tools/constants.ts"],"sourcesContent":["/**\n * Symbol used to attach an optional per-run cleanup hook to a tool.\n */\nexport const TOOL_CLEANUP = Symbol.for(\"better-agent.tool.cleanup\");\n\n/**\n * Symbol used to store the resolved JSON Schema for a tool.\n */\nexport const TOOL_JSON_SCHEMA = Symbol.for(\"better-agent.tool.json_schema\");\n"],"mappings":";;;;AAGA,MAAa,eAAe,OAAO,IAAI,4BAA4B;;;;AAKnE,MAAa,mBAAmB,OAAO,IAAI,gCAAgC"}
@@ -0,0 +1,25 @@
1
+ import { ResolvableSchema } from "../schema/types.mjs";
2
+ import { ToolContract, ToolContractConfig } from "./types.mjs";
3
+
4
+ //#region src/tools/define-tool.d.ts
5
+ /**
6
+ * Defines a tool contract.
7
+ *
8
+ * Call `.server(...)` for server execution or `.client()` for client-handled tools.
9
+ *
10
+ * @param def Tool definition.
11
+ * @returns A tool contract.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const getWeather = defineTool({
16
+ * name: "get_weather",
17
+ * description: "Get current weather",
18
+ * schema: { type: "object", properties: { city: { type: "string" } } },
19
+ * }).server(async ({ city }) => ({ city, temperature: 26 }));
20
+ * ```
21
+ */
22
+ declare const defineTool: <const N extends string, const S extends ResolvableSchema, TContext = any>(def: ToolContractConfig<S, N, TContext>) => ToolContract<S, N, TContext>;
23
+ //#endregion
24
+ export { defineTool };
25
+ //# sourceMappingURL=define-tool.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-tool.d.mts","names":[],"sources":["../../src/tools/define-tool.ts"],"mappings":";;;;;;;AAsBA;;;;;;;;;;;;;;cAAa,UAAA,2CAEO,gBAAA,kBAER,GAAA,EAEH,kBAAA,CAAmB,CAAA,EAAG,CAAA,EAAG,QAAA,MAC/B,YAAA,CAAa,CAAA,EAAG,CAAA,EAAG,QAAA"}
@@ -0,0 +1,76 @@
1
+ import { TOOL_CLEANUP, TOOL_JSON_SCHEMA } from "./constants.mjs";
2
+ import { validateToolDefinition } from "./validation.mjs";
3
+
4
+ //#region src/tools/define-tool.ts
5
+ /**
6
+ * Defines a tool contract.
7
+ *
8
+ * Call `.server(...)` for server execution or `.client()` for client-handled tools.
9
+ *
10
+ * @param def Tool definition.
11
+ * @returns A tool contract.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * const getWeather = defineTool({
16
+ * name: "get_weather",
17
+ * description: "Get current weather",
18
+ * schema: { type: "object", properties: { city: { type: "string" } } },
19
+ * }).server(async ({ city }) => ({ city, temperature: 26 }));
20
+ * ```
21
+ */
22
+ const defineTool = (def) => {
23
+ const resolvedSchema = validateToolDefinition(def);
24
+ const name = def.name;
25
+ const description = def.description;
26
+ const schema = def.schema;
27
+ const strict = def.strict;
28
+ const approval = def.approval;
29
+ const toolErrorMode = def.toolErrorMode;
30
+ const onToolError = def.onToolError;
31
+ const cleanup = def[TOOL_CLEANUP];
32
+ return {
33
+ name,
34
+ description,
35
+ schema,
36
+ [TOOL_JSON_SCHEMA]: resolvedSchema,
37
+ server: ((handler, options) => {
38
+ return {
39
+ name: options?.as ?? name,
40
+ kind: "server",
41
+ description,
42
+ schema,
43
+ strict,
44
+ approval,
45
+ toolErrorMode,
46
+ onToolError,
47
+ handler,
48
+ [TOOL_CLEANUP]: cleanup,
49
+ [TOOL_JSON_SCHEMA]: resolvedSchema
50
+ };
51
+ }),
52
+ client: ((options) => {
53
+ return {
54
+ name: options?.as ?? name,
55
+ kind: "client",
56
+ description,
57
+ schema,
58
+ strict,
59
+ approval,
60
+ toolErrorMode,
61
+ onToolError,
62
+ [TOOL_CLEANUP]: cleanup,
63
+ [TOOL_JSON_SCHEMA]: resolvedSchema
64
+ };
65
+ }),
66
+ strict,
67
+ approval,
68
+ toolErrorMode,
69
+ onToolError,
70
+ [TOOL_CLEANUP]: cleanup
71
+ };
72
+ };
73
+
74
+ //#endregion
75
+ export { defineTool };
76
+ //# sourceMappingURL=define-tool.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-tool.mjs","names":[],"sources":["../../src/tools/define-tool.ts"],"sourcesContent":["import type { ResolvableSchema } from \"../schema\";\nimport { TOOL_CLEANUP, TOOL_JSON_SCHEMA } from \"./constants\";\nimport type { ToolContract, ToolContractConfig } from \"./types\";\nimport { validateToolDefinition } from \"./validation\";\n\n/**\n * Defines a tool contract.\n *\n * Call `.server(...)` for server execution or `.client()` for client-handled tools.\n *\n * @param def Tool definition.\n * @returns A tool contract.\n *\n * @example\n * ```ts\n * const getWeather = defineTool({\n * name: \"get_weather\",\n * description: \"Get current weather\",\n * schema: { type: \"object\", properties: { city: { type: \"string\" } } },\n * }).server(async ({ city }) => ({ city, temperature: 26 }));\n * ```\n */\nexport const defineTool = <\n const N extends string,\n const S extends ResolvableSchema,\n // biome-ignore lint/suspicious/noExplicitAny: reusable tools default to loose run context unless explicitly narrowed\n TContext = any,\n>(\n def: ToolContractConfig<S, N, TContext>,\n): ToolContract<S, N, TContext> => {\n // Resolve once at definition time so server/client variants share the same schema payload.\n const resolvedSchema = validateToolDefinition(def);\n const name = def.name;\n const description = def.description;\n const schema = def.schema;\n const strict = def.strict;\n const approval = def.approval;\n const toolErrorMode = def.toolErrorMode;\n const onToolError = def.onToolError;\n const cleanup = def[TOOL_CLEANUP];\n\n return {\n name,\n description,\n schema,\n [TOOL_JSON_SCHEMA]: resolvedSchema,\n server: ((handler, options) => {\n const toolName = options?.as ?? name;\n return {\n name: toolName,\n kind: \"server\" as const,\n description,\n schema,\n strict,\n approval,\n toolErrorMode,\n onToolError,\n handler,\n [TOOL_CLEANUP]: cleanup,\n [TOOL_JSON_SCHEMA]: resolvedSchema,\n };\n }) as ToolContract<S, N, TContext>[\"server\"],\n client: ((options) => {\n const toolName = options?.as ?? name;\n return {\n name: toolName,\n kind: \"client\" as const,\n description,\n schema,\n strict,\n approval,\n toolErrorMode,\n onToolError,\n [TOOL_CLEANUP]: cleanup,\n [TOOL_JSON_SCHEMA]: resolvedSchema,\n };\n }) as ToolContract<S, N, TContext>[\"client\"],\n strict,\n approval,\n toolErrorMode,\n onToolError,\n [TOOL_CLEANUP]: cleanup,\n };\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA,MAAa,cAMT,QAC+B;CAE/B,MAAM,iBAAiB,uBAAuB,IAAI;CAClD,MAAM,OAAO,IAAI;CACjB,MAAM,cAAc,IAAI;CACxB,MAAM,SAAS,IAAI;CACnB,MAAM,SAAS,IAAI;CACnB,MAAM,WAAW,IAAI;CACrB,MAAM,gBAAgB,IAAI;CAC1B,MAAM,cAAc,IAAI;CACxB,MAAM,UAAU,IAAI;AAEpB,QAAO;EACH;EACA;EACA;GACC,mBAAmB;EACpB,UAAU,SAAS,YAAY;AAE3B,UAAO;IACH,MAFa,SAAS,MAAM;IAG5B,MAAM;IACN;IACA;IACA;IACA;IACA;IACA;IACA;KACC,eAAe;KACf,mBAAmB;IACvB;;EAEL,UAAU,YAAY;AAElB,UAAO;IACH,MAFa,SAAS,MAAM;IAG5B,MAAM;IACN;IACA;IACA;IACA;IACA;IACA;KACC,eAAe;KACf,mBAAmB;IACvB;;EAEL;EACA;EACA;EACA;GACC,eAAe;EACnB"}