veryfront 0.1.144 → 0.1.145

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 (71) hide show
  1. package/esm/deno.js +1 -1
  2. package/esm/src/agent/ag-ui-handler.d.ts +77 -0
  3. package/esm/src/agent/ag-ui-handler.d.ts.map +1 -0
  4. package/esm/src/agent/ag-ui-handler.js +331 -0
  5. package/esm/src/agent/index.d.ts +2 -1
  6. package/esm/src/agent/index.d.ts.map +1 -1
  7. package/esm/src/agent/index.js +2 -1
  8. package/esm/src/agent/runtime/index.d.ts +2 -0
  9. package/esm/src/agent/runtime/index.d.ts.map +1 -1
  10. package/esm/src/agent/runtime/index.js +15 -7
  11. package/esm/src/agent/runtime/resume-session.d.ts +61 -0
  12. package/esm/src/agent/runtime/resume-session.d.ts.map +1 -0
  13. package/esm/src/agent/runtime/resume-session.js +232 -0
  14. package/esm/src/agent/runtime/tool-helpers.d.ts +4 -2
  15. package/esm/src/agent/runtime/tool-helpers.d.ts.map +1 -1
  16. package/esm/src/agent/runtime/tool-helpers.js +57 -4
  17. package/esm/src/agent/types.d.ts +2 -1
  18. package/esm/src/agent/types.d.ts.map +1 -1
  19. package/esm/src/internal-agents/session-manager.d.ts +8 -24
  20. package/esm/src/internal-agents/session-manager.d.ts.map +1 -1
  21. package/esm/src/internal-agents/session-manager.js +55 -178
  22. package/esm/src/mcp/index.d.ts +2 -0
  23. package/esm/src/mcp/index.d.ts.map +1 -1
  24. package/esm/src/mcp/index.js +2 -0
  25. package/esm/src/mcp/server.d.ts +1 -0
  26. package/esm/src/mcp/server.d.ts.map +1 -1
  27. package/esm/src/mcp/server.js +47 -8
  28. package/esm/src/mcp/session.d.ts +9 -0
  29. package/esm/src/mcp/session.d.ts.map +1 -0
  30. package/esm/src/mcp/session.js +25 -0
  31. package/esm/src/mcp/sse.d.ts +8 -0
  32. package/esm/src/mcp/sse.d.ts.map +1 -0
  33. package/esm/src/mcp/sse.js +17 -0
  34. package/esm/src/platform/cloud/resolver.d.ts.map +1 -1
  35. package/esm/src/platform/cloud/resolver.js +13 -3
  36. package/esm/src/provider/index.d.ts +2 -0
  37. package/esm/src/provider/index.d.ts.map +1 -1
  38. package/esm/src/provider/index.js +1 -0
  39. package/esm/src/provider/veryfront-cloud/context.d.ts +10 -0
  40. package/esm/src/provider/veryfront-cloud/context.d.ts.map +1 -0
  41. package/esm/src/provider/veryfront-cloud/context.js +11 -0
  42. package/esm/src/tool/index.d.ts +3 -1
  43. package/esm/src/tool/index.d.ts.map +1 -1
  44. package/esm/src/tool/index.js +1 -0
  45. package/esm/src/tool/remote-mcp.d.ts +14 -0
  46. package/esm/src/tool/remote-mcp.d.ts.map +1 -0
  47. package/esm/src/tool/remote-mcp.js +176 -0
  48. package/esm/src/tool/types.d.ts +12 -0
  49. package/esm/src/tool/types.d.ts.map +1 -1
  50. package/esm/src/utils/version-constant.d.ts +1 -1
  51. package/esm/src/utils/version-constant.js +1 -1
  52. package/package.json +1 -1
  53. package/src/deno.js +1 -1
  54. package/src/src/agent/ag-ui-handler.ts +451 -0
  55. package/src/src/agent/index.ts +21 -1
  56. package/src/src/agent/runtime/index.ts +28 -3
  57. package/src/src/agent/runtime/resume-session.ts +319 -0
  58. package/src/src/agent/runtime/tool-helpers.ts +89 -6
  59. package/src/src/agent/types.ts +2 -1
  60. package/src/src/internal-agents/session-manager.ts +69 -243
  61. package/src/src/mcp/index.ts +3 -0
  62. package/src/src/mcp/server.ts +54 -9
  63. package/src/src/mcp/session.ts +31 -0
  64. package/src/src/mcp/sse.ts +19 -0
  65. package/src/src/platform/cloud/resolver.ts +15 -3
  66. package/src/src/provider/index.ts +5 -0
  67. package/src/src/provider/veryfront-cloud/context.ts +28 -0
  68. package/src/src/tool/index.ts +9 -1
  69. package/src/src/tool/remote-mcp.ts +261 -0
  70. package/src/src/tool/types.ts +17 -0
  71. package/src/src/utils/version-constant.ts +1 -1
@@ -0,0 +1,451 @@
1
+ import * as dntShim from "../../_dnt.shims.js";
2
+ import { z } from "zod";
3
+ import { getAgent } from "./composition/index.js";
4
+ import type { Agent, Message } from "./types.js";
5
+ import { INVALID_ARGUMENT } from "../errors/index.js";
6
+ import {
7
+ createStreamTransformState,
8
+ finalizeRunEvents,
9
+ formatAgUiEvent,
10
+ mapRuntimeEventToAgUi,
11
+ parseSseJsonEvents,
12
+ } from "../internal-agents/ag-ui-sse.js";
13
+
14
+ const AGENT_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
15
+ const MAX_TOOL_PARAMETERS_BYTES = 16_384;
16
+ const MAX_CONTEXT_ITEM_BYTES = 16_384;
17
+ const MAX_CONTEXT_TOTAL_BYTES = 65_536;
18
+ const MAX_FORWARDED_PROPS_BYTES = 65_536;
19
+ const MAX_TEXT_PART_LENGTH = 10_000;
20
+ const MAX_MESSAGES_PER_REQUEST = 100;
21
+
22
+ const encoder = new TextEncoder();
23
+
24
+ const AG_UI_HEADERS: Record<string, string> = {
25
+ "Content-Type": "text/event-stream",
26
+ "Cache-Control": "no-cache",
27
+ Connection: "keep-alive",
28
+ };
29
+
30
+ function isWithinJsonSizeLimit(value: unknown, maxBytes: number): boolean {
31
+ try {
32
+ return encoder.encode(JSON.stringify(value)).byteLength <= maxBytes;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ const AgUiRunIdSchema = z.string().min(1).max(128).regex(AGENT_ID_PATTERN);
39
+
40
+ const AgUiInjectedToolSchema = z.object({
41
+ name: z.string().min(1).max(128),
42
+ description: z.string().max(1024).optional(),
43
+ parameters: z.record(z.string(), z.unknown()).optional().refine(
44
+ (value) => value === undefined || isWithinJsonSizeLimit(value, MAX_TOOL_PARAMETERS_BYTES),
45
+ { message: "Tool parameters must be less than 16 KB" },
46
+ ),
47
+ });
48
+
49
+ const AgUiContextItemSchema = z.discriminatedUnion("type", [
50
+ z.object({
51
+ type: z.literal("text"),
52
+ title: z.string().max(256).optional(),
53
+ text: z.string().max(MAX_CONTEXT_ITEM_BYTES),
54
+ }),
55
+ z.object({
56
+ type: z.literal("json"),
57
+ title: z.string().max(256).optional(),
58
+ data: z.record(z.string(), z.unknown()).refine(
59
+ (value) => isWithinJsonSizeLimit(value, MAX_CONTEXT_ITEM_BYTES),
60
+ { message: "JSON context item must be less than 16 KB" },
61
+ ),
62
+ }),
63
+ z.object({
64
+ type: z.literal("resource"),
65
+ title: z.string().max(256).optional(),
66
+ uri: z.string().max(2048),
67
+ mimeType: z.string().max(256).optional(),
68
+ text: z.string().max(MAX_CONTEXT_ITEM_BYTES).optional(),
69
+ }),
70
+ ]);
71
+
72
+ const AgUiMessagePartSchema = z.object({ type: z.string().min(1) }).passthrough();
73
+
74
+ const AgUiMessageSchema = z.object({
75
+ id: z.string().min(1),
76
+ role: z.enum(["user", "assistant", "system", "tool"]),
77
+ parts: z.array(AgUiMessagePartSchema).default([]),
78
+ metadata: z.record(z.string(), z.unknown()).optional(),
79
+ createdAt: z.string().optional(),
80
+ });
81
+
82
+ export const AgUiRequestSchema = z.object({
83
+ threadId: z.string().uuid().optional(),
84
+ runId: AgUiRunIdSchema.optional(),
85
+ messages: z.array(AgUiMessageSchema).min(1).max(MAX_MESSAGES_PER_REQUEST),
86
+ tools: z.array(AgUiInjectedToolSchema).max(50).default([]),
87
+ context: z.array(AgUiContextItemSchema).max(10).default([]).refine(
88
+ (value) => isWithinJsonSizeLimit(value, MAX_CONTEXT_TOTAL_BYTES),
89
+ { message: "context must be less than 64 KB total" },
90
+ ),
91
+ forwardedProps: z.record(z.string(), z.unknown()).optional().refine(
92
+ (value) => value === undefined || isWithinJsonSizeLimit(value, MAX_FORWARDED_PROPS_BYTES),
93
+ { message: "forwardedProps must be less than 64 KB" },
94
+ ),
95
+ model: z.string().optional(),
96
+ maxOutputTokens: z.number().int().positive().optional(),
97
+ });
98
+
99
+ export type AgUiInjectedTool = z.infer<typeof AgUiInjectedToolSchema>;
100
+ export type AgUiContextItem = z.infer<typeof AgUiContextItemSchema>;
101
+ export type AgUiRequest = z.infer<typeof AgUiRequestSchema>;
102
+
103
+ type AgUiRuntimePart = Record<string, unknown> & { type: string };
104
+
105
+ function isRecord(value: unknown): value is Record<string, unknown> {
106
+ return typeof value === "object" && value !== null && !Array.isArray(value);
107
+ }
108
+
109
+ function normalizeToolArgs(part: Record<string, unknown>): Record<string, unknown> {
110
+ if (isRecord(part.args)) return part.args;
111
+ if (isRecord(part.input)) return part.input;
112
+ return {};
113
+ }
114
+
115
+ function normalizeMessagePart(part: Record<string, unknown>): Message["parts"][number] | null {
116
+ if (
117
+ part.type === "text" && typeof part.text === "string" &&
118
+ part.text.length <= MAX_TEXT_PART_LENGTH
119
+ ) {
120
+ return { type: "text", text: part.text };
121
+ }
122
+
123
+ if (part.type === "tool_call" && typeof part.id === "string" && typeof part.name === "string") {
124
+ return {
125
+ type: "tool-call",
126
+ toolCallId: part.id,
127
+ toolName: part.name,
128
+ args: normalizeToolArgs(part),
129
+ };
130
+ }
131
+
132
+ if (
133
+ part.type === "tool-call" &&
134
+ typeof part.toolCallId === "string" &&
135
+ typeof part.toolName === "string"
136
+ ) {
137
+ return {
138
+ type: "tool-call",
139
+ toolCallId: part.toolCallId,
140
+ toolName: part.toolName,
141
+ args: normalizeToolArgs(part),
142
+ };
143
+ }
144
+
145
+ if (
146
+ typeof part.type === "string" &&
147
+ part.type.startsWith("tool-") &&
148
+ part.type !== "tool-result" &&
149
+ typeof part.toolCallId === "string" &&
150
+ typeof part.toolName === "string"
151
+ ) {
152
+ return {
153
+ type: part.type,
154
+ toolCallId: part.toolCallId,
155
+ toolName: part.toolName,
156
+ args: normalizeToolArgs(part),
157
+ };
158
+ }
159
+
160
+ if (part.type === "tool_result" && typeof part.tool_call_id === "string") {
161
+ return {
162
+ type: "tool-result",
163
+ toolCallId: part.tool_call_id,
164
+ toolName: typeof part.tool_name === "string" ? part.tool_name : "unknown",
165
+ result: "output" in part ? part.output : undefined,
166
+ };
167
+ }
168
+
169
+ if (part.type === "tool-result" && typeof part.toolCallId === "string") {
170
+ return {
171
+ type: "tool-result",
172
+ toolCallId: part.toolCallId,
173
+ toolName: typeof part.toolName === "string" ? part.toolName : "unknown",
174
+ result: "result" in part ? part.result : undefined,
175
+ };
176
+ }
177
+
178
+ return null;
179
+ }
180
+
181
+ function normalizeMessages(messages: AgUiRequest["messages"]): Message[] {
182
+ return messages.map((message) => ({
183
+ id: message.id,
184
+ role: message.role,
185
+ parts: message.parts
186
+ .map((part) => normalizeMessagePart(part))
187
+ .filter((part): part is Message["parts"][number] => part !== null),
188
+ ...(message.createdAt ? { timestamp: Date.parse(message.createdAt) || undefined } : {}),
189
+ ...(message.metadata ? { metadata: message.metadata } : {}),
190
+ }));
191
+ }
192
+
193
+ function isRequest(obj: unknown): obj is dntShim.Request {
194
+ return (
195
+ typeof obj === "object" &&
196
+ obj !== null &&
197
+ "json" in obj &&
198
+ typeof obj.json === "function" &&
199
+ "url" in obj &&
200
+ typeof obj.url === "string" &&
201
+ "method" in obj &&
202
+ typeof obj.method === "string"
203
+ );
204
+ }
205
+
206
+ function extractRequest(requestOrCtx: unknown): dntShim.Request {
207
+ if (isRequest(requestOrCtx)) return requestOrCtx;
208
+
209
+ if (typeof requestOrCtx === "object" && requestOrCtx !== null && "request" in requestOrCtx) {
210
+ const candidate = (requestOrCtx as Record<string, unknown>).request;
211
+ if (isRequest(candidate)) return candidate;
212
+ }
213
+
214
+ throw INVALID_ARGUMENT.create({
215
+ detail: "Invalid handler argument: expected Request or APIContext",
216
+ });
217
+ }
218
+
219
+ function generateRunId(): string {
220
+ return `run_${dntShim.crypto.randomUUID().replaceAll("-", "")}`;
221
+ }
222
+
223
+ function buildStreamContext(
224
+ request: AgUiRequest,
225
+ baseContext: Record<string, unknown>,
226
+ threadId: string,
227
+ runId: string,
228
+ ): Record<string, unknown> {
229
+ return {
230
+ ...baseContext,
231
+ threadId,
232
+ runId,
233
+ agUi: {
234
+ context: request.context,
235
+ forwardedProps: request.forwardedProps,
236
+ },
237
+ };
238
+ }
239
+
240
+ function closeController(controller: ReadableStreamDefaultController<Uint8Array>): void {
241
+ try {
242
+ controller.close();
243
+ } catch {
244
+ return;
245
+ }
246
+ }
247
+
248
+ function enqueueEvent(
249
+ controller: ReadableStreamDefaultController<Uint8Array>,
250
+ event: string,
251
+ payload: Record<string, unknown>,
252
+ ): boolean {
253
+ try {
254
+ controller.enqueue(formatAgUiEvent(event, payload));
255
+ return true;
256
+ } catch {
257
+ return false;
258
+ }
259
+ }
260
+
261
+ async function createAgUiStreamResponse(
262
+ agent: Agent,
263
+ request: AgUiRequest,
264
+ baseContext: Record<string, unknown>,
265
+ ): Promise<dntShim.Response> {
266
+ const threadId = request.threadId ?? dntShim.crypto.randomUUID();
267
+ const runId = request.runId ?? generateRunId();
268
+
269
+ await agent.clearMemory();
270
+
271
+ const result = await agent.stream({
272
+ messages: normalizeMessages(request.messages),
273
+ context: buildStreamContext(request, baseContext, threadId, runId),
274
+ ...(request.model ? { model: request.model } : {}),
275
+ ...(request.maxOutputTokens ? { maxOutputTokens: request.maxOutputTokens } : {}),
276
+ });
277
+
278
+ const upstream = result.toDataStreamResponse();
279
+ const upstreamBody = upstream.body;
280
+
281
+ const stream = new ReadableStream<Uint8Array>({
282
+ start: async (controller) => {
283
+ const state = createStreamTransformState();
284
+ let reader: ReadableStreamDefaultReader<Uint8Array> | null = null;
285
+ let remainder = "";
286
+ const decoder = new TextDecoder();
287
+
288
+ if (!enqueueEvent(controller, "RunStarted", { runId, threadId, agentId: agent.id })) {
289
+ return;
290
+ }
291
+
292
+ try {
293
+ if (!upstreamBody) {
294
+ for (const event of finalizeRunEvents(state, null)) {
295
+ if (!enqueueEvent(controller, event.event, event.payload)) {
296
+ return;
297
+ }
298
+ }
299
+ closeController(controller);
300
+ return;
301
+ }
302
+
303
+ reader = upstreamBody.getReader();
304
+
305
+ while (true) {
306
+ const { done, value } = await reader.read();
307
+ if (done) break;
308
+
309
+ remainder += decoder.decode(value, { stream: true });
310
+ const parsed = parseSseJsonEvents(remainder);
311
+ remainder = parsed.remainder;
312
+
313
+ for (const event of parsed.events as AgUiRuntimePart[]) {
314
+ for (const mapped of mapRuntimeEventToAgUi(state, event)) {
315
+ if (!enqueueEvent(controller, mapped.event, mapped.payload)) {
316
+ return;
317
+ }
318
+ }
319
+ }
320
+ }
321
+
322
+ remainder += decoder.decode();
323
+ const parsed = parseSseJsonEvents(remainder);
324
+ for (const event of parsed.events as AgUiRuntimePart[]) {
325
+ for (const mapped of mapRuntimeEventToAgUi(state, event)) {
326
+ if (!enqueueEvent(controller, mapped.event, mapped.payload)) {
327
+ return;
328
+ }
329
+ }
330
+ }
331
+
332
+ for (const event of finalizeRunEvents(state, null)) {
333
+ if (!enqueueEvent(controller, event.event, event.payload)) {
334
+ return;
335
+ }
336
+ }
337
+ } catch (error) {
338
+ enqueueEvent(controller, "RunError", {
339
+ message: error instanceof Error ? error.message : "Agent run failed",
340
+ });
341
+ } finally {
342
+ await reader?.cancel().catch(() => undefined);
343
+ closeController(controller);
344
+ }
345
+ },
346
+ });
347
+
348
+ return new dntShim.Response(stream, {
349
+ status: upstream.status,
350
+ statusText: upstream.statusText,
351
+ headers: { ...AG_UI_HEADERS },
352
+ });
353
+ }
354
+
355
+ export interface AgUiHandlerOptions {
356
+ context?:
357
+ | Record<string, unknown>
358
+ | ((request: dntShim.Request) => Record<string, unknown> | Promise<Record<string, unknown>>);
359
+ }
360
+
361
+ export interface AgUiHandlerConfigWithAgent extends AgUiHandlerOptions {
362
+ agent: Agent;
363
+ }
364
+
365
+ function mergeConfig(
366
+ config: AgUiHandlerConfigWithAgent,
367
+ options?: AgUiHandlerOptions,
368
+ ): AgUiHandlerConfigWithAgent {
369
+ if (!options) return config;
370
+ return { ...options, ...config };
371
+ }
372
+
373
+ export function createAgUiHandler(
374
+ agentId: string,
375
+ options?: AgUiHandlerOptions,
376
+ ): (requestOrCtx: unknown) => Promise<dntShim.Response>;
377
+ export function createAgUiHandler(
378
+ config: AgUiHandlerConfigWithAgent,
379
+ options?: AgUiHandlerOptions,
380
+ ): (requestOrCtx: unknown) => Promise<dntShim.Response>;
381
+ export function createAgUiHandler(
382
+ agentIdOrConfig: string | AgUiHandlerConfigWithAgent,
383
+ options?: AgUiHandlerOptions,
384
+ ) {
385
+ return async function POST(requestOrCtx: unknown): Promise<dntShim.Response> {
386
+ const request = extractRequest(requestOrCtx);
387
+
388
+ let agent: Agent | undefined;
389
+
390
+ if (
391
+ typeof agentIdOrConfig === "object" &&
392
+ agentIdOrConfig !== null &&
393
+ "agent" in agentIdOrConfig
394
+ ) {
395
+ const config = mergeConfig(agentIdOrConfig, options);
396
+ agent = config.agent;
397
+ options = config;
398
+ } else {
399
+ const agentId = agentIdOrConfig as string;
400
+ try {
401
+ agent = getAgent(agentId);
402
+ } catch {
403
+ return dntShim.Response.json({ error: "Agent not found" }, { status: 404 });
404
+ }
405
+ }
406
+
407
+ if (!agent) {
408
+ return dntShim.Response.json({ error: "Agent not found" }, { status: 404 });
409
+ }
410
+
411
+ try {
412
+ const parsed = AgUiRequestSchema.parse(await request.json());
413
+
414
+ if (parsed.tools.length > 0) {
415
+ return dntShim.Response.json(
416
+ {
417
+ error:
418
+ "Injected AG-UI tools are not supported by createAgUiHandler yet. Use package-level wait/resume runtime primitives instead.",
419
+ },
420
+ { status: 501 },
421
+ );
422
+ }
423
+
424
+ const context = typeof options?.context === "function"
425
+ ? await options.context(request)
426
+ : options?.context ?? {};
427
+
428
+ return await createAgUiStreamResponse(agent, parsed, context);
429
+ } catch (error) {
430
+ if (error instanceof z.ZodError) {
431
+ return dntShim.Response.json(
432
+ {
433
+ error: "Invalid AG-UI request",
434
+ details: error.issues.map((issue) => ({
435
+ path: issue.path,
436
+ message: issue.message,
437
+ })),
438
+ },
439
+ { status: 400 },
440
+ );
441
+ }
442
+
443
+ return dntShim.Response.json(
444
+ {
445
+ error: error instanceof Error ? error.message : "Internal server error",
446
+ },
447
+ { status: 500 },
448
+ );
449
+ }
450
+ };
451
+ }
@@ -135,6 +135,15 @@ export {
135
135
  } from "./composition/index.js";
136
136
 
137
137
  export { agent } from "./factory.js";
138
+ export {
139
+ type AgUiContextItem,
140
+ type AgUiHandlerConfigWithAgent,
141
+ type AgUiHandlerOptions,
142
+ type AgUiInjectedTool,
143
+ type AgUiRequest,
144
+ AgUiRequestSchema,
145
+ createAgUiHandler,
146
+ } from "./ag-ui-handler.js";
138
147
  export {
139
148
  type ChatHandlerBeforeStream,
140
149
  type ChatHandlerBeforeStreamContext,
@@ -144,4 +153,15 @@ export {
144
153
  type ChatHandlerOptions,
145
154
  createChatHandler,
146
155
  } from "./chat-handler.js";
147
- export { AgentRuntime } from "./runtime/index.js";
156
+ export {
157
+ AgentRuntime,
158
+ RunAlreadyExistsError,
159
+ RunCancelledError,
160
+ RunNotActiveError,
161
+ RunResumeSessionManager,
162
+ type RunResumeSessionManagerOptions,
163
+ type RunSessionStatus,
164
+ type SubmitResumeValueOutcome,
165
+ WaitConflictError,
166
+ WaitNotPendingError,
167
+ } from "./runtime/index.js";
@@ -39,9 +39,23 @@ import { MiddlewareChain } from "../middleware/chain.js";
39
39
  import { generateText, type LanguageModel, streamText } from "ai";
40
40
  import { AGENT_DEFAULTS } from "../ai-defaults.js";
41
41
  import { tryGetCacheKeyContext } from "../../cache/cache-key-builder.js";
42
+ import type { ToolExecutionContext } from "../../tool/index.js";
42
43
 
43
44
  // Re-export from submodules
44
45
  export { closeSSEStream, generateMessageId, sendSSE } from "./sse-utils.js";
46
+ export {
47
+ RunAlreadyExistsError,
48
+ RunCancelledError,
49
+ RunNotActiveError,
50
+ RunResumeSessionManager,
51
+ WaitConflictError,
52
+ WaitNotPendingError,
53
+ } from "./resume-session.js";
54
+ export type {
55
+ RunResumeSessionManagerOptions,
56
+ RunSessionStatus,
57
+ SubmitResumeValueOutcome,
58
+ } from "./resume-session.js";
45
59
  export {
46
60
  executeConfiguredTool,
47
61
  getAvailableTools,
@@ -279,6 +293,11 @@ export class AgentRuntime {
279
293
  this.executeAgentLoop(
280
294
  systemPrompt,
281
295
  messages,
296
+ {
297
+ agentId: this.id,
298
+ projectId: tryGetCacheKeyContext()?.projectId,
299
+ ...context,
300
+ },
282
301
  resolvedModelString,
283
302
  maxOutputTokensOverride,
284
303
  ),
@@ -425,6 +444,7 @@ export class AgentRuntime {
425
444
  private async executeAgentLoop(
426
445
  systemPrompt: string,
427
446
  messages: Message[],
447
+ toolContext?: ToolExecutionContext,
428
448
  modelString?: string,
429
449
  maxOutputTokensOverride?: number,
430
450
  ): Promise<AgentResponse> {
@@ -460,6 +480,8 @@ export class AgentRuntime {
460
480
  let tools = isLocal ? [] : await getAvailableTools(this.config.tools, {
461
481
  includeSkillTools: Boolean(this.config.skills),
462
482
  allowedRemoteToolNames,
483
+ remoteToolSources: this.config.remoteTools,
484
+ remoteToolContext: toolContext,
463
485
  });
464
486
 
465
487
  // Layer 1: Filter tools based on active skill policy (planning-time)
@@ -583,11 +605,12 @@ export class AgentRuntime {
583
605
  toolCall.args,
584
606
  this.config.tools,
585
607
  {
586
- agentId: this.id,
587
608
  toolCallId: tc.toolCallId,
588
- projectId: cacheCtx?.projectId,
609
+ ...toolContext,
610
+ projectId: cacheCtx?.projectId ?? toolContext?.projectId,
589
611
  },
590
612
  allowedRemoteToolNames,
613
+ this.config.remoteTools,
591
614
  );
592
615
 
593
616
  toolCall.status = "completed";
@@ -712,6 +735,8 @@ export class AgentRuntime {
712
735
  let tools = isLocalStreaming ? [] : await getAvailableTools(this.config.tools, {
713
736
  includeSkillTools: Boolean(this.config.skills),
714
737
  allowedRemoteToolNames,
738
+ remoteToolSources: this.config.remoteTools,
739
+ remoteToolContext: toolContext,
715
740
  });
716
741
 
717
742
  // Layer 1: Filter tools based on active skill policy (planning-time)
@@ -873,11 +898,11 @@ export class AgentRuntime {
873
898
  toolCall.args,
874
899
  this.config.tools,
875
900
  {
876
- agentId: this.id,
877
901
  toolCallId: tc.id,
878
902
  ...toolContext,
879
903
  },
880
904
  allowedRemoteToolNames,
905
+ this.config.remoteTools,
881
906
  );
882
907
  throwIfAborted(abortSignal);
883
908