@illuma-ai/agents 1.1.25 → 1.3.0
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 +20 -3
- package/dist/cjs/agents/AgentContext.cjs.map +1 -1
- package/dist/cjs/common/spawnPath.cjs +104 -0
- package/dist/cjs/common/spawnPath.cjs.map +1 -0
- package/dist/cjs/graphs/Graph.cjs +87 -31
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/graphs/HandoffRegistry.cjs +143 -0
- package/dist/cjs/graphs/HandoffRegistry.cjs.map +1 -0
- package/dist/cjs/graphs/MultiAgentGraph.cjs +587 -184
- package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
- package/dist/cjs/graphs/phases/flushLoop.cjs +214 -0
- package/dist/cjs/graphs/phases/flushLoop.cjs.map +1 -0
- package/dist/cjs/graphs/phases/memoryFlushPhase.cjs +102 -0
- package/dist/cjs/graphs/phases/memoryFlushPhase.cjs.map +1 -0
- package/dist/cjs/llm/bedrock/index.cjs +4 -3
- package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
- package/dist/cjs/main.cjs +115 -0
- package/dist/cjs/main.cjs.map +1 -1
- package/dist/cjs/memory/citations.cjs +69 -0
- package/dist/cjs/memory/citations.cjs.map +1 -0
- package/dist/cjs/memory/compositeBackend.cjs +60 -0
- package/dist/cjs/memory/compositeBackend.cjs.map +1 -0
- package/dist/cjs/memory/constants.cjs +232 -0
- package/dist/cjs/memory/constants.cjs.map +1 -0
- package/dist/cjs/memory/embeddings.cjs +151 -0
- package/dist/cjs/memory/embeddings.cjs.map +1 -0
- package/dist/cjs/memory/factory.cjs +95 -0
- package/dist/cjs/memory/factory.cjs.map +1 -0
- package/dist/cjs/memory/migrate.cjs +81 -0
- package/dist/cjs/memory/migrate.cjs.map +1 -0
- package/dist/cjs/memory/mmr.cjs +138 -0
- package/dist/cjs/memory/mmr.cjs.map +1 -0
- package/dist/cjs/memory/paths.cjs +217 -0
- package/dist/cjs/memory/paths.cjs.map +1 -0
- package/dist/cjs/memory/pgvectorStore.cjs +225 -0
- package/dist/cjs/memory/pgvectorStore.cjs.map +1 -0
- package/dist/cjs/memory/recallTracking.cjs +98 -0
- package/dist/cjs/memory/recallTracking.cjs.map +1 -0
- package/dist/cjs/memory/schema.sql +51 -0
- package/dist/cjs/memory/temporalDecay.cjs +118 -0
- package/dist/cjs/memory/temporalDecay.cjs.map +1 -0
- package/dist/cjs/nodes/ApprovalGateNode.cjs +1 -1
- package/dist/cjs/nodes/ApprovalGateNode.cjs.map +1 -1
- package/dist/cjs/prompts/memoryFlushPrompt.cjs +49 -0
- package/dist/cjs/prompts/memoryFlushPrompt.cjs.map +1 -0
- package/dist/cjs/run.cjs +16 -3
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/stream.cjs +4 -4
- package/dist/cjs/stream.cjs.map +1 -1
- package/dist/cjs/tools/AskUser.cjs +6 -1
- package/dist/cjs/tools/AskUser.cjs.map +1 -1
- package/dist/cjs/tools/BrowserTools.cjs +1 -1
- package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +127 -10
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/approval/constants.cjs +2 -2
- package/dist/cjs/tools/approval/constants.cjs.map +1 -1
- package/dist/cjs/tools/memory/index.cjs +58 -0
- package/dist/cjs/tools/memory/index.cjs.map +1 -0
- package/dist/cjs/tools/memory/memoryAppendTool.cjs +69 -0
- package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -0
- package/dist/cjs/tools/memory/memoryGetTool.cjs +49 -0
- package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -0
- package/dist/cjs/tools/memory/memorySearchTool.cjs +65 -0
- package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -0
- package/dist/cjs/tools/memory/shared.cjs +106 -0
- package/dist/cjs/tools/memory/shared.cjs.map +1 -0
- package/dist/cjs/types/graph.cjs.map +1 -1
- package/dist/cjs/utils/childAgentContext.cjs +242 -0
- package/dist/cjs/utils/childAgentContext.cjs.map +1 -0
- package/dist/cjs/utils/events.cjs +36 -4
- package/dist/cjs/utils/events.cjs.map +1 -1
- package/dist/cjs/utils/finishReasons.cjs +44 -0
- package/dist/cjs/utils/finishReasons.cjs.map +1 -0
- package/dist/cjs/utils/llm.cjs.map +1 -1
- package/dist/cjs/utils/logging.cjs +34 -0
- package/dist/cjs/utils/logging.cjs.map +1 -0
- package/dist/cjs/utils/toolCallNormalization.cjs +250 -0
- package/dist/cjs/utils/toolCallNormalization.cjs.map +1 -0
- package/dist/esm/agents/AgentContext.mjs +20 -3
- package/dist/esm/agents/AgentContext.mjs.map +1 -1
- package/dist/esm/common/spawnPath.mjs +95 -0
- package/dist/esm/common/spawnPath.mjs.map +1 -0
- package/dist/esm/graphs/Graph.mjs +87 -31
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/graphs/HandoffRegistry.mjs +141 -0
- package/dist/esm/graphs/HandoffRegistry.mjs.map +1 -0
- package/dist/esm/graphs/MultiAgentGraph.mjs +587 -184
- package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
- package/dist/esm/graphs/phases/flushLoop.mjs +209 -0
- package/dist/esm/graphs/phases/flushLoop.mjs.map +1 -0
- package/dist/esm/graphs/phases/memoryFlushPhase.mjs +99 -0
- package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -0
- package/dist/esm/llm/bedrock/index.mjs +4 -3
- package/dist/esm/llm/bedrock/index.mjs.map +1 -1
- package/dist/esm/main.mjs +21 -0
- package/dist/esm/main.mjs.map +1 -1
- package/dist/esm/memory/citations.mjs +64 -0
- package/dist/esm/memory/citations.mjs.map +1 -0
- package/dist/esm/memory/compositeBackend.mjs +58 -0
- package/dist/esm/memory/compositeBackend.mjs.map +1 -0
- package/dist/esm/memory/constants.mjs +198 -0
- package/dist/esm/memory/constants.mjs.map +1 -0
- package/dist/esm/memory/embeddings.mjs +148 -0
- package/dist/esm/memory/embeddings.mjs.map +1 -0
- package/dist/esm/memory/factory.mjs +93 -0
- package/dist/esm/memory/factory.mjs.map +1 -0
- package/dist/esm/memory/migrate.mjs +78 -0
- package/dist/esm/memory/migrate.mjs.map +1 -0
- package/dist/esm/memory/mmr.mjs +130 -0
- package/dist/esm/memory/mmr.mjs.map +1 -0
- package/dist/esm/memory/paths.mjs +207 -0
- package/dist/esm/memory/paths.mjs.map +1 -0
- package/dist/esm/memory/pgvectorStore.mjs +223 -0
- package/dist/esm/memory/pgvectorStore.mjs.map +1 -0
- package/dist/esm/memory/recallTracking.mjs +94 -0
- package/dist/esm/memory/recallTracking.mjs.map +1 -0
- package/dist/esm/memory/schema.sql +51 -0
- package/dist/esm/memory/temporalDecay.mjs +110 -0
- package/dist/esm/memory/temporalDecay.mjs.map +1 -0
- package/dist/esm/nodes/ApprovalGateNode.mjs +1 -1
- package/dist/esm/nodes/ApprovalGateNode.mjs.map +1 -1
- package/dist/esm/prompts/memoryFlushPrompt.mjs +44 -0
- package/dist/esm/prompts/memoryFlushPrompt.mjs.map +1 -0
- package/dist/esm/run.mjs +16 -3
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/stream.mjs +4 -4
- package/dist/esm/stream.mjs.map +1 -1
- package/dist/esm/tools/AskUser.mjs +6 -1
- package/dist/esm/tools/AskUser.mjs.map +1 -1
- package/dist/esm/tools/BrowserTools.mjs +1 -1
- package/dist/esm/tools/BrowserTools.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +128 -11
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/approval/constants.mjs +2 -2
- package/dist/esm/tools/approval/constants.mjs.map +1 -1
- package/dist/esm/tools/memory/index.mjs +46 -0
- package/dist/esm/tools/memory/index.mjs.map +1 -0
- package/dist/esm/tools/memory/memoryAppendTool.mjs +67 -0
- package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -0
- package/dist/esm/tools/memory/memoryGetTool.mjs +47 -0
- package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -0
- package/dist/esm/tools/memory/memorySearchTool.mjs +63 -0
- package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -0
- package/dist/esm/tools/memory/shared.mjs +98 -0
- package/dist/esm/tools/memory/shared.mjs.map +1 -0
- package/dist/esm/types/graph.mjs.map +1 -1
- package/dist/esm/utils/childAgentContext.mjs +237 -0
- package/dist/esm/utils/childAgentContext.mjs.map +1 -0
- package/dist/esm/utils/events.mjs +36 -5
- package/dist/esm/utils/events.mjs.map +1 -1
- package/dist/esm/utils/finishReasons.mjs +41 -0
- package/dist/esm/utils/finishReasons.mjs.map +1 -0
- package/dist/esm/utils/llm.mjs.map +1 -1
- package/dist/esm/utils/logging.mjs +31 -0
- package/dist/esm/utils/logging.mjs.map +1 -0
- package/dist/esm/utils/toolCallNormalization.mjs +247 -0
- package/dist/esm/utils/toolCallNormalization.mjs.map +1 -0
- package/dist/types/common/index.d.ts +1 -0
- package/dist/types/common/spawnPath.d.ts +59 -0
- package/dist/types/graphs/HandoffRegistry.d.ts +97 -0
- package/dist/types/graphs/MultiAgentGraph.d.ts +58 -18
- package/dist/types/graphs/index.d.ts +1 -0
- package/dist/types/graphs/phases/flushLoop.d.ts +106 -0
- package/dist/types/graphs/phases/memoryFlushPhase.d.ts +100 -0
- package/dist/types/index.d.ts +7 -0
- package/dist/types/memory/__tests__/mockBackend.d.ts +40 -0
- package/dist/types/memory/citations.d.ts +39 -0
- package/dist/types/memory/compositeBackend.d.ts +30 -0
- package/dist/types/memory/constants.d.ts +121 -0
- package/dist/types/memory/embeddings.d.ts +15 -0
- package/dist/types/memory/factory.d.ts +23 -0
- package/dist/types/memory/index.d.ts +21 -0
- package/dist/types/memory/migrate.d.ts +14 -0
- package/dist/types/memory/mmr.d.ts +50 -0
- package/dist/types/memory/paths.d.ts +107 -0
- package/dist/types/memory/pgvectorStore.d.ts +56 -0
- package/dist/types/memory/recallTracking.d.ts +30 -0
- package/dist/types/memory/temporalDecay.d.ts +53 -0
- package/dist/types/memory/types.d.ts +182 -0
- package/dist/types/prompts/memoryFlushPrompt.d.ts +54 -0
- package/dist/types/run.d.ts +1 -0
- package/dist/types/tools/AskUser.d.ts +1 -1
- package/dist/types/tools/BrowserTools.d.ts +2 -2
- package/dist/types/tools/approval/constants.d.ts +2 -2
- package/dist/types/tools/memory/index.d.ts +39 -0
- package/dist/types/tools/memory/memoryAppendTool.d.ts +27 -0
- package/dist/types/tools/memory/memoryGetTool.d.ts +22 -0
- package/dist/types/tools/memory/memorySearchTool.d.ts +22 -0
- package/dist/types/tools/memory/shared.d.ts +106 -0
- package/dist/types/types/graph.d.ts +16 -3
- package/dist/types/utils/childAgentContext.d.ts +99 -0
- package/dist/types/utils/events.d.ts +21 -0
- package/dist/types/utils/finishReasons.d.ts +32 -0
- package/dist/types/utils/logging.d.ts +2 -0
- package/dist/types/utils/toolCallNormalization.d.ts +44 -0
- package/package.json +6 -4
- package/src/agents/AgentContext.ts +26 -3
- package/src/common/__tests__/enum.test.ts +4 -2
- package/src/common/__tests__/spawnPath.test.ts +110 -0
- package/src/common/index.ts +1 -0
- package/src/common/spawnPath.ts +101 -0
- package/src/graphs/Graph.ts +94 -43
- package/src/graphs/HandoffRegistry.ts +199 -0
- package/src/graphs/MultiAgentGraph.ts +694 -226
- package/src/graphs/__tests__/HandoffRegistry.test.ts +410 -0
- package/src/graphs/__tests__/multi-agent-delegate.test.ts +61 -16
- package/src/graphs/__tests__/multi-agent-edges.test.ts +4 -2
- package/src/graphs/__tests__/multi-agent-nested-subgraph.test.ts +221 -0
- package/src/graphs/__tests__/structured-output.integration.test.ts +212 -118
- package/src/graphs/contextManagement.e2e.test.ts +1 -1
- package/src/graphs/index.ts +1 -0
- package/src/graphs/phases/__tests__/flushLoop.test.ts +264 -0
- package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +37 -0
- package/src/graphs/phases/__tests__/runMemoryFlush.test.ts +150 -0
- package/src/graphs/phases/flushLoop.ts +303 -0
- package/src/graphs/phases/memoryFlushPhase.ts +209 -0
- package/src/index.ts +30 -1
- package/src/llm/bedrock/index.ts +4 -5
- package/src/memory/__tests__/citations.test.ts +61 -0
- package/src/memory/__tests__/compositeBackend.test.ts +79 -0
- package/src/memory/__tests__/isolation.test.ts +206 -0
- package/src/memory/__tests__/mmr.test.ts +148 -0
- package/src/memory/__tests__/mockBackend.ts +161 -0
- package/src/memory/__tests__/paths.test.ts +168 -0
- package/src/memory/__tests__/recallTracking.test.ts +96 -0
- package/src/memory/__tests__/temporalDecay.test.ts +151 -0
- package/src/memory/citations.ts +80 -0
- package/src/memory/compositeBackend.ts +99 -0
- package/src/memory/constants.ts +229 -0
- package/src/memory/embeddings.ts +188 -0
- package/src/memory/factory.ts +111 -0
- package/src/memory/index.ts +46 -0
- package/src/memory/migrate.ts +116 -0
- package/src/memory/mmr.ts +161 -0
- package/src/memory/paths.ts +258 -0
- package/src/memory/pgvectorStore.ts +324 -0
- package/src/memory/recallTracking.ts +127 -0
- package/src/memory/schema.sql +51 -0
- package/src/memory/temporalDecay.ts +134 -0
- package/src/memory/types.ts +185 -0
- package/src/nodes/ApprovalGateNode.ts +4 -10
- package/src/nodes/__tests__/ApprovalGateNode.test.ts +11 -20
- package/src/prompts/memoryFlushPrompt.ts +78 -0
- package/src/run.ts +17 -6
- package/src/scripts/test-bedrock-handoff-autonomous.ts +56 -20
- package/src/specs/agent-handoffs-bedrock.integration.test.ts +8 -5
- package/src/specs/agent-handoffs.test.ts +8 -2
- package/src/stream.ts +4 -6
- package/src/tools/AskUser.ts +7 -2
- package/src/tools/BrowserTools.ts +3 -5
- package/src/tools/ToolNode.ts +150 -13
- package/src/tools/__tests__/ToolApproval.test.ts +22 -9
- package/src/tools/approval/__tests__/constants.test.ts +4 -4
- package/src/tools/approval/constants.ts +2 -2
- package/src/tools/memory/__tests__/memoryTools.test.ts +205 -0
- package/src/tools/memory/index.ts +96 -0
- package/src/tools/memory/memoryAppendTool.ts +101 -0
- package/src/tools/memory/memoryGetTool.ts +53 -0
- package/src/tools/memory/memorySearchTool.ts +80 -0
- package/src/tools/memory/shared.ts +169 -0
- package/src/tools/search/search.test.ts +6 -1
- package/src/types/graph.ts +16 -3
- package/src/utils/__tests__/childAgentContext.test.ts +217 -0
- package/src/utils/__tests__/finishReasons.test.ts +55 -0
- package/src/utils/__tests__/toolCallNormalization.test.ts +181 -0
- package/src/utils/childAgentContext.ts +259 -0
- package/src/utils/events.ts +37 -4
- package/src/utils/finishReasons.ts +40 -0
- package/src/utils/llm.ts +0 -1
- package/src/utils/logging.ts +45 -8
- package/src/utils/toolCallNormalization.ts +271 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type MemoryToolBinding } from './shared';
|
|
2
|
+
export declare function createMemoryGetTool(binding: MemoryToolBinding): import("@langchain/core/tools").DynamicStructuredTool<import("zod").ZodObject<{
|
|
3
|
+
path: import("zod").ZodString;
|
|
4
|
+
from: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
5
|
+
lines: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
6
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
7
|
+
path: string;
|
|
8
|
+
from?: number | undefined;
|
|
9
|
+
lines?: number | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
path: string;
|
|
12
|
+
from?: number | undefined;
|
|
13
|
+
lines?: number | undefined;
|
|
14
|
+
}>, {
|
|
15
|
+
path: string;
|
|
16
|
+
from?: number | undefined;
|
|
17
|
+
lines?: number | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
path: string;
|
|
20
|
+
from?: number | undefined;
|
|
21
|
+
lines?: number | undefined;
|
|
22
|
+
}, string>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type MemoryToolBinding } from './shared';
|
|
2
|
+
export declare function createMemorySearchTool(binding: MemoryToolBinding): import("@langchain/core/tools").DynamicStructuredTool<import("zod").ZodObject<{
|
|
3
|
+
query: import("zod").ZodString;
|
|
4
|
+
maxResults: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
5
|
+
minScore: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
6
|
+
}, "strip", import("zod").ZodTypeAny, {
|
|
7
|
+
query: string;
|
|
8
|
+
maxResults?: number | undefined;
|
|
9
|
+
minScore?: number | undefined;
|
|
10
|
+
}, {
|
|
11
|
+
query: string;
|
|
12
|
+
maxResults?: number | undefined;
|
|
13
|
+
minScore?: number | undefined;
|
|
14
|
+
}>, {
|
|
15
|
+
query: string;
|
|
16
|
+
maxResults?: number | undefined;
|
|
17
|
+
minScore?: number | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
query: string;
|
|
20
|
+
maxResults?: number | undefined;
|
|
21
|
+
minScore?: number | undefined;
|
|
22
|
+
}, string>;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared Zod schemas + helpers for the memory tool family.
|
|
3
|
+
*
|
|
4
|
+
* The tool schemas deliberately do NOT expose `agent_id` or `user_id`. Scope
|
|
5
|
+
* is captured in a closure at tool construction time by {@link buildMemoryTools},
|
|
6
|
+
* so even a hallucinated tool call with extra fields cannot influence it.
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { MemoryAppendInput, MemoryBackend, MemoryEntry, MemoryScope } from '@/memory/types';
|
|
10
|
+
export declare const MemorySearchSchema: z.ZodObject<{
|
|
11
|
+
query: z.ZodString;
|
|
12
|
+
maxResults: z.ZodOptional<z.ZodNumber>;
|
|
13
|
+
minScore: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
}, "strip", z.ZodTypeAny, {
|
|
15
|
+
query: string;
|
|
16
|
+
maxResults?: number | undefined;
|
|
17
|
+
minScore?: number | undefined;
|
|
18
|
+
}, {
|
|
19
|
+
query: string;
|
|
20
|
+
maxResults?: number | undefined;
|
|
21
|
+
minScore?: number | undefined;
|
|
22
|
+
}>;
|
|
23
|
+
export declare const MemoryGetSchema: z.ZodObject<{
|
|
24
|
+
path: z.ZodString;
|
|
25
|
+
from: z.ZodOptional<z.ZodNumber>;
|
|
26
|
+
lines: z.ZodOptional<z.ZodNumber>;
|
|
27
|
+
}, "strip", z.ZodTypeAny, {
|
|
28
|
+
path: string;
|
|
29
|
+
from?: number | undefined;
|
|
30
|
+
lines?: number | undefined;
|
|
31
|
+
}, {
|
|
32
|
+
path: string;
|
|
33
|
+
from?: number | undefined;
|
|
34
|
+
lines?: number | undefined;
|
|
35
|
+
}>;
|
|
36
|
+
export declare const MemoryAppendSchema: z.ZodObject<{
|
|
37
|
+
path: z.ZodString;
|
|
38
|
+
content: z.ZodString;
|
|
39
|
+
}, "strip", z.ZodTypeAny, {
|
|
40
|
+
content: string;
|
|
41
|
+
path: string;
|
|
42
|
+
}, {
|
|
43
|
+
content: string;
|
|
44
|
+
path: string;
|
|
45
|
+
}>;
|
|
46
|
+
export type MemorySearchArgs = z.infer<typeof MemorySearchSchema>;
|
|
47
|
+
export type MemoryGetArgs = z.infer<typeof MemoryGetSchema>;
|
|
48
|
+
export type MemoryAppendArgs = z.infer<typeof MemoryAppendSchema>;
|
|
49
|
+
/** Shape returned by `memory_search` — serialised as the tool result string. */
|
|
50
|
+
export interface MemorySearchToolResult {
|
|
51
|
+
results: Array<Pick<MemoryEntry, 'id' | 'path' | 'content' | 'score' | 'createdAt'> & {
|
|
52
|
+
citation?: string;
|
|
53
|
+
}>;
|
|
54
|
+
disabled?: boolean;
|
|
55
|
+
unavailable?: boolean;
|
|
56
|
+
error?: string;
|
|
57
|
+
warning?: string;
|
|
58
|
+
action?: string;
|
|
59
|
+
}
|
|
60
|
+
/** Shape returned by `memory_get`. */
|
|
61
|
+
export interface MemoryGetToolResult {
|
|
62
|
+
path: string;
|
|
63
|
+
text: string;
|
|
64
|
+
disabled?: boolean;
|
|
65
|
+
error?: string;
|
|
66
|
+
}
|
|
67
|
+
export declare function buildMemorySearchUnavailableResult(error: string | undefined): MemorySearchToolResult;
|
|
68
|
+
/**
|
|
69
|
+
* Clamp a ranked result list to a total character budget.
|
|
70
|
+
* Ported from upstream `tools.citations.ts::clampResultsByInjectedChars`.
|
|
71
|
+
*/
|
|
72
|
+
export declare function clampResultsByInjectedChars<T extends {
|
|
73
|
+
content: string;
|
|
74
|
+
}>(results: T[], maxInjectedChars?: number): T[];
|
|
75
|
+
export interface MemoryToolBinding {
|
|
76
|
+
backend: MemoryBackend;
|
|
77
|
+
scope: MemoryScope;
|
|
78
|
+
maxInjectedChars?: number;
|
|
79
|
+
/** Phase 2 — per-call search options propagated into backend.search. */
|
|
80
|
+
searchOptions?: {
|
|
81
|
+
mmr?: {
|
|
82
|
+
enabled?: boolean;
|
|
83
|
+
lambda?: number;
|
|
84
|
+
};
|
|
85
|
+
temporalDecay?: {
|
|
86
|
+
enabled?: boolean;
|
|
87
|
+
halfLifeDays?: number;
|
|
88
|
+
};
|
|
89
|
+
citations?: 'on' | 'off' | 'auto';
|
|
90
|
+
};
|
|
91
|
+
/** Phase 2 — optional best-effort recall recorder. */
|
|
92
|
+
recallTracker?: {
|
|
93
|
+
record(params: {
|
|
94
|
+
agentId: string;
|
|
95
|
+
query: string;
|
|
96
|
+
hits: Array<{
|
|
97
|
+
id: string;
|
|
98
|
+
path: string;
|
|
99
|
+
score: number;
|
|
100
|
+
}>;
|
|
101
|
+
}): Promise<void>;
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
export declare function assertAppendAllowed(path: string): void;
|
|
105
|
+
/** Normalise an append call into the backend input shape. */
|
|
106
|
+
export declare function toAppendInput(args: MemoryAppendArgs): MemoryAppendInput;
|
|
@@ -23,6 +23,12 @@ export type AgentTransitionEvent = {
|
|
|
23
23
|
destinationAgentName: string;
|
|
24
24
|
edgeType: string;
|
|
25
25
|
timestamp: number;
|
|
26
|
+
/** When true, this event signals handoff completion (child → parent return) */
|
|
27
|
+
isCompletion?: boolean;
|
|
28
|
+
/** Duration of child agent execution in milliseconds (only on completion events) */
|
|
29
|
+
durationMs?: number;
|
|
30
|
+
/** Length of child agent result text in characters (only on completion events) */
|
|
31
|
+
resultLength?: number;
|
|
26
32
|
};
|
|
27
33
|
export type GraphNode = GraphNodeKeys | typeof START;
|
|
28
34
|
export type ClientCallback<T extends unknown[]> = (graph: StandardGraph, ...args: T) => void;
|
|
@@ -288,6 +294,13 @@ export type GraphEdge = {
|
|
|
288
294
|
* Defaults to DEFAULT_HANDOFF_MAX_RESULT_CHARS (32768 chars, ~8192 tokens).
|
|
289
295
|
*/
|
|
290
296
|
maxResultChars?: number;
|
|
297
|
+
/**
|
|
298
|
+
* For handoff edges: When true, the child agent receives the full parent
|
|
299
|
+
* conversation history plus the orchestrator's instructions appended.
|
|
300
|
+
* When false (default), the child only receives the orchestrator's scoped
|
|
301
|
+
* instructions — isolated from the parent conversation.
|
|
302
|
+
*/
|
|
303
|
+
passthrough?: boolean;
|
|
291
304
|
/**
|
|
292
305
|
* Approval gate configuration for sequence edges.
|
|
293
306
|
* When set, inserts an approval gate node between source and destination.
|
|
@@ -538,7 +551,7 @@ export interface AgentInputs {
|
|
|
538
551
|
discoveredTools?: string[];
|
|
539
552
|
/**
|
|
540
553
|
* Optional callback for summarizing messages that were pruned from context.
|
|
541
|
-
* When provided, discarded messages are summarized by the caller
|
|
554
|
+
* When provided, discarded messages are summarized by the host caller
|
|
542
555
|
* using a cheap LLM call, and the summary is prepended to the context.
|
|
543
556
|
*/
|
|
544
557
|
summarizeCallback?: (messagesToRefine: import('@langchain/core/messages').BaseMessage[]) => Promise<string | undefined>;
|
|
@@ -546,7 +559,7 @@ export interface AgentInputs {
|
|
|
546
559
|
* Pre-existing summary text loaded from persistent storage (MongoDB/Redis).
|
|
547
560
|
* When provided, this summary is injected into the initial message context
|
|
548
561
|
* so the agent has prior conversation history even on new turns.
|
|
549
|
-
* Set by
|
|
562
|
+
* Set by the host's summary store when resuming a conversation.
|
|
550
563
|
*/
|
|
551
564
|
persistedSummary?: string;
|
|
552
565
|
/**
|
|
@@ -567,7 +580,7 @@ export interface AgentInputs {
|
|
|
567
580
|
* - file_search (RAG semantic search over embedded files)
|
|
568
581
|
* - content_tool read (by contentId for exact file retrieval)
|
|
569
582
|
*
|
|
570
|
-
* Built by the orchestrator
|
|
583
|
+
* Built by the host orchestrator from message_file_map
|
|
571
584
|
* and metadata.context_files across all conversation messages.
|
|
572
585
|
*/
|
|
573
586
|
fileManifest?: FileManifestEntry[];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Child-agent context preparation utilities.
|
|
3
|
+
*
|
|
4
|
+
* When a parent agent invokes a child agent — via handoff, sequence edge,
|
|
5
|
+
* or scoped subgraph — the child cannot just receive `state.messages`
|
|
6
|
+
* verbatim. The parent's conversation contains tool_use/tool_result blocks
|
|
7
|
+
* that are (a) often incompatible with the child's tool registry, and
|
|
8
|
+
* (b) actively harmful to the child's ability to reason cleanly about its
|
|
9
|
+
* own task (noise → schema confusion → malformed tool_use).
|
|
10
|
+
*
|
|
11
|
+
* This module provides the two canonical strategies, extracted from
|
|
12
|
+
* `MultiAgentGraph` so they can be unit-tested in isolation and reused by
|
|
13
|
+
* future sub-agent orchestrators:
|
|
14
|
+
*
|
|
15
|
+
* 1. `prepareHandoffMessages` — "cleaned parent history"
|
|
16
|
+
* Used when the child still needs the orchestrator's context (it's
|
|
17
|
+
* the handoff target). Drops orphaned tool_use, compacts paired
|
|
18
|
+
* tool_use/tool_result into text summaries, and guarantees the tail
|
|
19
|
+
* is a HumanMessage so Bedrock/VertexAI won't reject the conversation
|
|
20
|
+
* with "assistant message prefill" errors.
|
|
21
|
+
*
|
|
22
|
+
* 2. `prepareIsolatedChildMessages` — "fresh session"
|
|
23
|
+
* Used for downstream sequence-node children inside a scoped subgraph.
|
|
24
|
+
* The child sees only the original user request plus a synthetic
|
|
25
|
+
* HumanMessage summarizing the upstream agent's final text output and
|
|
26
|
+
* directing the child to act. Raw upstream tool_use/tool_result blocks
|
|
27
|
+
* are discarded.
|
|
28
|
+
*
|
|
29
|
+
* Both helpers are pure functions over message arrays — no I/O, no
|
|
30
|
+
* LangGraph coupling — so they can be exercised by unit tests with
|
|
31
|
+
* synthetic message fixtures.
|
|
32
|
+
*/
|
|
33
|
+
import type { BaseMessage } from '@langchain/core/messages';
|
|
34
|
+
/**
|
|
35
|
+
* Prefix injected in front of a trailing AIMessage when we flip it to a
|
|
36
|
+
* HumanMessage to satisfy provider "last message must be user" rules.
|
|
37
|
+
*/
|
|
38
|
+
export declare const HANDOFF_TAIL_CONTEXT_PREFIX = "[Context from orchestrator]: ";
|
|
39
|
+
/**
|
|
40
|
+
* Directive task-framing wrapper for downstream scoped-subgraph children.
|
|
41
|
+
*
|
|
42
|
+
* Design notes — each line is load-bearing:
|
|
43
|
+
* - "Prior step output" names the upstream role without leaking the
|
|
44
|
+
* agent's internal id.
|
|
45
|
+
* - "You MUST now perform..." replaces ambiguity with obligation.
|
|
46
|
+
* - "system instructions" references the agent's stored system prompt
|
|
47
|
+
* as the source of task definition — so operators can tune behavior
|
|
48
|
+
* via data, not code.
|
|
49
|
+
* - The tool-first clause prevents small/fast models from stalling on a
|
|
50
|
+
* text-only acknowledgement when a tool action is expected.
|
|
51
|
+
*/
|
|
52
|
+
export declare function buildIsolatedChildPrompt(upstreamText: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* Prepare messages for a handoff child agent.
|
|
55
|
+
*
|
|
56
|
+
* Handles two problems that break Bedrock/Anthropic conversations:
|
|
57
|
+
*
|
|
58
|
+
* 1. **Orphaned tool_use**: The parent's AI message contains a `tool_use`
|
|
59
|
+
* block for the handoff tool itself, with no matching `tool_result`.
|
|
60
|
+
* Providers (Bedrock/Anthropic) reject this.
|
|
61
|
+
*
|
|
62
|
+
* 2. **Paired tool_use/tool_result in history**: The child may not have
|
|
63
|
+
* the same tools as the parent. Bedrock requires `toolConfig` when any
|
|
64
|
+
* tool_use/tool_result blocks exist in the history. Compacting these
|
|
65
|
+
* into text summaries avoids the requirement and reduces context bloat.
|
|
66
|
+
*
|
|
67
|
+
* Also ensures the tail is a HumanMessage — some providers reject a
|
|
68
|
+
* conversation that ends with an assistant message.
|
|
69
|
+
*
|
|
70
|
+
* @param messages - Current state messages from the parent
|
|
71
|
+
* @returns A sanitized copy, safe to pass to any provider as the child's
|
|
72
|
+
* input regardless of which tools the child has registered.
|
|
73
|
+
*/
|
|
74
|
+
export declare function prepareHandoffMessages(messages: BaseMessage[]): BaseMessage[];
|
|
75
|
+
/**
|
|
76
|
+
* Build an ISOLATED message context for a downstream scoped-subgraph node.
|
|
77
|
+
*
|
|
78
|
+
* Unlike `prepareHandoffMessages` (which cleans up tool_use artifacts but
|
|
79
|
+
* preserves most of the parent history), this helper produces a fresh
|
|
80
|
+
* minimal context containing only:
|
|
81
|
+
*
|
|
82
|
+
* 1. The original user request (first HumanMessage in the history)
|
|
83
|
+
* 2. A synthetic HumanMessage summarizing the upstream agent's final
|
|
84
|
+
* text output and directing the downstream agent to act on it
|
|
85
|
+
*
|
|
86
|
+
* Tool_use / tool_result blocks from the upstream agent are discarded —
|
|
87
|
+
* the downstream agent shouldn't reason about how the upstream agent did
|
|
88
|
+
* its work, only about the result.
|
|
89
|
+
*
|
|
90
|
+
* This "fresh subagent session" pattern is the primary defense against
|
|
91
|
+
* schema confusion / malformed tool_use JSON that occurs when downstream
|
|
92
|
+
* models see a noisy upstream conversation.
|
|
93
|
+
*
|
|
94
|
+
* Defensive fallback: if the messages array contains neither a user
|
|
95
|
+
* message nor a non-empty upstream AI message, return the input unchanged
|
|
96
|
+
* so the caller still has something to invoke on. This only matters for
|
|
97
|
+
* malformed state fixtures in tests.
|
|
98
|
+
*/
|
|
99
|
+
export declare function prepareIsolatedChildMessages(messages: BaseMessage[]): BaseMessage[];
|
|
@@ -1,6 +1,27 @@
|
|
|
1
1
|
import type { RunnableConfig } from '@langchain/core/runnables';
|
|
2
|
+
/**
|
|
3
|
+
* Returns the RunnableConfig currently active in LangChain's AsyncLocalStorage,
|
|
4
|
+
* or undefined if none is installed. This is the per-async-branch config that
|
|
5
|
+
* LangGraph installs when entering a node — it carries the correct
|
|
6
|
+
* `metadata.spawnKey` for child subgraph invocations inside `Promise.all`
|
|
7
|
+
* parallel handoffs.
|
|
8
|
+
*
|
|
9
|
+
* Prefer this over any Graph-instance-cached config (e.g. `this.config`)
|
|
10
|
+
* when dispatching events from code that may run concurrently across multiple
|
|
11
|
+
* child subgraphs. An instance-level cache is shared state and races between
|
|
12
|
+
* siblings — the last child to enter wins, so events fire with the wrong
|
|
13
|
+
* child's metadata and the backend routes them to the wrong spawnKey.
|
|
14
|
+
*/
|
|
15
|
+
export declare function getCurrentRunnableConfig(): RunnableConfig | undefined;
|
|
2
16
|
/**
|
|
3
17
|
* Safely dispatches a custom event and properly awaits it to avoid
|
|
4
18
|
* race conditions where events are dispatched after run cleanup.
|
|
19
|
+
*
|
|
20
|
+
* **Parallel-handoff correctness:** callers should prefer passing
|
|
21
|
+
* `undefined` (or the per-node runtime config). When `config` is omitted,
|
|
22
|
+
* LangChain's `ensureConfig` reads the current RunnableConfig from
|
|
23
|
+
* AsyncLocalStorage, which is correctly isolated per async branch under
|
|
24
|
+
* `Promise.all`. Passing a stale instance-cached config overrides that
|
|
25
|
+
* implicit config's metadata and cross-contaminates parallel children.
|
|
5
26
|
*/
|
|
6
27
|
export declare function safeDispatchCustomEvent(event: string, payload: unknown, config?: RunnableConfig): Promise<void>;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finish-reason constants and helpers.
|
|
3
|
+
*
|
|
4
|
+
* LLM providers emit different keys (`finish_reason`, `stop_reason`,
|
|
5
|
+
* `stopReason`, `finishReason`) and different values for the same concept.
|
|
6
|
+
* This module is the single source of truth for detecting *truncation* —
|
|
7
|
+
* i.e., the model hit its output budget and the response is incomplete.
|
|
8
|
+
*
|
|
9
|
+
* Used by:
|
|
10
|
+
* - `Graph.ts` — sticky `lastFinishReason` across inner subgraph invokes,
|
|
11
|
+
* so host's continuation retry can detect truncation that happens
|
|
12
|
+
* inside a scoped-subgraph child node.
|
|
13
|
+
* - Any future continuation / auto-retry logic added to agents.
|
|
14
|
+
*
|
|
15
|
+
* Kept as a small utils module (instead of inline in Graph.ts) so that
|
|
16
|
+
* additional callers — e.g., structured output recovery, sub-agent
|
|
17
|
+
* orchestrators — can reuse the exact same detection logic without drift.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Canonical set of finish-reason strings that mean "output was truncated".
|
|
21
|
+
*
|
|
22
|
+
* Covers:
|
|
23
|
+
* - `max_tokens` — Anthropic direct API, Bedrock
|
|
24
|
+
* - `length` — OpenAI/Azure
|
|
25
|
+
* - `MAX_TOKENS` — VertexAI/Google (uppercased enum)
|
|
26
|
+
*/
|
|
27
|
+
export declare const TRUNCATION_FINISH_REASONS: ReadonlySet<string>;
|
|
28
|
+
/**
|
|
29
|
+
* @returns true when the given finish/stop reason indicates the response
|
|
30
|
+
* was cut short by the output token budget.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isTruncationReason(reason: string | undefined | null): boolean;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool-call name normalization for malformed LLM tool_use outputs.
|
|
3
|
+
*
|
|
4
|
+
* LLMs — especially smaller / faster models — frequently emit tool_use
|
|
5
|
+
* blocks with names that don't exactly match the registered tool name:
|
|
6
|
+
*
|
|
7
|
+
* - Wrong delimiter: `outlook/operations` vs `outlook_operations`
|
|
8
|
+
* - Function-prefix style: `functions.outlook_operations`
|
|
9
|
+
* - Case drift: `Outlook_Operations`
|
|
10
|
+
* - Counter suffix: `outlook_operations_1`
|
|
11
|
+
* - Missing name, only id: `{name: "", id: "tool_outlook_operations_42"}`
|
|
12
|
+
*
|
|
13
|
+
* Before this normalization layer, any of the above caused the LangGraph
|
|
14
|
+
* ToolNode to throw "Tool X not found" and the whole turn to fail. With
|
|
15
|
+
* normalization, we resolve to the correct registered name case-insensitively
|
|
16
|
+
* and transparently fix the tool_call in place.
|
|
17
|
+
*
|
|
18
|
+
* Resolution order:
|
|
19
|
+
* 1. Exact match
|
|
20
|
+
* 2. Normalized delimiter + exact
|
|
21
|
+
* 3. Case-insensitive match
|
|
22
|
+
* 4. Structured candidates (strip prefixes, take suffix segments)
|
|
23
|
+
* 5. Infer from tool_call id (strip trailing digits, function/tool prefix)
|
|
24
|
+
*
|
|
25
|
+
* Any unresolvable tool_call is left as-is — the downstream ToolNode will
|
|
26
|
+
* fail with its usual error and the auto-recovery path kicks in.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Produce the best allowed tool name for a raw LLM tool_use name.
|
|
30
|
+
* Returns the original name unchanged if no resolution is possible —
|
|
31
|
+
* the downstream executor will then fail with its normal error path.
|
|
32
|
+
*/
|
|
33
|
+
export declare function normalizeToolCallName(rawName: string, allowedToolNames: Set<string>, rawToolCallId?: string): string;
|
|
34
|
+
/**
|
|
35
|
+
* In-place normalization of all tool_calls on an AIMessage-like object.
|
|
36
|
+
*
|
|
37
|
+
* Applies `normalizeToolCallName` to each tool_call's `name` field using the
|
|
38
|
+
* provided allowed-tool set (derived from the agent's toolMap keys). Also
|
|
39
|
+
* rewrites the `name` field inside any content blocks of type `tool_use` so
|
|
40
|
+
* that downstream providers see the corrected name.
|
|
41
|
+
*
|
|
42
|
+
* Returns `true` if any name was rewritten.
|
|
43
|
+
*/
|
|
44
|
+
export declare function normalizeMessageToolCalls(message: unknown, allowedToolNames: Set<string>): boolean;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@illuma-ai/agents",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"main": "./dist/cjs/main.cjs",
|
|
5
5
|
"module": "./dist/esm/main.mjs",
|
|
6
6
|
"types": "./dist/types/index.d.ts",
|
|
@@ -123,6 +123,8 @@
|
|
|
123
123
|
"dependencies": {
|
|
124
124
|
"@anthropic-ai/sdk": "^0.73.0",
|
|
125
125
|
"@aws-sdk/client-bedrock-runtime": "^3.980.0",
|
|
126
|
+
"@illuma-ai/observability-langchain": "^0.2.1",
|
|
127
|
+
"@illuma-ai/observability-otel": "^0.2.1",
|
|
126
128
|
"@langchain/anthropic": "^0.3.26",
|
|
127
129
|
"@langchain/aws": "^0.1.15",
|
|
128
130
|
"@langchain/core": "^0.3.80",
|
|
@@ -134,8 +136,6 @@
|
|
|
134
136
|
"@langchain/openai": "0.5.18",
|
|
135
137
|
"@langchain/textsplitters": "^0.1.0",
|
|
136
138
|
"@langchain/xai": "^0.0.3",
|
|
137
|
-
"@illuma-ai/observability-langchain": "^0.2.1",
|
|
138
|
-
"@illuma-ai/observability-otel": "^0.2.1",
|
|
139
139
|
"@opentelemetry/sdk-node": "^0.207.0",
|
|
140
140
|
"@scarf/scarf": "^1.4.0",
|
|
141
141
|
"@toon-format/toon": "^2.1.0",
|
|
@@ -148,7 +148,8 @@
|
|
|
148
148
|
"mathjs": "^15.1.0",
|
|
149
149
|
"nanoid": "^3.3.7",
|
|
150
150
|
"okapibm25": "^1.4.1",
|
|
151
|
-
"openai": "5.8.2"
|
|
151
|
+
"openai": "5.8.2",
|
|
152
|
+
"pg": "^8.20.0"
|
|
152
153
|
},
|
|
153
154
|
"imports": {
|
|
154
155
|
"@/*": "./src/*",
|
|
@@ -166,6 +167,7 @@
|
|
|
166
167
|
"@types/jest": "^30.0.0",
|
|
167
168
|
"@types/node": "^20.14.11",
|
|
168
169
|
"@types/node-fetch": "^2.6.13",
|
|
170
|
+
"@types/pg": "^8.20.0",
|
|
169
171
|
"@types/yargs-parser": "^21.0.3",
|
|
170
172
|
"@typescript-eslint/eslint-plugin": "^8.24.0",
|
|
171
173
|
"@typescript-eslint/parser": "^8.24.0",
|
|
@@ -622,18 +622,41 @@ export class AgentContext {
|
|
|
622
622
|
const isParallel = parallelSiblings.length > 0;
|
|
623
623
|
|
|
624
624
|
const lines: string[] = [];
|
|
625
|
-
lines.push('##
|
|
625
|
+
lines.push('## Subagent Context');
|
|
626
|
+
lines.push('');
|
|
626
627
|
lines.push(
|
|
627
|
-
`You are "${displayName}",
|
|
628
|
+
`You are "${displayName}", a subagent spawned by "${sourceAgentName}" for a specific task.`
|
|
628
629
|
);
|
|
629
630
|
|
|
630
631
|
if (isParallel) {
|
|
631
632
|
lines.push(`Running in parallel with: ${parallelSiblings.join(', ')}.`);
|
|
632
633
|
}
|
|
633
634
|
|
|
635
|
+
lines.push('');
|
|
636
|
+
lines.push('### Your Role');
|
|
637
|
+
lines.push('- Complete your assigned task. That is your entire purpose.');
|
|
638
|
+
lines.push(`- You are NOT "${sourceAgentName}". Do not try to be.`);
|
|
639
|
+
lines.push('');
|
|
640
|
+
lines.push('### Rules');
|
|
641
|
+
lines.push('1. **Stay focused** — Do your assigned task, nothing else.');
|
|
634
642
|
lines.push(
|
|
635
|
-
'
|
|
643
|
+
'2. **Complete the task** — Your final message will be automatically reported back.'
|
|
636
644
|
);
|
|
645
|
+
lines.push(
|
|
646
|
+
'3. **Be autonomous** — Execute directly without asking for user confirmation. Use your tools and best judgment.'
|
|
647
|
+
);
|
|
648
|
+
lines.push(
|
|
649
|
+
'4. **No placeholders** — Never generate fake or placeholder data. If you cannot retrieve real data, say so.'
|
|
650
|
+
);
|
|
651
|
+
lines.push(
|
|
652
|
+
'5. **No side effects** — Do not send messages, emails, or notifications unless explicitly tasked to do so.'
|
|
653
|
+
);
|
|
654
|
+
lines.push('');
|
|
655
|
+
lines.push('### Output');
|
|
656
|
+
lines.push('When complete, your final response should include:');
|
|
657
|
+
lines.push('- What you accomplished or found');
|
|
658
|
+
lines.push('- Any relevant details the orchestrator should know');
|
|
659
|
+
lines.push('- Keep it concise but informative');
|
|
637
660
|
|
|
638
661
|
return lines.join('\n');
|
|
639
662
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for centralized enums in @illuma-ai/agents.
|
|
3
3
|
* Verifies enum values remain stable — these are part of the public API
|
|
4
|
-
* and are referenced by
|
|
4
|
+
* and are referenced by host validation schemas and MongoDB documents.
|
|
5
5
|
*/
|
|
6
6
|
import {
|
|
7
7
|
EdgeType,
|
|
@@ -33,7 +33,9 @@ describe('EdgeType enum', () => {
|
|
|
33
33
|
it('has three members: handoff, transfer, sequence', () => {
|
|
34
34
|
const values = Object.values(EdgeType);
|
|
35
35
|
expect(values).toHaveLength(3);
|
|
36
|
-
expect(values).toEqual(
|
|
36
|
+
expect(values).toEqual(
|
|
37
|
+
expect.arrayContaining(['handoff', 'transfer', 'sequence'])
|
|
38
|
+
);
|
|
37
39
|
});
|
|
38
40
|
});
|
|
39
41
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import {
|
|
2
|
+
SPAWN_PATH_SEP,
|
|
3
|
+
MAX_NESTING_DEPTH,
|
|
4
|
+
buildSpawnPath,
|
|
5
|
+
spawnPathDepth,
|
|
6
|
+
parentSpawnPath,
|
|
7
|
+
spawnPathParts,
|
|
8
|
+
leafSpawnKey,
|
|
9
|
+
isAncestorSpawnPath,
|
|
10
|
+
} from '../spawnPath';
|
|
11
|
+
|
|
12
|
+
describe('spawnPath utilities', () => {
|
|
13
|
+
describe('constants', () => {
|
|
14
|
+
it('exports a slash separator', () => {
|
|
15
|
+
expect(SPAWN_PATH_SEP).toBe('/');
|
|
16
|
+
});
|
|
17
|
+
it('default max nesting depth is 5', () => {
|
|
18
|
+
expect(MAX_NESTING_DEPTH).toBe(5);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('buildSpawnPath', () => {
|
|
23
|
+
it('returns key as-is when parent is empty/null/undefined', () => {
|
|
24
|
+
expect(buildSpawnPath('', 'a')).toBe('a');
|
|
25
|
+
expect(buildSpawnPath(null, 'a')).toBe('a');
|
|
26
|
+
expect(buildSpawnPath(undefined, 'a')).toBe('a');
|
|
27
|
+
});
|
|
28
|
+
it('appends key to parent with separator', () => {
|
|
29
|
+
expect(buildSpawnPath('a', 'b')).toBe('a/b');
|
|
30
|
+
expect(buildSpawnPath('a/b', 'c')).toBe('a/b/c');
|
|
31
|
+
});
|
|
32
|
+
it('throws on empty key', () => {
|
|
33
|
+
expect(() => buildSpawnPath('a', '')).toThrow(/empty key/);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('spawnPathDepth', () => {
|
|
38
|
+
it('root is depth 0', () => {
|
|
39
|
+
expect(spawnPathDepth('')).toBe(0);
|
|
40
|
+
expect(spawnPathDepth(null)).toBe(0);
|
|
41
|
+
expect(spawnPathDepth(undefined)).toBe(0);
|
|
42
|
+
});
|
|
43
|
+
it('single segment is depth 1', () => {
|
|
44
|
+
expect(spawnPathDepth('a')).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
it('multi segment counts segments', () => {
|
|
47
|
+
expect(spawnPathDepth('a/b')).toBe(2);
|
|
48
|
+
expect(spawnPathDepth('a/b/c/d')).toBe(4);
|
|
49
|
+
});
|
|
50
|
+
it('ignores empty segments from stray separators', () => {
|
|
51
|
+
expect(spawnPathDepth('a//b')).toBe(2);
|
|
52
|
+
expect(spawnPathDepth('/a/b')).toBe(2);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('parentSpawnPath', () => {
|
|
57
|
+
it('root returns null', () => {
|
|
58
|
+
expect(parentSpawnPath('')).toBeNull();
|
|
59
|
+
expect(parentSpawnPath(null)).toBeNull();
|
|
60
|
+
});
|
|
61
|
+
it('single segment returns empty (root)', () => {
|
|
62
|
+
expect(parentSpawnPath('a')).toBe('');
|
|
63
|
+
});
|
|
64
|
+
it('multi segment drops last', () => {
|
|
65
|
+
expect(parentSpawnPath('a/b')).toBe('a');
|
|
66
|
+
expect(parentSpawnPath('a/b/c')).toBe('a/b');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('spawnPathParts', () => {
|
|
71
|
+
it('empty path returns empty array', () => {
|
|
72
|
+
expect(spawnPathParts('')).toEqual([]);
|
|
73
|
+
expect(spawnPathParts(null)).toEqual([]);
|
|
74
|
+
});
|
|
75
|
+
it('splits segments', () => {
|
|
76
|
+
expect(spawnPathParts('a/b/c')).toEqual(['a', 'b', 'c']);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe('leafSpawnKey', () => {
|
|
81
|
+
it('root returns null', () => {
|
|
82
|
+
expect(leafSpawnKey('')).toBeNull();
|
|
83
|
+
});
|
|
84
|
+
it('returns last segment', () => {
|
|
85
|
+
expect(leafSpawnKey('a')).toBe('a');
|
|
86
|
+
expect(leafSpawnKey('a/b/c')).toBe('c');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('isAncestorSpawnPath', () => {
|
|
91
|
+
it('root is ancestor of any non-root path', () => {
|
|
92
|
+
expect(isAncestorSpawnPath('', 'a')).toBe(true);
|
|
93
|
+
expect(isAncestorSpawnPath('', 'a/b')).toBe(true);
|
|
94
|
+
});
|
|
95
|
+
it('root is not ancestor of root', () => {
|
|
96
|
+
expect(isAncestorSpawnPath('', '')).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
it('strict ancestor detection', () => {
|
|
99
|
+
expect(isAncestorSpawnPath('a', 'a/b')).toBe(true);
|
|
100
|
+
expect(isAncestorSpawnPath('a/b', 'a/b/c')).toBe(true);
|
|
101
|
+
expect(isAncestorSpawnPath('a', 'a')).toBe(false);
|
|
102
|
+
});
|
|
103
|
+
it('sibling is not ancestor', () => {
|
|
104
|
+
expect(isAncestorSpawnPath('a/b', 'a/c')).toBe(false);
|
|
105
|
+
});
|
|
106
|
+
it('partial-prefix is not ancestor (must respect segment boundary)', () => {
|
|
107
|
+
expect(isAncestorSpawnPath('ab', 'abc')).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
});
|