illuma-agents 1.0.36 → 1.0.38
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/cjs/agents/AgentContext.cjs +69 -14
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/enum.cjs +3 -1
- package/dist/cjs/common/enum.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +50 -8
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/MultiAgentGraph.cjs +277 -11
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/llm/bedrock/index.cjs +128 -61
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +16 -7
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/messages/cache.cjs +1 -0
- package/dist/cjs/messages/cache.cjs.map +1 -1
- package/dist/cjs/messages/core.cjs +1 -1
- package/dist/cjs/messages/core.cjs.map +1 -1
- package/dist/cjs/messages/tools.cjs +2 -2
- package/dist/cjs/messages/tools.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -2
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/BrowserTools.cjs +27 -3
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
- package/dist/cjs/tools/CodeExecutor.cjs +22 -21
- package/dist/cjs/tools/CodeExecutor.cjs.map +1 -1
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs +14 -11
- package/dist/cjs/tools/ProgrammaticToolCalling.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +101 -2
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/ToolSearch.cjs +862 -0
- package/dist/cjs/tools/ToolSearch.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +69 -14
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/enum.mjs +3 -1
- package/dist/esm/common/enum.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +51 -9
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/MultiAgentGraph.mjs +278 -12
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/llm/bedrock/index.mjs +127 -60
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +1 -1
- package/dist/esm/messages/cache.mjs +1 -0
- package/dist/esm/messages/cache.mjs.map +1 -1
- package/dist/esm/messages/core.mjs +1 -1
- package/dist/esm/messages/core.mjs.map +1 -1
- package/dist/esm/messages/tools.mjs +2 -2
- package/dist/esm/messages/tools.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -2
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/BrowserTools.mjs +27 -3
- package/dist/esm/tools/BrowserTools.mjs.map +1 -1
- package/dist/esm/tools/CodeExecutor.mjs +22 -21
- package/dist/esm/tools/CodeExecutor.mjs.map +1 -1
- package/dist/esm/tools/ProgrammaticToolCalling.mjs +14 -11
- package/dist/esm/tools/ProgrammaticToolCalling.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +102 -3
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/ToolSearch.mjs +827 -0
- package/dist/esm/tools/ToolSearch.mjs.map +1 -0
- package/dist/types/agents/AgentContext.d.ts +33 -1
- package/dist/types/common/enum.d.ts +4 -2
- package/dist/types/graphs/Graph.d.ts +6 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +16 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/llm/bedrock/index.d.ts +89 -11
- package/dist/types/llm/bedrock/types.d.ts +27 -0
- package/dist/types/llm/bedrock/utils/index.d.ts +5 -0
- package/dist/types/llm/bedrock/utils/message_inputs.d.ts +31 -0
- package/dist/types/llm/bedrock/utils/message_outputs.d.ts +33 -0
- package/dist/types/tools/BrowserTools.d.ts +2 -0
- package/dist/types/tools/CodeExecutor.d.ts +0 -3
- package/dist/types/tools/ProgrammaticToolCalling.d.ts +0 -3
- package/dist/types/tools/ToolNode.d.ts +3 -1
- package/dist/types/tools/ToolSearch.d.ts +148 -0
- package/dist/types/types/graph.d.ts +2 -0
- package/dist/types/types/llm.d.ts +3 -1
- package/dist/types/types/tools.d.ts +42 -2
- package/package.json +12 -5
- package/src/agents/AgentContext.ts +88 -16
- package/src/common/enum.ts +3 -1
- package/src/graphs/Graph.ts +64 -13
- package/src/graphs/MultiAgentGraph.ts +350 -13
- package/src/index.ts +1 -1
- package/src/llm/bedrock/index.ts +221 -99
- package/src/llm/bedrock/llm.spec.ts +616 -0
- package/src/llm/bedrock/types.ts +51 -0
- package/src/llm/bedrock/utils/index.ts +18 -0
- package/src/llm/bedrock/utils/message_inputs.ts +563 -0
- package/src/llm/bedrock/utils/message_outputs.ts +310 -0
- package/src/messages/__tests__/tools.test.ts +21 -21
- package/src/messages/cache.test.ts +259 -0
- package/src/messages/cache.ts +104 -1
- package/src/messages/core.ts +1 -1
- package/src/messages/tools.ts +2 -2
- package/src/scripts/caching.ts +27 -19
- package/src/scripts/code_exec_files.ts +58 -15
- package/src/scripts/code_exec_multi_session.ts +241 -0
- package/src/scripts/code_exec_session.ts +282 -0
- package/src/scripts/multi-agent-conditional.ts +1 -0
- package/src/scripts/multi-agent-supervisor.ts +1 -0
- package/src/scripts/programmatic_exec_agent.ts +4 -4
- package/src/scripts/test-handoff-preamble.ts +277 -0
- package/src/scripts/test-parallel-handoffs.ts +291 -0
- package/src/scripts/test-tools-before-handoff.ts +8 -4
- package/src/scripts/test_code_api.ts +361 -0
- package/src/scripts/thinking-bedrock.ts +159 -0
- package/src/scripts/thinking.ts +39 -18
- package/src/scripts/{tool_search_regex.ts → tool_search.ts} +5 -5
- package/src/scripts/tools.ts +7 -3
- package/src/stream.ts +4 -2
- package/src/tools/BrowserTools.ts +68 -14
- package/src/tools/CodeExecutor.ts +26 -23
- package/src/tools/ProgrammaticToolCalling.ts +18 -14
- package/src/tools/ToolNode.ts +114 -1
- package/src/tools/ToolSearch.ts +1041 -0
- package/src/tools/__tests__/ProgrammaticToolCalling.test.ts +0 -2
- package/src/tools/__tests__/{ToolSearchRegex.integration.test.ts → ToolSearch.integration.test.ts} +6 -6
- package/src/tools/__tests__/ToolSearch.test.ts +1003 -0
- package/src/types/graph.ts +2 -0
- package/src/types/llm.ts +3 -1
- package/src/types/tools.ts +51 -2
- package/dist/cjs/tools/ToolSearchRegex.cjs +0 -455
- package/dist/cjs/tools/ToolSearchRegex.cjs.map +0 -1
- package/dist/esm/tools/ToolSearchRegex.mjs +0 -448
- package/dist/esm/tools/ToolSearchRegex.mjs.map +0 -1
- package/dist/types/tools/ToolSearchRegex.d.ts +0 -80
- package/src/tools/ToolSearchRegex.ts +0 -535
- package/src/tools/__tests__/ToolSearchRegex.test.ts +0 -232
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { DynamicStructuredTool } from '@langchain/core/tools';
|
|
3
|
+
import type * as t from '@/types';
|
|
4
|
+
/** Zod schema type for tool search parameters */
|
|
5
|
+
type ToolSearchSchema = z.ZodObject<{
|
|
6
|
+
query: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
7
|
+
fields: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodEnum<['name', 'description', 'parameters']>>>>;
|
|
8
|
+
max_results: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
9
|
+
mcp_server: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString>]>>;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Creates the Zod schema with dynamic query description based on mode.
|
|
13
|
+
* @param mode - The search mode determining query interpretation
|
|
14
|
+
* @returns Zod schema for tool search parameters
|
|
15
|
+
*/
|
|
16
|
+
declare function createToolSearchSchema(mode: t.ToolSearchMode): ToolSearchSchema;
|
|
17
|
+
/**
|
|
18
|
+
* Extracts the MCP server name from a tool name.
|
|
19
|
+
* MCP tools follow the pattern: toolName_mcp_serverName
|
|
20
|
+
* @param toolName - The full tool name
|
|
21
|
+
* @returns The server name if it's an MCP tool, undefined otherwise
|
|
22
|
+
*/
|
|
23
|
+
declare function extractMcpServerName(toolName: string): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Checks if a tool belongs to a specific MCP server.
|
|
26
|
+
* @param toolName - The full tool name
|
|
27
|
+
* @param serverName - The server name to match
|
|
28
|
+
* @returns True if the tool belongs to the specified server
|
|
29
|
+
*/
|
|
30
|
+
declare function isFromMcpServer(toolName: string, serverName: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Checks if a tool belongs to any of the specified MCP servers.
|
|
33
|
+
* @param toolName - The full tool name
|
|
34
|
+
* @param serverNames - Array of server names to match
|
|
35
|
+
* @returns True if the tool belongs to any of the specified servers
|
|
36
|
+
*/
|
|
37
|
+
declare function isFromAnyMcpServer(toolName: string, serverNames: string[]): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Normalizes server filter input to always be an array.
|
|
40
|
+
* @param serverFilter - String, array of strings, or undefined
|
|
41
|
+
* @returns Array of server names (empty if none specified)
|
|
42
|
+
*/
|
|
43
|
+
declare function normalizeServerFilter(serverFilter: string | string[] | undefined): string[];
|
|
44
|
+
/**
|
|
45
|
+
* Extracts all unique MCP server names from a tool registry.
|
|
46
|
+
* @param toolRegistry - The tool registry to scan
|
|
47
|
+
* @param onlyDeferred - If true, only considers deferred tools
|
|
48
|
+
* @returns Array of unique server names, sorted alphabetically
|
|
49
|
+
*/
|
|
50
|
+
declare function getAvailableMcpServers(toolRegistry: t.LCToolRegistry | undefined, onlyDeferred?: boolean): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Escapes special regex characters in a string to use as a literal pattern.
|
|
53
|
+
* @param pattern - The string to escape
|
|
54
|
+
* @returns The escaped string safe for use in a RegExp
|
|
55
|
+
*/
|
|
56
|
+
declare function escapeRegexSpecialChars(pattern: string): string;
|
|
57
|
+
/**
|
|
58
|
+
* Counts the maximum nesting depth of groups in a regex pattern.
|
|
59
|
+
* @param pattern - The regex pattern to analyze
|
|
60
|
+
* @returns The maximum nesting depth
|
|
61
|
+
*/
|
|
62
|
+
declare function countNestedGroups(pattern: string): number;
|
|
63
|
+
/**
|
|
64
|
+
* Detects nested quantifiers that can cause catastrophic backtracking.
|
|
65
|
+
* Patterns like (a+)+, (a*)*, (a+)*, etc.
|
|
66
|
+
* @param pattern - The regex pattern to check
|
|
67
|
+
* @returns True if nested quantifiers are detected
|
|
68
|
+
*/
|
|
69
|
+
declare function hasNestedQuantifiers(pattern: string): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Checks if a regex pattern contains potentially dangerous constructs.
|
|
72
|
+
* @param pattern - The regex pattern to validate
|
|
73
|
+
* @returns True if the pattern is dangerous
|
|
74
|
+
*/
|
|
75
|
+
declare function isDangerousPattern(pattern: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Sanitizes a regex pattern for safe execution.
|
|
78
|
+
* If the pattern is dangerous, it will be escaped to a literal string search.
|
|
79
|
+
* @param pattern - The regex pattern to sanitize
|
|
80
|
+
* @returns Object containing the safe pattern and whether it was escaped
|
|
81
|
+
*/
|
|
82
|
+
declare function sanitizeRegex(pattern: string): {
|
|
83
|
+
safe: string;
|
|
84
|
+
wasEscaped: boolean;
|
|
85
|
+
};
|
|
86
|
+
/**
|
|
87
|
+
* Performs BM25-based search for better relevance ranking.
|
|
88
|
+
* Uses Okapi BM25 algorithm for term frequency and document length normalization.
|
|
89
|
+
* @param tools - Array of tool metadata to search
|
|
90
|
+
* @param query - The search query
|
|
91
|
+
* @param fields - Which fields to search
|
|
92
|
+
* @param maxResults - Maximum results to return
|
|
93
|
+
* @returns Search response with matching tools ranked by BM25 score
|
|
94
|
+
*/
|
|
95
|
+
declare function performLocalSearch(tools: t.ToolMetadata[], query: string, fields: string[], maxResults: number): t.ToolSearchResponse;
|
|
96
|
+
/**
|
|
97
|
+
* Extracts the base tool name (without MCP server suffix) from a full tool name.
|
|
98
|
+
* @param toolName - The full tool name
|
|
99
|
+
* @returns The base tool name without server suffix
|
|
100
|
+
*/
|
|
101
|
+
declare function getBaseToolName(toolName: string): string;
|
|
102
|
+
/**
|
|
103
|
+
* Generates a compact listing of deferred tools grouped by server.
|
|
104
|
+
* Format: "server: tool1, tool2, tool3"
|
|
105
|
+
* Non-MCP tools are grouped under "other".
|
|
106
|
+
* @param toolRegistry - The tool registry
|
|
107
|
+
* @param onlyDeferred - Whether to only include deferred tools
|
|
108
|
+
* @returns Formatted string with tools grouped by server
|
|
109
|
+
*/
|
|
110
|
+
declare function getDeferredToolsListing(toolRegistry: t.LCToolRegistry | undefined, onlyDeferred: boolean): string;
|
|
111
|
+
/**
|
|
112
|
+
* Formats a server listing response as structured JSON.
|
|
113
|
+
* NOTE: This is a PREVIEW only - tools are NOT discovered/loaded.
|
|
114
|
+
* @param tools - Array of tool metadata from the server(s)
|
|
115
|
+
* @param serverNames - The MCP server name(s)
|
|
116
|
+
* @returns JSON string showing all tools grouped by server
|
|
117
|
+
*/
|
|
118
|
+
declare function formatServerListing(tools: t.ToolMetadata[], serverNames: string | string[]): string;
|
|
119
|
+
/**
|
|
120
|
+
* Creates a Tool Search tool for discovering tools from a large registry.
|
|
121
|
+
*
|
|
122
|
+
* This tool enables AI agents to dynamically discover tools from a large library
|
|
123
|
+
* without loading all tool definitions into the LLM context window. The agent
|
|
124
|
+
* can search for relevant tools on-demand.
|
|
125
|
+
*
|
|
126
|
+
* **Modes:**
|
|
127
|
+
* - `code_interpreter` (default): Uses external sandbox for regex search. Safer for complex patterns.
|
|
128
|
+
* - `local`: Uses safe substring matching locally. No network call, faster, completely safe from ReDoS.
|
|
129
|
+
*
|
|
130
|
+
* The tool registry can be provided either:
|
|
131
|
+
* 1. At initialization time via params.toolRegistry
|
|
132
|
+
* 2. At runtime via config.configurable.toolRegistry when invoking
|
|
133
|
+
*
|
|
134
|
+
* @param params - Configuration parameters for the tool (toolRegistry is optional)
|
|
135
|
+
* @returns A LangChain DynamicStructuredTool for tool searching
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // Option 1: Code interpreter mode (regex via sandbox)
|
|
139
|
+
* const tool = createToolSearch({ apiKey, toolRegistry });
|
|
140
|
+
* await tool.invoke({ query: 'expense.*report' });
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // Option 2: Local mode (safe substring search, no API key needed)
|
|
144
|
+
* const tool = createToolSearch({ mode: 'local', toolRegistry });
|
|
145
|
+
* await tool.invoke({ query: 'expense' });
|
|
146
|
+
*/
|
|
147
|
+
declare function createToolSearch(initParams?: t.ToolSearchParams): DynamicStructuredTool<ReturnType<typeof createToolSearchSchema>>;
|
|
148
|
+
export { createToolSearch, performLocalSearch, extractMcpServerName, isFromMcpServer, isFromAnyMcpServer, normalizeServerFilter, getAvailableMcpServers, getDeferredToolsListing, getBaseToolName, formatServerListing, sanitizeRegex, escapeRegexSpecialChars, isDangerousPattern, countNestedGroups, hasNestedQuantifiers, };
|
|
@@ -236,6 +236,8 @@ export type MultiAgentGraphInput = StandardGraphInput & {
|
|
|
236
236
|
};
|
|
237
237
|
export interface AgentInputs {
|
|
238
238
|
agentId: string;
|
|
239
|
+
/** Human-readable name for the agent (used in handoff context). Defaults to agentId if not provided. */
|
|
240
|
+
name?: string;
|
|
239
241
|
toolEnd?: boolean;
|
|
240
242
|
toolMap?: ToolMap;
|
|
241
243
|
tools?: GraphTools;
|
|
@@ -37,7 +37,9 @@ export type AnthropicReasoning = {
|
|
|
37
37
|
thinkingBudget?: number;
|
|
38
38
|
};
|
|
39
39
|
export type OpenAIClientOptions = ChatOpenAIFields;
|
|
40
|
-
export type AnthropicClientOptions = AnthropicInput
|
|
40
|
+
export type AnthropicClientOptions = AnthropicInput & {
|
|
41
|
+
promptCache?: boolean;
|
|
42
|
+
};
|
|
41
43
|
export type MistralAIClientOptions = ChatMistralAIInput;
|
|
42
44
|
export type VertexAIClientOptions = ChatVertexAIInput & {
|
|
43
45
|
includeThoughts?: boolean;
|
|
@@ -29,6 +29,8 @@ export type ToolNodeOptions = {
|
|
|
29
29
|
errorHandler?: (data: ToolErrorData, metadata?: Record<string, unknown>) => Promise<void>;
|
|
30
30
|
/** Tool registry for lazy computation of programmatic tools and tool search */
|
|
31
31
|
toolRegistry?: LCToolRegistry;
|
|
32
|
+
/** Reference to Graph's sessions map for automatic session injection */
|
|
33
|
+
sessions?: ToolSessionMap;
|
|
32
34
|
};
|
|
33
35
|
export type ToolNodeConstructorParams = ToolRefs & ToolNodeOptions;
|
|
34
36
|
export type ToolEndEvent = {
|
|
@@ -55,6 +57,8 @@ export type FileRef = {
|
|
|
55
57
|
id: string;
|
|
56
58
|
name: string;
|
|
57
59
|
path?: string;
|
|
60
|
+
/** Session ID this file belongs to (for multi-session file tracking) */
|
|
61
|
+
session_id?: string;
|
|
58
62
|
};
|
|
59
63
|
export type FileRefs = FileRef[];
|
|
60
64
|
export type ExecuteResult = {
|
|
@@ -99,12 +103,18 @@ export type ProgrammaticCache = {
|
|
|
99
103
|
toolMap: ToolMap;
|
|
100
104
|
toolDefs: LCTool[];
|
|
101
105
|
};
|
|
102
|
-
/**
|
|
103
|
-
export type
|
|
106
|
+
/** Search mode: code_interpreter uses external sandbox, local uses safe substring matching */
|
|
107
|
+
export type ToolSearchMode = 'code_interpreter' | 'local';
|
|
108
|
+
/** Parameters for creating a Tool Search tool */
|
|
109
|
+
export type ToolSearchParams = {
|
|
104
110
|
apiKey?: string;
|
|
105
111
|
toolRegistry?: LCToolRegistry;
|
|
106
112
|
onlyDeferred?: boolean;
|
|
107
113
|
baseUrl?: string;
|
|
114
|
+
/** Search mode: 'code_interpreter' (default) uses sandbox for regex, 'local' uses safe substring matching */
|
|
115
|
+
mode?: ToolSearchMode;
|
|
116
|
+
/** Filter tools to only those from specific MCP server(s). Can be a single name or array of names. */
|
|
117
|
+
mcpServer?: string | string[];
|
|
108
118
|
[key: string]: unknown;
|
|
109
119
|
};
|
|
110
120
|
/** Simplified tool metadata for search purposes */
|
|
@@ -199,3 +209,33 @@ export type ProgrammaticToolCallingParams = {
|
|
|
199
209
|
/** Environment variable key for API key */
|
|
200
210
|
[key: string]: unknown;
|
|
201
211
|
};
|
|
212
|
+
/**
|
|
213
|
+
* Tracks code execution session state for automatic file persistence.
|
|
214
|
+
* Stored in Graph.sessions and injected into subsequent tool invocations.
|
|
215
|
+
*/
|
|
216
|
+
export type CodeSessionContext = {
|
|
217
|
+
/** Session ID from the code execution environment */
|
|
218
|
+
session_id: string;
|
|
219
|
+
/** Files generated in this session (for context/tracking) */
|
|
220
|
+
files: FileRefs;
|
|
221
|
+
/** Timestamp of last update */
|
|
222
|
+
lastUpdated: number;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Artifact structure returned by code execution tools (CodeExecutor, PTC).
|
|
226
|
+
* Used to extract session context after tool completion.
|
|
227
|
+
*/
|
|
228
|
+
export type CodeExecutionArtifact = {
|
|
229
|
+
session_id?: string;
|
|
230
|
+
files?: FileRefs;
|
|
231
|
+
};
|
|
232
|
+
/**
|
|
233
|
+
* Generic session context union type for different tool types.
|
|
234
|
+
* Extend this as new tool session types are added.
|
|
235
|
+
*/
|
|
236
|
+
export type ToolSessionContext = CodeSessionContext;
|
|
237
|
+
/**
|
|
238
|
+
* Map of tool names to their session contexts.
|
|
239
|
+
* Keys are tool constants (e.g., Constants.EXECUTE_CODE, Constants.PROGRAMMATIC_TOOL_CALLING).
|
|
240
|
+
*/
|
|
241
|
+
export type ToolSessionMap = Map<string, ToolSessionContext>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "illuma-agents",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.38",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -44,14 +44,17 @@
|
|
|
44
44
|
"code_exec": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
45
45
|
"image": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/image.ts --provider 'google' --name 'Jo' --location 'New York, NY'",
|
|
46
46
|
"code_exec_files": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec_files.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
47
|
-
"
|
|
48
|
-
"
|
|
47
|
+
"code_exec_session": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec_session.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
48
|
+
"code_exec_multi_session": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec_multi_session.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
49
|
+
"code_exec_simple": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec_simple.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
50
|
+
"simple": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/simple.ts --provider 'bedrock' --name 'Jo' --location 'New York, NY'",
|
|
49
51
|
"caching": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/caching.ts --name 'Jo' --location 'New York, NY'",
|
|
50
52
|
"thinking": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/thinking.ts --name 'Jo' --location 'New York, NY'",
|
|
53
|
+
"thinking:bedrock": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/thinking-bedrock.ts --name 'Jo' --location 'New York, NY'",
|
|
51
54
|
"memory": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/memory.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
52
|
-
"tool": "node --trace-warnings -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tools.ts --provider '
|
|
55
|
+
"tool": "node --trace-warnings -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tools.ts --provider 'bedrock' --name 'Jo' --location 'New York, NY'",
|
|
53
56
|
"search": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/search.ts --provider 'bedrock' --name 'Jo' --location 'New York, NY'",
|
|
54
|
-
"
|
|
57
|
+
"tool_search": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/tool_search.ts",
|
|
55
58
|
"programmatic_exec": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/programmatic_exec.ts",
|
|
56
59
|
"code_exec_ptc": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/code_exec_ptc.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
57
60
|
"programmatic_exec_agent": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/programmatic_exec_agent.ts --provider 'openAI' --name 'Jo' --location 'New York, NY'",
|
|
@@ -69,6 +72,7 @@
|
|
|
69
72
|
"multi-agent-sequence": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-sequence.ts",
|
|
70
73
|
"multi-agent-conditional": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-conditional.ts",
|
|
71
74
|
"multi-agent-supervisor": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/multi-agent-supervisor.ts",
|
|
75
|
+
"test-handoff-preamble": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-handoff-preamble.ts",
|
|
72
76
|
"multi-agent-list-handoff": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-multi-agent-list-handoff.ts",
|
|
73
77
|
"test-parallel-agent-labeling": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-parallel-agent-labeling.ts",
|
|
74
78
|
"test-thinking-handoff": "node -r dotenv/config --loader ./tsconfig-paths-bootstrap.mjs --experimental-specifier-resolution=node ./src/scripts/test-thinking-handoff.ts",
|
|
@@ -102,6 +106,7 @@
|
|
|
102
106
|
}
|
|
103
107
|
},
|
|
104
108
|
"dependencies": {
|
|
109
|
+
"@aws-sdk/client-bedrock-runtime": "^3.970.0",
|
|
105
110
|
"@langchain/anthropic": "^0.3.26",
|
|
106
111
|
"@langchain/aws": "^0.1.15",
|
|
107
112
|
"@langchain/core": "^0.3.80",
|
|
@@ -124,6 +129,7 @@
|
|
|
124
129
|
"https-proxy-agent": "^7.0.6",
|
|
125
130
|
"mathjs": "^15.1.0",
|
|
126
131
|
"nanoid": "^3.3.7",
|
|
132
|
+
"okapibm25": "^1.4.1",
|
|
127
133
|
"openai": "5.8.2"
|
|
128
134
|
},
|
|
129
135
|
"imports": {
|
|
@@ -151,6 +157,7 @@
|
|
|
151
157
|
"eslint-plugin-import": "^2.31.0",
|
|
152
158
|
"husky": "^9.1.7",
|
|
153
159
|
"jest": "^30.2.0",
|
|
160
|
+
"jest-util": "^30.2.0",
|
|
154
161
|
"lint-staged": "^15.2.7",
|
|
155
162
|
"prettier": "^3.6.2",
|
|
156
163
|
"rollup": "^4.34.6",
|
|
@@ -27,6 +27,7 @@ export class AgentContext {
|
|
|
27
27
|
): AgentContext {
|
|
28
28
|
const {
|
|
29
29
|
agentId,
|
|
30
|
+
name,
|
|
30
31
|
provider,
|
|
31
32
|
clientOptions,
|
|
32
33
|
tools,
|
|
@@ -44,6 +45,7 @@ export class AgentContext {
|
|
|
44
45
|
|
|
45
46
|
const agentContext = new AgentContext({
|
|
46
47
|
agentId,
|
|
48
|
+
name: name ?? agentId,
|
|
47
49
|
provider,
|
|
48
50
|
clientOptions,
|
|
49
51
|
maxContextTokens,
|
|
@@ -87,6 +89,8 @@ export class AgentContext {
|
|
|
87
89
|
|
|
88
90
|
/** Agent identifier */
|
|
89
91
|
agentId: string;
|
|
92
|
+
/** Human-readable name for this agent (used in handoff context). Falls back to agentId if not provided. */
|
|
93
|
+
name?: string;
|
|
90
94
|
/** Provider for this specific agent */
|
|
91
95
|
provider: Providers;
|
|
92
96
|
/** Client options for this agent */
|
|
@@ -173,9 +177,20 @@ export class AgentContext {
|
|
|
173
177
|
artifacts: 0,
|
|
174
178
|
memory: 0,
|
|
175
179
|
};
|
|
180
|
+
/**
|
|
181
|
+
* Handoff context when this agent receives control via handoff.
|
|
182
|
+
* Contains source and parallel execution info for system message context.
|
|
183
|
+
*/
|
|
184
|
+
handoffContext?: {
|
|
185
|
+
/** Source agent that transferred control */
|
|
186
|
+
sourceAgentName: string;
|
|
187
|
+
/** Names of sibling agents executing in parallel (empty if sequential) */
|
|
188
|
+
parallelSiblings: string[];
|
|
189
|
+
};
|
|
176
190
|
|
|
177
191
|
constructor({
|
|
178
192
|
agentId,
|
|
193
|
+
name,
|
|
179
194
|
provider,
|
|
180
195
|
clientOptions,
|
|
181
196
|
maxContextTokens,
|
|
@@ -193,6 +208,7 @@ export class AgentContext {
|
|
|
193
208
|
useLegacyContent,
|
|
194
209
|
}: {
|
|
195
210
|
agentId: string;
|
|
211
|
+
name?: string;
|
|
196
212
|
provider: Providers;
|
|
197
213
|
clientOptions?: t.ClientOptions;
|
|
198
214
|
maxContextTokens?: number;
|
|
@@ -210,6 +226,7 @@ export class AgentContext {
|
|
|
210
226
|
useLegacyContent?: boolean;
|
|
211
227
|
}) {
|
|
212
228
|
this.agentId = agentId;
|
|
229
|
+
this.name = name;
|
|
213
230
|
this.provider = provider;
|
|
214
231
|
this.clientOptions = clientOptions;
|
|
215
232
|
this.maxContextTokens = maxContextTokens;
|
|
@@ -324,27 +341,65 @@ export class AgentContext {
|
|
|
324
341
|
|
|
325
342
|
/**
|
|
326
343
|
* Builds the raw instructions string (without creating SystemMessage).
|
|
344
|
+
* Includes agent identity preamble and handoff context when available.
|
|
327
345
|
*/
|
|
328
346
|
private buildInstructionsString(): string {
|
|
329
|
-
|
|
347
|
+
const parts: string[] = [];
|
|
348
|
+
|
|
349
|
+
/** Build agent identity and handoff context preamble */
|
|
350
|
+
const identityPreamble = this.buildIdentityPreamble();
|
|
351
|
+
if (identityPreamble) {
|
|
352
|
+
parts.push(identityPreamble);
|
|
353
|
+
}
|
|
330
354
|
|
|
355
|
+
/** Add main instructions */
|
|
356
|
+
if (this.instructions != null && this.instructions !== '') {
|
|
357
|
+
parts.push(this.instructions);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Add additional instructions */
|
|
331
361
|
if (
|
|
332
362
|
this.additionalInstructions != null &&
|
|
333
363
|
this.additionalInstructions !== ''
|
|
334
364
|
) {
|
|
335
|
-
|
|
336
|
-
? `${result}\n\n${this.additionalInstructions}`
|
|
337
|
-
: this.additionalInstructions;
|
|
365
|
+
parts.push(this.additionalInstructions);
|
|
338
366
|
}
|
|
339
367
|
|
|
368
|
+
/** Add programmatic tools documentation */
|
|
340
369
|
const programmaticToolsDoc = this.buildProgrammaticOnlyToolsInstructions();
|
|
341
370
|
if (programmaticToolsDoc) {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
371
|
+
parts.push(programmaticToolsDoc);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return parts.join('\n\n');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Builds the agent identity preamble including handoff context if present.
|
|
379
|
+
* This helps the agent understand its role in the multi-agent workflow.
|
|
380
|
+
*/
|
|
381
|
+
private buildIdentityPreamble(): string {
|
|
382
|
+
if (!this.handoffContext) return '';
|
|
383
|
+
|
|
384
|
+
const displayName = this.name ?? this.agentId;
|
|
385
|
+
const { sourceAgentName, parallelSiblings } = this.handoffContext;
|
|
386
|
+
const isParallel = parallelSiblings.length > 0;
|
|
387
|
+
|
|
388
|
+
const lines: string[] = [];
|
|
389
|
+
lines.push('## Multi-Agent Workflow');
|
|
390
|
+
lines.push(
|
|
391
|
+
`You are "${displayName}", transferred from "${sourceAgentName}".`
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
if (isParallel) {
|
|
395
|
+
lines.push(`Running in parallel with: ${parallelSiblings.join(', ')}.`);
|
|
345
396
|
}
|
|
346
397
|
|
|
347
|
-
|
|
398
|
+
lines.push(
|
|
399
|
+
'Execute only tasks relevant to your role. Routing is already handled if requested, unless you can route further.'
|
|
400
|
+
);
|
|
401
|
+
|
|
402
|
+
return lines.join('\n');
|
|
348
403
|
}
|
|
349
404
|
|
|
350
405
|
/**
|
|
@@ -374,14 +429,7 @@ export class AgentContext {
|
|
|
374
429
|
const anthropicOptions = this.clientOptions as
|
|
375
430
|
| t.AnthropicClientOptions
|
|
376
431
|
| undefined;
|
|
377
|
-
|
|
378
|
-
| Record<string, string>
|
|
379
|
-
| undefined;
|
|
380
|
-
const anthropicBeta = defaultHeaders?.['anthropic-beta'];
|
|
381
|
-
if (
|
|
382
|
-
typeof anthropicBeta === 'string' &&
|
|
383
|
-
anthropicBeta.includes('prompt-caching')
|
|
384
|
-
) {
|
|
432
|
+
if (anthropicOptions?.promptCache === true) {
|
|
385
433
|
finalInstructions = {
|
|
386
434
|
content: [
|
|
387
435
|
{
|
|
@@ -455,6 +503,7 @@ export class AgentContext {
|
|
|
455
503
|
this.tokenTypeSwitch = undefined;
|
|
456
504
|
this.currentTokenType = ContentTypes.TEXT;
|
|
457
505
|
this.discoveredToolNames.clear();
|
|
506
|
+
this.handoffContext = undefined;
|
|
458
507
|
}
|
|
459
508
|
|
|
460
509
|
/**
|
|
@@ -626,6 +675,29 @@ export class AgentContext {
|
|
|
626
675
|
return registry;
|
|
627
676
|
}
|
|
628
677
|
|
|
678
|
+
/**
|
|
679
|
+
* Sets the handoff context for this agent.
|
|
680
|
+
* Call this when the agent receives control via handoff from another agent.
|
|
681
|
+
* Marks system runnable as stale to include handoff context in system message.
|
|
682
|
+
* @param sourceAgentName - Name of the agent that transferred control
|
|
683
|
+
* @param parallelSiblings - Names of other agents executing in parallel with this one
|
|
684
|
+
*/
|
|
685
|
+
setHandoffContext(sourceAgentName: string, parallelSiblings: string[]): void {
|
|
686
|
+
this.handoffContext = { sourceAgentName, parallelSiblings };
|
|
687
|
+
this.systemRunnableStale = true;
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Clears any handoff context.
|
|
692
|
+
* Call this when resetting the agent or when handoff context is no longer relevant.
|
|
693
|
+
*/
|
|
694
|
+
clearHandoffContext(): void {
|
|
695
|
+
if (this.handoffContext) {
|
|
696
|
+
this.handoffContext = undefined;
|
|
697
|
+
this.systemRunnableStale = true;
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
629
701
|
/**
|
|
630
702
|
* Marks tools as discovered via tool search.
|
|
631
703
|
* Discovered tools will be included in the next model binding.
|
package/src/common/enum.ts
CHANGED
|
@@ -161,11 +161,13 @@ export enum Callback {
|
|
|
161
161
|
export enum Constants {
|
|
162
162
|
OFFICIAL_CODE_BASEURL = 'https://api.illuma.ai/v1',
|
|
163
163
|
EXECUTE_CODE = 'execute_code',
|
|
164
|
-
|
|
164
|
+
TOOL_SEARCH = 'tool_search',
|
|
165
165
|
PROGRAMMATIC_TOOL_CALLING = 'run_tools_with_code',
|
|
166
166
|
WEB_SEARCH = 'web_search',
|
|
167
167
|
CONTENT_AND_ARTIFACT = 'content_and_artifact',
|
|
168
168
|
LC_TRANSFER_TO_ = 'lc_transfer_to_',
|
|
169
|
+
/** Delimiter for MCP tools: toolName_mcp_serverName */
|
|
170
|
+
MCP_DELIMITER = '_mcp_',
|
|
169
171
|
}
|
|
170
172
|
|
|
171
173
|
export enum TitleMethod {
|
package/src/graphs/Graph.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
Providers,
|
|
38
38
|
StepTypes,
|
|
39
39
|
MessageTypes,
|
|
40
|
+
Constants,
|
|
40
41
|
} from '@/common';
|
|
41
42
|
import {
|
|
42
43
|
formatAnthropicArtifactContent,
|
|
@@ -141,6 +142,12 @@ export abstract class Graph<
|
|
|
141
142
|
/** Set of invoked tool call IDs from non-message run steps completed mid-run, if any */
|
|
142
143
|
invokedToolIds?: Set<string>;
|
|
143
144
|
handlerRegistry: HandlerRegistry | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Tool session contexts for automatic state persistence across tool invocations.
|
|
147
|
+
* Keyed by tool name (e.g., Constants.EXECUTE_CODE).
|
|
148
|
+
* Currently supports code execution session tracking (session_id, files).
|
|
149
|
+
*/
|
|
150
|
+
sessions: t.ToolSessionMap = new Map();
|
|
144
151
|
}
|
|
145
152
|
|
|
146
153
|
export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
@@ -487,11 +494,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
487
494
|
finalInstructions != null &&
|
|
488
495
|
finalInstructions &&
|
|
489
496
|
provider === Providers.ANTHROPIC &&
|
|
490
|
-
(
|
|
491
|
-
(clientOptions as t.AnthropicClientOptions).clientOptions
|
|
492
|
-
?.defaultHeaders as Record<string, string> | undefined
|
|
493
|
-
)?.['anthropic-beta']?.includes('prompt-caching') ??
|
|
494
|
-
false)
|
|
497
|
+
(clientOptions as t.AnthropicClientOptions).promptCache === true
|
|
495
498
|
) {
|
|
496
499
|
finalInstructions = {
|
|
497
500
|
content: [
|
|
@@ -528,6 +531,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
528
531
|
errorHandler: (data, metadata) =>
|
|
529
532
|
StandardGraph.handleToolCallErrorStatic(this, data, metadata),
|
|
530
533
|
toolRegistry: agentContext?.toolRegistry,
|
|
534
|
+
sessions: this.sessions,
|
|
531
535
|
});
|
|
532
536
|
}
|
|
533
537
|
|
|
@@ -860,14 +864,7 @@ export class StandardGraph extends Graph<t.BaseGraphState, t.GraphNode> {
|
|
|
860
864
|
const anthropicOptions = agentContext.clientOptions as
|
|
861
865
|
| t.AnthropicClientOptions
|
|
862
866
|
| undefined;
|
|
863
|
-
|
|
864
|
-
?.defaultHeaders as Record<string, string> | undefined;
|
|
865
|
-
const anthropicBeta = defaultHeaders?.['anthropic-beta'];
|
|
866
|
-
|
|
867
|
-
if (
|
|
868
|
-
typeof anthropicBeta === 'string' &&
|
|
869
|
-
anthropicBeta.includes('prompt-caching')
|
|
870
|
-
) {
|
|
867
|
+
if (anthropicOptions?.promptCache === true) {
|
|
871
868
|
finalMessages = addCacheControl<BaseMessage>(finalMessages);
|
|
872
869
|
}
|
|
873
870
|
} else if (agentContext.provider === Providers.BEDROCK) {
|
|
@@ -1366,6 +1363,60 @@ If I seem to be missing something we discussed earlier, just give me a quick rem
|
|
|
1366
1363
|
throw new Error(`No run step found for stepId ${stepId}`);
|
|
1367
1364
|
}
|
|
1368
1365
|
|
|
1366
|
+
/**
|
|
1367
|
+
* Extract and store code execution session context from artifacts.
|
|
1368
|
+
* Each file is stamped with its source session_id to support multi-session file tracking.
|
|
1369
|
+
* When the same filename appears in a later execution, the newer version replaces the old.
|
|
1370
|
+
*/
|
|
1371
|
+
const toolName = output.name;
|
|
1372
|
+
if (
|
|
1373
|
+
toolName === Constants.EXECUTE_CODE ||
|
|
1374
|
+
toolName === Constants.PROGRAMMATIC_TOOL_CALLING
|
|
1375
|
+
) {
|
|
1376
|
+
const artifact = output.artifact as t.CodeExecutionArtifact | undefined;
|
|
1377
|
+
const newFiles = artifact?.files ?? [];
|
|
1378
|
+
const hasNewFiles = newFiles.length > 0;
|
|
1379
|
+
|
|
1380
|
+
if (
|
|
1381
|
+
hasNewFiles &&
|
|
1382
|
+
artifact?.session_id != null &&
|
|
1383
|
+
artifact.session_id !== ''
|
|
1384
|
+
) {
|
|
1385
|
+
/**
|
|
1386
|
+
* Stamp each new file with its source session_id.
|
|
1387
|
+
* This enables files from different executions (parallel or sequential)
|
|
1388
|
+
* to be tracked and passed to subsequent calls.
|
|
1389
|
+
*/
|
|
1390
|
+
const filesWithSession: t.FileRefs = newFiles.map((file) => ({
|
|
1391
|
+
...file,
|
|
1392
|
+
session_id: artifact.session_id,
|
|
1393
|
+
}));
|
|
1394
|
+
|
|
1395
|
+
const existingSession = this.sessions.get(Constants.EXECUTE_CODE) as
|
|
1396
|
+
| t.CodeSessionContext
|
|
1397
|
+
| undefined;
|
|
1398
|
+
const existingFiles = existingSession?.files ?? [];
|
|
1399
|
+
|
|
1400
|
+
/**
|
|
1401
|
+
* Merge files, preferring latest versions by name.
|
|
1402
|
+
* If a file with the same name exists, replace it with the new version.
|
|
1403
|
+
* This handles cases where files are edited/recreated in subsequent executions.
|
|
1404
|
+
*/
|
|
1405
|
+
const newFileNames = new Set(filesWithSession.map((f) => f.name));
|
|
1406
|
+
const filteredExisting = existingFiles.filter(
|
|
1407
|
+
(f) => !newFileNames.has(f.name)
|
|
1408
|
+
);
|
|
1409
|
+
|
|
1410
|
+
this.sessions.set(Constants.EXECUTE_CODE, {
|
|
1411
|
+
/** Keep latest session_id for reference/fallback */
|
|
1412
|
+
session_id: artifact.session_id,
|
|
1413
|
+
/** Accumulated files with latest versions preferred */
|
|
1414
|
+
files: [...filteredExisting, ...filesWithSession],
|
|
1415
|
+
lastUpdated: Date.now(),
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1369
1420
|
const dispatchedOutput =
|
|
1370
1421
|
typeof output.content === 'string'
|
|
1371
1422
|
? output.content
|