agents 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +8 -9
  2. package/dist/abortable-C1SpfVoi.js +21 -0
  3. package/dist/abortable-C1SpfVoi.js.map +1 -0
  4. package/dist/{agent-routing-0DR0Tk2v.d.ts → agent-routing-QDcchcQP.d.ts} +16 -16
  5. package/dist/agent-routing.d.ts +1 -1
  6. package/dist/agent-tool-types.d.ts +1 -1
  7. package/dist/{agent-tools-YrATZJu2.d.ts → agent-tools-DHL2XI7u.d.ts} +2 -2
  8. package/dist/agent-tools.d.ts +1 -1
  9. package/dist/ai-ahruGw7e.d.ts +239 -0
  10. package/dist/ai-sdk-Bdsf6mSA.js +2444 -0
  11. package/dist/ai-sdk-Bdsf6mSA.js.map +1 -0
  12. package/dist/apply-chunks-1FheRb9W.js +99 -0
  13. package/dist/apply-chunks-1FheRb9W.js.map +1 -0
  14. package/dist/broadcast-state-CEyI4uAK.js +69 -0
  15. package/dist/broadcast-state-CEyI4uAK.js.map +1 -0
  16. package/dist/browser/ai.d.ts +8 -227
  17. package/dist/browser/ai.js +259 -1
  18. package/dist/browser/ai.js.map +1 -0
  19. package/dist/browser/index.d.ts +56 -46
  20. package/dist/browser/index.js +4 -361
  21. package/dist/browser/{ai-sdk.d.ts → tools/ai-sdk.d.ts} +7 -7
  22. package/dist/browser/{ai-sdk.js → tools/ai-sdk.js} +6 -5
  23. package/dist/browser/tools/ai-sdk.js.map +1 -0
  24. package/dist/browser/tools/pi.d.ts +111 -0
  25. package/dist/browser/tools/pi.js +119 -0
  26. package/dist/browser/tools/pi.js.map +1 -0
  27. package/dist/browser/{tanstack-ai.d.ts → tools/tanstack-ai.d.ts} +39 -37
  28. package/dist/browser/{tanstack-ai.js → tools/tanstack-ai.js} +73 -59
  29. package/dist/browser/tools/tanstack-ai.js.map +1 -0
  30. package/dist/browser-CEdNlsaD.js +481 -0
  31. package/dist/browser-CEdNlsaD.js.map +1 -0
  32. package/dist/{browser-DE3d-RLk.d.ts → browser-D30VlpcA.d.ts} +63 -37
  33. package/dist/{browser-tool-CQ10e6Kj.js → browser-tool-4iomavs_.js} +198 -62
  34. package/dist/browser-tool-4iomavs_.js.map +1 -0
  35. package/dist/{browser-tool-DuNdlwZ9.d.ts → browser-tool-vtrPOV8e.d.ts} +25 -6
  36. package/dist/{capability-B4WbF81e.js → capability-DBAKc6UZ.js} +1 -3
  37. package/dist/capability-DBAKc6UZ.js.map +1 -0
  38. package/dist/{capability-runner-DBAnGr6L.d.ts → capability-runner-BiVUtjqC.d.ts} +9 -7
  39. package/dist/channel-Bak_6zDP.d.ts +333 -0
  40. package/dist/chat/index.d.ts +11 -9
  41. package/dist/chat/index.js +5 -194
  42. package/dist/chat/index.js.map +1 -1
  43. package/dist/chat/react.d.ts +2 -4
  44. package/dist/chat/react.js +3 -1
  45. package/dist/chat/react.js.map +1 -1
  46. package/dist/chat-sdk/index.d.ts +1 -1
  47. package/dist/chat-sdk/index.js +1 -1
  48. package/dist/cli.js +144 -0
  49. package/dist/{client-je3koD9Z.d.ts → client-BGIQKJhX.d.ts} +2 -2
  50. package/dist/{client-fyQrLGkD.js → client-BnYbNT3z.js} +29 -5
  51. package/dist/client-BnYbNT3z.js.map +1 -0
  52. package/dist/client-tools-aIBO0Fk7.d.ts +53 -0
  53. package/dist/client.d.ts +1 -1
  54. package/dist/{connector-58XDGvc3.js → connector-CtZylXdk.js} +3 -603
  55. package/dist/connector-CtZylXdk.js.map +1 -0
  56. package/dist/{connector-Cl-VqPmA.d.ts → connector-DEXzVoaY.d.ts} +9 -374
  57. package/dist/conversations-FjHDRZyD.d.ts +192 -0
  58. package/dist/{current-agent-vAk_5JMt.d.ts → current-agent-CKgT5d1O.d.ts} +3 -5
  59. package/dist/current-agent-DhoDkSnH.js.map +1 -1
  60. package/dist/daemon-core-Buubj-y1.js +481 -0
  61. package/dist/daemon-core-Buubj-y1.js.map +1 -0
  62. package/dist/daemon-core-CbohVPAu.d.ts +132 -0
  63. package/dist/{do-oauth-client-provider-Tmf1vgKz.d.ts → do-oauth-client-provider-DfsUcpc3.d.ts} +7 -1
  64. package/dist/{durable-object-lifecycle-HhgOMWq9.d.ts → durable-object-lifecycle-DOI_bzCH.d.ts} +2 -2
  65. package/dist/{channels/email.d.ts → experimental/channels/email/index.d.ts} +25 -12
  66. package/dist/{channels/email.js → experimental/channels/email/index.js} +13 -35
  67. package/dist/experimental/channels/email/index.js.map +1 -0
  68. package/dist/experimental/channels/index.d.ts +270 -0
  69. package/dist/experimental/channels/index.js +1186 -0
  70. package/dist/experimental/channels/index.js.map +1 -0
  71. package/dist/{channels/slack.d.ts → experimental/channels/slack/index.d.ts} +19 -9
  72. package/dist/experimental/channels/slack/index.js +267 -0
  73. package/dist/experimental/channels/slack/index.js.map +1 -0
  74. package/dist/{channels/telegram.d.ts → experimental/channels/telegram/index.d.ts} +19 -9
  75. package/dist/experimental/channels/telegram/index.js +198 -0
  76. package/dist/experimental/channels/telegram/index.js.map +1 -0
  77. package/dist/experimental/channels/web/ai-sdk.d.ts +55 -0
  78. package/dist/experimental/channels/web/ai-sdk.js +266 -0
  79. package/dist/experimental/channels/web/ai-sdk.js.map +1 -0
  80. package/dist/experimental/channels/web/client.d.ts +97 -0
  81. package/dist/experimental/channels/web/client.js +349 -0
  82. package/dist/experimental/channels/web/client.js.map +1 -0
  83. package/dist/experimental/channels/web/index.d.ts +189 -0
  84. package/dist/experimental/channels/web/index.js +332 -0
  85. package/dist/experimental/channels/web/index.js.map +1 -0
  86. package/dist/gateway-BFO6vRDn.d.ts +100 -0
  87. package/dist/handle-zAzh0UW2.d.ts +460 -0
  88. package/dist/harness/ai-sdk/index.d.ts +175 -0
  89. package/dist/harness/ai-sdk/index.js +605 -0
  90. package/dist/harness/ai-sdk/index.js.map +1 -0
  91. package/dist/harness/container/daemon/index.d.ts +76 -0
  92. package/dist/harness/container/daemon/index.js +3 -0
  93. package/dist/harness/container/index.d.ts +740 -0
  94. package/dist/harness/container/index.js +2152 -0
  95. package/dist/harness/container/index.js.map +1 -0
  96. package/dist/harness/container/runtime/index.d.ts +144 -0
  97. package/dist/harness/container/runtime/index.js +1082 -0
  98. package/dist/harness/container/runtime/index.js.map +1 -0
  99. package/dist/harness/opencode/index.d.ts +369 -0
  100. package/dist/harness/opencode/index.js +1072 -0
  101. package/dist/harness/opencode/index.js.map +1 -0
  102. package/dist/harness/pi/index.d.ts +3 -2
  103. package/dist/harness/pi/index.js +3 -3
  104. package/dist/harness/store/index.d.ts +24 -0
  105. package/dist/harness/store/index.js +2 -0
  106. package/dist/harness/think/index.d.ts +821 -0
  107. package/dist/harness/think/index.js +2721 -0
  108. package/dist/harness/think/index.js.map +1 -0
  109. package/dist/harness-B6B9S3rb.d.ts +203 -0
  110. package/dist/{images-iiTDSyRu.js → images-Ce9SCu3X.js} +2 -248
  111. package/dist/images-Ce9SCu3X.js.map +1 -0
  112. package/dist/{index-BreL0YrQ.d.ts → index-BEL3ZV-s.d.ts} +2 -6
  113. package/dist/index-CBt8oRJt.d.ts +198 -0
  114. package/dist/index.d.ts +5 -5
  115. package/dist/index.js +2 -2
  116. package/dist/ingress-EFbrS547.js +9 -0
  117. package/dist/ingress-EFbrS547.js.map +1 -0
  118. package/dist/input-schema-BQykTEp_.js +20 -0
  119. package/dist/input-schema-BQykTEp_.js.map +1 -0
  120. package/dist/input-schema-D2QAtHZH.js +23 -0
  121. package/dist/input-schema-D2QAtHZH.js.map +1 -0
  122. package/dist/internal-kmw70xwR.js +46 -0
  123. package/dist/internal-kmw70xwR.js.map +1 -0
  124. package/dist/internal_context.d.ts +1 -1
  125. package/dist/lifecycle/index.d.ts +3 -3
  126. package/dist/lifecycle/index.js +2 -2
  127. package/dist/{lifecycle-CFu6OV03.js → lifecycle-DGorpAmA.js} +2 -4
  128. package/dist/lifecycle-DGorpAmA.js.map +1 -0
  129. package/dist/mcp/client/do-oauth-client-provider.d.ts +1 -1
  130. package/dist/mcp/client/do-oauth-client-provider.js +9 -0
  131. package/dist/mcp/client/do-oauth-client-provider.js.map +1 -1
  132. package/dist/mcp/client/index.d.ts +1 -1
  133. package/dist/mcp/client/index.js +1 -1
  134. package/dist/mcp/client/x402.d.ts +35 -4
  135. package/dist/mcp/client/x402.js +24 -3
  136. package/dist/mcp/client/x402.js.map +1 -1
  137. package/dist/mcp/index.d.ts +1 -1
  138. package/dist/mcp/index.js +2 -2
  139. package/dist/models/ai-sdk/index.d.ts +16 -14
  140. package/dist/models/ai-sdk/index.js +1 -2442
  141. package/dist/models/opencode/index.d.ts +65 -0
  142. package/dist/models/opencode/index.js +285 -0
  143. package/dist/models/opencode/index.js.map +1 -0
  144. package/dist/models/pi-ai/index.d.ts +9 -7
  145. package/dist/models/pi-ai/index.js +2 -1
  146. package/dist/models/pi-ai/index.js.map +1 -1
  147. package/dist/options-CiIsJHXR.d.ts +130 -0
  148. package/dist/protocol-BEgrJYC5.d.ts +331 -0
  149. package/dist/protocol-CukuI73M.d.ts +386 -0
  150. package/dist/protocol-cOklVBBD.js +227 -0
  151. package/dist/protocol-cOklVBBD.js.map +1 -0
  152. package/dist/{protocol-kHsr-f0X.js → protocol-eXeJxhgu.js} +2 -67
  153. package/dist/protocol-eXeJxhgu.js.map +1 -0
  154. package/dist/protocol-y2sSY584.js +148 -0
  155. package/dist/protocol-y2sSY584.js.map +1 -0
  156. package/dist/queue/index.d.ts +1 -1
  157. package/dist/queue/index.js +1 -1
  158. package/dist/{queue-BB-zfIYF.js → queue-CcIjVYTE.js} +2 -2
  159. package/dist/{queue-BB-zfIYF.js.map → queue-CcIjVYTE.js.map} +1 -1
  160. package/dist/react.d.ts +2 -2
  161. package/dist/routing/index.d.ts +2 -2
  162. package/dist/routing/index.js +1 -1
  163. package/dist/{scheduler-RdACNXDP.d.ts → scheduler-BCLESHSC.d.ts} +2 -12
  164. package/dist/{scheduler-DZ3xQfeK.js → scheduler-FIHv2-kJ.js} +2 -4
  165. package/dist/scheduler-FIHv2-kJ.js.map +1 -0
  166. package/dist/schedules/index.d.ts +1 -1
  167. package/dist/schedules/index.js +1 -1
  168. package/dist/serializable.d.ts +1 -1
  169. package/dist/session-store-Do4xkDcf.js +606 -0
  170. package/dist/session-store-Do4xkDcf.js.map +1 -0
  171. package/dist/session-store-DoPY12xM.d.ts +383 -0
  172. package/dist/sessions/index.d.ts +11 -434
  173. package/dist/sessions/index.js +1 -2271
  174. package/dist/sessions-_vN5BEZX.js +2272 -0
  175. package/dist/sessions-_vN5BEZX.js.map +1 -0
  176. package/dist/settings-C45hDliX.js +253 -0
  177. package/dist/settings-C45hDliX.js.map +1 -0
  178. package/dist/{transport-PfFnt-Ld.d.ts → settings-NgGwF7Qt.d.ts} +14 -208
  179. package/dist/{src-BZeu-SCD.js → src-CQxJFv4l.js} +8 -8
  180. package/dist/src-CQxJFv4l.js.map +1 -0
  181. package/dist/stall-watchdog-BpRF2uwG.js +195 -0
  182. package/dist/stall-watchdog-BpRF2uwG.js.map +1 -0
  183. package/dist/state/index.d.ts +1 -1
  184. package/dist/state/index.js +1 -3
  185. package/dist/state/index.js.map +1 -1
  186. package/dist/store-BKM4Xm9M.d.ts +332 -0
  187. package/dist/store-CslVzfN7.js +458 -0
  188. package/dist/store-CslVzfN7.js.map +1 -0
  189. package/dist/streams/index.d.ts +1 -1
  190. package/dist/streams/index.js +1 -1
  191. package/dist/{streams-DW-UCOUc.js → streams-lsHMJhRW.js} +17 -4
  192. package/dist/streams-lsHMJhRW.js.map +1 -0
  193. package/dist/{streams-Cmkj9QfW.d.ts → streams-vrCtbUIn.d.ts} +2 -2
  194. package/dist/sub-routing.d.ts +1 -1
  195. package/dist/tasks/index.d.ts +1 -1
  196. package/dist/tasks/index.js +1 -1
  197. package/dist/{tasks-68DESxf0.js → tasks-BN_5AWqI.js} +3 -2
  198. package/dist/tasks-BN_5AWqI.js.map +1 -0
  199. package/dist/{tasks-9YxYtByh.d.ts → tasks-E89KdwkS.d.ts} +2 -2
  200. package/dist/tool-CTp72Jb_.d.ts +278 -0
  201. package/dist/tool-CxKsvxjm.js +1126 -0
  202. package/dist/tool-CxKsvxjm.js.map +1 -0
  203. package/dist/tool-DoVberpN.d.ts +301 -0
  204. package/dist/tool-cQRkwSZZ.js +410 -0
  205. package/dist/tool-cQRkwSZZ.js.map +1 -0
  206. package/dist/tool-helpers-O5BDVEAY.js +58 -0
  207. package/dist/tool-helpers-O5BDVEAY.js.map +1 -0
  208. package/dist/transport-k2mFrtL8.d.ts +200 -0
  209. package/dist/turns-DCV3ycDl.js +317 -0
  210. package/dist/turns-DCV3ycDl.js.map +1 -0
  211. package/dist/voice/client.js +1 -1
  212. package/dist/voice/index.d.ts +2 -2
  213. package/dist/voice/index.js +1 -1
  214. package/dist/web/fetch/index.d.ts +89 -0
  215. package/dist/web/fetch/index.js +20 -0
  216. package/dist/web/fetch/index.js.map +1 -0
  217. package/dist/web/fetch/tools/ai-sdk.d.ts +80 -0
  218. package/dist/web/fetch/tools/ai-sdk.js +47 -0
  219. package/dist/web/fetch/tools/ai-sdk.js.map +1 -0
  220. package/dist/web/fetch/tools/pi.d.ts +84 -0
  221. package/dist/web/fetch/tools/pi.js +88 -0
  222. package/dist/web/fetch/tools/pi.js.map +1 -0
  223. package/dist/web/fetch/tools/tanstack-ai.d.ts +78 -0
  224. package/dist/web/fetch/tools/tanstack-ai.js +45 -0
  225. package/dist/web/fetch/tools/tanstack-ai.js.map +1 -0
  226. package/dist/web/search/index.d.ts +58 -0
  227. package/dist/web/search/index.js +2 -0
  228. package/dist/web/search/tools/ai-sdk.d.ts +77 -0
  229. package/dist/web/search/tools/ai-sdk.js +46 -0
  230. package/dist/web/search/tools/ai-sdk.js.map +1 -0
  231. package/dist/web/search/tools/pi.d.ts +82 -0
  232. package/dist/web/search/tools/pi.js +89 -0
  233. package/dist/web/search/tools/pi.js.map +1 -0
  234. package/dist/web/search/tools/tanstack-ai.d.ts +71 -0
  235. package/dist/web/search/tools/tanstack-ai.js +46 -0
  236. package/dist/web/search/tools/tanstack-ai.js.map +1 -0
  237. package/dist/websockets/index.d.ts +12 -296
  238. package/dist/websockets/index.js +1 -1
  239. package/dist/{websockets-Ci-LZFHH.js → websockets-C3zy8YR0.js} +20 -8
  240. package/dist/websockets-C3zy8YR0.js.map +1 -0
  241. package/dist/{wire-types-CrjKvDyi.d.ts → wire-types-Cv0iwjk5.d.ts} +3 -51
  242. package/dist/workflow-types.d.ts +1 -1
  243. package/dist/workflows.d.ts +1 -1
  244. package/dist/workflows.js +1 -1
  245. package/docs/browse-the-web.md +29 -1
  246. package/docs/fetch-the-web.md +276 -0
  247. package/docs/harnesses/pi.md +25 -0
  248. package/docs/harnesses/think.md +215 -0
  249. package/docs/index.md +99 -75
  250. package/docs/lifecycle.md +42 -3
  251. package/docs/queue.md +2 -3
  252. package/docs/scheduling.md +2 -6
  253. package/docs/search-the-web.md +135 -0
  254. package/package.json +148 -44
  255. package/dist/ai-fm7xx1BK.js +0 -312
  256. package/dist/ai-fm7xx1BK.js.map +0 -1
  257. package/dist/browser/ai-sdk.js.map +0 -1
  258. package/dist/browser/index.js.map +0 -1
  259. package/dist/browser/tanstack-ai.js.map +0 -1
  260. package/dist/browser-tool-CQ10e6Kj.js.map +0 -1
  261. package/dist/capability-B4WbF81e.js.map +0 -1
  262. package/dist/channel-Bnm4S7T2.d.ts +0 -491
  263. package/dist/channels/ai-sdk.d.ts +0 -46
  264. package/dist/channels/ai-sdk.js +0 -120
  265. package/dist/channels/ai-sdk.js.map +0 -1
  266. package/dist/channels/email.js.map +0 -1
  267. package/dist/channels/index.d.ts +0 -233
  268. package/dist/channels/index.js +0 -608
  269. package/dist/channels/index.js.map +0 -1
  270. package/dist/channels/slack.js +0 -614
  271. package/dist/channels/slack.js.map +0 -1
  272. package/dist/channels/tanstack-ai.d.ts +0 -39
  273. package/dist/channels/tanstack-ai.js +0 -17
  274. package/dist/channels/tanstack-ai.js.map +0 -1
  275. package/dist/channels/telegram.js +0 -427
  276. package/dist/channels/telegram.js.map +0 -1
  277. package/dist/channels/voice.d.ts +0 -45
  278. package/dist/channels/voice.js +0 -122
  279. package/dist/channels/voice.js.map +0 -1
  280. package/dist/client-fyQrLGkD.js.map +0 -1
  281. package/dist/connector-58XDGvc3.js.map +0 -1
  282. package/dist/images-iiTDSyRu.js.map +0 -1
  283. package/dist/index-BB0kqhIz.d.ts +0 -101
  284. package/dist/ingress-BfetZbMO.js +0 -83
  285. package/dist/ingress-BfetZbMO.js.map +0 -1
  286. package/dist/internal-CYlgHl1l.js +0 -59
  287. package/dist/internal-CYlgHl1l.js.map +0 -1
  288. package/dist/lifecycle-CFu6OV03.js.map +0 -1
  289. package/dist/models/ai-sdk/index.js.map +0 -1
  290. package/dist/protocol-kHsr-f0X.js.map +0 -1
  291. package/dist/scheduler-DZ3xQfeK.js.map +0 -1
  292. package/dist/sessions/index.js.map +0 -1
  293. package/dist/src-BZeu-SCD.js.map +0 -1
  294. package/dist/streams-DW-UCOUc.js.map +0 -1
  295. package/dist/surface-bZZJqBka.js +0 -17
  296. package/dist/surface-bZZJqBka.js.map +0 -1
  297. package/dist/tasks-68DESxf0.js.map +0 -1
  298. package/dist/tool-schema-CBjGPrsQ.js +0 -31
  299. package/dist/tool-schema-CBjGPrsQ.js.map +0 -1
  300. package/dist/websockets-Ci-LZFHH.js.map +0 -1
  301. package/docs/channels.md +0 -323
@@ -0,0 +1,2721 @@
1
+ import { i as _classPrivateFieldInitSpec, n as _classPrivateFieldSet2, r as _assertClassBrand, t as _classPrivateFieldGet2 } from "../../classPrivateFieldGet2-DZBYAB34.js";
2
+ import { t as _classPrivateMethodInitSpec } from "../../classPrivateMethodInitSpec-qMjJ6sHQ.js";
3
+ import { n as bindLifecycleCapability, t as LifecycleCapability } from "../../capability-DBAKc6UZ.js";
4
+ import { n as STREAM_RESUME_NONE_REASONS, r as StreamAccumulator, t as CHAT_MESSAGE_TYPES } from "../../protocol-eXeJxhgu.js";
5
+ import { n as Streams } from "../../streams-lsHMJhRW.js";
6
+ import { i as createToolsFromClientSchemas, n as iterateWithStallWatchdog, r as parseProtocolMessage, t as ChatStreamStalledError } from "../../stall-watchdog-BpRF2uwG.js";
7
+ import { t as Sessions } from "../../sessions-_vN5BEZX.js";
8
+ import { a as toTranscriptMessage, r as toResponseChunk, t as answerToolCall } from "../../turns-DCV3ycDl.js";
9
+ import { n as openHarnessStore } from "../../store-CslVzfN7.js";
10
+ import { convertToModelMessages, stepCountIs, streamText, toUIMessageStream } from "ai";
11
+ //#region src/harness/think/events.ts
12
+ var _listeners = /* @__PURE__ */ new WeakMap();
13
+ var _observers = /* @__PURE__ */ new WeakMap();
14
+ /** Fan-out of session events to listeners, per session. */
15
+ var SessionEvents = class {
16
+ constructor() {
17
+ _classPrivateFieldInitSpec(this, _listeners, /* @__PURE__ */ new Map());
18
+ _classPrivateFieldInitSpec(this, _observers, /* @__PURE__ */ new Set());
19
+ }
20
+ /** Listen to every session. Returns the unsubscribe. */
21
+ observe(listener) {
22
+ _classPrivateFieldGet2(_observers, this).add(listener);
23
+ return () => {
24
+ _classPrivateFieldGet2(_observers, this).delete(listener);
25
+ };
26
+ }
27
+ /** Listen to one session. Returns the unsubscribe. */
28
+ subscribe(session, listener) {
29
+ let listeners = _classPrivateFieldGet2(_listeners, this).get(session);
30
+ if (!listeners) {
31
+ listeners = /* @__PURE__ */ new Set();
32
+ _classPrivateFieldGet2(_listeners, this).set(session, listeners);
33
+ }
34
+ listeners.add(listener);
35
+ return () => {
36
+ listeners.delete(listener);
37
+ if (listeners.size === 0) _classPrivateFieldGet2(_listeners, this).delete(session);
38
+ };
39
+ }
40
+ /** Deliver an event to a session's listeners. A throwing listener is logged. */
41
+ emit(session, event) {
42
+ for (const observer of _classPrivateFieldGet2(_observers, this)) try {
43
+ observer(session, event);
44
+ } catch (error) {
45
+ console.error("A ThinkHarness observer failed", error);
46
+ }
47
+ for (const listener of _classPrivateFieldGet2(_listeners, this).get(session) ?? []) try {
48
+ listener(event);
49
+ } catch (error) {
50
+ console.error("A ThinkHarness session listener failed", error);
51
+ }
52
+ }
53
+ };
54
+ var _close = /* @__PURE__ */ new WeakMap();
55
+ var _listener = /* @__PURE__ */ new WeakMap();
56
+ var _queue = /* @__PURE__ */ new WeakMap();
57
+ var _onStop = /* @__PURE__ */ new WeakMap();
58
+ var _draining = /* @__PURE__ */ new WeakMap();
59
+ var _stopped = /* @__PURE__ */ new WeakMap();
60
+ var _Watcher_brand = /* @__PURE__ */ new WeakSet();
61
+ /** One watch: events queue until `start`, then reach the listener in order. */
62
+ var Watcher = class {
63
+ constructor(state, onStop) {
64
+ _classPrivateMethodInitSpec(this, _Watcher_brand);
65
+ _classPrivateFieldInitSpec(this, _close, () => {});
66
+ _classPrivateFieldInitSpec(this, _listener, void 0);
67
+ _classPrivateFieldInitSpec(this, _queue, []);
68
+ _classPrivateFieldInitSpec(this, _onStop, void 0);
69
+ _classPrivateFieldInitSpec(this, _draining, false);
70
+ _classPrivateFieldInitSpec(this, _stopped, false);
71
+ this.state = state;
72
+ _classPrivateFieldSet2(_onStop, this, onStop);
73
+ this.closed = new Promise((resolve) => {
74
+ _classPrivateFieldSet2(_close, this, resolve);
75
+ });
76
+ }
77
+ /** Start delivering queued and later events. */
78
+ start(listener) {
79
+ _classPrivateFieldSet2(_listener, this, listener);
80
+ _assertClassBrand(_Watcher_brand, this, _drain).call(this);
81
+ }
82
+ /** Stop the watch. */
83
+ async stop() {
84
+ if (_classPrivateFieldGet2(_stopped, this)) return;
85
+ _classPrivateFieldSet2(_stopped, this, true);
86
+ _classPrivateFieldGet2(_onStop, this).call(this);
87
+ _classPrivateFieldGet2(_close, this).call(this);
88
+ }
89
+ /** Queue events for the listener. */
90
+ push(events) {
91
+ if (_classPrivateFieldGet2(_stopped, this) || events.length === 0) return;
92
+ _classPrivateFieldGet2(_queue, this).push(events);
93
+ _assertClassBrand(_Watcher_brand, this, _drain).call(this);
94
+ }
95
+ };
96
+ async function _drain() {
97
+ if (!_classPrivateFieldGet2(_listener, this) || _classPrivateFieldGet2(_draining, this)) return;
98
+ _classPrivateFieldSet2(_draining, this, true);
99
+ try {
100
+ for (;;) {
101
+ const events = _classPrivateFieldGet2(_queue, this).shift();
102
+ if (!events || _classPrivateFieldGet2(_stopped, this)) return;
103
+ try {
104
+ await _classPrivateFieldGet2(_listener, this).call(this, events);
105
+ } catch (error) {
106
+ console.error("A ThinkHarness session watcher failed", error);
107
+ }
108
+ }
109
+ } finally {
110
+ _classPrivateFieldSet2(_draining, this, false);
111
+ }
112
+ }
113
+ //#endregion
114
+ //#region src/harness/think/records.ts
115
+ /** The shared store's table prefix for ThinkHarness's records. */
116
+ const STORE_PREFIX = "cf_think_harness_store_";
117
+ /** The tables ThinkHarness kept its records in before the shared store. */
118
+ const LEGACY_SESSIONS = "cf_think_harness_sessions";
119
+ const LEGACY_OPERATIONS = "cf_think_harness_operations";
120
+ const LEGACY_TOOL_CALLS = "cf_think_harness_tool_calls";
121
+ const OPEN$1 = ["queued", "running"];
122
+ const EMPTY_STATE = {
123
+ clientTools: [],
124
+ toolCalls: {}
125
+ };
126
+ function isRecord$1(value) {
127
+ return typeof value === "object" && value !== null && !Array.isArray(value);
128
+ }
129
+ function stringOrNull(value) {
130
+ return typeof value === "string" ? value : null;
131
+ }
132
+ function count(value) {
133
+ return typeof value === "number" ? value : 0;
134
+ }
135
+ function parseMeta(value) {
136
+ const fields = isRecord$1(value) ? value : {};
137
+ return {
138
+ source: fields.source === "client" ? "client" : "server",
139
+ parentId: stringOrNull(fields.parentId),
140
+ messageId: stringOrNull(fields.messageId),
141
+ streamId: stringOrNull(fields.streamId),
142
+ pendingModel: fields.pendingModel === true,
143
+ steps: count(fields.steps),
144
+ interruptions: count(fields.interruptions),
145
+ overflowRetries: count(fields.overflowRetries),
146
+ abandonReason: stringOrNull(fields.abandonReason)
147
+ };
148
+ }
149
+ function newMeta(source) {
150
+ return {
151
+ source,
152
+ parentId: null,
153
+ messageId: null,
154
+ streamId: null,
155
+ pendingModel: false,
156
+ steps: 0,
157
+ interruptions: 0,
158
+ overflowRetries: 0,
159
+ abandonReason: null
160
+ };
161
+ }
162
+ function metaJson(meta) {
163
+ return { ...meta };
164
+ }
165
+ function withProgress(meta, change) {
166
+ return {
167
+ source: meta.source,
168
+ parentId: change.parentId === void 0 ? meta.parentId : change.parentId,
169
+ messageId: change.messageId === void 0 ? meta.messageId : change.messageId,
170
+ streamId: change.streamId === void 0 ? meta.streamId : change.streamId,
171
+ pendingModel: change.pendingModel ?? meta.pendingModel,
172
+ steps: change.steps ?? meta.steps,
173
+ interruptions: change.interruptions ?? meta.interruptions,
174
+ overflowRetries: change.overflowRetries ?? meta.overflowRetries,
175
+ abandonReason: change.abandonReason === void 0 ? meta.abandonReason : change.abandonReason
176
+ };
177
+ }
178
+ function inputJson(input) {
179
+ return input;
180
+ }
181
+ function parseInput(value) {
182
+ return value;
183
+ }
184
+ function parseText(result) {
185
+ return isRecord$1(result) && typeof result.text === "string" ? result.text : void 0;
186
+ }
187
+ function parseSessionState(value) {
188
+ if (!isRecord$1(value)) return EMPTY_STATE;
189
+ const toolCalls = Object.create(null);
190
+ const calls = value.toolCalls;
191
+ if (isRecord$1(calls)) {
192
+ for (const [toolCallId, call] of Object.entries(calls)) if (isRecord$1(call) && typeof call.operationId === "string" && typeof call.attempts === "number") toolCalls[toolCallId] = {
193
+ operationId: call.operationId,
194
+ attempts: call.attempts
195
+ };
196
+ }
197
+ return {
198
+ clientTools: Array.isArray(value.clientTools) ? value.clientTools : [],
199
+ toolCalls
200
+ };
201
+ }
202
+ function sessionStateJson(state) {
203
+ return {
204
+ clientTools: state.clientTools,
205
+ toolCalls: { ...state.toolCalls }
206
+ };
207
+ }
208
+ function parseOperation(stored) {
209
+ const meta = parseMeta(stored.meta);
210
+ return {
211
+ session: stored.session,
212
+ operationId: stored.id,
213
+ seq: stored.seq,
214
+ input: parseInput(stored.input),
215
+ status: stored.status,
216
+ source: meta.source,
217
+ parentId: meta.parentId ?? void 0,
218
+ messageId: meta.messageId ?? void 0,
219
+ streamId: meta.streamId ?? void 0,
220
+ pendingModel: meta.pendingModel,
221
+ steps: meta.steps,
222
+ interruptions: meta.interruptions,
223
+ overflowRetries: meta.overflowRetries,
224
+ reason: stored.reason,
225
+ text: stored.status === "done" ? parseText(stored.result) : void 0,
226
+ abandonReason: meta.abandonReason ?? void 0,
227
+ createdAt: stored.createdAt
228
+ };
229
+ }
230
+ var _store = /* @__PURE__ */ new WeakMap();
231
+ var _OperationRecords_brand = /* @__PURE__ */ new WeakSet();
232
+ /**
233
+ * The harness's records, over the shared harness store: sessions, the
234
+ * operation queue, and started tool calls. Opening it moves records an
235
+ * earlier version left in its own tables into the store.
236
+ */
237
+ var OperationRecords = class {
238
+ /**
239
+ * Open the records, creating the store's tables and importing any legacy
240
+ * tables. Cheap after the first time: keep one per isolate.
241
+ *
242
+ * @param storage - The Durable Object's storage.
243
+ */
244
+ constructor(storage) {
245
+ _classPrivateMethodInitSpec(this, _OperationRecords_brand);
246
+ _classPrivateFieldInitSpec(this, _store, void 0);
247
+ _classPrivateFieldSet2(_store, this, openHarnessStore(storage, { prefix: STORE_PREFIX }));
248
+ importLegacyTables(storage, _classPrivateFieldGet2(_store, this));
249
+ }
250
+ /** Record a session, unless it exists. */
251
+ ensureSession(id, parent) {
252
+ _classPrivateFieldGet2(_store, this).createSession({
253
+ id,
254
+ ...parent !== void 0 && { parent },
255
+ state: sessionStateJson(EMPTY_STATE)
256
+ });
257
+ }
258
+ /** Every session, oldest first. */
259
+ sessions() {
260
+ return _classPrivateFieldGet2(_store, this).sessions().map((session) => ({
261
+ id: session.id,
262
+ parent: session.parent,
263
+ createdAt: session.createdAt
264
+ }));
265
+ }
266
+ /** The client tools a session's client last declared. */
267
+ clientTools(session) {
268
+ return [..._assertClassBrand(_OperationRecords_brand, this, _state).call(this, session).clientTools];
269
+ }
270
+ /** Replace the client tools a session's client declared. */
271
+ setClientTools(session, tools) {
272
+ _assertClassBrand(_OperationRecords_brand, this, _updateState).call(this, session, (state) => ({
273
+ ...state,
274
+ clientTools: tools
275
+ }));
276
+ }
277
+ /** Insert a queued operation. Returns false when the id already exists. */
278
+ insert(session, operationId, input, source) {
279
+ return _classPrivateFieldGet2(_store, this).enqueue({
280
+ session,
281
+ id: operationId,
282
+ input: inputJson(input),
283
+ meta: metaJson(newMeta(source))
284
+ }).accepted;
285
+ }
286
+ /** One operation. */
287
+ get(session, operationId) {
288
+ const stored = _classPrivateFieldGet2(_store, this).operation(session, operationId);
289
+ return stored ? parseOperation(stored) : void 0;
290
+ }
291
+ /** Unsettled operations, oldest first; every session when none is given. */
292
+ open(session) {
293
+ return _classPrivateFieldGet2(_store, this).operations({
294
+ ...session !== void 0 && { session },
295
+ status: OPEN$1
296
+ }).map(parseOperation);
297
+ }
298
+ /** Change an operation's progress. */
299
+ update(session, operationId, change) {
300
+ _classPrivateFieldGet2(_store, this).transaction(() => {
301
+ const stored = _classPrivateFieldGet2(_store, this).operation(session, operationId);
302
+ if (!stored) return;
303
+ _classPrivateFieldGet2(_store, this).setOperationMeta(session, operationId, metaJson(withProgress(parseMeta(stored.meta), change)));
304
+ });
305
+ }
306
+ /**
307
+ * Move a queued operation to running, with its progress so far.
308
+ * Returns false when it was not queued.
309
+ */
310
+ start(session, operationId, change) {
311
+ return _classPrivateFieldGet2(_store, this).transaction(() => {
312
+ if (!_classPrivateFieldGet2(_store, this).start(session, operationId)) return false;
313
+ this.update(session, operationId, change);
314
+ return true;
315
+ });
316
+ }
317
+ /**
318
+ * Settle an open operation, with a last change to its progress. Returns
319
+ * false when it had already settled.
320
+ */
321
+ settle(session, operationId, outcome, change = {}) {
322
+ return _classPrivateFieldGet2(_store, this).transaction(() => {
323
+ const settled = _classPrivateFieldGet2(_store, this).settle(session, operationId, outcome.status === "done" ? {
324
+ status: "done",
325
+ result: { text: outcome.text }
326
+ } : outcome);
327
+ if (settled) this.update(session, operationId, change);
328
+ return settled;
329
+ });
330
+ }
331
+ /** Forget every settled operation in a session, for a reset. */
332
+ deleteSettled(session) {
333
+ _classPrivateFieldGet2(_store, this).transaction(() => {
334
+ _classPrivateFieldGet2(_store, this).deleteSettled(session);
335
+ _assertClassBrand(_OperationRecords_brand, this, _updateState).call(this, session, (state) => ({
336
+ ...state,
337
+ toolCalls: {}
338
+ }));
339
+ });
340
+ }
341
+ /** The record of a started tool call, if it was started. */
342
+ toolCall(session, toolCallId) {
343
+ const { toolCalls } = _assertClassBrand(_OperationRecords_brand, this, _state).call(this, session);
344
+ if (!Object.hasOwn(toolCalls, toolCallId)) return void 0;
345
+ const call = toolCalls[toolCallId];
346
+ return call && {
347
+ session,
348
+ toolCallId,
349
+ ...call
350
+ };
351
+ }
352
+ /** Record that a tool call is starting. Returns the attempt number. */
353
+ startToolCall(session, toolCallId, operationId) {
354
+ const attempts = (this.toolCall(session, toolCallId)?.attempts ?? 0) + 1;
355
+ _assertClassBrand(_OperationRecords_brand, this, _updateState).call(this, session, (state) => ({
356
+ ...state,
357
+ toolCalls: {
358
+ ...state.toolCalls,
359
+ [toolCallId]: {
360
+ operationId,
361
+ attempts
362
+ }
363
+ }
364
+ }));
365
+ return attempts;
366
+ }
367
+ /** Forget a tool call once its result is in the transcript. */
368
+ finishToolCall(session, toolCallId) {
369
+ _assertClassBrand(_OperationRecords_brand, this, _updateState).call(this, session, (state) => {
370
+ const { [toolCallId]: _finished, ...toolCalls } = state.toolCalls;
371
+ return {
372
+ ...state,
373
+ toolCalls
374
+ };
375
+ });
376
+ }
377
+ };
378
+ function _state(session) {
379
+ return parseSessionState(_classPrivateFieldGet2(_store, this).session(session)?.state);
380
+ }
381
+ function _updateState(session, change) {
382
+ _classPrivateFieldGet2(_store, this).transaction(() => {
383
+ const stored = _classPrivateFieldGet2(_store, this).session(session);
384
+ if (!stored) return;
385
+ _classPrivateFieldGet2(_store, this).setSessionState(session, sessionStateJson(change(parseSessionState(stored.state))));
386
+ });
387
+ }
388
+ function parseJsonColumn(text) {
389
+ return JSON.parse(text);
390
+ }
391
+ /**
392
+ * Move the records an earlier version kept in its own tables into the
393
+ * store, then drop those tables, in one transaction: either every record
394
+ * moves or none does and the next start tries again. Operations keep their
395
+ * order, status and progress; sessions keep their creation time, client
396
+ * tools and started tool calls, so an operation that was in flight resumes
397
+ * and recovers as it would have.
398
+ */
399
+ function importLegacyTables(storage, store) {
400
+ const sql = storage.sql;
401
+ const present = new Set(sql.exec(`SELECT name FROM sqlite_master WHERE type = 'table' AND name IN (?, ?, ?)`, LEGACY_SESSIONS, LEGACY_OPERATIONS, LEGACY_TOOL_CALLS).toArray().map((row) => row.name));
402
+ if (present.size === 0) return;
403
+ storage.transactionSync(() => {
404
+ const states = /* @__PURE__ */ new Map();
405
+ if (present.has(LEGACY_SESSIONS)) {
406
+ const rows = sql.exec(`SELECT id, parent, client_tools, created_at FROM ${LEGACY_SESSIONS}
407
+ ORDER BY created_at, id`).toArray();
408
+ for (const row of rows) {
409
+ store.createSession({
410
+ id: row.id,
411
+ ...row.parent !== null && { parent: row.parent },
412
+ createdAt: row.created_at
413
+ });
414
+ states.set(row.id, parseSessionState({
415
+ clientTools: row.client_tools === null ? [] : parseJsonColumn(row.client_tools),
416
+ toolCalls: {}
417
+ }));
418
+ }
419
+ }
420
+ if (present.has(LEGACY_TOOL_CALLS)) {
421
+ const rows = sql.exec(`SELECT session_id, tool_call_id, operation_id, attempts
422
+ FROM ${LEGACY_TOOL_CALLS}`).toArray();
423
+ for (const row of rows) {
424
+ const state = states.get(row.session_id) ?? EMPTY_STATE;
425
+ states.set(row.session_id, {
426
+ ...state,
427
+ toolCalls: {
428
+ ...state.toolCalls,
429
+ [row.tool_call_id]: {
430
+ operationId: row.operation_id,
431
+ attempts: row.attempts
432
+ }
433
+ }
434
+ });
435
+ }
436
+ }
437
+ for (const [id, state] of states) {
438
+ store.createSession({ id });
439
+ store.setSessionState(id, sessionStateJson(state));
440
+ }
441
+ if (present.has(LEGACY_OPERATIONS)) {
442
+ const rows = sql.exec(`SELECT * FROM ${LEGACY_OPERATIONS} ORDER BY seq`).toArray();
443
+ for (const row of rows) importLegacyOperation(store, row);
444
+ }
445
+ for (const table of [
446
+ LEGACY_SESSIONS,
447
+ LEGACY_OPERATIONS,
448
+ LEGACY_TOOL_CALLS
449
+ ]) sql.exec(`DROP TABLE IF EXISTS ${table}`);
450
+ });
451
+ }
452
+ function importLegacyOperation(store, row) {
453
+ const session = row.session_id;
454
+ const id = row.operation_id;
455
+ if (!store.session(session)) store.createSession({
456
+ id: session,
457
+ state: sessionStateJson(EMPTY_STATE)
458
+ });
459
+ const meta = {
460
+ source: row.source === "client" ? "client" : "server",
461
+ parentId: row.parent_id,
462
+ messageId: row.message_id,
463
+ streamId: row.stream_id,
464
+ pendingModel: row.pending_model === 1,
465
+ steps: row.steps,
466
+ interruptions: row.interruptions,
467
+ overflowRetries: row.overflow_retries,
468
+ abandonReason: row.abandon_reason ?? null
469
+ };
470
+ store.deleteOperation(session, id);
471
+ store.enqueue({
472
+ session,
473
+ id,
474
+ input: parseJsonColumn(row.input),
475
+ meta: metaJson(meta)
476
+ });
477
+ switch (row.status) {
478
+ case "queued": return;
479
+ case "running":
480
+ store.start(session, id);
481
+ return;
482
+ case "done":
483
+ store.settle(session, id, {
484
+ status: "done",
485
+ result: { text: row.text ?? "" }
486
+ });
487
+ return;
488
+ case "unanswered":
489
+ store.settle(session, id, {
490
+ status: "unanswered",
491
+ reason: row.reason ?? "unanswered"
492
+ });
493
+ return;
494
+ default: throw new Error(`Unknown ThinkHarness operation status ${row.status} in ${LEGACY_OPERATIONS}`);
495
+ }
496
+ }
497
+ //#endregion
498
+ //#region src/harness/think/tools.ts
499
+ /** The model-facing copy of a tool set, restricted to `activeTools` if given. */
500
+ function modelTools(tools, activeTools) {
501
+ const active = activeTools ? new Set(activeTools) : void 0;
502
+ const offered = {};
503
+ for (const [name, tool] of Object.entries(tools)) {
504
+ if (active && !active.has(name)) continue;
505
+ if (tool.type === "provider") {
506
+ offered[name] = tool;
507
+ continue;
508
+ }
509
+ const { execute: _execute, needsApproval: _needsApproval, ...definition } = tool;
510
+ if ("recovery" in definition) delete definition.recovery;
511
+ offered[name] = definition;
512
+ }
513
+ return offered;
514
+ }
515
+ /** Whether the harness runs this tool, as opposed to a client or provider. */
516
+ function isServerTool(tools, name) {
517
+ const tool = Object.hasOwn(tools, name) ? tools[name] : void 0;
518
+ return tool !== void 0 && tool.type !== "provider" && typeof tool.execute === "function";
519
+ }
520
+ /** Whether a tool's own `needsApproval` asks for approval of this call. */
521
+ async function toolNeedsApproval(tool, input, options) {
522
+ const needsApproval = tool.needsApproval;
523
+ if (typeof needsApproval === "function") return Boolean(await needsApproval(input, {
524
+ ...options,
525
+ context: void 0
526
+ }));
527
+ return needsApproval === true;
528
+ }
529
+ function isAsyncIterable(value) {
530
+ return typeof value === "object" && value !== null && Symbol.asyncIterator in value && typeof value[Symbol.asyncIterator] === "function";
531
+ }
532
+ /**
533
+ * Run one tool's `execute`. A streaming tool's outputs before its last are
534
+ * reported to `onPreliminary`; its last output is the result.
535
+ */
536
+ async function executeTool(tool, input, options) {
537
+ const execute = tool.execute;
538
+ if (typeof execute !== "function") return {
539
+ ok: false,
540
+ errorText: "The tool has no execute function"
541
+ };
542
+ try {
543
+ const result = await execute(input, {
544
+ toolCallId: options.toolCallId,
545
+ messages: options.messages,
546
+ abortSignal: options.abortSignal,
547
+ context: void 0
548
+ });
549
+ if (!isAsyncIterable(result)) return {
550
+ ok: true,
551
+ output: result
552
+ };
553
+ let last;
554
+ let seen = false;
555
+ for await (const output of result) {
556
+ if (seen) options.onPreliminary(last);
557
+ last = output;
558
+ seen = true;
559
+ }
560
+ return {
561
+ ok: true,
562
+ output: last
563
+ };
564
+ } catch (error) {
565
+ return {
566
+ ok: false,
567
+ errorText: error instanceof Error ? error.message : String(error)
568
+ };
569
+ }
570
+ }
571
+ //#endregion
572
+ //#region src/harness/think/turn.ts
573
+ /** Whether a part is a tool call. */
574
+ function isToolCallPart(part) {
575
+ return (part.type.startsWith("tool-") || part.type === "dynamic-tool") && "toolCallId" in part;
576
+ }
577
+ /** The tool name a tool call part carries. */
578
+ function toolNameOf(part) {
579
+ return part.type === "dynamic-tool" ? part.toolName : part.type.slice(5);
580
+ }
581
+ /** The parts of a message's last model step. */
582
+ function lastStep(message) {
583
+ let start = 0;
584
+ message.parts.forEach((part, index) => {
585
+ if (part.type === "step-start") start = index + 1;
586
+ });
587
+ return message.parts.slice(start);
588
+ }
589
+ /** Whether a tool call has its result, error or denial. */
590
+ function isSettled(part) {
591
+ return part.state === "output-available" || part.state === "output-error" || part.state === "output-denied";
592
+ }
593
+ /**
594
+ * Decide the turn's next action.
595
+ *
596
+ * @param progress - The operation's durable progress.
597
+ * @param message - The turn's assistant message, once it has one.
598
+ * @param isServerTool - Whether the harness runs a tool of this name.
599
+ * @param maxSteps - Most model calls one operation makes.
600
+ * @returns What to do next.
601
+ */
602
+ function nextAction(progress, message, isServerTool, maxSteps) {
603
+ if (progress.streamId !== void 0) return { _tag: "recover-model" };
604
+ if (message === void 0) return progress.pendingModel ? { _tag: "call-model" } : { _tag: "end" };
605
+ const runnable = [];
606
+ let awaiting = false;
607
+ for (const part of lastStep(message)) {
608
+ if (!isToolCallPart(part) || isSettled(part) || part.providerExecuted) continue;
609
+ const server = isServerTool(toolNameOf(part));
610
+ switch (part.state) {
611
+ case "input-available":
612
+ if (server) runnable.push(part);
613
+ else awaiting = true;
614
+ break;
615
+ case "approval-responded":
616
+ if (server && part.approval.approved) runnable.push(part);
617
+ else awaiting = true;
618
+ break;
619
+ case "approval-requested":
620
+ awaiting = true;
621
+ break;
622
+ case "input-streaming": break;
623
+ }
624
+ }
625
+ if (runnable.length > 0) return {
626
+ _tag: "run-tools",
627
+ calls: runnable
628
+ };
629
+ if (awaiting) return { _tag: "await-input" };
630
+ if (progress.pendingModel && progress.steps < maxSteps) return { _tag: "call-model" };
631
+ return { _tag: "end" };
632
+ }
633
+ /** The text of a message's text parts. */
634
+ function textOf(message) {
635
+ if (!message) return "";
636
+ return message.parts.map((part) => part.type === "text" ? part.text : "").join("");
637
+ }
638
+ /**
639
+ * The partial message an interrupted model call leaves, made safe to keep:
640
+ * tool calls whose input never finished streaming are dropped, since the
641
+ * model never committed to them.
642
+ */
643
+ function settledPartial(message) {
644
+ return {
645
+ ...message,
646
+ parts: message.parts.filter((part) => !(isToolCallPart(part) && part.state === "input-streaming"))
647
+ };
648
+ }
649
+ /** Whether a message has anything besides step markers. */
650
+ function hasContent(message) {
651
+ return message.parts.some((part) => part.type !== "step-start");
652
+ }
653
+ //#endregion
654
+ //#region src/harness/think/harness.ts
655
+ /**
656
+ * Thrown by `submit()` when asked to steer a running turn, which
657
+ * ThinkHarness does not support. Submit without `whenBusy` to queue the
658
+ * input behind the running turn instead.
659
+ *
660
+ * @experimental
661
+ */
662
+ var SteerNotSupportedError = class extends Error {
663
+ /** @param session - The session the submission was for. */
664
+ constructor(session) {
665
+ super("ThinkHarness does not support whenBusy: \"steer\". Submit without it to queue the input behind the running turn.");
666
+ this.session = session;
667
+ this._tag = "SteerNotSupportedError";
668
+ this.name = "SteerNotSupportedError";
669
+ }
670
+ };
671
+ /** The root session's id: the Sessions capability's default session. */
672
+ const ROOT_SESSION = "";
673
+ const WAKE_FN = "wake";
674
+ const HEARTBEAT_MS = 3e4;
675
+ const DEFAULT_MAX_STEPS = 10;
676
+ const DEFAULT_MAX_ATTEMPTS = 10;
677
+ const DEFAULT_BACKOFF_MS = 1e3;
678
+ const MAX_BACKOFF_MS = 6e4;
679
+ const DEFAULT_STALL_TIMEOUT_MS = 12e4;
680
+ const MAX_OVERFLOW_RETRIES = 1;
681
+ /** Ceiling for one stored segment, under the 2 MB SQLite row limit. */
682
+ const STREAM_MAX_CHUNK_BYTES = 19e5;
683
+ /** Chunks packed into one stored stream segment. */
684
+ const SEGMENT_CHUNKS = 10;
685
+ /** Raw bytes packed into one stored segment before it is written. */
686
+ const SEGMENT_MAX_BYTES = 256e3;
687
+ /** Longest a streamed chunk waits in memory before it is written. */
688
+ const FLUSH_AFTER_MS = 100;
689
+ /** Segments read at a time when rebuilding an interrupted model call. */
690
+ const READ_PAGE = 50;
691
+ const INTERRUPTED_TOOL_ERROR = "The tool call was interrupted before it finished, because the agent restarted. It may or may not have taken effect.";
692
+ function wakeJobId(session) {
693
+ return `think-wake:${session}`;
694
+ }
695
+ function sessionOfJob(payload) {
696
+ return typeof payload === "object" && payload !== null && "session" in payload && typeof payload.session === "string" ? payload.session : void 0;
697
+ }
698
+ async function resolve(value, session) {
699
+ if (typeof value === "function") return value({ session });
700
+ return value;
701
+ }
702
+ function errorText(error) {
703
+ return error instanceof Error ? error.message : String(error);
704
+ }
705
+ function isToolAnswer(input) {
706
+ return typeof input === "object" && !Array.isArray(input) && "type" in input && (input.type === "approval" || input.type === "tool-result");
707
+ }
708
+ function isUIMessage(input) {
709
+ return typeof input === "object" && !Array.isArray(input) && "role" in input && "parts" in input;
710
+ }
711
+ function harnessInputMessage(input, id) {
712
+ return {
713
+ id: input.messageId ?? id,
714
+ role: "user",
715
+ parts: input.parts.map((part) => part.type === "text" ? {
716
+ type: "text",
717
+ text: part.text
718
+ } : {
719
+ type: "file",
720
+ mediaType: part.mediaType,
721
+ url: part.url,
722
+ ...part.filename !== void 0 && { filename: part.filename }
723
+ })
724
+ };
725
+ }
726
+ /** The stored form of an input. */
727
+ function operationInput(input, operationId, autoContinue) {
728
+ if (typeof input === "string") return {
729
+ kind: "messages",
730
+ messages: [{
731
+ id: operationId,
732
+ role: "user",
733
+ parts: [{
734
+ type: "text",
735
+ text: input
736
+ }]
737
+ }]
738
+ };
739
+ if (Array.isArray(input)) return {
740
+ kind: "messages",
741
+ messages: input
742
+ };
743
+ if (isToolAnswer(input)) return {
744
+ kind: "answer",
745
+ answer: input,
746
+ autoContinue
747
+ };
748
+ if (isUIMessage(input)) return {
749
+ kind: "messages",
750
+ messages: [input]
751
+ };
752
+ return {
753
+ kind: "messages",
754
+ messages: [harnessInputMessage(input, operationId)]
755
+ };
756
+ }
757
+ function toolCallAnswer(answer) {
758
+ return answer.type === "approval" ? {
759
+ type: "approval-response",
760
+ approvalId: answer.approvalId,
761
+ approved: answer.approved,
762
+ ...answer.reason !== void 0 && { reason: answer.reason }
763
+ } : {
764
+ type: "tool-result",
765
+ toolCallId: answer.toolCallId,
766
+ result: answer.result
767
+ };
768
+ }
769
+ /** Replace one tool call part of a message. */
770
+ function withToolPart(message, toolCallId, update) {
771
+ return {
772
+ ...message,
773
+ parts: message.parts.map((part) => isToolCallPart(part) && part.toolCallId === toolCallId ? update(part) : part)
774
+ };
775
+ }
776
+ function withOutcome(part, outcome) {
777
+ return outcome.ok ? {
778
+ ...part,
779
+ state: "output-available",
780
+ output: outcome.output,
781
+ preliminary: void 0
782
+ } : {
783
+ ...part,
784
+ state: "output-error",
785
+ errorText: outcome.errorText
786
+ };
787
+ }
788
+ function asUIMessages(messages) {
789
+ return messages;
790
+ }
791
+ function asUIMessage(message) {
792
+ return message;
793
+ }
794
+ function asSessionMessage(message) {
795
+ return message;
796
+ }
797
+ var _options = /* @__PURE__ */ new WeakMap();
798
+ var _events = /* @__PURE__ */ new WeakMap();
799
+ var _drives = /* @__PURE__ */ new WeakMap();
800
+ var _live = /* @__PURE__ */ new WeakMap();
801
+ var _waiters = /* @__PURE__ */ new WeakMap();
802
+ var _configured = /* @__PURE__ */ new WeakMap();
803
+ var _streams = /* @__PURE__ */ new WeakMap();
804
+ var _sessions = /* @__PURE__ */ new WeakMap();
805
+ var _bound = /* @__PURE__ */ new WeakMap();
806
+ var _ownMessages = /* @__PURE__ */ new WeakMap();
807
+ var _ownClears = /* @__PURE__ */ new WeakMap();
808
+ var _heartbeatMs = /* @__PURE__ */ new WeakMap();
809
+ var _records = /* @__PURE__ */ new WeakMap();
810
+ var _ThinkHarness_brand = /* @__PURE__ */ new WeakSet();
811
+ /**
812
+ * Think's agent loop hosted in a Durable Object, behind the same small
813
+ * harness interface as `PiHarness`: `harness.prompt()`, `harness.sessions`,
814
+ * `harness.session(id)`. It implements the shared `AgentHarness`
815
+ * interface, so a Channels host can serve its sessions.
816
+ *
817
+ * Transcripts live in a Sessions capability and in-flight model output in
818
+ * a Streams capability, both owned by the harness, and the harness's queue
819
+ * of operations in its own table. The harness runs every server tool call itself, so after an
820
+ * eviction it knows exactly which call was cut short and applies that
821
+ * tool's recovery policy.
822
+ *
823
+ * Durability is the Lifecycle's job queue, used directly: one wake job per
824
+ * session. A submission is written, then the session's wake is scheduled.
825
+ * While a session has work, the wake job reschedules itself as a heartbeat,
826
+ * so an eviction leaves it due and its alarm restarts the object. Each step
827
+ * of a turn reads only durable state, so the restarted object continues
828
+ * where the last write left off.
829
+ *
830
+ * @experimental The API may change between releases.
831
+ */
832
+ var ThinkHarness = class extends LifecycleCapability {
833
+ /**
834
+ * @param options - The model, tools and hooks.
835
+ */
836
+ constructor(options) {
837
+ super("think-harness");
838
+ _classPrivateMethodInitSpec(this, _ThinkHarness_brand);
839
+ _classPrivateFieldInitSpec(this, _options, void 0);
840
+ _classPrivateFieldInitSpec(this, _events, new SessionEvents());
841
+ _classPrivateFieldInitSpec(this, _drives, /* @__PURE__ */ new Map());
842
+ _classPrivateFieldInitSpec(this, _live, /* @__PURE__ */ new Map());
843
+ _classPrivateFieldInitSpec(this, _waiters, /* @__PURE__ */ new Map());
844
+ _classPrivateFieldInitSpec(this, _configured, /* @__PURE__ */ new Set());
845
+ _classPrivateFieldInitSpec(this, _streams, new Streams({ maxChunkBytes: STREAM_MAX_CHUNK_BYTES }));
846
+ _classPrivateFieldInitSpec(this, _sessions, void 0);
847
+ _classPrivateFieldInitSpec(this, _bound, false);
848
+ _classPrivateFieldInitSpec(this, _ownMessages, /* @__PURE__ */ new Map());
849
+ _classPrivateFieldInitSpec(this, _ownClears, /* @__PURE__ */ new Map());
850
+ _classPrivateFieldInitSpec(this, _heartbeatMs, HEARTBEAT_MS);
851
+ _classPrivateFieldInitSpec(this, _records, void 0);
852
+ _classPrivateFieldSet2(_options, this, options);
853
+ _classPrivateFieldSet2(_sessions, this, new Sessions({ ...options.reservedMetadataKeys && { reservedMetadataKeys: options.reservedMetadataKeys } }));
854
+ this.sessions = new ThinkSessions(this);
855
+ }
856
+ /** Create the harness's tables and wake every session with open work. */
857
+ async onStart() {
858
+ _assertClassBrand(_ThinkHarness_brand, this, _bindOwned).call(this);
859
+ await _classPrivateFieldGet2(_sessions, this).onStart();
860
+ await _classPrivateFieldGet2(_streams, this).onStart();
861
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
862
+ store.ensureSession("");
863
+ const sessions = new Set(store.open().map((op) => op.session));
864
+ for (const session of sessions) {
865
+ const wake = this.lifecycle.jobs.get(wakeJobId(session));
866
+ if (wake && wake.time > Date.now()) continue;
867
+ await _assertClassBrand(_ThinkHarness_brand, this, _wake).call(this, session);
868
+ }
869
+ }
870
+ /**
871
+ * The alarm memory-limit breaker sealed: the work it stopped would only
872
+ * run out of memory again. Mark the running operations to be settled
873
+ * `unanswered` with reason `"out_of_memory"` rather than run again.
874
+ * Queued operations behind them still run. Runs after the isolate's
875
+ * work unwound, so it keeps to small synchronous writes.
876
+ */
877
+ onMemoryLimit(context) {
878
+ if (!context.sealed) return;
879
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
880
+ for (const op of store.open()) {
881
+ if (op.status !== "running") continue;
882
+ store.update(op.session, op.operationId, { abandonReason: "out_of_memory" });
883
+ }
884
+ }
885
+ /** Run a session's wake job. */
886
+ async onJob(context) {
887
+ if (context.job.fn !== WAKE_FN) return;
888
+ const session = sessionOfJob(context.job.payload);
889
+ if (session !== void 0) return _assertClassBrand(_ThinkHarness_brand, this, _wakeStep).call(this, session);
890
+ }
891
+ /**
892
+ * A handle on one session. No I/O until you call it.
893
+ *
894
+ * @param id - The session. Default: the root session.
895
+ */
896
+ session(id = "") {
897
+ return new ThinkSession(this, id);
898
+ }
899
+ /** Submit to the root session and wait for the answer. */
900
+ prompt(input, options = {}) {
901
+ return this.session().prompt(input, options);
902
+ }
903
+ /** Durably submit to the root session. Resolves before the model runs. */
904
+ submit(input, options = {}) {
905
+ return this.session().submit(input, options);
906
+ }
907
+ /** Operations not settled yet, oldest first, in one session or all. */
908
+ async pending(session) {
909
+ await this.lifecycle.ready();
910
+ return _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).open(session).map((op) => ({
911
+ operationId: op.operationId,
912
+ session: op.session,
913
+ status: op.status === "queued" ? "queued" : "running"
914
+ }));
915
+ }
916
+ /** @internal */
917
+ async enqueue(session, input, options) {
918
+ if (options.whenBusy === "steer") throw new SteerNotSupportedError(session);
919
+ await this.lifecycle.ready();
920
+ const operationId = options.operationId ?? crypto.randomUUID();
921
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
922
+ let accepted = false;
923
+ this.lifecycle.storage.transactionSync(() => {
924
+ store.ensureSession(session);
925
+ if (options.clientTools !== void 0) store.setClientTools(session, options.clientTools);
926
+ accepted = store.insert(session, operationId, input, options.source ?? "server");
927
+ });
928
+ if (accepted) _classPrivateFieldGet2(_events, this).emit(session, {
929
+ type: "operation",
930
+ status: {
931
+ operationId,
932
+ status: "queued"
933
+ }
934
+ });
935
+ await _assertClassBrand(_ThinkHarness_brand, this, _wake).call(this, session);
936
+ _assertClassBrand(_ThinkHarness_brand, this, _ensureDrive).call(this, session);
937
+ return {
938
+ operationId,
939
+ session,
940
+ accepted
941
+ };
942
+ }
943
+ /** @internal Stop one operation, or everything open in a session. */
944
+ async abort(session, operationId) {
945
+ await this.lifecycle.ready();
946
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
947
+ const targets = operationId === void 0 ? store.open(session) : [store.get(session, operationId)].filter((op) => op !== void 0 && (op.status === "queued" || op.status === "running"));
948
+ for (const op of targets) {
949
+ const live = _classPrivateFieldGet2(_live, this).get(session);
950
+ if (live?.operationId === op.operationId) live.abort.abort();
951
+ else if (op.status === "queued") {
952
+ store.settle(session, op.operationId, {
953
+ status: "unanswered",
954
+ reason: "withdrawn"
955
+ });
956
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, op.operationId);
957
+ } else await _assertClassBrand(_ThinkHarness_brand, this, _abandon).call(this, op, "aborted");
958
+ }
959
+ return targets.length > 0;
960
+ }
961
+ /** @internal Wait for an operation to settle. */
962
+ async wait(session, operationId, signal) {
963
+ await this.lifecycle.ready();
964
+ for (;;) {
965
+ const op = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).get(session, operationId);
966
+ if (!op) return {
967
+ operationId,
968
+ session,
969
+ status: "unanswered",
970
+ reason: "not_found"
971
+ };
972
+ if (op.status === "done" || op.status === "unanswered") return _assertClassBrand(_ThinkHarness_brand, this, _result).call(this, op);
973
+ await new Promise((resolveWait, reject) => {
974
+ if (signal?.aborted) {
975
+ reject(signal.reason);
976
+ return;
977
+ }
978
+ const key = waiterKey(session, operationId);
979
+ const waiters = _classPrivateFieldGet2(_waiters, this).get(key) ?? /* @__PURE__ */ new Set();
980
+ _classPrivateFieldGet2(_waiters, this).set(key, waiters);
981
+ const done = () => {
982
+ signal?.removeEventListener("abort", aborted);
983
+ resolveWait();
984
+ };
985
+ const aborted = () => {
986
+ waiters.delete(done);
987
+ reject(signal?.reason);
988
+ };
989
+ waiters.add(done);
990
+ signal?.addEventListener("abort", aborted, { once: true });
991
+ });
992
+ }
993
+ }
994
+ /** @internal */
995
+ async inspect(session, operationId) {
996
+ await this.lifecycle.ready();
997
+ const op = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).get(session, operationId);
998
+ if (!op) return void 0;
999
+ switch (op.status) {
1000
+ case "queued":
1001
+ case "running": return {
1002
+ operationId,
1003
+ session,
1004
+ status: op.status
1005
+ };
1006
+ case "done":
1007
+ case "unanswered": return _assertClassBrand(_ThinkHarness_brand, this, _result).call(this, op);
1008
+ }
1009
+ }
1010
+ /** @internal The active transcript of a session. */
1011
+ async messages(session) {
1012
+ return asUIMessages(await _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session).getHistory());
1013
+ }
1014
+ /** @internal Abort everything in a session and clear its transcript. */
1015
+ async reset(session, handoff) {
1016
+ await this.abort(session);
1017
+ await _classPrivateFieldGet2(_drives, this).get(session);
1018
+ const handle = _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session);
1019
+ _classPrivateFieldGet2(_ownClears, this).set(session, (_classPrivateFieldGet2(_ownClears, this).get(session) ?? 0) + 1);
1020
+ try {
1021
+ await handle.clearMessages();
1022
+ } finally {
1023
+ const left = (_classPrivateFieldGet2(_ownClears, this).get(session) ?? 1) - 1;
1024
+ if (left === 0) _classPrivateFieldGet2(_ownClears, this).delete(session);
1025
+ else _classPrivateFieldGet2(_ownClears, this).set(session, left);
1026
+ }
1027
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).deleteSettled(session);
1028
+ _classPrivateFieldGet2(_events, this).emit(session, { type: "reset" });
1029
+ if (handoff) {
1030
+ const note = {
1031
+ id: crypto.randomUUID(),
1032
+ role: "system",
1033
+ parts: [{
1034
+ type: "text",
1035
+ text: handoff
1036
+ }]
1037
+ };
1038
+ const release = _assertClassBrand(_ThinkHarness_brand, this, _markOwn).call(this, session, [note.id]);
1039
+ try {
1040
+ await handle.appendMessage(asSessionMessage(note));
1041
+ } finally {
1042
+ release();
1043
+ }
1044
+ _classPrivateFieldGet2(_events, this).emit(session, {
1045
+ type: "message",
1046
+ message: note
1047
+ });
1048
+ }
1049
+ }
1050
+ /** @internal */
1051
+ subscribe(session, listener) {
1052
+ return _classPrivateFieldGet2(_events, this).subscribe(session, listener);
1053
+ }
1054
+ /** @internal The shared harness interface's watch. */
1055
+ async watch(session) {
1056
+ await this.lifecycle.ready();
1057
+ let watcher;
1058
+ const early = [];
1059
+ const unsubscribe = _classPrivateFieldGet2(_events, this).subscribe(session, (event) => {
1060
+ const events = toSessionEvents(event);
1061
+ if (watcher) watcher.push(events);
1062
+ else early.push(events);
1063
+ });
1064
+ const live = _classPrivateFieldGet2(_live, this).get(session);
1065
+ const partial = live ? [...live.partial] : _assertClassBrand(_ThinkHarness_brand, this, _recoverableChunks).call(this, session);
1066
+ const messages = await this.messages(session);
1067
+ const open = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).open(session);
1068
+ const running = open.find((op) => op.status === "running");
1069
+ watcher = new Watcher({
1070
+ messages: messages.map((message) => toTranscriptMessage(message)),
1071
+ pending: open.map((op) => ({
1072
+ operationId: op.operationId,
1073
+ status: op.status === "queued" ? "queued" : "placed"
1074
+ })),
1075
+ ...running && { run: {
1076
+ operations: [running.operationId],
1077
+ ...partial.length > 0 && { partial: partial.flatMap((chunk) => {
1078
+ const converted = toResponseChunk(chunk, {});
1079
+ return converted ? [converted] : [];
1080
+ }) }
1081
+ } }
1082
+ }, unsubscribe);
1083
+ for (const events of early) watcher.push(events);
1084
+ return watcher;
1085
+ }
1086
+ /** @internal The running operation and its model call's chunks so far. */
1087
+ async inFlight(session) {
1088
+ await this.lifecycle.ready();
1089
+ const running = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).open(session).find((op) => op.status === "running");
1090
+ if (!running) return void 0;
1091
+ const live = _classPrivateFieldGet2(_live, this).get(session);
1092
+ if (live?.operationId === running.operationId) return {
1093
+ operationId: running.operationId,
1094
+ continuation: live.continues,
1095
+ chunks: [...live.partial]
1096
+ };
1097
+ return {
1098
+ operationId: running.operationId,
1099
+ continuation: await _assertClassBrand(_ThinkHarness_brand, this, _message).call(this, running) !== void 0,
1100
+ chunks: running.streamId ? _assertClassBrand(_ThinkHarness_brand, this, _streamChunks).call(this, running.streamId) : []
1101
+ };
1102
+ }
1103
+ /**
1104
+ * Listen to every session's events, for a host that serves several
1105
+ * sessions over one set of connections. Returns the unsubscribe.
1106
+ */
1107
+ observe(listener) {
1108
+ return _classPrivateFieldGet2(_events, this).observe(listener);
1109
+ }
1110
+ /** @internal */
1111
+ async createSession(parent) {
1112
+ await this.lifecycle.ready();
1113
+ const id = crypto.randomUUID();
1114
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).ensureSession(id, parent);
1115
+ return this.session(id);
1116
+ }
1117
+ /** @internal Copy a session's active path into a new session. */
1118
+ async fork(from) {
1119
+ const source = _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, from);
1120
+ const rows = await source.getHistoryRowStats();
1121
+ const fork = await this.createSession(from);
1122
+ const target = _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, fork.id);
1123
+ let parentId = null;
1124
+ for (const row of rows) {
1125
+ const message = await source.getMessage(row.id);
1126
+ if (!message) continue;
1127
+ await target.appendMessage(message, { parentId });
1128
+ parentId = message.id;
1129
+ }
1130
+ return fork;
1131
+ }
1132
+ /** @internal */
1133
+ async listSessions() {
1134
+ await this.lifecycle.ready();
1135
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
1136
+ const busy = new Set(store.open().map((op) => op.session));
1137
+ return store.sessions().map((record) => ({
1138
+ id: record.id,
1139
+ ...record.parent !== void 0 && { parent: record.parent },
1140
+ busy: busy.has(record.id)
1141
+ }));
1142
+ }
1143
+ /** @internal The answers to a message: its children in the tree. */
1144
+ async branches(session, messageId) {
1145
+ return asUIMessages(await _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session).getBranches(messageId));
1146
+ }
1147
+ /** @internal Full-text search over a session's messages. */
1148
+ search(session, query, options) {
1149
+ return _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session).search(query, options?.limit === void 0 ? void 0 : { limit: options.limit });
1150
+ }
1151
+ /** @internal Compact a session with its compaction function. */
1152
+ async compact(session) {
1153
+ return _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session).compact();
1154
+ }
1155
+ };
1156
+ /** Schedule the session's wake job, now or at `time`. */
1157
+ function _wake(session, time = Date.now()) {
1158
+ return this.lifecycle.jobs.push({
1159
+ id: wakeJobId(session),
1160
+ fn: WAKE_FN,
1161
+ time,
1162
+ payload: { session },
1163
+ singleflight: true,
1164
+ recoveryLoop: true
1165
+ });
1166
+ }
1167
+ /**
1168
+ * One run of a session's wake job. With work open, it makes sure a drive
1169
+ * is running and keeps a heartbeat; with none, it completes.
1170
+ */
1171
+ function _wakeStep(session) {
1172
+ const heartbeat = { rescheduleAt: Date.now() + _classPrivateFieldGet2(_heartbeatMs, this) };
1173
+ if (_classPrivateFieldGet2(_drives, this).has(session)) return heartbeat;
1174
+ if (_assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).open(session).length === 0) return void 0;
1175
+ const drive = _assertClassBrand(_ThinkHarness_brand, this, _ensureDrive).call(this, session);
1176
+ this.lifecycle.trackAlarmWork(drive);
1177
+ return heartbeat;
1178
+ }
1179
+ /** Run the session's open operations in order, unless already running. */
1180
+ function _ensureDrive(session) {
1181
+ const running = _classPrivateFieldGet2(_drives, this).get(session);
1182
+ if (running) return running;
1183
+ let wakeAt;
1184
+ const drive = (async () => {
1185
+ try {
1186
+ await this.lifecycle.runInHostContext(async () => {
1187
+ for (;;) {
1188
+ const op = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).open(session)[0];
1189
+ if (!op) return;
1190
+ const outcome = await _assertClassBrand(_ThinkHarness_brand, this, _run).call(this, op);
1191
+ if (outcome.retryAt !== void 0) {
1192
+ wakeAt = outcome.retryAt;
1193
+ return;
1194
+ }
1195
+ }
1196
+ });
1197
+ } catch (error) {
1198
+ console.error("ThinkHarness drive failed", error);
1199
+ wakeAt = Date.now() + _classPrivateFieldGet2(_heartbeatMs, this);
1200
+ } finally {
1201
+ _classPrivateFieldGet2(_drives, this).delete(session);
1202
+ }
1203
+ await _assertClassBrand(_ThinkHarness_brand, this, _wake).call(this, session, wakeAt);
1204
+ })();
1205
+ _classPrivateFieldGet2(_drives, this).set(session, drive);
1206
+ return drive;
1207
+ }
1208
+ async function _run(first) {
1209
+ const { session, operationId } = first;
1210
+ const live = {
1211
+ operationId,
1212
+ abort: new AbortController(),
1213
+ partial: [],
1214
+ continues: false
1215
+ };
1216
+ _classPrivateFieldGet2(_live, this).set(session, live);
1217
+ try {
1218
+ let op = first;
1219
+ if (op.status === "queued") {
1220
+ op = await _assertClassBrand(_ThinkHarness_brand, this, _place).call(this, op);
1221
+ if (!op) return {};
1222
+ }
1223
+ const continuation = await _assertClassBrand(_ThinkHarness_brand, this, _message).call(this, op) !== void 0;
1224
+ _classPrivateFieldGet2(_events, this).emit(session, {
1225
+ type: "run-start",
1226
+ operationId,
1227
+ continuation
1228
+ });
1229
+ try {
1230
+ return await _assertClassBrand(_ThinkHarness_brand, this, _steps).call(this, session, operationId, live);
1231
+ } finally {
1232
+ _classPrivateFieldGet2(_events, this).emit(session, {
1233
+ type: "run-end",
1234
+ operationId
1235
+ });
1236
+ }
1237
+ } catch (error) {
1238
+ console.error(`ThinkHarness operation ${operationId} failed`, error);
1239
+ const op = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).get(session, operationId);
1240
+ if (op && (op.status === "running" || op.status === "queued")) await _assertClassBrand(_ThinkHarness_brand, this, _abandon).call(this, op, errorText(error));
1241
+ return {};
1242
+ } finally {
1243
+ _classPrivateFieldGet2(_live, this).delete(session);
1244
+ }
1245
+ }
1246
+ async function _steps(session, operationId, live) {
1247
+ const maxSteps = _classPrivateFieldGet2(_options, this).maxSteps ?? DEFAULT_MAX_STEPS;
1248
+ for (;;) {
1249
+ const op = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).get(session, operationId);
1250
+ if (!op || op.status !== "running") return {};
1251
+ if (live.abort.signal.aborted) {
1252
+ await _assertClassBrand(_ThinkHarness_brand, this, _abandon).call(this, op, "aborted");
1253
+ return {};
1254
+ }
1255
+ if (op.abandonReason !== void 0) {
1256
+ await _assertClassBrand(_ThinkHarness_brand, this, _abandon).call(this, op, op.abandonReason);
1257
+ return {};
1258
+ }
1259
+ const message = await _assertClassBrand(_ThinkHarness_brand, this, _message).call(this, op);
1260
+ const tools = await _assertClassBrand(_ThinkHarness_brand, this, _tools).call(this, session);
1261
+ const action = nextAction(op, message, (name) => isServerTool(tools, name), maxSteps);
1262
+ switch (action._tag) {
1263
+ case "recover-model": {
1264
+ const outcome = await _assertClassBrand(_ThinkHarness_brand, this, _recoverModel).call(this, op, message);
1265
+ if (outcome) return outcome;
1266
+ break;
1267
+ }
1268
+ case "call-model": {
1269
+ const outcome = await _assertClassBrand(_ThinkHarness_brand, this, _callModel).call(this, op, message, tools, live);
1270
+ if (outcome) return outcome;
1271
+ break;
1272
+ }
1273
+ case "run-tools":
1274
+ if (!message) return {};
1275
+ await _assertClassBrand(_ThinkHarness_brand, this, _runTools).call(this, op, message, action.calls, tools, live);
1276
+ break;
1277
+ case "await-input":
1278
+ case "end":
1279
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, () => {
1280
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).settle(session, operationId, {
1281
+ status: "done",
1282
+ text: textOf(message)
1283
+ });
1284
+ });
1285
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId, message);
1286
+ return {};
1287
+ }
1288
+ }
1289
+ }
1290
+ /**
1291
+ * Place a queued operation: write what it brings to the transcript and
1292
+ * mark it running, in one transaction. Returns the running operation, or
1293
+ * undefined when placing settled it.
1294
+ */
1295
+ async function _place(op) {
1296
+ const { session, operationId } = op;
1297
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
1298
+ const handle = _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session);
1299
+ const input = op.input;
1300
+ const placed = [];
1301
+ let settledText;
1302
+ let unanswered;
1303
+ const running = (change) => store.start(session, operationId, {
1304
+ parentId: change.parentId ?? null,
1305
+ messageId: change.messageId,
1306
+ pendingModel: true
1307
+ });
1308
+ switch (input.kind) {
1309
+ case "messages": {
1310
+ let messages = input.messages;
1311
+ if (op.source === "client") {
1312
+ if (messages.some((message) => message.role !== "user")) {
1313
+ unanswered = "client_role";
1314
+ break;
1315
+ }
1316
+ const fresh = [];
1317
+ for (const message of messages) if (!await handle.getMessage(message.id)) fresh.push(message);
1318
+ messages = fresh;
1319
+ }
1320
+ const last = messages.at(-1);
1321
+ if (!last) {
1322
+ unanswered = "empty";
1323
+ break;
1324
+ }
1325
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, (put) => {
1326
+ let parentId;
1327
+ for (const message of messages) {
1328
+ put(message, {
1329
+ parentId,
1330
+ source: op.source
1331
+ });
1332
+ parentId = message.id;
1333
+ placed.push(message);
1334
+ }
1335
+ running({
1336
+ parentId: last.id,
1337
+ messageId: crypto.randomUUID()
1338
+ });
1339
+ });
1340
+ break;
1341
+ }
1342
+ case "answer": {
1343
+ const history = asUIMessages(await handle.getHistory());
1344
+ const tools = await _assertClassBrand(_ThinkHarness_brand, this, _tools).call(this, session);
1345
+ const answer = input.answer;
1346
+ if (answer.type === "tool-result" && op.source === "client") {
1347
+ const call = history.flatMap((message) => message.parts).find((part) => isToolCallPart(part) && part.toolCallId === answer.toolCallId);
1348
+ if (call && isServerTool(tools, toolNameOf(call))) {
1349
+ unanswered = "not_client_tool";
1350
+ break;
1351
+ }
1352
+ }
1353
+ const answered = answerToolCall(history, toolCallAnswer(answer));
1354
+ if (!answered) {
1355
+ unanswered = "not_waiting";
1356
+ break;
1357
+ }
1358
+ const latest = history.at(-1);
1359
+ const continues = input.autoContinue && latest?.id === answered.id && nextAction({
1360
+ streamId: void 0,
1361
+ pendingModel: true,
1362
+ steps: 0
1363
+ }, answered, (name) => isServerTool(tools, name), Number.POSITIVE_INFINITY)._tag !== "await-input";
1364
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, (put) => {
1365
+ put(answered);
1366
+ placed.push(answered);
1367
+ if (continues) running({ messageId: answered.id });
1368
+ else store.settle(session, operationId, {
1369
+ status: "done",
1370
+ text: textOf(answered)
1371
+ });
1372
+ });
1373
+ if (!continues) settledText = textOf(answered);
1374
+ break;
1375
+ }
1376
+ case "regenerate": {
1377
+ const history = asUIMessages(await handle.getHistory());
1378
+ const target = input.messageId ?? [...history].reverse().find((message) => message.role === "user")?.id;
1379
+ if (!target || !await handle.getMessage(target)) {
1380
+ unanswered = "not_found";
1381
+ break;
1382
+ }
1383
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, () => running({
1384
+ parentId: target,
1385
+ messageId: crypto.randomUUID()
1386
+ }));
1387
+ break;
1388
+ }
1389
+ case "continue": {
1390
+ const latest = await handle.getLatestLeaf();
1391
+ if (!latest) {
1392
+ unanswered = "not_found";
1393
+ break;
1394
+ }
1395
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, () => latest.role === "assistant" ? running({ messageId: latest.id }) : running({
1396
+ parentId: latest.id,
1397
+ messageId: crypto.randomUUID()
1398
+ }));
1399
+ break;
1400
+ }
1401
+ }
1402
+ for (const message of placed) _classPrivateFieldGet2(_events, this).emit(session, {
1403
+ type: "message",
1404
+ message,
1405
+ operationId
1406
+ });
1407
+ if (unanswered !== void 0) {
1408
+ store.settle(session, operationId, {
1409
+ status: "unanswered",
1410
+ reason: unanswered
1411
+ });
1412
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId);
1413
+ return;
1414
+ }
1415
+ if (settledText !== void 0) {
1416
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId);
1417
+ return;
1418
+ }
1419
+ _classPrivateFieldGet2(_events, this).emit(session, {
1420
+ type: "operation",
1421
+ status: {
1422
+ operationId,
1423
+ status: "placed"
1424
+ }
1425
+ });
1426
+ return store.get(session, operationId);
1427
+ }
1428
+ /**
1429
+ * One model call. Its chunks go to a stream as they arrive; when it ends,
1430
+ * the message is persisted and the stream discarded in one transaction.
1431
+ * Returns an outcome only when the operation must wait.
1432
+ */
1433
+ async function _callModel(op, existing, tools, live) {
1434
+ const { session, operationId } = op;
1435
+ const messageId = op.messageId ?? crypto.randomUUID();
1436
+ const hooks = _classPrivateFieldGet2(_options, this).hooks;
1437
+ const handle = _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session);
1438
+ const history = asUIMessages(await handle.getHistory({ leafId: existing ? existing.id : op.parentId }));
1439
+ const system = await resolve(_classPrivateFieldGet2(_options, this).system, session);
1440
+ const config = await hooks?.beforeTurn?.({
1441
+ session,
1442
+ operationId,
1443
+ step: op.steps,
1444
+ continuation: existing !== void 0,
1445
+ messages: history,
1446
+ system,
1447
+ tools: Object.keys(tools)
1448
+ }) ?? {};
1449
+ const model = config.model ?? await resolve(_classPrivateFieldGet2(_options, this).model, session);
1450
+ if (model === void 0) throw new Error("ThinkHarness has no model");
1451
+ const offered = modelTools(tools, config.activeTools);
1452
+ const effectiveSystem = config.system ?? system;
1453
+ const messages = config.messages ?? await convertToModelMessages(history, {
1454
+ tools,
1455
+ ignoreIncompleteToolCalls: true
1456
+ });
1457
+ const streamId = `think:${crypto.randomUUID()}`;
1458
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).update(session, operationId, {
1459
+ streamId,
1460
+ messageId
1461
+ });
1462
+ const writer = await _classPrivateFieldGet2(_streams, this).open(streamId, {
1463
+ tag: streamTag(session, operationId),
1464
+ metadata: {
1465
+ session,
1466
+ operationId,
1467
+ messageId
1468
+ }
1469
+ });
1470
+ const stall = new AbortController();
1471
+ const signal = AbortSignal.any([live.abort.signal, stall.signal]);
1472
+ let modelError;
1473
+ let final;
1474
+ let finishReason;
1475
+ let caught;
1476
+ live.partial = [];
1477
+ live.continues = existing !== void 0;
1478
+ let segment = [];
1479
+ let segmentBytes = 0;
1480
+ let flushTimer;
1481
+ const flush = () => {
1482
+ if (flushTimer !== void 0) clearTimeout(flushTimer);
1483
+ flushTimer = void 0;
1484
+ if (segment.length === 0) return;
1485
+ writer.append(segment);
1486
+ segment = [];
1487
+ segmentBytes = 0;
1488
+ };
1489
+ try {
1490
+ const stream = toUIMessageStream({
1491
+ stream: streamText({
1492
+ model,
1493
+ ...effectiveSystem !== void 0 && { system: effectiveSystem },
1494
+ messages,
1495
+ tools: offered,
1496
+ ...config.toolChoice !== void 0 && { toolChoice: config.toolChoice },
1497
+ ...config.providerOptions !== void 0 && { providerOptions: config.providerOptions },
1498
+ ...config.maxOutputTokens !== void 0 && { maxOutputTokens: config.maxOutputTokens },
1499
+ ...config.temperature !== void 0 && { temperature: config.temperature },
1500
+ stopWhen: stepCountIs(1),
1501
+ abortSignal: signal,
1502
+ onError: ({ error }) => {
1503
+ modelError ??= error;
1504
+ }
1505
+ }).stream,
1506
+ tools: offered,
1507
+ originalMessages: existing ? history : [],
1508
+ generateMessageId: () => messageId,
1509
+ onEnd: ({ responseMessage }) => {
1510
+ final = responseMessage;
1511
+ }
1512
+ });
1513
+ const stallTimeoutMs = _classPrivateFieldGet2(_options, this).recovery?.stallTimeoutMs ?? DEFAULT_STALL_TIMEOUT_MS;
1514
+ for await (const chunk of untilAborted(iterateWithStallWatchdog(stream, stallTimeoutMs, () => stall.abort(new ChatStreamStalledError("Model stream stalled"))), live.abort.signal)) {
1515
+ if (chunk.type === "finish") finishReason = chunk.finishReason;
1516
+ live.partial.push(chunk);
1517
+ segment.push(chunk);
1518
+ segmentBytes += JSON.stringify(chunk).length;
1519
+ if (segment.length >= SEGMENT_CHUNKS || segmentBytes >= SEGMENT_MAX_BYTES) flush();
1520
+ else flushTimer ??= setTimeout(flush, FLUSH_AFTER_MS);
1521
+ _classPrivateFieldGet2(_events, this).emit(session, {
1522
+ type: "chunk",
1523
+ operationId,
1524
+ chunk
1525
+ });
1526
+ if (hooks?.onChunk) try {
1527
+ hooks.onChunk({
1528
+ session,
1529
+ operationId,
1530
+ chunk
1531
+ });
1532
+ } catch (error) {
1533
+ console.error("ThinkHarness onChunk hook failed", error);
1534
+ }
1535
+ }
1536
+ } catch (error) {
1537
+ caught = error;
1538
+ }
1539
+ flush();
1540
+ const message = final ?? accumulate(messageId, existing, live.partial);
1541
+ const partial = message ? settledPartial(message) : void 0;
1542
+ const keep = partial && hasContent(partial) ? partial : void 0;
1543
+ const stalled = caught instanceof ChatStreamStalledError || stall.signal.aborted;
1544
+ const failure = stalled ? void 0 : modelError ?? caught;
1545
+ if (live.abort.signal.aborted) {
1546
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, streamId, (put) => {
1547
+ if (keep) put(keep, { parentId: op.parentId });
1548
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).settle(session, operationId, {
1549
+ status: "unanswered",
1550
+ reason: "aborted"
1551
+ }, { streamId: null });
1552
+ });
1553
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId, keep);
1554
+ return {};
1555
+ }
1556
+ if (stalled || failure !== void 0) {
1557
+ if (failure !== void 0) try {
1558
+ await hooks?.onError?.(failure, {
1559
+ session,
1560
+ operationId
1561
+ });
1562
+ } catch (error) {
1563
+ console.error("ThinkHarness onError hook failed", error);
1564
+ }
1565
+ const kind = stalled ? "retry" : hooks?.classifyError?.(failure) ?? "fail";
1566
+ if (kind === "context-overflow" && op.overflowRetries < MAX_OVERFLOW_RETRIES) {
1567
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, streamId, (put) => {
1568
+ if (keep) put(keep, { parentId: op.parentId });
1569
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).update(session, operationId, {
1570
+ streamId: null,
1571
+ overflowRetries: op.overflowRetries + 1
1572
+ });
1573
+ });
1574
+ try {
1575
+ await handle.compact();
1576
+ } catch (error) {
1577
+ console.warn("ThinkHarness could not compact the session", error);
1578
+ }
1579
+ return;
1580
+ }
1581
+ if (kind === "retry") return _assertClassBrand(_ThinkHarness_brand, this, _interrupted).call(this, op, streamId, keep);
1582
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, streamId, (put) => {
1583
+ if (keep) put(keep, { parentId: op.parentId });
1584
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).settle(session, operationId, {
1585
+ status: "unanswered",
1586
+ reason: errorText(failure)
1587
+ }, { streamId: null });
1588
+ });
1589
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId, keep);
1590
+ return {};
1591
+ }
1592
+ if (!message) throw new Error("The model call produced no message");
1593
+ const toolCalls = lastStep(message).some((part) => isToolCallPart(part) && !part.providerExecuted);
1594
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, streamId, (put) => {
1595
+ put(message, { parentId: op.parentId });
1596
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).update(session, operationId, {
1597
+ streamId: null,
1598
+ steps: op.steps + 1,
1599
+ interruptions: 0,
1600
+ pendingModel: toolCalls
1601
+ });
1602
+ });
1603
+ live.partial = [];
1604
+ live.continues = true;
1605
+ _classPrivateFieldGet2(_events, this).emit(session, {
1606
+ type: "message",
1607
+ message,
1608
+ operationId
1609
+ });
1610
+ await hooks?.onStepFinish?.({
1611
+ session,
1612
+ operationId,
1613
+ step: op.steps,
1614
+ message,
1615
+ finishReason
1616
+ });
1617
+ }
1618
+ /**
1619
+ * A model call an eviction cut short: keep what it produced, then call
1620
+ * the model again to continue the same message, within the budget.
1621
+ */
1622
+ async function _recoverModel(op, existing) {
1623
+ const streamId = op.streamId;
1624
+ if (streamId === void 0) return void 0;
1625
+ const chunks = _assertClassBrand(_ThinkHarness_brand, this, _streamChunks).call(this, streamId);
1626
+ const rebuilt = chunks.length > 0 ? accumulate(op.messageId ?? crypto.randomUUID(), existing, chunks) : void 0;
1627
+ const partial = rebuilt ? settledPartial(rebuilt) : void 0;
1628
+ const keep = partial && hasContent(partial) ? partial : void 0;
1629
+ return _assertClassBrand(_ThinkHarness_brand, this, _interrupted).call(this, op, streamId, keep);
1630
+ }
1631
+ /** Count an interruption, keep the partial, and decide whether to go on. */
1632
+ async function _interrupted(op, streamId, partial) {
1633
+ const { session, operationId } = op;
1634
+ const recovery = _classPrivateFieldGet2(_options, this).recovery;
1635
+ const attempt = op.interruptions + 1;
1636
+ let decision = attempt > (recovery?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS) ? "abandon" : "continue";
1637
+ if (decision === "continue" && _classPrivateFieldGet2(_options, this).hooks?.onRecovery) decision = await _classPrivateFieldGet2(_options, this).hooks.onRecovery({
1638
+ session,
1639
+ operationId,
1640
+ attempt,
1641
+ partial
1642
+ });
1643
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, streamId, (put) => {
1644
+ if (partial) put(partial, { parentId: op.parentId });
1645
+ const progress = {
1646
+ streamId: null,
1647
+ interruptions: attempt
1648
+ };
1649
+ if (decision === "abandon") _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).settle(session, operationId, {
1650
+ status: "unanswered",
1651
+ reason: "interrupted"
1652
+ }, progress);
1653
+ else _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).update(session, operationId, progress);
1654
+ });
1655
+ if (partial) _classPrivateFieldGet2(_events, this).emit(session, {
1656
+ type: "message",
1657
+ message: partial,
1658
+ operationId
1659
+ });
1660
+ if (decision === "abandon") {
1661
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId, partial);
1662
+ return {};
1663
+ }
1664
+ const delay = backoff(attempt, recovery?.backoffMs ?? DEFAULT_BACKOFF_MS);
1665
+ return delay > 0 ? { retryAt: Date.now() + delay } : void 0;
1666
+ }
1667
+ async function _runTools(op, message, calls, tools, live) {
1668
+ const { session, operationId } = op;
1669
+ const store = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this);
1670
+ const hooks = _classPrivateFieldGet2(_options, this).hooks;
1671
+ const modelMessages = await convertToModelMessages(asUIMessages(await _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session).getHistory({ leafId: message.id })).slice(0, -1), {
1672
+ tools,
1673
+ ignoreIncompleteToolCalls: true
1674
+ });
1675
+ let current = message;
1676
+ const toRun = [];
1677
+ const approvals = [];
1678
+ for (const call of calls) {
1679
+ const toolName = toolNameOf(call);
1680
+ const tool = tools[toolName];
1681
+ if (!tool) continue;
1682
+ if (call.state === "input-available") {
1683
+ if (await _classPrivateFieldGet2(_options, this).toolApproval?.({
1684
+ session,
1685
+ toolName,
1686
+ toolCallId: call.toolCallId,
1687
+ input: call.input
1688
+ }) || await toolNeedsApproval(tool, call.input, {
1689
+ toolCallId: call.toolCallId,
1690
+ messages: modelMessages
1691
+ })) {
1692
+ approvals.push(call.toolCallId);
1693
+ continue;
1694
+ }
1695
+ }
1696
+ toRun.push(call);
1697
+ }
1698
+ if (approvals.length > 0) {
1699
+ for (const toolCallId of approvals) current = withToolPart(current, toolCallId, (part) => ({
1700
+ ...part,
1701
+ state: "approval-requested",
1702
+ approval: { id: `approval:${toolCallId}` }
1703
+ }));
1704
+ const snapshot = current;
1705
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, (put) => put(snapshot));
1706
+ _classPrivateFieldGet2(_events, this).emit(session, {
1707
+ type: "message",
1708
+ message: snapshot,
1709
+ operationId
1710
+ });
1711
+ }
1712
+ await Promise.all(toRun.map(async (call) => {
1713
+ const toolName = toolNameOf(call);
1714
+ const tool = tools[toolName];
1715
+ if (!tool) return;
1716
+ const { toolCallId } = call;
1717
+ const started = store.toolCall(session, toolCallId);
1718
+ let outcome;
1719
+ if (started) {
1720
+ const policy = toolRecovery(tool);
1721
+ const maxAttempts = _classPrivateFieldGet2(_options, this).recovery?.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
1722
+ if (policy === "report" || started.attempts >= maxAttempts) outcome = {
1723
+ ok: false,
1724
+ errorText: INTERRUPTED_TOOL_ERROR
1725
+ };
1726
+ }
1727
+ let attempt = started?.attempts ?? 0;
1728
+ let input = call.input;
1729
+ if (!outcome) {
1730
+ attempt = store.startToolCall(session, toolCallId, operationId);
1731
+ const decision = await hooks?.beforeToolCall?.({
1732
+ session,
1733
+ operationId,
1734
+ toolCallId,
1735
+ toolName,
1736
+ input,
1737
+ attempt
1738
+ });
1739
+ if (decision?.action === "block") outcome = {
1740
+ ok: false,
1741
+ errorText: decision.reason
1742
+ };
1743
+ else if (decision?.action === "substitute") outcome = {
1744
+ ok: true,
1745
+ output: decision.output
1746
+ };
1747
+ else {
1748
+ if (decision?.action === "run" && decision.input !== void 0) input = decision.input;
1749
+ outcome = await executeTool(tool, input, {
1750
+ toolCallId,
1751
+ messages: modelMessages,
1752
+ abortSignal: live.abort.signal,
1753
+ onPreliminary: (output) => _classPrivateFieldGet2(_events, this).emit(session, {
1754
+ type: "chunk",
1755
+ operationId,
1756
+ chunk: {
1757
+ type: "tool-output-available",
1758
+ toolCallId,
1759
+ output,
1760
+ preliminary: true
1761
+ }
1762
+ })
1763
+ });
1764
+ }
1765
+ }
1766
+ const settled = outcome;
1767
+ await hooks?.afterToolCall?.({
1768
+ session,
1769
+ operationId,
1770
+ toolCallId,
1771
+ toolName,
1772
+ input,
1773
+ attempt,
1774
+ ...settled
1775
+ });
1776
+ current = withToolPart(current, toolCallId, (part) => withOutcome(part, settled));
1777
+ const snapshot = current;
1778
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, void 0, (put) => {
1779
+ put(snapshot);
1780
+ store.finishToolCall(session, toolCallId);
1781
+ store.update(session, operationId, { interruptions: 0 });
1782
+ });
1783
+ _classPrivateFieldGet2(_events, this).emit(session, {
1784
+ type: "chunk",
1785
+ operationId,
1786
+ chunk: settled.ok ? {
1787
+ type: "tool-output-available",
1788
+ toolCallId,
1789
+ output: settled.output
1790
+ } : {
1791
+ type: "tool-output-error",
1792
+ toolCallId,
1793
+ errorText: settled.errorText
1794
+ }
1795
+ });
1796
+ _classPrivateFieldGet2(_events, this).emit(session, {
1797
+ type: "message",
1798
+ message: snapshot,
1799
+ operationId
1800
+ });
1801
+ }));
1802
+ }
1803
+ /**
1804
+ * End an operation that cannot run on: keep what its interrupted model
1805
+ * call produced, mark any tool call it left open as failed, and settle
1806
+ * it `unanswered`.
1807
+ */
1808
+ async function _abandon(op, reason) {
1809
+ const { session, operationId } = op;
1810
+ const existing = await _assertClassBrand(_ThinkHarness_brand, this, _message).call(this, op);
1811
+ let message = existing;
1812
+ const chunks = op.streamId === void 0 ? [] : _assertClassBrand(_ThinkHarness_brand, this, _streamChunks).call(this, op.streamId);
1813
+ if (chunks.length > 0) {
1814
+ const rebuilt = accumulate(op.messageId ?? crypto.randomUUID(), existing, chunks);
1815
+ if (rebuilt) message = settledPartial(rebuilt);
1816
+ }
1817
+ if (message) {
1818
+ for (const part of lastStep(message)) if (isToolCallPart(part) && !part.providerExecuted && (part.state === "input-available" || part.state === "approval-responded")) message = withToolPart(message, part.toolCallId, (call) => withOutcome(call, {
1819
+ ok: false,
1820
+ errorText: reason
1821
+ }));
1822
+ }
1823
+ const keep = message && hasContent(message) && message !== existing ? message : void 0;
1824
+ await _assertClassBrand(_ThinkHarness_brand, this, _commit).call(this, session, op.streamId, (put) => {
1825
+ if (keep) put(keep, { parentId: op.parentId });
1826
+ _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).settle(session, operationId, {
1827
+ status: "unanswered",
1828
+ reason
1829
+ }, { streamId: null });
1830
+ });
1831
+ if (keep) _classPrivateFieldGet2(_events, this).emit(session, {
1832
+ type: "message",
1833
+ message: keep,
1834
+ operationId
1835
+ });
1836
+ await _assertClassBrand(_ThinkHarness_brand, this, _settled).call(this, session, operationId, keep ?? existing);
1837
+ }
1838
+ /** Tell everyone an operation settled. Its row is already written. */
1839
+ async function _settled(session, operationId, message) {
1840
+ const op = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).get(session, operationId);
1841
+ if (!op) return;
1842
+ const result = _assertClassBrand(_ThinkHarness_brand, this, _result).call(this, op);
1843
+ _classPrivateFieldGet2(_events, this).emit(session, {
1844
+ type: "operation",
1845
+ status: result.status === "done" ? {
1846
+ operationId,
1847
+ status: "done",
1848
+ ...result.text !== void 0 && { text: result.text }
1849
+ } : {
1850
+ operationId,
1851
+ status: "unanswered",
1852
+ ...result.reason !== void 0 && { reason: result.reason }
1853
+ }
1854
+ });
1855
+ const key = waiterKey(session, operationId);
1856
+ for (const waiter of _classPrivateFieldGet2(_waiters, this).get(key) ?? []) waiter();
1857
+ _classPrivateFieldGet2(_waiters, this).delete(key);
1858
+ try {
1859
+ await _classPrivateFieldGet2(_options, this).hooks?.onTurnEnd?.({
1860
+ ...result,
1861
+ ...message && { message }
1862
+ });
1863
+ } catch (error) {
1864
+ console.error("ThinkHarness onTurnEnd hook failed", error);
1865
+ }
1866
+ }
1867
+ function _result(op) {
1868
+ const { operationId, session } = op;
1869
+ return op.status === "done" ? {
1870
+ operationId,
1871
+ session,
1872
+ status: "done",
1873
+ text: op.text ?? "",
1874
+ ...op.messageId !== void 0 && { messageId: op.messageId }
1875
+ } : {
1876
+ operationId,
1877
+ session,
1878
+ status: "unanswered",
1879
+ ...op.reason !== void 0 && { reason: op.reason }
1880
+ };
1881
+ }
1882
+ function _tables() {
1883
+ _classPrivateFieldGet2(_records, this) ?? _classPrivateFieldSet2(_records, this, new OperationRecords(this.lifecycle.storage));
1884
+ return _classPrivateFieldGet2(_records, this);
1885
+ }
1886
+ /** The Sessions handle for a session, configured on first use. */
1887
+ /**
1888
+ * Give the Sessions and Streams the harness owns this capability's
1889
+ * Lifecycle services. Done on first use, not only in `onStart`, because an
1890
+ * RPC can read a transcript before startup (the read itself then waits
1891
+ * for startup).
1892
+ */
1893
+ function _bindOwned() {
1894
+ if (_classPrivateFieldGet2(_bound, this)) return;
1895
+ bindLifecycleCapability(_classPrivateFieldGet2(_sessions, this), this.lifecycle);
1896
+ bindLifecycleCapability(_classPrivateFieldGet2(_streams, this), this.lifecycle);
1897
+ _classPrivateFieldSet2(_bound, this, true);
1898
+ _classPrivateFieldGet2(_sessions, this).subscribe((change) => {
1899
+ const session = change.sessionId;
1900
+ switch (change.type) {
1901
+ case "append":
1902
+ case "update":
1903
+ case "import":
1904
+ if (_assertClassBrand(_ThinkHarness_brand, this, _takeOwn).call(this, session, change.message.id)) return;
1905
+ _classPrivateFieldGet2(_events, this).emit(session, {
1906
+ type: "message",
1907
+ message: asUIMessage(change.message)
1908
+ });
1909
+ return;
1910
+ case "clear":
1911
+ if ((_classPrivateFieldGet2(_ownClears, this).get(session) ?? 0) > 0) return;
1912
+ _classPrivateFieldGet2(_events, this).emit(session, { type: "reset" });
1913
+ return;
1914
+ case "delete":
1915
+ case "compact":
1916
+ case "compaction":
1917
+ _classPrivateFieldGet2(_events, this).emit(session, { type: "transcript" });
1918
+ return;
1919
+ }
1920
+ });
1921
+ }
1922
+ /** Note messages the harness is about to write; returns the release. */
1923
+ function _markOwn(session, ids) {
1924
+ let own = _classPrivateFieldGet2(_ownMessages, this).get(session);
1925
+ if (!own) {
1926
+ own = /* @__PURE__ */ new Map();
1927
+ _classPrivateFieldGet2(_ownMessages, this).set(session, own);
1928
+ }
1929
+ for (const id of ids) own.set(id, (own.get(id) ?? 0) + 1);
1930
+ return () => {
1931
+ const current = _classPrivateFieldGet2(_ownMessages, this).get(session);
1932
+ if (!current) return;
1933
+ for (const id of ids) {
1934
+ const left = (current.get(id) ?? 1) - 1;
1935
+ if (left <= 0) current.delete(id);
1936
+ else current.set(id, left);
1937
+ }
1938
+ if (current.size === 0) _classPrivateFieldGet2(_ownMessages, this).delete(session);
1939
+ };
1940
+ }
1941
+ /** Whether a change is one of the harness's own writes; consumes the mark. */
1942
+ function _takeOwn(session, id) {
1943
+ const own = _classPrivateFieldGet2(_ownMessages, this).get(session);
1944
+ const count = own?.get(id);
1945
+ if (!own || count === void 0) return false;
1946
+ if (count <= 1) own.delete(id);
1947
+ else own.set(id, count - 1);
1948
+ if (own.size === 0) _classPrivateFieldGet2(_ownMessages, this).delete(session);
1949
+ return true;
1950
+ }
1951
+ function _handle$1(session) {
1952
+ _assertClassBrand(_ThinkHarness_brand, this, _bindOwned).call(this);
1953
+ const handle = _classPrivateFieldGet2(_sessions, this).session(session);
1954
+ if (!_classPrivateFieldGet2(_configured, this).has(session)) {
1955
+ _classPrivateFieldGet2(_configured, this).add(session);
1956
+ _classPrivateFieldGet2(_options, this).configureSession?.(handle, session);
1957
+ }
1958
+ return handle;
1959
+ }
1960
+ async function _message(op) {
1961
+ if (op.messageId === void 0) return void 0;
1962
+ const message = await _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, op.session).getMessage(op.messageId);
1963
+ return message ? asUIMessages([message])[0] : void 0;
1964
+ }
1965
+ async function _tools(session) {
1966
+ const tools = await resolve(_classPrivateFieldGet2(_options, this).tools, session) ?? {};
1967
+ const client = createToolsFromClientSchemas(_assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).clientTools(session));
1968
+ for (const name of Object.keys(client)) if (Object.hasOwn(tools, name)) console.warn(`ThinkHarness ignored a client tool named "${name}": a server tool has that name`);
1969
+ return {
1970
+ ...client,
1971
+ ...tools
1972
+ };
1973
+ }
1974
+ /**
1975
+ * Persist messages and operation changes in one transaction; with a
1976
+ * stream id, the transaction also settles and discards that stream (the
1977
+ * cutover), so an eviction leaves either the stream or the message.
1978
+ */
1979
+ async function _commit(session, streamId, writes) {
1980
+ const sync = _assertClassBrand(_ThinkHarness_brand, this, _handle$1).call(this, session).__DO_NOT_USE_WILL_BREAK__sync();
1981
+ const afters = [];
1982
+ const ids = [];
1983
+ const commit = () => {
1984
+ afters.length = 0;
1985
+ ids.length = 0;
1986
+ writes((message, options = {}) => {
1987
+ ids.push(message.id);
1988
+ afters.push(sync.upsert(asSessionMessage(message), {
1989
+ ...options.parentId !== void 0 && { parentId: options.parentId },
1990
+ source: options.source ?? "server"
1991
+ }).after);
1992
+ });
1993
+ };
1994
+ try {
1995
+ if (!(streamId !== void 0 && _classPrivateFieldGet2(_streams, this).__DO_NOT_USE_WILL_BREAK__sync().settle(streamId, "completed", null, {
1996
+ commit,
1997
+ discard: true
1998
+ }))) this.lifecycle.storage.transactionSync(commit);
1999
+ } catch (error) {
2000
+ sync.abandon();
2001
+ throw error;
2002
+ }
2003
+ const release = _assertClassBrand(_ThinkHarness_brand, this, _markOwn).call(this, session, ids);
2004
+ try {
2005
+ for (const after of afters) await after();
2006
+ } finally {
2007
+ release();
2008
+ }
2009
+ }
2010
+ /** Every chunk a stream holds, without waiting for more. */
2011
+ function _streamChunks(streamId) {
2012
+ const streams = _classPrivateFieldGet2(_streams, this).__DO_NOT_USE_WILL_BREAK__sync();
2013
+ if (!streams.getStream(streamId)) return [];
2014
+ const chunks = [];
2015
+ let from = 0;
2016
+ for (;;) {
2017
+ const rows = streams.readChunks(streamId, from, READ_PAGE);
2018
+ for (const row of rows) {
2019
+ const segment = JSON.parse(row.chunk);
2020
+ chunks.push(...segment);
2021
+ }
2022
+ if (rows.length < READ_PAGE) return chunks;
2023
+ from = (rows.at(-1)?.seq ?? from) + 1;
2024
+ }
2025
+ }
2026
+ /** Chunks an evicted model call left in its stream, for a late watcher. */
2027
+ function _recoverableChunks(session) {
2028
+ const running = _assertClassBrand(_ThinkHarness_brand, this, _tables).call(this).open(session).find((op) => op.status === "running");
2029
+ return running?.streamId ? _assertClassBrand(_ThinkHarness_brand, this, _streamChunks).call(this, running.streamId) : [];
2030
+ }
2031
+ /**
2032
+ * Rebuild a message from the chunks of a model call, applied on top of the
2033
+ * message as it was before the call.
2034
+ */
2035
+ function accumulate(messageId, existing, chunks) {
2036
+ if (chunks.length === 0) return existing;
2037
+ const accumulator = new StreamAccumulator({
2038
+ messageId: existing?.id ?? messageId,
2039
+ continuation: existing !== void 0,
2040
+ ...existing && {
2041
+ existingParts: existing.parts,
2042
+ ...isRecord(existing.metadata) && { existingMetadata: existing.metadata }
2043
+ }
2044
+ });
2045
+ for (const chunk of chunks) accumulator.applyChunk(chunk);
2046
+ return {
2047
+ ...accumulator.toMessage(),
2048
+ role: "assistant"
2049
+ };
2050
+ }
2051
+ /**
2052
+ * Stop iterating as soon as `signal` aborts, even if the source never
2053
+ * notices: a model stream that ignores its abort signal must not hold an
2054
+ * aborted turn open.
2055
+ */
2056
+ async function* untilAborted(source, signal) {
2057
+ const iterator = source[Symbol.asyncIterator]();
2058
+ let onAbort = () => {};
2059
+ const aborted = new Promise((resolveAbort) => {
2060
+ onAbort = () => resolveAbort("aborted");
2061
+ if (signal.aborted) onAbort();
2062
+ else signal.addEventListener("abort", onAbort, { once: true });
2063
+ });
2064
+ try {
2065
+ for (;;) {
2066
+ const next = await Promise.race([iterator.next(), aborted]);
2067
+ if (next === "aborted") {
2068
+ iterator.return?.().catch(() => {});
2069
+ return;
2070
+ }
2071
+ if (next.done) return;
2072
+ yield next.value;
2073
+ }
2074
+ } finally {
2075
+ signal.removeEventListener("abort", onAbort);
2076
+ }
2077
+ }
2078
+ /**
2079
+ * Classify the context-window overflow errors of common providers
2080
+ * (Anthropic, OpenAI, Google, Bedrock, Mistral and others) as
2081
+ * `"context-overflow"`, and everything else as `"fail"`. Pass it as
2082
+ * `hooks.classifyError`, or call it from your own classifier.
2083
+ *
2084
+ * @param error - The model call's error.
2085
+ * @returns `"context-overflow"` or `"fail"`.
2086
+ * @experimental
2087
+ */
2088
+ function classifyContextOverflow(error) {
2089
+ let text;
2090
+ if (error instanceof Error) text = error.message;
2091
+ else if (typeof error === "string") text = error;
2092
+ else try {
2093
+ text = JSON.stringify(error);
2094
+ } catch {
2095
+ text = String(error);
2096
+ }
2097
+ return CONTEXT_OVERFLOW_PATTERN.test(text) ? "context-overflow" : "fail";
2098
+ }
2099
+ const CONTEXT_OVERFLOW_PATTERN = /prompt is too long|context[_ ]length[_ ]exceeded|maximum context length|exceeds the maximum number of tokens|input token count|reduce the length of|input is too long|too many (?:input )?tokens|context window/i;
2100
+ /** A tool's own recovery field, or the default. */
2101
+ function toolRecovery(tool) {
2102
+ return "recovery" in tool && tool.recovery === "rerun" ? "rerun" : "report";
2103
+ }
2104
+ function isRecord(value) {
2105
+ return typeof value === "object" && value !== null;
2106
+ }
2107
+ function backoff(attempt, baseMs) {
2108
+ if (attempt <= 1) return 0;
2109
+ return Math.min(baseMs * 2 ** (attempt - 2), MAX_BACKOFF_MS);
2110
+ }
2111
+ function streamTag(session, operationId) {
2112
+ return `think:${JSON.stringify([session, operationId])}`;
2113
+ }
2114
+ function waiterKey(session, operationId) {
2115
+ return JSON.stringify([session, operationId]);
2116
+ }
2117
+ /** Project a session event onto the shared harness interface's events. */
2118
+ function toSessionEvents(event) {
2119
+ switch (event.type) {
2120
+ case "operation": return [{
2121
+ type: "operation",
2122
+ status: event.status
2123
+ }];
2124
+ case "run-start": return [{
2125
+ type: "run-start",
2126
+ operations: [event.operationId]
2127
+ }];
2128
+ case "run-end": return [{
2129
+ type: "run-end",
2130
+ operations: [event.operationId]
2131
+ }];
2132
+ case "chunk": {
2133
+ const chunk = toResponseChunk(event.chunk, {});
2134
+ return chunk ? [{
2135
+ type: "chunk",
2136
+ chunk
2137
+ }] : [];
2138
+ }
2139
+ case "message": return [{
2140
+ type: "message",
2141
+ message: toTranscriptMessage(event.message)
2142
+ }];
2143
+ case "reset": return [{ type: "reset" }];
2144
+ case "transcript": return [];
2145
+ }
2146
+ }
2147
+ var _harness$1 = /* @__PURE__ */ new WeakMap();
2148
+ /**
2149
+ * One session, addressed through the harness.
2150
+ *
2151
+ * @experimental
2152
+ */
2153
+ var ThinkSession = class {
2154
+ /**
2155
+ * @param harness - The harness that owns the session.
2156
+ * @param id - The session's id.
2157
+ */
2158
+ constructor(harness, id) {
2159
+ _classPrivateFieldInitSpec(this, _harness$1, void 0);
2160
+ _classPrivateFieldSet2(_harness$1, this, harness);
2161
+ this.id = id;
2162
+ }
2163
+ /** Durably submit input. Resolves before the model runs. */
2164
+ submit(input, options = {}) {
2165
+ const operationId = options.operationId ?? crypto.randomUUID();
2166
+ return _classPrivateFieldGet2(_harness$1, this).enqueue(this.id, operationInput(input, operationId, options.autoContinue ?? true), {
2167
+ ...options,
2168
+ operationId
2169
+ });
2170
+ }
2171
+ /** Submit and wait for the answer and the updated transcript. */
2172
+ async prompt(input, options = {}) {
2173
+ const receipt = await this.submit(input, options);
2174
+ return {
2175
+ ...await this.wait(receipt.operationId),
2176
+ messages: await this.messages()
2177
+ };
2178
+ }
2179
+ /**
2180
+ * Answer a user message again. The new answer is a branch beside the
2181
+ * earlier ones, which `Session.getBranches()` still reads.
2182
+ *
2183
+ * @param messageId - The user message. Default: the latest one.
2184
+ */
2185
+ regenerate(messageId, options = {}) {
2186
+ return _classPrivateFieldGet2(_harness$1, this).enqueue(this.id, {
2187
+ kind: "regenerate",
2188
+ ...messageId !== void 0 && { messageId }
2189
+ }, options);
2190
+ }
2191
+ /** Call the model again to continue the latest message. */
2192
+ continue(options = {}) {
2193
+ return _classPrivateFieldGet2(_harness$1, this).enqueue(this.id, { kind: "continue" }, options);
2194
+ }
2195
+ /** Wait for an operation to settle. Aborting `signal` stops only the wait. */
2196
+ wait(operationId, signal) {
2197
+ return _classPrivateFieldGet2(_harness$1, this).wait(this.id, operationId, signal);
2198
+ }
2199
+ /**
2200
+ * Withdraw one queued operation or abort it while it runs; with no id,
2201
+ * everything open in the session.
2202
+ */
2203
+ abort(operationId) {
2204
+ return _classPrivateFieldGet2(_harness$1, this).abort(this.id, operationId);
2205
+ }
2206
+ /** Abort everything and clear the transcript, optionally leaving a note. */
2207
+ reset(handoff) {
2208
+ return _classPrivateFieldGet2(_harness$1, this).reset(this.id, handoff);
2209
+ }
2210
+ /** The active transcript. */
2211
+ messages() {
2212
+ return _classPrivateFieldGet2(_harness$1, this).messages(this.id);
2213
+ }
2214
+ /** Operations not settled yet, oldest first. */
2215
+ pending() {
2216
+ return _classPrivateFieldGet2(_harness$1, this).pending(this.id);
2217
+ }
2218
+ /**
2219
+ * The running operation, with the chunks its model call has streamed so
2220
+ * far, including those an eviction left behind. Send them, then
2221
+ * `subscribe()`'s chunk events, to a client that joins mid-turn.
2222
+ */
2223
+ inFlight() {
2224
+ return _classPrivateFieldGet2(_harness$1, this).inFlight(this.id);
2225
+ }
2226
+ /**
2227
+ * The answers to a message: its children in the transcript tree. A
2228
+ * regenerated answer is a branch beside the earlier ones.
2229
+ *
2230
+ * @param messageId - The message, usually a user message.
2231
+ */
2232
+ branches(messageId) {
2233
+ return _classPrivateFieldGet2(_harness$1, this).branches(this.id, messageId);
2234
+ }
2235
+ /** Full-text search over this session's messages. */
2236
+ search(query, options) {
2237
+ return _classPrivateFieldGet2(_harness$1, this).search(this.id, query, options);
2238
+ }
2239
+ /**
2240
+ * Summarize older messages with the compaction function set in
2241
+ * `configureSession`. Resolves to null when there was nothing to compact.
2242
+ */
2243
+ compact() {
2244
+ return _classPrivateFieldGet2(_harness$1, this).compact(this.id);
2245
+ }
2246
+ /** Listen to the session's events in the AI SDK's vocabulary. */
2247
+ subscribe(listener) {
2248
+ return _classPrivateFieldGet2(_harness$1, this).subscribe(this.id, listener);
2249
+ }
2250
+ /** The shared harness interface's watch: the state, then every change. */
2251
+ watch() {
2252
+ return _classPrivateFieldGet2(_harness$1, this).watch(this.id);
2253
+ }
2254
+ /**
2255
+ * Submit input and stream its answer to `callback`, as Think's `chat()`
2256
+ * does: `onStart`, then `onEvent` with each UI chunk as JSON, then
2257
+ * `onDone` or `onError`. Works over RPC with an `RpcTarget` callback.
2258
+ *
2259
+ * The callback lives in memory. If the object is evicted mid-turn the
2260
+ * turn still finishes, but the callback is gone; a caller that must not
2261
+ * lose the answer should `wait()` on the receipt as well.
2262
+ */
2263
+ async chat(input, callback, options = {}) {
2264
+ const operationId = options.operationId ?? crypto.randomUUID();
2265
+ let delivered = Promise.resolve();
2266
+ const deliver = (fn) => {
2267
+ delivered = delivered.then(fn).catch((error) => {
2268
+ console.error("ThinkHarness chat callback failed", error);
2269
+ });
2270
+ };
2271
+ const unsubscribe = this.subscribe((event) => {
2272
+ if (event.type === "chunk" && event.operationId === operationId) deliver(() => callback.onEvent(JSON.stringify(event.chunk)));
2273
+ });
2274
+ try {
2275
+ const receipt = await this.submit(input, {
2276
+ ...options,
2277
+ operationId
2278
+ });
2279
+ deliver(() => callback.onStart?.({ operationId: receipt.operationId }));
2280
+ const result = await this.wait(receipt.operationId);
2281
+ unsubscribe();
2282
+ await delivered;
2283
+ if (result.status === "done") await callback.onDone();
2284
+ else await callback.onError(result.reason ?? "unanswered");
2285
+ return result;
2286
+ } finally {
2287
+ unsubscribe();
2288
+ }
2289
+ }
2290
+ /** One operation's status, or undefined when the id is unknown. */
2291
+ inspect(operationId) {
2292
+ return _classPrivateFieldGet2(_harness$1, this).inspect(this.id, operationId);
2293
+ }
2294
+ /** Whether the session has open work. */
2295
+ async busy() {
2296
+ return (await this.pending()).length > 0;
2297
+ }
2298
+ };
2299
+ var _harness2 = /* @__PURE__ */ new WeakMap();
2300
+ /**
2301
+ * Every session in this object.
2302
+ *
2303
+ * @experimental
2304
+ */
2305
+ var ThinkSessions = class {
2306
+ /** @param harness - The harness that owns the sessions. */
2307
+ constructor(harness) {
2308
+ _classPrivateFieldInitSpec(this, _harness2, void 0);
2309
+ _classPrivateFieldSet2(_harness2, this, harness);
2310
+ }
2311
+ /** A handle on one session. */
2312
+ get(id) {
2313
+ return _classPrivateFieldGet2(_harness2, this).session(id);
2314
+ }
2315
+ /** A new, empty session. */
2316
+ create() {
2317
+ return _classPrivateFieldGet2(_harness2, this).createSession();
2318
+ }
2319
+ /** A new session that starts with `from`'s active transcript. */
2320
+ fork(from) {
2321
+ return _classPrivateFieldGet2(_harness2, this).fork(from);
2322
+ }
2323
+ /** Every session, with whether it has open work. */
2324
+ list() {
2325
+ return _classPrivateFieldGet2(_harness2, this).listSessions();
2326
+ }
2327
+ };
2328
+ //#endregion
2329
+ //#region src/harness/think/chat.ts
2330
+ /** `WebSocket.OPEN`; the constant is not defined on every runtime's global. */
2331
+ const OPEN = 1;
2332
+ function send(connection, frame) {
2333
+ if (connection.readyState !== OPEN) return;
2334
+ try {
2335
+ connection.send(JSON.stringify(frame));
2336
+ } catch {}
2337
+ }
2338
+ function parseBody(body) {
2339
+ if (typeof body !== "string" || body === "") return {};
2340
+ try {
2341
+ const parsed = JSON.parse(body);
2342
+ return typeof parsed === "object" && parsed !== null ? parsed : {};
2343
+ } catch {
2344
+ return {};
2345
+ }
2346
+ }
2347
+ function clientToolsOf(value) {
2348
+ return value.map((tool) => ({
2349
+ name: tool.name,
2350
+ ...tool.description !== void 0 && { description: tool.description },
2351
+ ...tool.parameters !== void 0 && { parameters: tool.parameters }
2352
+ }));
2353
+ }
2354
+ var _harness = /* @__PURE__ */ new WeakMap();
2355
+ var _webSockets = /* @__PURE__ */ new WeakMap();
2356
+ var _session = /* @__PURE__ */ new WeakMap();
2357
+ var _framing = /* @__PURE__ */ new WeakMap();
2358
+ var _awaitingAck = /* @__PURE__ */ new WeakMap();
2359
+ var _replayed = /* @__PURE__ */ new WeakMap();
2360
+ var _requesters = /* @__PURE__ */ new WeakMap();
2361
+ var _pendingProbes = /* @__PURE__ */ new WeakMap();
2362
+ var _unobserve = /* @__PURE__ */ new WeakMap();
2363
+ var _ThinkChat_brand = /* @__PURE__ */ new WeakSet();
2364
+ /**
2365
+ * Serves one ThinkHarness session over Think's chat WebSocket protocol and
2366
+ * its `GET …/get-messages` route. Install it on the Lifecycle after the
2367
+ * harness and the WebSockets capability:
2368
+ *
2369
+ * ```ts
2370
+ * readonly webSockets = new WebSockets();
2371
+ * readonly chat = new ThinkChat({ harness: this.harness, webSockets: this.webSockets });
2372
+ * readonly lifecycle = Lifecycle.install(this)
2373
+ * .use(this.harness)
2374
+ * .use(this.webSockets).use(this.chat);
2375
+ * ```
2376
+ *
2377
+ * Supported: chat requests (`submit-message` and `regenerate-message`),
2378
+ * cancel, clear, client tool results, approvals, and stream resume after
2379
+ * a reconnect or an eviction. Not supported: `messageConcurrency`
2380
+ * strategies other than queueing, client transcript sync, and the
2381
+ * recovery progress frame.
2382
+ *
2383
+ * @experimental The API may change between releases.
2384
+ */
2385
+ var ThinkChat = class extends LifecycleCapability {
2386
+ /** @param options - The harness, its WebSockets, and the session to serve. */
2387
+ constructor(options) {
2388
+ super("think-chat");
2389
+ _classPrivateMethodInitSpec(this, _ThinkChat_brand);
2390
+ _classPrivateFieldInitSpec(this, _harness, void 0);
2391
+ _classPrivateFieldInitSpec(this, _webSockets, void 0);
2392
+ _classPrivateFieldInitSpec(this, _session, void 0);
2393
+ _classPrivateFieldInitSpec(this, _framing, /* @__PURE__ */ new Map());
2394
+ _classPrivateFieldInitSpec(this, _awaitingAck, /* @__PURE__ */ new Set());
2395
+ _classPrivateFieldInitSpec(this, _replayed, /* @__PURE__ */ new Map());
2396
+ _classPrivateFieldInitSpec(this, _requesters, /* @__PURE__ */ new Map());
2397
+ _classPrivateFieldInitSpec(this, _pendingProbes, /* @__PURE__ */ new Map());
2398
+ _classPrivateFieldInitSpec(this, _unobserve, void 0);
2399
+ _classPrivateFieldSet2(_harness, this, options.harness);
2400
+ _classPrivateFieldSet2(_webSockets, this, options.webSockets);
2401
+ _classPrivateFieldSet2(_session, this, options.session ?? "");
2402
+ _classPrivateFieldGet2(_webSockets, this).use({
2403
+ onConnect: (connection) => _assertClassBrand(_ThinkChat_brand, this, _onConnect).call(this, connection),
2404
+ onMessage: (connection, message) => _assertClassBrand(_ThinkChat_brand, this, _onMessage).call(this, connection, message),
2405
+ onClose: (connection) => _assertClassBrand(_ThinkChat_brand, this, _forget).call(this, connection),
2406
+ onError: (connection) => _assertClassBrand(_ThinkChat_brand, this, _forget).call(this, connection)
2407
+ });
2408
+ _classPrivateFieldSet2(_unobserve, this, _classPrivateFieldGet2(_harness, this).observe((session, event) => {
2409
+ if (session === _classPrivateFieldGet2(_session, this)) _assertClassBrand(_ThinkChat_brand, this, _onEvent).call(this, event);
2410
+ }));
2411
+ }
2412
+ /** Answer `GET …/get-messages` with the session's transcript. */
2413
+ async onRequest(context) {
2414
+ const { request } = context;
2415
+ if (request.method !== "GET") return void 0;
2416
+ if (!new URL(request.url).pathname.endsWith("/get-messages")) return;
2417
+ return Response.json(await _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this)).messages());
2418
+ }
2419
+ /** Stop listening to the harness. */
2420
+ dispose() {
2421
+ _classPrivateFieldGet2(_unobserve, this).call(this);
2422
+ }
2423
+ };
2424
+ async function _onConnect(connection) {
2425
+ const session = _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this));
2426
+ send(connection, {
2427
+ type: CHAT_MESSAGE_TYPES.CHAT_MESSAGES,
2428
+ messages: await session.messages(),
2429
+ connect: true
2430
+ });
2431
+ const inFlight = await session.inFlight();
2432
+ if (inFlight) _assertClassBrand(_ThinkChat_brand, this, _offerResume).call(this, connection, inFlight.operationId);
2433
+ }
2434
+ function _forget(connection) {
2435
+ _classPrivateFieldGet2(_awaitingAck, this).delete(connection.id);
2436
+ _classPrivateFieldGet2(_pendingProbes, this).delete(connection.id);
2437
+ _classPrivateFieldGet2(_replayed, this).delete(connection.id);
2438
+ }
2439
+ function _offerResume(connection, operationId, probeId) {
2440
+ _classPrivateFieldGet2(_awaitingAck, this).add(connection.id);
2441
+ send(connection, {
2442
+ type: CHAT_MESSAGE_TYPES.STREAM_RESUMING,
2443
+ id: operationId,
2444
+ ...probeId !== void 0 && { probeId }
2445
+ });
2446
+ }
2447
+ async function _onMessage(connection, raw) {
2448
+ if (typeof raw !== "string") return false;
2449
+ const event = parseProtocolMessage(raw);
2450
+ if (!event) return false;
2451
+ await _assertClassBrand(_ThinkChat_brand, this, _handle).call(this, connection, event);
2452
+ return true;
2453
+ }
2454
+ async function _handle(connection, event) {
2455
+ const session = _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this));
2456
+ switch (event.type) {
2457
+ case "chat-request": {
2458
+ const body = parseBody(event.init.body);
2459
+ const clientTools = body.clientTools ? { clientTools: body.clientTools } : {};
2460
+ _classPrivateFieldGet2(_requesters, this).set(event.id, connection.id);
2461
+ if (body.trigger === "regenerate-message") {
2462
+ await session.regenerate(void 0, { operationId: event.id });
2463
+ return;
2464
+ }
2465
+ const known = new Set((await session.messages()).map((message) => message.id));
2466
+ const fresh = (body.messages ?? []).filter((message) => !known.has(message.id));
2467
+ if (!(await session.submit(fresh, {
2468
+ operationId: event.id,
2469
+ source: "client",
2470
+ ...clientTools
2471
+ })).accepted) {
2472
+ const status = await session.inspect(event.id);
2473
+ if (status?.status === "done" || status?.status === "unanswered") {
2474
+ _classPrivateFieldGet2(_requesters, this).delete(event.id);
2475
+ await _assertClassBrand(_ThinkChat_brand, this, _finish).call(this, {
2476
+ operationId: status.operationId,
2477
+ status: status.status,
2478
+ ..."reason" in status && status.reason !== void 0 && { reason: status.reason }
2479
+ }, void 0, connection);
2480
+ }
2481
+ }
2482
+ return;
2483
+ }
2484
+ case "cancel":
2485
+ await session.abort(event.id);
2486
+ return;
2487
+ case "clear":
2488
+ await session.reset();
2489
+ for (const other of _classPrivateFieldGet2(_webSockets, this).getConnections()) if (other.id !== connection.id) send(other, { type: CHAT_MESSAGE_TYPES.CHAT_CLEAR });
2490
+ return;
2491
+ case "tool-result": {
2492
+ const operationId = crypto.randomUUID();
2493
+ _classPrivateFieldGet2(_requesters, this).set(operationId, connection.id);
2494
+ await session.submit({
2495
+ type: "tool-result",
2496
+ toolCallId: event.toolCallId,
2497
+ result: event.state === "output-error" ? {
2498
+ ok: false,
2499
+ ...event.errorText !== void 0 && { errorText: event.errorText }
2500
+ } : {
2501
+ ok: true,
2502
+ output: event.output ?? null
2503
+ }
2504
+ }, {
2505
+ operationId,
2506
+ source: "client",
2507
+ autoContinue: event.autoContinue ?? true,
2508
+ ...event.clientTools && { clientTools: clientToolsOf(event.clientTools) }
2509
+ });
2510
+ return;
2511
+ }
2512
+ case "tool-approval": {
2513
+ const approvalId = await _assertClassBrand(_ThinkChat_brand, this, _approvalId).call(this, event.toolCallId);
2514
+ if (approvalId === void 0) return;
2515
+ const operationId = crypto.randomUUID();
2516
+ _classPrivateFieldGet2(_requesters, this).set(operationId, connection.id);
2517
+ await session.submit({
2518
+ type: "approval",
2519
+ approvalId,
2520
+ approved: event.approved
2521
+ }, {
2522
+ operationId,
2523
+ source: "client",
2524
+ autoContinue: event.autoContinue ?? true
2525
+ });
2526
+ return;
2527
+ }
2528
+ case "stream-resume-request": {
2529
+ const inFlight = await session.inFlight();
2530
+ if (inFlight) _assertClassBrand(_ThinkChat_brand, this, _offerResume).call(this, connection, inFlight.operationId, event.probeId);
2531
+ else if ((await session.pending()).length > 0) {
2532
+ _classPrivateFieldGet2(_pendingProbes, this).set(connection.id, event.probeId);
2533
+ send(connection, {
2534
+ type: CHAT_MESSAGE_TYPES.STREAM_PENDING,
2535
+ ...event.probeId !== void 0 && { probeId: event.probeId }
2536
+ });
2537
+ } else send(connection, {
2538
+ type: CHAT_MESSAGE_TYPES.STREAM_RESUME_NONE,
2539
+ reason: STREAM_RESUME_NONE_REASONS.IDLE,
2540
+ ...event.probeId !== void 0 && { probeId: event.probeId }
2541
+ });
2542
+ return;
2543
+ }
2544
+ case "stream-resume-ack":
2545
+ await _assertClassBrand(_ThinkChat_brand, this, _replay).call(this, connection, event.id);
2546
+ return;
2547
+ case "messages": return;
2548
+ }
2549
+ }
2550
+ /** Replay a running model call's chunks to one connection, then go live. */
2551
+ async function _replay(connection, operationId) {
2552
+ const inFlight = await _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this)).inFlight();
2553
+ if (!inFlight || inFlight.operationId !== operationId) {
2554
+ _classPrivateFieldGet2(_awaitingAck, this).delete(connection.id);
2555
+ send(connection, {
2556
+ type: CHAT_MESSAGE_TYPES.USE_CHAT_RESPONSE,
2557
+ id: operationId,
2558
+ body: "",
2559
+ done: true,
2560
+ replay: true
2561
+ });
2562
+ return;
2563
+ }
2564
+ const { continuation, chunks } = inFlight;
2565
+ let seq = 0;
2566
+ for (const chunk of chunks) send(connection, {
2567
+ type: CHAT_MESSAGE_TYPES.USE_CHAT_RESPONSE,
2568
+ id: operationId,
2569
+ body: JSON.stringify(chunk),
2570
+ done: false,
2571
+ replay: true,
2572
+ seq: seq++,
2573
+ ...continuation && { continuation: true }
2574
+ });
2575
+ send(connection, {
2576
+ type: CHAT_MESSAGE_TYPES.USE_CHAT_RESPONSE,
2577
+ id: operationId,
2578
+ body: "",
2579
+ done: false,
2580
+ replay: true,
2581
+ replayComplete: true,
2582
+ ...continuation && { continuation: true }
2583
+ });
2584
+ _classPrivateFieldGet2(_replayed, this).set(connection.id, {
2585
+ operationId,
2586
+ continuation
2587
+ });
2588
+ _classPrivateFieldGet2(_awaitingAck, this).delete(connection.id);
2589
+ }
2590
+ async function _approvalId(toolCallId) {
2591
+ const messages = await _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this)).messages();
2592
+ for (const message of [...messages].reverse()) for (const part of message.parts) if ("toolCallId" in part && part.toolCallId === toolCallId && "approval" in part && part.approval) return part.approval.id;
2593
+ }
2594
+ function _onEvent(event) {
2595
+ switch (event.type) {
2596
+ case "run-start": {
2597
+ const requester = _classPrivateFieldGet2(_requesters, this).get(event.operationId);
2598
+ _classPrivateFieldGet2(_framing, this).set(event.operationId, {
2599
+ continuation: event.continuation,
2600
+ requester,
2601
+ finish: void 0,
2602
+ started: false
2603
+ });
2604
+ for (const connection of _classPrivateFieldGet2(_webSockets, this).getConnections()) {
2605
+ if (!_classPrivateFieldGet2(_pendingProbes, this).has(connection.id)) continue;
2606
+ const probeId = _classPrivateFieldGet2(_pendingProbes, this).get(connection.id);
2607
+ _classPrivateFieldGet2(_pendingProbes, this).delete(connection.id);
2608
+ _assertClassBrand(_ThinkChat_brand, this, _offerResume).call(this, connection, event.operationId, probeId);
2609
+ }
2610
+ if (event.continuation && requester !== void 0) {
2611
+ for (const connection of _classPrivateFieldGet2(_webSockets, this).getConnections()) if (connection.id === requester) _assertClassBrand(_ThinkChat_brand, this, _offerResume).call(this, connection, event.operationId);
2612
+ }
2613
+ return;
2614
+ }
2615
+ case "chunk": {
2616
+ const framing = _classPrivateFieldGet2(_framing, this).get(event.operationId);
2617
+ if (event.chunk.type === "finish") {
2618
+ if (framing) framing.finish = event.chunk;
2619
+ return;
2620
+ }
2621
+ if (event.chunk.type === "start" && framing) {
2622
+ if (framing.started) return;
2623
+ framing.started = true;
2624
+ }
2625
+ _assertClassBrand(_ThinkChat_brand, this, _broadcastChunk).call(this, event.operationId, event.chunk, framing);
2626
+ return;
2627
+ }
2628
+ case "message": {
2629
+ if (event.operationId !== void 0 && _classPrivateFieldGet2(_framing, this).has(event.operationId)) return;
2630
+ const requester = event.operationId === void 0 ? void 0 : _classPrivateFieldGet2(_requesters, this).get(event.operationId);
2631
+ _assertClassBrand(_ThinkChat_brand, this, _broadcastTranscript).call(this, requester);
2632
+ return;
2633
+ }
2634
+ case "operation": {
2635
+ const { status } = event;
2636
+ if (status.status !== "done" && status.status !== "unanswered") return;
2637
+ const framing = _classPrivateFieldGet2(_framing, this).get(status.operationId);
2638
+ _classPrivateFieldGet2(_framing, this).delete(status.operationId);
2639
+ _classPrivateFieldGet2(_requesters, this).delete(status.operationId);
2640
+ _assertClassBrand(_ThinkChat_brand, this, _finish).call(this, status, framing).then(() => _assertClassBrand(_ThinkChat_brand, this, _resolveProbes).call(this));
2641
+ return;
2642
+ }
2643
+ case "transcript":
2644
+ _assertClassBrand(_ThinkChat_brand, this, _broadcastTranscript).call(this);
2645
+ return;
2646
+ case "reset":
2647
+ for (const connection of _classPrivateFieldGet2(_webSockets, this).getConnections()) send(connection, { type: CHAT_MESSAGE_TYPES.CHAT_CLEAR });
2648
+ return;
2649
+ case "run-end": return;
2650
+ }
2651
+ }
2652
+ function _broadcastChunk(operationId, chunk, framing) {
2653
+ const body = JSON.stringify(chunk);
2654
+ for (const connection of _classPrivateFieldGet2(_webSockets, this).getConnections()) {
2655
+ if (_classPrivateFieldGet2(_awaitingAck, this).has(connection.id)) continue;
2656
+ const replayed = _classPrivateFieldGet2(_replayed, this).get(connection.id);
2657
+ const continuation = replayed?.operationId === operationId ? replayed.continuation : framing?.continuation ?? false;
2658
+ send(connection, {
2659
+ type: CHAT_MESSAGE_TYPES.USE_CHAT_RESPONSE,
2660
+ id: operationId,
2661
+ body,
2662
+ done: false,
2663
+ ...continuation && { continuation: true }
2664
+ });
2665
+ }
2666
+ }
2667
+ async function _broadcastTranscript(except) {
2668
+ const messages = await _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this)).messages();
2669
+ for (const connection of _classPrivateFieldGet2(_webSockets, this).getConnections()) {
2670
+ if (connection.id === except) continue;
2671
+ send(connection, {
2672
+ type: CHAT_MESSAGE_TYPES.CHAT_MESSAGES,
2673
+ messages
2674
+ });
2675
+ }
2676
+ }
2677
+ /** Tell probes still waiting that nothing will stream, once the session is idle. */
2678
+ async function _resolveProbes() {
2679
+ if (_classPrivateFieldGet2(_pendingProbes, this).size === 0) return;
2680
+ if ((await _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this)).pending()).length > 0) return;
2681
+ for (const connection of _classPrivateFieldGet2(_webSockets, this).getConnections()) {
2682
+ if (!_classPrivateFieldGet2(_pendingProbes, this).has(connection.id)) continue;
2683
+ const probeId = _classPrivateFieldGet2(_pendingProbes, this).get(connection.id);
2684
+ _classPrivateFieldGet2(_pendingProbes, this).delete(connection.id);
2685
+ send(connection, {
2686
+ type: CHAT_MESSAGE_TYPES.STREAM_RESUME_NONE,
2687
+ reason: STREAM_RESUME_NONE_REASONS.IDLE,
2688
+ ...probeId !== void 0 && { probeId }
2689
+ });
2690
+ }
2691
+ }
2692
+ /** The transcript first, then the terminal frame (#2119). */
2693
+ async function _finish(status, framing, only) {
2694
+ const { operationId } = status;
2695
+ if (framing?.finish) _assertClassBrand(_ThinkChat_brand, this, _broadcastChunk).call(this, operationId, framing.finish, framing);
2696
+ const messages = await _classPrivateFieldGet2(_harness, this).session(_classPrivateFieldGet2(_session, this)).messages();
2697
+ const reason = status.status === "unanswered" ? status.reason : void 0;
2698
+ const outcome = status.status === "done" ? "completed" : reason === "aborted" ? "aborted" : reason === "withdrawn" || reason === "not_waiting" || reason === "not_found" || reason === "empty" ? "skipped" : "error";
2699
+ const connections = only ? [only] : _classPrivateFieldGet2(_webSockets, this).getConnections();
2700
+ for (const connection of connections) {
2701
+ send(connection, {
2702
+ type: CHAT_MESSAGE_TYPES.CHAT_MESSAGES,
2703
+ messages
2704
+ });
2705
+ send(connection, {
2706
+ type: CHAT_MESSAGE_TYPES.USE_CHAT_RESPONSE,
2707
+ id: operationId,
2708
+ body: outcome === "error" ? reason ?? "" : "",
2709
+ done: true,
2710
+ outcome,
2711
+ ...outcome === "error" && { error: true },
2712
+ ...framing?.continuation && { continuation: true }
2713
+ });
2714
+ _classPrivateFieldGet2(_awaitingAck, this).delete(connection.id);
2715
+ if (_classPrivateFieldGet2(_replayed, this).get(connection.id)?.operationId === operationId) _classPrivateFieldGet2(_replayed, this).delete(connection.id);
2716
+ }
2717
+ }
2718
+ //#endregion
2719
+ export { ROOT_SESSION, SteerNotSupportedError, ThinkChat, ThinkHarness, ThinkSession, ThinkSessions, classifyContextOverflow };
2720
+
2721
+ //# sourceMappingURL=index.js.map