@centerseedwu/naru-agent 0.2.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 (247) hide show
  1. package/README.md +392 -0
  2. package/dist/agent.d.ts +39 -0
  3. package/dist/agent.d.ts.map +1 -0
  4. package/dist/agent.js +578 -0
  5. package/dist/agent.js.map +1 -0
  6. package/dist/compression/base.d.ts +12 -0
  7. package/dist/compression/base.d.ts.map +1 -0
  8. package/dist/compression/base.js +2 -0
  9. package/dist/compression/base.js.map +1 -0
  10. package/dist/compression/compressor.d.ts +20 -0
  11. package/dist/compression/compressor.d.ts.map +1 -0
  12. package/dist/compression/compressor.js +52 -0
  13. package/dist/compression/compressor.js.map +1 -0
  14. package/dist/compression/in-memory-store.d.ts +8 -0
  15. package/dist/compression/in-memory-store.d.ts.map +1 -0
  16. package/dist/compression/in-memory-store.js +13 -0
  17. package/dist/compression/in-memory-store.js.map +1 -0
  18. package/dist/decision/index.d.ts +7 -0
  19. package/dist/decision/index.d.ts.map +1 -0
  20. package/dist/decision/index.js +4 -0
  21. package/dist/decision/index.js.map +1 -0
  22. package/dist/decision/llm-structured-classifier.d.ts +18 -0
  23. package/dist/decision/llm-structured-classifier.d.ts.map +1 -0
  24. package/dist/decision/llm-structured-classifier.js +44 -0
  25. package/dist/decision/llm-structured-classifier.js.map +1 -0
  26. package/dist/decision/tool-planner.d.ts +14 -0
  27. package/dist/decision/tool-planner.d.ts.map +1 -0
  28. package/dist/decision/tool-planner.js +35 -0
  29. package/dist/decision/tool-planner.js.map +1 -0
  30. package/dist/decision/types.d.ts +53 -0
  31. package/dist/decision/types.d.ts.map +1 -0
  32. package/dist/decision/types.js +12 -0
  33. package/dist/decision/types.js.map +1 -0
  34. package/dist/event-bus.d.ts +10 -0
  35. package/dist/event-bus.d.ts.map +1 -0
  36. package/dist/event-bus.js +34 -0
  37. package/dist/event-bus.js.map +1 -0
  38. package/dist/guardrails/base.d.ts +10 -0
  39. package/dist/guardrails/base.d.ts.map +1 -0
  40. package/dist/guardrails/base.js +2 -0
  41. package/dist/guardrails/base.js.map +1 -0
  42. package/dist/guardrails/keyword.d.ts +15 -0
  43. package/dist/guardrails/keyword.d.ts.map +1 -0
  44. package/dist/guardrails/keyword.js +37 -0
  45. package/dist/guardrails/keyword.js.map +1 -0
  46. package/dist/index.d.ts +51 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +39 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/intent/base.d.ts +19 -0
  51. package/dist/intent/base.d.ts.map +1 -0
  52. package/dist/intent/base.js +2 -0
  53. package/dist/intent/base.js.map +1 -0
  54. package/dist/intent/llm-classifier.d.ts +22 -0
  55. package/dist/intent/llm-classifier.d.ts.map +1 -0
  56. package/dist/intent/llm-classifier.js +49 -0
  57. package/dist/intent/llm-classifier.js.map +1 -0
  58. package/dist/intent/tool-calling-classifier.d.ts +16 -0
  59. package/dist/intent/tool-calling-classifier.d.ts.map +1 -0
  60. package/dist/intent/tool-calling-classifier.js +56 -0
  61. package/dist/intent/tool-calling-classifier.js.map +1 -0
  62. package/dist/knowledge/base.d.ts +14 -0
  63. package/dist/knowledge/base.d.ts.map +1 -0
  64. package/dist/knowledge/base.js +10 -0
  65. package/dist/knowledge/base.js.map +1 -0
  66. package/dist/knowledge/chroma-store.d.ts +25 -0
  67. package/dist/knowledge/chroma-store.d.ts.map +1 -0
  68. package/dist/knowledge/chroma-store.js +70 -0
  69. package/dist/knowledge/chroma-store.js.map +1 -0
  70. package/dist/knowledge/contextualizer.d.ts +17 -0
  71. package/dist/knowledge/contextualizer.d.ts.map +1 -0
  72. package/dist/knowledge/contextualizer.js +47 -0
  73. package/dist/knowledge/contextualizer.js.map +1 -0
  74. package/dist/knowledge/graph-store.d.ts +22 -0
  75. package/dist/knowledge/graph-store.d.ts.map +1 -0
  76. package/dist/knowledge/graph-store.js +116 -0
  77. package/dist/knowledge/graph-store.js.map +1 -0
  78. package/dist/knowledge/hybrid-store.d.ts +16 -0
  79. package/dist/knowledge/hybrid-store.d.ts.map +1 -0
  80. package/dist/knowledge/hybrid-store.js +39 -0
  81. package/dist/knowledge/hybrid-store.js.map +1 -0
  82. package/dist/knowledge/in-memory-store.d.ts +27 -0
  83. package/dist/knowledge/in-memory-store.d.ts.map +1 -0
  84. package/dist/knowledge/in-memory-store.js +54 -0
  85. package/dist/knowledge/in-memory-store.js.map +1 -0
  86. package/dist/knowledge/pgvector-store.d.ts +27 -0
  87. package/dist/knowledge/pgvector-store.d.ts.map +1 -0
  88. package/dist/knowledge/pgvector-store.js +68 -0
  89. package/dist/knowledge/pgvector-store.js.map +1 -0
  90. package/dist/memory/base.d.ts +21 -0
  91. package/dist/memory/base.d.ts.map +1 -0
  92. package/dist/memory/base.js +9 -0
  93. package/dist/memory/base.js.map +1 -0
  94. package/dist/memory/in-memory-store.d.ts +10 -0
  95. package/dist/memory/in-memory-store.d.ts.map +1 -0
  96. package/dist/memory/in-memory-store.js +35 -0
  97. package/dist/memory/in-memory-store.js.map +1 -0
  98. package/dist/memory/manager.d.ts +29 -0
  99. package/dist/memory/manager.d.ts.map +1 -0
  100. package/dist/memory/manager.js +121 -0
  101. package/dist/memory/manager.js.map +1 -0
  102. package/dist/memory/mem0-manager.d.ts +32 -0
  103. package/dist/memory/mem0-manager.d.ts.map +1 -0
  104. package/dist/memory/mem0-manager.js +77 -0
  105. package/dist/memory/mem0-manager.js.map +1 -0
  106. package/dist/memory/pgvector-store.d.ts +22 -0
  107. package/dist/memory/pgvector-store.d.ts.map +1 -0
  108. package/dist/memory/pgvector-store.js +89 -0
  109. package/dist/memory/pgvector-store.js.map +1 -0
  110. package/dist/orchestration/channel.d.ts +18 -0
  111. package/dist/orchestration/channel.d.ts.map +1 -0
  112. package/dist/orchestration/channel.js +2 -0
  113. package/dist/orchestration/channel.js.map +1 -0
  114. package/dist/orchestration/executor.d.ts +19 -0
  115. package/dist/orchestration/executor.d.ts.map +1 -0
  116. package/dist/orchestration/executor.js +2 -0
  117. package/dist/orchestration/executor.js.map +1 -0
  118. package/dist/orchestration/index.d.ts +13 -0
  119. package/dist/orchestration/index.d.ts.map +1 -0
  120. package/dist/orchestration/index.js +9 -0
  121. package/dist/orchestration/index.js.map +1 -0
  122. package/dist/orchestration/intent.d.ts +49 -0
  123. package/dist/orchestration/intent.d.ts.map +1 -0
  124. package/dist/orchestration/intent.js +57 -0
  125. package/dist/orchestration/intent.js.map +1 -0
  126. package/dist/orchestration/orchestrator.d.ts +59 -0
  127. package/dist/orchestration/orchestrator.d.ts.map +1 -0
  128. package/dist/orchestration/orchestrator.js +216 -0
  129. package/dist/orchestration/orchestrator.js.map +1 -0
  130. package/dist/orchestration/pending.d.ts +28 -0
  131. package/dist/orchestration/pending.d.ts.map +1 -0
  132. package/dist/orchestration/pending.js +32 -0
  133. package/dist/orchestration/pending.js.map +1 -0
  134. package/dist/orchestration/result.d.ts +24 -0
  135. package/dist/orchestration/result.d.ts.map +1 -0
  136. package/dist/orchestration/result.js +2 -0
  137. package/dist/orchestration/result.js.map +1 -0
  138. package/dist/orchestration/session-state.d.ts +24 -0
  139. package/dist/orchestration/session-state.d.ts.map +1 -0
  140. package/dist/orchestration/session-state.js +19 -0
  141. package/dist/orchestration/session-state.js.map +1 -0
  142. package/dist/orchestration/trace.d.ts +20 -0
  143. package/dist/orchestration/trace.d.ts.map +1 -0
  144. package/dist/orchestration/trace.js +2 -0
  145. package/dist/orchestration/trace.js.map +1 -0
  146. package/dist/session/base.d.ts +7 -0
  147. package/dist/session/base.d.ts.map +1 -0
  148. package/dist/session/base.js +2 -0
  149. package/dist/session/base.js.map +1 -0
  150. package/dist/session/in-memory-store.d.ts +9 -0
  151. package/dist/session/in-memory-store.d.ts.map +1 -0
  152. package/dist/session/in-memory-store.js +13 -0
  153. package/dist/session/in-memory-store.js.map +1 -0
  154. package/dist/session/redis-store.d.ts +16 -0
  155. package/dist/session/redis-store.d.ts.map +1 -0
  156. package/dist/session/redis-store.js +29 -0
  157. package/dist/session/redis-store.js.map +1 -0
  158. package/dist/skills/base.d.ts +38 -0
  159. package/dist/skills/base.d.ts.map +1 -0
  160. package/dist/skills/base.js +24 -0
  161. package/dist/skills/base.js.map +1 -0
  162. package/dist/skills/registry.d.ts +11 -0
  163. package/dist/skills/registry.d.ts.map +1 -0
  164. package/dist/skills/registry.js +37 -0
  165. package/dist/skills/registry.js.map +1 -0
  166. package/dist/skills/selectors.d.ts +23 -0
  167. package/dist/skills/selectors.d.ts.map +1 -0
  168. package/dist/skills/selectors.js +52 -0
  169. package/dist/skills/selectors.js.map +1 -0
  170. package/dist/tools/base.d.ts +34 -0
  171. package/dist/tools/base.d.ts.map +1 -0
  172. package/dist/tools/base.js +22 -0
  173. package/dist/tools/base.js.map +1 -0
  174. package/dist/tools/vercel-adapter.d.ts +17 -0
  175. package/dist/tools/vercel-adapter.d.ts.map +1 -0
  176. package/dist/tools/vercel-adapter.js +66 -0
  177. package/dist/tools/vercel-adapter.js.map +1 -0
  178. package/dist/tracing/collector.d.ts +16 -0
  179. package/dist/tracing/collector.d.ts.map +1 -0
  180. package/dist/tracing/collector.js +92 -0
  181. package/dist/tracing/collector.js.map +1 -0
  182. package/dist/tracing/exporters/jsonl.d.ts +13 -0
  183. package/dist/tracing/exporters/jsonl.d.ts.map +1 -0
  184. package/dist/tracing/exporters/jsonl.js +14 -0
  185. package/dist/tracing/exporters/jsonl.js.map +1 -0
  186. package/dist/tracing/trace.d.ts +34 -0
  187. package/dist/tracing/trace.d.ts.map +1 -0
  188. package/dist/tracing/trace.js +28 -0
  189. package/dist/tracing/trace.js.map +1 -0
  190. package/dist/types.d.ts +77 -0
  191. package/dist/types.d.ts.map +1 -0
  192. package/dist/types.js +7 -0
  193. package/dist/types.js.map +1 -0
  194. package/dist/utils/math.d.ts +3 -0
  195. package/dist/utils/math.d.ts.map +1 -0
  196. package/dist/utils/math.js +14 -0
  197. package/dist/utils/math.js.map +1 -0
  198. package/dist-cjs/agent.js +581 -0
  199. package/dist-cjs/compression/base.js +2 -0
  200. package/dist-cjs/compression/compressor.js +55 -0
  201. package/dist-cjs/compression/in-memory-store.js +16 -0
  202. package/dist-cjs/decision/index.js +9 -0
  203. package/dist-cjs/decision/llm-structured-classifier.js +47 -0
  204. package/dist-cjs/decision/tool-planner.js +38 -0
  205. package/dist-cjs/decision/types.js +15 -0
  206. package/dist-cjs/event-bus.js +37 -0
  207. package/dist-cjs/guardrails/base.js +2 -0
  208. package/dist-cjs/guardrails/keyword.js +40 -0
  209. package/dist-cjs/index.js +84 -0
  210. package/dist-cjs/intent/base.js +2 -0
  211. package/dist-cjs/intent/llm-classifier.js +52 -0
  212. package/dist-cjs/intent/tool-calling-classifier.js +59 -0
  213. package/dist-cjs/knowledge/base.js +12 -0
  214. package/dist-cjs/knowledge/chroma-store.js +106 -0
  215. package/dist-cjs/knowledge/contextualizer.js +50 -0
  216. package/dist-cjs/knowledge/graph-store.js +152 -0
  217. package/dist-cjs/knowledge/hybrid-store.js +42 -0
  218. package/dist-cjs/knowledge/in-memory-store.js +57 -0
  219. package/dist-cjs/knowledge/pgvector-store.js +71 -0
  220. package/dist-cjs/memory/base.js +11 -0
  221. package/dist-cjs/memory/in-memory-store.js +38 -0
  222. package/dist-cjs/memory/manager.js +124 -0
  223. package/dist-cjs/memory/mem0-manager.js +113 -0
  224. package/dist-cjs/memory/pgvector-store.js +92 -0
  225. package/dist-cjs/orchestration/channel.js +2 -0
  226. package/dist-cjs/orchestration/executor.js +2 -0
  227. package/dist-cjs/orchestration/index.js +17 -0
  228. package/dist-cjs/orchestration/intent.js +61 -0
  229. package/dist-cjs/orchestration/orchestrator.js +219 -0
  230. package/dist-cjs/orchestration/pending.js +36 -0
  231. package/dist-cjs/orchestration/result.js +2 -0
  232. package/dist-cjs/orchestration/session-state.js +22 -0
  233. package/dist-cjs/orchestration/trace.js +2 -0
  234. package/dist-cjs/session/base.js +2 -0
  235. package/dist-cjs/session/in-memory-store.js +16 -0
  236. package/dist-cjs/session/redis-store.js +32 -0
  237. package/dist-cjs/skills/base.js +27 -0
  238. package/dist-cjs/skills/registry.js +40 -0
  239. package/dist-cjs/skills/selectors.js +56 -0
  240. package/dist-cjs/tools/base.js +24 -0
  241. package/dist-cjs/tools/vercel-adapter.js +69 -0
  242. package/dist-cjs/tracing/collector.js +95 -0
  243. package/dist-cjs/tracing/exporters/jsonl.js +17 -0
  244. package/dist-cjs/tracing/trace.js +31 -0
  245. package/dist-cjs/types.js +9 -0
  246. package/dist-cjs/utils/math.js +17 -0
  247. package/package.json +70 -0
@@ -0,0 +1,581 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NaruAgent = void 0;
4
+ const ai_1 = require("ai");
5
+ const uuid_1 = require("uuid");
6
+ const types_js_1 = require("./types.js");
7
+ const types_js_2 = require("./decision/types.js");
8
+ const tool_planner_js_1 = require("./decision/tool-planner.js");
9
+ const event_bus_js_1 = require("./event-bus.js");
10
+ const vercel_adapter_js_1 = require("./tools/vercel-adapter.js");
11
+ const registry_js_1 = require("./skills/registry.js");
12
+ const collector_js_1 = require("./tracing/collector.js");
13
+ const compressor_js_1 = require("./compression/compressor.js");
14
+ const in_memory_store_js_1 = require("./compression/in-memory-store.js");
15
+ const FALLBACK_RESPONSE = "抱歉,我剛剛出了一點狀況,可以再說一次嗎?";
16
+ class NaruAgent {
17
+ model;
18
+ name;
19
+ instructions;
20
+ config;
21
+ eventBus;
22
+ skillRegistry = null;
23
+ traceCollector = null;
24
+ contextCompressor = null;
25
+ traceExporters;
26
+ constructor(config) {
27
+ this.model = config.model;
28
+ this.name = config.name ?? "assistant";
29
+ this.instructions = config.instructions ?? [];
30
+ this.config = config;
31
+ this.eventBus = config.eventBus ?? new event_bus_js_1.EventBus();
32
+ this.traceExporters = config.traceExporters ?? [];
33
+ // Skills
34
+ if (config.skills && config.skills.length > 0) {
35
+ this.skillRegistry = new registry_js_1.SkillRegistry(config.skills, config.skillSelector, config.maxActiveSkills ?? 3);
36
+ }
37
+ // Tracing
38
+ if (this.traceExporters.length > 0) {
39
+ this.traceCollector = new collector_js_1.TraceCollector(this.eventBus);
40
+ }
41
+ // Context compression
42
+ if (config.contextCompression) {
43
+ const summaryStore = config.summaryStore ?? new in_memory_store_js_1.InMemorySummaryStore();
44
+ const summaryModel = config.summaryModel ?? config.model;
45
+ this.contextCompressor = new compressor_js_1.ContextCompressor({
46
+ summaryStore,
47
+ summaryModel,
48
+ keepLastRounds: config.compressionKeepLastRounds ?? 5,
49
+ thresholdRounds: config.compressionThresholdRounds ?? 5,
50
+ });
51
+ }
52
+ }
53
+ /**
54
+ * Main chat method — full orchestration loop.
55
+ */
56
+ async chat(message, options) {
57
+ const startTime = Date.now();
58
+ const userId = options?.userId;
59
+ const sessionId = options?.sessionId ?? (0, uuid_1.v4)();
60
+ const timings = {};
61
+ // Start trace
62
+ const traceId = this.traceCollector?.startTrace(message, userId, sessionId);
63
+ // === 1. Input Guardrails ===
64
+ if (this.config.guardrails) {
65
+ for (const guardrail of this.config.guardrails) {
66
+ const result = await guardrail.checkInput(message);
67
+ if (!result.passed) {
68
+ const naruResult = this.makeResult({
69
+ content: result.reason ?? "Blocked.",
70
+ blocked: true,
71
+ sessionId,
72
+ traceId: traceId ?? null,
73
+ });
74
+ this.traceCollector?.endTrace(naruResult);
75
+ return naruResult;
76
+ }
77
+ }
78
+ }
79
+ // === 2. Prefetch ===
80
+ const prefetchStart = Date.now();
81
+ const prefetched = await this.prefetch(message, sessionId, options);
82
+ timings.prefetch = Date.now() - prefetchStart;
83
+ const { intent, activeTools } = prefetched;
84
+ // === 3. Load Session History ===
85
+ let history = [];
86
+ if (this.config.sessionStore && sessionId) {
87
+ const stored = await this.config.sessionStore.get(sessionId);
88
+ if (stored) {
89
+ const limit = this.config.numHistoryMessages;
90
+ history = limit ? stored.slice(-limit) : stored;
91
+ }
92
+ }
93
+ // === 4. LLM Call ===
94
+ this.eventBus.emit("before_llm_call");
95
+ const llmStart = Date.now();
96
+ const vercelTools = activeTools.length > 0 ? (0, vercel_adapter_js_1.toVercelTools)(activeTools) : undefined;
97
+ const toolChoice = activeTools.length > 0
98
+ ? this.resolveToolChoice({
99
+ message,
100
+ userId,
101
+ sessionId,
102
+ intent,
103
+ activeTools,
104
+ })
105
+ : undefined;
106
+ let content = "";
107
+ const toolCallNames = [];
108
+ let usage = {
109
+ promptTokens: 0,
110
+ completionTokens: 0,
111
+ totalTokens: 0,
112
+ };
113
+ try {
114
+ const result = await (0, ai_1.generateText)({
115
+ model: this.model,
116
+ system: prefetched.systemPrompt,
117
+ messages: [
118
+ ...history,
119
+ { role: "user", content: message },
120
+ ],
121
+ tools: vercelTools,
122
+ toolChoice,
123
+ stopWhen: (0, ai_1.stepCountIs)(this.config.toolCallLimit ?? 10),
124
+ temperature: this.config.temperature ?? 0.7,
125
+ providerOptions: this.config.promptCaching ? {
126
+ anthropic: { cacheControl: true },
127
+ } : undefined,
128
+ });
129
+ content = result.text || "";
130
+ // Extract tool calls from all steps
131
+ for (const step of result.steps ?? []) {
132
+ for (const tc of step.toolCalls ?? []) {
133
+ toolCallNames.push(tc.toolName);
134
+ }
135
+ }
136
+ usage = (0, types_js_1.normalizeUsage)(result.usage);
137
+ }
138
+ catch (err) {
139
+ console.error("[NaruAgent] generateText failed:", err);
140
+ content = FALLBACK_RESPONSE;
141
+ }
142
+ timings.llm = Date.now() - llmStart;
143
+ this.eventBus.emit("after_llm_call");
144
+ // === 5. Output Guardrails ===
145
+ if (this.config.guardrails && content) {
146
+ for (const guardrail of this.config.guardrails) {
147
+ const result = await guardrail.checkOutput(content);
148
+ if (!result.passed) {
149
+ content = result.modifiedText ?? "I cannot provide that response.";
150
+ }
151
+ }
152
+ }
153
+ // === 6. Build Result ===
154
+ timings.total = Date.now() - startTime;
155
+ const naruResult = this.makeResult({
156
+ content,
157
+ blocked: false,
158
+ usage,
159
+ intent,
160
+ toolCalls: toolCallNames,
161
+ timings,
162
+ sessionId,
163
+ traceId: traceId ?? null,
164
+ });
165
+ // === 7. Background Tasks ===
166
+ const updatedHistory = [
167
+ ...history,
168
+ { role: "user", content: message },
169
+ { role: "assistant", content },
170
+ ];
171
+ if (this.config.sessionStore && sessionId) {
172
+ this.config.sessionStore.save(sessionId, updatedHistory).catch(() => { });
173
+ }
174
+ if (this.config.memoryManager && userId) {
175
+ this.config.memoryManager
176
+ .add(userId, [
177
+ { role: "user", content: message },
178
+ { role: "assistant", content },
179
+ ])
180
+ .catch(() => { });
181
+ }
182
+ if (this.contextCompressor && sessionId) {
183
+ this.contextCompressor
184
+ .maybeCompress(sessionId, updatedHistory)
185
+ .catch(() => { });
186
+ }
187
+ // Trace export
188
+ const trace = this.traceCollector?.endTrace(naruResult) ?? null;
189
+ naruResult.trace = trace;
190
+ if (trace) {
191
+ for (const exporter of this.traceExporters) {
192
+ exporter.export(trace).catch(() => { });
193
+ }
194
+ }
195
+ this.eventBus.emit("chat_complete", naruResult);
196
+ return naruResult;
197
+ }
198
+ /**
199
+ * Shared prefetch logic for chat() and chatStream().
200
+ */
201
+ async prefetch(message, sessionId, options, skip) {
202
+ const userId = options?.userId;
203
+ const prefetchTimeout = this.config.prefetchTimeout ?? 10000;
204
+ // === Parallel Prefetch ===
205
+ const prefetchTasks = {};
206
+ if (this.config.memoryManager && userId) {
207
+ prefetchTasks.memory = this.config.memoryManager
208
+ .getContextString(userId, message)
209
+ .catch(() => "");
210
+ }
211
+ if (this.config.intentClassifier && !skip?.intent) {
212
+ prefetchTasks.intent = this.config.intentClassifier
213
+ .classify(message)
214
+ .catch(() => ({ needsKnowledge: true, needsTools: true, raw: "YY" }));
215
+ }
216
+ if (this.contextCompressor && sessionId) {
217
+ prefetchTasks.summary = this.contextCompressor
218
+ .getSummary(sessionId)
219
+ .catch(() => null);
220
+ }
221
+ if (this.config.prefetchHooks) {
222
+ for (let i = 0; i < this.config.prefetchHooks.length; i++) {
223
+ const hook = this.config.prefetchHooks[i];
224
+ prefetchTasks[`hook_${i}`] = hook(message, userId).catch(() => "");
225
+ }
226
+ }
227
+ const prefetchKeys = Object.keys(prefetchTasks);
228
+ const prefetchResults = await Promise.allSettled(prefetchKeys.map((key) => Promise.race([
229
+ prefetchTasks[key],
230
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), prefetchTimeout)),
231
+ ])));
232
+ const prefetchData = {};
233
+ for (let i = 0; i < prefetchKeys.length; i++) {
234
+ const result = prefetchResults[i];
235
+ prefetchData[prefetchKeys[i]] =
236
+ result.status === "fulfilled" ? result.value : null;
237
+ }
238
+ const memoryContext = prefetchData.memory ?? "";
239
+ const intent = prefetchData.intent ?? null;
240
+ const summaryData = prefetchData.summary;
241
+ if (memoryContext)
242
+ this.eventBus.emit("memory_retrieved", { memoryContext });
243
+ if (intent)
244
+ this.eventBus.emit("intent_classified", intent);
245
+ // === Knowledge Retrieval ===
246
+ let knowledgeContext = "";
247
+ if (this.config.knowledgeStore &&
248
+ (intent?.needsKnowledge ?? true)) {
249
+ try {
250
+ const results = await this.config.knowledgeStore.search(message, this.config.knowledgeTopK ?? 3);
251
+ knowledgeContext = this.config.knowledgeStore.formatContext(results, this.config.knowledgeMinScore ?? 0.3);
252
+ if (knowledgeContext) {
253
+ this.eventBus.emit("knowledge_retrieved", { knowledgeContext });
254
+ }
255
+ }
256
+ catch {
257
+ // graceful degradation
258
+ }
259
+ }
260
+ // === Skill Execution ===
261
+ let skillResults = [];
262
+ if (this.skillRegistry && !skip?.skills) {
263
+ try {
264
+ const skillContext = {
265
+ message,
266
+ userId,
267
+ sessionId,
268
+ memoryContext,
269
+ knowledgeStore: this.config.knowledgeStore,
270
+ };
271
+ skillResults = await this.skillRegistry.runSkills(message, skillContext);
272
+ }
273
+ catch {
274
+ // graceful degradation
275
+ }
276
+ }
277
+ // === Tool Calling Classification ===
278
+ let toolCallingContext = "";
279
+ if (this.config.toolCallingClassifier &&
280
+ !skip?.toolCalling &&
281
+ this.getClassifierTools().length > 0) {
282
+ try {
283
+ const tcResult = await this.config.toolCallingClassifier.classify(message, this.getClassifierTools());
284
+ if (tcResult.toolResults.length > 0) {
285
+ toolCallingContext = tcResult.toolResults
286
+ .map((r) => `[${r.tool}]: ${r.result}`)
287
+ .join("\n");
288
+ this.eventBus.emit("tool_calling_classified", tcResult);
289
+ }
290
+ }
291
+ catch {
292
+ // graceful degradation
293
+ }
294
+ }
295
+ // === Build Dynamic Instructions ===
296
+ const dynamicInstructions = [...this.instructions];
297
+ if (summaryData?.summaryText) {
298
+ dynamicInstructions.push(`【Conversation History Summary】\n${summaryData.summaryText}`);
299
+ }
300
+ if (knowledgeContext) {
301
+ dynamicInstructions.push(`【Reference Knowledge】\n${knowledgeContext}`);
302
+ }
303
+ if (memoryContext) {
304
+ dynamicInstructions.push(memoryContext);
305
+ }
306
+ for (const key of prefetchKeys) {
307
+ if (key.startsWith("hook_") && prefetchData[key]) {
308
+ dynamicInstructions.push(prefetchData[key]);
309
+ }
310
+ }
311
+ let overridePrompt = null;
312
+ for (const sr of skillResults) {
313
+ if (sr.overrideSystemPrompt) {
314
+ overridePrompt = sr.overrideSystemPrompt;
315
+ }
316
+ if (sr.promptInjection) {
317
+ dynamicInstructions.push(sr.promptInjection);
318
+ }
319
+ }
320
+ if (toolCallingContext) {
321
+ dynamicInstructions.push(`【Tool Results】\n${toolCallingContext}`);
322
+ }
323
+ // === Prepare Tools ===
324
+ const activeTools = [];
325
+ if (intent?.needsTools ?? true) {
326
+ if (this.config.tools)
327
+ activeTools.push(...this.config.tools);
328
+ }
329
+ if (this.config.alwaysTools)
330
+ activeTools.push(...this.config.alwaysTools);
331
+ for (const sr of skillResults) {
332
+ activeTools.push(...sr.extraTools);
333
+ }
334
+ const systemPrompt = overridePrompt ?? dynamicInstructions.join("\n\n");
335
+ return {
336
+ memoryContext,
337
+ intent,
338
+ summaryData,
339
+ knowledgeContext,
340
+ skillResults,
341
+ toolCallingContext,
342
+ dynamicInstructions,
343
+ activeTools,
344
+ systemPrompt,
345
+ };
346
+ }
347
+ /**
348
+ * Streaming chat — returns an async iterable of text stream parts.
349
+ */
350
+ async *chatStream(message, options) {
351
+ const sessionId = options?.sessionId ?? (0, uuid_1.v4)();
352
+ const userId = options?.userId;
353
+ // === Input Guardrails ===
354
+ if (this.config.guardrails) {
355
+ for (const guardrail of this.config.guardrails) {
356
+ const result = await guardrail.checkInput(message);
357
+ if (!result.passed) {
358
+ yield { type: "text-delta", text: result.reason ?? "Blocked." };
359
+ return;
360
+ }
361
+ }
362
+ }
363
+ // === Prefetch ===
364
+ const prefetched = await this.prefetch(message, sessionId, options);
365
+ // Load history
366
+ let history = [];
367
+ if (this.config.sessionStore && sessionId) {
368
+ const stored = await this.config.sessionStore.get(sessionId);
369
+ if (stored) {
370
+ const limit = this.config.numHistoryMessages;
371
+ history = limit ? stored.slice(-limit) : stored;
372
+ }
373
+ }
374
+ const vercelTools = prefetched.activeTools.length > 0
375
+ ? (0, vercel_adapter_js_1.toVercelTools)(prefetched.activeTools)
376
+ : undefined;
377
+ const toolChoice = prefetched.activeTools.length > 0
378
+ ? this.resolveToolChoice({
379
+ message,
380
+ userId,
381
+ sessionId,
382
+ intent: prefetched.intent,
383
+ activeTools: prefetched.activeTools,
384
+ })
385
+ : undefined;
386
+ const result = (0, ai_1.streamText)({
387
+ model: this.model,
388
+ system: prefetched.systemPrompt,
389
+ messages: [
390
+ ...history,
391
+ { role: "user", content: message },
392
+ ],
393
+ tools: vercelTools,
394
+ toolChoice,
395
+ stopWhen: (0, ai_1.stepCountIs)(this.config.toolCallLimit ?? 10),
396
+ temperature: this.config.temperature ?? 0.7,
397
+ providerOptions: this.config.promptCaching ? {
398
+ anthropic: { cacheControl: true },
399
+ } : undefined,
400
+ });
401
+ let fullContent = "";
402
+ for await (const part of result.fullStream) {
403
+ if (part.type === "text-delta") {
404
+ fullContent += part.text;
405
+ }
406
+ yield part;
407
+ }
408
+ // === Background: save session, memory, compression ===
409
+ const updatedHistory = [
410
+ ...history,
411
+ { role: "user", content: message },
412
+ { role: "assistant", content: fullContent },
413
+ ];
414
+ if (this.config.sessionStore && sessionId) {
415
+ this.config.sessionStore.save(sessionId, updatedHistory).catch(() => { });
416
+ }
417
+ if (this.config.memoryManager && userId) {
418
+ this.config.memoryManager
419
+ .add(userId, [
420
+ { role: "user", content: message },
421
+ { role: "assistant", content: fullContent },
422
+ ])
423
+ .catch(() => { });
424
+ }
425
+ if (this.contextCompressor && sessionId) {
426
+ this.contextCompressor
427
+ .maybeCompress(sessionId, updatedHistory)
428
+ .catch(() => { });
429
+ }
430
+ }
431
+ /**
432
+ * Structured decision mode — uses full prefetch context (memory, summary,
433
+ * knowledge, hooks) but returns a typed JSON decision instead of generating
434
+ * a natural-language response or executing tools.
435
+ */
436
+ async decide(message, classifier, options) {
437
+ const startTime = Date.now();
438
+ const userId = options?.userId;
439
+ const sessionId = options?.sessionId ?? (0, uuid_1.v4)();
440
+ const timings = {};
441
+ // Start trace
442
+ const traceId = this.traceCollector?.startTrace(message, userId, sessionId) ?? null;
443
+ // === 1. Input Guardrails ===
444
+ if (this.config.guardrails) {
445
+ for (const guardrail of this.config.guardrails) {
446
+ const result = await guardrail.checkInput(message);
447
+ if (!result.passed) {
448
+ throw new types_js_2.DecisionError(result.reason ?? "Blocked by input guardrail.");
449
+ }
450
+ }
451
+ }
452
+ // === 2. Prefetch (skip intent/skills/toolCalling — not needed for decide) ===
453
+ const prefetchStart = Date.now();
454
+ const prefetched = await this.prefetch(message, sessionId, { userId }, {
455
+ intent: true,
456
+ skills: true,
457
+ toolCalling: true,
458
+ });
459
+ timings.prefetch = Date.now() - prefetchStart;
460
+ // === 3. Assemble StructuredClassifierInput ===
461
+ // Only pass hook-injected instructions as extraContext to avoid
462
+ // double-injecting summary/memory/knowledge (already in dedicated fields).
463
+ const hookInstructions = this.instructions.length > 0
464
+ ? [...this.instructions]
465
+ : [];
466
+ for (const di of prefetched.dynamicInstructions) {
467
+ // Skip items already covered by dedicated fields
468
+ if (di === prefetched.memoryContext)
469
+ continue;
470
+ if (prefetched.summaryData?.summaryText && di.includes(prefetched.summaryData.summaryText))
471
+ continue;
472
+ if (prefetched.knowledgeContext && di.includes(prefetched.knowledgeContext))
473
+ continue;
474
+ if (!hookInstructions.includes(di))
475
+ hookInstructions.push(di);
476
+ }
477
+ const classifierInput = {
478
+ message,
479
+ sessionId,
480
+ userId,
481
+ summary: prefetched.summaryData?.summaryText ?? null,
482
+ memoryContext: prefetched.memoryContext || undefined,
483
+ knowledgeContext: prefetched.knowledgeContext || undefined,
484
+ extraContext: hookInstructions.length > 0 ? hookInstructions : undefined,
485
+ };
486
+ // === 4. Classify + Optional Tool Planning (parallel) ===
487
+ const classifyStart = Date.now();
488
+ const classifyPromise = classifier.classify(classifierInput);
489
+ const toolPlanPromise = options?.includeToolPlan
490
+ ? (async () => {
491
+ const planStart = Date.now();
492
+ const allTools = this.getClassifierTools();
493
+ if (allTools.length === 0)
494
+ return undefined;
495
+ const planner = new tool_planner_js_1.ToolPlanner({ model: this.model });
496
+ const plan = await planner.plan(message, allTools);
497
+ timings.toolPlan = Date.now() - planStart;
498
+ return plan;
499
+ })().catch(() => undefined)
500
+ : Promise.resolve(undefined);
501
+ let classifierOutput;
502
+ let toolPlan;
503
+ try {
504
+ [classifierOutput, toolPlan] = await Promise.all([classifyPromise, toolPlanPromise]);
505
+ }
506
+ catch (err) {
507
+ throw new types_js_2.DecisionError("Classifier failed", err, err instanceof Error ? err.message : String(err));
508
+ }
509
+ timings.classify = Date.now() - classifyStart;
510
+ // === 5. Build Result ===
511
+ timings.total = Date.now() - startTime;
512
+ const decisionResult = {
513
+ decision: classifierOutput.result,
514
+ rawText: classifierOutput.rawText ?? JSON.stringify(classifierOutput.result),
515
+ usage: {
516
+ promptTokens: classifierOutput.usage?.promptTokens ?? 0,
517
+ completionTokens: classifierOutput.usage?.completionTokens ?? 0,
518
+ totalTokens: classifierOutput.usage?.totalTokens ?? 0,
519
+ },
520
+ timings,
521
+ sessionId,
522
+ traceId,
523
+ trace: {
524
+ classifier: classifier.name ?? "unknown",
525
+ usedSummary: !!prefetched.summaryData?.summaryText,
526
+ usedMemory: !!prefetched.memoryContext,
527
+ usedKnowledge: !!prefetched.knowledgeContext,
528
+ toolPlan,
529
+ classifierTrace: classifierOutput.trace,
530
+ },
531
+ };
532
+ // === 7. Background: memory (user message only, no assistant response) ===
533
+ if (this.config.memoryManager && userId) {
534
+ this.config.memoryManager
535
+ .add(userId, [{ role: "user", content: message }])
536
+ .catch(() => { });
537
+ }
538
+ return decisionResult;
539
+ }
540
+ getEventBus() {
541
+ return this.eventBus;
542
+ }
543
+ makeResult(partial) {
544
+ return {
545
+ blocked: false,
546
+ usage: {
547
+ promptTokens: 0,
548
+ completionTokens: 0,
549
+ totalTokens: 0,
550
+ },
551
+ intent: null,
552
+ toolCalls: [],
553
+ timings: {},
554
+ sessionId: null,
555
+ traceId: null,
556
+ trace: null,
557
+ ...partial,
558
+ };
559
+ }
560
+ resolveToolChoice(context) {
561
+ return this.config.toolChoiceResolver?.(context) ?? this.config.toolChoice;
562
+ }
563
+ getClassifierTools() {
564
+ const tools = [];
565
+ if (this.config.tools)
566
+ tools.push(...this.config.tools);
567
+ if (this.config.alwaysTools)
568
+ tools.push(...this.config.alwaysTools);
569
+ return dedupeToolsByName(tools);
570
+ }
571
+ }
572
+ exports.NaruAgent = NaruAgent;
573
+ function dedupeToolsByName(tools) {
574
+ const seen = new Set();
575
+ return tools.filter((tool) => {
576
+ if (seen.has(tool.name))
577
+ return false;
578
+ seen.add(tool.name);
579
+ return true;
580
+ });
581
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContextCompressor = void 0;
4
+ const ai_1 = require("ai");
5
+ class ContextCompressor {
6
+ summaryStore;
7
+ summaryModel;
8
+ keepLastRounds;
9
+ thresholdRounds;
10
+ constructor(config) {
11
+ this.summaryStore = config.summaryStore;
12
+ this.summaryModel = config.summaryModel;
13
+ this.keepLastRounds = config.keepLastRounds ?? 5;
14
+ this.thresholdRounds = config.thresholdRounds ?? 5;
15
+ }
16
+ async getSummary(sessionId) {
17
+ return this.summaryStore.get(sessionId);
18
+ }
19
+ /**
20
+ * Compress conversation history if it exceeds threshold.
21
+ */
22
+ async maybeCompress(sessionId, messages) {
23
+ // Count user-assistant rounds
24
+ const rounds = messages.filter((m) => m.role === "user").length;
25
+ if (rounds <= this.thresholdRounds)
26
+ return;
27
+ const existing = await this.summaryStore.get(sessionId);
28
+ const startFrom = existing?.compressedThroughRound ?? 0;
29
+ // Messages to compress (exclude last N rounds)
30
+ const keepCount = this.keepLastRounds * 2; // user + assistant pairs
31
+ const toCompress = messages.slice(0, Math.max(0, messages.length - keepCount));
32
+ if (toCompress.length === 0)
33
+ return;
34
+ const conversationText = toCompress
35
+ .map((m) => `${m.role}: ${typeof m.content === "string" ? m.content : JSON.stringify(m.content)}`)
36
+ .join("\n");
37
+ const previousSummary = existing?.summaryText ?? "";
38
+ const prompt = previousSummary
39
+ ? `Previous summary:\n${previousSummary}\n\nNew conversation to incorporate:\n${conversationText}\n\nCreate an updated comprehensive summary (max 500 words).`
40
+ : `Summarize this conversation concisely (max 500 words), preserving key facts, decisions, and context:\n\n${conversationText}`;
41
+ const result = await (0, ai_1.generateText)({
42
+ model: this.summaryModel,
43
+ prompt,
44
+ maxOutputTokens: 1000,
45
+ });
46
+ const summary = {
47
+ summaryText: result.text,
48
+ compressedThroughRound: startFrom + toCompress.filter((m) => m.role === "user").length,
49
+ createdAt: Date.now(),
50
+ modelUsed: "summary-model",
51
+ };
52
+ await this.summaryStore.save(sessionId, summary);
53
+ }
54
+ }
55
+ exports.ContextCompressor = ContextCompressor;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.InMemorySummaryStore = void 0;
4
+ class InMemorySummaryStore {
5
+ store = new Map();
6
+ async get(sessionId) {
7
+ return this.store.get(sessionId) ?? null;
8
+ }
9
+ async save(sessionId, summary) {
10
+ this.store.set(sessionId, summary);
11
+ }
12
+ async delete(sessionId) {
13
+ this.store.delete(sessionId);
14
+ }
15
+ }
16
+ exports.InMemorySummaryStore = InMemorySummaryStore;
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ToolPlanner = exports.LLMStructuredClassifier = exports.DecisionError = void 0;
4
+ var types_js_1 = require("./types.js");
5
+ Object.defineProperty(exports, "DecisionError", { enumerable: true, get: function () { return types_js_1.DecisionError; } });
6
+ var llm_structured_classifier_js_1 = require("./llm-structured-classifier.js");
7
+ Object.defineProperty(exports, "LLMStructuredClassifier", { enumerable: true, get: function () { return llm_structured_classifier_js_1.LLMStructuredClassifier; } });
8
+ var tool_planner_js_1 = require("./tool-planner.js");
9
+ Object.defineProperty(exports, "ToolPlanner", { enumerable: true, get: function () { return tool_planner_js_1.ToolPlanner; } });