@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,456 @@
|
|
|
1
|
+
import { resolveToolsForRun } from "../tools/resolve-tools.mjs";
|
|
2
|
+
import { BetterAgentError } from "@better-agent/shared/errors";
|
|
3
|
+
import { logger } from "@better-agent/shared/logger";
|
|
4
|
+
|
|
5
|
+
//#region src/plugins/runtime.ts
|
|
6
|
+
/**
|
|
7
|
+
* Defines a plugin.
|
|
8
|
+
*
|
|
9
|
+
* Plugin hook contexts are intentionally broad at authoring time. Some
|
|
10
|
+
* capability-sensitive controls may be omitted at runtime for models that do
|
|
11
|
+
* not support them.
|
|
12
|
+
*
|
|
13
|
+
* @param config Plugin configuration.
|
|
14
|
+
* @returns The plugin definition with preserved literal types.
|
|
15
|
+
*/
|
|
16
|
+
function definePlugin(config) {
|
|
17
|
+
return config;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Builds validated plugin runtime metadata for the runner and server.
|
|
21
|
+
*/
|
|
22
|
+
function createPluginRuntime(plugins) {
|
|
23
|
+
const source = plugins ?? [];
|
|
24
|
+
const seenPluginIds = /* @__PURE__ */ new Set();
|
|
25
|
+
const seenEndpoints = /* @__PURE__ */ new Set();
|
|
26
|
+
const eventMiddlewares = [];
|
|
27
|
+
const guards = [];
|
|
28
|
+
const endpoints = [];
|
|
29
|
+
const onEventHandlers = [];
|
|
30
|
+
const toolSources = [];
|
|
31
|
+
const onStepHooks = [];
|
|
32
|
+
const beforeModelHooks = [];
|
|
33
|
+
const afterModelHooks = [];
|
|
34
|
+
const beforeToolHooks = [];
|
|
35
|
+
const afterToolHooks = [];
|
|
36
|
+
const beforeSaveHooks = [];
|
|
37
|
+
for (const plugin of source) {
|
|
38
|
+
const pluginId = plugin.id.trim();
|
|
39
|
+
if (pluginId.length === 0) throw BetterAgentError.fromCode("VALIDATION_FAILED", "Plugin id must be a non-empty string.", { trace: [{ at: "core.plugins.createPluginRuntime" }] });
|
|
40
|
+
if (seenPluginIds.has(pluginId)) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Duplicate plugin id '${pluginId}'.`, {
|
|
41
|
+
context: { pluginId },
|
|
42
|
+
trace: [{ at: "core.plugins.createPluginRuntime" }]
|
|
43
|
+
});
|
|
44
|
+
seenPluginIds.add(pluginId);
|
|
45
|
+
if (plugin.events?.middleware) for (const middleware of plugin.events.middleware) eventMiddlewares.push({
|
|
46
|
+
pluginId,
|
|
47
|
+
middleware,
|
|
48
|
+
...plugin.events.subscribe !== void 0 ? { subscribe: plugin.events.subscribe } : {}
|
|
49
|
+
});
|
|
50
|
+
if (plugin.guards) for (const guard of plugin.guards) guards.push({
|
|
51
|
+
pluginId,
|
|
52
|
+
guard
|
|
53
|
+
});
|
|
54
|
+
for (const endpoint of plugin.endpoints ?? []) validateAndExpandEndpoint(endpoint, pluginId, seenEndpoints, endpoints);
|
|
55
|
+
if (plugin.onEvent) onEventHandlers.push(plugin.onEvent);
|
|
56
|
+
if (plugin.tools) toolSources.push(plugin.tools);
|
|
57
|
+
if (plugin.onStep) onStepHooks.push({
|
|
58
|
+
pluginId,
|
|
59
|
+
hook: plugin.onStep
|
|
60
|
+
});
|
|
61
|
+
if (plugin.onBeforeModelCall) beforeModelHooks.push({
|
|
62
|
+
pluginId,
|
|
63
|
+
hook: plugin.onBeforeModelCall
|
|
64
|
+
});
|
|
65
|
+
if (plugin.onAfterModelCall) afterModelHooks.push({
|
|
66
|
+
pluginId,
|
|
67
|
+
hook: plugin.onAfterModelCall
|
|
68
|
+
});
|
|
69
|
+
if (plugin.onBeforeToolCall) beforeToolHooks.push({
|
|
70
|
+
pluginId,
|
|
71
|
+
hook: plugin.onBeforeToolCall
|
|
72
|
+
});
|
|
73
|
+
if (plugin.onAfterToolCall) afterToolHooks.push({
|
|
74
|
+
pluginId,
|
|
75
|
+
hook: plugin.onAfterToolCall
|
|
76
|
+
});
|
|
77
|
+
if (plugin.onBeforeSave) beforeSaveHooks.push({
|
|
78
|
+
pluginId,
|
|
79
|
+
hook: plugin.onBeforeSave
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
const reportPluginRuntimeError = (params) => {
|
|
83
|
+
logger.error(`[better-agent] Plugin '${params.pluginId}' failed during ${params.phase}.`, params.error);
|
|
84
|
+
};
|
|
85
|
+
return {
|
|
86
|
+
plugins: source,
|
|
87
|
+
endpoints,
|
|
88
|
+
hasEventMiddleware: eventMiddlewares.length > 0,
|
|
89
|
+
hasOnEvent: onEventHandlers.length > 0,
|
|
90
|
+
hasGuards: guards.length > 0,
|
|
91
|
+
hasTools: toolSources.length > 0,
|
|
92
|
+
hasOnStep: onStepHooks.length > 0,
|
|
93
|
+
hasModelHooks: beforeModelHooks.length > 0 || afterModelHooks.length > 0,
|
|
94
|
+
hasToolHooks: beforeToolHooks.length > 0 || afterToolHooks.length > 0,
|
|
95
|
+
hasOnBeforeSave: beforeSaveHooks.length > 0,
|
|
96
|
+
async dispatchEvent(event, ctx) {
|
|
97
|
+
const run = async (index, current) => {
|
|
98
|
+
for (let i = index; i < eventMiddlewares.length; i++) {
|
|
99
|
+
const item = eventMiddlewares[i];
|
|
100
|
+
if (!item) continue;
|
|
101
|
+
if (item.subscribe && !item.subscribe.includes(current.type)) continue;
|
|
102
|
+
try {
|
|
103
|
+
return await item.middleware(current, ctx, async (nextEvent) => {
|
|
104
|
+
return run(i + 1, nextEvent);
|
|
105
|
+
});
|
|
106
|
+
} catch (error) {
|
|
107
|
+
reportPluginRuntimeError({
|
|
108
|
+
pluginId: item.pluginId,
|
|
109
|
+
phase: "event_middleware",
|
|
110
|
+
error
|
|
111
|
+
});
|
|
112
|
+
return run(i + 1, current);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return current;
|
|
116
|
+
};
|
|
117
|
+
return run(0, event);
|
|
118
|
+
},
|
|
119
|
+
async dispatchOnEvent(event, ctx) {
|
|
120
|
+
for (let i = 0; i < source.length; i += 1) {
|
|
121
|
+
const plugin = source[i];
|
|
122
|
+
if (!plugin?.onEvent) continue;
|
|
123
|
+
try {
|
|
124
|
+
await plugin.onEvent(event, ctx);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
reportPluginRuntimeError({
|
|
127
|
+
pluginId: plugin.id,
|
|
128
|
+
phase: "on_event",
|
|
129
|
+
error
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
async dispatchRun(ctx) {
|
|
135
|
+
for (const entry of guards) try {
|
|
136
|
+
const response = await entry.guard(ctx);
|
|
137
|
+
if (response) return response;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
reportPluginRuntimeError({
|
|
140
|
+
pluginId: entry.pluginId,
|
|
141
|
+
phase: "guard",
|
|
142
|
+
error
|
|
143
|
+
});
|
|
144
|
+
throw BetterAgentError.wrap({
|
|
145
|
+
err: error,
|
|
146
|
+
message: `Plugin '${entry.pluginId}' guard failed.`,
|
|
147
|
+
opts: {
|
|
148
|
+
code: "INTERNAL",
|
|
149
|
+
context: {
|
|
150
|
+
pluginId: entry.pluginId,
|
|
151
|
+
mode: ctx.mode,
|
|
152
|
+
agentName: ctx.agentName
|
|
153
|
+
},
|
|
154
|
+
trace: [{ at: "core.plugins.createPluginRuntime.dispatchRun" }]
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
return null;
|
|
159
|
+
},
|
|
160
|
+
async resolveTools(context) {
|
|
161
|
+
if (toolSources.length === 0) return {
|
|
162
|
+
tools: [],
|
|
163
|
+
runCleanup: async () => {}
|
|
164
|
+
};
|
|
165
|
+
const cleanupTasks = [];
|
|
166
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
167
|
+
for (const source of toolSources) {
|
|
168
|
+
const resolved = await resolveToolsForRun({
|
|
169
|
+
agentTools: source,
|
|
170
|
+
...context !== void 0 ? { context } : {}
|
|
171
|
+
});
|
|
172
|
+
cleanupTasks.push(resolved.runCleanup);
|
|
173
|
+
for (const tool of resolved.tools) {
|
|
174
|
+
if (typeof tool.name !== "string") continue;
|
|
175
|
+
if (deduped.has(tool.name)) deduped.delete(tool.name);
|
|
176
|
+
deduped.set(tool.name, tool);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
tools: [...deduped.values()],
|
|
181
|
+
runCleanup: async () => {
|
|
182
|
+
await Promise.allSettled(cleanupTasks.map(async (cleanup) => await cleanup()));
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
},
|
|
186
|
+
async applyOnStep(params) {
|
|
187
|
+
const prepared = { messages: [...params.messages] };
|
|
188
|
+
if (onStepHooks.length === 0) return prepared;
|
|
189
|
+
const supportsTools = params.modelCaps?.tools === true;
|
|
190
|
+
const supportsInstructionControl = params.modelCaps === void 0 ? true : params.modelCaps.inputShape !== "prompt" && params.modelCaps.supportsInstruction === true;
|
|
191
|
+
const baseHookContext = {
|
|
192
|
+
runId: params.runId,
|
|
193
|
+
agentName: params.agentName,
|
|
194
|
+
stepIndex: params.stepIndex,
|
|
195
|
+
maxSteps: params.maxSteps,
|
|
196
|
+
messages: prepared.messages,
|
|
197
|
+
...params.conversationId !== void 0 ? { conversationId: params.conversationId } : {},
|
|
198
|
+
...params.context !== void 0 ? { context: params.context } : {},
|
|
199
|
+
...params.previousStep !== void 0 ? { previousStep: params.previousStep } : {},
|
|
200
|
+
updateMessages: (updater) => {
|
|
201
|
+
prepared.messages = updater([...prepared.messages]);
|
|
202
|
+
baseHookContext.messages = prepared.messages;
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
const withInstructionControl = supportsInstructionControl ? {
|
|
206
|
+
...baseHookContext,
|
|
207
|
+
setSystemInstruction: (instruction) => {
|
|
208
|
+
prepared.systemInstruction = instruction;
|
|
209
|
+
}
|
|
210
|
+
} : baseHookContext;
|
|
211
|
+
const hookContext = supportsTools ? {
|
|
212
|
+
...withInstructionControl,
|
|
213
|
+
setToolChoice: (choice) => {
|
|
214
|
+
prepared.toolChoice = choice;
|
|
215
|
+
},
|
|
216
|
+
setActiveTools: (names) => {
|
|
217
|
+
prepared.activeTools = [...names];
|
|
218
|
+
}
|
|
219
|
+
} : withInstructionControl;
|
|
220
|
+
for (const entry of onStepHooks) try {
|
|
221
|
+
await entry.hook(hookContext);
|
|
222
|
+
} catch (error) {
|
|
223
|
+
reportPluginRuntimeError({
|
|
224
|
+
pluginId: entry.pluginId,
|
|
225
|
+
phase: "on_step",
|
|
226
|
+
error
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return prepared;
|
|
230
|
+
},
|
|
231
|
+
async applyBeforeModelCall(params) {
|
|
232
|
+
const prepared = {
|
|
233
|
+
input: [...params.input],
|
|
234
|
+
tools: [...params.tools],
|
|
235
|
+
toolChoice: params.toolChoice
|
|
236
|
+
};
|
|
237
|
+
if (beforeModelHooks.length === 0) return {
|
|
238
|
+
input: prepared.input,
|
|
239
|
+
tools: prepared.tools,
|
|
240
|
+
...prepared.toolChoice !== void 0 ? { toolChoice: prepared.toolChoice } : {}
|
|
241
|
+
};
|
|
242
|
+
const supportsTools = params.modelCaps?.tools === true;
|
|
243
|
+
const baseHookContext = {
|
|
244
|
+
runId: params.runId,
|
|
245
|
+
agentName: params.agentName,
|
|
246
|
+
stepIndex: params.stepIndex,
|
|
247
|
+
input: prepared.input,
|
|
248
|
+
...params.conversationId !== void 0 ? { conversationId: params.conversationId } : {},
|
|
249
|
+
setInput: (input) => {
|
|
250
|
+
prepared.input = [...input];
|
|
251
|
+
baseHookContext.input = prepared.input;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
if (supportsTools) {
|
|
255
|
+
const toolHookContext = {
|
|
256
|
+
...baseHookContext,
|
|
257
|
+
tools: prepared.tools,
|
|
258
|
+
...prepared.toolChoice !== void 0 ? { toolChoice: prepared.toolChoice } : {},
|
|
259
|
+
setTools: (tools) => {
|
|
260
|
+
prepared.tools = [...tools];
|
|
261
|
+
toolHookContext.tools = prepared.tools;
|
|
262
|
+
},
|
|
263
|
+
setToolChoice: (choice) => {
|
|
264
|
+
prepared.toolChoice = choice;
|
|
265
|
+
if (choice === void 0) delete toolHookContext.toolChoice;
|
|
266
|
+
else toolHookContext.toolChoice = choice;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
269
|
+
for (const entry of beforeModelHooks) try {
|
|
270
|
+
await entry.hook(toolHookContext);
|
|
271
|
+
} catch (error) {
|
|
272
|
+
reportPluginRuntimeError({
|
|
273
|
+
pluginId: entry.pluginId,
|
|
274
|
+
phase: "before_model_call",
|
|
275
|
+
error
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
} else for (const entry of beforeModelHooks) try {
|
|
279
|
+
await entry.hook(baseHookContext);
|
|
280
|
+
} catch (error) {
|
|
281
|
+
reportPluginRuntimeError({
|
|
282
|
+
pluginId: entry.pluginId,
|
|
283
|
+
phase: "before_model_call",
|
|
284
|
+
error
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
input: prepared.input,
|
|
289
|
+
tools: prepared.tools,
|
|
290
|
+
...prepared.toolChoice !== void 0 ? { toolChoice: prepared.toolChoice } : {}
|
|
291
|
+
};
|
|
292
|
+
},
|
|
293
|
+
async applyAfterModelCall(params) {
|
|
294
|
+
if (afterModelHooks.length === 0) return;
|
|
295
|
+
const hookContext = {
|
|
296
|
+
runId: params.runId,
|
|
297
|
+
agentName: params.agentName,
|
|
298
|
+
stepIndex: params.stepIndex,
|
|
299
|
+
response: params.response,
|
|
300
|
+
...params.conversationId !== void 0 ? { conversationId: params.conversationId } : {}
|
|
301
|
+
};
|
|
302
|
+
for (const entry of afterModelHooks) try {
|
|
303
|
+
await entry.hook(hookContext);
|
|
304
|
+
} catch (error) {
|
|
305
|
+
reportPluginRuntimeError({
|
|
306
|
+
pluginId: entry.pluginId,
|
|
307
|
+
phase: "after_model_call",
|
|
308
|
+
error
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
},
|
|
312
|
+
async applyBeforeToolCall(params) {
|
|
313
|
+
const prepared = { args: params.args };
|
|
314
|
+
if (beforeToolHooks.length === 0) return prepared;
|
|
315
|
+
const hookContext = {
|
|
316
|
+
runId: params.runId,
|
|
317
|
+
agentName: params.agentName,
|
|
318
|
+
toolName: params.toolName,
|
|
319
|
+
toolCallId: params.toolCallId,
|
|
320
|
+
args: prepared.args,
|
|
321
|
+
...params.conversationId !== void 0 ? { conversationId: params.conversationId } : {},
|
|
322
|
+
setArgs: (args) => {
|
|
323
|
+
prepared.args = args;
|
|
324
|
+
hookContext.args = args;
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
for (const entry of beforeToolHooks) try {
|
|
328
|
+
const decision = await entry.hook(hookContext);
|
|
329
|
+
if (decision && decision.skip === true) return {
|
|
330
|
+
args: prepared.args,
|
|
331
|
+
decision
|
|
332
|
+
};
|
|
333
|
+
} catch (error) {
|
|
334
|
+
reportPluginRuntimeError({
|
|
335
|
+
pluginId: entry.pluginId,
|
|
336
|
+
phase: "before_tool_call",
|
|
337
|
+
error
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
return { args: prepared.args };
|
|
341
|
+
},
|
|
342
|
+
async applyAfterToolCall(params) {
|
|
343
|
+
const prepared = { result: params.result };
|
|
344
|
+
if (afterToolHooks.length === 0) return prepared;
|
|
345
|
+
const hookContext = {
|
|
346
|
+
runId: params.runId,
|
|
347
|
+
agentName: params.agentName,
|
|
348
|
+
toolName: params.toolName,
|
|
349
|
+
toolCallId: params.toolCallId,
|
|
350
|
+
args: params.args,
|
|
351
|
+
result: prepared.result,
|
|
352
|
+
...params.conversationId !== void 0 ? { conversationId: params.conversationId } : {},
|
|
353
|
+
...params.error !== void 0 ? { error: params.error } : {},
|
|
354
|
+
setResult: (result) => {
|
|
355
|
+
prepared.result = result;
|
|
356
|
+
hookContext.result = result;
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
for (const entry of afterToolHooks) try {
|
|
360
|
+
await entry.hook(hookContext);
|
|
361
|
+
} catch (error) {
|
|
362
|
+
reportPluginRuntimeError({
|
|
363
|
+
pluginId: entry.pluginId,
|
|
364
|
+
phase: "after_tool_call",
|
|
365
|
+
error
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
return prepared;
|
|
369
|
+
},
|
|
370
|
+
async applyBeforeSave(params) {
|
|
371
|
+
let items = [...params.items];
|
|
372
|
+
if (beforeSaveHooks.length === 0) return items;
|
|
373
|
+
const hookContext = {
|
|
374
|
+
runId: params.runId,
|
|
375
|
+
agentName: params.agentName,
|
|
376
|
+
items,
|
|
377
|
+
...params.conversationId !== void 0 ? { conversationId: params.conversationId } : {},
|
|
378
|
+
setItems: (nextItems) => {
|
|
379
|
+
items = [...nextItems];
|
|
380
|
+
hookContext.items = items;
|
|
381
|
+
}
|
|
382
|
+
};
|
|
383
|
+
for (const entry of beforeSaveHooks) try {
|
|
384
|
+
await entry.hook(hookContext);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
reportPluginRuntimeError({
|
|
387
|
+
pluginId: entry.pluginId,
|
|
388
|
+
phase: "before_save",
|
|
389
|
+
error
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
return items;
|
|
393
|
+
}
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Validates and expands a plugin endpoint definition.
|
|
398
|
+
*/
|
|
399
|
+
function validateAndExpandEndpoint(endpoint, pluginId, seenEndpoints, endpoints) {
|
|
400
|
+
const path = endpoint.path.trim();
|
|
401
|
+
if (path.length === 0 || !path.startsWith("/")) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Plugin '${pluginId}' endpoint path must start with '/'.`, {
|
|
402
|
+
context: {
|
|
403
|
+
pluginId,
|
|
404
|
+
path: endpoint.path
|
|
405
|
+
},
|
|
406
|
+
trace: [{ at: "core.plugins.createPluginRuntime" }]
|
|
407
|
+
});
|
|
408
|
+
const methods = Array.isArray(endpoint.method) ? endpoint.method : [endpoint.method];
|
|
409
|
+
if (methods.length === 0) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Plugin '${pluginId}' endpoint must declare at least one method.`, {
|
|
410
|
+
context: {
|
|
411
|
+
pluginId,
|
|
412
|
+
path
|
|
413
|
+
},
|
|
414
|
+
trace: [{ at: "core.plugins.createPluginRuntime" }]
|
|
415
|
+
});
|
|
416
|
+
/** Allowed HTTP methods for plugin endpoints. */
|
|
417
|
+
const ALLOWED_METHODS = new Set([
|
|
418
|
+
"GET",
|
|
419
|
+
"POST",
|
|
420
|
+
"PUT",
|
|
421
|
+
"PATCH",
|
|
422
|
+
"DELETE",
|
|
423
|
+
"OPTIONS"
|
|
424
|
+
]);
|
|
425
|
+
for (const method of methods) {
|
|
426
|
+
if (!ALLOWED_METHODS.has(method)) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Plugin '${pluginId}' endpoint has unsupported HTTP method '${method}'.`, {
|
|
427
|
+
context: {
|
|
428
|
+
pluginId,
|
|
429
|
+
path,
|
|
430
|
+
method
|
|
431
|
+
},
|
|
432
|
+
trace: [{ at: "core.plugins.createPluginRuntime" }]
|
|
433
|
+
});
|
|
434
|
+
const key = `${method} ${path}`;
|
|
435
|
+
if (seenEndpoints.has(key)) throw BetterAgentError.fromCode("VALIDATION_FAILED", `Duplicate plugin endpoint '${key}'.`, {
|
|
436
|
+
context: {
|
|
437
|
+
pluginId,
|
|
438
|
+
method,
|
|
439
|
+
path
|
|
440
|
+
},
|
|
441
|
+
trace: [{ at: "core.plugins.createPluginRuntime" }]
|
|
442
|
+
});
|
|
443
|
+
seenEndpoints.add(key);
|
|
444
|
+
endpoints.push({
|
|
445
|
+
pluginId,
|
|
446
|
+
method,
|
|
447
|
+
path,
|
|
448
|
+
...endpoint.public === true ? { public: true } : {},
|
|
449
|
+
handler: endpoint.handler
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
//#endregion
|
|
455
|
+
export { createPluginRuntime, definePlugin };
|
|
456
|
+
//# sourceMappingURL=runtime.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.mjs","names":[],"sources":["../../src/plugins/runtime.ts"],"sourcesContent":["import { BetterAgentError } from \"@better-agent/shared/errors\";\nimport { logger } from \"@better-agent/shared/logger\";\nimport type { HttpMethod } from \"../api\";\nimport type { Event } from \"../events\";\nimport type {\n Capabilities,\n GenerativeModelInputItem,\n GenerativeModelInputMessageContent,\n ToolChoice,\n} from \"../providers\";\nimport type { PreviousStepResult } from \"../run\";\nimport { resolveToolsForRun } from \"../tools\";\nimport type { AgentToolDefinition, ToolSource } from \"../tools\";\nimport type {\n Plugin,\n PluginEndpoint,\n PluginEventMiddleware,\n PluginGuard,\n PluginModelCallContext,\n PluginModelResponseContext,\n PluginOnStepContext,\n PluginRuntime,\n PluginRuntimeEndpoint,\n PluginSaveContext,\n PluginToolCallContext,\n PluginToolResultContext,\n} from \"./types\";\n\n/**\n * Defines a plugin.\n *\n * Plugin hook contexts are intentionally broad at authoring time. Some\n * capability-sensitive controls may be omitted at runtime for models that do\n * not support them.\n *\n * @param config Plugin configuration.\n * @returns The plugin definition with preserved literal types.\n */\nexport function definePlugin<const T extends Plugin>(config: T): T {\n return config;\n}\n\n/**\n * Builds validated plugin runtime metadata for the runner and server.\n */\nexport function createPluginRuntime(plugins?: readonly Plugin[]): PluginRuntime {\n const source = plugins ?? [];\n\n const seenPluginIds = new Set<string>();\n const seenEndpoints = new Set<string>();\n\n const eventMiddlewares: Array<{\n pluginId: string;\n subscribe?: readonly Event[\"type\"][];\n middleware: PluginEventMiddleware;\n }> = [];\n const guards: Array<{ pluginId: string; guard: PluginGuard }> = [];\n const endpoints: PluginRuntimeEndpoint[] = [];\n const onEventHandlers: Array<NonNullable<Plugin[\"onEvent\"]>> = [];\n const toolSources: ToolSource<unknown>[] = [];\n const onStepHooks: Array<{ pluginId: string; hook: NonNullable<Plugin[\"onStep\"]> }> = [];\n const beforeModelHooks: Array<{\n pluginId: string;\n hook: NonNullable<Plugin[\"onBeforeModelCall\"]>;\n }> = [];\n const afterModelHooks: Array<{\n pluginId: string;\n hook: NonNullable<Plugin[\"onAfterModelCall\"]>;\n }> = [];\n const beforeToolHooks: Array<{\n pluginId: string;\n hook: NonNullable<Plugin[\"onBeforeToolCall\"]>;\n }> = [];\n const afterToolHooks: Array<{\n pluginId: string;\n hook: NonNullable<Plugin[\"onAfterToolCall\"]>;\n }> = [];\n const beforeSaveHooks: Array<{\n pluginId: string;\n hook: NonNullable<Plugin[\"onBeforeSave\"]>;\n }> = [];\n\n for (const plugin of source) {\n const pluginId = plugin.id.trim();\n\n // Validate plugin id before any hooks are collected.\n if (pluginId.length === 0) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n \"Plugin id must be a non-empty string.\",\n {\n trace: [{ at: \"core.plugins.createPluginRuntime\" }],\n },\n );\n }\n\n if (seenPluginIds.has(pluginId)) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n `Duplicate plugin id '${pluginId}'.`,\n {\n context: { pluginId },\n trace: [{ at: \"core.plugins.createPluginRuntime\" }],\n },\n );\n }\n\n seenPluginIds.add(pluginId);\n\n // Collect event middleware in registration order.\n if (plugin.events?.middleware) {\n for (const middleware of plugin.events.middleware) {\n eventMiddlewares.push({\n pluginId,\n middleware,\n ...(plugin.events.subscribe !== undefined\n ? { subscribe: plugin.events.subscribe }\n : {}),\n });\n }\n }\n\n // Collect guards in registration order.\n if (plugin.guards) {\n for (const guard of plugin.guards) {\n guards.push({ pluginId, guard });\n }\n }\n\n // Collect and validate endpoints up front so the server can mount them safely.\n for (const endpoint of plugin.endpoints ?? []) {\n validateAndExpandEndpoint(endpoint, pluginId, seenEndpoints, endpoints);\n }\n\n if (plugin.onEvent) {\n onEventHandlers.push(plugin.onEvent);\n }\n\n if (plugin.tools) {\n toolSources.push(plugin.tools);\n }\n\n if (plugin.onStep) {\n onStepHooks.push({ pluginId, hook: plugin.onStep });\n }\n\n if (plugin.onBeforeModelCall) {\n beforeModelHooks.push({ pluginId, hook: plugin.onBeforeModelCall });\n }\n\n if (plugin.onAfterModelCall) {\n afterModelHooks.push({ pluginId, hook: plugin.onAfterModelCall });\n }\n\n if (plugin.onBeforeToolCall) {\n beforeToolHooks.push({ pluginId, hook: plugin.onBeforeToolCall });\n }\n\n if (plugin.onAfterToolCall) {\n afterToolHooks.push({ pluginId, hook: plugin.onAfterToolCall });\n }\n\n if (plugin.onBeforeSave) {\n beforeSaveHooks.push({ pluginId, hook: plugin.onBeforeSave });\n }\n }\n\n const reportPluginRuntimeError = (params: {\n pluginId: string;\n phase:\n | \"event_middleware\"\n | \"guard\"\n | \"on_event\"\n | \"on_step\"\n | \"before_model_call\"\n | \"after_model_call\"\n | \"before_tool_call\"\n | \"after_tool_call\"\n | \"before_save\";\n error: unknown;\n }) => {\n logger.error(\n `[better-agent] Plugin '${params.pluginId}' failed during ${params.phase}.`,\n params.error,\n );\n };\n\n return {\n plugins: source,\n endpoints,\n hasEventMiddleware: eventMiddlewares.length > 0,\n hasOnEvent: onEventHandlers.length > 0,\n hasGuards: guards.length > 0,\n hasTools: toolSources.length > 0,\n hasOnStep: onStepHooks.length > 0,\n hasModelHooks: beforeModelHooks.length > 0 || afterModelHooks.length > 0,\n hasToolHooks: beforeToolHooks.length > 0 || afterToolHooks.length > 0,\n hasOnBeforeSave: beforeSaveHooks.length > 0,\n\n async dispatchEvent(event, ctx) {\n const run = async (index: number, current: Event): Promise<Event | null> => {\n for (let i = index; i < eventMiddlewares.length; i++) {\n const item = eventMiddlewares[i];\n if (!item) continue;\n\n if (item.subscribe && !item.subscribe.includes(current.type)) {\n continue;\n }\n\n try {\n const result = await item.middleware(current, ctx, async (nextEvent) => {\n return run(i + 1, nextEvent);\n });\n return result;\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: item.pluginId,\n phase: \"event_middleware\",\n error,\n });\n return run(i + 1, current);\n }\n }\n return current;\n };\n\n return run(0, event);\n },\n\n async dispatchOnEvent(event, ctx) {\n for (let i = 0; i < source.length; i += 1) {\n const plugin = source[i];\n if (!plugin?.onEvent) continue;\n\n try {\n await plugin.onEvent(event, ctx);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: plugin.id,\n phase: \"on_event\",\n error,\n });\n }\n }\n },\n\n async dispatchRun(ctx) {\n for (const entry of guards) {\n try {\n const response = await entry.guard(ctx);\n if (response) {\n // Guard rejected the request\n return response;\n }\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"guard\",\n error,\n });\n throw BetterAgentError.wrap({\n err: error,\n message: `Plugin '${entry.pluginId}' guard failed.`,\n opts: {\n code: \"INTERNAL\",\n context: {\n pluginId: entry.pluginId,\n mode: ctx.mode,\n agentName: ctx.agentName,\n },\n trace: [{ at: \"core.plugins.createPluginRuntime.dispatchRun\" }],\n },\n });\n }\n }\n // All guards passed\n return null;\n },\n\n async resolveTools<TContext>(context?: TContext) {\n if (toolSources.length === 0) {\n return {\n tools: [],\n runCleanup: async () => {},\n };\n }\n\n const cleanupTasks: Array<() => Promise<void> | void> = [];\n const deduped = new Map<string, AgentToolDefinition>();\n\n for (const source of toolSources) {\n const resolved = await resolveToolsForRun<unknown>({\n agentTools: source,\n ...(context !== undefined ? { context: context as unknown } : {}),\n });\n\n cleanupTasks.push(resolved.runCleanup);\n for (const tool of resolved.tools) {\n if (typeof tool.name !== \"string\") {\n continue;\n }\n\n if (deduped.has(tool.name)) {\n deduped.delete(tool.name);\n }\n deduped.set(tool.name, tool);\n }\n }\n\n return {\n tools: [...deduped.values()],\n runCleanup: async () => {\n await Promise.allSettled(cleanupTasks.map(async (cleanup) => await cleanup()));\n },\n };\n },\n\n async applyOnStep<\n TContext,\n TToolName extends string = string,\n TModelCaps extends Capabilities = Capabilities,\n >(params: {\n runId: string;\n agentName: string;\n conversationId?: string;\n stepIndex: number;\n maxSteps: number | undefined;\n messages: GenerativeModelInputItem<TModelCaps>[];\n context?: TContext;\n previousStep?: PreviousStepResult;\n /** Model capabilities to determine if setActiveTools should work */\n modelCaps?: TModelCaps;\n }) {\n const prepared: {\n messages: typeof params.messages;\n toolChoice?: ToolChoice;\n activeTools?: TToolName[];\n systemInstruction?: GenerativeModelInputMessageContent<TModelCaps>;\n } = {\n messages: [...params.messages],\n };\n\n if (onStepHooks.length === 0) {\n return prepared;\n }\n\n // Check if model supports tools\n const supportsTools = params.modelCaps?.tools === true;\n const supportsInstructionControl =\n params.modelCaps === undefined\n ? true\n : params.modelCaps.inputShape !== \"prompt\" &&\n params.modelCaps.supportsInstruction === true;\n\n const baseHookContext = {\n runId: params.runId,\n agentName: params.agentName,\n stepIndex: params.stepIndex,\n maxSteps: params.maxSteps,\n messages: prepared.messages,\n ...(params.conversationId !== undefined\n ? { conversationId: params.conversationId }\n : {}),\n ...(params.context !== undefined ? { context: params.context as unknown } : {}),\n ...(params.previousStep !== undefined ? { previousStep: params.previousStep } : {}),\n updateMessages: (\n updater: (\n messages: GenerativeModelInputItem<TModelCaps>[],\n ) => GenerativeModelInputItem<TModelCaps>[],\n ) => {\n prepared.messages = updater([...prepared.messages]);\n (baseHookContext as typeof hookContext).messages = prepared.messages;\n },\n };\n\n const withInstructionControl = supportsInstructionControl\n ? {\n ...baseHookContext,\n setSystemInstruction: (\n instruction: GenerativeModelInputMessageContent<TModelCaps>,\n ) => {\n prepared.systemInstruction = instruction;\n },\n }\n : baseHookContext;\n\n const hookContext = supportsTools\n ? {\n ...withInstructionControl,\n setToolChoice: (choice: ToolChoice) => {\n prepared.toolChoice = choice;\n },\n setActiveTools: (names: readonly TToolName[]) => {\n prepared.activeTools = [...names];\n },\n }\n : withInstructionControl;\n\n for (const entry of onStepHooks) {\n try {\n await entry.hook(hookContext as unknown as PluginOnStepContext);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"on_step\",\n error,\n });\n }\n }\n\n return prepared;\n },\n\n async applyBeforeModelCall(params) {\n const prepared: {\n input: GenerativeModelInputItem[];\n tools: AgentToolDefinition[];\n toolChoice: ToolChoice | undefined;\n } = {\n input: [...params.input],\n tools: [...params.tools],\n toolChoice: params.toolChoice,\n };\n\n if (beforeModelHooks.length === 0) {\n return {\n input: prepared.input,\n tools: prepared.tools,\n ...(prepared.toolChoice !== undefined\n ? { toolChoice: prepared.toolChoice }\n : {}),\n };\n }\n\n const supportsTools = params.modelCaps?.tools === true;\n\n const baseHookContext = {\n runId: params.runId,\n agentName: params.agentName,\n stepIndex: params.stepIndex,\n input: prepared.input,\n ...(params.conversationId !== undefined\n ? { conversationId: params.conversationId }\n : {}),\n setInput: (input: GenerativeModelInputItem[]) => {\n prepared.input = [...input];\n baseHookContext.input = prepared.input;\n },\n };\n\n if (supportsTools) {\n const toolHookContext = {\n ...baseHookContext,\n tools: prepared.tools,\n ...(prepared.toolChoice !== undefined\n ? { toolChoice: prepared.toolChoice }\n : {}),\n setTools: (tools: AgentToolDefinition[]) => {\n prepared.tools = [...tools];\n toolHookContext.tools = prepared.tools;\n },\n setToolChoice: (choice: ToolChoice | undefined) => {\n prepared.toolChoice = choice;\n if (choice === undefined) {\n // biome-ignore lint/performance/noDelete: <explanation>\n delete toolHookContext.toolChoice;\n } else {\n toolHookContext.toolChoice = choice;\n }\n },\n };\n\n for (const entry of beforeModelHooks) {\n try {\n await entry.hook(toolHookContext as PluginModelCallContext);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"before_model_call\",\n error,\n });\n }\n }\n } else {\n for (const entry of beforeModelHooks) {\n try {\n await entry.hook(baseHookContext as PluginModelCallContext);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"before_model_call\",\n error,\n });\n }\n }\n }\n\n return {\n input: prepared.input,\n tools: prepared.tools,\n ...(prepared.toolChoice !== undefined ? { toolChoice: prepared.toolChoice } : {}),\n };\n },\n\n async applyAfterModelCall(params) {\n if (afterModelHooks.length === 0) {\n return;\n }\n\n const hookContext: PluginModelResponseContext = {\n runId: params.runId,\n agentName: params.agentName,\n stepIndex: params.stepIndex,\n response: params.response,\n ...(params.conversationId !== undefined\n ? { conversationId: params.conversationId }\n : {}),\n };\n\n for (const entry of afterModelHooks) {\n try {\n await entry.hook(hookContext);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"after_model_call\",\n error,\n });\n }\n }\n },\n\n async applyBeforeToolCall(params) {\n const prepared = { args: params.args };\n\n if (beforeToolHooks.length === 0) {\n return prepared;\n }\n\n const hookContext: PluginToolCallContext = {\n runId: params.runId,\n agentName: params.agentName,\n toolName: params.toolName,\n toolCallId: params.toolCallId,\n args: prepared.args,\n ...(params.conversationId !== undefined\n ? { conversationId: params.conversationId }\n : {}),\n setArgs: (args) => {\n prepared.args = args;\n hookContext.args = args;\n },\n };\n\n for (const entry of beforeToolHooks) {\n try {\n const decision = await entry.hook(hookContext);\n if (decision && decision.skip === true) {\n return { args: prepared.args, decision };\n }\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"before_tool_call\",\n error,\n });\n }\n }\n\n return { args: prepared.args };\n },\n\n async applyAfterToolCall(params) {\n const prepared = { result: params.result };\n\n if (afterToolHooks.length === 0) {\n return prepared;\n }\n\n const hookContext: PluginToolResultContext = {\n runId: params.runId,\n agentName: params.agentName,\n toolName: params.toolName,\n toolCallId: params.toolCallId,\n args: params.args,\n result: prepared.result,\n ...(params.conversationId !== undefined\n ? { conversationId: params.conversationId }\n : {}),\n ...(params.error !== undefined ? { error: params.error } : {}),\n setResult: (result) => {\n prepared.result = result;\n hookContext.result = result;\n },\n };\n\n for (const entry of afterToolHooks) {\n try {\n await entry.hook(hookContext);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"after_tool_call\",\n error,\n });\n }\n }\n\n return prepared;\n },\n\n async applyBeforeSave(params) {\n let items = [...params.items];\n if (beforeSaveHooks.length === 0) {\n return items;\n }\n\n const hookContext: PluginSaveContext = {\n runId: params.runId,\n agentName: params.agentName,\n items,\n ...(params.conversationId !== undefined\n ? { conversationId: params.conversationId }\n : {}),\n setItems: (nextItems) => {\n items = [...nextItems];\n hookContext.items = items;\n },\n };\n\n for (const entry of beforeSaveHooks) {\n try {\n await entry.hook(hookContext);\n } catch (error) {\n reportPluginRuntimeError({\n pluginId: entry.pluginId,\n phase: \"before_save\",\n error,\n });\n }\n }\n\n return items;\n },\n };\n}\n\n/**\n * Validates and expands a plugin endpoint definition.\n */\nfunction validateAndExpandEndpoint(\n endpoint: PluginEndpoint,\n pluginId: string,\n seenEndpoints: Set<string>,\n endpoints: PluginRuntimeEndpoint[],\n): void {\n const path = endpoint.path.trim();\n\n // Endpoint paths are always absolute route patterns.\n if (path.length === 0 || !path.startsWith(\"/\")) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n `Plugin '${pluginId}' endpoint path must start with '/'.`,\n {\n context: { pluginId, path: endpoint.path },\n trace: [{ at: \"core.plugins.createPluginRuntime\" }],\n },\n );\n }\n\n // Normalize methods into a flat list.\n const methods = Array.isArray(endpoint.method) ? endpoint.method : [endpoint.method];\n\n // Endpoints must declare at least one method.\n if (methods.length === 0) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n `Plugin '${pluginId}' endpoint must declare at least one method.`,\n {\n context: { pluginId, path },\n trace: [{ at: \"core.plugins.createPluginRuntime\" }],\n },\n );\n }\n\n /** Allowed HTTP methods for plugin endpoints. */\n const ALLOWED_METHODS: Set<HttpMethod> = new Set([\n \"GET\",\n \"POST\",\n \"PUT\",\n \"PATCH\",\n \"DELETE\",\n \"OPTIONS\",\n ]);\n\n // Expand one endpoint per HTTP method.\n for (const method of methods) {\n if (!ALLOWED_METHODS.has(method)) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n `Plugin '${pluginId}' endpoint has unsupported HTTP method '${method}'.`,\n {\n context: { pluginId, path, method },\n trace: [{ at: \"core.plugins.createPluginRuntime\" }],\n },\n );\n }\n\n // Duplicate method/path pairs are rejected at runtime construction time.\n const key = `${method} ${path}`;\n if (seenEndpoints.has(key)) {\n throw BetterAgentError.fromCode(\n \"VALIDATION_FAILED\",\n `Duplicate plugin endpoint '${key}'.`,\n {\n context: { pluginId, method, path },\n trace: [{ at: \"core.plugins.createPluginRuntime\" }],\n },\n );\n }\n\n seenEndpoints.add(key);\n endpoints.push({\n pluginId,\n method,\n path,\n ...(endpoint.public === true ? { public: true } : {}),\n handler: endpoint.handler,\n });\n }\n}\n"],"mappings":"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"}
|