@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.
Files changed (272) hide show
  1. package/dist/cjs/agents/AgentContext.cjs +20 -3
  2. package/dist/cjs/agents/AgentContext.cjs.map +1 -1
  3. package/dist/cjs/common/spawnPath.cjs +104 -0
  4. package/dist/cjs/common/spawnPath.cjs.map +1 -0
  5. package/dist/cjs/graphs/Graph.cjs +87 -31
  6. package/dist/cjs/graphs/Graph.cjs.map +1 -1
  7. package/dist/cjs/graphs/HandoffRegistry.cjs +143 -0
  8. package/dist/cjs/graphs/HandoffRegistry.cjs.map +1 -0
  9. package/dist/cjs/graphs/MultiAgentGraph.cjs +587 -184
  10. package/dist/cjs/graphs/MultiAgentGraph.cjs.map +1 -1
  11. package/dist/cjs/graphs/phases/flushLoop.cjs +214 -0
  12. package/dist/cjs/graphs/phases/flushLoop.cjs.map +1 -0
  13. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs +102 -0
  14. package/dist/cjs/graphs/phases/memoryFlushPhase.cjs.map +1 -0
  15. package/dist/cjs/llm/bedrock/index.cjs +4 -3
  16. package/dist/cjs/llm/bedrock/index.cjs.map +1 -1
  17. package/dist/cjs/main.cjs +115 -0
  18. package/dist/cjs/main.cjs.map +1 -1
  19. package/dist/cjs/memory/citations.cjs +69 -0
  20. package/dist/cjs/memory/citations.cjs.map +1 -0
  21. package/dist/cjs/memory/compositeBackend.cjs +60 -0
  22. package/dist/cjs/memory/compositeBackend.cjs.map +1 -0
  23. package/dist/cjs/memory/constants.cjs +232 -0
  24. package/dist/cjs/memory/constants.cjs.map +1 -0
  25. package/dist/cjs/memory/embeddings.cjs +151 -0
  26. package/dist/cjs/memory/embeddings.cjs.map +1 -0
  27. package/dist/cjs/memory/factory.cjs +95 -0
  28. package/dist/cjs/memory/factory.cjs.map +1 -0
  29. package/dist/cjs/memory/migrate.cjs +81 -0
  30. package/dist/cjs/memory/migrate.cjs.map +1 -0
  31. package/dist/cjs/memory/mmr.cjs +138 -0
  32. package/dist/cjs/memory/mmr.cjs.map +1 -0
  33. package/dist/cjs/memory/paths.cjs +217 -0
  34. package/dist/cjs/memory/paths.cjs.map +1 -0
  35. package/dist/cjs/memory/pgvectorStore.cjs +225 -0
  36. package/dist/cjs/memory/pgvectorStore.cjs.map +1 -0
  37. package/dist/cjs/memory/recallTracking.cjs +98 -0
  38. package/dist/cjs/memory/recallTracking.cjs.map +1 -0
  39. package/dist/cjs/memory/schema.sql +51 -0
  40. package/dist/cjs/memory/temporalDecay.cjs +118 -0
  41. package/dist/cjs/memory/temporalDecay.cjs.map +1 -0
  42. package/dist/cjs/nodes/ApprovalGateNode.cjs +1 -1
  43. package/dist/cjs/nodes/ApprovalGateNode.cjs.map +1 -1
  44. package/dist/cjs/prompts/memoryFlushPrompt.cjs +49 -0
  45. package/dist/cjs/prompts/memoryFlushPrompt.cjs.map +1 -0
  46. package/dist/cjs/run.cjs +16 -3
  47. package/dist/cjs/run.cjs.map +1 -1
  48. package/dist/cjs/stream.cjs +4 -4
  49. package/dist/cjs/stream.cjs.map +1 -1
  50. package/dist/cjs/tools/AskUser.cjs +6 -1
  51. package/dist/cjs/tools/AskUser.cjs.map +1 -1
  52. package/dist/cjs/tools/BrowserTools.cjs +1 -1
  53. package/dist/cjs/tools/BrowserTools.cjs.map +1 -1
  54. package/dist/cjs/tools/ToolNode.cjs +127 -10
  55. package/dist/cjs/tools/ToolNode.cjs.map +1 -1
  56. package/dist/cjs/tools/approval/constants.cjs +2 -2
  57. package/dist/cjs/tools/approval/constants.cjs.map +1 -1
  58. package/dist/cjs/tools/memory/index.cjs +58 -0
  59. package/dist/cjs/tools/memory/index.cjs.map +1 -0
  60. package/dist/cjs/tools/memory/memoryAppendTool.cjs +69 -0
  61. package/dist/cjs/tools/memory/memoryAppendTool.cjs.map +1 -0
  62. package/dist/cjs/tools/memory/memoryGetTool.cjs +49 -0
  63. package/dist/cjs/tools/memory/memoryGetTool.cjs.map +1 -0
  64. package/dist/cjs/tools/memory/memorySearchTool.cjs +65 -0
  65. package/dist/cjs/tools/memory/memorySearchTool.cjs.map +1 -0
  66. package/dist/cjs/tools/memory/shared.cjs +106 -0
  67. package/dist/cjs/tools/memory/shared.cjs.map +1 -0
  68. package/dist/cjs/types/graph.cjs.map +1 -1
  69. package/dist/cjs/utils/childAgentContext.cjs +242 -0
  70. package/dist/cjs/utils/childAgentContext.cjs.map +1 -0
  71. package/dist/cjs/utils/events.cjs +36 -4
  72. package/dist/cjs/utils/events.cjs.map +1 -1
  73. package/dist/cjs/utils/finishReasons.cjs +44 -0
  74. package/dist/cjs/utils/finishReasons.cjs.map +1 -0
  75. package/dist/cjs/utils/llm.cjs.map +1 -1
  76. package/dist/cjs/utils/logging.cjs +34 -0
  77. package/dist/cjs/utils/logging.cjs.map +1 -0
  78. package/dist/cjs/utils/toolCallNormalization.cjs +250 -0
  79. package/dist/cjs/utils/toolCallNormalization.cjs.map +1 -0
  80. package/dist/esm/agents/AgentContext.mjs +20 -3
  81. package/dist/esm/agents/AgentContext.mjs.map +1 -1
  82. package/dist/esm/common/spawnPath.mjs +95 -0
  83. package/dist/esm/common/spawnPath.mjs.map +1 -0
  84. package/dist/esm/graphs/Graph.mjs +87 -31
  85. package/dist/esm/graphs/Graph.mjs.map +1 -1
  86. package/dist/esm/graphs/HandoffRegistry.mjs +141 -0
  87. package/dist/esm/graphs/HandoffRegistry.mjs.map +1 -0
  88. package/dist/esm/graphs/MultiAgentGraph.mjs +587 -184
  89. package/dist/esm/graphs/MultiAgentGraph.mjs.map +1 -1
  90. package/dist/esm/graphs/phases/flushLoop.mjs +209 -0
  91. package/dist/esm/graphs/phases/flushLoop.mjs.map +1 -0
  92. package/dist/esm/graphs/phases/memoryFlushPhase.mjs +99 -0
  93. package/dist/esm/graphs/phases/memoryFlushPhase.mjs.map +1 -0
  94. package/dist/esm/llm/bedrock/index.mjs +4 -3
  95. package/dist/esm/llm/bedrock/index.mjs.map +1 -1
  96. package/dist/esm/main.mjs +21 -0
  97. package/dist/esm/main.mjs.map +1 -1
  98. package/dist/esm/memory/citations.mjs +64 -0
  99. package/dist/esm/memory/citations.mjs.map +1 -0
  100. package/dist/esm/memory/compositeBackend.mjs +58 -0
  101. package/dist/esm/memory/compositeBackend.mjs.map +1 -0
  102. package/dist/esm/memory/constants.mjs +198 -0
  103. package/dist/esm/memory/constants.mjs.map +1 -0
  104. package/dist/esm/memory/embeddings.mjs +148 -0
  105. package/dist/esm/memory/embeddings.mjs.map +1 -0
  106. package/dist/esm/memory/factory.mjs +93 -0
  107. package/dist/esm/memory/factory.mjs.map +1 -0
  108. package/dist/esm/memory/migrate.mjs +78 -0
  109. package/dist/esm/memory/migrate.mjs.map +1 -0
  110. package/dist/esm/memory/mmr.mjs +130 -0
  111. package/dist/esm/memory/mmr.mjs.map +1 -0
  112. package/dist/esm/memory/paths.mjs +207 -0
  113. package/dist/esm/memory/paths.mjs.map +1 -0
  114. package/dist/esm/memory/pgvectorStore.mjs +223 -0
  115. package/dist/esm/memory/pgvectorStore.mjs.map +1 -0
  116. package/dist/esm/memory/recallTracking.mjs +94 -0
  117. package/dist/esm/memory/recallTracking.mjs.map +1 -0
  118. package/dist/esm/memory/schema.sql +51 -0
  119. package/dist/esm/memory/temporalDecay.mjs +110 -0
  120. package/dist/esm/memory/temporalDecay.mjs.map +1 -0
  121. package/dist/esm/nodes/ApprovalGateNode.mjs +1 -1
  122. package/dist/esm/nodes/ApprovalGateNode.mjs.map +1 -1
  123. package/dist/esm/prompts/memoryFlushPrompt.mjs +44 -0
  124. package/dist/esm/prompts/memoryFlushPrompt.mjs.map +1 -0
  125. package/dist/esm/run.mjs +16 -3
  126. package/dist/esm/run.mjs.map +1 -1
  127. package/dist/esm/stream.mjs +4 -4
  128. package/dist/esm/stream.mjs.map +1 -1
  129. package/dist/esm/tools/AskUser.mjs +6 -1
  130. package/dist/esm/tools/AskUser.mjs.map +1 -1
  131. package/dist/esm/tools/BrowserTools.mjs +1 -1
  132. package/dist/esm/tools/BrowserTools.mjs.map +1 -1
  133. package/dist/esm/tools/ToolNode.mjs +128 -11
  134. package/dist/esm/tools/ToolNode.mjs.map +1 -1
  135. package/dist/esm/tools/approval/constants.mjs +2 -2
  136. package/dist/esm/tools/approval/constants.mjs.map +1 -1
  137. package/dist/esm/tools/memory/index.mjs +46 -0
  138. package/dist/esm/tools/memory/index.mjs.map +1 -0
  139. package/dist/esm/tools/memory/memoryAppendTool.mjs +67 -0
  140. package/dist/esm/tools/memory/memoryAppendTool.mjs.map +1 -0
  141. package/dist/esm/tools/memory/memoryGetTool.mjs +47 -0
  142. package/dist/esm/tools/memory/memoryGetTool.mjs.map +1 -0
  143. package/dist/esm/tools/memory/memorySearchTool.mjs +63 -0
  144. package/dist/esm/tools/memory/memorySearchTool.mjs.map +1 -0
  145. package/dist/esm/tools/memory/shared.mjs +98 -0
  146. package/dist/esm/tools/memory/shared.mjs.map +1 -0
  147. package/dist/esm/types/graph.mjs.map +1 -1
  148. package/dist/esm/utils/childAgentContext.mjs +237 -0
  149. package/dist/esm/utils/childAgentContext.mjs.map +1 -0
  150. package/dist/esm/utils/events.mjs +36 -5
  151. package/dist/esm/utils/events.mjs.map +1 -1
  152. package/dist/esm/utils/finishReasons.mjs +41 -0
  153. package/dist/esm/utils/finishReasons.mjs.map +1 -0
  154. package/dist/esm/utils/llm.mjs.map +1 -1
  155. package/dist/esm/utils/logging.mjs +31 -0
  156. package/dist/esm/utils/logging.mjs.map +1 -0
  157. package/dist/esm/utils/toolCallNormalization.mjs +247 -0
  158. package/dist/esm/utils/toolCallNormalization.mjs.map +1 -0
  159. package/dist/types/common/index.d.ts +1 -0
  160. package/dist/types/common/spawnPath.d.ts +59 -0
  161. package/dist/types/graphs/HandoffRegistry.d.ts +97 -0
  162. package/dist/types/graphs/MultiAgentGraph.d.ts +58 -18
  163. package/dist/types/graphs/index.d.ts +1 -0
  164. package/dist/types/graphs/phases/flushLoop.d.ts +106 -0
  165. package/dist/types/graphs/phases/memoryFlushPhase.d.ts +100 -0
  166. package/dist/types/index.d.ts +7 -0
  167. package/dist/types/memory/__tests__/mockBackend.d.ts +40 -0
  168. package/dist/types/memory/citations.d.ts +39 -0
  169. package/dist/types/memory/compositeBackend.d.ts +30 -0
  170. package/dist/types/memory/constants.d.ts +121 -0
  171. package/dist/types/memory/embeddings.d.ts +15 -0
  172. package/dist/types/memory/factory.d.ts +23 -0
  173. package/dist/types/memory/index.d.ts +21 -0
  174. package/dist/types/memory/migrate.d.ts +14 -0
  175. package/dist/types/memory/mmr.d.ts +50 -0
  176. package/dist/types/memory/paths.d.ts +107 -0
  177. package/dist/types/memory/pgvectorStore.d.ts +56 -0
  178. package/dist/types/memory/recallTracking.d.ts +30 -0
  179. package/dist/types/memory/temporalDecay.d.ts +53 -0
  180. package/dist/types/memory/types.d.ts +182 -0
  181. package/dist/types/prompts/memoryFlushPrompt.d.ts +54 -0
  182. package/dist/types/run.d.ts +1 -0
  183. package/dist/types/tools/AskUser.d.ts +1 -1
  184. package/dist/types/tools/BrowserTools.d.ts +2 -2
  185. package/dist/types/tools/approval/constants.d.ts +2 -2
  186. package/dist/types/tools/memory/index.d.ts +39 -0
  187. package/dist/types/tools/memory/memoryAppendTool.d.ts +27 -0
  188. package/dist/types/tools/memory/memoryGetTool.d.ts +22 -0
  189. package/dist/types/tools/memory/memorySearchTool.d.ts +22 -0
  190. package/dist/types/tools/memory/shared.d.ts +106 -0
  191. package/dist/types/types/graph.d.ts +16 -3
  192. package/dist/types/utils/childAgentContext.d.ts +99 -0
  193. package/dist/types/utils/events.d.ts +21 -0
  194. package/dist/types/utils/finishReasons.d.ts +32 -0
  195. package/dist/types/utils/logging.d.ts +2 -0
  196. package/dist/types/utils/toolCallNormalization.d.ts +44 -0
  197. package/package.json +6 -4
  198. package/src/agents/AgentContext.ts +26 -3
  199. package/src/common/__tests__/enum.test.ts +4 -2
  200. package/src/common/__tests__/spawnPath.test.ts +110 -0
  201. package/src/common/index.ts +1 -0
  202. package/src/common/spawnPath.ts +101 -0
  203. package/src/graphs/Graph.ts +94 -43
  204. package/src/graphs/HandoffRegistry.ts +199 -0
  205. package/src/graphs/MultiAgentGraph.ts +694 -226
  206. package/src/graphs/__tests__/HandoffRegistry.test.ts +410 -0
  207. package/src/graphs/__tests__/multi-agent-delegate.test.ts +61 -16
  208. package/src/graphs/__tests__/multi-agent-edges.test.ts +4 -2
  209. package/src/graphs/__tests__/multi-agent-nested-subgraph.test.ts +221 -0
  210. package/src/graphs/__tests__/structured-output.integration.test.ts +212 -118
  211. package/src/graphs/contextManagement.e2e.test.ts +1 -1
  212. package/src/graphs/index.ts +1 -0
  213. package/src/graphs/phases/__tests__/flushLoop.test.ts +264 -0
  214. package/src/graphs/phases/__tests__/memoryFlushPhase.test.ts +37 -0
  215. package/src/graphs/phases/__tests__/runMemoryFlush.test.ts +150 -0
  216. package/src/graphs/phases/flushLoop.ts +303 -0
  217. package/src/graphs/phases/memoryFlushPhase.ts +209 -0
  218. package/src/index.ts +30 -1
  219. package/src/llm/bedrock/index.ts +4 -5
  220. package/src/memory/__tests__/citations.test.ts +61 -0
  221. package/src/memory/__tests__/compositeBackend.test.ts +79 -0
  222. package/src/memory/__tests__/isolation.test.ts +206 -0
  223. package/src/memory/__tests__/mmr.test.ts +148 -0
  224. package/src/memory/__tests__/mockBackend.ts +161 -0
  225. package/src/memory/__tests__/paths.test.ts +168 -0
  226. package/src/memory/__tests__/recallTracking.test.ts +96 -0
  227. package/src/memory/__tests__/temporalDecay.test.ts +151 -0
  228. package/src/memory/citations.ts +80 -0
  229. package/src/memory/compositeBackend.ts +99 -0
  230. package/src/memory/constants.ts +229 -0
  231. package/src/memory/embeddings.ts +188 -0
  232. package/src/memory/factory.ts +111 -0
  233. package/src/memory/index.ts +46 -0
  234. package/src/memory/migrate.ts +116 -0
  235. package/src/memory/mmr.ts +161 -0
  236. package/src/memory/paths.ts +258 -0
  237. package/src/memory/pgvectorStore.ts +324 -0
  238. package/src/memory/recallTracking.ts +127 -0
  239. package/src/memory/schema.sql +51 -0
  240. package/src/memory/temporalDecay.ts +134 -0
  241. package/src/memory/types.ts +185 -0
  242. package/src/nodes/ApprovalGateNode.ts +4 -10
  243. package/src/nodes/__tests__/ApprovalGateNode.test.ts +11 -20
  244. package/src/prompts/memoryFlushPrompt.ts +78 -0
  245. package/src/run.ts +17 -6
  246. package/src/scripts/test-bedrock-handoff-autonomous.ts +56 -20
  247. package/src/specs/agent-handoffs-bedrock.integration.test.ts +8 -5
  248. package/src/specs/agent-handoffs.test.ts +8 -2
  249. package/src/stream.ts +4 -6
  250. package/src/tools/AskUser.ts +7 -2
  251. package/src/tools/BrowserTools.ts +3 -5
  252. package/src/tools/ToolNode.ts +150 -13
  253. package/src/tools/__tests__/ToolApproval.test.ts +22 -9
  254. package/src/tools/approval/__tests__/constants.test.ts +4 -4
  255. package/src/tools/approval/constants.ts +2 -2
  256. package/src/tools/memory/__tests__/memoryTools.test.ts +205 -0
  257. package/src/tools/memory/index.ts +96 -0
  258. package/src/tools/memory/memoryAppendTool.ts +101 -0
  259. package/src/tools/memory/memoryGetTool.ts +53 -0
  260. package/src/tools/memory/memorySearchTool.ts +80 -0
  261. package/src/tools/memory/shared.ts +169 -0
  262. package/src/tools/search/search.test.ts +6 -1
  263. package/src/types/graph.ts +16 -3
  264. package/src/utils/__tests__/childAgentContext.test.ts +217 -0
  265. package/src/utils/__tests__/finishReasons.test.ts +55 -0
  266. package/src/utils/__tests__/toolCallNormalization.test.ts +181 -0
  267. package/src/utils/childAgentContext.ts +259 -0
  268. package/src/utils/events.ts +37 -4
  269. package/src/utils/finishReasons.ts +40 -0
  270. package/src/utils/llm.ts +0 -1
  271. package/src/utils/logging.ts +45 -8
  272. 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 (e.g., Ranger)
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 Ranger's SummaryStore when resuming a conversation.
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 (e.g., Ranger) from message_file_map
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;
@@ -1 +1,3 @@
1
+ export declare const mlog: (...args: any[]) => void;
2
+ export declare const mwarn: (...args: any[]) => void;
1
3
  export declare function setupLogging(logFileName: string): void;
@@ -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.1.25",
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('## Multi-Agent Workflow');
625
+ lines.push('## Subagent Context');
626
+ lines.push('');
626
627
  lines.push(
627
- `You are "${displayName}", transferred from "${sourceAgentName}".`
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
- 'Execute only tasks relevant to your role. Routing is already handled if requested, unless you can route further.'
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 ranger validation schemas and MongoDB documents.
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(expect.arrayContaining(['handoff', 'transfer', 'sequence']));
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
+ });
@@ -1,4 +1,5 @@
1
1
  // src/common/index.ts
2
2
  export * from './enum';
3
3
  export * from './constants';
4
+ export * from './spawnPath';
4
5
  export * from '../tools/approval/constants';