@townco/agent 0.1.81 → 0.1.83
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/dist/acp-server/adapter.js +12 -12
- package/dist/acp-server/http.js +1 -1
- package/dist/acp-server/session-storage.d.ts +13 -6
- package/dist/acp-server/session-storage.js +94 -59
- package/dist/runner/agent-runner.d.ts +3 -1
- package/dist/runner/hooks/executor.js +1 -1
- package/dist/runner/hooks/predefined/compaction-tool.js +31 -8
- package/dist/runner/hooks/predefined/tool-response-compactor.js +2 -2
- package/dist/runner/langchain/index.d.ts +1 -0
- package/dist/runner/langchain/index.js +151 -27
- package/dist/runner/langchain/tools/artifacts.d.ts +68 -0
- package/dist/runner/langchain/tools/artifacts.js +469 -0
- package/dist/runner/langchain/tools/browser.js +15 -3
- package/dist/runner/langchain/tools/filesystem.d.ts +8 -4
- package/dist/runner/langchain/tools/filesystem.js +118 -82
- package/dist/runner/langchain/tools/generate_image.d.ts +19 -0
- package/dist/runner/langchain/tools/generate_image.js +54 -14
- package/dist/runner/langchain/tools/subagent.js +2 -2
- package/dist/runner/langchain/tools/todo.js +3 -0
- package/dist/runner/langchain/tools/web_search.js +6 -0
- package/dist/runner/session-context.d.ts +40 -0
- package/dist/runner/session-context.js +69 -0
- package/dist/runner/tools.d.ts +2 -2
- package/dist/runner/tools.js +2 -0
- package/dist/scaffold/project-scaffold.js +7 -3
- package/dist/telemetry/setup.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/context-size-calculator.d.ts +1 -2
- package/dist/utils/context-size-calculator.js +2 -6
- package/dist/utils/token-counter.js +2 -2
- package/package.json +10 -10
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { mkdir } from "node:fs/promises";
|
|
2
|
+
import * as path from "node:path";
|
|
1
3
|
import { MultiServerMCPClient } from "@langchain/mcp-adapters";
|
|
2
4
|
import { context, propagation, trace } from "@opentelemetry/api";
|
|
3
5
|
import { getShedAuth } from "@townco/core/auth";
|
|
@@ -6,12 +8,14 @@ import { z } from "zod";
|
|
|
6
8
|
import { SUBAGENT_MODE_KEY } from "../../acp-server/adapter";
|
|
7
9
|
import { createLogger } from "../../logger.js";
|
|
8
10
|
import { telemetry } from "../../telemetry/index.js";
|
|
11
|
+
import { bindGeneratorToSessionContext } from "../session-context";
|
|
9
12
|
import { loadCustomToolModule, } from "../tool-loader.js";
|
|
10
13
|
import { createModelFromString, detectProvider } from "./model-factory.js";
|
|
11
14
|
import { makeOtelCallbacks } from "./otel-callbacks.js";
|
|
15
|
+
import { makeArtifactsTools } from "./tools/artifacts";
|
|
12
16
|
import { makeBrowserTools } from "./tools/browser";
|
|
13
17
|
import { makeFilesystemTools } from "./tools/filesystem";
|
|
14
|
-
import { makeGenerateImageTool } from "./tools/generate_image";
|
|
18
|
+
import { makeGenerateImageTool, makeTownGenerateImageTool, } from "./tools/generate_image";
|
|
15
19
|
import { SUBAGENT_TOOL_NAME } from "./tools/subagent";
|
|
16
20
|
import { hashQuery, queryToToolCallId, subagentEvents, } from "./tools/subagent-connections";
|
|
17
21
|
import { makeTodoWriteTool, TODO_WRITE_TOOL_NAME } from "./tools/todo";
|
|
@@ -24,15 +28,19 @@ const getWeather = tool(({ city }) => `It's always sunny in ${city}!`, {
|
|
|
24
28
|
city: z.string(),
|
|
25
29
|
}),
|
|
26
30
|
});
|
|
31
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
27
32
|
getWeather.prettyName = "Get Weather";
|
|
33
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
28
34
|
getWeather.icon = "Cloud";
|
|
29
35
|
export const TOOL_REGISTRY = {
|
|
36
|
+
artifacts: () => makeArtifactsTools(),
|
|
30
37
|
todo_write: () => makeTodoWriteTool(), // Factory function to create fresh instance per invocation
|
|
31
38
|
get_weather: getWeather, // TODO: Convert to factory function for full concurrency safety
|
|
32
39
|
web_search: () => makeWebSearchTools(),
|
|
33
40
|
town_web_search: () => makeTownWebSearchTools(),
|
|
34
|
-
filesystem: () => makeFilesystemTools(
|
|
41
|
+
filesystem: () => makeFilesystemTools(),
|
|
35
42
|
generate_image: () => makeGenerateImageTool(),
|
|
43
|
+
town_generate_image: () => makeTownGenerateImageTool(),
|
|
36
44
|
browser: () => makeBrowserTools(),
|
|
37
45
|
};
|
|
38
46
|
// ============================================================================
|
|
@@ -44,7 +52,9 @@ function toLangchainTool(resolved) {
|
|
|
44
52
|
description: resolved.description,
|
|
45
53
|
schema: resolved.schema,
|
|
46
54
|
});
|
|
55
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
47
56
|
t.prettyName = resolved.prettyName;
|
|
57
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
48
58
|
t.icon = resolved.icon;
|
|
49
59
|
return t;
|
|
50
60
|
}
|
|
@@ -70,6 +80,26 @@ export class LangchainAgent {
|
|
|
70
80
|
return telemetry.bindGeneratorToContext(sessionAttributes, generator);
|
|
71
81
|
}
|
|
72
82
|
async *invokeInternal(req) {
|
|
83
|
+
// Set up session context for session-scoped file storage
|
|
84
|
+
// This allows tools to access getSessionContext() / getToolOutputDir()
|
|
85
|
+
if (req.agentDir && req.sessionId) {
|
|
86
|
+
const sessionDir = path.join(req.agentDir, ".sessions", req.sessionId);
|
|
87
|
+
const artifactsDir = path.join(sessionDir, "artifacts");
|
|
88
|
+
// Ensure artifacts directory exists
|
|
89
|
+
await mkdir(artifactsDir, { recursive: true });
|
|
90
|
+
// Bind the generator to session context so every iteration has access
|
|
91
|
+
const sessionContext = {
|
|
92
|
+
sessionId: req.sessionId,
|
|
93
|
+
sessionDir,
|
|
94
|
+
artifactsDir,
|
|
95
|
+
};
|
|
96
|
+
const boundGenerator = bindGeneratorToSessionContext(sessionContext, this.invokeWithContext(req));
|
|
97
|
+
return yield* boundGenerator;
|
|
98
|
+
}
|
|
99
|
+
// Fallback: no session context (e.g., missing agentDir)
|
|
100
|
+
return yield* this.invokeWithContext(req);
|
|
101
|
+
}
|
|
102
|
+
async *invokeWithContext(req) {
|
|
73
103
|
// Derive the parent OTEL context for this invocation.
|
|
74
104
|
// If this is a subagent and the parent process propagated an OTEL trace
|
|
75
105
|
// context via sessionMeta.otelTraceContext, use that as the parent;
|
|
@@ -136,6 +166,7 @@ export class LangchainAgent {
|
|
|
136
166
|
// Helper to get next subagent update (returns immediately if queued, otherwise waits)
|
|
137
167
|
const waitForSubagentUpdate = () => {
|
|
138
168
|
if (subagentUpdateQueue.length > 0) {
|
|
169
|
+
// biome-ignore lint/style/noNonNullAssertion: We check length > 0, so shift() will return a value
|
|
139
170
|
return Promise.resolve(subagentUpdateQueue.shift());
|
|
140
171
|
}
|
|
141
172
|
return new Promise((resolve) => {
|
|
@@ -146,6 +177,8 @@ export class LangchainAgent {
|
|
|
146
177
|
async function* yieldPendingSubagentUpdates() {
|
|
147
178
|
while (subagentUpdateQueue.length > 0) {
|
|
148
179
|
const update = subagentUpdateQueue.shift();
|
|
180
|
+
if (!update)
|
|
181
|
+
continue;
|
|
149
182
|
_logger.info("Yielding queued subagent connection update", {
|
|
150
183
|
toolCallId: update.toolCallId,
|
|
151
184
|
subagentPort: update.port,
|
|
@@ -164,6 +197,8 @@ export class LangchainAgent {
|
|
|
164
197
|
// Also yield any pending messages updates
|
|
165
198
|
while (subagentMessagesQueue.length > 0) {
|
|
166
199
|
const messagesUpdate = subagentMessagesQueue.shift();
|
|
200
|
+
if (!messagesUpdate)
|
|
201
|
+
continue;
|
|
167
202
|
_logger.info("Yielding queued subagent messages update", {
|
|
168
203
|
toolCallId: messagesUpdate.toolCallId,
|
|
169
204
|
messageCount: messagesUpdate.messages.length,
|
|
@@ -246,11 +281,11 @@ export class LangchainAgent {
|
|
|
246
281
|
customToolPaths.push(t.modulePath);
|
|
247
282
|
}
|
|
248
283
|
else if (type === "filesystem") {
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
const fsTools = makeFilesystemTools(wd);
|
|
284
|
+
// Note: working_directory is ignored - filesystem tools now use session context
|
|
285
|
+
const fsTools = makeFilesystemTools();
|
|
252
286
|
// Tag filesystem tools with their group name for tool override filtering
|
|
253
287
|
for (const fsTool of fsTools) {
|
|
288
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom property for tool grouping
|
|
254
289
|
fsTool.__groupName = "filesystem";
|
|
255
290
|
}
|
|
256
291
|
enabledTools.push(...fsTools);
|
|
@@ -263,7 +298,9 @@ export class LangchainAgent {
|
|
|
263
298
|
description: t.description,
|
|
264
299
|
schema: t.schema,
|
|
265
300
|
});
|
|
301
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
266
302
|
addedTool.prettyName = t.prettyName;
|
|
303
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
267
304
|
addedTool.icon = t.icon;
|
|
268
305
|
enabledTools.push(addedTool);
|
|
269
306
|
}
|
|
@@ -279,6 +316,7 @@ export class LangchainAgent {
|
|
|
279
316
|
// Track which built-in group produced this tool so overrides can
|
|
280
317
|
// filter by the original config name (e.g. "web_search" filters
|
|
281
318
|
// both WebSearch and WebFetch helpers).
|
|
319
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom property for tool grouping
|
|
282
320
|
tool.__groupName = name;
|
|
283
321
|
};
|
|
284
322
|
if (typeof entry === "function") {
|
|
@@ -418,13 +456,17 @@ export class LangchainAgent {
|
|
|
418
456
|
return result;
|
|
419
457
|
};
|
|
420
458
|
// Create new tool with wrapped function
|
|
459
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to pass function with dynamic signature
|
|
421
460
|
const wrappedTool = tool(wrappedFunc, {
|
|
422
461
|
name: originalTool.name,
|
|
423
462
|
description: originalTool.description,
|
|
463
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing internal schema property
|
|
424
464
|
schema: originalTool.schema,
|
|
425
465
|
});
|
|
426
466
|
// Preserve metadata
|
|
467
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
427
468
|
wrappedTool.prettyName = originalTool.prettyName;
|
|
469
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to add custom properties to LangChain tool
|
|
428
470
|
wrappedTool.icon = originalTool.icon;
|
|
429
471
|
return wrappedTool;
|
|
430
472
|
});
|
|
@@ -434,15 +476,16 @@ export class LangchainAgent {
|
|
|
434
476
|
const filteredTools = isSubagent
|
|
435
477
|
? wrappedTools.filter((t) => t.name !== TODO_WRITE_TOOL_NAME && t.name !== SUBAGENT_TOOL_NAME)
|
|
436
478
|
: wrappedTools;
|
|
437
|
-
// Wrap tools with tracing
|
|
479
|
+
// Wrap tools with tracing and session_id injection (combined in one wrapper)
|
|
438
480
|
// This ensures subagent spans are children of the Task tool span.
|
|
439
481
|
// Pass the context getter so tools can nest under the current iteration span.
|
|
440
482
|
let finalTools = filteredTools.map((t) => wrapToolWithTracing(t, otelCallbacks?.getCurrentIterationContext ??
|
|
441
|
-
(() => invocationContext)));
|
|
483
|
+
(() => invocationContext), req.sessionId));
|
|
442
484
|
// Apply tool overrides if provided (Town Hall comparison feature)
|
|
443
485
|
if (req.configOverrides?.tools && req.configOverrides.tools.length > 0) {
|
|
444
486
|
const allowedToolNames = new Set(req.configOverrides.tools);
|
|
445
487
|
finalTools = finalTools.filter((t) => {
|
|
488
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing custom property for tool grouping
|
|
446
489
|
const groupName = t.__groupName;
|
|
447
490
|
return (allowedToolNames.has(groupName ?? "") ||
|
|
448
491
|
allowedToolNames.has(t.name));
|
|
@@ -472,6 +515,7 @@ export class LangchainAgent {
|
|
|
472
515
|
}
|
|
473
516
|
const agent = createAgent(agentConfig);
|
|
474
517
|
// Build messages from context history if available, otherwise use just the prompt
|
|
518
|
+
// Type includes tool messages for sending tool results
|
|
475
519
|
let messages;
|
|
476
520
|
// Helper to convert content blocks to LangChain format
|
|
477
521
|
// LangChain expects image_url type with data URL, not Claude's native image+source format
|
|
@@ -489,26 +533,28 @@ export class LangchainAgent {
|
|
|
489
533
|
// LangChain uses image_url type with data URL format
|
|
490
534
|
return blocks
|
|
491
535
|
.map((block) => {
|
|
492
|
-
|
|
536
|
+
const typedBlock = block;
|
|
537
|
+
if (typedBlock.type === "text") {
|
|
493
538
|
return {
|
|
494
539
|
type: "text",
|
|
495
|
-
text:
|
|
540
|
+
text: typedBlock.text,
|
|
496
541
|
};
|
|
497
542
|
}
|
|
498
|
-
else if (
|
|
543
|
+
else if (typedBlock.type === "image") {
|
|
499
544
|
// Extract base64 data and media type from various formats
|
|
500
545
|
let base64Data;
|
|
501
546
|
let mediaType = "image/png";
|
|
502
547
|
// Check if it has the source format (Claude API format)
|
|
503
|
-
if ("source" in
|
|
504
|
-
|
|
505
|
-
|
|
548
|
+
if ("source" in typedBlock && typedBlock.source) {
|
|
549
|
+
const source = typedBlock.source;
|
|
550
|
+
base64Data = source.data;
|
|
551
|
+
mediaType = source.media_type || "image/png";
|
|
506
552
|
}
|
|
507
553
|
// ACP format: { type: "image", data: "...", mimeType: "..." }
|
|
508
|
-
else if ("data" in
|
|
509
|
-
base64Data =
|
|
510
|
-
if (
|
|
511
|
-
const mt =
|
|
554
|
+
else if ("data" in typedBlock && typedBlock.data) {
|
|
555
|
+
base64Data = typedBlock.data;
|
|
556
|
+
if (typedBlock.mimeType) {
|
|
557
|
+
const mt = typedBlock.mimeType.toLowerCase();
|
|
512
558
|
if (mt === "image/jpeg" || mt === "image/jpg") {
|
|
513
559
|
mediaType = "image/jpeg";
|
|
514
560
|
}
|
|
@@ -535,15 +581,66 @@ export class LangchainAgent {
|
|
|
535
581
|
}
|
|
536
582
|
return null;
|
|
537
583
|
})
|
|
538
|
-
.filter(
|
|
584
|
+
.filter((item) => item !== null);
|
|
539
585
|
};
|
|
540
586
|
if (req.contextMessages && req.contextMessages.length > 0) {
|
|
541
587
|
// Use context messages (already resolved from context entries)
|
|
542
|
-
// Convert to LangChain format
|
|
543
|
-
messages =
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
588
|
+
// Convert to LangChain format, including tool calls and their results
|
|
589
|
+
messages = [];
|
|
590
|
+
for (const msg of req.contextMessages) {
|
|
591
|
+
if (msg.role === "user") {
|
|
592
|
+
messages.push({
|
|
593
|
+
type: "human",
|
|
594
|
+
content: convertContentBlocks(msg.content),
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
else if (msg.role === "assistant") {
|
|
598
|
+
// Check if message has tool calls
|
|
599
|
+
const toolCalls = msg.content.filter((block) => block.type === "tool_call");
|
|
600
|
+
const textBlocks = msg.content.filter((block) => block.type === "text" || block.type === "image");
|
|
601
|
+
if (toolCalls.length > 0) {
|
|
602
|
+
// Build AI message with tool_use blocks
|
|
603
|
+
const aiContent = [];
|
|
604
|
+
// Add any text content first
|
|
605
|
+
for (const block of textBlocks) {
|
|
606
|
+
if (block.type === "text") {
|
|
607
|
+
aiContent.push({ type: "text", text: block.text });
|
|
608
|
+
}
|
|
609
|
+
}
|
|
610
|
+
// Add tool_use blocks
|
|
611
|
+
for (const tc of toolCalls) {
|
|
612
|
+
if (tc.type === "tool_call") {
|
|
613
|
+
aiContent.push({
|
|
614
|
+
type: "tool_use",
|
|
615
|
+
id: tc.id,
|
|
616
|
+
name: tc.title,
|
|
617
|
+
input: tc.rawInput || {},
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
messages.push({ type: "ai", content: aiContent });
|
|
622
|
+
// Add tool result messages for each tool call that has output
|
|
623
|
+
for (const tc of toolCalls) {
|
|
624
|
+
if (tc.type === "tool_call" && tc.rawOutput) {
|
|
625
|
+
messages.push({
|
|
626
|
+
type: "tool",
|
|
627
|
+
tool_call_id: tc.id,
|
|
628
|
+
content: typeof tc.rawOutput === "string"
|
|
629
|
+
? tc.rawOutput
|
|
630
|
+
: JSON.stringify(tc.rawOutput),
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
else {
|
|
636
|
+
// No tool calls - simple AI message
|
|
637
|
+
messages.push({
|
|
638
|
+
type: "ai",
|
|
639
|
+
content: convertContentBlocks(msg.content),
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
547
644
|
// Add the current prompt as the final human message
|
|
548
645
|
const promptContent = convertContentBlocks(req.prompt);
|
|
549
646
|
messages.push({
|
|
@@ -615,6 +712,7 @@ export class LangchainAgent {
|
|
|
615
712
|
}
|
|
616
713
|
// Also yield any queued subagent updates before processing stream item
|
|
617
714
|
yield* yieldPendingSubagentUpdates();
|
|
715
|
+
// biome-ignore lint/suspicious/noExplicitAny: LangChain stream items are tuples with dynamic types
|
|
618
716
|
const [streamMode, chunk] = streamItem;
|
|
619
717
|
if (streamMode === "updates") {
|
|
620
718
|
const updatesChunk = modelRequestSchema.safeParse(chunk);
|
|
@@ -689,8 +787,11 @@ export class LangchainAgent {
|
|
|
689
787
|
// continue;
|
|
690
788
|
//}
|
|
691
789
|
const matchingTool = finalTools.find((t) => t.name === toolCall.name);
|
|
790
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing custom property on LangChain tool
|
|
692
791
|
let prettyName = matchingTool?.prettyName;
|
|
792
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing custom property on LangChain tool
|
|
693
793
|
const icon = matchingTool?.icon;
|
|
794
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing custom property on LangChain tool
|
|
694
795
|
const verbiage = matchingTool?.verbiage;
|
|
695
796
|
// For the Task tool, use the displayName (or agentName as fallback) as the prettyName
|
|
696
797
|
if (toolCall.name === SUBAGENT_TOOL_NAME &&
|
|
@@ -704,6 +805,7 @@ export class LangchainAgent {
|
|
|
704
805
|
const taskTool = this.definition.tools?.find((t) => typeof t === "object" &&
|
|
705
806
|
t.type === "direct" &&
|
|
706
807
|
t.name === SUBAGENT_TOOL_NAME);
|
|
808
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing custom property on tool definition
|
|
707
809
|
const subagentConfigs = taskTool?.subagentConfigs;
|
|
708
810
|
const subagentConfig = subagentConfigs?.find((config) => config.agentName === agentName);
|
|
709
811
|
prettyName = subagentConfig?.displayName ?? agentName;
|
|
@@ -1120,16 +1222,33 @@ I've found some existing telemetry code. Let me mark the first todo as in_progre
|
|
|
1120
1222
|
// Re-export subagent tool utility
|
|
1121
1223
|
export { makeSubagentsTool } from "./tools/subagent.js";
|
|
1122
1224
|
/**
|
|
1123
|
-
* Wraps a LangChain tool with OpenTelemetry tracing.
|
|
1225
|
+
* Wraps a LangChain tool with OpenTelemetry tracing and session_id injection.
|
|
1124
1226
|
* This ensures the tool executes within its own span context,
|
|
1125
1227
|
* so any child operations (like subagent spawning) become children
|
|
1126
1228
|
* of the tool span rather than the parent invocation span.
|
|
1127
1229
|
* @param originalTool The tool to wrap
|
|
1128
1230
|
* @param getIterationContext Function that returns the current iteration context
|
|
1231
|
+
* @param sessionId Optional session ID to inject for artifact tools
|
|
1129
1232
|
*/
|
|
1130
|
-
function wrapToolWithTracing(originalTool, getIterationContext) {
|
|
1233
|
+
function wrapToolWithTracing(originalTool, getIterationContext, sessionId) {
|
|
1234
|
+
// Check if this tool needs session_id injection
|
|
1235
|
+
const TOOLS_NEEDING_SESSION_ID = [
|
|
1236
|
+
"artifacts_cp",
|
|
1237
|
+
"artifacts_del",
|
|
1238
|
+
"artifacts_ls",
|
|
1239
|
+
"artifacts_url",
|
|
1240
|
+
];
|
|
1241
|
+
const needsSessionId = TOOLS_NEEDING_SESSION_ID.includes(originalTool.name);
|
|
1131
1242
|
const wrappedFunc = async (input) => {
|
|
1132
|
-
|
|
1243
|
+
// Inject session_id if needed
|
|
1244
|
+
let finalInput = input;
|
|
1245
|
+
if (needsSessionId && sessionId) {
|
|
1246
|
+
finalInput = {
|
|
1247
|
+
...input,
|
|
1248
|
+
session_id: sessionId,
|
|
1249
|
+
};
|
|
1250
|
+
}
|
|
1251
|
+
const toolInputJson = JSON.stringify(finalInput);
|
|
1133
1252
|
// CRITICAL: Get the iteration context synchronously when the tool is invoked.
|
|
1134
1253
|
// We must capture both the context AND the parent span at this moment.
|
|
1135
1254
|
const iterationContext = getIterationContext();
|
|
@@ -1151,7 +1270,7 @@ function wrapToolWithTracing(originalTool, getIterationContext) {
|
|
|
1151
1270
|
: iterationContext;
|
|
1152
1271
|
try {
|
|
1153
1272
|
// Execute within the tool span's context
|
|
1154
|
-
const result = await context.with(spanContext, () => originalTool.invoke(
|
|
1273
|
+
const result = await context.with(spanContext, () => originalTool.invoke(finalInput));
|
|
1155
1274
|
const resultStr = typeof result === "string" ? result : JSON.stringify(result);
|
|
1156
1275
|
if (toolSpan) {
|
|
1157
1276
|
telemetry.setSpanAttributes(toolSpan, {
|
|
@@ -1170,14 +1289,19 @@ function wrapToolWithTracing(originalTool, getIterationContext) {
|
|
|
1170
1289
|
});
|
|
1171
1290
|
};
|
|
1172
1291
|
// Create new tool with wrapped function
|
|
1292
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to pass function with dynamic signature
|
|
1173
1293
|
const wrappedTool = tool(wrappedFunc, {
|
|
1174
1294
|
name: originalTool.name,
|
|
1175
1295
|
description: originalTool.description,
|
|
1296
|
+
// biome-ignore lint/suspicious/noExplicitAny: Accessing internal schema property
|
|
1176
1297
|
schema: originalTool.schema,
|
|
1177
1298
|
});
|
|
1178
1299
|
// Preserve metadata
|
|
1300
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to copy custom properties between LangChain tools
|
|
1179
1301
|
wrappedTool.prettyName = originalTool.prettyName;
|
|
1302
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to copy custom properties between LangChain tools
|
|
1180
1303
|
wrappedTool.icon = originalTool.icon;
|
|
1304
|
+
// biome-ignore lint/suspicious/noExplicitAny: Need to copy custom properties between LangChain tools
|
|
1181
1305
|
wrappedTool.__groupName = originalTool.__groupName;
|
|
1182
1306
|
return wrappedTool;
|
|
1183
1307
|
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supabase Storage-backed artifacts tool for agent backend
|
|
3
|
+
*
|
|
4
|
+
* Provides file storage capabilities using Supabase Storage with the following operations:
|
|
5
|
+
* - artifacts_cp: Copy files to/from Supabase Storage
|
|
6
|
+
* - artifacts_del: Delete files from Supabase Storage
|
|
7
|
+
* - artifacts_ls: List files in Supabase Storage
|
|
8
|
+
* - artifacts_url: Generate signed URLs
|
|
9
|
+
*
|
|
10
|
+
* Storage keys are scoped by: <deploying_user>/<agent_name>/<session_id>/<file_path>
|
|
11
|
+
*/
|
|
12
|
+
import { z } from "zod";
|
|
13
|
+
/**
|
|
14
|
+
* Factory function to create the artifacts tools
|
|
15
|
+
* Returns an array of all four artifact management tools
|
|
16
|
+
*/
|
|
17
|
+
export declare function makeArtifactsTools(): (import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
18
|
+
session_id: z.ZodOptional<z.ZodString>;
|
|
19
|
+
source: z.ZodString;
|
|
20
|
+
destination: z.ZodString;
|
|
21
|
+
direction: z.ZodEnum<{
|
|
22
|
+
upload: "upload";
|
|
23
|
+
download: "download";
|
|
24
|
+
}>;
|
|
25
|
+
}, z.core.$strip>, {
|
|
26
|
+
session_id: string;
|
|
27
|
+
source: string;
|
|
28
|
+
destination: string;
|
|
29
|
+
direction: "upload" | "download";
|
|
30
|
+
}, {
|
|
31
|
+
source: string;
|
|
32
|
+
destination: string;
|
|
33
|
+
direction: "upload" | "download";
|
|
34
|
+
session_id?: string | undefined;
|
|
35
|
+
}, string> | import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
36
|
+
session_id: z.ZodOptional<z.ZodString>;
|
|
37
|
+
path: z.ZodString;
|
|
38
|
+
}, z.core.$strip>, {
|
|
39
|
+
session_id: string;
|
|
40
|
+
path: string;
|
|
41
|
+
}, {
|
|
42
|
+
path: string;
|
|
43
|
+
session_id?: string | undefined;
|
|
44
|
+
}, string> | import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
45
|
+
session_id: z.ZodOptional<z.ZodString>;
|
|
46
|
+
path: z.ZodOptional<z.ZodString>;
|
|
47
|
+
recursive: z.ZodOptional<z.ZodBoolean>;
|
|
48
|
+
}, z.core.$strip>, {
|
|
49
|
+
session_id: string;
|
|
50
|
+
path?: string;
|
|
51
|
+
recursive?: boolean;
|
|
52
|
+
}, {
|
|
53
|
+
session_id?: string | undefined;
|
|
54
|
+
path?: string | undefined;
|
|
55
|
+
recursive?: boolean | undefined;
|
|
56
|
+
}, string> | import("langchain").DynamicStructuredTool<z.ZodObject<{
|
|
57
|
+
session_id: z.ZodOptional<z.ZodString>;
|
|
58
|
+
path: z.ZodString;
|
|
59
|
+
expires_in: z.ZodOptional<z.ZodNumber>;
|
|
60
|
+
}, z.core.$strip>, {
|
|
61
|
+
session_id: string;
|
|
62
|
+
path: string;
|
|
63
|
+
expires_in?: number;
|
|
64
|
+
}, {
|
|
65
|
+
path: string;
|
|
66
|
+
session_id?: string | undefined;
|
|
67
|
+
expires_in?: number | undefined;
|
|
68
|
+
}, string>)[];
|