gauss-ai 4.0.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 (232) hide show
  1. package/CHANGELOG.md +489 -0
  2. package/LICENSE +21 -0
  3. package/README.md +269 -0
  4. package/dist/a2a/index.cjs +7 -0
  5. package/dist/a2a/index.cjs.map +1 -0
  6. package/dist/a2a/index.d.cts +30 -0
  7. package/dist/a2a/index.d.ts +30 -0
  8. package/dist/a2a/index.js +7 -0
  9. package/dist/a2a/index.js.map +1 -0
  10. package/dist/agent-UIQDSYCE.js +16 -0
  11. package/dist/agent-UIQDSYCE.js.map +1 -0
  12. package/dist/agent-builder-8W3mBR-N.d.ts +1075 -0
  13. package/dist/agent-builder-GEMYdb1p.d.cts +1075 -0
  14. package/dist/agent-graph-AMQYAWNF.js +1422 -0
  15. package/dist/agent-graph-AMQYAWNF.js.map +1 -0
  16. package/dist/ai-sdk-mcp.adapter-SEN6KHNU.js +124 -0
  17. package/dist/ai-sdk-mcp.adapter-SEN6KHNU.js.map +1 -0
  18. package/dist/browser/index.js +10 -0
  19. package/dist/browser/index.js.map +1 -0
  20. package/dist/bun-runtime.adapter-MQDAJLQM.js +8 -0
  21. package/dist/bun-runtime.adapter-MQDAJLQM.js.map +1 -0
  22. package/dist/bun-runtime.adapter-XKOUXVAK.cjs +8 -0
  23. package/dist/bun-runtime.adapter-XKOUXVAK.cjs.map +1 -0
  24. package/dist/chat-A3XMRPJL.js +129 -0
  25. package/dist/chat-A3XMRPJL.js.map +1 -0
  26. package/dist/chunk-2ZRU47NC.js +91 -0
  27. package/dist/chunk-2ZRU47NC.js.map +1 -0
  28. package/dist/chunk-3LD3JTH4.cjs +18 -0
  29. package/dist/chunk-3LD3JTH4.cjs.map +1 -0
  30. package/dist/chunk-5FE5TG2W.cjs +16 -0
  31. package/dist/chunk-5FE5TG2W.cjs.map +1 -0
  32. package/dist/chunk-6XF673YC.cjs +436 -0
  33. package/dist/chunk-6XF673YC.cjs.map +1 -0
  34. package/dist/chunk-7CKWZJNS.js +230 -0
  35. package/dist/chunk-7CKWZJNS.js.map +1 -0
  36. package/dist/chunk-BI2G665F.js +4588 -0
  37. package/dist/chunk-BI2G665F.js.map +1 -0
  38. package/dist/chunk-C5NLWJS2.js +139 -0
  39. package/dist/chunk-C5NLWJS2.js.map +1 -0
  40. package/dist/chunk-CJZ66SU3.cjs +4321 -0
  41. package/dist/chunk-CJZ66SU3.cjs.map +1 -0
  42. package/dist/chunk-DAMT2CXW.cjs +91 -0
  43. package/dist/chunk-DAMT2CXW.cjs.map +1 -0
  44. package/dist/chunk-E7WG3MO5.js +18 -0
  45. package/dist/chunk-E7WG3MO5.js.map +1 -0
  46. package/dist/chunk-EFDM6R4J.js +99 -0
  47. package/dist/chunk-EFDM6R4J.js.map +1 -0
  48. package/dist/chunk-F7WIPPEO.js +256 -0
  49. package/dist/chunk-F7WIPPEO.js.map +1 -0
  50. package/dist/chunk-FAYDE67N.js +6927 -0
  51. package/dist/chunk-FAYDE67N.js.map +1 -0
  52. package/dist/chunk-GAE2KKCM.js +21 -0
  53. package/dist/chunk-GAE2KKCM.js.map +1 -0
  54. package/dist/chunk-INLNGRXM.cjs +130 -0
  55. package/dist/chunk-INLNGRXM.cjs.map +1 -0
  56. package/dist/chunk-JKXKXB5O.js +130 -0
  57. package/dist/chunk-JKXKXB5O.js.map +1 -0
  58. package/dist/chunk-K6SAETGP.js +375 -0
  59. package/dist/chunk-K6SAETGP.js.map +1 -0
  60. package/dist/chunk-KEASLAYR.js +157 -0
  61. package/dist/chunk-KEASLAYR.js.map +1 -0
  62. package/dist/chunk-KKJVNM6O.js +436 -0
  63. package/dist/chunk-KKJVNM6O.js.map +1 -0
  64. package/dist/chunk-KYIMVRIM.js +16 -0
  65. package/dist/chunk-KYIMVRIM.js.map +1 -0
  66. package/dist/chunk-MB7NXIZD.js +4321 -0
  67. package/dist/chunk-MB7NXIZD.js.map +1 -0
  68. package/dist/chunk-MHHDXPGE.js +209 -0
  69. package/dist/chunk-MHHDXPGE.js.map +1 -0
  70. package/dist/chunk-NE6JJA5W.js +401 -0
  71. package/dist/chunk-NE6JJA5W.js.map +1 -0
  72. package/dist/chunk-PF46XZBF.cjs +6927 -0
  73. package/dist/chunk-PF46XZBF.cjs.map +1 -0
  74. package/dist/chunk-PSJIAGDE.cjs +375 -0
  75. package/dist/chunk-PSJIAGDE.cjs.map +1 -0
  76. package/dist/chunk-PWOQDXNQ.js +16 -0
  77. package/dist/chunk-PWOQDXNQ.js.map +1 -0
  78. package/dist/chunk-QYOMQBBZ.cjs +230 -0
  79. package/dist/chunk-QYOMQBBZ.cjs.map +1 -0
  80. package/dist/chunk-UDFXLC4J.cjs +16 -0
  81. package/dist/chunk-UDFXLC4J.cjs.map +1 -0
  82. package/dist/chunk-UO4NGXRT.cjs +259 -0
  83. package/dist/chunk-UO4NGXRT.cjs.map +1 -0
  84. package/dist/chunk-UPFDFLEW.js +40 -0
  85. package/dist/chunk-UPFDFLEW.js.map +1 -0
  86. package/dist/chunk-V55JSQS7.cjs +16 -0
  87. package/dist/chunk-V55JSQS7.cjs.map +1 -0
  88. package/dist/chunk-VJADHXZL.cjs +16 -0
  89. package/dist/chunk-VJADHXZL.cjs.map +1 -0
  90. package/dist/chunk-VRWD7LCI.js +59 -0
  91. package/dist/chunk-VRWD7LCI.js.map +1 -0
  92. package/dist/chunk-WKKQ443C.js +487 -0
  93. package/dist/chunk-WKKQ443C.js.map +1 -0
  94. package/dist/chunk-X2GHUHAF.js +436 -0
  95. package/dist/chunk-X2GHUHAF.js.map +1 -0
  96. package/dist/chunk-XLGW3XNI.cjs +256 -0
  97. package/dist/chunk-XLGW3XNI.cjs.map +1 -0
  98. package/dist/chunk-ZFJKX4DP.js +16 -0
  99. package/dist/chunk-ZFJKX4DP.js.map +1 -0
  100. package/dist/chunk-ZM2OEWM2.js +259 -0
  101. package/dist/chunk-ZM2OEWM2.js.map +1 -0
  102. package/dist/chunk-ZNAIP2XV.js +16 -0
  103. package/dist/chunk-ZNAIP2XV.js.map +1 -0
  104. package/dist/chunk-ZYFAZYSL.js +42 -0
  105. package/dist/chunk-ZYFAZYSL.js.map +1 -0
  106. package/dist/cli/index.js +421 -0
  107. package/dist/cli/index.js.map +1 -0
  108. package/dist/config-4MHT6TQW.js +153 -0
  109. package/dist/config-4MHT6TQW.js.map +1 -0
  110. package/dist/config-REERQFK4.cjs +153 -0
  111. package/dist/config-REERQFK4.cjs.map +1 -0
  112. package/dist/cost-tracker-JLOU7IZJ.js +7 -0
  113. package/dist/cost-tracker-JLOU7IZJ.js.map +1 -0
  114. package/dist/demo-C52GMSYH.js +188 -0
  115. package/dist/demo-C52GMSYH.js.map +1 -0
  116. package/dist/deno/index.js +306 -0
  117. package/dist/deno/index.js.map +1 -0
  118. package/dist/deno-runtime.adapter-F744HY7K.js +8 -0
  119. package/dist/deno-runtime.adapter-F744HY7K.js.map +1 -0
  120. package/dist/deno-runtime.adapter-RFEVNSCV.cjs +8 -0
  121. package/dist/deno-runtime.adapter-RFEVNSCV.cjs.map +1 -0
  122. package/dist/dev-D7DDVDA4.js +218 -0
  123. package/dist/dev-D7DDVDA4.js.map +1 -0
  124. package/dist/edge/index.js +10 -0
  125. package/dist/edge/index.js.map +1 -0
  126. package/dist/edge-runtime.adapter-UQCW2F7X.js +8 -0
  127. package/dist/edge-runtime.adapter-UQCW2F7X.js.map +1 -0
  128. package/dist/edge-runtime.adapter-YED6F3AY.cjs +8 -0
  129. package/dist/edge-runtime.adapter-YED6F3AY.cjs.map +1 -0
  130. package/dist/graph-MGFAQZ5W.js +50 -0
  131. package/dist/graph-MGFAQZ5W.js.map +1 -0
  132. package/dist/graph-visualization-HBSVQXJK.js +9 -0
  133. package/dist/graph-visualization-HBSVQXJK.js.map +1 -0
  134. package/dist/index-BRgqNnh3.d.cts +982 -0
  135. package/dist/index-CZxpYUxZ.d.ts +982 -0
  136. package/dist/index.cjs +14789 -0
  137. package/dist/index.cjs.map +1 -0
  138. package/dist/index.d.cts +10275 -0
  139. package/dist/index.d.ts +10275 -0
  140. package/dist/index.js +14789 -0
  141. package/dist/index.js.map +1 -0
  142. package/dist/init-CFWXTQ35.js +133 -0
  143. package/dist/init-CFWXTQ35.js.map +1 -0
  144. package/dist/llm-VWO4MC7J.cjs +17 -0
  145. package/dist/llm-VWO4MC7J.cjs.map +1 -0
  146. package/dist/llm-XLXVSPBI.js +17 -0
  147. package/dist/llm-XLXVSPBI.js.map +1 -0
  148. package/dist/logging-WRAK5ZXT.js +33 -0
  149. package/dist/logging-WRAK5ZXT.js.map +1 -0
  150. package/dist/metrics-FAHZVVD4.js +47 -0
  151. package/dist/metrics-FAHZVVD4.js.map +1 -0
  152. package/dist/node/index.cjs +280 -0
  153. package/dist/node/index.cjs.map +1 -0
  154. package/dist/node/index.d.cts +51 -0
  155. package/dist/node/index.d.ts +51 -0
  156. package/dist/node/index.js +280 -0
  157. package/dist/node/index.js.map +1 -0
  158. package/dist/node-runtime.adapter-5L7PJ6W2.js +8 -0
  159. package/dist/node-runtime.adapter-5L7PJ6W2.js.map +1 -0
  160. package/dist/node-runtime.adapter-CCRZVGHB.cjs +8 -0
  161. package/dist/node-runtime.adapter-CCRZVGHB.cjs.map +1 -0
  162. package/dist/persist-usage-WTBTCWEF.js +7 -0
  163. package/dist/persist-usage-WTBTCWEF.js.map +1 -0
  164. package/dist/plugin-RCPBWUUA.js +207 -0
  165. package/dist/plugin-RCPBWUUA.js.map +1 -0
  166. package/dist/plugins/index.cjs +75 -0
  167. package/dist/plugins/index.cjs.map +1 -0
  168. package/dist/plugins/index.d.cts +8 -0
  169. package/dist/plugins/index.d.ts +8 -0
  170. package/dist/plugins/index.js +75 -0
  171. package/dist/plugins/index.js.map +1 -0
  172. package/dist/plugins-L4ING3CX.js +4625 -0
  173. package/dist/plugins-L4ING3CX.js.map +1 -0
  174. package/dist/providers/index.cjs +189 -0
  175. package/dist/providers/index.cjs.map +1 -0
  176. package/dist/providers/index.d.cts +168 -0
  177. package/dist/providers/index.d.ts +168 -0
  178. package/dist/providers/index.js +189 -0
  179. package/dist/providers/index.js.map +1 -0
  180. package/dist/providers-3RNQ5CKZ.js +59 -0
  181. package/dist/providers-3RNQ5CKZ.js.map +1 -0
  182. package/dist/providers-66GPXUGQ.cjs +59 -0
  183. package/dist/providers-66GPXUGQ.cjs.map +1 -0
  184. package/dist/repl-K6QN4I2S.js +678 -0
  185. package/dist/repl-K6QN4I2S.js.map +1 -0
  186. package/dist/rest/index.cjs +17 -0
  187. package/dist/rest/index.cjs.map +1 -0
  188. package/dist/rest/index.d.cts +102 -0
  189. package/dist/rest/index.d.ts +102 -0
  190. package/dist/rest/index.js +17 -0
  191. package/dist/rest/index.js.map +1 -0
  192. package/dist/runtime-deno.js +15 -0
  193. package/dist/runtime-deno.js.map +1 -0
  194. package/dist/runtime-edge.js +15 -0
  195. package/dist/runtime-edge.js.map +1 -0
  196. package/dist/runtime-node.js +15 -0
  197. package/dist/runtime-node.js.map +1 -0
  198. package/dist/scraping/index.cjs +11 -0
  199. package/dist/scraping/index.cjs.map +1 -0
  200. package/dist/scraping/index.d.cts +17 -0
  201. package/dist/scraping/index.d.ts +17 -0
  202. package/dist/scraping/index.js +11 -0
  203. package/dist/scraping/index.js.map +1 -0
  204. package/dist/semantic-scraping.port-CZWUea88.d.cts +54 -0
  205. package/dist/semantic-scraping.port-CZWUea88.d.ts +54 -0
  206. package/dist/server/index.js +166 -0
  207. package/dist/server/index.js.map +1 -0
  208. package/dist/testing/index.cjs +25 -0
  209. package/dist/testing/index.cjs.map +1 -0
  210. package/dist/testing/index.d.cts +63 -0
  211. package/dist/testing/index.d.ts +63 -0
  212. package/dist/testing/index.js +25 -0
  213. package/dist/testing/index.js.map +1 -0
  214. package/dist/token-counter.port-CRgxZZGe.d.ts +334 -0
  215. package/dist/token-counter.port-D7BHMCRR.d.cts +334 -0
  216. package/dist/tools-BZM33OBZ.js +10 -0
  217. package/dist/tools-BZM33OBZ.js.map +1 -0
  218. package/dist/tracing-XA3TEWP4.js +48 -0
  219. package/dist/tracing-XA3TEWP4.js.map +1 -0
  220. package/dist/types-CVsP7gFI.d.cts +235 -0
  221. package/dist/types-CVsP7gFI.d.ts +235 -0
  222. package/dist/virtual-fs.adapter-BBLS-3AY.d.ts +26 -0
  223. package/dist/virtual-fs.adapter-nb0CTYOj.d.cts +26 -0
  224. package/dist/workflow/index.cjs +9 -0
  225. package/dist/workflow/index.cjs.map +1 -0
  226. package/dist/workflow/index.d.cts +62 -0
  227. package/dist/workflow/index.d.ts +62 -0
  228. package/dist/workflow/index.js +9 -0
  229. package/dist/workflow/index.js.map +1 -0
  230. package/dist/workflow.port-BaCttxrw.d.cts +153 -0
  231. package/dist/workflow.port-BaCttxrw.d.ts +153 -0
  232. package/package.json +230 -0
@@ -0,0 +1,4588 @@
1
+ import {
2
+ CircuitBreaker,
3
+ DEFAULT_CIRCUIT_BREAKER_CONFIG,
4
+ DEFAULT_TOOL_CACHE_CONFIG,
5
+ PluginManager,
6
+ ToolCache
7
+ } from "./chunk-X2GHUHAF.js";
8
+ import {
9
+ generateText,
10
+ stepCountIs,
11
+ streamText,
12
+ tool
13
+ } from "./chunk-K6SAETGP.js";
14
+
15
+ // src/adapters/runtime/detect-runtime.ts
16
+ function detectRuntimeName() {
17
+ if (typeof globalThis !== "undefined") {
18
+ if ("Deno" in globalThis) return "deno";
19
+ if ("Bun" in globalThis) return "bun";
20
+ if (typeof globalThis.process?.versions?.node === "string") return "node";
21
+ if (typeof globalThis.fetch === "function" && !("process" in globalThis)) return "edge";
22
+ }
23
+ return "unknown";
24
+ }
25
+ async function createRuntimeAdapterAsync(name) {
26
+ const resolved = name ?? detectRuntimeName();
27
+ switch (resolved) {
28
+ case "deno": {
29
+ const { DenoRuntimeAdapter: D } = await import("./deno-runtime.adapter-F744HY7K.js");
30
+ return new D();
31
+ }
32
+ case "bun": {
33
+ const { BunRuntimeAdapter: B } = await import("./bun-runtime.adapter-MQDAJLQM.js");
34
+ return new B();
35
+ }
36
+ case "edge": {
37
+ const { EdgeRuntimeAdapter: E } = await import("./edge-runtime.adapter-UQCW2F7X.js");
38
+ return new E();
39
+ }
40
+ case "node":
41
+ case "unknown":
42
+ default: {
43
+ const { NodeRuntimeAdapter: N } = await import("./node-runtime.adapter-5L7PJ6W2.js");
44
+ return new N();
45
+ }
46
+ }
47
+ }
48
+
49
+ // src/agent/event-bus.ts
50
+ var EventBus = class _EventBus {
51
+ sessionId;
52
+ listeners = /* @__PURE__ */ new Map();
53
+ maxListenersPerEvent;
54
+ maxBubblesPerSecond;
55
+ // Hierarchical relationships
56
+ parent = null;
57
+ children = /* @__PURE__ */ new Map();
58
+ namespace = null;
59
+ // Namespaced listeners: namespace -> Set<handler>
60
+ namespacedListeners = /* @__PURE__ */ new Map();
61
+ // Anti-storm: track bubble counts per child per second window
62
+ bubbleCounts = /* @__PURE__ */ new Map();
63
+ constructor(sessionId, options) {
64
+ this.sessionId = sessionId;
65
+ this.maxListenersPerEvent = options?.maxListenersPerEvent ?? 100;
66
+ this.maxBubblesPerSecond = options?.maxBubblesPerSecond ?? 100;
67
+ }
68
+ /** Subscribe to a specific event type or '*' for all events. Returns unsubscribe fn. */
69
+ on(eventType, handler) {
70
+ let set = this.listeners.get(eventType);
71
+ if (!set) {
72
+ set = /* @__PURE__ */ new Set();
73
+ this.listeners.set(eventType, set);
74
+ }
75
+ if (set.size >= this.maxListenersPerEvent) {
76
+ throw new Error(
77
+ `EventBus: max listeners (${this.maxListenersPerEvent}) reached for "${String(eventType)}"`
78
+ );
79
+ }
80
+ set.add(handler);
81
+ return () => this.off(eventType, handler);
82
+ }
83
+ /** Unsubscribe a handler from a specific event type. */
84
+ off(eventType, handler) {
85
+ const set = this.listeners.get(eventType);
86
+ if (!set) return;
87
+ set.delete(handler);
88
+ if (set.size === 0) this.listeners.delete(eventType);
89
+ }
90
+ /** Emit an event, auto-filling timestamp and sessionId. Also bubbles to parent. */
91
+ emit(type, data) {
92
+ const event = {
93
+ type,
94
+ timestamp: Date.now(),
95
+ sessionId: this.sessionId,
96
+ data
97
+ };
98
+ this.dispatchLocal(event);
99
+ this.bubbleToParent(event);
100
+ }
101
+ /** Remove all listeners for a given event type, or all listeners if no type specified. */
102
+ removeAllListeners(eventType) {
103
+ if (eventType) {
104
+ this.listeners.delete(eventType);
105
+ } else {
106
+ this.listeners.clear();
107
+ }
108
+ }
109
+ /** Return the number of listeners for a given event type. */
110
+ listenerCount(eventType) {
111
+ return this.listeners.get(eventType)?.size ?? 0;
112
+ }
113
+ // ===========================================================================
114
+ // Hierarchical API
115
+ // ===========================================================================
116
+ /** Create a child bus with a given namespace. */
117
+ createChild(childNamespace) {
118
+ if (this.children.has(childNamespace)) {
119
+ throw new Error(`EventBus: child namespace "${childNamespace}" already exists`);
120
+ }
121
+ const child = new _EventBus(this.sessionId, {
122
+ maxListenersPerEvent: this.maxListenersPerEvent,
123
+ maxBubblesPerSecond: this.maxBubblesPerSecond
124
+ });
125
+ child.parent = this;
126
+ child.namespace = childNamespace;
127
+ this.children.set(childNamespace, child);
128
+ return child;
129
+ }
130
+ /** Broadcast an event from this bus to ALL children recursively (capturing phase). */
131
+ broadcast(type, data) {
132
+ const event = {
133
+ type,
134
+ timestamp: Date.now(),
135
+ sessionId: this.sessionId,
136
+ data
137
+ };
138
+ for (const child of this.children.values()) {
139
+ child.dispatchLocal(event);
140
+ child.broadcast(type, data);
141
+ }
142
+ }
143
+ /** Subscribe to events bubbled from a specific child namespace. */
144
+ onNamespaced(childNamespace, handler) {
145
+ let set = this.namespacedListeners.get(childNamespace);
146
+ if (!set) {
147
+ set = /* @__PURE__ */ new Set();
148
+ this.namespacedListeners.set(childNamespace, set);
149
+ }
150
+ set.add(handler);
151
+ return () => {
152
+ set.delete(handler);
153
+ if (set.size === 0) this.namespacedListeners.delete(childNamespace);
154
+ };
155
+ }
156
+ /** Get all child buses. */
157
+ getChildren() {
158
+ return new Map(this.children);
159
+ }
160
+ /** Get the parent bus, or null for root. */
161
+ getParent() {
162
+ return this.parent;
163
+ }
164
+ // ===========================================================================
165
+ // Internal helpers
166
+ // ===========================================================================
167
+ /** Dispatch an event to local listeners (specific + wildcard). */
168
+ dispatchLocal(event) {
169
+ const specific = this.listeners.get(event.type);
170
+ if (specific) {
171
+ for (const handler of specific) handler(event);
172
+ }
173
+ const wildcard = this.listeners.get("*");
174
+ if (wildcard) {
175
+ for (const handler of wildcard) handler(event);
176
+ }
177
+ }
178
+ /** Bubble an event to the parent bus with _source and _bubbled metadata. */
179
+ bubbleToParent(event) {
180
+ if (!this.parent || !this.namespace) return;
181
+ if (!this.parent.checkBubbleRate(this.namespace)) return;
182
+ const bubbledData = typeof event.data === "object" && event.data !== null ? { ...event.data, _source: this.namespace, _bubbled: true } : { _value: event.data, _source: this.namespace, _bubbled: true };
183
+ const bubbledEvent = {
184
+ type: event.type,
185
+ timestamp: event.timestamp,
186
+ sessionId: event.sessionId,
187
+ data: bubbledData
188
+ };
189
+ this.parent.dispatchLocal(bubbledEvent);
190
+ const nsListeners = this.parent.namespacedListeners.get(this.namespace);
191
+ if (nsListeners) {
192
+ for (const handler of nsListeners) handler(bubbledEvent);
193
+ }
194
+ this.parent.bubbleToParent(bubbledEvent);
195
+ }
196
+ /** Check if a child is within the allowed bubble rate. Returns true if allowed. */
197
+ checkBubbleRate(childNamespace) {
198
+ const now = Date.now();
199
+ let tracker = this.bubbleCounts.get(childNamespace);
200
+ if (!tracker || now - tracker.windowStart >= 1e3) {
201
+ tracker = { count: 0, windowStart: now };
202
+ this.bubbleCounts.set(childNamespace, tracker);
203
+ }
204
+ tracker.count++;
205
+ if (tracker.count > this.maxBubblesPerSecond) {
206
+ if (tracker.count === this.maxBubblesPerSecond + 1) {
207
+ console.warn(
208
+ `EventBus: anti-storm \u2014 child "${childNamespace}" exceeded ${this.maxBubblesPerSecond} bubbles/sec, dropping events`
209
+ );
210
+ }
211
+ return false;
212
+ }
213
+ return true;
214
+ }
215
+ };
216
+
217
+ // src/context/token-tracker.ts
218
+ var TokenTracker = class {
219
+ constructor(counter, budget) {
220
+ this.counter = counter;
221
+ this.budget = budget;
222
+ }
223
+ inputTokens = 0;
224
+ outputTokens = 0;
225
+ addInput(tokens) {
226
+ this.inputTokens += tokens;
227
+ }
228
+ addOutput(tokens) {
229
+ this.outputTokens += tokens;
230
+ }
231
+ getUsage() {
232
+ const totalTokens = this.inputTokens + this.outputTokens;
233
+ return {
234
+ inputTokens: this.inputTokens,
235
+ outputTokens: this.outputTokens,
236
+ totalTokens,
237
+ estimatedCost: this.counter.estimateCost(
238
+ this.inputTokens,
239
+ this.outputTokens,
240
+ ""
241
+ ),
242
+ isOverBudget: this.isOverBudget()
243
+ };
244
+ }
245
+ isOverBudget() {
246
+ return this.inputTokens > this.budget.maxInputTokens || this.outputTokens > this.budget.maxOutputTokens || this.inputTokens + this.outputTokens > this.budget.maxTotalTokens;
247
+ }
248
+ isNearBudget(threshold) {
249
+ const t = threshold ?? this.budget.warningThreshold;
250
+ return this.inputTokens > this.budget.maxInputTokens * t || this.outputTokens > this.budget.maxOutputTokens * t || this.inputTokens + this.outputTokens > this.budget.maxTotalTokens * t;
251
+ }
252
+ getRemainingBudget() {
253
+ const total = this.inputTokens + this.outputTokens;
254
+ return {
255
+ inputTokens: Math.max(0, this.budget.maxInputTokens - this.inputTokens),
256
+ outputTokens: Math.max(0, this.budget.maxOutputTokens - this.outputTokens),
257
+ totalTokens: Math.max(0, this.budget.maxTotalTokens - total)
258
+ };
259
+ }
260
+ reset() {
261
+ this.inputTokens = 0;
262
+ this.outputTokens = 0;
263
+ }
264
+ };
265
+
266
+ // src/agent/tool-manager.ts
267
+ import { z as z18 } from "zod";
268
+
269
+ // src/agent/approval-manager.ts
270
+ var ApprovalManager = class {
271
+ config;
272
+ sessionId;
273
+ eventHandler;
274
+ constructor(config, sessionId, eventHandler) {
275
+ this.config = config;
276
+ this.sessionId = sessionId;
277
+ this.eventHandler = eventHandler;
278
+ }
279
+ /**
280
+ * Synchronous check whether a tool is auto-approved by configuration.
281
+ *
282
+ * - "approve-all": returns true unless tool is in `requireApproval`
283
+ * - "deny-all": returns true only if tool is in `autoApprove`
284
+ */
285
+ shouldApprove(toolName) {
286
+ if (this.config.defaultMode === "approve-all") {
287
+ return !this.config.requireApproval.includes(toolName);
288
+ }
289
+ return this.config.autoApprove.includes(toolName);
290
+ }
291
+ /**
292
+ * Requests approval via the configured callback, emitting lifecycle events.
293
+ */
294
+ async requestApproval(request) {
295
+ this.emit("tool:approval-required", request);
296
+ const approved = await this.config.onApprovalRequired(request);
297
+ if (approved) {
298
+ this.emit("tool:approved", { toolName: request.toolName, toolCallId: request.toolCallId });
299
+ } else {
300
+ this.emit("tool:denied", { toolName: request.toolName, toolCallId: request.toolCallId });
301
+ }
302
+ return {
303
+ approved,
304
+ reason: approved ? void 0 : "Approval denied by callback"
305
+ };
306
+ }
307
+ /**
308
+ * Combined check: auto-approves when policy allows, otherwise delegates
309
+ * to the async approval flow.
310
+ */
311
+ async checkAndApprove(toolName, toolCallId, args, stepIndex) {
312
+ if (this.shouldApprove(toolName)) {
313
+ return { approved: true };
314
+ }
315
+ return this.requestApproval({
316
+ toolName,
317
+ toolCallId,
318
+ args,
319
+ sessionId: this.sessionId,
320
+ stepIndex
321
+ });
322
+ }
323
+ // ---------------------------------------------------------------------------
324
+ // Helpers
325
+ // ---------------------------------------------------------------------------
326
+ emit(type, data) {
327
+ this.eventHandler?.({
328
+ type,
329
+ timestamp: Date.now(),
330
+ sessionId: this.sessionId,
331
+ data
332
+ });
333
+ }
334
+ };
335
+
336
+ // src/tools/filesystem/ls.tool.ts
337
+ import { z } from "zod";
338
+ var zoneSchema = z.enum(["transient", "persistent"]).default("transient").describe("Filesystem zone to operate in");
339
+ function createLsTool(fs) {
340
+ return tool({
341
+ description: "List files and directories at the given path. Returns names, sizes, and types. Use recursive to walk subdirectories.",
342
+ inputSchema: z.object({
343
+ path: z.string().describe("Directory path to list"),
344
+ recursive: z.boolean().optional().default(false).describe("Whether to list subdirectories recursively"),
345
+ zone: zoneSchema.optional()
346
+ }),
347
+ execute: async ({ path, recursive, zone }) => {
348
+ const entries = await fs.list(
349
+ path,
350
+ { recursive },
351
+ zone ?? "transient"
352
+ );
353
+ if (entries.length === 0) return "Directory is empty.";
354
+ const lines = entries.map((e) => {
355
+ const tag = e.isDirectory ? "[dir] " : " ";
356
+ return `${tag}${e.path} (${e.size} bytes)`;
357
+ });
358
+ return lines.join("\n");
359
+ }
360
+ });
361
+ }
362
+
363
+ // src/tools/filesystem/read-file.tool.ts
364
+ import { z as z2 } from "zod";
365
+ var MAX_CHARS = 5e4;
366
+ function createReadFileTool(fs) {
367
+ return tool({
368
+ description: "Read the contents of a file and return it as a string. Very large files are truncated to 50000 characters.",
369
+ inputSchema: z2.object({
370
+ path: z2.string().describe("File path to read"),
371
+ zone: z2.enum(["transient", "persistent"]).default("transient").optional().describe("Filesystem zone")
372
+ }),
373
+ execute: async ({ path, zone }) => {
374
+ const content = await fs.read(path, zone ?? "transient");
375
+ if (content.length <= MAX_CHARS) return content;
376
+ return content.slice(0, MAX_CHARS) + `
377
+
378
+ [Truncated: showing ${MAX_CHARS} of ${content.length} characters]`;
379
+ }
380
+ });
381
+ }
382
+
383
+ // src/tools/filesystem/write-file.tool.ts
384
+ import { z as z3 } from "zod";
385
+ function createWriteFileTool(fs) {
386
+ return tool({
387
+ description: "Create or overwrite a file with the given content. Parent directories are created automatically.",
388
+ inputSchema: z3.object({
389
+ path: z3.string().describe("File path to write"),
390
+ content: z3.string().describe("Content to write to the file"),
391
+ zone: z3.enum(["transient", "persistent"]).default("transient").optional().describe("Filesystem zone")
392
+ }),
393
+ execute: async ({ path, content, zone }) => {
394
+ await fs.write(path, content, zone ?? "transient");
395
+ return `File written: ${path} (${content.length} bytes)`;
396
+ }
397
+ });
398
+ }
399
+
400
+ // src/tools/filesystem/edit-file.tool.ts
401
+ import { z as z4 } from "zod";
402
+ function createEditFileTool(fs) {
403
+ return tool({
404
+ description: "Perform a surgical string replacement in a file. Replaces the first occurrence of oldStr with newStr. Fails if oldStr is not found.",
405
+ inputSchema: z4.object({
406
+ path: z4.string().describe("File path to edit"),
407
+ oldStr: z4.string().describe("Exact string to find and replace"),
408
+ newStr: z4.string().describe("Replacement string"),
409
+ zone: z4.enum(["transient", "persistent"]).default("transient").optional().describe("Filesystem zone")
410
+ }),
411
+ execute: async ({ path, oldStr, newStr, zone }) => {
412
+ const z22 = zone ?? "transient";
413
+ const content = await fs.read(path, z22);
414
+ if (!content.includes(oldStr)) {
415
+ throw new Error(`oldStr not found in ${path}`);
416
+ }
417
+ const updated = content.replace(oldStr, newStr);
418
+ await fs.write(path, updated, z22);
419
+ return `File edited: ${path}`;
420
+ }
421
+ });
422
+ }
423
+
424
+ // src/tools/filesystem/glob.tool.ts
425
+ import { z as z5 } from "zod";
426
+ function createGlobTool(fs) {
427
+ return tool({
428
+ description: "Find files matching a glob pattern (e.g. '**/*.ts'). Returns a list of matching file paths.",
429
+ inputSchema: z5.object({
430
+ pattern: z5.string().describe("Glob pattern to match files"),
431
+ zone: z5.enum(["transient", "persistent"]).default("transient").optional().describe("Filesystem zone")
432
+ }),
433
+ execute: async ({ pattern, zone }) => {
434
+ const matches = await fs.glob(pattern, zone ?? "transient");
435
+ if (matches.length === 0) return "No files matched the pattern.";
436
+ return matches.join("\n");
437
+ }
438
+ });
439
+ }
440
+
441
+ // src/tools/filesystem/grep.tool.ts
442
+ import { z as z6 } from "zod";
443
+ function createGrepTool(fs) {
444
+ return tool({
445
+ description: "Search file contents for a regex or string pattern. Returns matching lines with file paths and line numbers.",
446
+ inputSchema: z6.object({
447
+ pattern: z6.string().describe("Regex or string pattern to search for"),
448
+ path: z6.string().optional().describe("Directory or file to search in"),
449
+ caseSensitive: z6.boolean().optional().default(true),
450
+ maxResults: z6.number().int().optional().default(50).describe("Maximum number of results to return"),
451
+ zone: z6.enum(["transient", "persistent"]).default("transient").optional().describe("Filesystem zone")
452
+ }),
453
+ execute: async ({ pattern, path, caseSensitive, maxResults, zone }) => {
454
+ const results = await fs.search(
455
+ pattern,
456
+ { caseSensitive, maxResults, includeLineNumbers: true, filePattern: path },
457
+ zone ?? "transient"
458
+ );
459
+ if (results.length === 0) return "No matches found.";
460
+ const lines = results.map(
461
+ (r) => `${r.filePath}:${r.lineNumber}: ${r.lineContent}`
462
+ );
463
+ return lines.join("\n");
464
+ }
465
+ });
466
+ }
467
+
468
+ // src/tools/filesystem/index.ts
469
+ function createFilesystemTools(fs) {
470
+ return {
471
+ ls: createLsTool(fs),
472
+ read_file: createReadFileTool(fs),
473
+ write_file: createWriteFileTool(fs),
474
+ edit_file: createEditFileTool(fs),
475
+ glob: createGlobTool(fs),
476
+ grep: createGrepTool(fs)
477
+ };
478
+ }
479
+
480
+ // src/tools/planning/write-todos.tool.ts
481
+ import { z as z7 } from "zod";
482
+
483
+ // src/tools/planning/shared.ts
484
+ var TODOS_PATH = "todos.json";
485
+ async function loadTodos(fs) {
486
+ const exists = await fs.exists(TODOS_PATH, "persistent");
487
+ if (!exists) return [];
488
+ const raw = await fs.read(TODOS_PATH, "persistent");
489
+ return JSON.parse(raw);
490
+ }
491
+
492
+ // src/tools/planning/write-todos.tool.ts
493
+ var TodoInputSchema = z7.object({
494
+ id: z7.string().describe("Unique identifier (kebab-case)"),
495
+ title: z7.string().describe("Short title of the task"),
496
+ description: z7.string().optional(),
497
+ status: z7.enum(["pending", "in_progress", "done", "blocked"]).optional(),
498
+ dependencies: z7.array(z7.string()).optional(),
499
+ priority: z7.enum(["low", "medium", "high", "critical"]).optional()
500
+ });
501
+ function createWriteTodosTool(fs) {
502
+ return tool({
503
+ description: "Create or update a task plan. Each todo has an id, title, optional description, status, dependencies, and priority.",
504
+ inputSchema: z7.object({
505
+ todos: z7.array(TodoInputSchema).describe("Todos to create or update")
506
+ }),
507
+ execute: async ({ todos }) => {
508
+ const existing = await loadTodos(fs);
509
+ const byId = new Map(existing.map((t) => [t.id, t]));
510
+ let created = 0;
511
+ let updated = 0;
512
+ for (const input of todos) {
513
+ const now = Date.now();
514
+ const prev = byId.get(input.id);
515
+ if (prev) {
516
+ byId.set(input.id, { ...prev, ...input, updatedAt: now });
517
+ updated++;
518
+ } else {
519
+ byId.set(input.id, {
520
+ status: "pending",
521
+ dependencies: [],
522
+ priority: "medium",
523
+ createdAt: now,
524
+ updatedAt: now,
525
+ ...input
526
+ });
527
+ created++;
528
+ }
529
+ }
530
+ const all = Array.from(byId.values());
531
+ await fs.write(TODOS_PATH, JSON.stringify(all, null, 2), "persistent");
532
+ return `Plan updated: ${created} created, ${updated} updated, ${all.length} total.`;
533
+ }
534
+ });
535
+ }
536
+
537
+ // src/tools/planning/review-todos.tool.ts
538
+ import { z as z8 } from "zod";
539
+ function createReviewTodosTool(fs) {
540
+ return tool({
541
+ description: "Review current task plan and optionally update todo statuses. Returns the full plan with progress.",
542
+ inputSchema: z8.object({
543
+ updates: z8.array(
544
+ z8.object({
545
+ id: z8.string().describe("Todo ID to update"),
546
+ status: z8.enum(["pending", "in_progress", "done", "blocked"])
547
+ })
548
+ ).optional().describe("Optional status updates to apply")
549
+ }),
550
+ execute: async ({ updates }) => {
551
+ const todos = await loadTodos(fs);
552
+ if (todos.length === 0) return "No todos found. Use write_todos first.";
553
+ if (updates?.length) {
554
+ const byId = new Map(todos.map((t) => [t.id, t]));
555
+ for (const u of updates) {
556
+ const t = byId.get(u.id);
557
+ if (!t) continue;
558
+ t.status = u.status;
559
+ t.updatedAt = Date.now();
560
+ if (u.status === "done") t.completedAt = Date.now();
561
+ }
562
+ await fs.write(
563
+ TODOS_PATH,
564
+ JSON.stringify(todos, null, 2),
565
+ "persistent"
566
+ );
567
+ }
568
+ return formatPlan(todos);
569
+ }
570
+ });
571
+ }
572
+ function formatPlan(todos) {
573
+ const counts = { pending: 0, in_progress: 0, done: 0, blocked: 0 };
574
+ for (const t of todos) counts[t.status]++;
575
+ const header = `Plan: ${todos.length} total | ${counts.done} done, ${counts.in_progress} in-progress, ${counts.pending} pending, ${counts.blocked} blocked`;
576
+ const lines = todos.map(
577
+ (t) => `[${t.status}] ${t.id}: ${t.title}` + (t.dependencies?.length ? ` (deps: ${t.dependencies.join(", ")})` : "")
578
+ );
579
+ return [header, "---", ...lines].join("\n");
580
+ }
581
+
582
+ // src/tools/planning/plan-create.tool.ts
583
+ import { z as z10 } from "zod";
584
+
585
+ // src/domain/plan.schema.ts
586
+ import { z as z9 } from "zod";
587
+ var StepExecutionModeSchema = z9.enum([
588
+ "sequential",
589
+ "parallel",
590
+ "conditional",
591
+ "loop"
592
+ ]);
593
+ var StepStatusSchema = z9.enum([
594
+ "idle",
595
+ "pending",
596
+ "running",
597
+ "completed",
598
+ "failed",
599
+ "skipped",
600
+ "blocked",
601
+ "cancelled"
602
+ ]);
603
+ var StepPrioritySchema = z9.enum(["low", "medium", "high", "critical"]);
604
+ var PlanStatusSchema = z9.enum([
605
+ "draft",
606
+ "active",
607
+ "paused",
608
+ "completed",
609
+ "failed",
610
+ "cancelled"
611
+ ]);
612
+ var STEP_STATUS_TRANSITIONS = {
613
+ idle: ["pending", "skipped", "cancelled"],
614
+ pending: ["running", "blocked", "skipped", "cancelled"],
615
+ running: ["completed", "failed", "cancelled"],
616
+ completed: [],
617
+ failed: ["pending"],
618
+ // retry
619
+ skipped: [],
620
+ blocked: ["pending", "cancelled"],
621
+ cancelled: []
622
+ };
623
+ var ResourceRequirementsSchema = z9.object({
624
+ /** Budget massimo di token (input + output) per questo step */
625
+ maxTokenBudget: z9.number().positive().optional(),
626
+ /** Timeout in millisecondi */
627
+ timeoutMs: z9.number().positive().optional(),
628
+ /** Numero massimo di retry in caso di fallimento */
629
+ maxRetries: z9.number().int().min(0).optional().default(0),
630
+ /** Modello preferito (override del default del piano) */
631
+ preferredModel: z9.string().optional(),
632
+ /** Livello di concorrenza massimo per sotto-step paralleli */
633
+ maxConcurrency: z9.number().int().positive().optional().default(5)
634
+ });
635
+ var IOFieldSchema = z9.object({
636
+ name: z9.string().describe("Nome del campo"),
637
+ type: z9.enum(["string", "number", "boolean", "array", "object", "any"]).describe("Tipo del campo"),
638
+ description: z9.string().optional(),
639
+ required: z9.boolean().default(true)
640
+ });
641
+ var StepContractSchema = z9.object({
642
+ /** Campi di input attesi — cosa serve allo step per eseguire */
643
+ inputs: z9.array(IOFieldSchema).default([]),
644
+ /** Campi di output prodotti — cosa produce lo step */
645
+ outputs: z9.array(IOFieldSchema).default([])
646
+ });
647
+ var StepResultSchema = z9.object({
648
+ /** Output testuale prodotto */
649
+ output: z9.string().optional(),
650
+ /** Dati strutturati di output (match con contract.outputs) */
651
+ data: z9.record(z9.string(), z9.unknown()).optional(),
652
+ /** Uso token */
653
+ tokenUsage: z9.object({ input: z9.number(), output: z9.number() }).optional(),
654
+ /** Durata in ms */
655
+ durationMs: z9.number().optional(),
656
+ /** Errore se fallito */
657
+ error: z9.string().optional()
658
+ });
659
+ var StepConditionSchema = z9.object({
660
+ /** Espressione condizionale (riferimenti a output di step precedenti) */
661
+ expression: z9.string().describe(
662
+ "Espressione valutabile, es: 'steps.auth-design.status === completed'"
663
+ ),
664
+ /** ID dello step da eseguire se la condizione è vera */
665
+ ifTrueStepId: z9.string().optional(),
666
+ /** ID dello step da eseguire se la condizione è falsa */
667
+ ifFalseStepId: z9.string().optional()
668
+ });
669
+ var LoopConfigSchema = z9.object({
670
+ /** Condizione di continuazione */
671
+ condition: z9.string().describe(
672
+ "Espressione per continuare il loop, es: 'iteration < 3 && !steps.validate.data.allPassing'"
673
+ ),
674
+ /** Numero massimo di iterazioni (safety) */
675
+ maxIterations: z9.number().int().positive().default(10),
676
+ /** ID dello step che funge da corpo del loop */
677
+ bodyStepIds: z9.array(z9.string()).min(1)
678
+ });
679
+ var BaseSubStepSchema = z9.object({
680
+ id: z9.string().describe("ID univoco del sotto-step (kebab-case)"),
681
+ title: z9.string().describe("Titolo breve del sotto-step"),
682
+ description: z9.string().optional(),
683
+ status: StepStatusSchema.default("idle"),
684
+ executionMode: StepExecutionModeSchema.default("sequential"),
685
+ contract: StepContractSchema.optional(),
686
+ resources: ResourceRequirementsSchema.optional(),
687
+ result: StepResultSchema.optional(),
688
+ /** Istruzioni/prompt per l'agente che esegue questo sotto-step */
689
+ prompt: z9.string().optional(),
690
+ /** Tool specifici richiesti per questo sotto-step */
691
+ requiredTools: z9.array(z9.string()).default([])
692
+ });
693
+ var SubStepSchema = BaseSubStepSchema.extend({
694
+ children: z9.lazy(() => z9.array(SubStepSchema)).optional()
695
+ });
696
+ var StepSchema = z9.object({
697
+ id: z9.string().describe("ID univoco dello step (kebab-case)"),
698
+ title: z9.string().describe("Titolo breve dello step"),
699
+ description: z9.string().optional().describe("Descrizione dettagliata"),
700
+ /** Modalità di esecuzione */
701
+ executionMode: StepExecutionModeSchema.default("sequential"),
702
+ /** Stato corrente */
703
+ status: StepStatusSchema.default("idle"),
704
+ /** Priorità */
705
+ priority: StepPrioritySchema.default("medium"),
706
+ /** Contratto input/output strutturato */
707
+ contract: StepContractSchema.default({ inputs: [], outputs: [] }),
708
+ /** Requisiti di risorse */
709
+ resources: ResourceRequirementsSchema.optional(),
710
+ /** Dipendenze esplicite: ID di step nella stessa fase o in fasi precedenti */
711
+ dependencies: z9.array(z9.string()).default([]),
712
+ /** Istruzioni/prompt per l'agente */
713
+ prompt: z9.string().optional().describe(
714
+ "Prompt specifico per l'agente che esegue questo step"
715
+ ),
716
+ /** Tool specifici richiesti */
717
+ requiredTools: z9.array(z9.string()).default([]),
718
+ /** Sotto-step (gerarchia ricorsiva) */
719
+ subSteps: z9.array(SubStepSchema).default([]),
720
+ /** Condizione (solo se executionMode === 'conditional') */
721
+ condition: StepConditionSchema.optional(),
722
+ /** Configurazione loop (solo se executionMode === 'loop') */
723
+ loopConfig: LoopConfigSchema.optional(),
724
+ /** Risultato dell'esecuzione */
725
+ result: StepResultSchema.optional(),
726
+ /** Timestamp */
727
+ createdAt: z9.number().default(() => Date.now()),
728
+ updatedAt: z9.number().default(() => Date.now()),
729
+ startedAt: z9.number().optional(),
730
+ completedAt: z9.number().optional(),
731
+ /** Metadati arbitrari */
732
+ metadata: z9.record(z9.string(), z9.unknown()).optional()
733
+ });
734
+ var PhaseSchema = z9.object({
735
+ id: z9.string().describe("ID univoco della fase (kebab-case)"),
736
+ title: z9.string().describe("Titolo della fase"),
737
+ description: z9.string().optional(),
738
+ /** Come eseguire gli step dentro questa fase */
739
+ executionMode: StepExecutionModeSchema.default("sequential"),
740
+ /** Stato derivato dagli step contenuti */
741
+ status: StepStatusSchema.default("idle"),
742
+ /** Step contenuti in questa fase */
743
+ steps: z9.array(StepSchema).min(1).describe("Step della fase"),
744
+ /** Ordine (per fasi sequenziali nel piano) */
745
+ order: z9.number().int().min(0).default(0),
746
+ /** Dipendenze da altre fasi (ID) */
747
+ dependencies: z9.array(z9.string()).default([]),
748
+ /** Requisiti di risorse per l'intera fase */
749
+ resources: ResourceRequirementsSchema.optional(),
750
+ /** Timestamp */
751
+ createdAt: z9.number().default(() => Date.now()),
752
+ updatedAt: z9.number().default(() => Date.now())
753
+ });
754
+ var PlanMetadataSchema = z9.object({
755
+ /** Chi ha creato il piano (agente o umano) */
756
+ createdBy: z9.string().default("agent"),
757
+ /** Versione del piano (incrementa ad ogni modifica) */
758
+ version: z9.number().int().min(1).default(1),
759
+ /** Tag liberi per classificazione */
760
+ tags: z9.array(z9.string()).default([]),
761
+ /** Obiettivo di alto livello che il piano deve raggiungere */
762
+ goal: z9.string().describe("Obiettivo principale del piano"),
763
+ /** Contesto/vincoli aggiuntivi */
764
+ constraints: z9.array(z9.string()).default([]),
765
+ /** Stima dei token totali per l'intero piano */
766
+ estimatedTotalTokens: z9.number().optional(),
767
+ /** Stima della durata totale in ms */
768
+ estimatedDurationMs: z9.number().optional()
769
+ });
770
+ var PlanSchema = z9.object({
771
+ id: z9.string().describe("ID univoco del piano (kebab-case)"),
772
+ title: z9.string().describe("Titolo del piano"),
773
+ description: z9.string().optional().describe("Descrizione del piano"),
774
+ /** Stato globale del piano */
775
+ status: PlanStatusSchema.default("draft"),
776
+ /** Metadata del piano */
777
+ metadata: PlanMetadataSchema,
778
+ /** Fasi del piano (ordinate) */
779
+ phases: z9.array(PhaseSchema).min(1).describe("Fasi del piano"),
780
+ /** Configurazione risorse globali */
781
+ globalResources: ResourceRequirementsSchema.optional(),
782
+ /** Timestamp */
783
+ createdAt: z9.number().default(() => Date.now()),
784
+ updatedAt: z9.number().default(() => Date.now()),
785
+ startedAt: z9.number().optional(),
786
+ completedAt: z9.number().optional()
787
+ });
788
+ var PlanEventTypeSchema = z9.enum([
789
+ "plan:created",
790
+ "plan:started",
791
+ "plan:paused",
792
+ "plan:resumed",
793
+ "plan:completed",
794
+ "plan:failed",
795
+ "plan:cancelled",
796
+ "plan:updated",
797
+ "phase:started",
798
+ "phase:completed",
799
+ "phase:failed",
800
+ "step:started",
801
+ "step:completed",
802
+ "step:failed",
803
+ "step:skipped",
804
+ "step:blocked",
805
+ "step:retrying",
806
+ "substep:started",
807
+ "substep:completed",
808
+ "substep:failed"
809
+ ]);
810
+ var PlanEventSchema = z9.object({
811
+ type: PlanEventTypeSchema,
812
+ planId: z9.string(),
813
+ phaseId: z9.string().optional(),
814
+ stepId: z9.string().optional(),
815
+ subStepId: z9.string().optional(),
816
+ timestamp: z9.number().default(() => Date.now()),
817
+ data: z9.record(z9.string(), z9.unknown()).optional()
818
+ });
819
+ var StepProgressSchema = z9.object({
820
+ stepId: z9.string(),
821
+ title: z9.string(),
822
+ status: StepStatusSchema,
823
+ /** Progresso dei sotto-step (0.0 - 1.0) */
824
+ progress: z9.number().min(0).max(1).default(0),
825
+ /** Numero sotto-step completati / totali */
826
+ subStepsDone: z9.number().int().min(0).default(0),
827
+ subStepsTotal: z9.number().int().min(0).default(0)
828
+ });
829
+ var PhaseProgressSchema = z9.object({
830
+ phaseId: z9.string(),
831
+ title: z9.string(),
832
+ status: StepStatusSchema,
833
+ progress: z9.number().min(0).max(1).default(0),
834
+ steps: z9.array(StepProgressSchema)
835
+ });
836
+ var PlanProgressSchema = z9.object({
837
+ planId: z9.string(),
838
+ title: z9.string(),
839
+ status: PlanStatusSchema,
840
+ /** Progresso globale (0.0 - 1.0) */
841
+ progress: z9.number().min(0).max(1).default(0),
842
+ /** Conteggi globali */
843
+ totalSteps: z9.number().int().min(0),
844
+ completedSteps: z9.number().int().min(0),
845
+ failedSteps: z9.number().int().min(0),
846
+ /** Dettaglio per fase */
847
+ phases: z9.array(PhaseProgressSchema),
848
+ /** Durata totale fino ad ora */
849
+ elapsedMs: z9.number().optional(),
850
+ /** Token usati fino ad ora */
851
+ tokenUsage: z9.object({ input: z9.number(), output: z9.number() }).optional()
852
+ });
853
+ function validatePlan(plan) {
854
+ const errors = [];
855
+ const warnings = [];
856
+ const allStepIds = /* @__PURE__ */ new Set();
857
+ const allPhaseIds = /* @__PURE__ */ new Set();
858
+ for (const phase of plan.phases) {
859
+ if (allPhaseIds.has(phase.id)) {
860
+ errors.push(`ID fase duplicato: "${phase.id}"`);
861
+ }
862
+ allPhaseIds.add(phase.id);
863
+ for (const step of phase.steps) {
864
+ if (allStepIds.has(step.id)) {
865
+ errors.push(`ID step duplicato: "${step.id}"`);
866
+ }
867
+ allStepIds.add(step.id);
868
+ collectSubStepIds(step.subSteps, allStepIds, errors);
869
+ }
870
+ }
871
+ for (const phase of plan.phases) {
872
+ for (const dep of phase.dependencies) {
873
+ if (!allPhaseIds.has(dep)) {
874
+ errors.push(`Fase "${phase.id}" dipende da fase inesistente "${dep}"`);
875
+ }
876
+ if (dep === phase.id) {
877
+ errors.push(`Fase "${phase.id}" dipende da se stessa`);
878
+ }
879
+ }
880
+ for (const step of phase.steps) {
881
+ for (const dep of step.dependencies) {
882
+ if (!allStepIds.has(dep)) {
883
+ errors.push(
884
+ `Step "${step.id}" dipende da step inesistente "${dep}"`
885
+ );
886
+ }
887
+ if (dep === step.id) {
888
+ errors.push(`Step "${step.id}" dipende da se stesso`);
889
+ }
890
+ }
891
+ if (step.executionMode === "conditional" && !step.condition) {
892
+ errors.push(
893
+ `Step "${step.id}" \xE8 condizionale ma manca la configurazione condition`
894
+ );
895
+ }
896
+ if (step.executionMode === "loop" && !step.loopConfig) {
897
+ errors.push(
898
+ `Step "${step.id}" \xE8 loop ma manca la configurazione loopConfig`
899
+ );
900
+ }
901
+ if (step.contract.inputs.length === 0 && step.dependencies.length > 0) {
902
+ warnings.push(
903
+ `Step "${step.id}" ha dipendenze ma nessun input definito nel contratto`
904
+ );
905
+ }
906
+ }
907
+ }
908
+ const phaseDepErrors = detectCycles(
909
+ plan.phases.map((p) => p.id),
910
+ plan.phases.reduce(
911
+ (acc, p) => {
912
+ acc[p.id] = p.dependencies;
913
+ return acc;
914
+ },
915
+ {}
916
+ )
917
+ );
918
+ errors.push(...phaseDepErrors);
919
+ return { valid: errors.length === 0, errors, warnings };
920
+ }
921
+ function collectSubStepIds(subSteps, allIds, errors) {
922
+ for (const ss of subSteps) {
923
+ if (allIds.has(ss.id)) {
924
+ errors.push(`ID sotto-step duplicato: "${ss.id}"`);
925
+ }
926
+ allIds.add(ss.id);
927
+ if (ss.children) {
928
+ collectSubStepIds(ss.children, allIds, errors);
929
+ }
930
+ }
931
+ }
932
+ function detectCycles(nodeIds, deps) {
933
+ const errors = [];
934
+ const visited = /* @__PURE__ */ new Set();
935
+ const stack = /* @__PURE__ */ new Set();
936
+ const visit = (id) => {
937
+ if (stack.has(id)) {
938
+ errors.push(`Ciclo rilevato che coinvolge "${id}"`);
939
+ return;
940
+ }
941
+ if (visited.has(id)) return;
942
+ stack.add(id);
943
+ for (const dep of deps[id] ?? []) {
944
+ visit(dep);
945
+ }
946
+ stack.delete(id);
947
+ visited.add(id);
948
+ };
949
+ for (const id of nodeIds) {
950
+ visit(id);
951
+ }
952
+ return errors;
953
+ }
954
+ function isValidStepTransition(from, to) {
955
+ return STEP_STATUS_TRANSITIONS[from]?.includes(to) ?? false;
956
+ }
957
+ function calculateProgress(plan) {
958
+ let totalSteps = 0;
959
+ let completedSteps = 0;
960
+ let failedSteps = 0;
961
+ const phaseProgresses = plan.phases.map((phase) => {
962
+ const stepProgresses = phase.steps.map((step) => {
963
+ totalSteps++;
964
+ const subTotal = countSubSteps(step.subSteps);
965
+ const subDone = countCompletedSubSteps(step.subSteps);
966
+ if (step.status === "completed") completedSteps++;
967
+ if (step.status === "failed") failedSteps++;
968
+ const stepProgress = step.status === "completed" ? 1 : step.status === "running" && subTotal > 0 ? subDone / subTotal : 0;
969
+ return {
970
+ stepId: step.id,
971
+ title: step.title,
972
+ status: step.status,
973
+ progress: stepProgress,
974
+ subStepsDone: subDone,
975
+ subStepsTotal: subTotal
976
+ };
977
+ });
978
+ const phaseTotal = stepProgresses.length;
979
+ const phaseProgress = phaseTotal > 0 ? stepProgresses.reduce((sum, s) => sum + s.progress, 0) / phaseTotal : 0;
980
+ const phaseStatus = derivePhaseStatus(phase.steps);
981
+ return {
982
+ phaseId: phase.id,
983
+ title: phase.title,
984
+ status: phaseStatus,
985
+ progress: phaseProgress,
986
+ steps: stepProgresses
987
+ };
988
+ });
989
+ const globalProgress = totalSteps > 0 ? completedSteps / totalSteps : 0;
990
+ const elapsedMs = plan.startedAt ? Date.now() - plan.startedAt : void 0;
991
+ return {
992
+ planId: plan.id,
993
+ title: plan.title,
994
+ status: plan.status,
995
+ progress: globalProgress,
996
+ totalSteps,
997
+ completedSteps,
998
+ failedSteps,
999
+ phases: phaseProgresses,
1000
+ elapsedMs
1001
+ };
1002
+ }
1003
+ function countSubSteps(subSteps) {
1004
+ let count = 0;
1005
+ for (const ss of subSteps) {
1006
+ count++;
1007
+ if (ss.children) count += countSubSteps(ss.children);
1008
+ }
1009
+ return count;
1010
+ }
1011
+ function countCompletedSubSteps(subSteps) {
1012
+ let count = 0;
1013
+ for (const ss of subSteps) {
1014
+ if (ss.status === "completed") count++;
1015
+ if (ss.children) count += countCompletedSubSteps(ss.children);
1016
+ }
1017
+ return count;
1018
+ }
1019
+ function derivePhaseStatus(steps) {
1020
+ if (steps.every((s) => s.status === "completed")) return "completed";
1021
+ if (steps.some((s) => s.status === "failed")) return "failed";
1022
+ if (steps.some((s) => s.status === "running")) return "running";
1023
+ if (steps.some((s) => s.status === "pending")) return "pending";
1024
+ if (steps.some((s) => s.status === "blocked")) return "blocked";
1025
+ return "idle";
1026
+ }
1027
+
1028
+ // src/tools/planning/plan-shared.ts
1029
+ var PLAN_PATH = "plan.json";
1030
+ async function loadPlan(fs) {
1031
+ const exists = await fs.exists(PLAN_PATH, "persistent");
1032
+ if (!exists) return null;
1033
+ const raw = await fs.read(PLAN_PATH, "persistent");
1034
+ return JSON.parse(raw);
1035
+ }
1036
+ async function savePlan(fs, plan) {
1037
+ await fs.write(PLAN_PATH, JSON.stringify(plan, null, 2), "persistent");
1038
+ }
1039
+
1040
+ // src/tools/planning/plan-create.tool.ts
1041
+ var StepInputSchema = z10.object({
1042
+ id: z10.string().describe("ID univoco dello step (kebab-case)"),
1043
+ title: z10.string().describe("Titolo breve"),
1044
+ description: z10.string().optional(),
1045
+ executionMode: StepExecutionModeSchema.optional(),
1046
+ priority: StepPrioritySchema.optional(),
1047
+ prompt: z10.string().optional().describe("Istruzioni per l'agente"),
1048
+ dependencies: z10.array(z10.string()).optional(),
1049
+ requiredTools: z10.array(z10.string()).optional(),
1050
+ inputs: z10.array(IOFieldSchema).optional().describe("Contratto input"),
1051
+ outputs: z10.array(IOFieldSchema).optional().describe("Contratto output"),
1052
+ maxTokenBudget: z10.number().optional(),
1053
+ timeoutMs: z10.number().optional(),
1054
+ subSteps: z10.array(z10.object({
1055
+ id: z10.string(),
1056
+ title: z10.string(),
1057
+ description: z10.string().optional(),
1058
+ prompt: z10.string().optional()
1059
+ })).optional()
1060
+ });
1061
+ var PhaseInputSchema = z10.object({
1062
+ id: z10.string().describe("ID univoco della fase (kebab-case)"),
1063
+ title: z10.string().describe("Titolo della fase"),
1064
+ description: z10.string().optional(),
1065
+ executionMode: StepExecutionModeSchema.optional(),
1066
+ dependencies: z10.array(z10.string()).optional(),
1067
+ steps: z10.array(StepInputSchema).min(1)
1068
+ });
1069
+ function createPlanCreateTool(fs) {
1070
+ return tool({
1071
+ description: "Crea un piano strutturato gerarchico con fasi, step e sotto-step. Ogni step ha contratti input/output, priorit\xE0, risorse e dipendenze. Supporta esecuzione sequenziale, parallela, condizionale e loop.",
1072
+ inputSchema: z10.object({
1073
+ id: z10.string().describe("ID univoco del piano (kebab-case)"),
1074
+ title: z10.string().describe("Titolo del piano"),
1075
+ description: z10.string().optional(),
1076
+ goal: z10.string().describe("Obiettivo principale del piano"),
1077
+ tags: z10.array(z10.string()).optional(),
1078
+ constraints: z10.array(z10.string()).optional(),
1079
+ phases: z10.array(PhaseInputSchema).min(1).describe("Fasi del piano"),
1080
+ maxTokenBudget: z10.number().optional(),
1081
+ timeoutMs: z10.number().optional()
1082
+ }),
1083
+ execute: async (input) => {
1084
+ const now = Date.now();
1085
+ const phases = input.phases.map((p, idx) => ({
1086
+ id: p.id,
1087
+ title: p.title,
1088
+ description: p.description,
1089
+ executionMode: p.executionMode ?? "sequential",
1090
+ status: "idle",
1091
+ order: idx,
1092
+ dependencies: p.dependencies ?? [],
1093
+ steps: p.steps.map((s) => ({
1094
+ id: s.id,
1095
+ title: s.title,
1096
+ description: s.description,
1097
+ executionMode: s.executionMode ?? "sequential",
1098
+ status: "idle",
1099
+ priority: s.priority ?? "medium",
1100
+ contract: {
1101
+ inputs: s.inputs ?? [],
1102
+ outputs: s.outputs ?? []
1103
+ },
1104
+ resources: s.maxTokenBudget || s.timeoutMs ? {
1105
+ maxTokenBudget: s.maxTokenBudget,
1106
+ timeoutMs: s.timeoutMs,
1107
+ maxRetries: 0,
1108
+ maxConcurrency: 5
1109
+ } : void 0,
1110
+ dependencies: s.dependencies ?? [],
1111
+ prompt: s.prompt,
1112
+ requiredTools: s.requiredTools ?? [],
1113
+ subSteps: (s.subSteps ?? []).map((ss) => ({
1114
+ id: ss.id,
1115
+ title: ss.title,
1116
+ description: ss.description,
1117
+ prompt: ss.prompt,
1118
+ status: "idle",
1119
+ executionMode: "sequential",
1120
+ requiredTools: []
1121
+ })),
1122
+ createdAt: now,
1123
+ updatedAt: now
1124
+ })),
1125
+ createdAt: now,
1126
+ updatedAt: now
1127
+ }));
1128
+ const plan = PlanSchema.parse({
1129
+ id: input.id,
1130
+ title: input.title,
1131
+ description: input.description,
1132
+ status: "draft",
1133
+ metadata: {
1134
+ goal: input.goal,
1135
+ createdBy: "agent",
1136
+ version: 1,
1137
+ tags: input.tags ?? [],
1138
+ constraints: input.constraints ?? []
1139
+ },
1140
+ phases,
1141
+ globalResources: input.maxTokenBudget || input.timeoutMs ? {
1142
+ maxTokenBudget: input.maxTokenBudget,
1143
+ timeoutMs: input.timeoutMs,
1144
+ maxRetries: 0,
1145
+ maxConcurrency: 5
1146
+ } : void 0,
1147
+ createdAt: now,
1148
+ updatedAt: now
1149
+ });
1150
+ const validation = validatePlan(plan);
1151
+ if (!validation.valid) {
1152
+ return `Errore nella creazione del piano:
1153
+ ${validation.errors.join("\n")}`;
1154
+ }
1155
+ await savePlan(fs, plan);
1156
+ const totalSteps = plan.phases.reduce((sum, p) => sum + p.steps.length, 0);
1157
+ const warnings = validation.warnings.length > 0 ? `
1158
+ Warning: ${validation.warnings.join("; ")}` : "";
1159
+ return `Piano "${plan.title}" creato con successo.
1160
+ ID: ${plan.id}
1161
+ Fasi: ${plan.phases.length}
1162
+ Step totali: ${totalSteps}
1163
+ Stato: ${plan.status}` + warnings;
1164
+ }
1165
+ });
1166
+ }
1167
+
1168
+ // src/tools/planning/plan-update.tool.ts
1169
+ import { z as z11 } from "zod";
1170
+ function createPlanUpdateTool(fs) {
1171
+ return tool({
1172
+ description: "Modifica un piano esistente a runtime: aggiorna stato, aggiungi/rimuovi step, modifica prompt, priorit\xE0, dipendenze. Supporta transizioni di stato validate.",
1173
+ inputSchema: z11.object({
1174
+ action: z11.enum([
1175
+ "update_step",
1176
+ "add_step",
1177
+ "remove_step",
1178
+ "update_phase",
1179
+ "add_phase",
1180
+ "update_plan_status",
1181
+ "set_result"
1182
+ ]).describe("Tipo di modifica"),
1183
+ // Per update_step / add_step / remove_step
1184
+ phaseId: z11.string().optional().describe("ID della fase target"),
1185
+ stepId: z11.string().optional().describe("ID dello step target"),
1186
+ // Campi aggiornabili
1187
+ status: StepStatusSchema.optional(),
1188
+ title: z11.string().optional(),
1189
+ description: z11.string().optional(),
1190
+ prompt: z11.string().optional(),
1191
+ priority: StepPrioritySchema.optional(),
1192
+ executionMode: StepExecutionModeSchema.optional(),
1193
+ dependencies: z11.array(z11.string()).optional(),
1194
+ requiredTools: z11.array(z11.string()).optional(),
1195
+ inputs: z11.array(IOFieldSchema).optional(),
1196
+ outputs: z11.array(IOFieldSchema).optional(),
1197
+ // Per set_result
1198
+ resultOutput: z11.string().optional(),
1199
+ resultData: z11.record(z11.string(), z11.unknown()).optional(),
1200
+ // Per add_phase
1201
+ phaseTitle: z11.string().optional(),
1202
+ phaseOrder: z11.number().optional(),
1203
+ // Per update_plan_status
1204
+ planStatus: z11.enum(["draft", "active", "paused", "completed", "failed", "cancelled"]).optional()
1205
+ }),
1206
+ execute: async (input) => {
1207
+ const plan = await loadPlan(fs);
1208
+ if (!plan) return "Nessun piano trovato. Usa plan_create prima.";
1209
+ const now = Date.now();
1210
+ switch (input.action) {
1211
+ case "update_step": {
1212
+ if (!input.stepId) return "stepId richiesto per update_step";
1213
+ const { phase, step } = findStep(plan, input.stepId);
1214
+ if (!step) return `Step "${input.stepId}" non trovato`;
1215
+ if (input.status && input.status !== step.status) {
1216
+ if (!isValidStepTransition(step.status, input.status)) {
1217
+ return `Transizione non valida: "${step.status}" \u2192 "${input.status}" per step "${step.id}"`;
1218
+ }
1219
+ step.status = input.status;
1220
+ if (input.status === "running") step.startedAt = now;
1221
+ if (input.status === "completed" || input.status === "failed") {
1222
+ step.completedAt = now;
1223
+ }
1224
+ }
1225
+ if (input.title) step.title = input.title;
1226
+ if (input.description) step.description = input.description;
1227
+ if (input.prompt) step.prompt = input.prompt;
1228
+ if (input.priority) step.priority = input.priority;
1229
+ if (input.executionMode) step.executionMode = input.executionMode;
1230
+ if (input.dependencies) step.dependencies = input.dependencies;
1231
+ if (input.requiredTools) step.requiredTools = input.requiredTools;
1232
+ if (input.inputs) step.contract.inputs = input.inputs;
1233
+ if (input.outputs) step.contract.outputs = input.outputs;
1234
+ step.updatedAt = now;
1235
+ plan.updatedAt = now;
1236
+ plan.metadata.version++;
1237
+ await savePlan(fs, plan);
1238
+ return `Step "${step.id}" aggiornato (v${plan.metadata.version}).`;
1239
+ }
1240
+ case "add_step": {
1241
+ if (!input.phaseId) return "phaseId richiesto per add_step";
1242
+ if (!input.stepId || !input.title) return "stepId e title richiesti per add_step";
1243
+ const phase = plan.phases.find((p) => p.id === input.phaseId);
1244
+ if (!phase) return `Fase "${input.phaseId}" non trovata`;
1245
+ const newStep = {
1246
+ id: input.stepId,
1247
+ title: input.title,
1248
+ description: input.description,
1249
+ executionMode: input.executionMode ?? "sequential",
1250
+ status: "idle",
1251
+ priority: input.priority ?? "medium",
1252
+ contract: {
1253
+ inputs: input.inputs ?? [],
1254
+ outputs: input.outputs ?? []
1255
+ },
1256
+ dependencies: input.dependencies ?? [],
1257
+ prompt: input.prompt,
1258
+ requiredTools: input.requiredTools ?? [],
1259
+ subSteps: [],
1260
+ createdAt: now,
1261
+ updatedAt: now
1262
+ };
1263
+ phase.steps.push(newStep);
1264
+ phase.updatedAt = now;
1265
+ plan.updatedAt = now;
1266
+ plan.metadata.version++;
1267
+ const validation = validatePlan(plan);
1268
+ if (!validation.valid) {
1269
+ phase.steps.pop();
1270
+ return `Errore: lo step creerebbe problemi:
1271
+ ${validation.errors.join("\n")}`;
1272
+ }
1273
+ await savePlan(fs, plan);
1274
+ return `Step "${input.stepId}" aggiunto alla fase "${input.phaseId}" (v${plan.metadata.version}).`;
1275
+ }
1276
+ case "remove_step": {
1277
+ if (!input.stepId) return "stepId richiesto per remove_step";
1278
+ const { phase } = findStep(plan, input.stepId);
1279
+ if (!phase) return `Step "${input.stepId}" non trovato`;
1280
+ phase.steps = phase.steps.filter((s) => s.id !== input.stepId);
1281
+ phase.updatedAt = now;
1282
+ plan.updatedAt = now;
1283
+ plan.metadata.version++;
1284
+ await savePlan(fs, plan);
1285
+ return `Step "${input.stepId}" rimosso (v${plan.metadata.version}).`;
1286
+ }
1287
+ case "update_phase": {
1288
+ if (!input.phaseId) return "phaseId richiesto per update_phase";
1289
+ const phase = plan.phases.find((p) => p.id === input.phaseId);
1290
+ if (!phase) return `Fase "${input.phaseId}" non trovata`;
1291
+ if (input.title) phase.title = input.title;
1292
+ if (input.description) phase.description = input.description;
1293
+ if (input.executionMode) phase.executionMode = input.executionMode;
1294
+ if (input.dependencies) phase.dependencies = input.dependencies;
1295
+ phase.updatedAt = now;
1296
+ plan.updatedAt = now;
1297
+ plan.metadata.version++;
1298
+ await savePlan(fs, plan);
1299
+ return `Fase "${phase.id}" aggiornata (v${plan.metadata.version}).`;
1300
+ }
1301
+ case "add_phase": {
1302
+ if (!input.phaseId || !input.phaseTitle) {
1303
+ return "phaseId e phaseTitle richiesti per add_phase";
1304
+ }
1305
+ const newPhase = {
1306
+ id: input.phaseId,
1307
+ title: input.phaseTitle,
1308
+ description: input.description,
1309
+ executionMode: input.executionMode ?? "sequential",
1310
+ status: "idle",
1311
+ steps: [],
1312
+ order: input.phaseOrder ?? plan.phases.length,
1313
+ dependencies: input.dependencies ?? [],
1314
+ createdAt: now,
1315
+ updatedAt: now
1316
+ };
1317
+ plan.phases.push(newPhase);
1318
+ plan.updatedAt = now;
1319
+ plan.metadata.version++;
1320
+ await savePlan(fs, plan);
1321
+ return `Fase "${input.phaseId}" aggiunta (v${plan.metadata.version}). Aggiungi step con add_step.`;
1322
+ }
1323
+ case "update_plan_status": {
1324
+ if (!input.planStatus) return "planStatus richiesto";
1325
+ plan.status = input.planStatus;
1326
+ if (input.planStatus === "active" && !plan.startedAt) {
1327
+ plan.startedAt = now;
1328
+ }
1329
+ if (input.planStatus === "completed" || input.planStatus === "failed") {
1330
+ plan.completedAt = now;
1331
+ }
1332
+ plan.updatedAt = now;
1333
+ plan.metadata.version++;
1334
+ await savePlan(fs, plan);
1335
+ return `Piano aggiornato a stato "${input.planStatus}" (v${plan.metadata.version}).`;
1336
+ }
1337
+ case "set_result": {
1338
+ if (!input.stepId) return "stepId richiesto per set_result";
1339
+ const { step } = findStep(plan, input.stepId);
1340
+ if (!step) return `Step "${input.stepId}" non trovato`;
1341
+ step.result = {
1342
+ output: input.resultOutput,
1343
+ data: input.resultData
1344
+ };
1345
+ step.updatedAt = now;
1346
+ plan.updatedAt = now;
1347
+ await savePlan(fs, plan);
1348
+ return `Risultato salvato per step "${input.stepId}".`;
1349
+ }
1350
+ default:
1351
+ return `Azione "${input.action}" non supportata.`;
1352
+ }
1353
+ }
1354
+ });
1355
+ }
1356
+ function findStep(plan, stepId) {
1357
+ for (const phase of plan.phases) {
1358
+ const step = phase.steps.find((s) => s.id === stepId);
1359
+ if (step) return { phase, step };
1360
+ }
1361
+ return {};
1362
+ }
1363
+
1364
+ // src/tools/planning/plan-status.tool.ts
1365
+ import { z as z12 } from "zod";
1366
+ function createPlanStatusTool(fs) {
1367
+ return tool({
1368
+ description: "Mostra lo stato corrente del piano con progress tree. Visualizza avanzamento per fase, step e sotto-step.",
1369
+ inputSchema: z12.object({
1370
+ verbose: z12.boolean().optional().default(false).describe("Se true, mostra anche i sotto-step")
1371
+ }),
1372
+ execute: async ({ verbose }) => {
1373
+ const plan = await loadPlan(fs);
1374
+ if (!plan) return "Nessun piano trovato. Usa plan_create prima.";
1375
+ const progress = calculateProgress(plan);
1376
+ return formatProgressTree(progress, plan, verbose);
1377
+ }
1378
+ });
1379
+ }
1380
+ function formatProgressTree(progress, plan, verbose) {
1381
+ const lines = [];
1382
+ const pct = (n) => `${Math.round(n * 100)}%`;
1383
+ lines.push(
1384
+ `\u{1F4CB} ${progress.title} [${progress.status}] \u2014 ${pct(progress.progress)} completato`
1385
+ );
1386
+ lines.push(
1387
+ ` Step: ${progress.completedSteps}/${progress.totalSteps} completati` + (progress.failedSteps > 0 ? `, ${progress.failedSteps} falliti` : "")
1388
+ );
1389
+ if (progress.elapsedMs) {
1390
+ lines.push(` Tempo: ${Math.round(progress.elapsedMs / 1e3)}s`);
1391
+ }
1392
+ if (progress.tokenUsage) {
1393
+ lines.push(
1394
+ ` Token: ${progress.tokenUsage.input + progress.tokenUsage.output} totali`
1395
+ );
1396
+ }
1397
+ lines.push(` Versione: v${plan.metadata.version}`);
1398
+ lines.push("");
1399
+ for (const phase of progress.phases) {
1400
+ const phaseIcon = statusIcon(phase.status);
1401
+ lines.push(
1402
+ `${phaseIcon} Fase: ${phase.title} [${phase.status}] \u2014 ${pct(phase.progress)}`
1403
+ );
1404
+ for (const step of phase.steps) {
1405
+ const stepIcon = statusIcon(step.status);
1406
+ const subInfo = step.subStepsTotal > 0 ? ` (${step.subStepsDone}/${step.subStepsTotal} sotto-step)` : "";
1407
+ lines.push(
1408
+ ` ${stepIcon} ${step.stepId}: ${step.title} [${step.status}]${subInfo}`
1409
+ );
1410
+ if (verbose) {
1411
+ const planPhase = plan.phases.find(
1412
+ (p) => p.steps.some((s) => s.id === step.stepId)
1413
+ );
1414
+ const planStep = planPhase?.steps.find((s) => s.id === step.stepId);
1415
+ if (planStep?.subSteps.length) {
1416
+ for (const ss of planStep.subSteps) {
1417
+ const ssIcon = statusIcon(ss.status);
1418
+ lines.push(` ${ssIcon} ${ss.id}: ${ss.title} [${ss.status}]`);
1419
+ }
1420
+ }
1421
+ if (planStep?.dependencies.length) {
1422
+ lines.push(` \u21B3 dipende da: ${planStep.dependencies.join(", ")}`);
1423
+ }
1424
+ if (planStep?.contract.outputs.length) {
1425
+ const outs = planStep.contract.outputs.map((o) => o.name).join(", ");
1426
+ lines.push(` \u21B3 produce: ${outs}`);
1427
+ }
1428
+ }
1429
+ }
1430
+ lines.push("");
1431
+ }
1432
+ return lines.join("\n");
1433
+ }
1434
+ function statusIcon(status) {
1435
+ const icons = {
1436
+ idle: "\u26AA",
1437
+ pending: "\u{1F535}",
1438
+ running: "\u{1F504}",
1439
+ completed: "\u2705",
1440
+ failed: "\u274C",
1441
+ skipped: "\u23ED\uFE0F",
1442
+ blocked: "\u{1F6AB}",
1443
+ cancelled: "\u{1F534}"
1444
+ };
1445
+ return icons[status] ?? "\u2753";
1446
+ }
1447
+
1448
+ // src/tools/planning/plan-visualize.tool.ts
1449
+ import { z as z13 } from "zod";
1450
+ function createPlanVisualizeTool(fs) {
1451
+ return tool({
1452
+ description: "Visualizza il piano come diagramma ASCII o Mermaid. Mostra la gerarchia fasi/step e le dipendenze.",
1453
+ inputSchema: z13.object({
1454
+ format: z13.enum(["ascii", "mermaid"]).default("ascii").describe("Formato di output")
1455
+ }),
1456
+ execute: async ({ format }) => {
1457
+ const plan = await loadPlan(fs);
1458
+ if (!plan) return "Nessun piano trovato. Usa plan_create prima.";
1459
+ return format === "mermaid" ? renderMermaid(plan) : renderAscii(plan);
1460
+ }
1461
+ });
1462
+ }
1463
+ function renderMermaid(plan) {
1464
+ const lines = [];
1465
+ lines.push("```mermaid");
1466
+ lines.push("graph TD");
1467
+ lines.push(` subgraph ${sanitize(plan.title)}`);
1468
+ for (const phase of plan.phases) {
1469
+ const phaseLabel = `phase_${phase.id}`;
1470
+ lines.push(` subgraph ${phaseLabel}["${phase.title} (${phase.executionMode})"]`);
1471
+ for (const step of phase.steps) {
1472
+ const shape = stepShape(step);
1473
+ lines.push(` ${step.id}${shape}`);
1474
+ }
1475
+ if (phase.executionMode === "sequential") {
1476
+ for (let i = 1; i < phase.steps.length; i++) {
1477
+ const prev = phase.steps[i - 1];
1478
+ const curr = phase.steps[i];
1479
+ if (!curr.dependencies.includes(prev.id)) {
1480
+ lines.push(` ${prev.id} --> ${curr.id}`);
1481
+ }
1482
+ }
1483
+ }
1484
+ lines.push(" end");
1485
+ }
1486
+ for (const phase of plan.phases) {
1487
+ for (const step of phase.steps) {
1488
+ for (const dep of step.dependencies) {
1489
+ lines.push(` ${dep} --> ${step.id}`);
1490
+ }
1491
+ }
1492
+ }
1493
+ for (const phase of plan.phases) {
1494
+ for (const depPhaseId of phase.dependencies) {
1495
+ const depPhase = plan.phases.find((p) => p.id === depPhaseId);
1496
+ if (depPhase) {
1497
+ const lastStep = depPhase.steps[depPhase.steps.length - 1];
1498
+ const firstStep = phase.steps[0];
1499
+ if (lastStep && firstStep) {
1500
+ lines.push(` ${lastStep.id} ==> ${firstStep.id}`);
1501
+ }
1502
+ }
1503
+ }
1504
+ }
1505
+ lines.push(" end");
1506
+ lines.push("```");
1507
+ lines.push("");
1508
+ lines.push("Legenda:");
1509
+ lines.push(" [step] = sequenziale");
1510
+ lines.push(" {step} = condizionale");
1511
+ lines.push(" ((step)) = loop");
1512
+ lines.push(" ==> = dipendenza tra fasi");
1513
+ return lines.join("\n");
1514
+ }
1515
+ function stepShape(step) {
1516
+ const label = `${step.title}`;
1517
+ switch (step.executionMode) {
1518
+ case "conditional":
1519
+ return `{${label}}`;
1520
+ case "loop":
1521
+ return `((${label}))`;
1522
+ case "parallel":
1523
+ return `[/${label}/]`;
1524
+ default:
1525
+ return `["${label}"]`;
1526
+ }
1527
+ }
1528
+ function sanitize(s) {
1529
+ return s.replace(/[^a-zA-Z0-9_\- àèéìòù]/g, "_");
1530
+ }
1531
+ function renderAscii(plan) {
1532
+ const lines = [];
1533
+ const W = 72;
1534
+ lines.push("\u2554" + "\u2550".repeat(W) + "\u2557");
1535
+ lines.push(
1536
+ "\u2551" + centerPad(`\u{1F4CB} ${plan.title}`, W) + "\u2551"
1537
+ );
1538
+ lines.push(
1539
+ "\u2551" + centerPad(`Stato: ${plan.status} | v${plan.metadata.version}`, W) + "\u2551"
1540
+ );
1541
+ lines.push("\u2560" + "\u2550".repeat(W) + "\u2563");
1542
+ for (let pi = 0; pi < plan.phases.length; pi++) {
1543
+ const phase = plan.phases[pi];
1544
+ const modeLabel = phase.executionMode === "parallel" ? "\u26A1 PARALLEL" : "\u2192 SEQ";
1545
+ lines.push(
1546
+ "\u2551" + leftPad(
1547
+ ` Fase ${pi + 1}: ${phase.title} [${modeLabel}]`,
1548
+ W
1549
+ ) + "\u2551"
1550
+ );
1551
+ lines.push("\u2551" + " ".repeat(2) + "\u2500".repeat(W - 2) + "\u2551");
1552
+ for (const step of phase.steps) {
1553
+ const icon = statusIconAscii(step.status);
1554
+ const depStr = step.dependencies.length > 0 ? ` \u2190 [${step.dependencies.join(", ")}]` : "";
1555
+ const modeBadge = step.executionMode !== "sequential" ? ` (${step.executionMode})` : "";
1556
+ lines.push(
1557
+ "\u2551" + leftPad(
1558
+ ` ${icon} ${step.id}: ${step.title}${modeBadge}${depStr}`,
1559
+ W
1560
+ ) + "\u2551"
1561
+ );
1562
+ for (const ss of step.subSteps) {
1563
+ const ssIcon = statusIconAscii(ss.status);
1564
+ lines.push(
1565
+ "\u2551" + leftPad(` ${ssIcon} \u2514\u2500 ${ss.id}: ${ss.title}`, W) + "\u2551"
1566
+ );
1567
+ }
1568
+ if (step.contract.outputs.length > 0) {
1569
+ const outs = step.contract.outputs.map((o) => o.name).join(", ");
1570
+ lines.push(
1571
+ "\u2551" + leftPad(` \u21B3 output: ${outs}`, W) + "\u2551"
1572
+ );
1573
+ }
1574
+ }
1575
+ if (pi < plan.phases.length - 1) {
1576
+ lines.push("\u2551" + " ".repeat(W) + "\u2551");
1577
+ const nextPhase = plan.phases[pi + 1];
1578
+ if (nextPhase.dependencies.includes(phase.id)) {
1579
+ lines.push("\u2551" + centerPad("\u2502", W) + "\u2551");
1580
+ lines.push("\u2551" + centerPad("\u25BC", W) + "\u2551");
1581
+ } else {
1582
+ lines.push("\u2551" + centerPad("\u250A", W) + "\u2551");
1583
+ }
1584
+ lines.push("\u2551" + " ".repeat(W) + "\u2551");
1585
+ }
1586
+ }
1587
+ lines.push("\u255A" + "\u2550".repeat(W) + "\u255D");
1588
+ return lines.join("\n");
1589
+ }
1590
+ function statusIconAscii(status) {
1591
+ const icons = {
1592
+ idle: "\u25CB",
1593
+ pending: "\u25C9",
1594
+ running: "\u25BA",
1595
+ completed: "\u2713",
1596
+ failed: "\u2717",
1597
+ skipped: "\u2298",
1598
+ blocked: "\u2297",
1599
+ cancelled: "\u2296"
1600
+ };
1601
+ return icons[status] ?? "?";
1602
+ }
1603
+ function centerPad(text, width) {
1604
+ const visLen = [...text].length;
1605
+ if (visLen >= width) return text.slice(0, width);
1606
+ const left = Math.floor((width - visLen) / 2);
1607
+ const right = width - visLen - left;
1608
+ return " ".repeat(left) + text + " ".repeat(right);
1609
+ }
1610
+ function leftPad(text, width) {
1611
+ const visLen = [...text].length;
1612
+ if (visLen >= width) return text.slice(0, width);
1613
+ return text + " ".repeat(width - visLen);
1614
+ }
1615
+
1616
+ // src/utils/abstract-builder.ts
1617
+ var AbstractBuilder = class {
1618
+ build() {
1619
+ this.validate();
1620
+ return this.construct();
1621
+ }
1622
+ };
1623
+
1624
+ // src/adapters/filesystem/glob-utils.ts
1625
+ var regexCache = /* @__PURE__ */ new Map();
1626
+ var MAX_CACHE_SIZE = 500;
1627
+ function globToRegex(pattern) {
1628
+ const cached = regexCache.get(pattern);
1629
+ if (cached) return cached;
1630
+ let result = "";
1631
+ let i = 0;
1632
+ while (i < pattern.length) {
1633
+ const char = pattern[i];
1634
+ if (char === "*" && pattern[i + 1] === "*") {
1635
+ result += ".*";
1636
+ i += pattern[i + 2] === "/" ? 3 : 2;
1637
+ } else if (char === "*") {
1638
+ result += "[^/]*";
1639
+ i++;
1640
+ } else if (char === "?") {
1641
+ result += "[^/]";
1642
+ i++;
1643
+ } else if (".+^${}()|[]\\".includes(char)) {
1644
+ result += "\\" + char;
1645
+ i++;
1646
+ } else {
1647
+ result += char;
1648
+ i++;
1649
+ }
1650
+ }
1651
+ const regex = new RegExp("^" + result + "$");
1652
+ if (regexCache.size >= MAX_CACHE_SIZE) {
1653
+ const oldest = regexCache.keys().next().value;
1654
+ regexCache.delete(oldest);
1655
+ }
1656
+ regexCache.set(pattern, regex);
1657
+ return regex;
1658
+ }
1659
+
1660
+ // src/adapters/filesystem/virtual-fs.adapter.ts
1661
+ var VirtualFilesystem = class {
1662
+ zones = {
1663
+ transient: /* @__PURE__ */ new Map(),
1664
+ persistent: /* @__PURE__ */ new Map()
1665
+ };
1666
+ basePath;
1667
+ onSync;
1668
+ constructor(options = {}) {
1669
+ this.basePath = options.basePath ?? "";
1670
+ this.onSync = options.onSync;
1671
+ }
1672
+ async read(path, zone = "transient") {
1673
+ const entry = this.zones[zone].get(normalizePath(path));
1674
+ if (!entry || entry.isDirectory) {
1675
+ throw new Error(`File not found: ${path}`);
1676
+ }
1677
+ return entry.content;
1678
+ }
1679
+ async write(path, content, zone = "transient") {
1680
+ const normalized = normalizePath(path);
1681
+ this.ensureParentDirs(normalized, zone);
1682
+ const now = Date.now();
1683
+ const existing = this.zones[zone].get(normalized);
1684
+ this.zones[zone].set(normalized, {
1685
+ content,
1686
+ isDirectory: false,
1687
+ createdAt: existing?.createdAt ?? now,
1688
+ modifiedAt: now
1689
+ });
1690
+ }
1691
+ async exists(path, zone = "transient") {
1692
+ return this.zones[zone].has(normalizePath(path));
1693
+ }
1694
+ async delete(path, zone = "transient") {
1695
+ const normalized = normalizePath(path);
1696
+ const store = this.zones[zone];
1697
+ const prefix = normalized.endsWith("/") ? normalized : normalized + "/";
1698
+ for (const key of store.keys()) {
1699
+ if (key === normalized || key.startsWith(prefix)) {
1700
+ store.delete(key);
1701
+ }
1702
+ }
1703
+ }
1704
+ async list(path, options = {}, zone = "transient") {
1705
+ const normalized = normalizePath(path);
1706
+ const prefix = normalized === "" ? "" : normalized + "/";
1707
+ const maxDepth = options.maxDepth ?? (options.recursive ? Infinity : 1);
1708
+ const results = [];
1709
+ for (const [key, entry] of this.zones[zone]) {
1710
+ if (!key.startsWith(prefix) || key === normalized) continue;
1711
+ const relative = key.slice(prefix.length);
1712
+ if (!options.includeHidden && relative.split("/").some((s) => s.startsWith("."))) continue;
1713
+ const depth = relative.split("/").length;
1714
+ if (depth > maxDepth) continue;
1715
+ results.push({
1716
+ name: relative.split("/").pop(),
1717
+ path: key,
1718
+ isDirectory: entry.isDirectory,
1719
+ size: entry.content.length,
1720
+ modifiedAt: entry.modifiedAt
1721
+ });
1722
+ }
1723
+ return results;
1724
+ }
1725
+ async stat(path, zone = "transient") {
1726
+ const entry = this.zones[zone].get(normalizePath(path));
1727
+ if (!entry) throw new Error(`Not found: ${path}`);
1728
+ return {
1729
+ size: entry.content.length,
1730
+ isDirectory: entry.isDirectory,
1731
+ isFile: !entry.isDirectory,
1732
+ createdAt: entry.createdAt,
1733
+ modifiedAt: entry.modifiedAt
1734
+ };
1735
+ }
1736
+ async glob(pattern, zone = "transient") {
1737
+ const regex = globToRegex(pattern);
1738
+ const results = [];
1739
+ for (const [key, entry] of this.zones[zone]) {
1740
+ if (!entry.isDirectory && regex.test(key)) {
1741
+ results.push(key);
1742
+ }
1743
+ }
1744
+ return results;
1745
+ }
1746
+ async search(pattern, options = {}, zone = "transient") {
1747
+ const flags = options.caseSensitive === false ? "gi" : "g";
1748
+ const regex = new RegExp(pattern, flags);
1749
+ const fileRegex = options.filePattern ? globToRegex(options.filePattern) : null;
1750
+ const results = [];
1751
+ const max = options.maxResults ?? Infinity;
1752
+ for (const [filePath, entry] of this.zones[zone]) {
1753
+ if (entry.isDirectory) continue;
1754
+ if (fileRegex && !fileRegex.test(filePath)) continue;
1755
+ const lines = entry.content.split("\n");
1756
+ for (let i = 0; i < lines.length && results.length < max; i++) {
1757
+ regex.lastIndex = 0;
1758
+ const match = regex.exec(lines[i]);
1759
+ if (match) {
1760
+ results.push({
1761
+ filePath,
1762
+ lineNumber: i + 1,
1763
+ lineContent: lines[i],
1764
+ matchStart: match.index,
1765
+ matchEnd: match.index + match[0].length
1766
+ });
1767
+ }
1768
+ }
1769
+ if (results.length >= max) break;
1770
+ }
1771
+ return results;
1772
+ }
1773
+ async syncToPersistent() {
1774
+ if (!this.onSync) {
1775
+ throw new Error("syncToPersistent requires an onSync callback");
1776
+ }
1777
+ for (const [filePath, entry] of this.zones.persistent) {
1778
+ if (entry.isDirectory) continue;
1779
+ const fullPath = this.basePath ? this.basePath + "/" + filePath : filePath;
1780
+ await this.onSync(fullPath, entry.content);
1781
+ }
1782
+ }
1783
+ async clearTransient() {
1784
+ this.zones.transient.clear();
1785
+ }
1786
+ ensureParentDirs(filePath, zone) {
1787
+ const parts = filePath.split("/");
1788
+ const now = Date.now();
1789
+ for (let i = 1; i < parts.length; i++) {
1790
+ const dirPath = parts.slice(0, i).join("/");
1791
+ if (dirPath && !this.zones[zone].has(dirPath)) {
1792
+ this.zones[zone].set(dirPath, {
1793
+ content: "",
1794
+ isDirectory: true,
1795
+ createdAt: now,
1796
+ modifiedAt: now
1797
+ });
1798
+ }
1799
+ }
1800
+ }
1801
+ };
1802
+ function normalizePath(p) {
1803
+ const segments = p.replace(/\/+/g, "/").split("/");
1804
+ const resolved = [];
1805
+ for (const seg of segments) {
1806
+ if (seg === "." || seg === "") continue;
1807
+ if (seg === "..") {
1808
+ resolved.pop();
1809
+ } else {
1810
+ resolved.push(seg);
1811
+ }
1812
+ }
1813
+ return resolved.join("/");
1814
+ }
1815
+
1816
+ // src/tools/planning/index.ts
1817
+ function createPlanningTools(fs) {
1818
+ return {
1819
+ // Legacy (retrocompatibilità)
1820
+ write_todos: createWriteTodosTool(fs),
1821
+ review_todos: createReviewTodosTool(fs),
1822
+ // Structured planning
1823
+ plan_create: createPlanCreateTool(fs),
1824
+ plan_update: createPlanUpdateTool(fs),
1825
+ plan_status: createPlanStatusTool(fs),
1826
+ plan_visualize: createPlanVisualizeTool(fs)
1827
+ };
1828
+ }
1829
+
1830
+ // src/tools/policy/index.ts
1831
+ import { z as z14 } from "zod";
1832
+ var policyRuleSchema = z14.object({
1833
+ id: z14.string().min(1),
1834
+ effect: z14.enum(["allow", "deny"]),
1835
+ resourcePattern: z14.string().min(1),
1836
+ serverPattern: z14.string().optional(),
1837
+ toolPattern: z14.string().optional(),
1838
+ priority: z14.number().int().optional(),
1839
+ reason: z14.string().optional(),
1840
+ context: z14.object({
1841
+ sessionId: z14.string().optional(),
1842
+ userId: z14.string().optional(),
1843
+ tenantId: z14.string().optional()
1844
+ }).optional()
1845
+ });
1846
+ function normalizeRule(input) {
1847
+ const context = input.context ? {
1848
+ ...input.context.sessionId ? { sessionId: input.context.sessionId } : {},
1849
+ ...input.context.userId ? { userId: input.context.userId } : {},
1850
+ ...input.context.tenantId ? { tenantId: input.context.tenantId } : {}
1851
+ } : void 0;
1852
+ return {
1853
+ id: input.id,
1854
+ effect: input.effect,
1855
+ resourcePattern: input.resourcePattern,
1856
+ ...input.serverPattern ? { serverPattern: input.serverPattern } : {},
1857
+ ...input.toolPattern ? { toolPattern: input.toolPattern } : {},
1858
+ ...input.priority !== void 0 ? { priority: input.priority } : {},
1859
+ ...input.reason ? { reason: input.reason } : {},
1860
+ ...context ? { context } : {}
1861
+ };
1862
+ }
1863
+ function createPolicyTools(policyEngine) {
1864
+ return {
1865
+ policy_list_rules: tool({
1866
+ description: "List current policy rules sorted by priority",
1867
+ inputSchema: z14.object({}),
1868
+ execute: async () => {
1869
+ const rules = await policyEngine.listRules();
1870
+ return { rules, count: rules.length };
1871
+ }
1872
+ }),
1873
+ policy_add_rule: tool({
1874
+ description: "Add a new policy rule (allow or deny)",
1875
+ inputSchema: policyRuleSchema,
1876
+ execute: async (input) => {
1877
+ const rule = normalizeRule(input);
1878
+ await policyEngine.addRule(rule);
1879
+ return { ok: true, ruleId: rule.id };
1880
+ }
1881
+ }),
1882
+ policy_remove_rule: tool({
1883
+ description: "Remove an existing policy rule by id",
1884
+ inputSchema: z14.object({ id: z14.string().min(1) }),
1885
+ execute: async ({ id }) => {
1886
+ const removed = await policyEngine.removeRule(id);
1887
+ return { ok: true, removed, ruleId: id };
1888
+ }
1889
+ }),
1890
+ policy_list_audit: tool({
1891
+ description: "List policy audit records (most recent first)",
1892
+ inputSchema: z14.object({ limit: z14.number().int().positive().optional() }),
1893
+ execute: async ({ limit }) => {
1894
+ const audit = await policyEngine.getAuditLog(limit);
1895
+ return { audit, count: audit.length };
1896
+ }
1897
+ }),
1898
+ policy_clear_audit: tool({
1899
+ description: "Clear policy audit records",
1900
+ inputSchema: z14.object({}),
1901
+ execute: async () => {
1902
+ await policyEngine.clearAuditLog();
1903
+ return { ok: true };
1904
+ }
1905
+ })
1906
+ };
1907
+ }
1908
+
1909
+ // src/tools/subagent/dispatch.tool.ts
1910
+ import { z as z15 } from "zod";
1911
+
1912
+ // src/tools/subagent/subagent-registry.ts
1913
+ var TERMINAL_STATES = /* @__PURE__ */ new Set([
1914
+ "completed",
1915
+ "failed",
1916
+ "timeout",
1917
+ "cancelled"
1918
+ ]);
1919
+ var VALID_TRANSITIONS = /* @__PURE__ */ new Map([
1920
+ ["queued", /* @__PURE__ */ new Set(["running", "cancelled"])],
1921
+ ["running", /* @__PURE__ */ new Set(["streaming", "completed", "failed", "timeout", "cancelled"])],
1922
+ ["streaming", /* @__PURE__ */ new Set(["completed", "failed", "timeout", "cancelled"])],
1923
+ ["completed", /* @__PURE__ */ new Set()],
1924
+ ["failed", /* @__PURE__ */ new Set()],
1925
+ ["timeout", /* @__PURE__ */ new Set()],
1926
+ ["cancelled", /* @__PURE__ */ new Set()]
1927
+ ]);
1928
+ var DEFAULT_LIMITS = {
1929
+ maxConcurrentPerParent: 5,
1930
+ maxConcurrentGlobal: 50,
1931
+ maxDepth: 3,
1932
+ defaultTimeoutMs: 3e5,
1933
+ maxTimeoutMs: 6e5,
1934
+ maxQueueSize: 100,
1935
+ gcTtlMs: 6e4,
1936
+ gcIntervalMs: 3e4,
1937
+ maxStepsPerSubagent: 20
1938
+ };
1939
+ var SubagentQueueFullError = class extends Error {
1940
+ constructor(queueSize, maxSize) {
1941
+ super(
1942
+ `Subagent queue is full (${queueSize}/${maxSize}). Try again after some tasks complete.`
1943
+ );
1944
+ this.name = "SubagentQueueFullError";
1945
+ }
1946
+ };
1947
+ var SubagentDepthExceededError = class extends Error {
1948
+ constructor(currentDepth, maxDepth) {
1949
+ super(
1950
+ `Maximum subagent nesting depth exceeded (${currentDepth}/${maxDepth}).`
1951
+ );
1952
+ this.name = "SubagentDepthExceededError";
1953
+ }
1954
+ };
1955
+ var SubagentQuotaExceededError = class extends Error {
1956
+ constructor(parentId, reason) {
1957
+ super(`Quota exceeded for parent "${parentId}": ${reason}`);
1958
+ this.name = "SubagentQuotaExceededError";
1959
+ }
1960
+ };
1961
+ var SubagentRegistry = class {
1962
+ handles = /* @__PURE__ */ new Map();
1963
+ parentIndex = /* @__PURE__ */ new Map();
1964
+ limits;
1965
+ eventBus;
1966
+ telemetry;
1967
+ hooks;
1968
+ generateId;
1969
+ gcTimer = null;
1970
+ /** Listeners waiting for a specific task to reach terminal state */
1971
+ completionListeners = /* @__PURE__ */ new Map();
1972
+ /** Scheduler reference — set via setScheduler() after construction */
1973
+ scheduler = null;
1974
+ constructor(eventBus, options) {
1975
+ this.eventBus = eventBus;
1976
+ this.limits = { ...DEFAULT_LIMITS, ...options?.limits };
1977
+ this.telemetry = options?.telemetry;
1978
+ this.hooks = options?.hooks;
1979
+ this.generateId = options?.generateId ?? (() => crypto.randomUUID());
1980
+ }
1981
+ /** Wire the scheduler (called during setup) */
1982
+ setScheduler(scheduler) {
1983
+ this.scheduler = scheduler;
1984
+ }
1985
+ get resourceLimits() {
1986
+ return this.limits;
1987
+ }
1988
+ // -------------------------------------------------------------------------
1989
+ // Lifecycle
1990
+ // -------------------------------------------------------------------------
1991
+ start() {
1992
+ this.gcTimer = setInterval(() => this.gc(), this.limits.gcIntervalMs);
1993
+ }
1994
+ async shutdown() {
1995
+ if (this.gcTimer) {
1996
+ clearInterval(this.gcTimer);
1997
+ this.gcTimer = null;
1998
+ }
1999
+ for (const handle of this.handles.values()) {
2000
+ if (!TERMINAL_STATES.has(handle.status)) {
2001
+ this.cancel(handle.taskId, "registry-shutdown");
2002
+ }
2003
+ }
2004
+ this.handles.clear();
2005
+ this.parentIndex.clear();
2006
+ this.completionListeners.clear();
2007
+ }
2008
+ // -------------------------------------------------------------------------
2009
+ // Dispatch
2010
+ // -------------------------------------------------------------------------
2011
+ dispatch(parentId, currentDepth, params) {
2012
+ if (currentDepth >= this.limits.maxDepth) {
2013
+ throw new SubagentDepthExceededError(currentDepth, this.limits.maxDepth);
2014
+ }
2015
+ const parentHandles = this.parentIndex.get(parentId);
2016
+ const parentActive = parentHandles ? [...parentHandles].filter((id) => {
2017
+ const h = this.handles.get(id);
2018
+ return h && !TERMINAL_STATES.has(h.status);
2019
+ }).length : 0;
2020
+ if (parentActive >= this.limits.maxConcurrentPerParent) {
2021
+ throw new SubagentQuotaExceededError(
2022
+ parentId,
2023
+ `max concurrent per parent (${this.limits.maxConcurrentPerParent}) reached`
2024
+ );
2025
+ }
2026
+ const queuedCount = [...this.handles.values()].filter(
2027
+ (h) => h.status === "queued"
2028
+ ).length;
2029
+ if (queuedCount >= this.limits.maxQueueSize) {
2030
+ throw new SubagentQueueFullError(queuedCount, this.limits.maxQueueSize);
2031
+ }
2032
+ const timeoutMs = Math.min(
2033
+ params.timeoutMs ?? this.limits.defaultTimeoutMs,
2034
+ this.limits.maxTimeoutMs
2035
+ );
2036
+ const handle = {
2037
+ taskId: this.generateId(),
2038
+ parentId,
2039
+ depth: currentDepth,
2040
+ createdAt: Date.now(),
2041
+ status: "queued",
2042
+ statusChangedAt: Date.now(),
2043
+ priority: params.priority ?? 5,
2044
+ partialOutput: "",
2045
+ finalOutput: null,
2046
+ error: null,
2047
+ abortController: new AbortController(),
2048
+ prompt: params.prompt,
2049
+ instructions: params.instructions ?? null,
2050
+ timeoutMs,
2051
+ timeoutTimer: null,
2052
+ tokenUsage: { input: 0, output: 0 },
2053
+ metadata: params.metadata ?? {}
2054
+ };
2055
+ this.handles.set(handle.taskId, handle);
2056
+ if (!this.parentIndex.has(parentId)) {
2057
+ this.parentIndex.set(parentId, /* @__PURE__ */ new Set());
2058
+ }
2059
+ this.parentIndex.get(parentId).add(handle.taskId);
2060
+ this.eventBus.emit("subagent:spawn", {
2061
+ taskId: handle.taskId,
2062
+ parentId,
2063
+ depth: currentDepth,
2064
+ prompt: params.prompt,
2065
+ priority: handle.priority
2066
+ });
2067
+ this.eventBus.emit("delegation:start", {
2068
+ taskId: handle.taskId,
2069
+ parentId,
2070
+ depth: currentDepth,
2071
+ prompt: params.prompt,
2072
+ priority: handle.priority,
2073
+ timeoutMs: handle.timeoutMs,
2074
+ metadata: handle.metadata
2075
+ });
2076
+ this.telemetry?.recordMetric("subagent.dispatch.count", 1);
2077
+ this.scheduler?.enqueue(handle);
2078
+ return handle;
2079
+ }
2080
+ // -------------------------------------------------------------------------
2081
+ // Status Transitions
2082
+ // -------------------------------------------------------------------------
2083
+ transition(taskId, newStatus, data) {
2084
+ const handle = this.handles.get(taskId);
2085
+ if (!handle) return;
2086
+ if (TERMINAL_STATES.has(handle.status)) return;
2087
+ const allowedTargets = VALID_TRANSITIONS.get(handle.status);
2088
+ if (!allowedTargets || !allowedTargets.has(newStatus)) return;
2089
+ const previousStatus = handle.status;
2090
+ handle.status = newStatus;
2091
+ handle.statusChangedAt = Date.now();
2092
+ if (data?.partialOutput !== void 0) {
2093
+ handle.partialOutput += data.partialOutput;
2094
+ }
2095
+ if (data?.finalOutput !== void 0) {
2096
+ handle.finalOutput = data.finalOutput;
2097
+ }
2098
+ if (data?.error !== void 0) {
2099
+ handle.error = data.error;
2100
+ }
2101
+ this.eventBus.emit("subagent:status-change", {
2102
+ taskId,
2103
+ previousStatus,
2104
+ newStatus,
2105
+ parentId: handle.parentId
2106
+ });
2107
+ this.eventBus.emit("delegation:iteration", {
2108
+ taskId,
2109
+ parentId: handle.parentId,
2110
+ previousStatus,
2111
+ status: newStatus,
2112
+ durationMs: Date.now() - handle.createdAt,
2113
+ tokenUsage: handle.tokenUsage
2114
+ });
2115
+ this.invokeIterationHook(handle, previousStatus, newStatus);
2116
+ if (TERMINAL_STATES.has(newStatus)) {
2117
+ if (handle.timeoutTimer) {
2118
+ clearTimeout(handle.timeoutTimer);
2119
+ handle.timeoutTimer = null;
2120
+ }
2121
+ this.eventBus.emit("subagent:complete", {
2122
+ taskId,
2123
+ status: newStatus,
2124
+ parentId: handle.parentId,
2125
+ durationMs: Date.now() - handle.createdAt,
2126
+ tokenUsage: handle.tokenUsage
2127
+ });
2128
+ this.eventBus.emit("delegation:complete", {
2129
+ taskId,
2130
+ status: newStatus,
2131
+ parentId: handle.parentId,
2132
+ durationMs: Date.now() - handle.createdAt,
2133
+ tokenUsage: handle.tokenUsage,
2134
+ error: handle.error
2135
+ });
2136
+ this.telemetry?.recordMetric(`subagent.status.${newStatus}`, 1);
2137
+ this.telemetry?.recordMetric(
2138
+ "subagent.completion.duration_ms",
2139
+ Date.now() - handle.createdAt
2140
+ );
2141
+ this.invokeCompletionHook(handle);
2142
+ const listeners = this.completionListeners.get(taskId);
2143
+ if (listeners) {
2144
+ for (const cb of listeners) cb(handle);
2145
+ this.completionListeners.delete(taskId);
2146
+ }
2147
+ }
2148
+ }
2149
+ // -------------------------------------------------------------------------
2150
+ // Query
2151
+ // -------------------------------------------------------------------------
2152
+ invokeIterationHook(handle, previousStatus, status) {
2153
+ if (!this.hooks?.onIterationComplete) return;
2154
+ void Promise.resolve(
2155
+ this.hooks.onIterationComplete({
2156
+ taskId: handle.taskId,
2157
+ parentId: handle.parentId,
2158
+ previousStatus,
2159
+ status,
2160
+ partialOutput: handle.partialOutput,
2161
+ finalOutput: handle.finalOutput,
2162
+ error: handle.error,
2163
+ durationMs: Date.now() - handle.createdAt,
2164
+ tokenUsage: handle.tokenUsage,
2165
+ metadata: handle.metadata
2166
+ })
2167
+ ).then((result) => {
2168
+ if (!result) return;
2169
+ if (result.shouldEscalate) {
2170
+ this.eventBus.emit("delegation:blocked", {
2171
+ taskId: handle.taskId,
2172
+ parentId: handle.parentId,
2173
+ reason: result.reason ?? "delegation escalation requested",
2174
+ score: result.score
2175
+ });
2176
+ }
2177
+ }).catch(() => {
2178
+ });
2179
+ }
2180
+ invokeCompletionHook(handle) {
2181
+ if (!this.hooks?.onDelegationComplete) return;
2182
+ void Promise.resolve(
2183
+ this.hooks.onDelegationComplete({
2184
+ taskId: handle.taskId,
2185
+ parentId: handle.parentId,
2186
+ status: handle.status,
2187
+ finalOutput: handle.finalOutput,
2188
+ error: handle.error,
2189
+ durationMs: Date.now() - handle.createdAt,
2190
+ tokenUsage: handle.tokenUsage,
2191
+ metadata: handle.metadata
2192
+ })
2193
+ ).catch(() => {
2194
+ });
2195
+ }
2196
+ get(taskId) {
2197
+ return this.handles.get(taskId);
2198
+ }
2199
+ getByParent(parentId) {
2200
+ const ids = this.parentIndex.get(parentId);
2201
+ if (!ids) return [];
2202
+ return [...ids].map((id) => this.handles.get(id)).filter(Boolean);
2203
+ }
2204
+ get activeCount() {
2205
+ return [...this.handles.values()].filter(
2206
+ (h) => h.status === "running" || h.status === "streaming"
2207
+ ).length;
2208
+ }
2209
+ get queuedCount() {
2210
+ return [...this.handles.values()].filter(
2211
+ (h) => h.status === "queued"
2212
+ ).length;
2213
+ }
2214
+ get totalCount() {
2215
+ return this.handles.size;
2216
+ }
2217
+ // -------------------------------------------------------------------------
2218
+ // Await
2219
+ // -------------------------------------------------------------------------
2220
+ waitForCompletion(taskId, timeoutMs) {
2221
+ const handle = this.handles.get(taskId);
2222
+ if (!handle) {
2223
+ return Promise.reject(new Error(`Task "${taskId}" not found`));
2224
+ }
2225
+ if (TERMINAL_STATES.has(handle.status)) {
2226
+ return Promise.resolve(handle);
2227
+ }
2228
+ return new Promise((resolve) => {
2229
+ const timer = setTimeout(() => {
2230
+ const listeners = this.completionListeners.get(taskId);
2231
+ if (listeners) {
2232
+ const idx = listeners.indexOf(onComplete);
2233
+ if (idx !== -1) listeners.splice(idx, 1);
2234
+ }
2235
+ resolve(handle);
2236
+ }, timeoutMs);
2237
+ const onComplete = (h) => {
2238
+ clearTimeout(timer);
2239
+ resolve(h);
2240
+ };
2241
+ if (!this.completionListeners.has(taskId)) {
2242
+ this.completionListeners.set(taskId, []);
2243
+ }
2244
+ this.completionListeners.get(taskId).push(onComplete);
2245
+ });
2246
+ }
2247
+ // -------------------------------------------------------------------------
2248
+ // Cancellation
2249
+ // -------------------------------------------------------------------------
2250
+ cancel(taskId, reason = "cancelled") {
2251
+ const handle = this.handles.get(taskId);
2252
+ if (!handle || TERMINAL_STATES.has(handle.status)) return false;
2253
+ handle.abortController.abort(reason);
2254
+ this.transition(taskId, "cancelled", { error: reason });
2255
+ for (const h of this.handles.values()) {
2256
+ if (h.parentId === taskId && !TERMINAL_STATES.has(h.status)) {
2257
+ this.cancel(h.taskId, `parent-cancelled:${reason}`);
2258
+ }
2259
+ }
2260
+ return true;
2261
+ }
2262
+ cancelAll(parentId) {
2263
+ let count = 0;
2264
+ const ids = this.parentIndex.get(parentId);
2265
+ if (!ids) return 0;
2266
+ for (const id of ids) {
2267
+ if (this.cancel(id, "parent-shutdown")) count++;
2268
+ }
2269
+ return count;
2270
+ }
2271
+ // -------------------------------------------------------------------------
2272
+ // Garbage Collection
2273
+ // -------------------------------------------------------------------------
2274
+ /** Exposed for testing */
2275
+ gc() {
2276
+ const now = Date.now();
2277
+ let collected = 0;
2278
+ for (const [taskId, handle] of this.handles) {
2279
+ if (TERMINAL_STATES.has(handle.status) && now - handle.statusChangedAt > this.limits.gcTtlMs) {
2280
+ this.handles.delete(taskId);
2281
+ this.parentIndex.get(handle.parentId)?.delete(taskId);
2282
+ this.completionListeners.delete(taskId);
2283
+ collected++;
2284
+ }
2285
+ if ((handle.status === "running" || handle.status === "streaming") && now - handle.createdAt > handle.timeoutMs * 2) {
2286
+ this.cancel(taskId, "watchdog-timeout");
2287
+ }
2288
+ }
2289
+ if (this.handles.size > this.limits.maxConcurrentGlobal * 10) {
2290
+ for (const [taskId, handle] of this.handles) {
2291
+ if (TERMINAL_STATES.has(handle.status)) {
2292
+ this.handles.delete(taskId);
2293
+ this.parentIndex.get(handle.parentId)?.delete(taskId);
2294
+ this.completionListeners.delete(taskId);
2295
+ collected++;
2296
+ }
2297
+ }
2298
+ }
2299
+ if (collected > 0) {
2300
+ this.telemetry?.recordMetric("subagent.gc.collected", collected);
2301
+ }
2302
+ this.telemetry?.recordMetric("subagent.queue.depth", this.queuedCount);
2303
+ this.telemetry?.recordMetric("subagent.active.count", this.activeCount);
2304
+ }
2305
+ };
2306
+ function isTerminalStatus(status) {
2307
+ return TERMINAL_STATES.has(status);
2308
+ }
2309
+
2310
+ // src/tools/subagent/dispatch.tool.ts
2311
+ var DispatchSubagentInputSchema = z15.object({
2312
+ prompt: z15.string().min(1).max(1e4).describe("Task description for the subagent"),
2313
+ instructions: z15.string().max(5e3).optional().describe("Optional system instructions for the subagent"),
2314
+ priority: z15.number().int().min(1).max(10).default(5).describe("Priority (1=highest, 10=lowest). Default: 5"),
2315
+ timeoutMs: z15.number().int().min(5e3).max(6e5).optional().describe("Timeout in milliseconds. Default: 300000 (5 min)"),
2316
+ metadata: z15.record(z15.string(), z15.unknown()).optional().describe("Optional metadata to pass to the subagent")
2317
+ });
2318
+ function createDispatchTool(config) {
2319
+ const { registry, parentId, currentDepth, hooks } = config;
2320
+ return tool({
2321
+ description: "Dispatch a subtask to a specialized subagent. Returns immediately with a taskId. Use poll_subagent to check progress, or await_subagent to wait for completion. You can dispatch multiple tasks in a single step for parallel execution.",
2322
+ inputSchema: DispatchSubagentInputSchema,
2323
+ execute: async (input) => {
2324
+ try {
2325
+ let prompt = input.prompt;
2326
+ let instructions = input.instructions;
2327
+ let priority = input.priority;
2328
+ let timeoutMs = input.timeoutMs;
2329
+ let metadata = input.metadata;
2330
+ if (hooks?.onDelegationStart) {
2331
+ const hookResult = await hooks.onDelegationStart({
2332
+ parentId,
2333
+ currentDepth,
2334
+ prompt,
2335
+ instructions,
2336
+ priority,
2337
+ timeoutMs,
2338
+ metadata
2339
+ });
2340
+ if (hookResult?.allow === false) {
2341
+ return JSON.stringify({
2342
+ blocked: true,
2343
+ error: hookResult.reason ?? "Delegation blocked by supervisor hook"
2344
+ });
2345
+ }
2346
+ if (hookResult?.prompt !== void 0) {
2347
+ prompt = hookResult.prompt;
2348
+ }
2349
+ if (hookResult?.instructions !== void 0) {
2350
+ instructions = hookResult.instructions;
2351
+ }
2352
+ if (hookResult?.priority !== void 0) {
2353
+ priority = hookResult.priority;
2354
+ }
2355
+ if (hookResult?.timeoutMs !== void 0) {
2356
+ timeoutMs = hookResult.timeoutMs;
2357
+ }
2358
+ if (hookResult?.metadata !== void 0) {
2359
+ metadata = hookResult.metadata;
2360
+ }
2361
+ }
2362
+ const handle = registry.dispatch(parentId, currentDepth, {
2363
+ prompt,
2364
+ instructions,
2365
+ priority,
2366
+ timeoutMs,
2367
+ metadata
2368
+ });
2369
+ return JSON.stringify({
2370
+ taskId: handle.taskId,
2371
+ status: "queued",
2372
+ queuePosition: registry.queuedCount,
2373
+ message: "Task dispatched. Use poll_subagent or await_subagent to get results."
2374
+ });
2375
+ } catch (error) {
2376
+ if (error instanceof SubagentQueueFullError || error instanceof SubagentDepthExceededError || error instanceof SubagentQuotaExceededError) {
2377
+ return JSON.stringify({ error: error.message });
2378
+ }
2379
+ const msg = error instanceof Error ? error.message : String(error);
2380
+ return JSON.stringify({ error: `Dispatch failed: ${msg}` });
2381
+ }
2382
+ }
2383
+ });
2384
+ }
2385
+
2386
+ // src/tools/subagent/poll.tool.ts
2387
+ import { z as z16 } from "zod";
2388
+ var PollSubagentInputSchema = z16.object({
2389
+ taskIds: z16.array(z16.string()).min(1).max(50).describe("Task IDs to check status for"),
2390
+ includePartialOutput: z16.boolean().default(true).describe("Whether to include partial output from streaming tasks"),
2391
+ maxPartialOutputLength: z16.number().int().min(0).max(1e4).default(2e3).describe("Maximum characters of partial output to return per task")
2392
+ });
2393
+ function createPollTool(config) {
2394
+ const { registry } = config;
2395
+ return tool({
2396
+ description: "Check the status of dispatched subtasks. Returns current status and partial output. Never blocks \u2014 returns immediately.",
2397
+ inputSchema: PollSubagentInputSchema,
2398
+ execute: async (input) => {
2399
+ const summary = {
2400
+ total: 0,
2401
+ queued: 0,
2402
+ running: 0,
2403
+ streaming: 0,
2404
+ completed: 0,
2405
+ failed: 0,
2406
+ timeout: 0,
2407
+ cancelled: 0
2408
+ };
2409
+ const tasks = input.taskIds.map((taskId) => {
2410
+ summary.total++;
2411
+ const handle = registry.get(taskId);
2412
+ if (!handle) {
2413
+ return {
2414
+ taskId,
2415
+ status: "not_found",
2416
+ error: "Task not found",
2417
+ durationMs: 0
2418
+ };
2419
+ }
2420
+ const status = handle.status;
2421
+ if (status in summary) summary[status]++;
2422
+ const result = {
2423
+ taskId,
2424
+ status,
2425
+ durationMs: Date.now() - handle.createdAt
2426
+ };
2427
+ if (input.includePartialOutput && (status === "streaming" || status === "running") && handle.partialOutput) {
2428
+ result.partialOutput = handle.partialOutput.slice(
2429
+ -input.maxPartialOutputLength
2430
+ );
2431
+ }
2432
+ if (status === "completed" && handle.finalOutput) {
2433
+ result.finalOutput = handle.finalOutput;
2434
+ }
2435
+ if ((status === "failed" || status === "timeout" || status === "cancelled") && handle.error) {
2436
+ result.error = handle.error;
2437
+ }
2438
+ if (handle.tokenUsage.input > 0 || handle.tokenUsage.output > 0) {
2439
+ result.tokenUsage = handle.tokenUsage;
2440
+ }
2441
+ return result;
2442
+ });
2443
+ return JSON.stringify({ tasks, summary }, null, 2);
2444
+ }
2445
+ });
2446
+ }
2447
+
2448
+ // src/tools/subagent/await.tool.ts
2449
+ import { z as z17 } from "zod";
2450
+ var AwaitSubagentInputSchema = z17.object({
2451
+ taskIds: z17.array(z17.string()).min(1).max(50).describe("Task IDs to wait for"),
2452
+ timeoutMs: z17.number().int().min(1e3).max(6e5).default(6e4).describe("Maximum time to wait in milliseconds. Default: 60000"),
2453
+ pollIntervalMs: z17.number().int().min(50).max(5e3).default(200).describe("Polling interval used for optional completion checks")
2454
+ });
2455
+ function createAwaitTool(config) {
2456
+ const { registry, hooks } = config;
2457
+ const wait = (ms) => new Promise((resolve) => {
2458
+ setTimeout(resolve, ms);
2459
+ });
2460
+ const normalizeCompletionResult = (result) => {
2461
+ if (typeof result === "boolean") {
2462
+ return { isComplete: result };
2463
+ }
2464
+ if (!result) {
2465
+ return { isComplete: false };
2466
+ }
2467
+ return {
2468
+ isComplete: result.isComplete,
2469
+ reason: result.reason
2470
+ };
2471
+ };
2472
+ return tool({
2473
+ description: "Wait for one or more subtasks to complete. Blocks until all are done, timed out, or cancelled. Returns partial results if timeout is reached. Use poll_subagent for non-blocking checks.",
2474
+ inputSchema: AwaitSubagentInputSchema,
2475
+ execute: async (input) => {
2476
+ const results = await Promise.allSettled(
2477
+ input.taskIds.map(async (taskId) => {
2478
+ const handle = registry.get(taskId);
2479
+ if (!handle) {
2480
+ return {
2481
+ taskId,
2482
+ status: "not_found",
2483
+ error: "Task not found. It may have been garbage collected."
2484
+ };
2485
+ }
2486
+ let completionOverride = false;
2487
+ let completionReason;
2488
+ let resolved = handle;
2489
+ if (!hooks?.isTaskComplete) {
2490
+ resolved = await registry.waitForCompletion(
2491
+ taskId,
2492
+ input.timeoutMs
2493
+ );
2494
+ } else {
2495
+ const startedAt = Date.now();
2496
+ let iterations = 0;
2497
+ while (Date.now() - startedAt < input.timeoutMs) {
2498
+ const current = registry.get(taskId);
2499
+ if (!current) {
2500
+ return {
2501
+ taskId,
2502
+ status: "not_found",
2503
+ error: "Task not found. It may have been garbage collected."
2504
+ };
2505
+ }
2506
+ resolved = current;
2507
+ if (isTerminalStatus(current.status)) {
2508
+ break;
2509
+ }
2510
+ try {
2511
+ const decision = normalizeCompletionResult(
2512
+ await hooks.isTaskComplete({
2513
+ taskId: current.taskId,
2514
+ parentId: current.parentId,
2515
+ status: current.status,
2516
+ partialOutput: current.partialOutput,
2517
+ finalOutput: current.finalOutput,
2518
+ error: current.error,
2519
+ elapsedMs: Date.now() - current.createdAt,
2520
+ iterations,
2521
+ tokenUsage: current.tokenUsage,
2522
+ metadata: current.metadata
2523
+ })
2524
+ );
2525
+ if (decision.isComplete) {
2526
+ completionOverride = true;
2527
+ completionReason = decision.reason;
2528
+ break;
2529
+ }
2530
+ } catch {
2531
+ }
2532
+ iterations++;
2533
+ await wait(input.pollIntervalMs);
2534
+ }
2535
+ }
2536
+ const response = {
2537
+ taskId: resolved.taskId,
2538
+ status: resolved.status,
2539
+ output: resolved.finalOutput ?? void 0,
2540
+ error: resolved.error ?? void 0,
2541
+ durationMs: Date.now() - resolved.createdAt,
2542
+ tokenUsage: resolved.tokenUsage
2543
+ };
2544
+ if (completionOverride) {
2545
+ response.completionOverride = true;
2546
+ if (completionReason) {
2547
+ response.completionReason = completionReason;
2548
+ }
2549
+ }
2550
+ return response;
2551
+ })
2552
+ );
2553
+ const formatted = results.map((r) => {
2554
+ if (r.status === "fulfilled") return r.value;
2555
+ return {
2556
+ status: "error",
2557
+ error: r.reason instanceof Error ? r.reason.message : String(r.reason)
2558
+ };
2559
+ });
2560
+ return JSON.stringify(formatted, null, 2);
2561
+ }
2562
+ });
2563
+ }
2564
+
2565
+ // src/tools/subagent/subagent-scheduler.ts
2566
+ var DEFAULT_POOL_CONFIG = {
2567
+ minWorkers: 2,
2568
+ maxWorkers: 20,
2569
+ scaleUpThreshold: 0.8,
2570
+ scaleDownThreshold: 0.3,
2571
+ resizeCooldownMs: 1e4
2572
+ };
2573
+ var PriorityQueue = class {
2574
+ heap = [];
2575
+ agingIntervalMs;
2576
+ constructor(agingIntervalMs = 5e3) {
2577
+ this.agingIntervalMs = agingIntervalMs;
2578
+ }
2579
+ get size() {
2580
+ return this.heap.length;
2581
+ }
2582
+ enqueue(handle) {
2583
+ const entry = {
2584
+ handle,
2585
+ effectivePriority: handle.priority,
2586
+ enqueuedAt: Date.now()
2587
+ };
2588
+ this.heap.push(entry);
2589
+ this.bubbleUp(this.heap.length - 1);
2590
+ }
2591
+ dequeue() {
2592
+ if (this.heap.length === 0) return null;
2593
+ this.refreshPriorities();
2594
+ const top = this.heap[0];
2595
+ const last = this.heap.pop();
2596
+ if (this.heap.length > 0) {
2597
+ this.heap[0] = last;
2598
+ this.sinkDown(0);
2599
+ }
2600
+ return top.handle;
2601
+ }
2602
+ peek() {
2603
+ return this.heap.length > 0 ? this.heap[0].handle : null;
2604
+ }
2605
+ remove(taskId) {
2606
+ const idx = this.heap.findIndex((e) => e.handle.taskId === taskId);
2607
+ if (idx === -1) return false;
2608
+ const last = this.heap.pop();
2609
+ if (idx < this.heap.length) {
2610
+ this.heap[idx] = last;
2611
+ this.bubbleUp(idx);
2612
+ this.sinkDown(idx);
2613
+ }
2614
+ return true;
2615
+ }
2616
+ refreshPriorities() {
2617
+ const now = Date.now();
2618
+ let dirty = false;
2619
+ for (const entry of this.heap) {
2620
+ const ageBonus = Math.floor(
2621
+ (now - entry.enqueuedAt) / this.agingIntervalMs
2622
+ );
2623
+ const newPriority = Math.max(1, entry.handle.priority - ageBonus);
2624
+ if (newPriority !== entry.effectivePriority) {
2625
+ entry.effectivePriority = newPriority;
2626
+ dirty = true;
2627
+ }
2628
+ }
2629
+ if (dirty) {
2630
+ for (let i = Math.floor(this.heap.length / 2) - 1; i >= 0; i--) {
2631
+ this.sinkDown(i);
2632
+ }
2633
+ }
2634
+ }
2635
+ bubbleUp(idx) {
2636
+ while (idx > 0) {
2637
+ const parentIdx = Math.floor((idx - 1) / 2);
2638
+ if (this.heap[parentIdx].effectivePriority <= this.heap[idx].effectivePriority)
2639
+ break;
2640
+ [this.heap[parentIdx], this.heap[idx]] = [
2641
+ this.heap[idx],
2642
+ this.heap[parentIdx]
2643
+ ];
2644
+ idx = parentIdx;
2645
+ }
2646
+ }
2647
+ sinkDown(idx) {
2648
+ const length = this.heap.length;
2649
+ while (true) {
2650
+ let smallest = idx;
2651
+ const left = 2 * idx + 1;
2652
+ const right = 2 * idx + 2;
2653
+ if (left < length && this.heap[left].effectivePriority < this.heap[smallest].effectivePriority) {
2654
+ smallest = left;
2655
+ }
2656
+ if (right < length && this.heap[right].effectivePriority < this.heap[smallest].effectivePriority) {
2657
+ smallest = right;
2658
+ }
2659
+ if (smallest === idx) break;
2660
+ [this.heap[smallest], this.heap[idx]] = [
2661
+ this.heap[idx],
2662
+ this.heap[smallest]
2663
+ ];
2664
+ idx = smallest;
2665
+ }
2666
+ }
2667
+ };
2668
+ var SubagentScheduler = class {
2669
+ registry;
2670
+ queue;
2671
+ poolConfig;
2672
+ model;
2673
+ limits;
2674
+ telemetry;
2675
+ currentPoolSize;
2676
+ activeWorkers = 0;
2677
+ lastResizeAt = 0;
2678
+ drainTimer = null;
2679
+ circuitBreakers = /* @__PURE__ */ new Map();
2680
+ cbConfig = {
2681
+ failureThreshold: 3,
2682
+ monitorWindowMs: 6e4,
2683
+ resetTimeoutMs: 3e4
2684
+ };
2685
+ constructor(registry, model, limits, options) {
2686
+ this.registry = registry;
2687
+ this.model = model;
2688
+ this.limits = limits;
2689
+ this.poolConfig = { ...DEFAULT_POOL_CONFIG, ...options?.poolConfig };
2690
+ this.telemetry = options?.telemetry;
2691
+ this.queue = new PriorityQueue();
2692
+ this.currentPoolSize = this.poolConfig.minWorkers;
2693
+ registry.setScheduler(this);
2694
+ }
2695
+ // -------------------------------------------------------------------------
2696
+ // Lifecycle
2697
+ // -------------------------------------------------------------------------
2698
+ start() {
2699
+ this.drainTimer = setInterval(() => this.drain(), 100);
2700
+ }
2701
+ async shutdown() {
2702
+ if (this.drainTimer) {
2703
+ clearInterval(this.drainTimer);
2704
+ this.drainTimer = null;
2705
+ }
2706
+ }
2707
+ // -------------------------------------------------------------------------
2708
+ // Enqueue (called by registry on dispatch)
2709
+ // -------------------------------------------------------------------------
2710
+ enqueue(handle) {
2711
+ this.queue.enqueue(handle);
2712
+ this.drain();
2713
+ }
2714
+ // -------------------------------------------------------------------------
2715
+ // Metrics
2716
+ // -------------------------------------------------------------------------
2717
+ getMetrics() {
2718
+ return {
2719
+ activeWorkers: this.activeWorkers,
2720
+ poolSize: this.currentPoolSize,
2721
+ queueSize: this.queue.size
2722
+ };
2723
+ }
2724
+ // -------------------------------------------------------------------------
2725
+ // Drain loop
2726
+ // -------------------------------------------------------------------------
2727
+ drain() {
2728
+ while (this.queue.size > 0 && this.activeWorkers < this.currentPoolSize) {
2729
+ const handle = this.queue.dequeue();
2730
+ if (!handle) break;
2731
+ if (handle.abortController.signal.aborted) {
2732
+ this.registry.transition(handle.taskId, "cancelled", {
2733
+ error: "cancelled-while-queued"
2734
+ });
2735
+ continue;
2736
+ }
2737
+ const taskType = this.getTaskType(handle);
2738
+ if (this.isCircuitOpen(taskType)) {
2739
+ this.registry.transition(handle.taskId, "failed", {
2740
+ error: "Circuit breaker open for this task type. Too many recent failures."
2741
+ });
2742
+ continue;
2743
+ }
2744
+ this.activeWorkers++;
2745
+ this.executeHandle(handle).finally(() => {
2746
+ this.activeWorkers--;
2747
+ this.maybeResize();
2748
+ this.drain();
2749
+ });
2750
+ }
2751
+ }
2752
+ // -------------------------------------------------------------------------
2753
+ // Execute a single handle
2754
+ // -------------------------------------------------------------------------
2755
+ async executeHandle(handle) {
2756
+ this.registry.transition(handle.taskId, "running");
2757
+ const subVfs = new VirtualFilesystem();
2758
+ const fsTools = createFilesystemTools(subVfs);
2759
+ handle.timeoutTimer = setTimeout(() => {
2760
+ handle.abortController.abort("timeout");
2761
+ this.registry.transition(handle.taskId, "timeout", {
2762
+ error: `Subagent timed out after ${handle.timeoutMs}ms`
2763
+ });
2764
+ }, handle.timeoutMs);
2765
+ try {
2766
+ const result = await generateText({
2767
+ model: this.model,
2768
+ system: handle.instructions ?? "You are a specialized subagent. Complete the task and return your findings.",
2769
+ tools: { ...fsTools },
2770
+ stopWhen: stepCountIs(this.limits.maxStepsPerSubagent),
2771
+ prompt: handle.prompt,
2772
+ abortSignal: handle.abortController.signal
2773
+ });
2774
+ const output = result.text || "[Subagent completed with no text output]";
2775
+ const usage = result.usage;
2776
+ if (usage) {
2777
+ handle.tokenUsage.input += usage.inputTokens ?? 0;
2778
+ handle.tokenUsage.output += usage.outputTokens ?? 0;
2779
+ }
2780
+ this.registry.transition(handle.taskId, "completed", {
2781
+ finalOutput: output
2782
+ });
2783
+ this.recordSuccess(this.getTaskType(handle));
2784
+ } catch (error) {
2785
+ if (handle.abortController.signal.aborted) return;
2786
+ const message = error instanceof Error ? error.message : String(error);
2787
+ this.registry.transition(handle.taskId, "failed", {
2788
+ error: message
2789
+ });
2790
+ this.recordFailure(this.getTaskType(handle));
2791
+ }
2792
+ }
2793
+ // -------------------------------------------------------------------------
2794
+ // Pool Sizing
2795
+ // -------------------------------------------------------------------------
2796
+ maybeResize() {
2797
+ const now = Date.now();
2798
+ if (now - this.lastResizeAt < this.poolConfig.resizeCooldownMs) return;
2799
+ const utilization = this.currentPoolSize > 0 ? this.activeWorkers / this.currentPoolSize : 0;
2800
+ let newSize = this.currentPoolSize;
2801
+ if (utilization > this.poolConfig.scaleUpThreshold) {
2802
+ newSize = Math.min(
2803
+ Math.ceil(this.currentPoolSize * 1.5),
2804
+ this.poolConfig.maxWorkers
2805
+ );
2806
+ } else if (utilization < this.poolConfig.scaleDownThreshold) {
2807
+ newSize = Math.max(
2808
+ Math.floor(this.currentPoolSize * 0.75),
2809
+ this.poolConfig.minWorkers
2810
+ );
2811
+ }
2812
+ if (newSize !== this.currentPoolSize) {
2813
+ this.currentPoolSize = newSize;
2814
+ this.lastResizeAt = now;
2815
+ this.telemetry?.recordMetric("subagent.pool.size", newSize);
2816
+ }
2817
+ this.telemetry?.recordMetric(
2818
+ "subagent.pool.utilization",
2819
+ utilization
2820
+ );
2821
+ }
2822
+ // -------------------------------------------------------------------------
2823
+ // Circuit Breaker per task type
2824
+ // -------------------------------------------------------------------------
2825
+ getTaskType(handle) {
2826
+ return handle.instructions ?? "__default__";
2827
+ }
2828
+ isCircuitOpen(taskType) {
2829
+ const state = this.circuitBreakers.get(taskType);
2830
+ if (!state) return false;
2831
+ if (state.state === "open") {
2832
+ if (Date.now() - state.lastFailure > this.cbConfig.resetTimeoutMs) {
2833
+ state.state = "half-open";
2834
+ return false;
2835
+ }
2836
+ return true;
2837
+ }
2838
+ return false;
2839
+ }
2840
+ recordFailure(taskType) {
2841
+ const now = Date.now();
2842
+ let state = this.circuitBreakers.get(taskType);
2843
+ if (!state) {
2844
+ state = { failures: [], state: "closed", lastFailure: 0 };
2845
+ this.circuitBreakers.set(taskType, state);
2846
+ }
2847
+ state.failures.push(now);
2848
+ state.lastFailure = now;
2849
+ state.failures = state.failures.filter(
2850
+ (t) => now - t < this.cbConfig.monitorWindowMs
2851
+ );
2852
+ if (state.failures.length >= this.cbConfig.failureThreshold) {
2853
+ state.state = "open";
2854
+ this.telemetry?.recordMetric(
2855
+ "subagent.circuit_breaker.open",
2856
+ 1
2857
+ );
2858
+ }
2859
+ }
2860
+ recordSuccess(taskType) {
2861
+ const state = this.circuitBreakers.get(taskType);
2862
+ if (state?.state === "half-open") {
2863
+ state.state = "closed";
2864
+ state.failures = [];
2865
+ }
2866
+ }
2867
+ };
2868
+
2869
+ // src/tools/subagent/index.ts
2870
+ function createAsyncSubagentTools(config) {
2871
+ const { registry, parentId, currentDepth, hooks } = config;
2872
+ return {
2873
+ dispatch_subagent: createDispatchTool({
2874
+ registry,
2875
+ parentId,
2876
+ currentDepth,
2877
+ hooks
2878
+ }),
2879
+ poll_subagent: createPollTool({ registry }),
2880
+ await_subagent: createAwaitTool({ registry, hooks })
2881
+ };
2882
+ }
2883
+
2884
+ // src/agent/tool-manager.ts
2885
+ var ToolManager = class {
2886
+ config;
2887
+ pluginManager;
2888
+ circuitBreaker;
2889
+ rateLimiter;
2890
+ toolCache;
2891
+ constructor(config, pluginManager, circuitBreaker, rateLimiter, toolCache) {
2892
+ this.config = config;
2893
+ this.pluginManager = pluginManager;
2894
+ this.circuitBreaker = circuitBreaker;
2895
+ this.rateLimiter = rateLimiter;
2896
+ this.toolCache = toolCache;
2897
+ }
2898
+ // ---------------------------------------------------------------------------
2899
+ // Tool registration
2900
+ // ---------------------------------------------------------------------------
2901
+ registerTools(target, source, sourceLabel) {
2902
+ for (const [toolName, toolDef] of Object.entries(source)) {
2903
+ if (toolName in target) {
2904
+ throw new Error(`Tool "${toolName}" already registered (source: ${sourceLabel})`);
2905
+ }
2906
+ target[toolName] = toolDef;
2907
+ }
2908
+ }
2909
+ // ---------------------------------------------------------------------------
2910
+ // Build catalog
2911
+ // ---------------------------------------------------------------------------
2912
+ async buildToolCatalog(runMetadata) {
2913
+ const tools = {};
2914
+ this.registerTools(tools, createFilesystemTools(this.config.fs), "filesystem");
2915
+ this.registerTools(tools, this.config.extraTools ?? {}, "builder.withTools");
2916
+ this.registerTools(tools, this.pluginManager.collectTools(), "plugins");
2917
+ if (this.config.planning) {
2918
+ this.registerTools(tools, createPlanningTools(this.config.fs), "planning");
2919
+ }
2920
+ if (this.config.subagents) {
2921
+ const limits = {
2922
+ ...DEFAULT_LIMITS,
2923
+ maxDepth: this.config.subagentConfig?.maxDepth ?? DEFAULT_LIMITS.maxDepth,
2924
+ defaultTimeoutMs: this.config.subagentConfig?.timeoutMs ?? DEFAULT_LIMITS.defaultTimeoutMs
2925
+ };
2926
+ const eventBus = new EventBus("subagent-bus");
2927
+ const registry = new SubagentRegistry(eventBus, {
2928
+ limits,
2929
+ hooks: this.config.subagentConfig?.hooks
2930
+ });
2931
+ const scheduler = new SubagentScheduler(registry, this.config.model, limits);
2932
+ scheduler.start();
2933
+ this.registerTools(
2934
+ tools,
2935
+ createAsyncSubagentTools({
2936
+ registry,
2937
+ parentId: "root",
2938
+ maxDepth: limits.maxDepth,
2939
+ currentDepth: 0,
2940
+ hooks: this.config.subagentConfig?.hooks
2941
+ }),
2942
+ "subagents"
2943
+ );
2944
+ }
2945
+ if (this.config.mcp) {
2946
+ const mcpDefs = await this.config.mcp.discoverTools();
2947
+ const mcp = this.config.mcp;
2948
+ const mcpTools = {};
2949
+ const selection = this.extractMcpToolsetSelection(runMetadata);
2950
+ for (const [name, def] of Object.entries(mcpDefs)) {
2951
+ const [serverName = "mcp", ...toolParts] = name.split(":");
2952
+ const shortName = toolParts.length > 0 ? toolParts.join(":") : name;
2953
+ if (!this.shouldIncludeMcpTool(selection, {
2954
+ serverName,
2955
+ fullName: name,
2956
+ shortName
2957
+ })) {
2958
+ continue;
2959
+ }
2960
+ mcpTools[`mcp:${name}`] = tool({
2961
+ description: def.description,
2962
+ inputSchema: z18.object({}).passthrough(),
2963
+ execute: async (args) => {
2964
+ const result = await mcp.executeTool(name, args);
2965
+ if (result.isError) throw new Error(result.content[0]?.text ?? "MCP tool error");
2966
+ return result.content.map((c) => c.text ?? "").join("\n");
2967
+ }
2968
+ });
2969
+ }
2970
+ this.registerTools(tools, mcpTools, "mcp");
2971
+ }
2972
+ if (this.config.policyEngine) {
2973
+ this.registerTools(
2974
+ tools,
2975
+ createPolicyTools(this.config.policyEngine),
2976
+ "policy"
2977
+ );
2978
+ }
2979
+ return tools;
2980
+ }
2981
+ // ---------------------------------------------------------------------------
2982
+ // Wrapping — delegates to standalone pure functions
2983
+ // ---------------------------------------------------------------------------
2984
+ wrapToolsWithApproval(tools, sessionId, eventBus, runtime) {
2985
+ if (!this.config.approvalConfig) return;
2986
+ const approval = new ApprovalManager(
2987
+ this.config.approvalConfig,
2988
+ sessionId,
2989
+ (evt) => eventBus.emit(evt.type, evt.data)
2990
+ );
2991
+ applyApprovalWrapping(tools, approval, runtime);
2992
+ }
2993
+ wrapToolsWithResilience(tools) {
2994
+ applyResilienceWrapping(tools, this.circuitBreaker, this.toolCache);
2995
+ }
2996
+ wrapToolsWithPolicy(tools, sessionId, runMetadata) {
2997
+ if (!this.config.policyEngine) return;
2998
+ const context = this.resolvePolicyContext(sessionId, runMetadata);
2999
+ applyPolicyWrapping(tools, this.config.policyEngine, context);
3000
+ }
3001
+ wrapToolsWithPlugins(tools, pluginCtx) {
3002
+ applyPluginWrapping(tools, this.pluginManager, pluginCtx);
3003
+ }
3004
+ resolvePolicyContext(sessionId, runMetadata) {
3005
+ const base = {
3006
+ sessionId,
3007
+ userId: this.config.userId
3008
+ };
3009
+ const raw = runMetadata?.policyContext;
3010
+ if (!raw || typeof raw !== "object") {
3011
+ return base;
3012
+ }
3013
+ const candidate = raw;
3014
+ return {
3015
+ ...base,
3016
+ ...candidate.sessionId ? { sessionId: candidate.sessionId } : {},
3017
+ ...candidate.userId ? { userId: candidate.userId } : {},
3018
+ ...candidate.tenantId ? { tenantId: candidate.tenantId } : {},
3019
+ ...candidate.metadata && typeof candidate.metadata === "object" ? { metadata: candidate.metadata } : {}
3020
+ };
3021
+ }
3022
+ createRateLimitedModel() {
3023
+ if (!this.rateLimiter) {
3024
+ return this.config.model;
3025
+ }
3026
+ const rateLimiter = this.rateLimiter;
3027
+ const originalModel = this.config.model;
3028
+ return new Proxy(originalModel, {
3029
+ get(target, prop, receiver) {
3030
+ const value = Reflect.get(target, prop, receiver);
3031
+ if (typeof value === "function" && prop === "doGenerate") {
3032
+ return async function(...args) {
3033
+ await rateLimiter.acquire();
3034
+ return value.apply(target, args);
3035
+ };
3036
+ }
3037
+ if (typeof value === "function" && prop === "doStream") {
3038
+ return async function(...args) {
3039
+ await rateLimiter.acquire();
3040
+ return value.apply(target, args);
3041
+ };
3042
+ }
3043
+ return value;
3044
+ }
3045
+ });
3046
+ }
3047
+ // ---------------------------------------------------------------------------
3048
+ // Full preparation pipeline
3049
+ // ---------------------------------------------------------------------------
3050
+ async prepareTools(sessionId, eventBus, runtime, runMetadata) {
3051
+ const tools = await this.buildToolCatalog(runMetadata);
3052
+ const toolNames = Object.keys(tools);
3053
+ const setupCtx = this.createPluginSetupContext(sessionId, toolNames, eventBus);
3054
+ await this.pluginManager.initialize(setupCtx);
3055
+ const pluginCtx = this.createPluginContext(sessionId, toolNames, runMetadata);
3056
+ this.wrapToolsWithApproval(tools, sessionId, eventBus, runtime);
3057
+ this.wrapToolsWithResilience(tools);
3058
+ if (this.config.policyEngine) {
3059
+ this.wrapToolsWithPolicy(tools, sessionId, runMetadata);
3060
+ }
3061
+ this.wrapToolsWithPlugins(tools, pluginCtx);
3062
+ return { tools, pluginCtx };
3063
+ }
3064
+ // ---------------------------------------------------------------------------
3065
+ // Plugin context factories
3066
+ // ---------------------------------------------------------------------------
3067
+ createPluginContext(sessionId, toolNames, runMetadata) {
3068
+ return {
3069
+ sessionId,
3070
+ agentName: this.config.name,
3071
+ config: {
3072
+ instructions: this.config.instructions,
3073
+ maxSteps: this.config.maxSteps
3074
+ },
3075
+ filesystem: this.config.fs,
3076
+ memory: this.config.memory,
3077
+ learning: this.config.learning,
3078
+ toolNames,
3079
+ runMetadata
3080
+ };
3081
+ }
3082
+ createPluginSetupContext(sessionId, toolNames, eventBus) {
3083
+ return {
3084
+ ...this.createPluginContext(sessionId, toolNames),
3085
+ on: (eventType, handler) => eventBus.on(eventType, handler)
3086
+ };
3087
+ }
3088
+ extractMcpToolsetSelection(runMetadata) {
3089
+ const raw = runMetadata?.mcpToolset;
3090
+ if (!raw || typeof raw !== "object") {
3091
+ return void 0;
3092
+ }
3093
+ const normalize = (value) => {
3094
+ if (!Array.isArray(value)) return void 0;
3095
+ const cleaned = value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
3096
+ return cleaned.length > 0 ? cleaned : void 0;
3097
+ };
3098
+ const candidate = raw;
3099
+ const selection = {
3100
+ includeServers: normalize(candidate.includeServers),
3101
+ excludeServers: normalize(candidate.excludeServers),
3102
+ includeTools: normalize(candidate.includeTools),
3103
+ excludeTools: normalize(candidate.excludeTools)
3104
+ };
3105
+ if (!selection.includeServers && !selection.excludeServers && !selection.includeTools && !selection.excludeTools) {
3106
+ return void 0;
3107
+ }
3108
+ return selection;
3109
+ }
3110
+ shouldIncludeMcpTool(selection, toolInfo) {
3111
+ if (!selection) {
3112
+ return true;
3113
+ }
3114
+ if (selection.includeServers && !selection.includeServers.includes(toolInfo.serverName)) {
3115
+ return false;
3116
+ }
3117
+ if (selection.excludeServers && selection.excludeServers.includes(toolInfo.serverName)) {
3118
+ return false;
3119
+ }
3120
+ if (selection.includeTools) {
3121
+ const allowed = selection.includeTools.includes(toolInfo.fullName) || selection.includeTools.includes(toolInfo.shortName);
3122
+ if (!allowed) {
3123
+ return false;
3124
+ }
3125
+ }
3126
+ if (selection.excludeTools && (selection.excludeTools.includes(toolInfo.fullName) || selection.excludeTools.includes(toolInfo.shortName))) {
3127
+ return false;
3128
+ }
3129
+ return true;
3130
+ }
3131
+ };
3132
+ function wrapAllTools(tools, wrapFn) {
3133
+ for (const [name, toolDef] of Object.entries(tools)) {
3134
+ if (!toolDef) continue;
3135
+ const maybeExecutable = toolDef;
3136
+ if (!maybeExecutable.execute) continue;
3137
+ const originalExecute = maybeExecutable.execute.bind(maybeExecutable);
3138
+ maybeExecutable.execute = wrapFn(name, originalExecute);
3139
+ }
3140
+ }
3141
+ function applyApprovalWrapping(tools, approval, runtime) {
3142
+ let stepIndex = 0;
3143
+ wrapAllTools(tools, (name, originalExecute) => async (...args) => {
3144
+ const { approved, reason } = await approval.checkAndApprove(
3145
+ name,
3146
+ runtime.randomUUID(),
3147
+ args[0],
3148
+ stepIndex++
3149
+ );
3150
+ if (!approved) {
3151
+ return `Tool call denied: ${reason ?? "not approved"}`;
3152
+ }
3153
+ return originalExecute(...args);
3154
+ });
3155
+ }
3156
+ function applyResilienceWrapping(tools, circuitBreaker, toolCache) {
3157
+ if (circuitBreaker) {
3158
+ wrapAllTools(tools, (_name, originalExecute) => async (...args) => {
3159
+ return circuitBreaker.execute(async () => originalExecute(...args));
3160
+ });
3161
+ }
3162
+ if (toolCache) {
3163
+ wrapAllTools(tools, (name, originalExecute) => async (...args) => {
3164
+ const cacheKey = `${name}:${JSON.stringify(args)}`;
3165
+ if (toolCache.has(cacheKey)) {
3166
+ return toolCache.get(cacheKey);
3167
+ }
3168
+ const result = await originalExecute(...args);
3169
+ toolCache.set(cacheKey, result);
3170
+ return result;
3171
+ });
3172
+ }
3173
+ }
3174
+ function applyPolicyWrapping(tools, policyEngine, context) {
3175
+ wrapAllTools(tools, (name, originalExecute) => async (...args) => {
3176
+ if (!name.startsWith("mcp:")) {
3177
+ return originalExecute(...args);
3178
+ }
3179
+ const resource = name.slice("mcp:".length);
3180
+ const [serverName = "mcp", ...toolParts] = resource.split(":");
3181
+ const toolName = toolParts.length > 0 ? toolParts.join(":") : resource;
3182
+ const request = {
3183
+ action: "invoke",
3184
+ resource,
3185
+ serverName,
3186
+ toolName
3187
+ };
3188
+ const decision = await policyEngine.evaluate(request, context);
3189
+ if (!decision.allowed) {
3190
+ throw new Error(
3191
+ `MCP policy denied (${decision.auditId}) for ${resource}: ${decision.reason ?? "access denied"}`
3192
+ );
3193
+ }
3194
+ return originalExecute(...args);
3195
+ });
3196
+ }
3197
+ function applyPluginWrapping(tools, pluginManager, pluginCtx) {
3198
+ if (pluginManager.count === 0) return;
3199
+ wrapAllTools(tools, (name, originalExecute) => async (...args) => {
3200
+ const beforeResult = await pluginManager.runBeforeTool(pluginCtx, {
3201
+ toolName: name,
3202
+ args: args[0]
3203
+ });
3204
+ if (beforeResult.skip) {
3205
+ return beforeResult.result;
3206
+ }
3207
+ const finalArgs = beforeResult.args !== void 0 ? beforeResult.args : args[0];
3208
+ try {
3209
+ const result = await originalExecute(finalArgs, ...args.slice(1));
3210
+ await pluginManager.runAfterTool(pluginCtx, {
3211
+ toolName: name,
3212
+ args: finalArgs,
3213
+ result
3214
+ });
3215
+ return result;
3216
+ } catch (error) {
3217
+ const onErrorResult = await pluginManager.runOnError(pluginCtx, {
3218
+ error,
3219
+ phase: "tool"
3220
+ });
3221
+ if (onErrorResult.suppress) return void 0;
3222
+ throw error;
3223
+ }
3224
+ });
3225
+ }
3226
+
3227
+ // src/agent/execution-engine.ts
3228
+ var ExecutionEngine = class {
3229
+ config;
3230
+ toolManager;
3231
+ pluginManager;
3232
+ eventBus;
3233
+ tokenTracker;
3234
+ middlewareChain;
3235
+ constructor(config, toolManager, pluginManager, eventBus, tokenTracker, middlewareChain) {
3236
+ this.config = config;
3237
+ this.toolManager = toolManager;
3238
+ this.pluginManager = pluginManager;
3239
+ this.eventBus = eventBus;
3240
+ this.tokenTracker = tokenTracker;
3241
+ this.middlewareChain = middlewareChain;
3242
+ }
3243
+ // ---------------------------------------------------------------------------
3244
+ // Middleware context factory
3245
+ // ---------------------------------------------------------------------------
3246
+ createMiddlewareContext(sessionId) {
3247
+ return {
3248
+ sessionId,
3249
+ agentName: this.config.agentName,
3250
+ timestamp: Date.now(),
3251
+ metadata: {}
3252
+ };
3253
+ }
3254
+ // ---------------------------------------------------------------------------
3255
+ // Telemetry helper — DRY span lifecycle (try/catch/finally)
3256
+ // ---------------------------------------------------------------------------
3257
+ isDelegationTool(toolName) {
3258
+ return toolName === "dispatch_subagent" || toolName === "poll_subagent" || toolName === "await_subagent";
3259
+ }
3260
+ async applyDelegationMessageFilter(params) {
3261
+ const hook = this.config.delegationHooks?.messageFilter;
3262
+ if (!hook || !this.isDelegationTool(params.toolName)) {
3263
+ return {
3264
+ allow: true,
3265
+ payload: params.payload
3266
+ };
3267
+ }
3268
+ try {
3269
+ const filtered = await hook(params);
3270
+ if (typeof filtered === "boolean") {
3271
+ return {
3272
+ allow: filtered,
3273
+ payload: params.payload
3274
+ };
3275
+ }
3276
+ if (!filtered) {
3277
+ return {
3278
+ allow: true,
3279
+ payload: params.payload
3280
+ };
3281
+ }
3282
+ return {
3283
+ allow: filtered.allow !== false,
3284
+ payload: filtered.payload !== void 0 ? filtered.payload : params.payload,
3285
+ reason: filtered.reason
3286
+ };
3287
+ } catch {
3288
+ return {
3289
+ allow: true,
3290
+ payload: params.payload
3291
+ };
3292
+ }
3293
+ }
3294
+ async withSpan(name, attrs, fn) {
3295
+ const span = this.config.telemetry?.startSpan(name, attrs);
3296
+ try {
3297
+ const result = await fn();
3298
+ span?.setStatus("OK");
3299
+ return result;
3300
+ } catch (error) {
3301
+ span?.setStatus("ERROR", error instanceof Error ? error.message : String(error));
3302
+ throw error;
3303
+ } finally {
3304
+ span?.end();
3305
+ }
3306
+ }
3307
+ // ---------------------------------------------------------------------------
3308
+ // Run
3309
+ // ---------------------------------------------------------------------------
3310
+ async run(prompt, sessionId, runtime, options = {}) {
3311
+ const runMetadata = {
3312
+ ...options.pluginMetadata ?? {},
3313
+ ...options.mcpToolset ? { mcpToolset: options.mcpToolset } : {},
3314
+ ...options.policyContext ? { policyContext: options.policyContext } : {}
3315
+ };
3316
+ let pluginCtx = this.toolManager.createPluginContext(
3317
+ sessionId,
3318
+ [],
3319
+ runMetadata
3320
+ );
3321
+ try {
3322
+ const prepared = await this.toolManager.prepareTools(
3323
+ sessionId,
3324
+ this.eventBus,
3325
+ runtime,
3326
+ runMetadata
3327
+ );
3328
+ const tools = prepared.tools;
3329
+ pluginCtx = prepared.pluginCtx;
3330
+ if (this.config.learning) {
3331
+ const userId = this.config.userId ?? sessionId;
3332
+ const [profile, memories] = await Promise.all([
3333
+ this.config.learning.getProfile(userId),
3334
+ this.config.learning.getMemories(userId, { limit: 10 })
3335
+ ]);
3336
+ const learningContext = this.buildLearningContext(profile, memories);
3337
+ if (learningContext) prompt = `${learningContext}
3338
+
3339
+ ${prompt}`;
3340
+ }
3341
+ const beforeRunResult = await this.pluginManager.runBeforeRun(pluginCtx, { prompt });
3342
+ if (beforeRunResult.prompt !== void 0) {
3343
+ prompt = beforeRunResult.prompt;
3344
+ }
3345
+ const mwCtx = this.createMiddlewareContext(sessionId);
3346
+ let instructions = this.config.instructions;
3347
+ if (this.middlewareChain) {
3348
+ const mwResult = await this.middlewareChain.runBeforeAgent(mwCtx, {
3349
+ prompt,
3350
+ instructions,
3351
+ tools
3352
+ });
3353
+ if (mwResult.aborted) {
3354
+ const earlyText = mwResult.earlyResult ?? "";
3355
+ this.eventBus.emit("agent:stop", {
3356
+ result: { text: earlyText, steps: [], sessionId, toolCalls: [] }
3357
+ });
3358
+ return { text: earlyText, steps: [], sessionId, toolCalls: [] };
3359
+ }
3360
+ prompt = mwResult.prompt;
3361
+ instructions = mwResult.instructions;
3362
+ }
3363
+ this.eventBus.emit("agent:start", { prompt });
3364
+ const cpConfig = this.config.checkpointConfig;
3365
+ if (cpConfig?.enabled) {
3366
+ const cp = await this.config.memory.loadLatestCheckpoint(sessionId);
3367
+ if (cp) this.eventBus.emit("checkpoint:load", { checkpoint: cp });
3368
+ }
3369
+ const agentOptions = {
3370
+ model: this.toolManager.createRateLimitedModel(),
3371
+ system: instructions,
3372
+ tools,
3373
+ stopWhen: stepCountIs(this.config.maxSteps),
3374
+ prompt
3375
+ };
3376
+ if (this.config.output) agentOptions.output = this.config.output;
3377
+ const result = await this.withSpan(
3378
+ "llm.generate",
3379
+ { "llm.model": String(this.config.model) },
3380
+ () => generateText(agentOptions)
3381
+ );
3382
+ const resultObj = result;
3383
+ const usage = resultObj.usage;
3384
+ if (usage) {
3385
+ if (usage.promptTokens) {
3386
+ this.tokenTracker.addInput(usage.promptTokens);
3387
+ this.config.telemetry?.recordMetric("llm.tokens.input", usage.promptTokens);
3388
+ }
3389
+ if (usage.completionTokens) {
3390
+ this.tokenTracker.addOutput(usage.completionTokens);
3391
+ this.config.telemetry?.recordMetric("llm.tokens.output", usage.completionTokens);
3392
+ }
3393
+ if (this.config.costTracker && (usage.promptTokens || usage.completionTokens)) {
3394
+ const modelId = this.config.model.modelId ?? "unknown";
3395
+ const provider = this.config.model.provider ?? "unknown";
3396
+ this.config.costTracker.recordUsage({
3397
+ inputTokens: usage.promptTokens ?? 0,
3398
+ outputTokens: usage.completionTokens ?? 0,
3399
+ model: modelId,
3400
+ provider,
3401
+ timestamp: Date.now()
3402
+ });
3403
+ }
3404
+ }
3405
+ const steps = result.steps ?? [];
3406
+ for (let i = 0; i < steps.length; i++) {
3407
+ let step = steps[i];
3408
+ const stepObj = step;
3409
+ const toolName = stepObj?.toolName ?? `step.${i}`;
3410
+ const toolSpanStart = Date.now();
3411
+ try {
3412
+ await this.withSpan(`tool.${toolName}`, { "tool.name": toolName, "step.index": i }, async () => {
3413
+ const beforeStepResult = await this.pluginManager.runBeforeStep(pluginCtx, {
3414
+ stepIndex: i,
3415
+ step
3416
+ });
3417
+ if (beforeStepResult.skip) return;
3418
+ if (beforeStepResult.step !== void 0) {
3419
+ step = beforeStepResult.step;
3420
+ steps[i] = step;
3421
+ }
3422
+ this.eventBus.emit("step:start", { stepIndex: i, step });
3423
+ const toolCalls = stepObj?.toolCalls ?? [];
3424
+ for (const tc of toolCalls) {
3425
+ const filtered = await this.applyDelegationMessageFilter({
3426
+ direction: "tool:call",
3427
+ toolName: tc.toolName,
3428
+ stepIndex: i,
3429
+ payload: tc.args
3430
+ });
3431
+ if (!filtered.allow) {
3432
+ this.eventBus.emit("delegation:message-filtered", {
3433
+ direction: "tool:call",
3434
+ toolName: tc.toolName,
3435
+ stepIndex: i,
3436
+ reason: filtered.reason
3437
+ });
3438
+ continue;
3439
+ }
3440
+ this.eventBus.emit("tool:call", {
3441
+ toolName: tc.toolName,
3442
+ toolCallId: tc.toolCallId,
3443
+ args: filtered.payload,
3444
+ stepIndex: i
3445
+ });
3446
+ }
3447
+ const toolResults = stepObj?.toolResults ?? [];
3448
+ for (const tr of toolResults) {
3449
+ const filtered = await this.applyDelegationMessageFilter({
3450
+ direction: "tool:result",
3451
+ toolName: tr.toolName,
3452
+ stepIndex: i,
3453
+ payload: tr.result
3454
+ });
3455
+ if (!filtered.allow) {
3456
+ this.eventBus.emit("delegation:message-filtered", {
3457
+ direction: "tool:result",
3458
+ toolName: tr.toolName,
3459
+ stepIndex: i,
3460
+ reason: filtered.reason
3461
+ });
3462
+ continue;
3463
+ }
3464
+ this.eventBus.emit("tool:result", {
3465
+ toolName: tr.toolName,
3466
+ toolCallId: tr.toolCallId,
3467
+ result: filtered.payload,
3468
+ stepIndex: i
3469
+ });
3470
+ }
3471
+ this.eventBus.emit("step:end", { stepIndex: i, step });
3472
+ await this.pluginManager.runAfterStep(pluginCtx, {
3473
+ stepIndex: i,
3474
+ step
3475
+ });
3476
+ const toolDurationMs = Date.now() - toolSpanStart;
3477
+ this.config.telemetry?.recordMetric("tool.duration_ms", toolDurationMs, { tool: toolName });
3478
+ });
3479
+ } catch (error) {
3480
+ const onStepError = await this.pluginManager.runOnError(pluginCtx, {
3481
+ error,
3482
+ phase: "step"
3483
+ });
3484
+ if (!onStepError.suppress) throw error;
3485
+ }
3486
+ }
3487
+ if (cpConfig?.enabled) {
3488
+ const checkpoint = {
3489
+ id: runtime.randomUUID(),
3490
+ sessionId,
3491
+ stepIndex: steps.length,
3492
+ conversation: [
3493
+ { role: "user", content: prompt },
3494
+ { role: "assistant", content: result.text ?? "" }
3495
+ ],
3496
+ todos: await this.config.memory.loadTodos(sessionId),
3497
+ filesSnapshot: {},
3498
+ toolResults: {},
3499
+ generatedTokens: 0,
3500
+ lastToolCallId: null,
3501
+ metadata: {},
3502
+ createdAt: Date.now()
3503
+ };
3504
+ await this.config.memory.saveCheckpoint(sessionId, checkpoint);
3505
+ this.eventBus.emit("checkpoint:save", { checkpoint });
3506
+ }
3507
+ const allToolCalls = [];
3508
+ for (let i = 0; i < steps.length; i++) {
3509
+ const s = steps[i];
3510
+ const calls = s?.toolCalls ?? [];
3511
+ for (const tc of calls) {
3512
+ allToolCalls.push({ name: tc.toolName, args: tc.args, stepIndex: i });
3513
+ }
3514
+ }
3515
+ const agentResult = {
3516
+ text: result.text ?? "",
3517
+ steps,
3518
+ sessionId,
3519
+ output: result.output,
3520
+ toolCalls: allToolCalls
3521
+ };
3522
+ if (this.middlewareChain) {
3523
+ const mwAfterResult = await this.middlewareChain.runAfterAgent(mwCtx, {
3524
+ prompt,
3525
+ result: { text: agentResult.text, steps: agentResult.steps, sessionId }
3526
+ });
3527
+ agentResult.text = mwAfterResult.result.text;
3528
+ }
3529
+ this.eventBus.emit("agent:stop", { result: agentResult });
3530
+ await this.pluginManager.runAfterRun(pluginCtx, { result: agentResult });
3531
+ return agentResult;
3532
+ } catch (error) {
3533
+ const onErrorResult = await this.pluginManager.runOnError(pluginCtx, {
3534
+ error,
3535
+ phase: "run"
3536
+ });
3537
+ if (onErrorResult.suppress) {
3538
+ return { text: "", steps: [], sessionId, toolCalls: [] };
3539
+ }
3540
+ this.eventBus.emit("error", { error });
3541
+ throw error;
3542
+ }
3543
+ }
3544
+ // ---------------------------------------------------------------------------
3545
+ // Stream
3546
+ // ---------------------------------------------------------------------------
3547
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3548
+ async stream(params, sessionId, runtime, options = {}) {
3549
+ let pluginCtx = this.toolManager.createPluginContext(sessionId, [], options.pluginMetadata);
3550
+ try {
3551
+ const prepared = await this.toolManager.prepareTools(
3552
+ sessionId,
3553
+ this.eventBus,
3554
+ runtime,
3555
+ options.pluginMetadata
3556
+ );
3557
+ const tools = prepared.tools;
3558
+ pluginCtx = prepared.pluginCtx;
3559
+ if (this.config.learning) {
3560
+ const userId = this.config.userId ?? sessionId;
3561
+ const [profile, memories] = await Promise.all([
3562
+ this.config.learning.getProfile(userId),
3563
+ this.config.learning.getMemories(userId, { limit: 10 })
3564
+ ]);
3565
+ const learningContext = this.buildLearningContext(profile, memories);
3566
+ if (learningContext) {
3567
+ params = {
3568
+ ...params,
3569
+ messages: [
3570
+ { role: "system", content: learningContext },
3571
+ ...params.messages
3572
+ ]
3573
+ };
3574
+ }
3575
+ }
3576
+ this.eventBus.emit("agent:start", { messages: params.messages });
3577
+ return this.withSpan("stream", { "stream.messages": params.messages.length }, async () => {
3578
+ const streamAgentOptions = {
3579
+ model: this.toolManager.createRateLimitedModel(),
3580
+ system: this.config.instructions,
3581
+ tools,
3582
+ stopWhen: stepCountIs(this.config.maxSteps)
3583
+ };
3584
+ if (this.config.output) streamAgentOptions.output = this.config.output;
3585
+ const streamResult = streamText(streamAgentOptions);
3586
+ if (this.config.costTracker) {
3587
+ const costTracker = this.config.costTracker;
3588
+ const model = this.config.model;
3589
+ const tokenTracker = this.tokenTracker;
3590
+ return Promise.resolve(streamResult).then((stream) => {
3591
+ const usagePromise = stream.usage ?? stream.totalUsage;
3592
+ if (usagePromise && typeof usagePromise.then === "function") {
3593
+ usagePromise.then((u) => {
3594
+ if (u) {
3595
+ if (u.promptTokens) tokenTracker.addInput(u.promptTokens);
3596
+ if (u.completionTokens) tokenTracker.addOutput(u.completionTokens);
3597
+ if (u.promptTokens || u.completionTokens) {
3598
+ const modelId = model.modelId ?? "unknown";
3599
+ const provider = model.provider ?? "unknown";
3600
+ costTracker.recordUsage({
3601
+ inputTokens: u.promptTokens ?? 0,
3602
+ outputTokens: u.completionTokens ?? 0,
3603
+ model: modelId,
3604
+ provider,
3605
+ timestamp: Date.now()
3606
+ });
3607
+ }
3608
+ }
3609
+ }).catch((err) => {
3610
+ console.warn("[usage-tracking] Failed to record usage:", err instanceof Error ? err.message : String(err));
3611
+ });
3612
+ }
3613
+ return stream;
3614
+ });
3615
+ }
3616
+ return streamResult;
3617
+ });
3618
+ } catch (error) {
3619
+ const onErrorResult = await this.pluginManager.runOnError(pluginCtx, {
3620
+ error,
3621
+ phase: "stream"
3622
+ });
3623
+ if (onErrorResult.suppress) {
3624
+ return new ReadableStream({ start(c) {
3625
+ c.close();
3626
+ } });
3627
+ }
3628
+ this.eventBus.emit("error", { error });
3629
+ throw error;
3630
+ }
3631
+ }
3632
+ // ---------------------------------------------------------------------------
3633
+ // Learning context
3634
+ // ---------------------------------------------------------------------------
3635
+ buildLearningContext(profile, memories) {
3636
+ const parts = [];
3637
+ if (profile) {
3638
+ if (profile.context) parts.push(`User context: ${profile.context}`);
3639
+ if (profile.style) parts.push(`Preferred style: ${profile.style}`);
3640
+ if (profile.language) parts.push(`Language: ${profile.language}`);
3641
+ }
3642
+ if (memories.length > 0) {
3643
+ parts.push(`Known facts about this user:
3644
+ ${memories.map((m) => `- ${m.content}`).join("\n")}`);
3645
+ }
3646
+ return parts.length > 0 ? `[Learning Context]
3647
+ ${parts.join("\n")}` : "";
3648
+ }
3649
+ };
3650
+
3651
+ // src/agent/lifecycle.ts
3652
+ var SHUTDOWN_TIMEOUT_MS = 3e4;
3653
+ var LifecycleManager = class {
3654
+ _isReady = false;
3655
+ _isShuttingDown = false;
3656
+ hooks;
3657
+ runningOperations = /* @__PURE__ */ new Set();
3658
+ constructor(hooks) {
3659
+ this.hooks = hooks;
3660
+ }
3661
+ async startup() {
3662
+ if (this._isReady) return;
3663
+ try {
3664
+ if (this.hooks.onStartup) {
3665
+ await this.hooks.onStartup();
3666
+ }
3667
+ this._isReady = true;
3668
+ } catch (error) {
3669
+ this._isReady = false;
3670
+ throw error;
3671
+ }
3672
+ }
3673
+ async shutdown() {
3674
+ if (this._isShuttingDown) return;
3675
+ this._isShuttingDown = true;
3676
+ this._isReady = false;
3677
+ if (this.runningOperations.size > 0) {
3678
+ const controller = new AbortController();
3679
+ const timeout = new Promise((resolve) => {
3680
+ const timer = setTimeout(resolve, SHUTDOWN_TIMEOUT_MS);
3681
+ controller.signal.addEventListener("abort", () => {
3682
+ clearTimeout(timer);
3683
+ resolve();
3684
+ });
3685
+ });
3686
+ const allOperations = Promise.all(Array.from(this.runningOperations)).then(() => {
3687
+ }).catch((err) => {
3688
+ console.warn("[shutdown] Error in running operation:", err instanceof Error ? err.message : String(err));
3689
+ });
3690
+ await Promise.race([allOperations, timeout]);
3691
+ controller.abort();
3692
+ }
3693
+ try {
3694
+ if (this.hooks.onShutdown) {
3695
+ await this.hooks.onShutdown();
3696
+ }
3697
+ } finally {
3698
+ this.runningOperations.clear();
3699
+ }
3700
+ }
3701
+ async healthCheck() {
3702
+ if (this.hooks.onHealthCheck) {
3703
+ return this.hooks.onHealthCheck();
3704
+ }
3705
+ return {
3706
+ healthy: this._isReady && !this._isShuttingDown,
3707
+ details: {
3708
+ lifecycle: {
3709
+ status: this._isReady && !this._isShuttingDown ? "up" : "down",
3710
+ message: this._isShuttingDown ? "Agent is shutting down" : this._isReady ? "Agent is ready" : "Agent not started"
3711
+ }
3712
+ }
3713
+ };
3714
+ }
3715
+ get isReady() {
3716
+ return this._isReady;
3717
+ }
3718
+ get isShuttingDown() {
3719
+ return this._isShuttingDown;
3720
+ }
3721
+ /**
3722
+ * Track a running operation for graceful shutdown.
3723
+ * Returns a cleanup function to remove the operation from tracking.
3724
+ */
3725
+ trackOperation(operation) {
3726
+ this.runningOperations.add(operation);
3727
+ const cleanup = () => {
3728
+ this.runningOperations.delete(operation);
3729
+ };
3730
+ operation.finally(cleanup);
3731
+ return operation;
3732
+ }
3733
+ };
3734
+
3735
+ // src/middleware/chain.ts
3736
+ var MiddlewareChain = class {
3737
+ middlewares = [];
3738
+ sorted = true;
3739
+ // ---------------------------------------------------------------------------
3740
+ // Registration
3741
+ // ---------------------------------------------------------------------------
3742
+ use(middleware) {
3743
+ if (this.middlewares.some((m) => m.name === middleware.name)) {
3744
+ throw new Error(`Middleware "${middleware.name}" is already registered`);
3745
+ }
3746
+ this.middlewares.push(middleware);
3747
+ this.sorted = false;
3748
+ }
3749
+ remove(name) {
3750
+ const idx = this.middlewares.findIndex((m) => m.name === name);
3751
+ if (idx === -1) return false;
3752
+ this.middlewares.splice(idx, 1);
3753
+ return true;
3754
+ }
3755
+ list() {
3756
+ this.ensureSorted();
3757
+ return this.middlewares;
3758
+ }
3759
+ // ---------------------------------------------------------------------------
3760
+ // Lifecycle
3761
+ // ---------------------------------------------------------------------------
3762
+ async setup(ctx) {
3763
+ this.ensureSorted();
3764
+ for (const mw of this.middlewares) {
3765
+ if (mw.setup) {
3766
+ await mw.setup(ctx);
3767
+ }
3768
+ }
3769
+ }
3770
+ async teardown(ctx) {
3771
+ for (let i = this.middlewares.length - 1; i >= 0; i--) {
3772
+ const mw = this.middlewares[i];
3773
+ if (mw.teardown) {
3774
+ try {
3775
+ await mw.teardown(ctx);
3776
+ } catch {
3777
+ }
3778
+ }
3779
+ }
3780
+ }
3781
+ // ---------------------------------------------------------------------------
3782
+ // beforeAgent — forward order, accumulate mutations
3783
+ // ---------------------------------------------------------------------------
3784
+ async runBeforeAgent(ctx, params) {
3785
+ this.ensureSorted();
3786
+ let current = { ...params };
3787
+ for (const mw of this.middlewares) {
3788
+ if (!mw.beforeAgent) continue;
3789
+ let result;
3790
+ try {
3791
+ result = await mw.beforeAgent(ctx, current);
3792
+ } catch (error) {
3793
+ const shouldContinue = await this.handleError(ctx, {
3794
+ error,
3795
+ phase: "beforeAgent",
3796
+ middlewareName: mw.name
3797
+ });
3798
+ if (!shouldContinue) throw error;
3799
+ continue;
3800
+ }
3801
+ if (!result) continue;
3802
+ if (result.abort) {
3803
+ return {
3804
+ ...current,
3805
+ aborted: true,
3806
+ earlyResult: result.earlyResult
3807
+ };
3808
+ }
3809
+ current = {
3810
+ prompt: result.prompt ?? current.prompt,
3811
+ instructions: result.instructions ?? current.instructions,
3812
+ tools: result.tools ? { ...current.tools, ...result.tools } : current.tools
3813
+ };
3814
+ }
3815
+ return current;
3816
+ }
3817
+ // ---------------------------------------------------------------------------
3818
+ // afterAgent — reverse order, accumulate mutations
3819
+ // ---------------------------------------------------------------------------
3820
+ async runAfterAgent(ctx, params) {
3821
+ this.ensureSorted();
3822
+ let current = { ...params };
3823
+ for (let i = this.middlewares.length - 1; i >= 0; i--) {
3824
+ const mw = this.middlewares[i];
3825
+ if (!mw.afterAgent) continue;
3826
+ let result;
3827
+ try {
3828
+ result = await mw.afterAgent(ctx, current);
3829
+ } catch (error) {
3830
+ const shouldContinue = await this.handleError(ctx, {
3831
+ error,
3832
+ phase: "afterAgent",
3833
+ middlewareName: mw.name
3834
+ });
3835
+ if (!shouldContinue) throw error;
3836
+ continue;
3837
+ }
3838
+ if (result?.text !== void 0) {
3839
+ current = {
3840
+ ...current,
3841
+ result: { ...current.result, text: result.text }
3842
+ };
3843
+ }
3844
+ }
3845
+ return current;
3846
+ }
3847
+ // ---------------------------------------------------------------------------
3848
+ // beforeTool — forward order
3849
+ // ---------------------------------------------------------------------------
3850
+ async runBeforeTool(ctx, params) {
3851
+ this.ensureSorted();
3852
+ let currentArgs = params.args;
3853
+ for (const mw of this.middlewares) {
3854
+ if (!mw.beforeTool) continue;
3855
+ let result;
3856
+ try {
3857
+ result = await mw.beforeTool(ctx, {
3858
+ ...params,
3859
+ args: currentArgs
3860
+ });
3861
+ } catch (error) {
3862
+ const shouldContinue = await this.handleError(ctx, {
3863
+ error,
3864
+ phase: "beforeTool",
3865
+ middlewareName: mw.name
3866
+ });
3867
+ if (!shouldContinue) throw error;
3868
+ continue;
3869
+ }
3870
+ if (!result) continue;
3871
+ if (result.skip) {
3872
+ return { skip: true, mockResult: result.mockResult };
3873
+ }
3874
+ if (result.args !== void 0) {
3875
+ currentArgs = result.args;
3876
+ }
3877
+ }
3878
+ return { args: currentArgs };
3879
+ }
3880
+ // ---------------------------------------------------------------------------
3881
+ // afterTool — reverse order
3882
+ // ---------------------------------------------------------------------------
3883
+ async runAfterTool(ctx, params) {
3884
+ this.ensureSorted();
3885
+ let current = { ...params };
3886
+ for (let i = this.middlewares.length - 1; i >= 0; i--) {
3887
+ const mw = this.middlewares[i];
3888
+ if (!mw.afterTool) continue;
3889
+ let result;
3890
+ try {
3891
+ result = await mw.afterTool(ctx, current);
3892
+ } catch (error) {
3893
+ const shouldContinue = await this.handleError(ctx, {
3894
+ error,
3895
+ phase: "afterTool",
3896
+ middlewareName: mw.name
3897
+ });
3898
+ if (!shouldContinue) throw error;
3899
+ continue;
3900
+ }
3901
+ if (result?.result !== void 0) {
3902
+ current = { ...current, result: result.result };
3903
+ }
3904
+ }
3905
+ return current;
3906
+ }
3907
+ // ---------------------------------------------------------------------------
3908
+ // Error handling — delegates to middleware onError hooks
3909
+ // ---------------------------------------------------------------------------
3910
+ async handleError(ctx, params) {
3911
+ for (const mw of this.middlewares) {
3912
+ if (!mw.onError) continue;
3913
+ try {
3914
+ const result = await mw.onError(ctx, params);
3915
+ if (result?.suppress) return true;
3916
+ } catch {
3917
+ }
3918
+ }
3919
+ return false;
3920
+ }
3921
+ // ---------------------------------------------------------------------------
3922
+ // Internal — sort by priority (stable sort)
3923
+ // ---------------------------------------------------------------------------
3924
+ ensureSorted() {
3925
+ if (this.sorted) return;
3926
+ this.middlewares.sort((a, b) => a.priority - b.priority);
3927
+ this.sorted = true;
3928
+ }
3929
+ };
3930
+
3931
+ // src/agent/agent-config.ts
3932
+ var DEFAULT_APPROVAL_CONFIG = {
3933
+ defaultMode: "approve-all",
3934
+ requireApproval: [],
3935
+ autoApprove: [],
3936
+ onApprovalRequired: async () => true
3937
+ };
3938
+ var DEFAULT_CHECKPOINT_CONFIG = {
3939
+ enabled: true,
3940
+ baseStepInterval: 5,
3941
+ maxCheckpoints: 10
3942
+ };
3943
+ function resolveApprovalConfig(partial) {
3944
+ if (!partial) return { ...DEFAULT_APPROVAL_CONFIG };
3945
+ return { ...DEFAULT_APPROVAL_CONFIG, ...partial };
3946
+ }
3947
+ function resolveCheckpointConfig(partial) {
3948
+ if (!partial) return { ...DEFAULT_CHECKPOINT_CONFIG };
3949
+ return { ...DEFAULT_CHECKPOINT_CONFIG, ...partial };
3950
+ }
3951
+
3952
+ // src/adapters/memory/in-memory.adapter.ts
3953
+ var InMemoryAdapter = class _InMemoryAdapter {
3954
+ static MAX_SESSIONS = 1e3;
3955
+ sessionOrder = /* @__PURE__ */ new Map();
3956
+ todosMap = /* @__PURE__ */ new Map();
3957
+ checkpointsMap = /* @__PURE__ */ new Map();
3958
+ conversationMap = /* @__PURE__ */ new Map();
3959
+ metadataMap = /* @__PURE__ */ new Map();
3960
+ trackSession(sessionId) {
3961
+ this.sessionOrder.delete(sessionId);
3962
+ this.sessionOrder.set(sessionId, true);
3963
+ while (this.sessionOrder.size > _InMemoryAdapter.MAX_SESSIONS) {
3964
+ const oldest = this.sessionOrder.keys().next().value;
3965
+ if (oldest !== void 0) {
3966
+ this.sessionOrder.delete(oldest);
3967
+ this.todosMap.delete(oldest);
3968
+ this.checkpointsMap.delete(oldest);
3969
+ this.conversationMap.delete(oldest);
3970
+ this.metadataMap.delete(oldest);
3971
+ }
3972
+ }
3973
+ }
3974
+ // -- Todos ------------------------------------------------------------------
3975
+ async saveTodos(sessionId, todos) {
3976
+ this.todosMap.set(sessionId, [...todos]);
3977
+ this.trackSession(sessionId);
3978
+ }
3979
+ async loadTodos(sessionId) {
3980
+ return this.todosMap.get(sessionId) ?? [];
3981
+ }
3982
+ // -- Checkpoints ------------------------------------------------------------
3983
+ async saveCheckpoint(sessionId, checkpoint) {
3984
+ const list = this.checkpointsMap.get(sessionId) ?? [];
3985
+ list.push(checkpoint);
3986
+ this.checkpointsMap.set(sessionId, list);
3987
+ this.trackSession(sessionId);
3988
+ }
3989
+ async loadLatestCheckpoint(sessionId) {
3990
+ const list = this.checkpointsMap.get(sessionId);
3991
+ if (!list || list.length === 0) return null;
3992
+ return list[list.length - 1] ?? null;
3993
+ }
3994
+ async listCheckpoints(sessionId) {
3995
+ return this.checkpointsMap.get(sessionId) ?? [];
3996
+ }
3997
+ async deleteOldCheckpoints(sessionId, keepCount) {
3998
+ const list = this.checkpointsMap.get(sessionId);
3999
+ if (!list || list.length <= keepCount) return;
4000
+ this.checkpointsMap.set(sessionId, list.slice(-keepCount));
4001
+ }
4002
+ // -- Conversation -----------------------------------------------------------
4003
+ async saveConversation(sessionId, messages) {
4004
+ this.conversationMap.set(sessionId, [...messages]);
4005
+ this.trackSession(sessionId);
4006
+ }
4007
+ async loadConversation(sessionId) {
4008
+ return this.conversationMap.get(sessionId) ?? [];
4009
+ }
4010
+ // -- Metadata ---------------------------------------------------------------
4011
+ async saveMetadata(sessionId, key, value) {
4012
+ let map = this.metadataMap.get(sessionId);
4013
+ if (!map) {
4014
+ map = /* @__PURE__ */ new Map();
4015
+ this.metadataMap.set(sessionId, map);
4016
+ }
4017
+ map.set(key, value);
4018
+ this.trackSession(sessionId);
4019
+ }
4020
+ async loadMetadata(sessionId, key) {
4021
+ const map = this.metadataMap.get(sessionId);
4022
+ if (!map || !map.has(key)) return null;
4023
+ return map.get(key);
4024
+ }
4025
+ async deleteMetadata(sessionId, key) {
4026
+ this.metadataMap.get(sessionId)?.delete(key);
4027
+ }
4028
+ // -- Utility ----------------------------------------------------------------
4029
+ /** Clear all data for a session */
4030
+ clear(sessionId) {
4031
+ this.todosMap.delete(sessionId);
4032
+ this.checkpointsMap.delete(sessionId);
4033
+ this.conversationMap.delete(sessionId);
4034
+ this.metadataMap.delete(sessionId);
4035
+ this.sessionOrder.delete(sessionId);
4036
+ }
4037
+ /** Clear all sessions */
4038
+ clearAll() {
4039
+ this.todosMap.clear();
4040
+ this.checkpointsMap.clear();
4041
+ this.conversationMap.clear();
4042
+ this.metadataMap.clear();
4043
+ this.sessionOrder.clear();
4044
+ }
4045
+ };
4046
+
4047
+ // src/adapters/token-counter/approximate.adapter.ts
4048
+ var CHARS_PER_TOKEN = 4;
4049
+ var ROLE_OVERHEAD_TOKENS = 4;
4050
+ var CONTEXT_WINDOW_SIZES = {
4051
+ "gpt-5.2": 128e3,
4052
+ "gpt-5.2-mini": 128e3,
4053
+ "gpt-4-turbo": 128e3,
4054
+ "gpt-4": 8192,
4055
+ "gpt-3.5-turbo": 16385,
4056
+ "claude-3.5-sonnet": 2e5,
4057
+ "claude-3-sonnet": 2e5,
4058
+ "claude-3-haiku": 2e5,
4059
+ "claude-3-opus": 2e5,
4060
+ "claude-4-sonnet": 2e5,
4061
+ "gemini-2.5-flash-preview-05-20": 1e6,
4062
+ "gemini-2.5-flash": 1e6,
4063
+ "gemini-2.5-pro": 1e6,
4064
+ "gemini-1.5-flash": 1e6,
4065
+ "gemini-1.5-pro": 2e6
4066
+ };
4067
+ var DEFAULT_CONTEXT_WINDOW = 128e3;
4068
+ var COST_TABLE = {
4069
+ "gpt-5.2": [2.5, 10],
4070
+ "gpt-5.2-mini": [0.15, 0.6],
4071
+ "claude-3.5-sonnet": [3, 15],
4072
+ "claude-3-haiku": [0.25, 1.25],
4073
+ "gemini-2.5-flash-preview-05-20": [0.075, 0.3],
4074
+ "gemini-2.5-flash": [0.15, 0.6],
4075
+ "gemini-2.5-pro": [1.25, 10]
4076
+ };
4077
+ var DEFAULT_COST = [1, 3];
4078
+ var ApproximateTokenCounter = class {
4079
+ count(text) {
4080
+ if (!text) return 0;
4081
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
4082
+ }
4083
+ countMessages(messages) {
4084
+ if (!messages.length) return 0;
4085
+ return messages.reduce(
4086
+ (sum, msg) => sum + this.count(msg.content) + ROLE_OVERHEAD_TOKENS,
4087
+ 0
4088
+ );
4089
+ }
4090
+ getContextWindowSize(model) {
4091
+ return CONTEXT_WINDOW_SIZES[model] ?? DEFAULT_CONTEXT_WINDOW;
4092
+ }
4093
+ estimateCost(inputTokens, outputTokens, model) {
4094
+ const [inputRate, outputRate] = COST_TABLE[model] ?? DEFAULT_COST;
4095
+ return inputTokens / 1e6 * inputRate + outputTokens / 1e6 * outputRate;
4096
+ }
4097
+ truncate(text, maxTokens) {
4098
+ if (!text) return text;
4099
+ const maxChars = maxTokens * CHARS_PER_TOKEN;
4100
+ if (text.length <= maxChars) return text;
4101
+ return text.slice(0, maxChars);
4102
+ }
4103
+ };
4104
+
4105
+ // src/agent/defaults.ts
4106
+ function defaultFilesystem() {
4107
+ return new VirtualFilesystem();
4108
+ }
4109
+ function defaultMemory() {
4110
+ return new InMemoryAdapter();
4111
+ }
4112
+ function defaultTokenCounter() {
4113
+ return new ApproximateTokenCounter();
4114
+ }
4115
+
4116
+ // src/adapters/resilience/rate-limiter.ts
4117
+ var DEFAULT_RATE_LIMITER_CONFIG = {
4118
+ maxTokens: 10,
4119
+ refillRateMs: 1e3
4120
+ };
4121
+ var RateLimiter = class {
4122
+ constructor(config = DEFAULT_RATE_LIMITER_CONFIG) {
4123
+ this.config = config;
4124
+ this.tokens = config.maxTokens;
4125
+ this.lastRefillTime = Date.now();
4126
+ }
4127
+ tokens;
4128
+ lastRefillTime;
4129
+ waitQueue = [];
4130
+ processingScheduled = false;
4131
+ /**
4132
+ * Acquire a token, waiting if necessary
4133
+ */
4134
+ async acquire() {
4135
+ if (this.tryAcquire()) {
4136
+ return;
4137
+ }
4138
+ return new Promise((resolve, reject) => {
4139
+ this.waitQueue.push({ resolve, reject });
4140
+ this.processQueue();
4141
+ });
4142
+ }
4143
+ /**
4144
+ * Try to acquire a token immediately
4145
+ * @returns true if token acquired, false otherwise
4146
+ */
4147
+ tryAcquire() {
4148
+ this.refillTokens();
4149
+ if (this.tokens > 0) {
4150
+ this.tokens--;
4151
+ return true;
4152
+ }
4153
+ return false;
4154
+ }
4155
+ /**
4156
+ * Get number of available tokens
4157
+ */
4158
+ get availableTokens() {
4159
+ this.refillTokens();
4160
+ return this.tokens;
4161
+ }
4162
+ /**
4163
+ * Reset the rate limiter to initial state
4164
+ */
4165
+ reset() {
4166
+ this.tokens = this.config.maxTokens;
4167
+ this.lastRefillTime = Date.now();
4168
+ this.processingScheduled = false;
4169
+ const queue = [...this.waitQueue];
4170
+ this.waitQueue = [];
4171
+ queue.forEach(({ reject }) => {
4172
+ reject(new RateLimiterError("Rate limiter was reset"));
4173
+ });
4174
+ }
4175
+ refillTokens() {
4176
+ const now = Date.now();
4177
+ const timePassed = now - this.lastRefillTime;
4178
+ if (timePassed >= this.config.refillRateMs) {
4179
+ const tokensToAdd = Math.floor(timePassed / this.config.refillRateMs);
4180
+ this.tokens = Math.min(this.config.maxTokens, this.tokens + tokensToAdd);
4181
+ this.lastRefillTime = now - timePassed % this.config.refillRateMs;
4182
+ }
4183
+ }
4184
+ processQueue() {
4185
+ if (this.waitQueue.length === 0 || this.processingScheduled) {
4186
+ return;
4187
+ }
4188
+ this.processingScheduled = true;
4189
+ const timeUntilNextRefill = this.config.refillRateMs - (Date.now() - this.lastRefillTime);
4190
+ const delay = Math.max(0, timeUntilNextRefill);
4191
+ setTimeout(() => {
4192
+ this.processingScheduled = false;
4193
+ this.refillTokens();
4194
+ while (this.waitQueue.length > 0 && this.tokens > 0) {
4195
+ const waiter = this.waitQueue.shift();
4196
+ this.tokens--;
4197
+ waiter.resolve();
4198
+ }
4199
+ if (this.waitQueue.length > 0) {
4200
+ this.processQueue();
4201
+ }
4202
+ }, delay);
4203
+ }
4204
+ };
4205
+ var RateLimiterError = class extends Error {
4206
+ constructor(message) {
4207
+ super(message);
4208
+ this.name = "RateLimiterError";
4209
+ }
4210
+ };
4211
+
4212
+ // src/agent/agent-builder.ts
4213
+ var AgentBuilder = class extends AbstractBuilder {
4214
+ agentConfig;
4215
+ maxStepsOverride;
4216
+ fs;
4217
+ memory;
4218
+ learning;
4219
+ userId;
4220
+ tokenCounter;
4221
+ mcp;
4222
+ policyEngine;
4223
+ runtime;
4224
+ costTracker;
4225
+ planning = false;
4226
+ subagents = false;
4227
+ subagentConfig;
4228
+ approvalConfig;
4229
+ // Resilience patterns
4230
+ circuitBreaker;
4231
+ rateLimiter;
4232
+ toolCache;
4233
+ // Telemetry
4234
+ telemetryAdapter;
4235
+ extraTools = {};
4236
+ plugins = [];
4237
+ lifecycleHooks;
4238
+ eventHandlers = [];
4239
+ /** AI SDK Output specification (e.g. Output.object({schema})) */
4240
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4241
+ outputSpec;
4242
+ constructor(config) {
4243
+ super();
4244
+ this.agentConfig = config;
4245
+ }
4246
+ withFilesystem(fs) {
4247
+ this.fs = fs;
4248
+ return this;
4249
+ }
4250
+ withMemory(memory) {
4251
+ this.memory = memory;
4252
+ return this;
4253
+ }
4254
+ withLearning(learning, userId) {
4255
+ this.learning = learning;
4256
+ this.userId = userId;
4257
+ return this;
4258
+ }
4259
+ withTokenCounter(counter) {
4260
+ this.tokenCounter = counter;
4261
+ return this;
4262
+ }
4263
+ withMcp(mcp) {
4264
+ this.mcp = mcp;
4265
+ return this;
4266
+ }
4267
+ withPolicyEngine(policyEngine) {
4268
+ this.policyEngine = policyEngine;
4269
+ return this;
4270
+ }
4271
+ withRuntime(runtime) {
4272
+ this.runtime = runtime;
4273
+ return this;
4274
+ }
4275
+ withCostTracker(tracker) {
4276
+ this.costTracker = tracker;
4277
+ return this;
4278
+ }
4279
+ withPlanning() {
4280
+ this.planning = true;
4281
+ return this;
4282
+ }
4283
+ withSubagents(config) {
4284
+ this.subagents = true;
4285
+ this.subagentConfig = config;
4286
+ return this;
4287
+ }
4288
+ withApproval(config) {
4289
+ this.approvalConfig = config;
4290
+ return this;
4291
+ }
4292
+ withMaxSteps(steps) {
4293
+ this.maxStepsOverride = steps;
4294
+ return this;
4295
+ }
4296
+ withCircuitBreaker(config) {
4297
+ this.circuitBreaker = new CircuitBreaker({ ...DEFAULT_CIRCUIT_BREAKER_CONFIG, ...config });
4298
+ return this;
4299
+ }
4300
+ withRateLimiter(config) {
4301
+ this.rateLimiter = new RateLimiter({ ...DEFAULT_RATE_LIMITER_CONFIG, ...config });
4302
+ return this;
4303
+ }
4304
+ withToolCache(config) {
4305
+ this.toolCache = new ToolCache({ ...DEFAULT_TOOL_CACHE_CONFIG, ...config });
4306
+ return this;
4307
+ }
4308
+ withTools(tools) {
4309
+ Object.assign(this.extraTools, tools);
4310
+ return this;
4311
+ }
4312
+ withTool(name, tool2) {
4313
+ this.extraTools[name] = tool2;
4314
+ return this;
4315
+ }
4316
+ withPlugin(plugin) {
4317
+ this.plugins.push(plugin);
4318
+ return this;
4319
+ }
4320
+ on(type, handler) {
4321
+ this.eventHandlers.push({ type, handler });
4322
+ return this;
4323
+ }
4324
+ withLifecycle(hooks) {
4325
+ this.lifecycleHooks = hooks;
4326
+ return this;
4327
+ }
4328
+ withTelemetry(adapter) {
4329
+ this.telemetryAdapter = adapter;
4330
+ return this;
4331
+ }
4332
+ /**
4333
+ * Set structured output specification (AI SDK passthrough).
4334
+ * Usage: `.withOutput(Output.object({ schema: z.object({...}) }))`
4335
+ */
4336
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4337
+ withOutput(output) {
4338
+ this.outputSpec = output;
4339
+ return this;
4340
+ }
4341
+ withInstructions(instructionsOrTemplate, variables) {
4342
+ if (typeof instructionsOrTemplate === "string") {
4343
+ this.agentConfig.instructions = instructionsOrTemplate;
4344
+ } else {
4345
+ this.agentConfig.instructions = instructionsOrTemplate.compile(variables);
4346
+ }
4347
+ return this;
4348
+ }
4349
+ validate() {
4350
+ if (!this.agentConfig.model) throw new Error("model is required");
4351
+ if (!this.agentConfig.instructions) throw new Error("instructions is required");
4352
+ }
4353
+ construct() {
4354
+ const agent = new Agent({
4355
+ model: this.agentConfig.model,
4356
+ instructions: this.agentConfig.instructions,
4357
+ id: this.agentConfig.id,
4358
+ name: this.agentConfig.name,
4359
+ maxSteps: this.maxStepsOverride ?? this.agentConfig.maxSteps ?? 30,
4360
+ fs: this.fs ?? defaultFilesystem(),
4361
+ memory: this.memory ?? defaultMemory(),
4362
+ learning: this.learning,
4363
+ userId: this.userId,
4364
+ tokenCounter: this.tokenCounter ?? defaultTokenCounter(),
4365
+ mcp: this.mcp,
4366
+ policyEngine: this.policyEngine,
4367
+ runtime: this.runtime,
4368
+ costTracker: this.costTracker,
4369
+ planning: this.planning,
4370
+ subagents: this.subagents,
4371
+ subagentConfig: this.subagentConfig,
4372
+ approvalConfig: this.approvalConfig ? resolveApprovalConfig(this.approvalConfig) : void 0,
4373
+ checkpointConfig: resolveCheckpointConfig(
4374
+ this.agentConfig.checkpoint
4375
+ ),
4376
+ extraTools: this.extraTools,
4377
+ plugins: this.plugins,
4378
+ circuitBreaker: this.circuitBreaker,
4379
+ rateLimiter: this.rateLimiter,
4380
+ toolCache: this.toolCache,
4381
+ lifecycleHooks: this.lifecycleHooks,
4382
+ telemetry: this.telemetryAdapter,
4383
+ output: this.outputSpec
4384
+ });
4385
+ for (const { type, handler } of this.eventHandlers) {
4386
+ agent.eventBus.on(type, handler);
4387
+ }
4388
+ return agent;
4389
+ }
4390
+ build() {
4391
+ return super.build();
4392
+ }
4393
+ };
4394
+
4395
+ // src/agent/agent.ts
4396
+ var Agent = class _Agent {
4397
+ sessionId;
4398
+ eventBus;
4399
+ config;
4400
+ _runtime;
4401
+ _runtimePromise = null;
4402
+ tokenTracker;
4403
+ pluginManager;
4404
+ middlewareChain;
4405
+ _toolManager;
4406
+ _executionEngine;
4407
+ lifecycleManager;
4408
+ // Resilience patterns
4409
+ circuitBreaker;
4410
+ rateLimiter;
4411
+ toolCache;
4412
+ // Telemetry
4413
+ telemetry;
4414
+ constructor(config) {
4415
+ this._runtime = config.runtime ?? null;
4416
+ this.sessionId = config.id ?? (this._runtime ? this._runtime.randomUUID() : crypto.randomUUID());
4417
+ this.eventBus = new EventBus(this.sessionId);
4418
+ this.config = config;
4419
+ this.tokenTracker = new TokenTracker(config.tokenCounter, {
4420
+ maxInputTokens: Infinity,
4421
+ maxOutputTokens: Infinity,
4422
+ maxTotalTokens: Infinity,
4423
+ warningThreshold: 0.9
4424
+ });
4425
+ this.pluginManager = new PluginManager();
4426
+ for (const plugin of config.plugins ?? []) {
4427
+ this.pluginManager.register(plugin);
4428
+ }
4429
+ this.middlewareChain = new MiddlewareChain();
4430
+ for (const mw of config.middleware ?? []) {
4431
+ this.middlewareChain.use(mw);
4432
+ }
4433
+ this.circuitBreaker = config.circuitBreaker;
4434
+ this.rateLimiter = config.rateLimiter;
4435
+ this.toolCache = config.toolCache;
4436
+ this.telemetry = config.telemetry;
4437
+ this.lifecycleManager = new LifecycleManager(config.lifecycleHooks ?? {});
4438
+ }
4439
+ // ---------------------------------------------------------------------------
4440
+ // Lazy-initialized heavy managers (created on first access)
4441
+ // ---------------------------------------------------------------------------
4442
+ get toolManager() {
4443
+ return this._toolManager ??= new ToolManager(
4444
+ {
4445
+ model: this.config.model,
4446
+ instructions: this.config.instructions,
4447
+ name: this.config.name,
4448
+ maxSteps: this.config.maxSteps,
4449
+ fs: this.config.fs,
4450
+ memory: this.config.memory,
4451
+ learning: this.config.learning,
4452
+ mcp: this.config.mcp,
4453
+ policyEngine: this.config.policyEngine,
4454
+ userId: this.config.userId,
4455
+ planning: this.config.planning,
4456
+ subagents: this.config.subagents,
4457
+ subagentConfig: this.config.subagentConfig,
4458
+ approvalConfig: this.config.approvalConfig,
4459
+ extraTools: this.config.extraTools
4460
+ },
4461
+ this.pluginManager,
4462
+ this.circuitBreaker,
4463
+ this.rateLimiter,
4464
+ this.toolCache
4465
+ );
4466
+ }
4467
+ get executionEngine() {
4468
+ return this._executionEngine ??= new ExecutionEngine(
4469
+ {
4470
+ model: this.config.model,
4471
+ instructions: this.config.instructions,
4472
+ maxSteps: this.config.maxSteps,
4473
+ memory: this.config.memory,
4474
+ learning: this.config.learning,
4475
+ userId: this.config.userId,
4476
+ agentName: this.config.name,
4477
+ delegationHooks: this.config.subagentConfig?.hooks,
4478
+ checkpointConfig: this.config.checkpointConfig,
4479
+ telemetry: this.config.telemetry,
4480
+ costTracker: this.config.costTracker,
4481
+ output: this.config.output
4482
+ },
4483
+ this.toolManager,
4484
+ this.pluginManager,
4485
+ this.eventBus,
4486
+ this.tokenTracker,
4487
+ this.middlewareChain
4488
+ );
4489
+ }
4490
+ /** Lazy-initialized runtime adapter. Resolves on first use. */
4491
+ async ensureRuntime() {
4492
+ if (this._runtime) return this._runtime;
4493
+ if (!this._runtimePromise) {
4494
+ this._runtimePromise = createRuntimeAdapterAsync().then((rt) => {
4495
+ this._runtime = rt;
4496
+ return rt;
4497
+ });
4498
+ }
4499
+ return this._runtimePromise;
4500
+ }
4501
+ // ---------------------------------------------------------------------------
4502
+ // Static factories
4503
+ // ---------------------------------------------------------------------------
4504
+ static create(config) {
4505
+ return new AgentBuilder(config);
4506
+ }
4507
+ static minimal(config) {
4508
+ return _Agent.create(config).withPlanning().build();
4509
+ }
4510
+ static full(config) {
4511
+ const builder = _Agent.create(config).withPlanning().withSubagents();
4512
+ if (config.memory) builder.withMemory(config.memory);
4513
+ if (config.mcp) builder.withMcp(config.mcp);
4514
+ if (config.tokenCounter) builder.withTokenCounter(config.tokenCounter);
4515
+ return builder.build();
4516
+ }
4517
+ /**
4518
+ * Auto-configuring factory that works in any runtime.
4519
+ * Uses universal adapters (VirtualFilesystem, InMemoryAdapter, ApproximateTokenCounter)
4520
+ * that require zero platform-specific APIs.
4521
+ *
4522
+ * For runtime-specific adapters (LocalFilesystem, DenoFilesystem, OpfsFilesystem, etc.),
4523
+ * use `Agent.create()` and compose manually.
4524
+ */
4525
+ static auto(config) {
4526
+ return _Agent.create(config).withPlanning().build();
4527
+ }
4528
+ // ---------------------------------------------------------------------------
4529
+ // Lifecycle Management
4530
+ // ---------------------------------------------------------------------------
4531
+ async startup() {
4532
+ return this.lifecycleManager.startup();
4533
+ }
4534
+ async shutdown() {
4535
+ return this.lifecycleManager.shutdown();
4536
+ }
4537
+ async healthCheck() {
4538
+ return this.lifecycleManager.healthCheck();
4539
+ }
4540
+ get isReady() {
4541
+ return this.lifecycleManager.isReady;
4542
+ }
4543
+ get isShuttingDown() {
4544
+ return this.lifecycleManager.isShuttingDown;
4545
+ }
4546
+ // ---------------------------------------------------------------------------
4547
+ // Run & Stream - Delegation to ExecutionEngine
4548
+ // ---------------------------------------------------------------------------
4549
+ async run(prompt, options = {}) {
4550
+ await this.ensureRuntime();
4551
+ return this.executionEngine.run(prompt, this.sessionId, this._runtime, options);
4552
+ }
4553
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
4554
+ async stream(params, options = {}) {
4555
+ await this.ensureRuntime();
4556
+ return this.executionEngine.stream(params, this.sessionId, this._runtime, options);
4557
+ }
4558
+ // ---------------------------------------------------------------------------
4559
+ // Cleanup
4560
+ // ---------------------------------------------------------------------------
4561
+ async dispose() {
4562
+ try {
4563
+ await this.lifecycleManager.shutdown();
4564
+ } catch {
4565
+ }
4566
+ try {
4567
+ await this.config.telemetry?.flush();
4568
+ } catch {
4569
+ }
4570
+ try {
4571
+ await this.pluginManager.dispose();
4572
+ } finally {
4573
+ try {
4574
+ if (this.config.mcp) await this.config.mcp.closeAll();
4575
+ } finally {
4576
+ this.eventBus.removeAllListeners();
4577
+ }
4578
+ }
4579
+ }
4580
+ };
4581
+
4582
+ export {
4583
+ AbstractBuilder,
4584
+ VirtualFilesystem,
4585
+ AgentBuilder,
4586
+ Agent
4587
+ };
4588
+ //# sourceMappingURL=chunk-BI2G665F.js.map