@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,96 @@
1
+ /**
2
+ * Public entry for the memory tool family.
3
+ *
4
+ * `buildMemoryTools` is the one function host calls. It returns an array
5
+ * of LangChain tools with scope captured in a closure — the LLM cannot
6
+ * override the `(agentId, userId)` pair through any tool argument.
7
+ *
8
+ * Phase 2: accepts independent `readEnabled` / `writeEnabled` flags that
9
+ * mirror host's existing `agent.memory_read_enabled` /
10
+ * `agent.memory_write_enabled` fields. Either flag can be set without the
11
+ * other: a read-only agent can consult memory without mutating it; a
12
+ * write-only agent can reflect without reading.
13
+ */
14
+ import type { StructuredToolInterface } from '@langchain/core/tools';
15
+ import type { MemoryConfig, MemoryPhase } from '@/memory/types';
16
+ import { createMemorySearchTool } from './memorySearchTool';
17
+ import { createMemoryGetTool } from './memoryGetTool';
18
+ import { createMemoryAppendTool } from './memoryAppendTool';
19
+ import type { MemoryToolBinding } from './shared';
20
+
21
+ export * from './shared';
22
+ export { createMemorySearchTool } from './memorySearchTool';
23
+ export { createMemoryGetTool } from './memoryGetTool';
24
+ export { createMemoryAppendTool } from './memoryAppendTool';
25
+
26
+ export interface BuildMemoryToolsOptions extends MemoryConfig {
27
+ /**
28
+ * Attach `memory_search` + `memory_get`. Maps to
29
+ * `agent.memory_read_enabled` on the host Agent document.
30
+ */
31
+ readEnabled?: boolean;
32
+ /**
33
+ * Attach `memory_append` (still phase-gated to `memory_flushing`).
34
+ * Maps to `agent.memory_write_enabled` on the host Agent document.
35
+ */
36
+ writeEnabled?: boolean;
37
+ /**
38
+ * @deprecated — legacy Phase 1 flag. When set, equivalent to
39
+ * `{ readEnabled: true, writeEnabled: false }`. Kept so existing tests
40
+ * don't break during the Phase 1→2 transition. New callers should pass
41
+ * `readEnabled`/`writeEnabled` explicitly.
42
+ */
43
+ readOnly?: boolean;
44
+ }
45
+
46
+ export function buildMemoryTools(
47
+ options: BuildMemoryToolsOptions
48
+ ): StructuredToolInterface[] {
49
+ // Back-compat for the Phase 1 readOnly flag — maps to read-only mode.
50
+ let readEnabled = options.readEnabled;
51
+ let writeEnabled = options.writeEnabled;
52
+ if (options.readOnly) {
53
+ readEnabled = true;
54
+ writeEnabled = false;
55
+ }
56
+ // Default: if neither flag supplied, attach both (back-compat with the
57
+ // pre-Phase-2 single-flag caller).
58
+ if (readEnabled === undefined && writeEnabled === undefined) {
59
+ readEnabled = true;
60
+ writeEnabled = true;
61
+ }
62
+
63
+ const binding: MemoryToolBinding = {
64
+ backend: options.backend,
65
+ scope: options.scope,
66
+ maxInjectedChars: options.search?.maxInjectedChars,
67
+ searchOptions: {
68
+ mmr: options.search?.mmr,
69
+ temporalDecay: options.search?.temporalDecay,
70
+ citations: options.search?.citations,
71
+ },
72
+ recallTracker: options.recallTracker,
73
+ };
74
+
75
+ const tools: StructuredToolInterface[] = [];
76
+
77
+ if (readEnabled) {
78
+ tools.push(
79
+ createMemorySearchTool(binding) as unknown as StructuredToolInterface,
80
+ createMemoryGetTool(binding) as unknown as StructuredToolInterface
81
+ );
82
+ }
83
+
84
+ if (writeEnabled) {
85
+ const getPhase: () => MemoryPhase =
86
+ options.getPhase ?? ((): MemoryPhase => 'normal');
87
+ tools.push(
88
+ createMemoryAppendTool({
89
+ ...binding,
90
+ getPhase,
91
+ }) as unknown as StructuredToolInterface
92
+ );
93
+ }
94
+
95
+ return tools;
96
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * `memory_append` — the reflection-phase-only write tool.
3
+ *
4
+ * New (not present in upstream verbatim). Upstream enforces append-only via
5
+ * `wrapToolMemoryFlushAppendOnlyWrite`; we combine the tool and the phase
6
+ * gate in one place because the agents library has a simpler graph state.
7
+ *
8
+ * Behaviour:
9
+ * - Outside `memory_flushing` phase: returns an error object instead of
10
+ * calling the backend. The LLM sees this and stops trying.
11
+ * - Inside `memory_flushing`: persists the note via {@link MemoryBackend.append}.
12
+ * - Hard-caps total appends per flush (protects against runaway writes).
13
+ */
14
+ import { tool } from '@langchain/core/tools';
15
+ import {
16
+ DEFAULT_MAX_APPENDS_PER_FLUSH,
17
+ MEMORY_APPEND_DESCRIPTION,
18
+ MEMORY_APPEND_TOOL_NAME,
19
+ MEMORY_PHASE_FLUSHING,
20
+ } from '@/memory/constants';
21
+ import type { MemoryPhase } from '@/memory/types';
22
+ import {
23
+ MemoryAppendSchema,
24
+ toAppendInput,
25
+ type MemoryToolBinding,
26
+ } from './shared';
27
+
28
+ export interface MemoryAppendBinding extends MemoryToolBinding {
29
+ /**
30
+ * Reads the current phase at call-time. Required — without it the append
31
+ * tool rejects every call. Supplied by the graph runtime.
32
+ */
33
+ getPhase: () => MemoryPhase;
34
+ /** Hard cap on appends per flush phase. Default 20. */
35
+ maxAppendsPerFlush?: number;
36
+ }
37
+
38
+ interface AppendResult {
39
+ ok: boolean;
40
+ error?: string;
41
+ path?: string;
42
+ }
43
+
44
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
45
+ export function createMemoryAppendTool(binding: MemoryAppendBinding) {
46
+ const maxAppends =
47
+ binding.maxAppendsPerFlush ?? DEFAULT_MAX_APPENDS_PER_FLUSH;
48
+ let appendsInCurrentFlush = 0;
49
+ let lastSeenPhase: MemoryPhase = 'normal';
50
+
51
+ return tool(
52
+ async (args): Promise<string> => {
53
+ const phase = binding.getPhase();
54
+
55
+ // Reset counter on entry to a new flush.
56
+ if (
57
+ phase === MEMORY_PHASE_FLUSHING &&
58
+ lastSeenPhase !== MEMORY_PHASE_FLUSHING
59
+ ) {
60
+ appendsInCurrentFlush = 0;
61
+ }
62
+ lastSeenPhase = phase;
63
+
64
+ if (phase !== MEMORY_PHASE_FLUSHING) {
65
+ const result: AppendResult = {
66
+ ok: false,
67
+ error:
68
+ 'memory_append can only be called during the reflection (memory_flushing) phase',
69
+ };
70
+ return JSON.stringify(result);
71
+ }
72
+
73
+ if (appendsInCurrentFlush >= maxAppends) {
74
+ const result: AppendResult = {
75
+ ok: false,
76
+ error: `memory_append hit the per-flush cap of ${maxAppends} notes`,
77
+ };
78
+ return JSON.stringify(result);
79
+ }
80
+
81
+ try {
82
+ const input = toAppendInput(args);
83
+ await binding.backend.append(binding.scope, input);
84
+ appendsInCurrentFlush += 1;
85
+ const result: AppendResult = { ok: true, path: input.path };
86
+ return JSON.stringify(result);
87
+ } catch (err) {
88
+ const result: AppendResult = {
89
+ ok: false,
90
+ error: err instanceof Error ? err.message : String(err),
91
+ };
92
+ return JSON.stringify(result);
93
+ }
94
+ },
95
+ {
96
+ name: MEMORY_APPEND_TOOL_NAME,
97
+ description: MEMORY_APPEND_DESCRIPTION,
98
+ schema: MemoryAppendSchema,
99
+ }
100
+ );
101
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * `memory_get` — safe snippet read.
3
+ *
4
+ * Port of upstream `createMemoryGetTool` at
5
+ * `upstream reference`.
6
+ */
7
+ import { tool } from '@langchain/core/tools';
8
+ import {
9
+ MEMORY_GET_DESCRIPTION,
10
+ MEMORY_GET_TOOL_NAME,
11
+ } from '@/memory/constants';
12
+ import {
13
+ MemoryGetSchema,
14
+ type MemoryGetToolResult,
15
+ type MemoryToolBinding,
16
+ } from './shared';
17
+
18
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
19
+ export function createMemoryGetTool(binding: MemoryToolBinding) {
20
+ return tool(
21
+ async (args): Promise<string> => {
22
+ try {
23
+ const result = await binding.backend.get(binding.scope, {
24
+ path: args.path,
25
+ from: args.from,
26
+ lines: args.lines,
27
+ });
28
+ if (!result) {
29
+ const payload: MemoryGetToolResult = { path: args.path, text: '' };
30
+ return JSON.stringify(payload);
31
+ }
32
+ const payload: MemoryGetToolResult = {
33
+ path: result.path,
34
+ text: result.text,
35
+ };
36
+ return JSON.stringify(payload);
37
+ } catch (err) {
38
+ const payload: MemoryGetToolResult = {
39
+ path: args.path,
40
+ text: '',
41
+ disabled: true,
42
+ error: err instanceof Error ? err.message : String(err),
43
+ };
44
+ return JSON.stringify(payload);
45
+ }
46
+ },
47
+ {
48
+ name: MEMORY_GET_TOOL_NAME,
49
+ description: MEMORY_GET_DESCRIPTION,
50
+ schema: MemoryGetSchema,
51
+ }
52
+ );
53
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * `memory_search` — the mandatory-recall tool.
3
+ *
4
+ * Port of upstream `createMemorySearchTool` at
5
+ * `upstream reference`. The tool description
6
+ * is verbatim "Mandatory recall step..." from upstream — this is the load-
7
+ * bearing line that turns "the agent may recall" into "the agent will recall".
8
+ */
9
+ import { tool } from '@langchain/core/tools';
10
+ import {
11
+ MEMORY_SEARCH_DESCRIPTION,
12
+ MEMORY_SEARCH_TOOL_NAME,
13
+ } from '@/memory/constants';
14
+ import {
15
+ buildMemorySearchUnavailableResult,
16
+ clampResultsByInjectedChars,
17
+ MemorySearchSchema,
18
+ type MemorySearchToolResult,
19
+ type MemoryToolBinding,
20
+ } from './shared';
21
+
22
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
23
+ export function createMemorySearchTool(binding: MemoryToolBinding) {
24
+ return tool(
25
+ async (args): Promise<string> => {
26
+ try {
27
+ const entries = await binding.backend.search(
28
+ binding.scope,
29
+ args.query,
30
+ {
31
+ maxResults: args.maxResults,
32
+ minScore: args.minScore,
33
+ mmr: binding.searchOptions?.mmr,
34
+ temporalDecay: binding.searchOptions?.temporalDecay,
35
+ citations: binding.searchOptions?.citations,
36
+ }
37
+ );
38
+ const clamped = clampResultsByInjectedChars(
39
+ entries,
40
+ binding.maxInjectedChars
41
+ );
42
+
43
+ // [phase2-recall-tracking] debug: fire-and-forget best-effort record
44
+ if (binding.recallTracker && clamped.length > 0) {
45
+ void binding.recallTracker
46
+ .record({
47
+ agentId: binding.scope.agentId,
48
+ query: args.query,
49
+ hits: clamped.map((e) => ({
50
+ id: e.id,
51
+ path: e.path,
52
+ score: e.score,
53
+ })),
54
+ })
55
+ .catch(() => undefined);
56
+ }
57
+
58
+ const payload: MemorySearchToolResult = {
59
+ results: clamped.map((entry) => ({
60
+ id: entry.id,
61
+ path: entry.path,
62
+ content: entry.content,
63
+ score: entry.score,
64
+ createdAt: entry.createdAt,
65
+ citation: entry.citation,
66
+ })),
67
+ };
68
+ return JSON.stringify(payload);
69
+ } catch (err) {
70
+ const message = err instanceof Error ? err.message : String(err);
71
+ return JSON.stringify(buildMemorySearchUnavailableResult(message));
72
+ }
73
+ },
74
+ {
75
+ name: MEMORY_SEARCH_TOOL_NAME,
76
+ description: MEMORY_SEARCH_DESCRIPTION,
77
+ schema: MemorySearchSchema,
78
+ }
79
+ );
80
+ }
@@ -0,0 +1,169 @@
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 {
10
+ DEFAULT_MAX_INJECTED_CHARS,
11
+ MEMORY_PATH_PREFIX,
12
+ } from '@/memory/constants';
13
+ import type {
14
+ MemoryAppendInput,
15
+ MemoryBackend,
16
+ MemoryEntry,
17
+ MemoryScope,
18
+ } from '@/memory/types';
19
+
20
+ export const MemorySearchSchema = z.object({
21
+ query: z.string().describe('Natural-language query to search memory for'),
22
+ maxResults: z
23
+ .number()
24
+ .int()
25
+ .min(1)
26
+ .max(50)
27
+ .optional()
28
+ .describe('Maximum number of results to return (default 10)'),
29
+ minScore: z
30
+ .number()
31
+ .min(0)
32
+ .max(1)
33
+ .optional()
34
+ .describe('Minimum hybrid score (0..1) below which results are dropped'),
35
+ });
36
+
37
+ export const MemoryGetSchema = z.object({
38
+ path: z
39
+ .string()
40
+ .describe(
41
+ 'Memory path, e.g. "memory/decisions.md". Must start with "memory/"'
42
+ ),
43
+ from: z
44
+ .number()
45
+ .int()
46
+ .min(1)
47
+ .optional()
48
+ .describe('Starting line number (1-indexed)'),
49
+ lines: z
50
+ .number()
51
+ .int()
52
+ .min(1)
53
+ .optional()
54
+ .describe('Number of lines to read starting at `from`'),
55
+ });
56
+
57
+ export const MemoryAppendSchema = z.object({
58
+ path: z
59
+ .string()
60
+ .describe(
61
+ 'Memory path to append to (e.g. "memory/decisions.md"). Must start with "memory/"'
62
+ ),
63
+ content: z
64
+ .string()
65
+ .min(1)
66
+ .describe(
67
+ "Note content in the agent's own voice. Markdown allowed. Each call appends an immutable entry."
68
+ ),
69
+ });
70
+
71
+ export type MemorySearchArgs = z.infer<typeof MemorySearchSchema>;
72
+ export type MemoryGetArgs = z.infer<typeof MemoryGetSchema>;
73
+ export type MemoryAppendArgs = z.infer<typeof MemoryAppendSchema>;
74
+
75
+ /** Shape returned by `memory_search` — serialised as the tool result string. */
76
+ export interface MemorySearchToolResult {
77
+ results: Array<
78
+ Pick<MemoryEntry, 'id' | 'path' | 'content' | 'score' | 'createdAt'> & {
79
+ citation?: string;
80
+ }
81
+ >;
82
+ disabled?: boolean;
83
+ unavailable?: boolean;
84
+ error?: string;
85
+ warning?: string;
86
+ action?: string;
87
+ }
88
+
89
+ /** Shape returned by `memory_get`. */
90
+ export interface MemoryGetToolResult {
91
+ path: string;
92
+ text: string;
93
+ disabled?: boolean;
94
+ error?: string;
95
+ }
96
+
97
+ export function buildMemorySearchUnavailableResult(
98
+ error: string | undefined
99
+ ): MemorySearchToolResult {
100
+ const reason =
101
+ (error ?? 'memory search unavailable').trim() ||
102
+ 'memory search unavailable';
103
+ const isQuota = /insufficient_quota|quota|429/i.test(reason);
104
+ return {
105
+ results: [],
106
+ disabled: true,
107
+ unavailable: true,
108
+ error: reason,
109
+ warning: isQuota
110
+ ? 'Memory search is unavailable because the embedding provider quota is exhausted.'
111
+ : 'Memory search is unavailable due to an embedding/provider error.',
112
+ action: isQuota
113
+ ? 'Top up or switch embedding provider, then retry memory_search.'
114
+ : 'Check embedding provider configuration and retry memory_search.',
115
+ };
116
+ }
117
+
118
+ /**
119
+ * Clamp a ranked result list to a total character budget.
120
+ * Ported from upstream `tools.citations.ts::clampResultsByInjectedChars`.
121
+ */
122
+ export function clampResultsByInjectedChars<T extends { content: string }>(
123
+ results: T[],
124
+ maxInjectedChars: number = DEFAULT_MAX_INJECTED_CHARS
125
+ ): T[] {
126
+ const out: T[] = [];
127
+ let total = 0;
128
+ for (const result of results) {
129
+ const size = result.content.length ?? 0;
130
+ if (total + size > maxInjectedChars && out.length > 0) break;
131
+ out.push(result);
132
+ total += size;
133
+ }
134
+ return out;
135
+ }
136
+
137
+ export interface MemoryToolBinding {
138
+ backend: MemoryBackend;
139
+ scope: MemoryScope;
140
+ maxInjectedChars?: number;
141
+ /** Phase 2 — per-call search options propagated into backend.search. */
142
+ searchOptions?: {
143
+ mmr?: { enabled?: boolean; lambda?: number };
144
+ temporalDecay?: { enabled?: boolean; halfLifeDays?: number };
145
+ citations?: 'on' | 'off' | 'auto';
146
+ };
147
+ /** Phase 2 — optional best-effort recall recorder. */
148
+ recallTracker?: {
149
+ record(params: {
150
+ agentId: string;
151
+ query: string;
152
+ hits: Array<{ id: string; path: string; score: number }>;
153
+ }): Promise<void>;
154
+ };
155
+ }
156
+
157
+ export function assertAppendAllowed(path: string): void {
158
+ if (!path || !path.startsWith(MEMORY_PATH_PREFIX)) {
159
+ throw new Error(
160
+ `memory_append path must start with "${MEMORY_PATH_PREFIX}"`
161
+ );
162
+ }
163
+ }
164
+
165
+ /** Normalise an append call into the backend input shape. */
166
+ export function toAppendInput(args: MemoryAppendArgs): MemoryAppendInput {
167
+ assertAppendAllowed(args.path);
168
+ return { path: args.path, content: args.content };
169
+ }
@@ -12,7 +12,12 @@ describe('createSearchAPI', () => {
12
12
  const mockSerperResponse = {
13
13
  data: {
14
14
  organic: [
15
- { position: 1, title: 'Test', link: 'https://example.com', snippet: 'test' },
15
+ {
16
+ position: 1,
17
+ title: 'Test',
18
+ link: 'https://example.com',
19
+ snippet: 'test',
20
+ },
16
21
  ],
17
22
  topStories: [],
18
23
  images: [],
@@ -56,6 +56,12 @@ export type AgentTransitionEvent = {
56
56
  destinationAgentName: string;
57
57
  edgeType: string; // 'handoff' | 'transfer' | 'sequence'
58
58
  timestamp: number;
59
+ /** When true, this event signals handoff completion (child → parent return) */
60
+ isCompletion?: boolean;
61
+ /** Duration of child agent execution in milliseconds (only on completion events) */
62
+ durationMs?: number;
63
+ /** Length of child agent result text in characters (only on completion events) */
64
+ resultLength?: number;
59
65
  };
60
66
 
61
67
  export type GraphNode = GraphNodeKeys | typeof START;
@@ -415,6 +421,13 @@ export type GraphEdge = {
415
421
  * Defaults to DEFAULT_HANDOFF_MAX_RESULT_CHARS (32768 chars, ~8192 tokens).
416
422
  */
417
423
  maxResultChars?: number;
424
+ /**
425
+ * For handoff edges: When true, the child agent receives the full parent
426
+ * conversation history plus the orchestrator's instructions appended.
427
+ * When false (default), the child only receives the orchestrator's scoped
428
+ * instructions — isolated from the parent conversation.
429
+ */
430
+ passthrough?: boolean;
418
431
  /**
419
432
  * Approval gate configuration for sequence edges.
420
433
  * When set, inserts an approval gate node between source and destination.
@@ -696,7 +709,7 @@ export interface AgentInputs {
696
709
  discoveredTools?: string[];
697
710
  /**
698
711
  * Optional callback for summarizing messages that were pruned from context.
699
- * When provided, discarded messages are summarized by the caller (e.g., Ranger)
712
+ * When provided, discarded messages are summarized by the host caller
700
713
  * using a cheap LLM call, and the summary is prepended to the context.
701
714
  */
702
715
  summarizeCallback?: (
@@ -706,7 +719,7 @@ export interface AgentInputs {
706
719
  * Pre-existing summary text loaded from persistent storage (MongoDB/Redis).
707
720
  * When provided, this summary is injected into the initial message context
708
721
  * so the agent has prior conversation history even on new turns.
709
- * Set by Ranger's SummaryStore when resuming a conversation.
722
+ * Set by the host's summary store when resuming a conversation.
710
723
  */
711
724
  persistedSummary?: string;
712
725
  /**
@@ -727,7 +740,7 @@ export interface AgentInputs {
727
740
  * - file_search (RAG semantic search over embedded files)
728
741
  * - content_tool read (by contentId for exact file retrieval)
729
742
  *
730
- * Built by the orchestrator (e.g., Ranger) from message_file_map
743
+ * Built by the host orchestrator from message_file_map
731
744
  * and metadata.context_files across all conversation messages.
732
745
  */
733
746
  fileManifest?: FileManifestEntry[];