@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.
Files changed (31) hide show
  1. package/dist/acp-server/adapter.js +12 -12
  2. package/dist/acp-server/http.js +1 -1
  3. package/dist/acp-server/session-storage.d.ts +13 -6
  4. package/dist/acp-server/session-storage.js +94 -59
  5. package/dist/runner/agent-runner.d.ts +3 -1
  6. package/dist/runner/hooks/executor.js +1 -1
  7. package/dist/runner/hooks/predefined/compaction-tool.js +31 -8
  8. package/dist/runner/hooks/predefined/tool-response-compactor.js +2 -2
  9. package/dist/runner/langchain/index.d.ts +1 -0
  10. package/dist/runner/langchain/index.js +151 -27
  11. package/dist/runner/langchain/tools/artifacts.d.ts +68 -0
  12. package/dist/runner/langchain/tools/artifacts.js +469 -0
  13. package/dist/runner/langchain/tools/browser.js +15 -3
  14. package/dist/runner/langchain/tools/filesystem.d.ts +8 -4
  15. package/dist/runner/langchain/tools/filesystem.js +118 -82
  16. package/dist/runner/langchain/tools/generate_image.d.ts +19 -0
  17. package/dist/runner/langchain/tools/generate_image.js +54 -14
  18. package/dist/runner/langchain/tools/subagent.js +2 -2
  19. package/dist/runner/langchain/tools/todo.js +3 -0
  20. package/dist/runner/langchain/tools/web_search.js +6 -0
  21. package/dist/runner/session-context.d.ts +40 -0
  22. package/dist/runner/session-context.js +69 -0
  23. package/dist/runner/tools.d.ts +2 -2
  24. package/dist/runner/tools.js +2 -0
  25. package/dist/scaffold/project-scaffold.js +7 -3
  26. package/dist/telemetry/setup.js +1 -1
  27. package/dist/tsconfig.tsbuildinfo +1 -1
  28. package/dist/utils/context-size-calculator.d.ts +1 -2
  29. package/dist/utils/context-size-calculator.js +2 -6
  30. package/dist/utils/token-counter.js +2 -2
  31. 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(process.cwd()),
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
- const wd = t.working_directory ??
250
- process.cwd();
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 so each tool executes within its own span context.
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
- if (block.type === "text") {
536
+ const typedBlock = block;
537
+ if (typedBlock.type === "text") {
493
538
  return {
494
539
  type: "text",
495
- text: block.text,
540
+ text: typedBlock.text,
496
541
  };
497
542
  }
498
- else if (block.type === "image") {
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 block && block.source) {
504
- base64Data = block.source.data;
505
- mediaType = block.source.media_type || "image/png";
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 block && block.data) {
509
- base64Data = block.data;
510
- if (block.mimeType) {
511
- const mt = block.mimeType.toLowerCase();
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(Boolean);
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 = req.contextMessages.map((msg) => ({
544
- type: msg.role === "user" ? "human" : "ai",
545
- content: convertContentBlocks(msg.content),
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
- const toolInputJson = JSON.stringify(input);
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(input));
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>)[];