@agentick/core 0.0.1

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 (626) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +875 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/.tsbuildinfo.build +1 -0
  5. package/dist/agent.d.ts +32 -0
  6. package/dist/agent.d.ts.map +1 -0
  7. package/dist/agent.js +26 -0
  8. package/dist/agent.js.map +1 -0
  9. package/dist/agentick-instance.d.ts +285 -0
  10. package/dist/agentick-instance.d.ts.map +1 -0
  11. package/dist/agentick-instance.js +700 -0
  12. package/dist/agentick-instance.js.map +1 -0
  13. package/dist/aidk-instance.d.ts +294 -0
  14. package/dist/aidk-instance.d.ts.map +1 -0
  15. package/dist/aidk-instance.js +340 -0
  16. package/dist/aidk-instance.js.map +1 -0
  17. package/dist/app/session-store.d.ts +57 -0
  18. package/dist/app/session-store.d.ts.map +1 -0
  19. package/dist/app/session-store.js +87 -0
  20. package/dist/app/session-store.js.map +1 -0
  21. package/dist/app/session.d.ts +209 -0
  22. package/dist/app/session.d.ts.map +1 -0
  23. package/dist/app/session.js +2131 -0
  24. package/dist/app/session.js.map +1 -0
  25. package/dist/app/sqlite-session-store.d.ts +60 -0
  26. package/dist/app/sqlite-session-store.d.ts.map +1 -0
  27. package/dist/app/sqlite-session-store.js +234 -0
  28. package/dist/app/sqlite-session-store.js.map +1 -0
  29. package/dist/app/types.d.ts +1461 -0
  30. package/dist/app/types.d.ts.map +1 -0
  31. package/dist/app/types.js +14 -0
  32. package/dist/app/types.js.map +1 -0
  33. package/dist/app.d.ts +79 -0
  34. package/dist/app.d.ts.map +1 -0
  35. package/dist/app.js +83 -0
  36. package/dist/app.js.map +1 -0
  37. package/dist/channels/adapters/index.d.ts +2 -0
  38. package/dist/channels/adapters/index.d.ts.map +1 -0
  39. package/dist/channels/adapters/index.js +2 -0
  40. package/dist/channels/adapters/index.js.map +1 -0
  41. package/dist/channels/adapters/redis.d.ts +77 -0
  42. package/dist/channels/adapters/redis.d.ts.map +1 -0
  43. package/dist/channels/adapters/redis.js +259 -0
  44. package/dist/channels/adapters/redis.js.map +1 -0
  45. package/dist/channels/index.d.ts +38 -0
  46. package/dist/channels/index.d.ts.map +1 -0
  47. package/dist/channels/index.js +38 -0
  48. package/dist/channels/index.js.map +1 -0
  49. package/dist/channels/service.d.ts +684 -0
  50. package/dist/channels/service.d.ts.map +1 -0
  51. package/dist/channels/service.js +870 -0
  52. package/dist/channels/service.js.map +1 -0
  53. package/dist/channels/transports/index.d.ts +4 -0
  54. package/dist/channels/transports/index.d.ts.map +1 -0
  55. package/dist/channels/transports/index.js +4 -0
  56. package/dist/channels/transports/index.js.map +1 -0
  57. package/dist/channels/transports/socketio.d.ts +98 -0
  58. package/dist/channels/transports/socketio.d.ts.map +1 -0
  59. package/dist/channels/transports/socketio.js +246 -0
  60. package/dist/channels/transports/socketio.js.map +1 -0
  61. package/dist/channels/transports/streamable-http.d.ts +107 -0
  62. package/dist/channels/transports/streamable-http.d.ts.map +1 -0
  63. package/dist/channels/transports/streamable-http.js +353 -0
  64. package/dist/channels/transports/streamable-http.js.map +1 -0
  65. package/dist/channels/transports/websocket.d.ts +117 -0
  66. package/dist/channels/transports/websocket.d.ts.map +1 -0
  67. package/dist/channels/transports/websocket.js +416 -0
  68. package/dist/channels/transports/websocket.js.map +1 -0
  69. package/dist/com/index.d.ts +29 -0
  70. package/dist/com/index.d.ts.map +1 -0
  71. package/dist/com/index.js +29 -0
  72. package/dist/com/index.js.map +1 -0
  73. package/dist/com/object-model.d.ts +634 -0
  74. package/dist/com/object-model.d.ts.map +1 -0
  75. package/dist/com/object-model.js +963 -0
  76. package/dist/com/object-model.js.map +1 -0
  77. package/dist/com/types.d.ts +192 -0
  78. package/dist/com/types.d.ts.map +1 -0
  79. package/dist/com/types.js +1 -0
  80. package/dist/com/types.js.map +1 -0
  81. package/dist/compiler/collector.d.ts +16 -0
  82. package/dist/compiler/collector.d.ts.map +1 -0
  83. package/dist/compiler/collector.js +388 -0
  84. package/dist/compiler/collector.js.map +1 -0
  85. package/dist/compiler/content-block-registry.d.ts +11 -0
  86. package/dist/compiler/content-block-registry.d.ts.map +1 -0
  87. package/dist/compiler/content-block-registry.js +312 -0
  88. package/dist/compiler/content-block-registry.js.map +1 -0
  89. package/dist/compiler/extractors.d.ts +68 -0
  90. package/dist/compiler/extractors.d.ts.map +1 -0
  91. package/dist/compiler/extractors.js +547 -0
  92. package/dist/compiler/extractors.js.map +1 -0
  93. package/dist/compiler/fiber-compiler.d.ts +203 -0
  94. package/dist/compiler/fiber-compiler.d.ts.map +1 -0
  95. package/dist/compiler/fiber-compiler.js +498 -0
  96. package/dist/compiler/fiber-compiler.js.map +1 -0
  97. package/dist/compiler/fiber.d.ts +61 -0
  98. package/dist/compiler/fiber.d.ts.map +1 -0
  99. package/dist/compiler/fiber.js +244 -0
  100. package/dist/compiler/fiber.js.map +1 -0
  101. package/dist/compiler/index.d.ts +18 -0
  102. package/dist/compiler/index.d.ts.map +1 -0
  103. package/dist/compiler/index.js +38 -0
  104. package/dist/compiler/index.js.map +1 -0
  105. package/dist/compiler/scheduler.d.ts +95 -0
  106. package/dist/compiler/scheduler.d.ts.map +1 -0
  107. package/dist/compiler/scheduler.js +138 -0
  108. package/dist/compiler/scheduler.js.map +1 -0
  109. package/dist/compiler/structure-renderer.d.ts +42 -0
  110. package/dist/compiler/structure-renderer.d.ts.map +1 -0
  111. package/dist/compiler/structure-renderer.js +189 -0
  112. package/dist/compiler/structure-renderer.js.map +1 -0
  113. package/dist/compiler/types.d.ts +96 -0
  114. package/dist/compiler/types.d.ts.map +1 -0
  115. package/dist/compiler/types.js +19 -0
  116. package/dist/compiler/types.js.map +1 -0
  117. package/dist/component/component-hooks.d.ts +68 -0
  118. package/dist/component/component-hooks.d.ts.map +1 -0
  119. package/dist/component/component-hooks.js +112 -0
  120. package/dist/component/component-hooks.js.map +1 -0
  121. package/dist/component/component.d.ts +314 -0
  122. package/dist/component/component.d.ts.map +1 -0
  123. package/dist/component/component.js +64 -0
  124. package/dist/component/component.js.map +1 -0
  125. package/dist/component/index.d.ts +47 -0
  126. package/dist/component/index.d.ts.map +1 -0
  127. package/dist/component/index.js +47 -0
  128. package/dist/component/index.js.map +1 -0
  129. package/dist/component/tentickle-component.d.ts +185 -0
  130. package/dist/component/tentickle-component.d.ts.map +1 -0
  131. package/dist/component/tentickle-component.js +182 -0
  132. package/dist/component/tentickle-component.js.map +1 -0
  133. package/dist/content/index.d.ts +12 -0
  134. package/dist/content/index.d.ts.map +1 -0
  135. package/dist/content/index.js +17 -0
  136. package/dist/content/index.js.map +1 -0
  137. package/dist/context/index.d.ts +51 -0
  138. package/dist/context/index.d.ts.map +1 -0
  139. package/dist/context/index.js +69 -0
  140. package/dist/context/index.js.map +1 -0
  141. package/dist/core/channel-helpers.d.ts +31 -0
  142. package/dist/core/channel-helpers.d.ts.map +1 -0
  143. package/dist/core/channel-helpers.js +62 -0
  144. package/dist/core/channel-helpers.js.map +1 -0
  145. package/dist/core/channel.d.ts +164 -0
  146. package/dist/core/channel.d.ts.map +1 -0
  147. package/dist/core/channel.js +199 -0
  148. package/dist/core/channel.js.map +1 -0
  149. package/dist/core/context.d.ts +412 -0
  150. package/dist/core/context.d.ts.map +1 -0
  151. package/dist/core/context.js +290 -0
  152. package/dist/core/context.js.map +1 -0
  153. package/dist/core/event-buffer.d.ts +212 -0
  154. package/dist/core/event-buffer.d.ts.map +1 -0
  155. package/dist/core/event-buffer.js +346 -0
  156. package/dist/core/event-buffer.js.map +1 -0
  157. package/dist/core/execution-helpers.d.ts +179 -0
  158. package/dist/core/execution-helpers.d.ts.map +1 -0
  159. package/dist/core/execution-helpers.js +212 -0
  160. package/dist/core/execution-helpers.js.map +1 -0
  161. package/dist/core/execution-tracker.d.ts +53 -0
  162. package/dist/core/execution-tracker.d.ts.map +1 -0
  163. package/dist/core/execution-tracker.js +309 -0
  164. package/dist/core/execution-tracker.js.map +1 -0
  165. package/dist/core/index.d.ts +58 -0
  166. package/dist/core/index.d.ts.map +1 -0
  167. package/dist/core/index.js +58 -0
  168. package/dist/core/index.js.map +1 -0
  169. package/dist/core/logger.d.ts +341 -0
  170. package/dist/core/logger.d.ts.map +1 -0
  171. package/dist/core/logger.js +346 -0
  172. package/dist/core/logger.js.map +1 -0
  173. package/dist/core/metrics-helpers.d.ts +40 -0
  174. package/dist/core/metrics-helpers.d.ts.map +1 -0
  175. package/dist/core/metrics-helpers.js +72 -0
  176. package/dist/core/metrics-helpers.js.map +1 -0
  177. package/dist/core/otel-provider.d.ts +54 -0
  178. package/dist/core/otel-provider.d.ts.map +1 -0
  179. package/dist/core/otel-provider.js +107 -0
  180. package/dist/core/otel-provider.js.map +1 -0
  181. package/dist/core/procedure-graph.d.ts +136 -0
  182. package/dist/core/procedure-graph.d.ts.map +1 -0
  183. package/dist/core/procedure-graph.js +272 -0
  184. package/dist/core/procedure-graph.js.map +1 -0
  185. package/dist/core/procedure.d.ts +755 -0
  186. package/dist/core/procedure.d.ts.map +1 -0
  187. package/dist/core/procedure.js +899 -0
  188. package/dist/core/procedure.js.map +1 -0
  189. package/dist/core/stream.d.ts +106 -0
  190. package/dist/core/stream.d.ts.map +1 -0
  191. package/dist/core/stream.js +186 -0
  192. package/dist/core/stream.js.map +1 -0
  193. package/dist/core/telemetry.d.ts +182 -0
  194. package/dist/core/telemetry.d.ts.map +1 -0
  195. package/dist/core/telemetry.js +124 -0
  196. package/dist/core/telemetry.js.map +1 -0
  197. package/dist/engine/client-tool-coordinator.d.ts +50 -0
  198. package/dist/engine/client-tool-coordinator.d.ts.map +1 -0
  199. package/dist/engine/client-tool-coordinator.js +121 -0
  200. package/dist/engine/client-tool-coordinator.js.map +1 -0
  201. package/dist/engine/engine-events.d.ts +117 -0
  202. package/dist/engine/engine-events.d.ts.map +1 -0
  203. package/dist/engine/engine-events.js +178 -0
  204. package/dist/engine/engine-events.js.map +1 -0
  205. package/dist/engine/engine-response.d.ts +48 -0
  206. package/dist/engine/engine-response.d.ts.map +1 -0
  207. package/dist/engine/engine-response.js +2 -0
  208. package/dist/engine/engine-response.js.map +1 -0
  209. package/dist/engine/execution-graph.d.ts +104 -0
  210. package/dist/engine/execution-graph.d.ts.map +1 -0
  211. package/dist/engine/execution-graph.js +257 -0
  212. package/dist/engine/execution-graph.js.map +1 -0
  213. package/dist/engine/execution-handle.d.ts +212 -0
  214. package/dist/engine/execution-handle.d.ts.map +1 -0
  215. package/dist/engine/execution-handle.js +602 -0
  216. package/dist/engine/execution-handle.js.map +1 -0
  217. package/dist/engine/execution-types.d.ts +248 -0
  218. package/dist/engine/execution-types.d.ts.map +1 -0
  219. package/dist/engine/execution-types.js +23 -0
  220. package/dist/engine/execution-types.js.map +1 -0
  221. package/dist/engine/index.d.ts +21 -0
  222. package/dist/engine/index.d.ts.map +1 -0
  223. package/dist/engine/index.js +23 -0
  224. package/dist/engine/index.js.map +1 -0
  225. package/dist/engine/tool-confirmation-coordinator.d.ts +74 -0
  226. package/dist/engine/tool-confirmation-coordinator.d.ts.map +1 -0
  227. package/dist/engine/tool-confirmation-coordinator.js +137 -0
  228. package/dist/engine/tool-confirmation-coordinator.js.map +1 -0
  229. package/dist/engine/tool-executor.d.ts +127 -0
  230. package/dist/engine/tool-executor.d.ts.map +1 -0
  231. package/dist/engine/tool-executor.js +363 -0
  232. package/dist/engine/tool-executor.js.map +1 -0
  233. package/dist/hibernation/index.d.ts +126 -0
  234. package/dist/hibernation/index.d.ts.map +1 -0
  235. package/dist/hibernation/index.js +127 -0
  236. package/dist/hibernation/index.js.map +1 -0
  237. package/dist/hooks/base-hook-registry.d.ts +41 -0
  238. package/dist/hooks/base-hook-registry.d.ts.map +1 -0
  239. package/dist/hooks/base-hook-registry.js +76 -0
  240. package/dist/hooks/base-hook-registry.js.map +1 -0
  241. package/dist/hooks/com-state.d.ts +40 -0
  242. package/dist/hooks/com-state.d.ts.map +1 -0
  243. package/dist/hooks/com-state.js +90 -0
  244. package/dist/hooks/com-state.js.map +1 -0
  245. package/dist/hooks/context-info.d.ts +139 -0
  246. package/dist/hooks/context-info.d.ts.map +1 -0
  247. package/dist/hooks/context-info.js +115 -0
  248. package/dist/hooks/context-info.js.map +1 -0
  249. package/dist/hooks/context-internal.d.ts +21 -0
  250. package/dist/hooks/context-internal.d.ts.map +1 -0
  251. package/dist/hooks/context-internal.js +20 -0
  252. package/dist/hooks/context-internal.js.map +1 -0
  253. package/dist/hooks/context.d.ts +64 -0
  254. package/dist/hooks/context.d.ts.map +1 -0
  255. package/dist/hooks/context.js +83 -0
  256. package/dist/hooks/context.js.map +1 -0
  257. package/dist/hooks/data.d.ts +33 -0
  258. package/dist/hooks/data.d.ts.map +1 -0
  259. package/dist/hooks/data.js +84 -0
  260. package/dist/hooks/data.js.map +1 -0
  261. package/dist/hooks/formatter-context.d.ts +34 -0
  262. package/dist/hooks/formatter-context.d.ts.map +1 -0
  263. package/dist/hooks/formatter-context.js +34 -0
  264. package/dist/hooks/formatter-context.js.map +1 -0
  265. package/dist/hooks/hook-registry.d.ts +45 -0
  266. package/dist/hooks/hook-registry.d.ts.map +1 -0
  267. package/dist/hooks/hook-registry.js +109 -0
  268. package/dist/hooks/hook-registry.js.map +1 -0
  269. package/dist/hooks/index.d.ts +20 -0
  270. package/dist/hooks/index.d.ts.map +1 -0
  271. package/dist/hooks/index.js +47 -0
  272. package/dist/hooks/index.js.map +1 -0
  273. package/dist/hooks/knob.d.ts +87 -0
  274. package/dist/hooks/knob.d.ts.map +1 -0
  275. package/dist/hooks/knob.js +129 -0
  276. package/dist/hooks/knob.js.map +1 -0
  277. package/dist/hooks/knobs-component.d.ts +70 -0
  278. package/dist/hooks/knobs-component.d.ts.map +1 -0
  279. package/dist/hooks/knobs-component.js +300 -0
  280. package/dist/hooks/knobs-component.js.map +1 -0
  281. package/dist/hooks/lifecycle.d.ts +158 -0
  282. package/dist/hooks/lifecycle.d.ts.map +1 -0
  283. package/dist/hooks/lifecycle.js +217 -0
  284. package/dist/hooks/lifecycle.js.map +1 -0
  285. package/dist/hooks/message-context.d.ts +101 -0
  286. package/dist/hooks/message-context.d.ts.map +1 -0
  287. package/dist/hooks/message-context.js +145 -0
  288. package/dist/hooks/message-context.js.map +1 -0
  289. package/dist/hooks/policy-context.d.ts.map +1 -0
  290. package/dist/hooks/runtime-context.d.ts +122 -0
  291. package/dist/hooks/runtime-context.d.ts.map +1 -0
  292. package/dist/hooks/runtime-context.js +149 -0
  293. package/dist/hooks/runtime-context.js.map +1 -0
  294. package/dist/hooks/signal.d.ts +267 -0
  295. package/dist/hooks/signal.d.ts.map +1 -0
  296. package/dist/hooks/signal.js +825 -0
  297. package/dist/hooks/signal.js.map +1 -0
  298. package/dist/hooks/types.d.ts +179 -0
  299. package/dist/hooks/types.d.ts.map +1 -0
  300. package/dist/hooks/types.js +5 -0
  301. package/dist/hooks/types.js.map +1 -0
  302. package/dist/index.d.ts +20 -0
  303. package/dist/index.d.ts.map +1 -0
  304. package/dist/index.js +50 -0
  305. package/dist/index.js.map +1 -0
  306. package/dist/jsx/components/agent.d.ts +64 -0
  307. package/dist/jsx/components/agent.d.ts.map +1 -0
  308. package/dist/jsx/components/agent.js +80 -0
  309. package/dist/jsx/components/agent.js.map +1 -0
  310. package/dist/jsx/components/complete.d.ts +65 -0
  311. package/dist/jsx/components/complete.d.ts.map +1 -0
  312. package/dist/jsx/components/complete.js +64 -0
  313. package/dist/jsx/components/complete.js.map +1 -0
  314. package/dist/jsx/components/content.d.ts +98 -0
  315. package/dist/jsx/components/content.d.ts.map +1 -0
  316. package/dist/jsx/components/content.js +51 -0
  317. package/dist/jsx/components/content.js.map +1 -0
  318. package/dist/jsx/components/harness.d.ts +118 -0
  319. package/dist/jsx/components/harness.d.ts.map +1 -0
  320. package/dist/jsx/components/harness.js +117 -0
  321. package/dist/jsx/components/harness.js.map +1 -0
  322. package/dist/jsx/components/index.d.ts +11 -0
  323. package/dist/jsx/components/index.d.ts.map +1 -0
  324. package/dist/jsx/components/index.js +11 -0
  325. package/dist/jsx/components/index.js.map +1 -0
  326. package/dist/jsx/components/markdown.d.ts +31 -0
  327. package/dist/jsx/components/markdown.d.ts.map +1 -0
  328. package/dist/jsx/components/markdown.js +17 -0
  329. package/dist/jsx/components/markdown.js.map +1 -0
  330. package/dist/jsx/components/messages.d.ts +283 -0
  331. package/dist/jsx/components/messages.d.ts.map +1 -0
  332. package/dist/jsx/components/messages.js +257 -0
  333. package/dist/jsx/components/messages.js.map +1 -0
  334. package/dist/jsx/components/model.d.ts +94 -0
  335. package/dist/jsx/components/model.d.ts.map +1 -0
  336. package/dist/jsx/components/model.js +96 -0
  337. package/dist/jsx/components/model.js.map +1 -0
  338. package/dist/jsx/components/primitives.d.ts +117 -0
  339. package/dist/jsx/components/primitives.d.ts.map +1 -0
  340. package/dist/jsx/components/primitives.js +83 -0
  341. package/dist/jsx/components/primitives.js.map +1 -0
  342. package/dist/jsx/components/renderer.d.ts +24 -0
  343. package/dist/jsx/components/renderer.d.ts.map +1 -0
  344. package/dist/jsx/components/renderer.js +11 -0
  345. package/dist/jsx/components/renderer.js.map +1 -0
  346. package/dist/jsx/components/semantic.d.ts +155 -0
  347. package/dist/jsx/components/semantic.d.ts.map +1 -0
  348. package/dist/jsx/components/semantic.js +39 -0
  349. package/dist/jsx/components/semantic.js.map +1 -0
  350. package/dist/jsx/components/timeline.d.ts +157 -0
  351. package/dist/jsx/components/timeline.d.ts.map +1 -0
  352. package/dist/jsx/components/timeline.js +357 -0
  353. package/dist/jsx/components/timeline.js.map +1 -0
  354. package/dist/jsx/components/token-budget.d.ts +70 -0
  355. package/dist/jsx/components/token-budget.d.ts.map +1 -0
  356. package/dist/jsx/components/token-budget.js +135 -0
  357. package/dist/jsx/components/token-budget.js.map +1 -0
  358. package/dist/jsx/components/xml.d.ts +27 -0
  359. package/dist/jsx/components/xml.d.ts.map +1 -0
  360. package/dist/jsx/components/xml.js +17 -0
  361. package/dist/jsx/components/xml.js.map +1 -0
  362. package/dist/jsx/index.d.ts +58 -0
  363. package/dist/jsx/index.d.ts.map +1 -0
  364. package/dist/jsx/index.js +59 -0
  365. package/dist/jsx/index.js.map +1 -0
  366. package/dist/jsx/jsx-runtime.d.ts +370 -0
  367. package/dist/jsx/jsx-runtime.d.ts.map +1 -0
  368. package/dist/jsx/jsx-runtime.js +79 -0
  369. package/dist/jsx/jsx-runtime.js.map +1 -0
  370. package/dist/jsx/jsx-types.d.ts +23 -0
  371. package/dist/jsx/jsx-types.d.ts.map +1 -0
  372. package/dist/jsx/jsx-types.js +1 -0
  373. package/dist/jsx/jsx-types.js.map +1 -0
  374. package/dist/mcp/client.d.ts +46 -0
  375. package/dist/mcp/client.d.ts.map +1 -0
  376. package/dist/mcp/client.js +138 -0
  377. package/dist/mcp/client.js.map +1 -0
  378. package/dist/mcp/component.d.ts +95 -0
  379. package/dist/mcp/component.d.ts.map +1 -0
  380. package/dist/mcp/component.js +185 -0
  381. package/dist/mcp/component.js.map +1 -0
  382. package/dist/mcp/create-mcp-tool.d.ts +191 -0
  383. package/dist/mcp/create-mcp-tool.d.ts.map +1 -0
  384. package/dist/mcp/create-mcp-tool.js +228 -0
  385. package/dist/mcp/create-mcp-tool.js.map +1 -0
  386. package/dist/mcp/index.d.ts +49 -0
  387. package/dist/mcp/index.d.ts.map +1 -0
  388. package/dist/mcp/index.js +48 -0
  389. package/dist/mcp/index.js.map +1 -0
  390. package/dist/mcp/service.d.ts +39 -0
  391. package/dist/mcp/service.d.ts.map +1 -0
  392. package/dist/mcp/service.js +77 -0
  393. package/dist/mcp/service.js.map +1 -0
  394. package/dist/mcp/tool.d.ts +55 -0
  395. package/dist/mcp/tool.d.ts.map +1 -0
  396. package/dist/mcp/tool.js +119 -0
  397. package/dist/mcp/tool.js.map +1 -0
  398. package/dist/mcp/types.d.ts +72 -0
  399. package/dist/mcp/types.d.ts.map +1 -0
  400. package/dist/mcp/types.js +6 -0
  401. package/dist/mcp/types.js.map +1 -0
  402. package/dist/middleware/defaults.d.ts +9 -0
  403. package/dist/middleware/defaults.d.ts.map +1 -0
  404. package/dist/middleware/defaults.js +47 -0
  405. package/dist/middleware/defaults.js.map +1 -0
  406. package/dist/model/adapter-helpers.d.ts +161 -0
  407. package/dist/model/adapter-helpers.d.ts.map +1 -0
  408. package/dist/model/adapter-helpers.js +351 -0
  409. package/dist/model/adapter-helpers.js.map +1 -0
  410. package/dist/model/adapter.d.ts +399 -0
  411. package/dist/model/adapter.d.ts.map +1 -0
  412. package/dist/model/adapter.js +497 -0
  413. package/dist/model/adapter.js.map +1 -0
  414. package/dist/model/index.d.ts +54 -0
  415. package/dist/model/index.d.ts.map +1 -0
  416. package/dist/model/index.js +55 -0
  417. package/dist/model/index.js.map +1 -0
  418. package/dist/model/model-hooks.d.ts +45 -0
  419. package/dist/model/model-hooks.d.ts.map +1 -0
  420. package/dist/model/model-hooks.js +24 -0
  421. package/dist/model/model-hooks.js.map +1 -0
  422. package/dist/model/model.d.ts +302 -0
  423. package/dist/model/model.d.ts.map +1 -0
  424. package/dist/model/model.js +20 -0
  425. package/dist/model/model.js.map +1 -0
  426. package/dist/model/simple-adapter.d.ts +176 -0
  427. package/dist/model/simple-adapter.d.ts.map +1 -0
  428. package/dist/model/simple-adapter.js +264 -0
  429. package/dist/model/simple-adapter.js.map +1 -0
  430. package/dist/model/stream-accumulator.d.ts +284 -0
  431. package/dist/model/stream-accumulator.d.ts.map +1 -0
  432. package/dist/model/stream-accumulator.js +532 -0
  433. package/dist/model/stream-accumulator.js.map +1 -0
  434. package/dist/model/utils/index.d.ts +2 -0
  435. package/dist/model/utils/index.d.ts.map +1 -0
  436. package/dist/model/utils/index.js +2 -0
  437. package/dist/model/utils/index.js.map +1 -0
  438. package/dist/model/utils/language-model.d.ts +26 -0
  439. package/dist/model/utils/language-model.d.ts.map +1 -0
  440. package/dist/model/utils/language-model.js +706 -0
  441. package/dist/model/utils/language-model.js.map +1 -0
  442. package/dist/procedure/index.d.ts +20 -0
  443. package/dist/procedure/index.d.ts.map +1 -0
  444. package/dist/procedure/index.js +19 -0
  445. package/dist/procedure/index.js.map +1 -0
  446. package/dist/reconciler/devtools-bridge.d.ts +40 -0
  447. package/dist/reconciler/devtools-bridge.d.ts.map +1 -0
  448. package/dist/reconciler/devtools-bridge.js +79 -0
  449. package/dist/reconciler/devtools-bridge.js.map +1 -0
  450. package/dist/reconciler/host-config.d.ts +39 -0
  451. package/dist/reconciler/host-config.d.ts.map +1 -0
  452. package/dist/reconciler/host-config.js +195 -0
  453. package/dist/reconciler/host-config.js.map +1 -0
  454. package/dist/reconciler/index.d.ts +7 -0
  455. package/dist/reconciler/index.d.ts.map +1 -0
  456. package/dist/reconciler/index.js +7 -0
  457. package/dist/reconciler/index.js.map +1 -0
  458. package/dist/reconciler/reconciler.d.ts +47 -0
  459. package/dist/reconciler/reconciler.d.ts.map +1 -0
  460. package/dist/reconciler/reconciler.js +89 -0
  461. package/dist/reconciler/reconciler.js.map +1 -0
  462. package/dist/reconciler/types.d.ts +86 -0
  463. package/dist/reconciler/types.d.ts.map +1 -0
  464. package/dist/reconciler/types.js +37 -0
  465. package/dist/reconciler/types.js.map +1 -0
  466. package/dist/renderers/base.d.ts +98 -0
  467. package/dist/renderers/base.d.ts.map +1 -0
  468. package/dist/renderers/base.js +82 -0
  469. package/dist/renderers/base.js.map +1 -0
  470. package/dist/renderers/index.d.ts +31 -0
  471. package/dist/renderers/index.d.ts.map +1 -0
  472. package/dist/renderers/index.js +31 -0
  473. package/dist/renderers/index.js.map +1 -0
  474. package/dist/renderers/markdown.d.ts +48 -0
  475. package/dist/renderers/markdown.d.ts.map +1 -0
  476. package/dist/renderers/markdown.js +432 -0
  477. package/dist/renderers/markdown.js.map +1 -0
  478. package/dist/renderers/types.d.ts +7 -0
  479. package/dist/renderers/types.d.ts.map +1 -0
  480. package/dist/renderers/types.js +7 -0
  481. package/dist/renderers/types.js.map +1 -0
  482. package/dist/renderers/xml.d.ts +49 -0
  483. package/dist/renderers/xml.d.ts.map +1 -0
  484. package/dist/renderers/xml.js +444 -0
  485. package/dist/renderers/xml.js.map +1 -0
  486. package/dist/state/boundary.d.ts +347 -0
  487. package/dist/state/boundary.d.ts.map +1 -0
  488. package/dist/state/boundary.js +341 -0
  489. package/dist/state/boundary.js.map +1 -0
  490. package/dist/state/context.d.ts +138 -0
  491. package/dist/state/context.d.ts.map +1 -0
  492. package/dist/state/context.js +139 -0
  493. package/dist/state/context.js.map +1 -0
  494. package/dist/state/hooks.d.ts +798 -0
  495. package/dist/state/hooks.d.ts.map +1 -0
  496. package/dist/state/hooks.js +1437 -0
  497. package/dist/state/hooks.js.map +1 -0
  498. package/dist/state/index.d.ts +72 -0
  499. package/dist/state/index.d.ts.map +1 -0
  500. package/dist/state/index.js +73 -0
  501. package/dist/state/index.js.map +1 -0
  502. package/dist/state/signal.d.ts +223 -0
  503. package/dist/state/signal.d.ts.map +1 -0
  504. package/dist/state/signal.js +699 -0
  505. package/dist/state/signal.js.map +1 -0
  506. package/dist/state/use-state.d.ts +210 -0
  507. package/dist/state/use-state.d.ts.map +1 -0
  508. package/dist/state/use-state.js +327 -0
  509. package/dist/state/use-state.js.map +1 -0
  510. package/dist/tentickle-instance.d.ts +285 -0
  511. package/dist/tentickle-instance.d.ts.map +1 -0
  512. package/dist/tentickle-instance.js +700 -0
  513. package/dist/tentickle-instance.js.map +1 -0
  514. package/dist/testing/act.d.ts +59 -0
  515. package/dist/testing/act.d.ts.map +1 -0
  516. package/dist/testing/act.js +92 -0
  517. package/dist/testing/act.js.map +1 -0
  518. package/dist/testing/async-helpers.d.ts +99 -0
  519. package/dist/testing/async-helpers.d.ts.map +1 -0
  520. package/dist/testing/async-helpers.js +193 -0
  521. package/dist/testing/async-helpers.js.map +1 -0
  522. package/dist/testing/compile-agent.d.ts +101 -0
  523. package/dist/testing/compile-agent.d.ts.map +1 -0
  524. package/dist/testing/compile-agent.js +136 -0
  525. package/dist/testing/compile-agent.js.map +1 -0
  526. package/dist/testing/index.d.ts +57 -0
  527. package/dist/testing/index.d.ts.map +1 -0
  528. package/dist/testing/index.js +59 -0
  529. package/dist/testing/index.js.map +1 -0
  530. package/dist/testing/mock-app.d.ts +163 -0
  531. package/dist/testing/mock-app.d.ts.map +1 -0
  532. package/dist/testing/mock-app.js +393 -0
  533. package/dist/testing/mock-app.js.map +1 -0
  534. package/dist/testing/mocks.d.ts +142 -0
  535. package/dist/testing/mocks.d.ts.map +1 -0
  536. package/dist/testing/mocks.js +191 -0
  537. package/dist/testing/mocks.js.map +1 -0
  538. package/dist/testing/render-agent.d.ts +146 -0
  539. package/dist/testing/render-agent.d.ts.map +1 -0
  540. package/dist/testing/render-agent.js +200 -0
  541. package/dist/testing/render-agent.js.map +1 -0
  542. package/dist/testing/test-adapter.d.ts +157 -0
  543. package/dist/testing/test-adapter.d.ts.map +1 -0
  544. package/dist/testing/test-adapter.js +297 -0
  545. package/dist/testing/test-adapter.js.map +1 -0
  546. package/dist/testing/test-model.d.ts +132 -0
  547. package/dist/testing/test-model.d.ts.map +1 -0
  548. package/dist/testing/test-model.js +260 -0
  549. package/dist/testing/test-model.js.map +1 -0
  550. package/dist/tool/index.d.ts +61 -0
  551. package/dist/tool/index.d.ts.map +1 -0
  552. package/dist/tool/index.js +63 -0
  553. package/dist/tool/index.js.map +1 -0
  554. package/dist/tool/tool-hooks.d.ts +45 -0
  555. package/dist/tool/tool-hooks.d.ts.map +1 -0
  556. package/dist/tool/tool-hooks.js +35 -0
  557. package/dist/tool/tool-hooks.js.map +1 -0
  558. package/dist/tool/tool.d.ts +403 -0
  559. package/dist/tool/tool.d.ts.map +1 -0
  560. package/dist/tool/tool.js +176 -0
  561. package/dist/tool/tool.js.map +1 -0
  562. package/dist/types.d.ts +442 -0
  563. package/dist/types.d.ts.map +1 -0
  564. package/dist/types.js +97 -0
  565. package/dist/types.js.map +1 -0
  566. package/dist/utils/abort-utils.d.ts +5 -0
  567. package/dist/utils/abort-utils.d.ts.map +1 -0
  568. package/dist/utils/abort-utils.js +50 -0
  569. package/dist/utils/abort-utils.js.map +1 -0
  570. package/dist/utils/classify-error.d.ts +19 -0
  571. package/dist/utils/classify-error.d.ts.map +1 -0
  572. package/dist/utils/classify-error.js +77 -0
  573. package/dist/utils/classify-error.js.map +1 -0
  574. package/dist/utils/index.d.ts +21 -0
  575. package/dist/utils/index.d.ts.map +1 -0
  576. package/dist/utils/index.js +21 -0
  577. package/dist/utils/index.js.map +1 -0
  578. package/dist/utils/normalization.d.ts +6 -0
  579. package/dist/utils/normalization.d.ts.map +1 -0
  580. package/dist/utils/normalization.js +103 -0
  581. package/dist/utils/normalization.js.map +1 -0
  582. package/dist/utils/registry.d.ts +15 -0
  583. package/dist/utils/registry.d.ts.map +1 -0
  584. package/dist/utils/registry.js +28 -0
  585. package/dist/utils/registry.js.map +1 -0
  586. package/dist/utils/schema.d.ts +7 -0
  587. package/dist/utils/schema.d.ts.map +1 -0
  588. package/dist/utils/schema.js +13 -0
  589. package/dist/utils/schema.js.map +1 -0
  590. package/dist/utils/token-estimate.d.ts +87 -0
  591. package/dist/utils/token-estimate.d.ts.map +1 -0
  592. package/dist/utils/token-estimate.js +199 -0
  593. package/dist/utils/token-estimate.js.map +1 -0
  594. package/dist/v2/reconciler/host-config.d.ts +31 -0
  595. package/dist/v2/reconciler/host-config.d.ts.map +1 -0
  596. package/dist/v2/reconciler/host-config.js +197 -0
  597. package/dist/v2/reconciler/host-config.js.map +1 -0
  598. package/dist/v2/reconciler/index.d.ts +7 -0
  599. package/dist/v2/reconciler/index.d.ts.map +1 -0
  600. package/dist/v2/reconciler/index.js +7 -0
  601. package/dist/v2/reconciler/index.js.map +1 -0
  602. package/dist/v2/reconciler/reconciler.d.ts +39 -0
  603. package/dist/v2/reconciler/reconciler.d.ts.map +1 -0
  604. package/dist/v2/reconciler/reconciler.js +54 -0
  605. package/dist/v2/reconciler/reconciler.js.map +1 -0
  606. package/dist/v2/reconciler/types.d.ts +64 -0
  607. package/dist/v2/reconciler/types.d.ts.map +1 -0
  608. package/dist/v2/reconciler/types.js +20 -0
  609. package/dist/v2/reconciler/types.js.map +1 -0
  610. package/dist/v2/renderers/index.d.ts +7 -0
  611. package/dist/v2/renderers/index.d.ts.map +1 -0
  612. package/dist/v2/renderers/index.js +7 -0
  613. package/dist/v2/renderers/index.js.map +1 -0
  614. package/dist/v2/renderers/markdown.d.ts +16 -0
  615. package/dist/v2/renderers/markdown.d.ts.map +1 -0
  616. package/dist/v2/renderers/markdown.js +65 -0
  617. package/dist/v2/renderers/markdown.js.map +1 -0
  618. package/dist/v2/renderers/types.d.ts +26 -0
  619. package/dist/v2/renderers/types.d.ts.map +1 -0
  620. package/dist/v2/renderers/types.js +6 -0
  621. package/dist/v2/renderers/types.js.map +1 -0
  622. package/dist/v2/renderers/xml.d.ts +17 -0
  623. package/dist/v2/renderers/xml.d.ts.map +1 -0
  624. package/dist/v2/renderers/xml.js +73 -0
  625. package/dist/v2/renderers/xml.js.map +1 -0
  626. package/package.json +49 -0
@@ -0,0 +1,2131 @@
1
+ /**
2
+ * Session - The Execution Unit
3
+ *
4
+ * A session holds component state (fiber tree) across ticks.
5
+ * Each tick: compile JSX -> call model -> execute tools -> decide continue?
6
+ *
7
+ * Design principles:
8
+ * - Single class, no intermediate layers
9
+ * - Minimal state - just what's needed for execution
10
+ * - render() is a Procedure returning SessionExecutionHandle (AsyncIterable)
11
+ * - Clean, elegant code over feature completeness
12
+ *
13
+ * @module agentick/app/session
14
+ */
15
+ import { EventEmitter } from "node:events";
16
+ import { randomUUID } from "node:crypto";
17
+ import { Context, createProcedure, Channel, EventBuffer, ExecutionHandleBrand, Logger, } from "@agentick/kernel";
18
+ import { FiberCompiler, StructureRenderer, ReconciliationScheduler, } from "../compiler";
19
+ import { COM } from "../com/object-model";
20
+ import { MarkdownRenderer } from "../renderers/index";
21
+ import { ToolExecutor } from "../engine/tool-executor";
22
+ import { AbortError } from "../utils/abort-utils";
23
+ import { jsx } from "../jsx/jsx-runtime";
24
+ import { devToolsEmitter, forwardToDevTools, FrameworkChannels, getEffectiveModelInfo, getContextUtilization, } from "@agentick/shared";
25
+ import { computeTokenSummary } from "../utils/token-estimate";
26
+ import React from "react";
27
+ /**
28
+ * Get session context from the current ALS context.
29
+ */
30
+ function getSessionContext() {
31
+ const ctx = Context.tryGet();
32
+ return {
33
+ sessionId: ctx?.sessionId,
34
+ rootComponent: ctx?.rootComponent,
35
+ devToolsEnabled: ctx?.devToolsEnabled,
36
+ };
37
+ }
38
+ /**
39
+ * Ensure a message has an ID. If no ID exists, generate one.
40
+ * This is critical for deduplication in reactive clients.
41
+ */
42
+ function ensureMessageId(message) {
43
+ if (message.id)
44
+ return message;
45
+ return { ...message, id: randomUUID() };
46
+ }
47
+ /**
48
+ * Session implementation.
49
+ *
50
+ * A session manages the execution lifecycle: compile JSX, call model, execute tools.
51
+ * Component state (hooks, signals) persists across ticks within a session.
52
+ */
53
+ export class SessionImpl extends EventEmitter {
54
+ id;
55
+ log = Logger.for("SessionImpl");
56
+ // Core execution state
57
+ _status = "idle";
58
+ _tick = 1;
59
+ _isAborted = false;
60
+ _currentExecutionId = null;
61
+ // Hydration state (pending fiber tree data to restore)
62
+ _pendingHydrationData = null;
63
+ // Compilation infrastructure (no intermediate layer)
64
+ compiler = null;
65
+ ctx = null;
66
+ structureRenderer = null;
67
+ scheduler = null;
68
+ // State that persists across ticks
69
+ _previousOutput = null;
70
+ _currentOutput = null;
71
+ // Track timeline sent to model (for combining with response in complete())
72
+ _lastSentTimeline = [];
73
+ // Estimated context tokens from last compilation (pre-model-call)
74
+ _estimatedContextTokens;
75
+ // Track last published timeline length for delta publishing
76
+ _lastPublishedTimelineLength = 0;
77
+ // Message queue
78
+ _queuedMessages = [];
79
+ // Abort handling
80
+ sessionAbortController;
81
+ executionAbortController = null;
82
+ executionAbortCleanup = [];
83
+ // Event streaming
84
+ _eventQueue = [];
85
+ _eventResolvers = [];
86
+ _executionComplete = false;
87
+ _sequence = 0; // Monotonically increasing sequence number for durable streams
88
+ // Usage tracking
89
+ _totalUsage = {
90
+ inputTokens: 0,
91
+ outputTokens: 0,
92
+ totalTokens: 0,
93
+ ticks: 0,
94
+ };
95
+ // Last model output tracking
96
+ _lastModelOutput = null;
97
+ // Recording support
98
+ _recording = null;
99
+ _recordingMode = null;
100
+ _recordingStartedAt = null;
101
+ _snapshots = [];
102
+ // Configuration
103
+ Component;
104
+ appOptions;
105
+ sessionOptions;
106
+ // Last props for hot-update support
107
+ _lastProps = null;
108
+ // Captured context from session creation
109
+ _capturedContext;
110
+ // Channels for pub/sub communication
111
+ _channels = new Map();
112
+ // Current execution handle (for concurrent send idempotency)
113
+ _currentHandle = null;
114
+ _currentResultResolve = null;
115
+ _currentResultReject = null;
116
+ // Hibernate callback (set by App when registering session)
117
+ _hibernateCallback = null;
118
+ // Spawn hierarchy
119
+ _parent = null;
120
+ _children = [];
121
+ _spawnDepth = 0;
122
+ static MAX_SPAWN_DEPTH = 10;
123
+ constructor(Component, appOptions, sessionOptions = {}) {
124
+ super();
125
+ this.id = sessionOptions.sessionId ?? randomUUID();
126
+ this.Component = Component;
127
+ this.appOptions = appOptions;
128
+ this.sessionOptions = sessionOptions;
129
+ // Capture ALS context at session creation time
130
+ // Include Agentick middleware registry if available (for procedure middleware support)
131
+ const currentContext = Context.tryGet();
132
+ const agentickInstance = appOptions
133
+ ._agentickInstance;
134
+ if (agentickInstance && currentContext) {
135
+ this._capturedContext = { ...currentContext, middleware: agentickInstance };
136
+ }
137
+ else if (agentickInstance && !currentContext) {
138
+ this._capturedContext = Context.create({ middleware: agentickInstance });
139
+ }
140
+ else {
141
+ this._capturedContext = currentContext;
142
+ }
143
+ // Create session abort controller linked to external signals
144
+ this.sessionAbortController = new AbortController();
145
+ const externalSignal = sessionOptions.signal ?? appOptions.signal;
146
+ if (externalSignal) {
147
+ if (externalSignal.aborted) {
148
+ this.sessionAbortController.abort(externalSignal.reason);
149
+ }
150
+ else {
151
+ externalSignal.addEventListener("abort", () => {
152
+ this.sessionAbortController.abort(externalSignal.reason);
153
+ });
154
+ }
155
+ }
156
+ // Hydrate from snapshot if provided
157
+ if (sessionOptions.snapshot) {
158
+ this.hydrate(sessionOptions.snapshot);
159
+ }
160
+ // Seed initial timeline if provided
161
+ if (sessionOptions.initialTimeline?.length) {
162
+ this._previousOutput = {
163
+ timeline: sessionOptions.initialTimeline,
164
+ system: [],
165
+ ephemeral: [],
166
+ sections: {},
167
+ tools: [],
168
+ metadata: {},
169
+ };
170
+ }
171
+ // Start recording if enabled via options
172
+ if (sessionOptions.recording) {
173
+ this.startRecording(sessionOptions.recording);
174
+ }
175
+ // Initialize procedures
176
+ this.initProcedures();
177
+ }
178
+ // ════════════════════════════════════════════════════════════════════════
179
+ // Public Properties
180
+ // ════════════════════════════════════════════════════════════════════════
181
+ get status() {
182
+ return this._status;
183
+ }
184
+ get currentTick() {
185
+ return this._tick;
186
+ }
187
+ get isAborted() {
188
+ return this._isAborted;
189
+ }
190
+ get parent() {
191
+ return this._parent;
192
+ }
193
+ get children() {
194
+ return this._children;
195
+ }
196
+ get queuedMessages() {
197
+ return this._queuedMessages;
198
+ }
199
+ /**
200
+ * Observable scheduler state for DevTools.
201
+ *
202
+ * Returns a Signal containing the scheduler's current state,
203
+ * including status, pending reasons, and reconciliation metrics.
204
+ *
205
+ * Returns null if the session hasn't been initialized yet.
206
+ *
207
+ * @example
208
+ * ```typescript
209
+ * // In DevTools
210
+ * effect(() => {
211
+ * const state = session.schedulerState?.();
212
+ * if (state) {
213
+ * console.log(`Status: ${state.status}, reconciles: ${state.reconcileCount}`);
214
+ * }
215
+ * });
216
+ * ```
217
+ */
218
+ get schedulerState() {
219
+ return this.scheduler?.getState() ?? null;
220
+ }
221
+ // ════════════════════════════════════════════════════════════════════════
222
+ // Queue Procedure
223
+ // ════════════════════════════════════════════════════════════════════════
224
+ queue;
225
+ send;
226
+ render;
227
+ spawn;
228
+ initProcedures() {
229
+ // Queue procedure - queues messages and notifies components
230
+ this.queue = createProcedure({
231
+ name: "session:queue",
232
+ metadata: { operation: "queue" },
233
+ handleFactory: false,
234
+ executionBoundary: false,
235
+ }, async (message) => {
236
+ if (this._status === "closed") {
237
+ throw new Error("Session is closed");
238
+ }
239
+ const messageWithId = ensureMessageId(message);
240
+ this._queuedMessages.push(messageWithId);
241
+ // Publish to messages channel for reactive updates
242
+ this.channel("messages").publish({
243
+ type: "message_queued",
244
+ channel: "messages",
245
+ payload: messageWithId,
246
+ });
247
+ // Notify components via useOnMessage hooks if compiler exists
248
+ if (this.compiler) {
249
+ const executionMessage = {
250
+ id: randomUUID(),
251
+ type: "message",
252
+ content: message,
253
+ timestamp: Date.now(),
254
+ };
255
+ const tickState = {
256
+ tick: this._tick,
257
+ stop: () => { },
258
+ queuedMessages: [],
259
+ };
260
+ await this.compiler.notifyOnMessage(executionMessage, tickState);
261
+ }
262
+ });
263
+ // Send procedure - queues messages + delegates to render
264
+ this.send = createProcedure({
265
+ name: "session:send",
266
+ metadata: { operation: "send" },
267
+ handleFactory: false,
268
+ executionBoundary: false,
269
+ }, async (input) => {
270
+ if (this._status === "closed") {
271
+ throw new Error("Session is closed");
272
+ }
273
+ const { messages = [], props, metadata, maxTicks, signal } = input;
274
+ // Apply metadata to messages
275
+ const allMessages = messages.map((m) => (metadata ? { ...m, metadata } : m));
276
+ // Update props if provided
277
+ if (props) {
278
+ this._lastProps = props;
279
+ }
280
+ // Queue messages synchronously and notify components
281
+ for (const msg of allMessages) {
282
+ const msgWithId = ensureMessageId(msg);
283
+ this._queuedMessages.push(msgWithId);
284
+ this.channel("messages").publish({
285
+ type: "message_queued",
286
+ channel: "messages",
287
+ payload: msgWithId,
288
+ });
289
+ // Notify components via useOnMessage hooks if compiler exists
290
+ if (this.compiler) {
291
+ const executionMessage = {
292
+ id: randomUUID(),
293
+ type: "message",
294
+ content: msgWithId,
295
+ timestamp: Date.now(),
296
+ };
297
+ const tickState = {
298
+ tick: this._tick,
299
+ stop: () => { },
300
+ queuedMessages: [],
301
+ };
302
+ // Fire and forget - don't await to keep send() fast
303
+ this.compiler.notifyOnMessage(executionMessage, tickState).catch(() => { });
304
+ }
305
+ }
306
+ // Build execution options from input
307
+ const executionOptions = {};
308
+ if (maxTicks !== undefined)
309
+ executionOptions.maxTicks = maxTicks;
310
+ if (signal)
311
+ executionOptions.signal = signal;
312
+ // If already running, return the existing handle (concurrent send idempotency)
313
+ if (this._status === "running" && this._currentHandle) {
314
+ this.addExecutionSignal(signal);
315
+ return this._currentHandle;
316
+ }
317
+ // If idle and we have something to do, start tick
318
+ if ((allMessages.length > 0 || props) && this._status === "idle") {
319
+ // Use last known props if available, otherwise default to empty props.
320
+ const tickProps = (this._lastProps ?? {});
321
+ return await this.render(tickProps, executionOptions);
322
+ }
323
+ // Nothing to do - create a handle that resolves immediately with empty result
324
+ return this.createEmptyHandle();
325
+ });
326
+ // Render procedure - creates and runs a tick execution
327
+ this.render = createProcedure({
328
+ name: "session:render",
329
+ metadata: { operation: "render" },
330
+ handleFactory: false,
331
+ executionBoundary: false,
332
+ }, (props, options) => {
333
+ if (this._status === "closed") {
334
+ throw new Error("Session is closed");
335
+ }
336
+ // Props is explicitly provided (even if empty object) - always run tick
337
+ // Only skip if props is undefined/null AND no queued messages
338
+ const propsProvided = props !== undefined && props !== null;
339
+ if (!propsProvided && this._queuedMessages.length === 0) {
340
+ return this.createEmptyHandle();
341
+ }
342
+ // Hot-update: if already running, update props and return existing handle
343
+ if (this._status === "running" && this._currentHandle) {
344
+ this._lastProps = props;
345
+ this.addExecutionSignal(options?.signal);
346
+ return this._currentHandle;
347
+ }
348
+ // Create new execution
349
+ this._lastProps = props;
350
+ const handle = this.createSessionHandle(props, options);
351
+ this._currentHandle = handle;
352
+ return handle;
353
+ });
354
+ // Spawn procedure - creates ephemeral child session
355
+ this.spawn = createProcedure({
356
+ name: "session:spawn",
357
+ metadata: { operation: "spawn" },
358
+ handleFactory: false,
359
+ executionBoundary: false,
360
+ }, async (component, input = {}) => {
361
+ if (this._status === "closed") {
362
+ throw new Error("Session is closed");
363
+ }
364
+ if (this._spawnDepth >= SessionImpl.MAX_SPAWN_DEPTH) {
365
+ throw new Error(`Maximum spawn depth (${SessionImpl.MAX_SPAWN_DEPTH}) exceeded`);
366
+ }
367
+ // 1. Resolve to ComponentFunction
368
+ const { Component, mergedProps } = this.resolveSpawnTarget(component, input);
369
+ // 2. Create child SessionImpl (ephemeral — NOT registered in App's registry)
370
+ // Whitelist structural fields only — lifecycle callbacks, session management,
371
+ // signal, and devTools are intentionally excluded. New AppOptions fields
372
+ // must be explicitly added here if children should inherit them.
373
+ const childAppOptions = {
374
+ model: this.appOptions.model,
375
+ tools: this.appOptions.tools,
376
+ mcpServers: this.appOptions.mcpServers,
377
+ maxTicks: this.appOptions.maxTicks,
378
+ inheritDefaults: this.appOptions.inheritDefaults,
379
+ };
380
+ const childOptions = {
381
+ signal: this.executionAbortController?.signal,
382
+ devTools: this.sessionOptions.devTools ?? this.appOptions.devTools,
383
+ };
384
+ const child = new SessionImpl(Component, childAppOptions, childOptions);
385
+ child._parent = this;
386
+ child._spawnDepth = this._spawnDepth + 1;
387
+ this._children.push(child);
388
+ // 3. Delegate to child.send()
389
+ const handle = await child.send({
390
+ ...input,
391
+ props: mergedProps,
392
+ });
393
+ // 4. Cleanup on completion
394
+ handle.result
395
+ .finally(() => {
396
+ this._children = this._children.filter((c) => c !== child);
397
+ child.close();
398
+ })
399
+ .catch(() => { });
400
+ return handle;
401
+ });
402
+ }
403
+ // ════════════════════════════════════════════════════════════════════════
404
+ // SessionExecutionHandle Creation
405
+ // ════════════════════════════════════════════════════════════════════════
406
+ /**
407
+ * Create a SessionExecutionHandle with explicit delegation.
408
+ * The handle is AsyncIterable (not PromiseLike — use `.result` for the final value).
409
+ */
410
+ createSessionHandle(props, options) {
411
+ const session = this;
412
+ const events = new EventEmitter();
413
+ const traceId = randomUUID();
414
+ // Create the result promise
415
+ let resolveResult;
416
+ let rejectResult;
417
+ const resultPromise = new Promise((resolve, reject) => {
418
+ resolveResult = resolve;
419
+ rejectResult = reject;
420
+ });
421
+ // Prevent unhandled rejections when abort happens before awaiting.
422
+ resultPromise.catch(() => { });
423
+ // Store resolvers for external completion
424
+ this._currentResultResolve = resolveResult;
425
+ this._currentResultReject = rejectResult;
426
+ // Track status
427
+ let status = "running";
428
+ // Event queue for async iteration
429
+ const eventQueue = [];
430
+ const eventResolvers = [];
431
+ let _iterationComplete = false;
432
+ this.startExecutionAbort(options?.signal);
433
+ // Create event buffer for dual consumption BEFORE starting execution
434
+ const eventBuffer = new EventBuffer();
435
+ // Forward events to event buffer
436
+ events.on("*", (event) => {
437
+ eventBuffer.push(event);
438
+ });
439
+ const pushEvent = (event) => {
440
+ if (eventResolvers.length > 0) {
441
+ const resolver = eventResolvers.shift();
442
+ resolver({ value: event, done: false });
443
+ }
444
+ else {
445
+ eventQueue.push(event);
446
+ }
447
+ events.emit("event", event);
448
+ events.emit("*", event);
449
+ };
450
+ const completeIteration = () => {
451
+ _iterationComplete = true;
452
+ eventBuffer.close();
453
+ for (const resolver of eventResolvers) {
454
+ resolver({ value: undefined, done: true });
455
+ }
456
+ eventResolvers.length = 0;
457
+ };
458
+ // Start execution asynchronously
459
+ (async () => {
460
+ try {
461
+ const result = await this.runWithContext(() => this.executeTick(props, options));
462
+ status = "completed";
463
+ // Call onComplete callback
464
+ try {
465
+ this.callbacks.onComplete?.(result);
466
+ }
467
+ catch {
468
+ // Callbacks should not throw
469
+ }
470
+ resolveResult(result);
471
+ completeIteration();
472
+ }
473
+ catch (error) {
474
+ status = "error";
475
+ const err = error instanceof Error ? error : new Error(String(error));
476
+ // Call onError callback
477
+ try {
478
+ this.callbacks.onError?.(err);
479
+ }
480
+ catch {
481
+ // Callbacks should not throw
482
+ }
483
+ eventBuffer.error(err);
484
+ rejectResult(err);
485
+ completeIteration();
486
+ }
487
+ finally {
488
+ this._currentHandle = null;
489
+ this._currentResultResolve = null;
490
+ this._currentResultReject = null;
491
+ this._isAborted = false;
492
+ this.finishExecutionAbort();
493
+ }
494
+ })();
495
+ // Create handle - use .result for final value, not PromiseLike
496
+ const handle = {
497
+ [ExecutionHandleBrand]: true,
498
+ // AsyncIterable delegation - delegates to EventBuffer for dual consumption
499
+ [Symbol.asyncIterator]() {
500
+ return eventBuffer[Symbol.asyncIterator]();
501
+ },
502
+ // ExecutionHandle properties
503
+ get status() {
504
+ return status;
505
+ },
506
+ get traceId() {
507
+ return traceId;
508
+ },
509
+ get events() {
510
+ return eventBuffer;
511
+ },
512
+ get result() {
513
+ return resultPromise;
514
+ },
515
+ abort(reason) {
516
+ if (status === "running") {
517
+ status = "aborted";
518
+ session._isAborted = true;
519
+ session.executionAbortController?.abort(reason ?? "Aborted via handle");
520
+ eventBuffer.close();
521
+ rejectResult(new AbortError(reason ?? "Aborted via handle", "ABORT_SIGNAL"));
522
+ completeIteration();
523
+ }
524
+ },
525
+ // Session-specific properties
526
+ get sessionId() {
527
+ return session.id;
528
+ },
529
+ get currentTick() {
530
+ return session._tick;
531
+ },
532
+ queueMessage(message) {
533
+ session._queuedMessages.push(message);
534
+ session.channel("messages").publish({
535
+ type: "message_queued",
536
+ channel: "messages",
537
+ payload: message,
538
+ });
539
+ },
540
+ submitToolResult(toolUseId, response) {
541
+ session.channel("tool_confirmation").publish({
542
+ type: "response",
543
+ channel: "tool_confirmation",
544
+ id: toolUseId,
545
+ payload: response,
546
+ });
547
+ },
548
+ };
549
+ // Wire up event emission from session to handle
550
+ this.on("event", pushEvent);
551
+ return handle;
552
+ }
553
+ /**
554
+ * Create an empty handle for when there's nothing to do.
555
+ */
556
+ createEmptyHandle() {
557
+ const session = this;
558
+ const emptyResult = {
559
+ response: "",
560
+ outputs: {},
561
+ usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
562
+ raw: {
563
+ timeline: [],
564
+ system: [],
565
+ ephemeral: [],
566
+ sections: {},
567
+ tools: [],
568
+ metadata: {},
569
+ },
570
+ };
571
+ // Empty event buffer for empty handle
572
+ const emptyEventBuffer = new EventBuffer();
573
+ emptyEventBuffer.close();
574
+ const handle = {
575
+ [ExecutionHandleBrand]: true,
576
+ [Symbol.asyncIterator]() {
577
+ return emptyEventBuffer[Symbol.asyncIterator]();
578
+ },
579
+ get status() {
580
+ return "completed";
581
+ },
582
+ get traceId() {
583
+ return randomUUID();
584
+ },
585
+ get events() {
586
+ return emptyEventBuffer;
587
+ },
588
+ get result() {
589
+ return Promise.resolve(emptyResult);
590
+ },
591
+ abort() { },
592
+ get sessionId() {
593
+ return session.id;
594
+ },
595
+ get currentTick() {
596
+ return session._tick;
597
+ },
598
+ queueMessage(message) {
599
+ session._queuedMessages.push(message);
600
+ },
601
+ submitToolResult(_toolUseId, _response) { },
602
+ };
603
+ return handle;
604
+ }
605
+ // ════════════════════════════════════════════════════════════════════════
606
+ // Spawn Target Resolution
607
+ // ════════════════════════════════════════════════════════════════════════
608
+ /**
609
+ * Resolve spawn target to a ComponentFunction and merged props.
610
+ */
611
+ resolveSpawnTarget(component, input) {
612
+ // JSX Element
613
+ if (React.isValidElement(component)) {
614
+ return {
615
+ Component: component.type,
616
+ mergedProps: {
617
+ ...component.props,
618
+ ...(input.props ?? {}),
619
+ },
620
+ };
621
+ }
622
+ // Component function
623
+ return {
624
+ Component: component,
625
+ mergedProps: input.props ?? {},
626
+ };
627
+ }
628
+ // ════════════════════════════════════════════════════════════════════════
629
+ // Channels
630
+ // ════════════════════════════════════════════════════════════════════════
631
+ channel(name) {
632
+ let channel = this._channels.get(name);
633
+ if (!channel) {
634
+ channel = new Channel(name);
635
+ this._channels.set(name, channel);
636
+ }
637
+ return channel;
638
+ }
639
+ submitToolResult(toolUseId, response) {
640
+ this.channel("tool_confirmation").publish({
641
+ type: "response",
642
+ channel: "tool_confirmation",
643
+ id: toolUseId,
644
+ payload: response,
645
+ });
646
+ }
647
+ // ════════════════════════════════════════════════════════════════════════
648
+ // Interrupt & Abort
649
+ // ════════════════════════════════════════════════════════════════════════
650
+ interrupt(message, reason) {
651
+ if (this._status === "closed") {
652
+ throw new Error("Session is closed");
653
+ }
654
+ if (message) {
655
+ this._queuedMessages.push(message);
656
+ }
657
+ if (this._status === "running") {
658
+ this._isAborted = true;
659
+ this.executionAbortController?.abort(reason ?? "interrupt");
660
+ // Propagate interrupt to child sessions (spread-copy: interrupt cleanup may mutate array)
661
+ for (const child of [...this._children]) {
662
+ child.interrupt(undefined, reason ?? "Parent interrupted");
663
+ }
664
+ }
665
+ }
666
+ clearAbort() {
667
+ this._isAborted = false;
668
+ }
669
+ startExecutionAbort(signal) {
670
+ this.executionAbortCleanup.forEach((cleanup) => cleanup());
671
+ this.executionAbortCleanup = [];
672
+ this._isAborted = false;
673
+ this.executionAbortController = new AbortController();
674
+ this.addExecutionSignal(this.sessionAbortController.signal);
675
+ this.addExecutionSignal(signal);
676
+ }
677
+ addExecutionSignal(signal) {
678
+ if (!signal || !this.executionAbortController)
679
+ return;
680
+ if (signal.aborted) {
681
+ this._isAborted = true;
682
+ this.executionAbortController.abort(signal.reason);
683
+ return;
684
+ }
685
+ const handler = () => {
686
+ this._isAborted = true;
687
+ this.executionAbortController?.abort(signal.reason);
688
+ };
689
+ signal.addEventListener("abort", handler);
690
+ this.executionAbortCleanup.push(() => signal.removeEventListener("abort", handler));
691
+ }
692
+ finishExecutionAbort() {
693
+ this.executionAbortCleanup.forEach((cleanup) => cleanup());
694
+ this.executionAbortCleanup = [];
695
+ this.executionAbortController = null;
696
+ }
697
+ // ════════════════════════════════════════════════════════════════════════
698
+ // Events (AsyncIterable)
699
+ // ════════════════════════════════════════════════════════════════════════
700
+ events() {
701
+ const self = this;
702
+ return {
703
+ [Symbol.asyncIterator]() {
704
+ return {
705
+ async next() {
706
+ if (self._eventQueue.length > 0) {
707
+ return { value: self._eventQueue.shift(), done: false };
708
+ }
709
+ if (self._executionComplete) {
710
+ return { value: undefined, done: true };
711
+ }
712
+ return new Promise((resolve) => {
713
+ self._eventResolvers.push(resolve);
714
+ });
715
+ },
716
+ };
717
+ },
718
+ };
719
+ }
720
+ /**
721
+ * Get merged lifecycle callbacks (session overrides app).
722
+ */
723
+ get callbacks() {
724
+ return {
725
+ onEvent: this.sessionOptions.onEvent ?? this.appOptions.onEvent,
726
+ onTickStart: this.sessionOptions.onTickStart ?? this.appOptions.onTickStart,
727
+ onTickEnd: this.sessionOptions.onTickEnd ?? this.appOptions.onTickEnd,
728
+ onComplete: this.sessionOptions.onComplete ?? this.appOptions.onComplete,
729
+ onError: this.sessionOptions.onError ?? this.appOptions.onError,
730
+ };
731
+ }
732
+ emitEvent(event) {
733
+ const executionId = this._currentExecutionId ?? Context.tryGet()?.executionId;
734
+ // Enrich event with executionId and tick if missing
735
+ let enrichedEvent = executionId && !("executionId" in event) ? { ...event, executionId } : event;
736
+ if (!("id" in event)) {
737
+ enrichedEvent = { ...enrichedEvent, id: randomUUID() };
738
+ }
739
+ if (!("timestamp" in event)) {
740
+ enrichedEvent = { ...enrichedEvent, timestamp: new Date().toISOString() };
741
+ }
742
+ // Always set tick if missing - session knows the actual tick
743
+ if (!("tick" in event)) {
744
+ enrichedEvent = { ...enrichedEvent, tick: this._tick || 1 };
745
+ }
746
+ // Assign monotonically increasing sequence number for durable streams
747
+ this._sequence++;
748
+ enrichedEvent = { ...enrichedEvent, sequence: this._sequence };
749
+ // Forward to DevTools (check context for overrides)
750
+ const sessionCtx = getSessionContext();
751
+ forwardToDevTools(enrichedEvent, {
752
+ sessionId: sessionCtx.sessionId ?? this.id,
753
+ rootComponent: sessionCtx.rootComponent ?? (this.Component.name || "Agent"),
754
+ devToolsEnabled: sessionCtx.devToolsEnabled ?? this.sessionOptions.devTools ?? false,
755
+ });
756
+ // Invoke lifecycle callbacks
757
+ const cb = this.callbacks;
758
+ try {
759
+ cb.onEvent?.(enrichedEvent);
760
+ // Call specific callbacks based on event type
761
+ const eventType = enrichedEvent.type;
762
+ if (eventType === "tick_start") {
763
+ cb.onTickStart?.(enrichedEvent.tick, enrichedEvent.executionId);
764
+ }
765
+ else if (eventType === "tick_end") {
766
+ cb.onTickEnd?.(enrichedEvent.tick, enrichedEvent.usage);
767
+ }
768
+ }
769
+ catch {
770
+ // Callbacks should not throw, but don't let them break execution
771
+ }
772
+ // Emit via EventEmitter
773
+ this.emit("event", enrichedEvent);
774
+ // Also queue for AsyncIterable consumers
775
+ if (this._eventResolvers.length > 0) {
776
+ const resolve = this._eventResolvers.shift();
777
+ resolve({ value: enrichedEvent, done: false });
778
+ }
779
+ else {
780
+ this._eventQueue.push(enrichedEvent);
781
+ }
782
+ }
783
+ // ════════════════════════════════════════════════════════════════════════
784
+ // Snapshot & Inspect
785
+ // ════════════════════════════════════════════════════════════════════════
786
+ snapshot() {
787
+ return {
788
+ version: "1.0",
789
+ sessionId: this.id,
790
+ tick: this._tick,
791
+ timeline: this._previousOutput?.timeline ?? null,
792
+ componentState: this.serializeFiberTree(), // Serialized fiber tree with hook states
793
+ usage: { ...this._totalUsage },
794
+ timestamp: Date.now(),
795
+ };
796
+ }
797
+ /**
798
+ * Set the hibernate callback.
799
+ * Called by the App when registering this session.
800
+ * @internal
801
+ */
802
+ setHibernateCallback(callback) {
803
+ this._hibernateCallback = callback;
804
+ }
805
+ async hibernate() {
806
+ if (this._status === "closed") {
807
+ return null;
808
+ }
809
+ if (this._status === "running") {
810
+ // Cannot hibernate while running - abort first
811
+ this.interrupt(undefined, "Hibernation requested");
812
+ }
813
+ // Delegate to the App's hibernate callback if available
814
+ if (this._hibernateCallback) {
815
+ return this._hibernateCallback();
816
+ }
817
+ // No callback - just return the snapshot without persisting
818
+ // This allows sessions created outside of App management to still hibernate
819
+ const snapshot = this.snapshot();
820
+ this.close();
821
+ return snapshot;
822
+ }
823
+ inspect() {
824
+ // Get fiber summary for component/hook stats
825
+ const fiberSummary = this.getFiberSummary();
826
+ // Get component names from fiber tree
827
+ const componentNames = this.collectComponentNames();
828
+ // Get last tick's tool data from snapshots if available
829
+ const lastSnapshot = this._snapshots.length > 0 ? this._snapshots[this._snapshots.length - 1] : null;
830
+ return {
831
+ id: this.id,
832
+ status: this._status,
833
+ currentTick: this._tick,
834
+ queuedMessages: [...this._queuedMessages],
835
+ currentPhase: this._status === "running" ? "model" : undefined, // Approximate
836
+ isAborted: this._isAborted,
837
+ lastOutput: this._previousOutput,
838
+ lastModelOutput: this._lastModelOutput,
839
+ lastToolCalls: lastSnapshot?.tools.calls ?? [],
840
+ lastToolResults: lastSnapshot?.tools.results.map((r) => ({
841
+ toolUseId: r.toolUseId,
842
+ name: r.name,
843
+ success: r.success,
844
+ })) ?? [],
845
+ totalUsage: { ...this._totalUsage },
846
+ tickCount: this._totalUsage.ticks ?? 0,
847
+ components: {
848
+ count: fiberSummary.componentCount,
849
+ names: componentNames,
850
+ },
851
+ hooks: {
852
+ count: fiberSummary.hookCount,
853
+ // React manages hooks internally, so detailed type info isn't available
854
+ byType: {},
855
+ },
856
+ };
857
+ }
858
+ /**
859
+ * Collect unique component names from the fiber tree.
860
+ */
861
+ collectComponentNames() {
862
+ if (!this.compiler)
863
+ return [];
864
+ const tree = this.compiler.serializeFiberTree();
865
+ if (!tree)
866
+ return [];
867
+ const names = new Set();
868
+ const collectNames = (node) => {
869
+ // Skip host primitives (Section, Message, etc.) and fragments
870
+ if (!node.type.startsWith("agentick.") && node.type !== "Fragment") {
871
+ names.add(node.type);
872
+ }
873
+ for (const child of node.children) {
874
+ collectNames(child);
875
+ }
876
+ };
877
+ collectNames(tree);
878
+ return Array.from(names);
879
+ }
880
+ // ════════════════════════════════════════════════════════════════════════
881
+ // Recording (stub - can add back if needed)
882
+ // ════════════════════════════════════════════════════════════════════════
883
+ startRecording(mode) {
884
+ this._recordingMode = mode;
885
+ this._recordingStartedAt = new Date().toISOString();
886
+ this._snapshots = [];
887
+ this._recording = {
888
+ sessionId: this.id,
889
+ startedAt: this._recordingStartedAt,
890
+ config: {
891
+ componentName: this.Component.name || "Anonymous",
892
+ initialProps: {},
893
+ maxTicks: this.appOptions.maxTicks ?? 10,
894
+ mode,
895
+ },
896
+ inputs: [],
897
+ snapshots: this._snapshots,
898
+ summary: {
899
+ tickCount: 0,
900
+ totalDuration: 0,
901
+ totalUsage: { inputTokens: 0, outputTokens: 0, totalTokens: 0, ticks: 0 },
902
+ finalStatus: "running",
903
+ },
904
+ };
905
+ }
906
+ stopRecording() {
907
+ if (this._recording) {
908
+ this._recording.endedAt = new Date().toISOString();
909
+ this._recording.summary.tickCount = this._snapshots.length;
910
+ this._recording.summary.totalUsage = { ...this._totalUsage };
911
+ this._recording.summary.finalStatus =
912
+ this._status === "idle" ? "completed" : this._status;
913
+ }
914
+ this._recordingMode = null;
915
+ }
916
+ getRecording() {
917
+ if (this._recording) {
918
+ // Update summary with current state
919
+ this._recording.summary.tickCount = this._snapshots.length;
920
+ this._recording.summary.totalUsage = { ...this._totalUsage };
921
+ }
922
+ return this._recording;
923
+ }
924
+ getSnapshotAt(tick) {
925
+ return this._snapshots.find((s) => s.tick === tick) ?? null;
926
+ }
927
+ /**
928
+ * Record a tick snapshot for time-travel debugging.
929
+ */
930
+ recordSnapshot(tickNumber, tickStartTime, tickData) {
931
+ if (!this._recordingMode)
932
+ return;
933
+ const { formatted, modelId, modelInput, modelStartTime, modelOutput, toolCalls = [], toolResults = [], toolStartTime, shouldContinue = false, stopReason, } = tickData;
934
+ const now = Date.now();
935
+ const fiberSummary = this.getFiberSummary();
936
+ // Extract COM data from formatted input
937
+ const comSections = {};
938
+ if (formatted?.sections) {
939
+ for (const [id, section] of Object.entries(formatted.sections)) {
940
+ comSections[id] = {
941
+ content: typeof section.content === "string" ? section.content : JSON.stringify(section.content),
942
+ priority: section.priority,
943
+ };
944
+ }
945
+ }
946
+ // Extract tool definitions (ToolDefinition has name/description/input directly)
947
+ const comTools = (formatted?.tools ?? []).map((t) => ({
948
+ name: t.name ?? "unknown",
949
+ description: t.description,
950
+ inputSchema: t.input,
951
+ }));
952
+ const snapshot = {
953
+ sessionId: this.id,
954
+ tick: tickNumber,
955
+ timestamp: new Date().toISOString(),
956
+ duration: now - tickStartTime,
957
+ fiber: {
958
+ tree: this._recordingMode === "full" ? this.serializeFiberTree() : null,
959
+ summary: {
960
+ componentCount: fiberSummary.componentCount,
961
+ hookCount: fiberSummary.hookCount,
962
+ hooksByType: fiberSummary.hooksByType,
963
+ },
964
+ },
965
+ com: {
966
+ sections: comSections,
967
+ timeline: formatted?.timeline ?? [],
968
+ tools: comTools,
969
+ modelId: modelId ?? null,
970
+ metadata: formatted?.metadata ?? {},
971
+ },
972
+ model: {
973
+ input: {
974
+ formatted: modelInput ?? "",
975
+ tokenCount: undefined, // Could be populated if available
976
+ },
977
+ output: {
978
+ content: modelOutput?.message?.content ?? [],
979
+ stopReason: modelOutput?.stopReason ?? "unknown",
980
+ tokenCount: modelOutput?.usage?.outputTokens,
981
+ },
982
+ latency: modelStartTime ? (toolStartTime ?? now) - modelStartTime : 0,
983
+ },
984
+ tools: {
985
+ calls: toolCalls,
986
+ results: toolResults.map((r) => ({
987
+ toolUseId: r.toolUseId,
988
+ name: r.name ?? "unknown",
989
+ success: !r.error,
990
+ content: r.content,
991
+ })),
992
+ totalDuration: toolStartTime ? now - toolStartTime : 0,
993
+ },
994
+ execution: {
995
+ phase: "complete",
996
+ shouldContinue,
997
+ stopReason,
998
+ queuedMessages: [...this._queuedMessages],
999
+ executionId: Context.tryGet()?.executionId,
1000
+ },
1001
+ };
1002
+ this._snapshots.push(snapshot);
1003
+ }
1004
+ serializeFiberTree() {
1005
+ if (!this.compiler)
1006
+ return null;
1007
+ return this.compiler.serializeFiberTree();
1008
+ }
1009
+ getFiberSummary() {
1010
+ if (!this.compiler) {
1011
+ return { componentCount: 0, hookCount: 0, effectCount: 0, depth: 0, hooksByType: {} };
1012
+ }
1013
+ return this.compiler.getFiberSummary();
1014
+ }
1015
+ /**
1016
+ * Broadcast context utilization info via the session:context channel.
1017
+ * Enables real-time context tracking in UI via useContextInfo() hook.
1018
+ */
1019
+ broadcastContextInfo(executionId, modelId, modelMetadata, tickUsage, tick, cumulativeUsage, timestampStr) {
1020
+ // Get context info (effective = adapter metadata merged with catalog)
1021
+ const modelInfo = getEffectiveModelInfo({
1022
+ model: modelId,
1023
+ provider: modelMetadata?.provider,
1024
+ contextWindow: modelMetadata?.contextWindow,
1025
+ maxOutputTokens: modelMetadata?.maxOutputTokens,
1026
+ }, modelId);
1027
+ const inputTokens = tickUsage?.inputTokens ?? 0;
1028
+ const outputTokens = tickUsage?.outputTokens ?? 0;
1029
+ const totalTokens = tickUsage?.totalTokens ?? inputTokens + outputTokens;
1030
+ // Calculate utilization if we have context window info
1031
+ const utilization = modelInfo?.contextWindow
1032
+ ? (getContextUtilization(modelId, inputTokens) ?? undefined)
1033
+ : undefined;
1034
+ const payload = {
1035
+ modelId,
1036
+ modelName: modelInfo?.name,
1037
+ provider: modelInfo?.provider || modelMetadata?.provider,
1038
+ contextWindow: modelInfo?.contextWindow,
1039
+ inputTokens,
1040
+ outputTokens,
1041
+ totalTokens,
1042
+ utilization,
1043
+ maxOutputTokens: modelInfo?.maxOutputTokens,
1044
+ supportsVision: modelInfo?.supportsVision,
1045
+ supportsToolUse: modelInfo?.supportsToolUse,
1046
+ isReasoningModel: modelInfo?.isReasoningModel,
1047
+ tick,
1048
+ cumulativeUsage: {
1049
+ inputTokens: cumulativeUsage.inputTokens,
1050
+ outputTokens: cumulativeUsage.outputTokens,
1051
+ totalTokens: cumulativeUsage.totalTokens,
1052
+ ticks: cumulativeUsage.ticks ?? tick,
1053
+ },
1054
+ timestamp: timestampStr,
1055
+ };
1056
+ // Publish to channel (if channel service is available)
1057
+ const ctx = Context.tryGet();
1058
+ if (ctx?.channels) {
1059
+ try {
1060
+ ctx.channels.publish(ctx, FrameworkChannels.CONTEXT, {
1061
+ type: "context_update",
1062
+ payload,
1063
+ });
1064
+ }
1065
+ catch {
1066
+ // Silently ignore if channel publish fails
1067
+ }
1068
+ }
1069
+ // Also emit as DevTools event for DevTools UI
1070
+ if (devToolsEmitter.hasSubscribers()) {
1071
+ devToolsEmitter.emitEvent({
1072
+ type: "context_update",
1073
+ executionId,
1074
+ sessionId: this.id,
1075
+ sequence: ++this._sequence,
1076
+ timestamp: Date.now(),
1077
+ modelId: payload.modelId,
1078
+ modelName: payload.modelName,
1079
+ provider: payload.provider,
1080
+ contextWindow: payload.contextWindow,
1081
+ inputTokens: payload.inputTokens,
1082
+ outputTokens: payload.outputTokens,
1083
+ totalTokens: payload.totalTokens,
1084
+ utilization: payload.utilization,
1085
+ maxOutputTokens: payload.maxOutputTokens,
1086
+ supportsVision: payload.supportsVision,
1087
+ supportsToolUse: payload.supportsToolUse,
1088
+ isReasoningModel: payload.isReasoningModel,
1089
+ tick: payload.tick,
1090
+ cumulativeUsage: payload.cumulativeUsage,
1091
+ });
1092
+ }
1093
+ // Update compiler's contextInfoStore so JSX components can access via useContextInfo
1094
+ if (this.compiler) {
1095
+ this.compiler.contextInfoStore.update({
1096
+ modelId: payload.modelId,
1097
+ modelName: payload.modelName,
1098
+ provider: payload.provider,
1099
+ contextWindow: payload.contextWindow,
1100
+ inputTokens: payload.inputTokens,
1101
+ outputTokens: payload.outputTokens,
1102
+ totalTokens: payload.totalTokens,
1103
+ utilization: payload.utilization,
1104
+ maxOutputTokens: payload.maxOutputTokens,
1105
+ supportsVision: payload.supportsVision,
1106
+ supportsToolUse: payload.supportsToolUse,
1107
+ isReasoningModel: payload.isReasoningModel,
1108
+ tick: payload.tick,
1109
+ cumulativeUsage: payload.cumulativeUsage,
1110
+ estimatedContextTokens: this._estimatedContextTokens,
1111
+ });
1112
+ }
1113
+ // Emit as stream event for client-side React consumption
1114
+ this.emitEvent({
1115
+ type: "context_update",
1116
+ modelId: payload.modelId,
1117
+ modelName: payload.modelName,
1118
+ provider: payload.provider,
1119
+ contextWindow: payload.contextWindow,
1120
+ inputTokens: payload.inputTokens,
1121
+ outputTokens: payload.outputTokens,
1122
+ totalTokens: payload.totalTokens,
1123
+ utilization: payload.utilization,
1124
+ maxOutputTokens: payload.maxOutputTokens,
1125
+ supportsVision: payload.supportsVision,
1126
+ supportsToolUse: payload.supportsToolUse,
1127
+ isReasoningModel: payload.isReasoningModel,
1128
+ cumulativeUsage: payload.cumulativeUsage,
1129
+ tick,
1130
+ timestamp: timestampStr,
1131
+ });
1132
+ }
1133
+ /**
1134
+ * Emit a fiber_snapshot event to DevTools after each tick.
1135
+ * This enables the Fiber Tree panel in DevTools to show component hierarchy and hook values.
1136
+ *
1137
+ * @param tick - The tick number
1138
+ * @param executionId - The execution ID
1139
+ * @param compiled - Optional compiled structure for token estimation
1140
+ */
1141
+ emitFiberSnapshot(tick, executionId, compiled) {
1142
+ // Only emit if DevTools has subscribers (avoid serialization overhead)
1143
+ if (!devToolsEmitter.hasSubscribers())
1144
+ return;
1145
+ try {
1146
+ const tree = this.serializeFiberTree();
1147
+ const summary = this.getFiberSummary();
1148
+ // Compute token summary if compiled structure is available
1149
+ let tokenSummary;
1150
+ let compiledPreview;
1151
+ if (compiled) {
1152
+ try {
1153
+ const tokens = computeTokenSummary(compiled);
1154
+ tokenSummary = {
1155
+ system: tokens.system,
1156
+ messages: tokens.messages,
1157
+ tools: tokens.tools,
1158
+ ephemeral: tokens.ephemeral,
1159
+ total: tokens.total,
1160
+ byComponent: Object.fromEntries(tokens.byComponent),
1161
+ };
1162
+ // Get system prompt preview (first 200 chars)
1163
+ let systemPrompt;
1164
+ if (compiled.systemEntries.length > 0) {
1165
+ const firstSystem = compiled.systemEntries[0];
1166
+ if (firstSystem.content.length > 0) {
1167
+ const firstBlock = firstSystem.content[0];
1168
+ if ("text" in firstBlock && typeof firstBlock.text === "string") {
1169
+ systemPrompt = firstBlock.text.slice(0, 200);
1170
+ }
1171
+ }
1172
+ }
1173
+ compiledPreview = {
1174
+ systemPrompt,
1175
+ messageCount: compiled.timelineEntries.length,
1176
+ toolCount: compiled.tools.length,
1177
+ ephemeralCount: compiled.ephemeral.length,
1178
+ };
1179
+ }
1180
+ catch {
1181
+ // Ignore token estimation errors
1182
+ }
1183
+ }
1184
+ const event = {
1185
+ type: "fiber_snapshot",
1186
+ sessionId: this.id,
1187
+ executionId,
1188
+ sequence: ++this._sequence, // Use session's sequence counter for DevTools events
1189
+ tick,
1190
+ timestamp: Date.now(),
1191
+ tree: tree,
1192
+ summary: summary,
1193
+ tokenSummary,
1194
+ compiledPreview,
1195
+ };
1196
+ devToolsEmitter.emitEvent(event);
1197
+ }
1198
+ catch {
1199
+ // Silently ignore serialization errors - DevTools is optional
1200
+ }
1201
+ }
1202
+ // ════════════════════════════════════════════════════════════════════════
1203
+ // Close
1204
+ // ════════════════════════════════════════════════════════════════════════
1205
+ close() {
1206
+ if (this._status === "closed")
1207
+ return;
1208
+ this._status = "closed";
1209
+ // Close all child sessions
1210
+ for (const child of [...this._children]) {
1211
+ child.close();
1212
+ }
1213
+ this._children = [];
1214
+ this.sessionAbortController.abort("Session closed");
1215
+ this.executionAbortController?.abort("Session closed");
1216
+ // Complete any pending event iterators
1217
+ this._executionComplete = true;
1218
+ for (const resolve of this._eventResolvers) {
1219
+ resolve({ value: undefined, done: true });
1220
+ }
1221
+ this._eventResolvers = [];
1222
+ // Dispose scheduler (cancels pending work and cleans up signal)
1223
+ if (this.scheduler) {
1224
+ this.scheduler.dispose();
1225
+ this.scheduler = null;
1226
+ }
1227
+ // Unmount compiler if it exists
1228
+ if (this.compiler) {
1229
+ this.compiler.unmount().catch(() => { });
1230
+ this.compiler = null;
1231
+ }
1232
+ this.ctx = null;
1233
+ this.structureRenderer = null;
1234
+ this.emit("close", this.id);
1235
+ }
1236
+ // ════════════════════════════════════════════════════════════════════════
1237
+ // Internal: Execution
1238
+ // ════════════════════════════════════════════════════════════════════════
1239
+ async runWithContext(fn) {
1240
+ const current = Context.tryGet();
1241
+ const baseContext = this._capturedContext || current;
1242
+ // Create a channel service that wraps session channels
1243
+ // This allows tools to publish events via Context.get().channels
1244
+ const channelService = {
1245
+ getChannel: (_ctx, channelName) => this.channel(channelName),
1246
+ publish: (_ctx, channelName, event) => {
1247
+ this.channel(channelName).publish({ ...event, channel: channelName });
1248
+ },
1249
+ subscribe: (_ctx, channelName, handler) => {
1250
+ return this.channel(channelName).subscribe(handler);
1251
+ },
1252
+ waitForResponse: (_ctx, channelName, requestId, timeoutMs) => {
1253
+ return this.channel(channelName).waitForResponse(requestId, timeoutMs);
1254
+ },
1255
+ };
1256
+ // Session-specific context fields for DevTools enrichment
1257
+ const sessionContext = {
1258
+ sessionId: this.id,
1259
+ rootComponent: this.Component.name || "Agent",
1260
+ devToolsEnabled: this.sessionOptions.devTools ?? false,
1261
+ channels: channelService,
1262
+ };
1263
+ if (!baseContext) {
1264
+ // No base context - create minimal context with session fields
1265
+ return Context.run(Context.create(sessionContext), fn);
1266
+ }
1267
+ // Merge base, current, and session context
1268
+ const merged = { ...baseContext, ...(current ?? {}), ...sessionContext };
1269
+ return Context.run(merged, fn);
1270
+ }
1271
+ hydrate(snapshot) {
1272
+ this._tick = snapshot.tick;
1273
+ // Hydrate timeline (conversation history)
1274
+ if (snapshot.timeline) {
1275
+ this._previousOutput = {
1276
+ timeline: snapshot.timeline,
1277
+ system: [],
1278
+ ephemeral: [],
1279
+ sections: {},
1280
+ tools: [],
1281
+ metadata: {},
1282
+ };
1283
+ }
1284
+ // Hydrate usage stats
1285
+ if (snapshot.usage) {
1286
+ this._totalUsage = { ...snapshot.usage };
1287
+ }
1288
+ // Hydrate fiber tree (component state)
1289
+ // This will be applied when the compiler is first created
1290
+ if (snapshot.componentState) {
1291
+ this._pendingHydrationData = snapshot.componentState;
1292
+ }
1293
+ }
1294
+ /**
1295
+ * The core tick execution loop.
1296
+ *
1297
+ * This is where the magic happens:
1298
+ * 1. Setup compilation infrastructure (COM, FiberCompiler)
1299
+ * 2. Loop: compile -> model -> tools -> ingest
1300
+ * 3. Return result
1301
+ */
1302
+ async executeTick(props, options) {
1303
+ if (this._status === "closed") {
1304
+ throw new Error("Session is closed");
1305
+ }
1306
+ const signal = this.executionAbortController?.signal ?? this.sessionAbortController.signal;
1307
+ if (signal.aborted) {
1308
+ throw new AbortError("Session aborted", signal.reason);
1309
+ }
1310
+ this._status = "running";
1311
+ this._executionComplete = false;
1312
+ this._lastProps = props;
1313
+ // Clear _currentOutput from previous execution to prevent stale data
1314
+ // from being included in useConversationHistory() via state.current
1315
+ this._currentOutput = null;
1316
+ const executionId = Context.tryGet()?.executionId || randomUUID();
1317
+ this._currentExecutionId = executionId;
1318
+ const maxTicks = options?.maxTicks ?? this.sessionOptions.maxTicks ?? this.appOptions.maxTicks ?? 10;
1319
+ const timestamp = () => new Date().toISOString();
1320
+ // Create root JSX element
1321
+ const rootElement = jsx(this.Component, props);
1322
+ // Setup or reuse compilation infrastructure
1323
+ await this.ensureCompilationInfrastructure(rootElement);
1324
+ // Transfer queued messages to COM
1325
+ if (this._queuedMessages.length > 0 && this.ctx) {
1326
+ this.log.debug({ count: this._queuedMessages.length }, "Transferring queued messages to COM");
1327
+ for (const msg of this._queuedMessages) {
1328
+ this.log.debug({ role: msg.role }, "Queuing message to COM");
1329
+ this.ctx.queueMessage({
1330
+ id: randomUUID(),
1331
+ type: "message",
1332
+ content: msg,
1333
+ });
1334
+ }
1335
+ this._queuedMessages = [];
1336
+ this.log.debug({ comQueuedCount: this.ctx.getQueuedMessages().length }, "COM queued messages after transfer");
1337
+ }
1338
+ // Track state for this execution
1339
+ const usage = {
1340
+ inputTokens: 0,
1341
+ outputTokens: 0,
1342
+ totalTokens: 0,
1343
+ ticks: 0,
1344
+ };
1345
+ let stopReason;
1346
+ let output;
1347
+ const outputs = {};
1348
+ const responseChunks = [];
1349
+ const toolExecutor = new ToolExecutor();
1350
+ this.emitEvent({
1351
+ type: "execution_start",
1352
+ executionId,
1353
+ timestamp: timestamp(),
1354
+ });
1355
+ try {
1356
+ // Tick loop
1357
+ let shouldContinue = true;
1358
+ while (shouldContinue && this._tick <= maxTicks) {
1359
+ if (signal.aborted) {
1360
+ throw new AbortError("Execution aborted", signal.reason);
1361
+ }
1362
+ const currentTick = this._tick;
1363
+ const tickStartTime = Date.now();
1364
+ this.emitEvent({
1365
+ type: "tick_start",
1366
+ tick: currentTick,
1367
+ timestamp: timestamp(),
1368
+ });
1369
+ // Phase 1: Compile
1370
+ const compiled = await this.compileTick(rootElement);
1371
+ // Emit compiled context to DevTools
1372
+ // Extract system text from COMTimelineEntry[]
1373
+ const systemText = compiled.formatted?.system
1374
+ ?.map((entry) => {
1375
+ const content = entry.message?.content ?? entry.content;
1376
+ if (Array.isArray(content)) {
1377
+ return content
1378
+ .filter((b) => b.type === "text")
1379
+ .map((b) => b.text)
1380
+ .join("\n");
1381
+ }
1382
+ return String(content ?? "");
1383
+ })
1384
+ .filter(Boolean)
1385
+ .join("\n\n");
1386
+ this.emitEvent({
1387
+ type: "compiled",
1388
+ tick: currentTick,
1389
+ timestamp: timestamp(),
1390
+ // Rendered output (existing)
1391
+ system: systemText || undefined,
1392
+ messages: compiled.formatted?.timeline,
1393
+ tools: compiled.tools?.map((t) => ({
1394
+ name: t.metadata?.name ?? t.name,
1395
+ description: t.metadata?.description ?? t.description,
1396
+ })),
1397
+ // Raw compiled structure (before rendering)
1398
+ rawCompiled: compiled.rawCompiled
1399
+ ? {
1400
+ sections: Object.fromEntries(compiled.rawCompiled.sections ?? new Map()),
1401
+ timelineEntries: compiled.rawCompiled.timelineEntries,
1402
+ systemEntries: compiled.rawCompiled.systemEntries,
1403
+ tools: compiled.rawCompiled.tools,
1404
+ ephemeral: compiled.rawCompiled.ephemeral,
1405
+ }
1406
+ : undefined,
1407
+ // Formatted COMInput (after rendering)
1408
+ formattedInput: compiled.formatted,
1409
+ });
1410
+ if (compiled.shouldStop) {
1411
+ stopReason = compiled.stopReason;
1412
+ this.emitEvent({
1413
+ type: "tick_end",
1414
+ tick: currentTick,
1415
+ shouldContinue: false,
1416
+ timestamp: timestamp(),
1417
+ });
1418
+ // Emit fiber snapshot to DevTools
1419
+ this.emitFiberSnapshot(currentTick, executionId, compiled.rawCompiled);
1420
+ // Clear queued messages - they were made available during compilation
1421
+ this.ctx?.clearQueuedMessages();
1422
+ break;
1423
+ }
1424
+ // Phase 2: Model
1425
+ const model = this.appOptions.model ?? compiled.model;
1426
+ if (!model) {
1427
+ throw new Error("No model configured. Add a <Model> component or pass model in options.");
1428
+ }
1429
+ if (signal.aborted) {
1430
+ throw new AbortError("Execution aborted", signal.reason);
1431
+ }
1432
+ const modelInput = compiled.modelInput ?? compiled.formatted;
1433
+ const modelStartTime = Date.now();
1434
+ // Emit model request to DevTools
1435
+ // modelInput is the Agentick ModelInput format (after fromEngineState transformation)
1436
+ this.emitEvent({
1437
+ type: "model_request",
1438
+ tick: currentTick,
1439
+ timestamp: timestamp(),
1440
+ modelId: model?.metadata?.id ?? model?.metadata?.model,
1441
+ // ModelInput: Agentick's model input format
1442
+ input: modelInput,
1443
+ // Stage marker for pipeline visualization
1444
+ stage: "model_input",
1445
+ });
1446
+ // Emit provider request to DevTools (Stage 4: what the SDK actually receives)
1447
+ if (model.getProviderInput) {
1448
+ const providerInput = await model.getProviderInput(modelInput);
1449
+ this.emitEvent({
1450
+ type: "provider_request",
1451
+ tick: currentTick,
1452
+ timestamp: timestamp(),
1453
+ modelId: model?.metadata?.id ?? model?.metadata?.model,
1454
+ provider: model?.metadata?.provider,
1455
+ providerInput,
1456
+ });
1457
+ }
1458
+ // Stream model output if supported
1459
+ let modelOutput;
1460
+ if (model.stream) {
1461
+ const streamEvents = [];
1462
+ const streamIterable = await model.stream(modelInput);
1463
+ for await (const event of streamIterable) {
1464
+ if (signal.aborted) {
1465
+ throw new AbortError("Execution aborted", signal.reason);
1466
+ }
1467
+ streamEvents.push(event);
1468
+ this.emitEvent(event);
1469
+ if (event.type === "content_delta" && "delta" in event) {
1470
+ responseChunks.push(event.delta);
1471
+ }
1472
+ if (event.type === "message" && "message" in event) {
1473
+ const messageEvent = event;
1474
+ modelOutput = {
1475
+ messages: [messageEvent.message],
1476
+ message: messageEvent.message,
1477
+ stopReason: messageEvent.stopReason,
1478
+ usage: messageEvent.usage,
1479
+ raw: messageEvent.raw, // Reconstructed provider response from adapter
1480
+ };
1481
+ }
1482
+ }
1483
+ // Aggregate stream events into final output
1484
+ if (!modelOutput) {
1485
+ if (model.processStream) {
1486
+ // Use model's processStream if available
1487
+ modelOutput = await model.processStream(streamEvents);
1488
+ }
1489
+ else if (responseChunks.length > 0) {
1490
+ // Construct from collected chunks
1491
+ const text = responseChunks.join("");
1492
+ modelOutput = {
1493
+ message: {
1494
+ role: "assistant",
1495
+ content: [{ type: "text", text }],
1496
+ },
1497
+ stopReason: "stop",
1498
+ raw: { streamed: true },
1499
+ };
1500
+ }
1501
+ else {
1502
+ throw new Error("Streaming completed but no response was received");
1503
+ }
1504
+ }
1505
+ }
1506
+ else {
1507
+ // Procedure returns ExecutionHandle by default - access .result for actual return value
1508
+ // Handle both real procedures (with .result) and mock functions (direct value)
1509
+ const generateResult = model.generate(modelInput);
1510
+ modelOutput =
1511
+ generateResult && "result" in generateResult
1512
+ ? await generateResult.result
1513
+ : await generateResult;
1514
+ }
1515
+ // Extract text from model output
1516
+ if (modelOutput?.message) {
1517
+ const textContent = modelOutput.message.content?.find((b) => b.type === "text");
1518
+ if (textContent && "text" in textContent) {
1519
+ responseChunks.length = 0;
1520
+ responseChunks.push(textContent.text);
1521
+ }
1522
+ }
1523
+ // Update usage
1524
+ if (modelOutput?.usage) {
1525
+ usage.inputTokens += modelOutput.usage.inputTokens ?? 0;
1526
+ usage.outputTokens += modelOutput.usage.outputTokens ?? 0;
1527
+ usage.totalTokens += modelOutput.usage.totalTokens ?? 0;
1528
+ }
1529
+ if (signal.aborted) {
1530
+ throw new AbortError("Execution aborted", signal.reason);
1531
+ }
1532
+ // Convert model output to engine format
1533
+ if (!model.toEngineState) {
1534
+ throw new Error("Model missing toEngineState method");
1535
+ }
1536
+ const response = await model.toEngineState(modelOutput);
1537
+ // Emit model response to DevTools with full pipeline visibility
1538
+ this.emitEvent({
1539
+ type: "model_response",
1540
+ tick: currentTick,
1541
+ timestamp: timestamp(),
1542
+ // Provider output (raw from SDK - may be reconstructed for streaming)
1543
+ providerOutput: modelOutput?.raw,
1544
+ // ModelOutput (normalized Agentick format)
1545
+ modelOutput: {
1546
+ model: modelOutput?.model,
1547
+ message: modelOutput?.message,
1548
+ usage: modelOutput?.usage,
1549
+ stopReason: modelOutput?.stopReason,
1550
+ toolCalls: modelOutput?.toolCalls,
1551
+ },
1552
+ // Engine state (how it's ingested into timeline)
1553
+ engineState: {
1554
+ newTimelineEntries: response.newTimelineEntries,
1555
+ toolCalls: response.toolCalls,
1556
+ shouldStop: response.shouldStop,
1557
+ stopReason: response.stopReason,
1558
+ },
1559
+ });
1560
+ // Track last model output for inspection
1561
+ if (modelOutput?.message) {
1562
+ this._lastModelOutput = {
1563
+ content: modelOutput.message.content ?? [],
1564
+ stopReason: modelOutput.stopReason ?? "unknown",
1565
+ };
1566
+ }
1567
+ // Phase 3: Tools
1568
+ let toolResults = [];
1569
+ let toolStartTime;
1570
+ if (response.toolCalls?.length && this.ctx) {
1571
+ toolStartTime = Date.now();
1572
+ for (const call of response.toolCalls) {
1573
+ const toolCallTimestamp = timestamp();
1574
+ this.emitEvent({
1575
+ type: "tool_call",
1576
+ callId: call.id,
1577
+ blockIndex: 0,
1578
+ name: call.name,
1579
+ input: call.input,
1580
+ startedAt: toolCallTimestamp,
1581
+ completedAt: toolCallTimestamp,
1582
+ });
1583
+ }
1584
+ toolResults = await this.executeTools(toolExecutor, response.toolCalls, compiled.tools, outputs, currentTick, timestamp);
1585
+ }
1586
+ // Phase 4: Ingest & Tick End Callbacks
1587
+ const ingestResult = await this.ingestTickResult(response, toolResults);
1588
+ // Build TickResult for useTickEnd/useContinuation callbacks
1589
+ // Extract text from model response
1590
+ let tickText;
1591
+ if (modelOutput?.message?.content) {
1592
+ const textContent = modelOutput.message.content.find((b) => b.type === "text");
1593
+ if (textContent && "text" in textContent) {
1594
+ tickText = textContent.text;
1595
+ }
1596
+ }
1597
+ // Build TickResult with data and control methods
1598
+ const tickResult = {
1599
+ tick: currentTick,
1600
+ text: tickText,
1601
+ content: modelOutput?.message?.content ?? [],
1602
+ toolCalls: response.toolCalls ?? [],
1603
+ toolResults,
1604
+ stopReason: modelOutput?.stopReason,
1605
+ usage: modelOutput?.usage,
1606
+ timeline: ingestResult.timeline,
1607
+ stop: (reasonOrOptions) => {
1608
+ if (typeof reasonOrOptions === "string") {
1609
+ this.ctx?.requestStop({ reason: reasonOrOptions });
1610
+ }
1611
+ else {
1612
+ this.ctx?.requestStop(reasonOrOptions ?? {});
1613
+ }
1614
+ },
1615
+ continue: (reasonOrOptions) => {
1616
+ if (typeof reasonOrOptions === "string") {
1617
+ this.ctx?.requestContinue({ reason: reasonOrOptions });
1618
+ }
1619
+ else {
1620
+ this.ctx?.requestContinue(reasonOrOptions ?? {});
1621
+ }
1622
+ },
1623
+ };
1624
+ // Run useTickEnd/useContinuation callbacks
1625
+ // These can call tickResult.stop() or tickResult.continue() to influence continuation
1626
+ const tickEndState = {
1627
+ tick: currentTick,
1628
+ previous: this._previousOutput ?? undefined,
1629
+ current: this._currentOutput,
1630
+ queuedMessages: [],
1631
+ stop: () => { }, // No-op at tick end - use tickResult.stop() instead
1632
+ };
1633
+ await this.compiler?.notifyTickEnd(tickEndState, tickResult);
1634
+ // Resolve final tick control using COM's priority-based system
1635
+ // This considers: explicit stop/continue requests from callbacks > default behavior
1636
+ const defaultStatus = ingestResult.shouldContinue ? "continue" : "completed";
1637
+ const tickDecision = this.ctx?._resolveTickControl(defaultStatus, ingestResult.stopReason) ?? { status: defaultStatus };
1638
+ shouldContinue = tickDecision.status === "continue";
1639
+ // Get actual model ID: prefer response model, then metadata.model, then metadata.id
1640
+ const actualModelId = modelOutput?.model || model?.metadata?.model || model?.metadata?.id || "unknown";
1641
+ this.emitEvent({
1642
+ type: "tick_end",
1643
+ tick: currentTick,
1644
+ shouldContinue,
1645
+ usage: modelOutput?.usage,
1646
+ stopReason: modelOutput?.stopReason,
1647
+ model: actualModelId,
1648
+ timestamp: timestamp(),
1649
+ });
1650
+ // Broadcast context utilization via channel
1651
+ this.broadcastContextInfo(executionId, actualModelId, model?.metadata, modelOutput?.usage, currentTick, usage, timestamp());
1652
+ // Emit fiber snapshot to DevTools
1653
+ this.emitFiberSnapshot(currentTick, executionId, compiled.rawCompiled);
1654
+ // Clear queued messages after tick completes - they've been processed
1655
+ this.ctx?.clearQueuedMessages();
1656
+ // Record snapshot if recording is enabled
1657
+ this.recordSnapshot(currentTick, tickStartTime, {
1658
+ formatted: compiled.formatted,
1659
+ modelId: model?.metadata?.id ?? model?.metadata?.model ?? null,
1660
+ modelInput: typeof modelInput === "string" ? modelInput : JSON.stringify(modelInput),
1661
+ modelStartTime,
1662
+ modelOutput,
1663
+ toolCalls: response.toolCalls ?? [],
1664
+ toolResults,
1665
+ toolStartTime,
1666
+ shouldContinue,
1667
+ stopReason,
1668
+ });
1669
+ // Update _previousOutput after each tick so the next tick has access to the timeline
1670
+ // This is critical for <Timeline> to render conversation history on subsequent ticks
1671
+ output = await this.complete();
1672
+ this._previousOutput = output;
1673
+ this.log.debug({ timelineLength: output.timeline?.length ?? 0 }, "Updated _previousOutput after tick");
1674
+ this._tick++;
1675
+ usage.ticks = (usage.ticks ?? 0) + 1;
1676
+ }
1677
+ // Final complete (may be redundant but ensures consistency)
1678
+ output = await this.complete();
1679
+ this._previousOutput = output;
1680
+ // Accumulate usage
1681
+ this._totalUsage.inputTokens += usage.inputTokens;
1682
+ this._totalUsage.outputTokens += usage.outputTokens;
1683
+ this._totalUsage.totalTokens += usage.totalTokens;
1684
+ this._totalUsage.ticks = (this._totalUsage.ticks ?? 0) + (usage.ticks ?? 0);
1685
+ const resultPayload = {
1686
+ response: responseChunks.join(""),
1687
+ outputs,
1688
+ usage,
1689
+ stopReason,
1690
+ };
1691
+ this.emitEvent({
1692
+ type: "result",
1693
+ result: resultPayload,
1694
+ timestamp: timestamp(),
1695
+ });
1696
+ }
1697
+ finally {
1698
+ this._executionComplete = true;
1699
+ // Ensure queued messages are cleared even on error/abort paths
1700
+ // (normal path already clears at tick_end, this is a safety net)
1701
+ this.ctx?.clearQueuedMessages();
1702
+ this.emitEvent({
1703
+ type: "execution_end",
1704
+ executionId,
1705
+ stopReason,
1706
+ aborted: this._isAborted,
1707
+ usage,
1708
+ output: output ?? null,
1709
+ timestamp: timestamp(),
1710
+ });
1711
+ // Publish timeline delta to channel for real-time sync across clients
1712
+ // Only send NEW messages since last publish - O(delta) not O(n)
1713
+ if (output?.timeline) {
1714
+ const allMessages = output.timeline
1715
+ .filter((entry) => entry.message)
1716
+ .map((entry) => entry.message);
1717
+ // Only publish if there are new messages
1718
+ if (allMessages.length > this._lastPublishedTimelineLength) {
1719
+ const newMessages = allMessages.slice(this._lastPublishedTimelineLength);
1720
+ this._lastPublishedTimelineLength = allMessages.length;
1721
+ this.channel("timeline").publish({
1722
+ type: "timeline_delta",
1723
+ channel: "timeline",
1724
+ payload: {
1725
+ messages: newMessages, // Only new messages
1726
+ totalCount: allMessages.length, // For sync verification
1727
+ tick: this._tick,
1728
+ },
1729
+ });
1730
+ }
1731
+ }
1732
+ // Clear execution ID after emitting execution_end
1733
+ this._currentExecutionId = null;
1734
+ for (const resolve of this._eventResolvers) {
1735
+ resolve({ value: undefined, done: true });
1736
+ }
1737
+ this._eventResolvers = [];
1738
+ this._status = "idle";
1739
+ }
1740
+ return {
1741
+ response: responseChunks.join(""),
1742
+ outputs,
1743
+ usage,
1744
+ stopReason,
1745
+ raw: output,
1746
+ };
1747
+ }
1748
+ /**
1749
+ * Ensure compilation infrastructure exists or reset for new run.
1750
+ */
1751
+ async ensureCompilationInfrastructure(_rootElement) {
1752
+ if (this.ctx && this.compiler && this.structureRenderer) {
1753
+ // Reuse existing - reset for new run
1754
+ this.ctx.clear();
1755
+ this._tick = 1;
1756
+ return;
1757
+ }
1758
+ // Create new infrastructure
1759
+ this.ctx = new COM({
1760
+ metadata: {},
1761
+ });
1762
+ this.compiler = new FiberCompiler(this.ctx, undefined, {});
1763
+ // Apply pending hydration data if available
1764
+ if (this._pendingHydrationData) {
1765
+ this.compiler.setHydrationData(this._pendingHydrationData);
1766
+ this._pendingHydrationData = null; // Clear after applying
1767
+ }
1768
+ // Create scheduler and wire it to the compiler
1769
+ // This enables the reactive model: state changes between ticks trigger reconciliation
1770
+ this.scheduler = new ReconciliationScheduler(this.compiler, {
1771
+ onReconcile: (event) => {
1772
+ this.emit("reconcile", event);
1773
+ },
1774
+ });
1775
+ // Wire compiler state changes to scheduler
1776
+ this.compiler.setReconcileCallback((reason) => {
1777
+ this.scheduler.schedule(reason ?? "compiler recompile request");
1778
+ });
1779
+ // Wire COM recompile requests to scheduler
1780
+ // This unifies COM state signals with the reactive model
1781
+ this.ctx.setRecompileCallback((reason) => {
1782
+ this.scheduler.schedule(reason ?? "COM recompile request");
1783
+ });
1784
+ // Wire COM spawn delegate to session's spawn Procedure
1785
+ this.ctx.setSpawnCallback((agent, input) => this.spawn(agent, input));
1786
+ this.structureRenderer = new StructureRenderer(this.ctx);
1787
+ this.structureRenderer.setDefaultRenderer(new MarkdownRenderer());
1788
+ // Register tools from appOptions
1789
+ if (this.appOptions.tools) {
1790
+ for (const tool of this.appOptions.tools) {
1791
+ this.ctx.addTool(tool);
1792
+ }
1793
+ }
1794
+ // Notify compiler that compilation is starting
1795
+ await this.compiler.notifyStart();
1796
+ }
1797
+ /**
1798
+ * Compile a single tick.
1799
+ */
1800
+ async compileTick(rootElement) {
1801
+ if (!this.ctx || !this.compiler || !this.structureRenderer) {
1802
+ throw new Error("Compilation infrastructure not initialized");
1803
+ }
1804
+ // Clear COM for this tick
1805
+ this.ctx.clear();
1806
+ // NOTE: Previous timeline entries are NOT injected into COM here.
1807
+ // They are merged in at formatInput time. This keeps the architecture declarative:
1808
+ // - JSX compilation produces CompiledStructure with NEW entries
1809
+ // - formatInput merges previousTimeline + new entries
1810
+ // - No imperative accumulation during render
1811
+ // Re-register tools
1812
+ if (this.appOptions.tools) {
1813
+ for (const tool of this.appOptions.tools) {
1814
+ this.ctx.addTool(tool);
1815
+ }
1816
+ }
1817
+ // Get queued messages for TickState - these are NEW messages for this tick
1818
+ const queuedMessages = this.ctx.getQueuedMessages();
1819
+ this.log.debug({ queuedCount: queuedMessages.length }, "Queued messages available for TickState");
1820
+ // Prepare tick state
1821
+ const tickState = {
1822
+ tick: this._tick,
1823
+ previous: this._previousOutput ?? undefined,
1824
+ current: this._currentOutput,
1825
+ queuedMessages: queuedMessages,
1826
+ stop: (reason) => {
1827
+ tickState.stopReason = reason;
1828
+ },
1829
+ };
1830
+ // Notify compiler of tick start
1831
+ await this.compiler.notifyTickStart(tickState);
1832
+ // Create tick control for useTick() hook
1833
+ const tickControl = {
1834
+ requestTick: () => {
1835
+ // For now, requesting a tick schedules the next iteration
1836
+ // This will be used by the reactive model for external triggers
1837
+ this.emit("tick:request");
1838
+ },
1839
+ cancelTick: () => {
1840
+ this.emit("tick:cancel");
1841
+ },
1842
+ status: this._status,
1843
+ tickCount: this._tick,
1844
+ };
1845
+ // Create channel accessor for useChannel() hook
1846
+ const getChannel = (name) => this.channel(name);
1847
+ // Enter tick mode - scheduler will defer reconciliations until exitTick
1848
+ this.scheduler?.enterTick();
1849
+ let compiled;
1850
+ const wasHydrating = this.compiler.isHydratingNow();
1851
+ try {
1852
+ // Compile until stable
1853
+ // Note: tickControl and getChannel are available for future use
1854
+ // but not currently used by the v2 FiberCompiler
1855
+ void tickControl;
1856
+ void getChannel;
1857
+ const result = await this.compiler.compileUntilStable(rootElement, tickState, {
1858
+ maxIterations: 50,
1859
+ });
1860
+ compiled = result.compiled;
1861
+ // Complete hydration after first successful compile
1862
+ if (wasHydrating) {
1863
+ this.compiler.completeHydration();
1864
+ }
1865
+ }
1866
+ finally {
1867
+ // Exit tick mode - any pending reconciliations will now flush
1868
+ this.scheduler?.exitTick();
1869
+ }
1870
+ // Apply compiled structure (sections, tools, metadata - but NOT timeline entries)
1871
+ await this.structureRenderer.apply(compiled);
1872
+ // Format input - compiled structure IS the complete projection
1873
+ // JSX components render history as <Message>, so compiled.timelineEntries is complete
1874
+ const formatted = await this.structureRenderer.formatInput(this.ctx.toInput());
1875
+ // Track what we're sending to the model (for combining with response in complete())
1876
+ this._lastSentTimeline = formatted.timeline ?? [];
1877
+ // Track estimated context tokens for contextInfo
1878
+ this._estimatedContextTokens = formatted.totalTokens;
1879
+ // Get model from COM if not in options
1880
+ const model = this.ctx.getModel?.();
1881
+ let modelInput;
1882
+ if (model?.fromEngineState) {
1883
+ modelInput = await model.fromEngineState(formatted);
1884
+ }
1885
+ // Get tools
1886
+ const tools = this.ctx.getTools?.() ?? [];
1887
+ // Check for stop
1888
+ const stopReason = tickState.stopReason;
1889
+ return {
1890
+ formatted,
1891
+ model,
1892
+ modelInput,
1893
+ tools: tools,
1894
+ shouldStop: !!stopReason,
1895
+ stopReason,
1896
+ rawCompiled: compiled,
1897
+ };
1898
+ }
1899
+ /**
1900
+ * Execute tools and emit results.
1901
+ */
1902
+ async executeTools(executor, toolCalls, configTools, outputs, currentTick, timestamp) {
1903
+ const results = [];
1904
+ const executableTools = configTools.filter((t) => "run" in t);
1905
+ for (const call of toolCalls) {
1906
+ const startedAt = timestamp();
1907
+ // Check if OUTPUT tool
1908
+ const tool = executableTools.find((t) => t.metadata?.name === call.name);
1909
+ const isOutputTool = tool && tool.metadata?.type === "output";
1910
+ if (isOutputTool) {
1911
+ outputs[call.name] = call.input;
1912
+ const completedAt = timestamp();
1913
+ this.emitEvent({
1914
+ type: "tool_result",
1915
+ callId: call.id,
1916
+ name: call.name,
1917
+ result: call.input,
1918
+ isError: false,
1919
+ executedBy: "engine",
1920
+ startedAt,
1921
+ completedAt,
1922
+ });
1923
+ continue;
1924
+ }
1925
+ // Execute tool
1926
+ try {
1927
+ const result = await executor.processToolWithConfirmation(call, this.ctx, executableTools);
1928
+ results.push(result.result);
1929
+ const completedAt = timestamp();
1930
+ this.emitEvent({
1931
+ type: "tool_result",
1932
+ callId: result.result.toolUseId,
1933
+ name: result.result.name,
1934
+ result: result.result,
1935
+ isError: !result.result.success,
1936
+ executedBy: "engine",
1937
+ startedAt,
1938
+ completedAt,
1939
+ });
1940
+ }
1941
+ catch (error) {
1942
+ const errorResult = {
1943
+ id: randomUUID(),
1944
+ toolUseId: call.id,
1945
+ name: call.name,
1946
+ success: false,
1947
+ content: [
1948
+ {
1949
+ type: "text",
1950
+ text: `Tool execution failed: ${error instanceof Error ? error.message : String(error)}`,
1951
+ },
1952
+ ],
1953
+ };
1954
+ results.push(errorResult);
1955
+ const completedAt = timestamp();
1956
+ this.emitEvent({
1957
+ type: "tool_result",
1958
+ callId: call.id,
1959
+ name: call.name,
1960
+ result: errorResult,
1961
+ isError: true,
1962
+ executedBy: "engine",
1963
+ startedAt,
1964
+ completedAt,
1965
+ });
1966
+ }
1967
+ }
1968
+ return results;
1969
+ }
1970
+ /**
1971
+ * Ingest model response and tool results.
1972
+ */
1973
+ async ingestTickResult(response, toolResults) {
1974
+ if (!this.ctx || !this.compiler) {
1975
+ throw new Error("Compilation infrastructure not initialized");
1976
+ }
1977
+ // Convert queued user messages to timeline entries BEFORE clearing
1978
+ // This preserves the user message in the conversation history
1979
+ const queuedMessages = this.ctx.getQueuedMessages();
1980
+ const existingTimeline = this.ctx.getTimeline();
1981
+ const existingByMessage = new Map();
1982
+ for (const entry of existingTimeline) {
1983
+ if (entry.message) {
1984
+ existingByMessage.set(entry.message, entry);
1985
+ }
1986
+ }
1987
+ const userEntries = [];
1988
+ const newUserEntries = [];
1989
+ for (const queued of queuedMessages) {
1990
+ if (queued.type !== "user" || !queued.content)
1991
+ continue;
1992
+ const message = queued.content;
1993
+ const existing = existingByMessage.get(message);
1994
+ if (existing) {
1995
+ userEntries.push(existing);
1996
+ continue;
1997
+ }
1998
+ const entry = {
1999
+ kind: "message",
2000
+ message,
2001
+ tags: ["user_input"],
2002
+ };
2003
+ userEntries.push(entry);
2004
+ newUserEntries.push(entry);
2005
+ }
2006
+ // Build tool result entries
2007
+ const toolResultEntries = toolResults.length > 0
2008
+ ? [
2009
+ {
2010
+ kind: "message",
2011
+ message: {
2012
+ role: "tool",
2013
+ content: toolResults.map((r) => ({
2014
+ id: r.id,
2015
+ type: "tool_result",
2016
+ toolUseId: r.toolUseId,
2017
+ name: r.name,
2018
+ content: r.content || [],
2019
+ isError: !r.success,
2020
+ })),
2021
+ },
2022
+ tags: ["tool_output"],
2023
+ },
2024
+ ]
2025
+ : [];
2026
+ // Build current output - user messages first, then assistant response, then tool results
2027
+ const current = {
2028
+ timeline: [...userEntries, ...(response.newTimelineEntries || []), ...toolResultEntries],
2029
+ toolCalls: response.toolCalls,
2030
+ toolResults,
2031
+ };
2032
+ // Add entries to COM - user entries first, then assistant response
2033
+ for (const entry of newUserEntries) {
2034
+ this.ctx.addTimelineEntry(entry);
2035
+ }
2036
+ if (response.newTimelineEntries) {
2037
+ for (const entry of response.newTimelineEntries) {
2038
+ this.ctx.addTimelineEntry(entry);
2039
+ }
2040
+ }
2041
+ if (toolResults.length > 0) {
2042
+ this.ctx.addTimelineEntry({
2043
+ kind: "message",
2044
+ message: {
2045
+ role: "tool",
2046
+ content: toolResults.map((r) => ({
2047
+ type: "tool_result",
2048
+ toolUseId: r.toolUseId,
2049
+ name: r.name,
2050
+ content: r.content || [],
2051
+ isError: !r.success,
2052
+ })),
2053
+ },
2054
+ tags: ["tool_output"],
2055
+ });
2056
+ }
2057
+ this._currentOutput = current;
2058
+ // Resolve tick control
2059
+ const shouldStop = response.shouldStop || false;
2060
+ const stopReason = response.stopReason?.reason;
2061
+ return {
2062
+ shouldContinue: !shouldStop && (response.toolCalls?.length > 0 || false),
2063
+ stopReason,
2064
+ timeline: current.timeline,
2065
+ };
2066
+ }
2067
+ /**
2068
+ * Complete execution and return final state.
2069
+ *
2070
+ * Returns the complete timeline for this execution:
2071
+ * - What was sent to the model (from compiled JSX via _lastSentTimeline)
2072
+ * - What the model responded (from _currentOutput.timeline - only NEW entries)
2073
+ *
2074
+ * This ensures the full conversation history is preserved across executions.
2075
+ */
2076
+ async complete() {
2077
+ if (!this.ctx || !this.structureRenderer || !this.compiler) {
2078
+ throw new Error("Compilation infrastructure not initialized");
2079
+ }
2080
+ // Build complete timeline:
2081
+ // 1. _lastSentTimeline = what was sent to model (includes history rendered by JSX)
2082
+ // 2. _currentOutput.timeline = new entries from this tick (user input + model response + tool results)
2083
+ //
2084
+ // Only USER messages might overlap (rendered via JSX AND in queuedMessages).
2085
+ // Model responses (assistant/tool) are ALWAYS new and should NEVER be deduplicated.
2086
+ //
2087
+ // NOTE: We use content-based comparison for user messages because <Message {...entry.message}>
2088
+ // creates NEW message objects when rendering history, so reference equality fails.
2089
+ const sentTimeline = this._lastSentTimeline ?? [];
2090
+ const currentTimeline = this._currentOutput?.timeline ?? [];
2091
+ // Create a signature for user messages only
2092
+ const userMessageSignature = (entry) => {
2093
+ if (!entry.message || entry.message.role !== "user")
2094
+ return null;
2095
+ const m = entry.message;
2096
+ if (m.id)
2097
+ return `id:${m.id}`;
2098
+ const contentStr = JSON.stringify(m.content);
2099
+ return `user:${contentStr}`;
2100
+ };
2101
+ // Only track signatures of USER messages that were sent
2102
+ const sentUserSignatures = new Set(sentTimeline.map(userMessageSignature).filter((s) => s !== null));
2103
+ // Filter currentTimeline:
2104
+ // - USER messages: dedupe against sentTimeline (might be rendered via JSX)
2105
+ // - Other roles (assistant, tool): always include (they're new from this tick)
2106
+ const newEntries = currentTimeline.filter((entry) => {
2107
+ if (!entry.message)
2108
+ return true; // Non-message entries always included
2109
+ if (entry.message.role !== "user")
2110
+ return true; // Assistant/tool always included
2111
+ // User message: check for duplicates
2112
+ const sig = userMessageSignature(entry);
2113
+ return sig === null || !sentUserSignatures.has(sig);
2114
+ });
2115
+ const timeline = [...sentTimeline, ...newEntries];
2116
+ const comOutput = this.ctx.toInput();
2117
+ const finalOutput = {
2118
+ ...comOutput,
2119
+ timeline,
2120
+ };
2121
+ // Notify compiler of completion
2122
+ try {
2123
+ await this.compiler.notifyComplete(finalOutput);
2124
+ }
2125
+ catch {
2126
+ // Ignore completion errors
2127
+ }
2128
+ return finalOutput;
2129
+ }
2130
+ }
2131
+ //# sourceMappingURL=session.js.map