@geminixiang/mikan 1.0.0-beta.20 → 1.0.0-beta.21

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 (292) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/README.md +18 -11
  3. package/dist/adapters/buffered-responder.d.ts +60 -0
  4. package/dist/adapters/buffered-responder.d.ts.map +1 -0
  5. package/dist/adapters/buffered-responder.js +201 -0
  6. package/dist/adapters/buffered-responder.js.map +1 -0
  7. package/dist/adapters/discord/context.d.ts.map +1 -1
  8. package/dist/adapters/discord/context.js +42 -177
  9. package/dist/adapters/discord/context.js.map +1 -1
  10. package/dist/adapters/github/bot.d.ts +14 -76
  11. package/dist/adapters/github/bot.d.ts.map +1 -1
  12. package/dist/adapters/github/bot.js +52 -443
  13. package/dist/adapters/github/bot.js.map +1 -1
  14. package/dist/adapters/github/client.d.ts +4 -0
  15. package/dist/adapters/github/client.d.ts.map +1 -1
  16. package/dist/adapters/github/client.js +5 -0
  17. package/dist/adapters/github/client.js.map +1 -1
  18. package/dist/adapters/github/context.d.ts.map +1 -1
  19. package/dist/adapters/github/context.js +32 -104
  20. package/dist/adapters/github/context.js.map +1 -1
  21. package/dist/adapters/github/github-ops.d.ts +90 -0
  22. package/dist/adapters/github/github-ops.d.ts.map +1 -0
  23. package/dist/adapters/github/github-ops.js +405 -0
  24. package/dist/adapters/github/github-ops.js.map +1 -0
  25. package/dist/adapters/github/repo.d.ts +2 -0
  26. package/dist/adapters/github/repo.d.ts.map +1 -1
  27. package/dist/adapters/github/repo.js +5 -0
  28. package/dist/adapters/github/repo.js.map +1 -1
  29. package/dist/adapters/github/tools/checks.d.ts.map +1 -1
  30. package/dist/adapters/github/tools/checks.js +6 -17
  31. package/dist/adapters/github/tools/checks.js.map +1 -1
  32. package/dist/adapters/github/tools/issue.d.ts.map +1 -1
  33. package/dist/adapters/github/tools/issue.js +6 -17
  34. package/dist/adapters/github/tools/issue.js.map +1 -1
  35. package/dist/adapters/github/tools/pr.d.ts.map +1 -1
  36. package/dist/adapters/github/tools/pr.js +6 -17
  37. package/dist/adapters/github/tools/pr.js.map +1 -1
  38. package/dist/adapters/github/tools/read.d.ts.map +1 -1
  39. package/dist/adapters/github/tools/read.js +6 -17
  40. package/dist/adapters/github/tools/read.js.map +1 -1
  41. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -1
  42. package/dist/adapters/github/tools/review-reply.js +6 -17
  43. package/dist/adapters/github/tools/review-reply.js.map +1 -1
  44. package/dist/adapters/github/tools/sync.d.ts.map +1 -1
  45. package/dist/adapters/github/tools/sync.js +6 -17
  46. package/dist/adapters/github/tools/sync.js.map +1 -1
  47. package/dist/adapters/slack/bot.js +1 -1
  48. package/dist/adapters/slack/bot.js.map +1 -1
  49. package/dist/adapters/telegram/context.d.ts.map +1 -1
  50. package/dist/adapters/telegram/context.js +33 -151
  51. package/dist/adapters/telegram/context.js.map +1 -1
  52. package/dist/agent.d.ts +0 -1
  53. package/dist/agent.d.ts.map +1 -1
  54. package/dist/agent.js +28 -32
  55. package/dist/agent.js.map +1 -1
  56. package/dist/cli/arg-grammar.d.ts +25 -0
  57. package/dist/cli/arg-grammar.d.ts.map +1 -0
  58. package/dist/cli/arg-grammar.js +59 -0
  59. package/dist/cli/arg-grammar.js.map +1 -0
  60. package/dist/cli/boot.d.ts +16 -0
  61. package/dist/cli/boot.d.ts.map +1 -0
  62. package/dist/cli/boot.js +119 -0
  63. package/dist/cli/boot.js.map +1 -0
  64. package/dist/cli/ext.js +9 -11
  65. package/dist/cli/ext.js.map +1 -1
  66. package/dist/commands/extensions.js +2 -2
  67. package/dist/commands/extensions.js.map +1 -1
  68. package/dist/commands/login.d.ts.map +1 -1
  69. package/dist/commands/login.js +5 -2
  70. package/dist/commands/login.js.map +1 -1
  71. package/dist/commands/model.d.ts.map +1 -1
  72. package/dist/commands/model.js +8 -8
  73. package/dist/commands/model.js.map +1 -1
  74. package/dist/commands/sandbox.d.ts.map +1 -1
  75. package/dist/commands/sandbox.js +7 -5
  76. package/dist/commands/sandbox.js.map +1 -1
  77. package/dist/config.d.ts +0 -2
  78. package/dist/config.d.ts.map +1 -1
  79. package/dist/config.js +8 -41
  80. package/dist/config.js.map +1 -1
  81. package/dist/env-manifest.d.ts +35 -0
  82. package/dist/env-manifest.d.ts.map +1 -0
  83. package/dist/env-manifest.js +225 -0
  84. package/dist/env-manifest.js.map +1 -0
  85. package/dist/events.d.ts +0 -10
  86. package/dist/events.d.ts.map +1 -1
  87. package/dist/events.js +9 -82
  88. package/dist/events.js.map +1 -1
  89. package/dist/execution-resolver.d.ts +1 -0
  90. package/dist/execution-resolver.d.ts.map +1 -1
  91. package/dist/execution-resolver.js +33 -36
  92. package/dist/execution-resolver.js.map +1 -1
  93. package/dist/harness/event-format.d.ts +66 -0
  94. package/dist/harness/event-format.d.ts.map +1 -0
  95. package/dist/harness/event-format.js +112 -0
  96. package/dist/harness/event-format.js.map +1 -0
  97. package/dist/harness/extensions/loader.d.ts +9 -6
  98. package/dist/harness/extensions/loader.d.ts.map +1 -1
  99. package/dist/harness/extensions/loader.js +35 -50
  100. package/dist/harness/extensions/loader.js.map +1 -1
  101. package/dist/harness/extensions/types.d.ts +10 -14
  102. package/dist/harness/extensions/types.d.ts.map +1 -1
  103. package/dist/harness/extensions/types.js.map +1 -1
  104. package/dist/harness/index.d.ts +1 -0
  105. package/dist/harness/index.d.ts.map +1 -1
  106. package/dist/harness/index.js +1 -0
  107. package/dist/harness/index.js.map +1 -1
  108. package/dist/harness/runner.d.ts +16 -0
  109. package/dist/harness/runner.d.ts.map +1 -1
  110. package/dist/harness/runner.js +27 -1
  111. package/dist/harness/runner.js.map +1 -1
  112. package/dist/harness/subagent-runner.d.ts +18 -1
  113. package/dist/harness/subagent-runner.d.ts.map +1 -1
  114. package/dist/harness/subagent-runner.js +62 -30
  115. package/dist/harness/subagent-runner.js.map +1 -1
  116. package/dist/index.d.ts +2 -2
  117. package/dist/index.d.ts.map +1 -1
  118. package/dist/index.js +2 -2
  119. package/dist/index.js.map +1 -1
  120. package/dist/main.js +69 -121
  121. package/dist/main.js.map +1 -1
  122. package/dist/observability/instrument.js +6 -4
  123. package/dist/observability/instrument.js.map +1 -1
  124. package/dist/observability/sentry.d.ts.map +1 -1
  125. package/dist/observability/sentry.js +3 -2
  126. package/dist/observability/sentry.js.map +1 -1
  127. package/dist/runtime/conversation-runtime.js +13 -3
  128. package/dist/runtime/conversation-runtime.js.map +1 -1
  129. package/dist/runtime/types.d.ts +4 -0
  130. package/dist/runtime/types.d.ts.map +1 -1
  131. package/dist/runtime/types.js.map +1 -1
  132. package/dist/sandbox/cloudflare.d.ts +1 -0
  133. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  134. package/dist/sandbox/cloudflare.js +4 -1
  135. package/dist/sandbox/cloudflare.js.map +1 -1
  136. package/dist/sandbox/container.d.ts +1 -0
  137. package/dist/sandbox/container.d.ts.map +1 -1
  138. package/dist/sandbox/container.js +4 -1
  139. package/dist/sandbox/container.js.map +1 -1
  140. package/dist/sandbox/firecracker.d.ts +1 -0
  141. package/dist/sandbox/firecracker.d.ts.map +1 -1
  142. package/dist/sandbox/firecracker.js +4 -1
  143. package/dist/sandbox/firecracker.js.map +1 -1
  144. package/dist/sandbox/gondolin-bootstrap.d.ts +26 -0
  145. package/dist/sandbox/gondolin-bootstrap.d.ts.map +1 -0
  146. package/dist/sandbox/gondolin-bootstrap.js +27 -0
  147. package/dist/sandbox/gondolin-bootstrap.js.map +1 -0
  148. package/dist/sandbox/gondolin-contract.d.ts +125 -0
  149. package/dist/sandbox/gondolin-contract.d.ts.map +1 -0
  150. package/dist/sandbox/gondolin-contract.js +155 -0
  151. package/dist/sandbox/gondolin-contract.js.map +1 -0
  152. package/dist/sandbox/gondolin-fleet.d.ts +1 -1
  153. package/dist/sandbox/gondolin-fleet.d.ts.map +1 -1
  154. package/dist/sandbox/gondolin-fleet.js +1 -1
  155. package/dist/sandbox/gondolin-fleet.js.map +1 -1
  156. package/dist/sandbox/gondolin-gateway.d.ts +1 -0
  157. package/dist/sandbox/gondolin-gateway.d.ts.map +1 -1
  158. package/dist/sandbox/gondolin-gateway.js +13 -6
  159. package/dist/sandbox/gondolin-gateway.js.map +1 -1
  160. package/dist/sandbox/gondolin-inventory.d.ts +2 -16
  161. package/dist/sandbox/gondolin-inventory.d.ts.map +1 -1
  162. package/dist/sandbox/gondolin-inventory.js +2 -12
  163. package/dist/sandbox/gondolin-inventory.js.map +1 -1
  164. package/dist/sandbox/gondolin-placement.d.ts +0 -2
  165. package/dist/sandbox/gondolin-placement.d.ts.map +1 -1
  166. package/dist/sandbox/gondolin-placement.js +0 -6
  167. package/dist/sandbox/gondolin-placement.js.map +1 -1
  168. package/dist/sandbox/gondolin-recovery.d.ts +33 -0
  169. package/dist/sandbox/gondolin-recovery.d.ts.map +1 -0
  170. package/dist/sandbox/gondolin-recovery.js +39 -0
  171. package/dist/sandbox/gondolin-recovery.js.map +1 -0
  172. package/dist/sandbox/gondolin-remote.d.ts +2 -5
  173. package/dist/sandbox/gondolin-remote.d.ts.map +1 -1
  174. package/dist/sandbox/gondolin-remote.js +12 -39
  175. package/dist/sandbox/gondolin-remote.js.map +1 -1
  176. package/dist/sandbox/gondolin-worker-client.d.ts +8 -12
  177. package/dist/sandbox/gondolin-worker-client.d.ts.map +1 -1
  178. package/dist/sandbox/gondolin-worker-client.js +13 -15
  179. package/dist/sandbox/gondolin-worker-client.js.map +1 -1
  180. package/dist/sandbox/gondolin-worker.d.ts +2 -40
  181. package/dist/sandbox/gondolin-worker.d.ts.map +1 -1
  182. package/dist/sandbox/gondolin-worker.js.map +1 -1
  183. package/dist/sandbox/gondolin.d.ts +1 -0
  184. package/dist/sandbox/gondolin.d.ts.map +1 -1
  185. package/dist/sandbox/gondolin.js +8 -4
  186. package/dist/sandbox/gondolin.js.map +1 -1
  187. package/dist/sandbox/host.d.ts +1 -0
  188. package/dist/sandbox/host.d.ts.map +1 -1
  189. package/dist/sandbox/host.js +3 -0
  190. package/dist/sandbox/host.js.map +1 -1
  191. package/dist/sandbox/index.d.ts +9 -1
  192. package/dist/sandbox/index.d.ts.map +1 -1
  193. package/dist/sandbox/index.js +14 -0
  194. package/dist/sandbox/index.js.map +1 -1
  195. package/dist/sandbox/types.d.ts +5 -0
  196. package/dist/sandbox/types.d.ts.map +1 -1
  197. package/dist/sandbox/types.js.map +1 -1
  198. package/dist/sandbox/utils.d.ts +1 -0
  199. package/dist/sandbox/utils.d.ts.map +1 -1
  200. package/dist/sandbox/utils.js +5 -2
  201. package/dist/sandbox/utils.js.map +1 -1
  202. package/dist/sessions/chat-history-sync.d.ts +32 -0
  203. package/dist/sessions/chat-history-sync.d.ts.map +1 -0
  204. package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +24 -83
  205. package/dist/sessions/chat-history-sync.js.map +1 -0
  206. package/dist/sessions/conversation-log.d.ts +13 -0
  207. package/dist/sessions/conversation-log.d.ts.map +1 -0
  208. package/dist/sessions/conversation-log.js +47 -0
  209. package/dist/sessions/conversation-log.js.map +1 -0
  210. package/dist/sessions/rotation.d.ts +2 -0
  211. package/dist/sessions/rotation.d.ts.map +1 -0
  212. package/dist/sessions/rotation.js +39 -0
  213. package/dist/sessions/rotation.js.map +1 -0
  214. package/dist/sessions/store.d.ts.map +1 -1
  215. package/dist/sessions/store.js +4 -10
  216. package/dist/sessions/store.js.map +1 -1
  217. package/dist/sessions/types.d.ts +2 -2
  218. package/dist/sessions/types.d.ts.map +1 -1
  219. package/dist/sessions/types.js.map +1 -1
  220. package/dist/settings-mutation.d.ts +23 -0
  221. package/dist/settings-mutation.d.ts.map +1 -0
  222. package/dist/settings-mutation.js +42 -0
  223. package/dist/settings-mutation.js.map +1 -0
  224. package/dist/tools/event.d.ts.map +1 -1
  225. package/dist/tools/event.js +27 -46
  226. package/dist/tools/event.js.map +1 -1
  227. package/dist/tools/host-fn-tool.d.ts +21 -0
  228. package/dist/tools/host-fn-tool.d.ts.map +1 -0
  229. package/dist/tools/host-fn-tool.js +32 -0
  230. package/dist/tools/host-fn-tool.js.map +1 -0
  231. package/dist/tools/index.d.ts +1 -0
  232. package/dist/tools/index.d.ts.map +1 -1
  233. package/dist/tools/index.js +1 -0
  234. package/dist/tools/index.js.map +1 -1
  235. package/dist/tools/react.d.ts.map +1 -1
  236. package/dist/tools/react.js +6 -17
  237. package/dist/tools/react.js.map +1 -1
  238. package/dist/tools/read.d.ts.map +1 -1
  239. package/dist/tools/read.js +7 -26
  240. package/dist/tools/read.js.map +1 -1
  241. package/dist/tools/sandbox.d.ts.map +1 -1
  242. package/dist/tools/sandbox.js +5 -2
  243. package/dist/tools/sandbox.js.map +1 -1
  244. package/dist/tools/subagent-slots.d.ts +28 -0
  245. package/dist/tools/subagent-slots.d.ts.map +1 -0
  246. package/dist/tools/subagent-slots.js +57 -0
  247. package/dist/tools/subagent-slots.js.map +1 -0
  248. package/dist/tools/subagent.d.ts +7 -2
  249. package/dist/tools/subagent.d.ts.map +1 -1
  250. package/dist/tools/subagent.js +160 -111
  251. package/dist/tools/subagent.js.map +1 -1
  252. package/dist/tools/types.d.ts +10 -9
  253. package/dist/tools/types.d.ts.map +1 -1
  254. package/dist/tools/types.js.map +1 -1
  255. package/dist/types.d.ts +5 -24
  256. package/dist/types.d.ts.map +1 -1
  257. package/dist/types.js.map +1 -1
  258. package/dist/utils/date.d.ts +1 -1
  259. package/dist/utils/date.js +1 -1
  260. package/dist/utils/date.js.map +1 -1
  261. package/dist/vault/disabled.d.ts.map +1 -1
  262. package/dist/vault/disabled.js +0 -1
  263. package/dist/vault/disabled.js.map +1 -1
  264. package/dist/vault/index.d.ts +0 -2
  265. package/dist/vault/index.d.ts.map +1 -1
  266. package/dist/vault/index.js +0 -10
  267. package/dist/vault/index.js.map +1 -1
  268. package/dist/vault/injection.d.ts +20 -0
  269. package/dist/vault/injection.d.ts.map +1 -0
  270. package/dist/vault/injection.js +27 -0
  271. package/dist/vault/injection.js.map +1 -0
  272. package/dist/vault/types.d.ts +0 -2
  273. package/dist/vault/types.d.ts.map +1 -1
  274. package/dist/vault/types.js.map +1 -1
  275. package/dist/web/admin/portal.d.ts +3 -3
  276. package/dist/web/admin/portal.d.ts.map +1 -1
  277. package/dist/web/admin/portal.js +41 -39
  278. package/dist/web/admin/portal.js.map +1 -1
  279. package/dist/web/admin/types.d.ts +3 -0
  280. package/dist/web/admin/types.d.ts.map +1 -1
  281. package/dist/web/admin/types.js.map +1 -1
  282. package/dist/web/agent-events/portal.d.ts.map +1 -1
  283. package/dist/web/agent-events/portal.js +2 -1
  284. package/dist/web/agent-events/portal.js.map +1 -1
  285. package/package.json +1 -1
  286. package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
  287. package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
  288. package/dist/sessions/agent-memory-file-manager.js.map +0 -1
  289. package/dist/vault/routing.d.ts +0 -4
  290. package/dist/vault/routing.d.ts.map +0 -1
  291. package/dist/vault/routing.js +0 -6
  292. package/dist/vault/routing.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/tools/event.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAKtD,QAAA,MAAM,WAAW;;;;;;;;;;;EA0Df,CAAC;AAEH,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;CAChB;AAeD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,qBAAa,cAAe,YAAW,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAtC,YAA6B,SAAS,EAAE,MAAM,EAAI;IAElD,MAAM,CAAC,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc,CAE5D;IAEK,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAE5F;IAEK,IAAI,IAAI,OAAO,CACnB,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CACzF,CA6BA;IAEK,IAAI,CACR,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAkBrF;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAE7F;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAI5D;YAEa,YAAY;CAe3B;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG;IACvD,IAAI,EAAE,SAAS,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,eAAe,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD,CAuGA"}
1
+ {"version":3,"file":"event.d.ts","sourceRoot":"","sources":["../../src/tools/event.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAKtD,QAAA,MAAM,WAAW;;;;;;;;;;;EAwDf,CAAC;AAEH,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,MAAM,EAAE,MAAM,CAAC;CAChB;AAeD,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE3D,qBAAa,cAAe,YAAW,UAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAAtC,YAA6B,SAAS,EAAE,MAAM,EAAI;IAElD,MAAM,CAAC,gBAAgB,CAAC,YAAY,EAAE,MAAM,GAAG,cAAc,CAE5D;IAEK,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAE5F;IAEK,IAAI,IAAI,OAAO,CACnB,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CACzF,CA6BA;IAEK,IAAI,CACR,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,YAAY,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAcrF;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAE7F;IAEK,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAI5D;YAEa,YAAY;CAe3B;AAED,wBAAgB,eAAe,CAAC,UAAU,EAAE,UAAU,GAAG;IACvD,IAAI,EAAE,SAAS,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,eAAe,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;CACtD,CAuGA"}
@@ -1,7 +1,7 @@
1
1
  import { mkdir, readdir, readFile, rm, stat } from "node:fs/promises";
2
2
  import { basename, join } from "node:path";
3
3
  import { Type } from "@sinclair/typebox";
4
- import { resolveEventConversationId } from "../events.js";
4
+ import { buildEventPayload, EventTypeSchema, parseEventPayload } from "../harness/event-format.js";
5
5
  import * as log from "../log.js";
6
6
  import { atomicWritePrivateFile } from "../utils/fs-atomic.js";
7
7
  const eventSchema = Type.Object({
@@ -21,7 +21,7 @@ const eventSchema = Type.Object({
21
21
  label: Type.Optional(Type.String({
22
22
  description: "Brief description of the event you're scheduling (shown to user)",
23
23
  })),
24
- type: Type.Optional(Type.Union([Type.Literal("immediate"), Type.Literal("one-shot"), Type.Literal("periodic")])),
24
+ type: Type.Optional(EventTypeSchema),
25
25
  text: Type.Optional(Type.String({
26
26
  description: "A self-contained task for the future run. Include the necessary context, tone, and constraints in the text itself because events do not inherit normal conversation history.",
27
27
  })),
@@ -82,13 +82,10 @@ export class HostEventStore {
82
82
  const safeFilename = validateEventFilename(filename);
83
83
  const filePath = join(this.eventsDir, safeFilename);
84
84
  const [raw, fileStat] = await Promise.all([readFile(filePath, "utf-8"), stat(filePath)]);
85
- const parsed = JSON.parse(raw);
86
- // Normalize the legacy `channelId` alias so consumers always see
87
- // `conversationId` (alias resolution is owned by src/events.ts).
88
- const conversationId = resolveEventConversationId(parsed);
89
- const payload = conversationId !== undefined && parsed.conversationId !== conversationId
90
- ? { ...parsed, conversationId }
91
- : parsed;
85
+ // Validation (shape, per-type fields, channelId alias) is owned by the
86
+ // event-format module; files that fail it surface as payload:null in
87
+ // list() and as errors here.
88
+ const payload = parseEventPayload(raw, safeFilename);
92
89
  return {
93
90
  filename: safeFilename,
94
91
  payload,
@@ -167,7 +164,7 @@ export function createEventTool(eventStore) {
167
164
  details: undefined,
168
165
  };
169
166
  }
170
- const payload = buildEventPayload(params, eventContext);
167
+ const payload = buildToolEventPayload(params, eventContext);
171
168
  const filename = action === "update"
172
169
  ? requireFilename(params)
173
170
  : `${sanitizeFileSegment(params.filenamePrefix || payload.type || "event")}-${Date.now()}.json`;
@@ -195,63 +192,47 @@ export function createEventTool(eventStore) {
195
192
  },
196
193
  };
197
194
  }
198
- function buildEventPayload(params, context) {
195
+ function buildToolEventPayload(params, context) {
199
196
  if (!params.type) {
200
197
  throw new Error("`type` is required for create and update actions");
201
198
  }
202
199
  if (!params.text) {
203
200
  throw new Error("`text` is required for create and update actions");
204
201
  }
205
- const base = {
202
+ // Per-type field rules live in the event-format module. No sessionKey or
203
+ // threadTs in the payload: reminders should fire as top-level messages,
204
+ // not buried in old threads.
205
+ const payload = buildEventPayload({
206
+ type: params.type,
206
207
  platform: context.platform,
207
208
  conversationId: context.conversationId,
208
209
  conversationKind: context.conversationKind,
209
210
  userId: context.userId,
210
211
  text: params.text,
211
- };
212
- if (params.type === "immediate") {
213
- return {
214
- ...base,
215
- type: "immediate",
216
- };
217
- }
218
- if (params.type === "one-shot") {
219
- if (!params.at) {
220
- throw new Error("`at` is required for one-shot events");
221
- }
222
- const atTime = new Date(params.at).getTime();
223
- if (Number.isNaN(atTime)) {
224
- throw new Error("`at` must be a valid ISO 8601 timestamp with UTC offset");
225
- }
226
- if (atTime <= Date.now()) {
227
- throw new Error(`\`at\` must be in the future; got ${params.at} (now=${new Date().toISOString()}). Check the timezone offset.`);
228
- }
229
- // No sessionKey or threadTs: reminders should fire as top-level messages, not buried in old threads
230
- return { ...base, type: "one-shot", at: params.at };
231
- }
232
- if (!params.schedule) {
233
- throw new Error("`schedule` is required for periodic events");
234
- }
235
- if (!params.timezone) {
236
- throw new Error("`timezone` is required for periodic events");
237
- }
238
- return {
239
- ...base,
240
- type: "periodic",
212
+ at: params.at,
241
213
  schedule: params.schedule,
242
214
  timezone: params.timezone,
243
- };
215
+ });
216
+ // Tool-side write policy, not format knowledge: a reminder in the past
217
+ // would be deleted unfired by the watcher, so reject it here.
218
+ if (payload.type === "one-shot" && new Date(payload.at).getTime() <= Date.now()) {
219
+ throw new Error(`\`at\` must be in the future; got ${payload.at} (now=${new Date().toISOString()}). Check the timezone offset.`);
220
+ }
221
+ return payload;
244
222
  }
245
223
  function formatEventWriteResult(action, filename, payload) {
246
224
  const scheduledVerb = action === "update" ? "Updated" : "Scheduled";
247
225
  const immediateVerb = action === "update" ? "Updated" : "Queued";
226
+ const target = payload.platform
227
+ ? `${payload.platform}/${payload.conversationId}`
228
+ : payload.conversationId;
248
229
  switch (payload.type) {
249
230
  case "periodic":
250
- return `${scheduledVerb} periodic event ${filename} for ${payload.platform}/${payload.conversationId} (${payload.schedule} ${payload.timezone})`;
231
+ return `${scheduledVerb} periodic event ${filename} for ${target} (${payload.schedule} ${payload.timezone})`;
251
232
  case "one-shot":
252
- return `${scheduledVerb} one-shot event ${filename} for ${payload.platform}/${payload.conversationId} at ${payload.at}`;
233
+ return `${scheduledVerb} one-shot event ${filename} for ${target} at ${payload.at}`;
253
234
  case "immediate":
254
- return `${immediateVerb} immediate event ${filename} for ${payload.platform}/${payload.conversationId}`;
235
+ return `${immediateVerb} immediate event ${filename} for ${target}`;
255
236
  }
256
237
  }
257
238
  function requireFilename(params) {
@@ -1 +1 @@
1
- {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/tools/event.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readdir, readFile, rm, stat } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { ConversationKind } from \"../adapter.js\";\nimport { resolveEventConversationId } from \"../events.js\";\nimport * as log from \"../log.js\";\nimport { atomicWritePrivateFile } from \"../utils/fs-atomic.js\";\n\nconst eventSchema = Type.Object({\n action: Type.Optional(\n Type.Union(\n [\n Type.Literal(\"create\"),\n Type.Literal(\"list\"),\n Type.Literal(\"read\"),\n Type.Literal(\"update\"),\n Type.Literal(\"delete\"),\n ],\n { description: \"CRUD action. Defaults to create for backward compatibility.\" },\n ),\n ),\n filename: Type.Optional(\n Type.String({\n description: \"Event filename for read, update, or delete actions\",\n }),\n ),\n scope: Type.Optional(\n Type.Union([Type.Literal(\"conversation\"), Type.Literal(\"all\")], {\n description:\n \"List scope. Defaults to conversation, which only shows events for the current conversation. Use all only when the user explicitly asks for all events.\",\n }),\n ),\n label: Type.Optional(\n Type.String({\n description: \"Brief description of the event you're scheduling (shown to user)\",\n }),\n ),\n type: Type.Optional(\n Type.Union([Type.Literal(\"immediate\"), Type.Literal(\"one-shot\"), Type.Literal(\"periodic\")]),\n ),\n text: Type.Optional(\n Type.String({\n description:\n \"A self-contained task for the future run. Include the necessary context, tone, and constraints in the text itself because events do not inherit normal conversation history.\",\n }),\n ),\n at: Type.Optional(\n Type.String({\n description: \"ISO 8601 timestamp with offset, required for one-shot events\",\n }),\n ),\n schedule: Type.Optional(\n Type.String({\n description: \"Cron schedule, required for periodic events\",\n }),\n ),\n timezone: Type.Optional(\n Type.String({\n description: \"IANA timezone, required for periodic events\",\n }),\n ),\n filenamePrefix: Type.Optional(\n Type.String({\n description: \"Optional filename prefix for the event file\",\n }),\n ),\n});\n\ninterface EventToolContext {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n}\n\ntype EventToolParams = {\n action?: \"create\" | \"list\" | \"read\" | \"update\" | \"delete\";\n filename?: string;\n scope?: \"conversation\" | \"all\";\n label?: string;\n type?: \"immediate\" | \"one-shot\" | \"periodic\";\n text?: string;\n at?: string;\n schedule?: string;\n timezone?: string;\n filenamePrefix?: string;\n};\n\nexport type { EventPayload, EventStore } from \"./types.js\";\nimport type { EventPayload, EventStore } from \"./types.js\";\n\nexport class HostEventStore implements EventStore {\n constructor(private readonly eventsDir: string) {}\n\n static fromWorkspaceDir(workspaceDir: string): HostEventStore {\n return new HostEventStore(join(workspaceDir, \"events\"));\n }\n\n async write(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n return this.writePayload(filename, payload);\n }\n\n async list(): Promise<\n Array<{ filename: string; payload: EventPayload | null; size: number; mtimeMs: number }>\n > {\n await mkdir(this.eventsDir, { recursive: true });\n const entries = await readdir(this.eventsDir, { withFileTypes: true });\n const events = await Promise.all(\n entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n .map(async (entry) => {\n try {\n return await this.read(entry.name);\n } catch {\n // Keep unparseable files visible (payload: null) so consumers can\n // surface and delete them; skip files that vanished mid-listing.\n try {\n const fileStat = await stat(join(this.eventsDir, entry.name));\n return {\n filename: entry.name,\n payload: null,\n size: fileStat.size,\n mtimeMs: fileStat.mtimeMs,\n };\n } catch {\n return null;\n }\n }\n }),\n );\n return events\n .filter((event) => event !== null)\n .toSorted((a, b) => a.filename.localeCompare(b.filename));\n }\n\n async read(\n filename: string,\n ): Promise<{ filename: string; payload: EventPayload; size: number; mtimeMs: number }> {\n const safeFilename = validateEventFilename(filename);\n const filePath = join(this.eventsDir, safeFilename);\n const [raw, fileStat] = await Promise.all([readFile(filePath, \"utf-8\"), stat(filePath)]);\n const parsed = JSON.parse(raw) as EventPayload & { channelId?: string };\n // Normalize the legacy `channelId` alias so consumers always see\n // `conversationId` (alias resolution is owned by src/events.ts).\n const conversationId = resolveEventConversationId(parsed);\n const payload: EventPayload =\n conversationId !== undefined && parsed.conversationId !== conversationId\n ? { ...parsed, conversationId }\n : parsed;\n return {\n filename: safeFilename,\n payload,\n size: fileStat.size,\n mtimeMs: fileStat.mtimeMs,\n };\n }\n\n async update(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n return this.writePayload(filename, payload, true);\n }\n\n async delete(filename: string): Promise<{ deleted: boolean }> {\n const safeFilename = validateEventFilename(filename);\n await rm(join(this.eventsDir, safeFilename), { force: true });\n return { deleted: true };\n }\n\n private async writePayload(\n filename: string,\n payload: EventPayload,\n requireExisting = false,\n ): Promise<{ path: string; size: number }> {\n await mkdir(this.eventsDir, { recursive: true });\n const safeFilename = validateEventFilename(filename);\n const filePath = join(this.eventsDir, safeFilename);\n if (requireExisting) {\n await stat(filePath);\n }\n atomicWritePrivateFile(filePath, JSON.stringify(payload) + \"\\n\");\n const fileStat = await stat(filePath);\n return { path: filePath, size: fileStat.size };\n }\n}\n\nexport function createEventTool(eventStore: EventStore): {\n tool: AgentTool<typeof eventSchema>;\n setEventContext: (context: EventToolContext) => void;\n} {\n let eventContext: EventToolContext | null = null;\n\n const tool: AgentTool<typeof eventSchema> = {\n name: \"event\",\n label: \"event\",\n description:\n \"CRUD tool for scheduled events. Create immediate, one-shot, or periodic events for the current conversation. List defaults to events for the current conversation only; use scope=all only when the user explicitly asks to list all events. Event text must be self-contained because events do not inherit normal conversation history.\",\n parameters: eventSchema,\n execute: async (_toolCallId: string, params: EventToolParams, signal?: AbortSignal) => {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!eventContext) {\n throw new Error(\"Event context not configured\");\n }\n\n const action = params.action ?? \"create\";\n\n if (action === \"list\") {\n const events = await eventStore.list();\n const conversationId = eventContext.conversationId;\n const scopedEvents =\n params.scope === \"all\"\n ? events\n : events.filter((event) => event.payload?.conversationId === conversationId);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n scope: params.scope ?? \"conversation\",\n conversationId: params.scope === \"all\" ? undefined : conversationId,\n events: scopedEvents,\n },\n null,\n 2,\n ),\n },\n ],\n details: undefined,\n };\n }\n\n if (action === \"read\") {\n const filename = requireFilename(params);\n const event = await eventStore.read(filename);\n return {\n content: [{ type: \"text\", text: JSON.stringify(event, null, 2) }],\n details: undefined,\n };\n }\n\n if (action === \"delete\") {\n const filename = requireFilename(params);\n await eventStore.delete(filename);\n return {\n content: [{ type: \"text\", text: `Deleted event ${filename}` }],\n details: undefined,\n };\n }\n\n const payload = buildEventPayload(params, eventContext);\n const filename =\n action === \"update\"\n ? requireFilename(params)\n : `${sanitizeFileSegment(params.filenamePrefix || payload.type || \"event\")}-${Date.now()}.json`;\n\n log.logInfo(\n `${action === \"update\" ? \"Updating\" : \"Writing\"} event file via control plane store: ${filename} (type=${payload.type}, platform=${payload.platform}, conversation=${payload.conversationId})`,\n );\n\n try {\n const result =\n action === \"update\"\n ? await eventStore.update(filename, payload)\n : await eventStore.write(filename, payload);\n log.logInfo(\n `${action === \"update\" ? \"Updated\" : \"Wrote\"} event file via control plane store: ${result.path} (${result.size} bytes)`,\n );\n } catch (err) {\n log.logWarning(\n `Failed to ${action === \"update\" ? \"update\" : \"write\"} event file via control plane store: ${filename}`,\n String(err),\n );\n throw err;\n }\n\n return {\n content: [{ type: \"text\", text: formatEventWriteResult(action, filename, payload) }],\n details: undefined,\n };\n },\n };\n\n return {\n tool,\n setEventContext: (context: EventToolContext) => {\n eventContext = context;\n },\n };\n}\n\nfunction buildEventPayload(params: EventToolParams, context: EventToolContext): EventPayload {\n if (!params.type) {\n throw new Error(\"`type` is required for create and update actions\");\n }\n if (!params.text) {\n throw new Error(\"`text` is required for create and update actions\");\n }\n\n const base = {\n platform: context.platform,\n conversationId: context.conversationId,\n conversationKind: context.conversationKind,\n userId: context.userId,\n text: params.text,\n };\n\n if (params.type === \"immediate\") {\n return {\n ...base,\n type: \"immediate\",\n };\n }\n\n if (params.type === \"one-shot\") {\n if (!params.at) {\n throw new Error(\"`at` is required for one-shot events\");\n }\n\n const atTime = new Date(params.at).getTime();\n if (Number.isNaN(atTime)) {\n throw new Error(\"`at` must be a valid ISO 8601 timestamp with UTC offset\");\n }\n if (atTime <= Date.now()) {\n throw new Error(\n `\\`at\\` must be in the future; got ${params.at} (now=${new Date().toISOString()}). Check the timezone offset.`,\n );\n }\n\n // No sessionKey or threadTs: reminders should fire as top-level messages, not buried in old threads\n return { ...base, type: \"one-shot\", at: params.at };\n }\n\n if (!params.schedule) {\n throw new Error(\"`schedule` is required for periodic events\");\n }\n if (!params.timezone) {\n throw new Error(\"`timezone` is required for periodic events\");\n }\n return {\n ...base,\n type: \"periodic\",\n schedule: params.schedule,\n timezone: params.timezone,\n };\n}\n\nfunction formatEventWriteResult(\n action: \"create\" | \"update\",\n filename: string,\n payload: EventPayload,\n): string {\n const scheduledVerb = action === \"update\" ? \"Updated\" : \"Scheduled\";\n const immediateVerb = action === \"update\" ? \"Updated\" : \"Queued\";\n switch (payload.type) {\n case \"periodic\":\n return `${scheduledVerb} periodic event ${filename} for ${payload.platform}/${payload.conversationId} (${payload.schedule} ${payload.timezone})`;\n case \"one-shot\":\n return `${scheduledVerb} one-shot event ${filename} for ${payload.platform}/${payload.conversationId} at ${payload.at}`;\n case \"immediate\":\n return `${immediateVerb} immediate event ${filename} for ${payload.platform}/${payload.conversationId}`;\n }\n}\n\nfunction requireFilename(params: EventToolParams): string {\n if (!params.filename) {\n throw new Error(\"`filename` is required for read, update, and delete actions\");\n }\n return validateEventFilename(params.filename);\n}\n\nfunction validateEventFilename(filename: string): string {\n const trimmed = filename.trim();\n if (\n !trimmed ||\n trimmed !== basename(trimmed) ||\n trimmed.includes(\"..\") ||\n !trimmed.endsWith(\".json\")\n ) {\n throw new Error(\"Invalid event filename\");\n }\n return trimmed;\n}\n\nfunction sanitizeFileSegment(value: string): string {\n const sanitized = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"event\";\n}\n"]}
1
+ {"version":3,"file":"event.js","sourceRoot":"","sources":["../../src/tools/event.ts"],"names":[],"mappings":"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","sourcesContent":["import { mkdir, readdir, readFile, rm, stat } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport type { ConversationKind } from \"../adapter.js\";\nimport { buildEventPayload, EventTypeSchema, parseEventPayload } from \"../harness/event-format.js\";\nimport * as log from \"../log.js\";\nimport { atomicWritePrivateFile } from \"../utils/fs-atomic.js\";\n\nconst eventSchema = Type.Object({\n action: Type.Optional(\n Type.Union(\n [\n Type.Literal(\"create\"),\n Type.Literal(\"list\"),\n Type.Literal(\"read\"),\n Type.Literal(\"update\"),\n Type.Literal(\"delete\"),\n ],\n { description: \"CRUD action. Defaults to create for backward compatibility.\" },\n ),\n ),\n filename: Type.Optional(\n Type.String({\n description: \"Event filename for read, update, or delete actions\",\n }),\n ),\n scope: Type.Optional(\n Type.Union([Type.Literal(\"conversation\"), Type.Literal(\"all\")], {\n description:\n \"List scope. Defaults to conversation, which only shows events for the current conversation. Use all only when the user explicitly asks for all events.\",\n }),\n ),\n label: Type.Optional(\n Type.String({\n description: \"Brief description of the event you're scheduling (shown to user)\",\n }),\n ),\n type: Type.Optional(EventTypeSchema),\n text: Type.Optional(\n Type.String({\n description:\n \"A self-contained task for the future run. Include the necessary context, tone, and constraints in the text itself because events do not inherit normal conversation history.\",\n }),\n ),\n at: Type.Optional(\n Type.String({\n description: \"ISO 8601 timestamp with offset, required for one-shot events\",\n }),\n ),\n schedule: Type.Optional(\n Type.String({\n description: \"Cron schedule, required for periodic events\",\n }),\n ),\n timezone: Type.Optional(\n Type.String({\n description: \"IANA timezone, required for periodic events\",\n }),\n ),\n filenamePrefix: Type.Optional(\n Type.String({\n description: \"Optional filename prefix for the event file\",\n }),\n ),\n});\n\ninterface EventToolContext {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n}\n\ntype EventToolParams = {\n action?: \"create\" | \"list\" | \"read\" | \"update\" | \"delete\";\n filename?: string;\n scope?: \"conversation\" | \"all\";\n label?: string;\n type?: \"immediate\" | \"one-shot\" | \"periodic\";\n text?: string;\n at?: string;\n schedule?: string;\n timezone?: string;\n filenamePrefix?: string;\n};\n\nexport type { EventPayload, EventStore } from \"./types.js\";\nimport type { EventPayload, EventStore } from \"./types.js\";\n\nexport class HostEventStore implements EventStore {\n constructor(private readonly eventsDir: string) {}\n\n static fromWorkspaceDir(workspaceDir: string): HostEventStore {\n return new HostEventStore(join(workspaceDir, \"events\"));\n }\n\n async write(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n return this.writePayload(filename, payload);\n }\n\n async list(): Promise<\n Array<{ filename: string; payload: EventPayload | null; size: number; mtimeMs: number }>\n > {\n await mkdir(this.eventsDir, { recursive: true });\n const entries = await readdir(this.eventsDir, { withFileTypes: true });\n const events = await Promise.all(\n entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n .map(async (entry) => {\n try {\n return await this.read(entry.name);\n } catch {\n // Keep unparseable files visible (payload: null) so consumers can\n // surface and delete them; skip files that vanished mid-listing.\n try {\n const fileStat = await stat(join(this.eventsDir, entry.name));\n return {\n filename: entry.name,\n payload: null,\n size: fileStat.size,\n mtimeMs: fileStat.mtimeMs,\n };\n } catch {\n return null;\n }\n }\n }),\n );\n return events\n .filter((event) => event !== null)\n .toSorted((a, b) => a.filename.localeCompare(b.filename));\n }\n\n async read(\n filename: string,\n ): Promise<{ filename: string; payload: EventPayload; size: number; mtimeMs: number }> {\n const safeFilename = validateEventFilename(filename);\n const filePath = join(this.eventsDir, safeFilename);\n const [raw, fileStat] = await Promise.all([readFile(filePath, \"utf-8\"), stat(filePath)]);\n // Validation (shape, per-type fields, channelId alias) is owned by the\n // event-format module; files that fail it surface as payload:null in\n // list() and as errors here.\n const payload = parseEventPayload(raw, safeFilename);\n return {\n filename: safeFilename,\n payload,\n size: fileStat.size,\n mtimeMs: fileStat.mtimeMs,\n };\n }\n\n async update(filename: string, payload: EventPayload): Promise<{ path: string; size: number }> {\n return this.writePayload(filename, payload, true);\n }\n\n async delete(filename: string): Promise<{ deleted: boolean }> {\n const safeFilename = validateEventFilename(filename);\n await rm(join(this.eventsDir, safeFilename), { force: true });\n return { deleted: true };\n }\n\n private async writePayload(\n filename: string,\n payload: EventPayload,\n requireExisting = false,\n ): Promise<{ path: string; size: number }> {\n await mkdir(this.eventsDir, { recursive: true });\n const safeFilename = validateEventFilename(filename);\n const filePath = join(this.eventsDir, safeFilename);\n if (requireExisting) {\n await stat(filePath);\n }\n atomicWritePrivateFile(filePath, JSON.stringify(payload) + \"\\n\");\n const fileStat = await stat(filePath);\n return { path: filePath, size: fileStat.size };\n }\n}\n\nexport function createEventTool(eventStore: EventStore): {\n tool: AgentTool<typeof eventSchema>;\n setEventContext: (context: EventToolContext) => void;\n} {\n let eventContext: EventToolContext | null = null;\n\n const tool: AgentTool<typeof eventSchema> = {\n name: \"event\",\n label: \"event\",\n description:\n \"CRUD tool for scheduled events. Create immediate, one-shot, or periodic events for the current conversation. List defaults to events for the current conversation only; use scope=all only when the user explicitly asks to list all events. Event text must be self-contained because events do not inherit normal conversation history.\",\n parameters: eventSchema,\n execute: async (_toolCallId: string, params: EventToolParams, signal?: AbortSignal) => {\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n\n if (!eventContext) {\n throw new Error(\"Event context not configured\");\n }\n\n const action = params.action ?? \"create\";\n\n if (action === \"list\") {\n const events = await eventStore.list();\n const conversationId = eventContext.conversationId;\n const scopedEvents =\n params.scope === \"all\"\n ? events\n : events.filter((event) => event.payload?.conversationId === conversationId);\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n scope: params.scope ?? \"conversation\",\n conversationId: params.scope === \"all\" ? undefined : conversationId,\n events: scopedEvents,\n },\n null,\n 2,\n ),\n },\n ],\n details: undefined,\n };\n }\n\n if (action === \"read\") {\n const filename = requireFilename(params);\n const event = await eventStore.read(filename);\n return {\n content: [{ type: \"text\", text: JSON.stringify(event, null, 2) }],\n details: undefined,\n };\n }\n\n if (action === \"delete\") {\n const filename = requireFilename(params);\n await eventStore.delete(filename);\n return {\n content: [{ type: \"text\", text: `Deleted event ${filename}` }],\n details: undefined,\n };\n }\n\n const payload = buildToolEventPayload(params, eventContext);\n const filename =\n action === \"update\"\n ? requireFilename(params)\n : `${sanitizeFileSegment(params.filenamePrefix || payload.type || \"event\")}-${Date.now()}.json`;\n\n log.logInfo(\n `${action === \"update\" ? \"Updating\" : \"Writing\"} event file via control plane store: ${filename} (type=${payload.type}, platform=${payload.platform}, conversation=${payload.conversationId})`,\n );\n\n try {\n const result =\n action === \"update\"\n ? await eventStore.update(filename, payload)\n : await eventStore.write(filename, payload);\n log.logInfo(\n `${action === \"update\" ? \"Updated\" : \"Wrote\"} event file via control plane store: ${result.path} (${result.size} bytes)`,\n );\n } catch (err) {\n log.logWarning(\n `Failed to ${action === \"update\" ? \"update\" : \"write\"} event file via control plane store: ${filename}`,\n String(err),\n );\n throw err;\n }\n\n return {\n content: [{ type: \"text\", text: formatEventWriteResult(action, filename, payload) }],\n details: undefined,\n };\n },\n };\n\n return {\n tool,\n setEventContext: (context: EventToolContext) => {\n eventContext = context;\n },\n };\n}\n\nfunction buildToolEventPayload(params: EventToolParams, context: EventToolContext): EventPayload {\n if (!params.type) {\n throw new Error(\"`type` is required for create and update actions\");\n }\n if (!params.text) {\n throw new Error(\"`text` is required for create and update actions\");\n }\n\n // Per-type field rules live in the event-format module. No sessionKey or\n // threadTs in the payload: reminders should fire as top-level messages,\n // not buried in old threads.\n const payload = buildEventPayload({\n type: params.type,\n platform: context.platform,\n conversationId: context.conversationId,\n conversationKind: context.conversationKind,\n userId: context.userId,\n text: params.text,\n at: params.at,\n schedule: params.schedule,\n timezone: params.timezone,\n });\n\n // Tool-side write policy, not format knowledge: a reminder in the past\n // would be deleted unfired by the watcher, so reject it here.\n if (payload.type === \"one-shot\" && new Date(payload.at).getTime() <= Date.now()) {\n throw new Error(\n `\\`at\\` must be in the future; got ${payload.at} (now=${new Date().toISOString()}). Check the timezone offset.`,\n );\n }\n\n return payload;\n}\n\nfunction formatEventWriteResult(\n action: \"create\" | \"update\",\n filename: string,\n payload: EventPayload,\n): string {\n const scheduledVerb = action === \"update\" ? \"Updated\" : \"Scheduled\";\n const immediateVerb = action === \"update\" ? \"Updated\" : \"Queued\";\n const target = payload.platform\n ? `${payload.platform}/${payload.conversationId}`\n : payload.conversationId;\n switch (payload.type) {\n case \"periodic\":\n return `${scheduledVerb} periodic event ${filename} for ${target} (${payload.schedule} ${payload.timezone})`;\n case \"one-shot\":\n return `${scheduledVerb} one-shot event ${filename} for ${target} at ${payload.at}`;\n case \"immediate\":\n return `${immediateVerb} immediate event ${filename} for ${target}`;\n }\n}\n\nfunction requireFilename(params: EventToolParams): string {\n if (!params.filename) {\n throw new Error(\"`filename` is required for read, update, and delete actions\");\n }\n return validateEventFilename(params.filename);\n}\n\nfunction validateEventFilename(filename: string): string {\n const trimmed = filename.trim();\n if (\n !trimmed ||\n trimmed !== basename(trimmed) ||\n trimmed.includes(\"..\") ||\n !trimmed.endsWith(\".json\")\n ) {\n throw new Error(\"Invalid event filename\");\n }\n return trimmed;\n}\n\nfunction sanitizeFileSegment(value: string): string {\n const sanitized = value\n .trim()\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"event\";\n}\n"]}
@@ -0,0 +1,21 @@
1
+ import type { AgentTool } from "@earendil-works/pi-agent-core";
2
+ import type { Static, TSchema } from "@sinclair/typebox";
3
+ /**
4
+ * A host-backed tool whose implementation is injected per run: a pack's
5
+ * bindRun binds the function for the current conversation (or null to
6
+ * disable the tool). Owns the choreography every such tool used to repeat
7
+ * by hand — the holder + setter pair, the disabled-tool error, and the
8
+ * abort guard — so a tool module only states its schema and its run body.
9
+ */
10
+ export declare function defineHostFnTool<TFn, TParams extends TSchema>(definition: {
11
+ name: string;
12
+ description: string;
13
+ parameters: TParams;
14
+ /** Error thrown when the tool executes with no bound implementation. */
15
+ unavailable: string;
16
+ run: (fn: TFn, args: Static<TParams>, signal?: AbortSignal) => ReturnType<AgentTool<TParams>["execute"]>;
17
+ }): {
18
+ tool: AgentTool<TParams>;
19
+ setFn: (fn: TFn | null) => void;
20
+ };
21
+ //# sourceMappingURL=host-fn-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-fn-tool.d.ts","sourceRoot":"","sources":["../../src/tools/host-fn-tool.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEzD;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,SAAS,OAAO,EAAE,UAAU,EAAE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,wEAAwE;IACxE,WAAW,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,CACH,EAAE,EAAE,GAAG,EACP,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,EACrB,MAAM,CAAC,EAAE,WAAW,KACjB,UAAU,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC;CAChD,GAAG;IAAE,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;IAAC,KAAK,EAAE,CAAC,EAAE,EAAE,GAAG,GAAG,IAAI,KAAK,IAAI,CAAA;CAAE,CAyBhE"}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * A host-backed tool whose implementation is injected per run: a pack's
3
+ * bindRun binds the function for the current conversation (or null to
4
+ * disable the tool). Owns the choreography every such tool used to repeat
5
+ * by hand — the holder + setter pair, the disabled-tool error, and the
6
+ * abort guard — so a tool module only states its schema and its run body.
7
+ */
8
+ export function defineHostFnTool(definition) {
9
+ let bound = null;
10
+ const tool = {
11
+ name: definition.name,
12
+ label: definition.name,
13
+ description: definition.description,
14
+ parameters: definition.parameters,
15
+ execute: async (_toolCallId, args, signal) => {
16
+ if (!bound) {
17
+ throw new Error(definition.unavailable);
18
+ }
19
+ if (signal?.aborted) {
20
+ throw new Error("Operation aborted");
21
+ }
22
+ return definition.run(bound, args, signal);
23
+ },
24
+ };
25
+ return {
26
+ tool,
27
+ setFn: (fn) => {
28
+ bound = fn;
29
+ },
30
+ };
31
+ }
32
+ //# sourceMappingURL=host-fn-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host-fn-tool.js","sourceRoot":"","sources":["../../src/tools/host-fn-tool.ts"],"names":[],"mappings":"AAGA;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAA+B,UAW9D;IACC,IAAI,KAAK,GAAe,IAAI,CAAC;IAE7B,MAAM,IAAI,GAAuB;QAC/B,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,KAAK,EAAE,UAAU,CAAC,IAAI;QACtB,WAAW,EAAE,UAAU,CAAC,WAAW;QACnC,UAAU,EAAE,UAAU,CAAC,UAAU;QACjC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;YAC3C,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;YAC1C,CAAC;YACD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;YACD,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,IAAuB,EAAE,MAAM,CAAC,CAAC;QAChE,CAAC;KACF,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,KAAK,EAAE,CAAC,EAAE,EAAE,EAAE;YACZ,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { Static, TSchema } from \"@sinclair/typebox\";\n\n/**\n * A host-backed tool whose implementation is injected per run: a pack's\n * bindRun binds the function for the current conversation (or null to\n * disable the tool). Owns the choreography every such tool used to repeat\n * by hand — the holder + setter pair, the disabled-tool error, and the\n * abort guard — so a tool module only states its schema and its run body.\n */\nexport function defineHostFnTool<TFn, TParams extends TSchema>(definition: {\n name: string;\n description: string;\n parameters: TParams;\n /** Error thrown when the tool executes with no bound implementation. */\n unavailable: string;\n run: (\n fn: TFn,\n args: Static<TParams>,\n signal?: AbortSignal,\n ) => ReturnType<AgentTool<TParams>[\"execute\"]>;\n}): { tool: AgentTool<TParams>; setFn: (fn: TFn | null) => void } {\n let bound: TFn | null = null;\n\n const tool: AgentTool<TParams> = {\n name: definition.name,\n label: definition.name,\n description: definition.description,\n parameters: definition.parameters,\n execute: async (_toolCallId, args, signal) => {\n if (!bound) {\n throw new Error(definition.unavailable);\n }\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n return definition.run(bound, args as Static<TParams>, signal);\n },\n };\n\n return {\n tool,\n setFn: (fn) => {\n bound = fn;\n },\n };\n}\n"]}
@@ -6,6 +6,7 @@ import type { Executor, SandboxConfig } from "../sandbox/index.js";
6
6
  import type { SandboxResourceController } from "../types.js";
7
7
  import type { PlatformToolPackFactory } from "./types.js";
8
8
  export { createSubagentTool } from "./subagent.js";
9
+ export { DEFAULT_GLOBAL_SUBAGENT_SLOTS, SubagentSlotPool } from "./subagent-slots.js";
9
10
  export declare function createMikanTools(executor: Executor, workspaceDir: string, sandboxController?: {
10
11
  sandbox: SandboxConfig;
11
12
  resourceController?: Pick<SandboxResourceController, "getLimitStatus" | "setLimits">;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAQ7D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,iBAAiB,CAAC,EAAE;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,kBAAkB,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CACtF;AACD;sEACsE;AACtE,yBAAyB,GAAE,SAAS,uBAAuB,EAAO,EAClE,eAAe,CAAC,EAAE;IAChB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9C,GACA;IACD,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACrF,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1E,qBAAqB,EAAE,CAAC,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvF,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,iBAAiB,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,CAwCA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,OAAO,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACnE,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAQ7D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEtF,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,EACpB,iBAAiB,CAAC,EAAE;IAClB,OAAO,EAAE,aAAa,CAAC;IACvB,kBAAkB,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CACtF;AACD;sEACsE;AACtE,yBAAyB,GAAE,SAAS,uBAAuB,EAAO,EAClE,eAAe,CAAC,EAAE;IAChB,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC;IAClB,SAAS,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC9C,GACA;IACD,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;IAC5B,iBAAiB,EAAE,CAAC,EAAE,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IACrF,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;IAC1E,qBAAqB,EAAE,CAAC,GAAG,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACvF,eAAe,EAAE,CAAC,OAAO,EAAE;QACzB,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,gBAAgB,EAAE,gBAAgB,CAAC;QACnC,MAAM,EAAE,MAAM,CAAC;KAChB,KAAK,IAAI,CAAC;IACX,iBAAiB,EAAE,CAAC,OAAO,EAAE;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;CAClF,CAwCA"}
@@ -8,6 +8,7 @@ import { createReadTool } from "./read.js";
8
8
  import { createSandboxTool } from "./sandbox.js";
9
9
  import { createWriteTool } from "./write.js";
10
10
  export { createSubagentTool } from "./subagent.js";
11
+ export { DEFAULT_GLOBAL_SUBAGENT_SLOTS, SubagentSlotPool } from "./subagent-slots.js";
11
12
  export function createMikanTools(executor, workspaceDir, sandboxController,
12
13
  /** Platform capability pack factories (e.g. GitHub PR/CI); instantiated
13
14
  * here so each runner owns its packs' bind state. Not core tools. */
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,YAAoB,EACpB,iBAGC;AACD;sEACsE;AACtE,yBAAyB,GAAuC,EAAE,EAClE,eAGC;IAcD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,SAAS,GAAG,eAAe;QAC/B,CAAC,CAAC,uBAAuB,CAAC,EAAE,GAAG,eAAe,EAAE,YAAY,EAAE,CAAC;QAC/D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,eAAe,EAAE,CAAC;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,eAAe,CAC1D,cAAc,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAC9C,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAChE,iBAAiB,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAC9D,CAAC;IACF,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;IACtF,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClE,OAAO;QACL,KAAK,EAAE;YACL,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,EAAE,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;YAC7D,cAAc,CAAC,QAAQ,CAAC;YACxB,eAAe,CAAC,QAAQ,CAAC;YACzB,SAAS;YACT,WAAW;YACX,UAAU;YACV,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,SAAS;YACT,GAAG,SAAS;SACb;QACD,iBAAiB,EAAE,CAAC,EAAE,EAAE,EAAE;YACxB,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACtB,SAAS,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,gBAAgB;QAChB,qBAAqB,EAAE,CAAC,GAAG,EAAE,EAAE;YAC7B,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;gBACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ConversationKind } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport type { SandboxResourceController } from \"../types.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createGenerateImageTool } from \"./generate-image.js\";\nimport { createReactTool } from \"./react.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport type { PlatformToolPackFactory } from \"./types.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport { createSubagentTool } from \"./subagent.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n resourceController?: Pick<SandboxResourceController, \"getLimitStatus\" | \"setLimits\">;\n },\n /** Platform capability pack factories (e.g. GitHub PR/CI); instantiated\n * here so each runner owns its packs' bind state. Not core tools. */\n platformToolPackFactories: readonly PlatformToolPackFactory[] = [],\n imageGeneration?: {\n model: Model<Api>;\n getApiKey: () => Promise<string | undefined>;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setReactFunction: (fn: ((emoji: string) => Promise<void>) | null) => void;\n bindPlatformToolPacks: (ctx: { conversationId: string; platformName: string }) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n }) => void;\n setSandboxContext: (context: { conversationId: string; userId: string }) => void;\n} {\n const { tool: attachTool, setUploadFunction } = createAttachTool();\n const imageTool = imageGeneration\n ? createGenerateImageTool({ ...imageGeneration, workspaceDir })\n : undefined;\n const { tool: reactTool, setReactFunction } = createReactTool();\n const { tool: eventTool, setEventContext } = createEventTool(\n HostEventStore.fromWorkspaceDir(workspaceDir),\n );\n const { tool: sandboxTool, setSandboxContext } = createSandboxTool(\n sandboxController ?? { sandbox: executor.getSandboxConfig() },\n );\n const platformToolPacks = platformToolPackFactories.map((createPack) => createPack());\n const packTools = platformToolPacks.flatMap((pack) => pack.tools);\n return {\n tools: [\n createReadTool(executor),\n createBashTool(executor, { hostWorkspaceRoot: workspaceDir }),\n createEditTool(executor),\n createWriteTool(executor),\n eventTool,\n sandboxTool,\n attachTool,\n ...(imageTool ? [imageTool.tool] : []),\n reactTool,\n ...packTools,\n ],\n setUploadFunction: (fn) => {\n setUploadFunction(fn);\n imageTool?.setUploadFunction(fn);\n },\n setReactFunction,\n bindPlatformToolPacks: (ctx) => {\n for (const pack of platformToolPacks) {\n pack.bindRun(ctx);\n }\n },\n setEventContext,\n setSandboxContext,\n };\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAGrE,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAEtF,MAAM,UAAU,gBAAgB,CAC9B,QAAkB,EAClB,YAAoB,EACpB,iBAGC;AACD;sEACsE;AACtE,yBAAyB,GAAuC,EAAE,EAClE,eAGC;IAcD,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,iBAAiB,EAAE,GAAG,gBAAgB,EAAE,CAAC;IACnE,MAAM,SAAS,GAAG,eAAe;QAC/B,CAAC,CAAC,uBAAuB,CAAC,EAAE,GAAG,eAAe,EAAE,YAAY,EAAE,CAAC;QAC/D,CAAC,CAAC,SAAS,CAAC;IACd,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,eAAe,EAAE,CAAC;IAChE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,eAAe,CAC1D,cAAc,CAAC,gBAAgB,CAAC,YAAY,CAAC,CAC9C,CAAC;IACF,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,iBAAiB,EAAE,GAAG,iBAAiB,CAChE,iBAAiB,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAC9D,CAAC;IACF,MAAM,iBAAiB,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,EAAE,CAAC,CAAC;IACtF,MAAM,SAAS,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClE,OAAO;QACL,KAAK,EAAE;YACL,cAAc,CAAC,QAAQ,CAAC;YACxB,cAAc,CAAC,QAAQ,EAAE,EAAE,iBAAiB,EAAE,YAAY,EAAE,CAAC;YAC7D,cAAc,CAAC,QAAQ,CAAC;YACxB,eAAe,CAAC,QAAQ,CAAC;YACzB,SAAS;YACT,WAAW;YACX,UAAU;YACV,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACtC,SAAS;YACT,GAAG,SAAS;SACb;QACD,iBAAiB,EAAE,CAAC,EAAE,EAAE,EAAE;YACxB,iBAAiB,CAAC,EAAE,CAAC,CAAC;YACtB,SAAS,EAAE,iBAAiB,CAAC,EAAE,CAAC,CAAC;QACnC,CAAC;QACD,gBAAgB;QAChB,qBAAqB,EAAE,CAAC,GAAG,EAAE,EAAE;YAC7B,KAAK,MAAM,IAAI,IAAI,iBAAiB,EAAE,CAAC;gBACrC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;QACH,CAAC;QACD,eAAe;QACf,iBAAiB;KAClB,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport type { TSchema } from \"@sinclair/typebox\";\nimport type { ConversationKind } from \"../adapter.js\";\nimport { createAttachTool } from \"../adapters/slack/tools/attach.js\";\nimport type { Executor, SandboxConfig } from \"../sandbox/index.js\";\nimport type { SandboxResourceController } from \"../types.js\";\nimport { createBashTool } from \"./bash.js\";\nimport { createEditTool } from \"./edit.js\";\nimport { createEventTool, HostEventStore } from \"./event.js\";\nimport { createGenerateImageTool } from \"./generate-image.js\";\nimport { createReactTool } from \"./react.js\";\nimport { createReadTool } from \"./read.js\";\nimport { createSandboxTool } from \"./sandbox.js\";\nimport type { PlatformToolPackFactory } from \"./types.js\";\nimport { createWriteTool } from \"./write.js\";\n\nexport { createSubagentTool } from \"./subagent.js\";\nexport { DEFAULT_GLOBAL_SUBAGENT_SLOTS, SubagentSlotPool } from \"./subagent-slots.js\";\n\nexport function createMikanTools(\n executor: Executor,\n workspaceDir: string,\n sandboxController?: {\n sandbox: SandboxConfig;\n resourceController?: Pick<SandboxResourceController, \"getLimitStatus\" | \"setLimits\">;\n },\n /** Platform capability pack factories (e.g. GitHub PR/CI); instantiated\n * here so each runner owns its packs' bind state. Not core tools. */\n platformToolPackFactories: readonly PlatformToolPackFactory[] = [],\n imageGeneration?: {\n model: Model<Api>;\n getApiKey: () => Promise<string | undefined>;\n },\n): {\n tools: AgentTool<TSchema>[];\n setUploadFunction: (fn: (filePath: string, title?: string) => Promise<void>) => void;\n setReactFunction: (fn: ((emoji: string) => Promise<void>) | null) => void;\n bindPlatformToolPacks: (ctx: { conversationId: string; platformName: string }) => void;\n setEventContext: (context: {\n platform: string;\n conversationId: string;\n conversationKind: ConversationKind;\n userId: string;\n }) => void;\n setSandboxContext: (context: { conversationId: string; userId: string }) => void;\n} {\n const { tool: attachTool, setUploadFunction } = createAttachTool();\n const imageTool = imageGeneration\n ? createGenerateImageTool({ ...imageGeneration, workspaceDir })\n : undefined;\n const { tool: reactTool, setReactFunction } = createReactTool();\n const { tool: eventTool, setEventContext } = createEventTool(\n HostEventStore.fromWorkspaceDir(workspaceDir),\n );\n const { tool: sandboxTool, setSandboxContext } = createSandboxTool(\n sandboxController ?? { sandbox: executor.getSandboxConfig() },\n );\n const platformToolPacks = platformToolPackFactories.map((createPack) => createPack());\n const packTools = platformToolPacks.flatMap((pack) => pack.tools);\n return {\n tools: [\n createReadTool(executor),\n createBashTool(executor, { hostWorkspaceRoot: workspaceDir }),\n createEditTool(executor),\n createWriteTool(executor),\n eventTool,\n sandboxTool,\n attachTool,\n ...(imageTool ? [imageTool.tool] : []),\n reactTool,\n ...packTools,\n ],\n setUploadFunction: (fn) => {\n setUploadFunction(fn);\n imageTool?.setUploadFunction(fn);\n },\n setReactFunction,\n bindPlatformToolPacks: (ctx) => {\n for (const pack of platformToolPacks) {\n pack.bindRun(ctx);\n }\n },\n setEventContext,\n setSandboxContext,\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/tools/react.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D,QAAA,MAAM,WAAW;;EAKf,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,eAAe,IAAI;IACjC,IAAI,EAAE,SAAS,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3E,CA8BA"}
1
+ {"version":3,"file":"react.d.ts","sourceRoot":"","sources":["../../src/tools/react.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAI/D,QAAA,MAAM,WAAW;;EAKf,CAAC;AAEH;;;;GAIG;AACH,wBAAgB,eAAe,IAAI;IACjC,IAAI,EAAE,SAAS,CAAC,OAAO,WAAW,CAAC,CAAC;IACpC,gBAAgB,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,GAAG,IAAI,KAAK,IAAI,CAAC;CAC3E,CAiBA"}
@@ -1,4 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
+ import { defineHostFnTool } from "./host-fn-tool.js";
2
3
  const reactSchema = Type.Object({
3
4
  emoji: Type.String({
4
5
  description: "Emoji to react with. Use a short name without colons (e.g. eyes, white_check_mark, +1) on Slack, or a Unicode emoji on Discord/Telegram.",
@@ -10,31 +11,19 @@ const reactSchema = Type.Object({
10
11
  * {@link createReactTool}'s setter; the model only supplies the emoji.
11
12
  */
12
13
  export function createReactTool() {
13
- let reactFn = null;
14
- const tool = {
14
+ const { tool, setFn } = defineHostFnTool({
15
15
  name: "react",
16
- label: "react",
17
16
  description: "Add an emoji reaction to the message you are responding to. Useful for a lightweight acknowledgement (e.g. eyes to show you saw a request, white_check_mark when done) instead of a full reply.",
18
17
  parameters: reactSchema,
19
- execute: async (_toolCallId, { emoji }, signal) => {
20
- if (!reactFn) {
21
- throw new Error("Reactions are not supported in this conversation.");
22
- }
23
- if (signal?.aborted) {
24
- throw new Error("Operation aborted");
25
- }
18
+ unavailable: "Reactions are not supported in this conversation.",
19
+ run: async (reactFn, { emoji }) => {
26
20
  await reactFn(emoji);
27
21
  return {
28
22
  content: [{ type: "text", text: `Reacted with :${emoji.replace(/^:|:$/g, "")}:` }],
29
23
  details: undefined,
30
24
  };
31
25
  },
32
- };
33
- return {
34
- tool,
35
- setReactFunction: (fn) => {
36
- reactFn = fn;
37
- },
38
- };
26
+ });
27
+ return { tool, setReactFunction: setFn };
39
28
  }
40
29
  //# sourceMappingURL=react.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/tools/react.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEzC,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,WAAW,EACT,0IAA0I;KAC7I,CAAC;CACH,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAI7B,IAAI,OAAO,GAA8C,IAAI,CAAC;IAE9D,MAAM,IAAI,GAAkC;QAC1C,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,OAAO;QACd,WAAW,EACT,iMAAiM;QACnM,UAAU,EAAE,WAAW;QACvB,OAAO,EAAE,KAAK,EAAE,WAAmB,EAAE,EAAE,KAAK,EAAqB,EAAE,MAAoB,EAAE,EAAE;YACzF,IAAI,CAAC,OAAO,EAAE,CAAC;gBACb,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;YACvE,CAAC;YACD,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;YACvC,CAAC;YACD,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC;gBAC3F,OAAO,EAAE,SAAS;aACnB,CAAC;QACJ,CAAC;KACF,CAAC;IAEF,OAAO;QACL,IAAI;QACJ,gBAAgB,EAAE,CAAC,EAAE,EAAE,EAAE;YACvB,OAAO,GAAG,EAAE,CAAC;QACf,CAAC;KACF,CAAC;AACJ,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\n\nconst reactSchema = Type.Object({\n emoji: Type.String({\n description:\n \"Emoji to react with. Use a short name without colons (e.g. eyes, white_check_mark, +1) on Slack, or a Unicode emoji on Discord/Telegram.\",\n }),\n});\n\n/**\n * The `react` tool adds an emoji reaction to the message that triggered the\n * current run. The channel and message timestamp are captured per run via\n * {@link createReactTool}'s setter; the model only supplies the emoji.\n */\nexport function createReactTool(): {\n tool: AgentTool<typeof reactSchema>;\n setReactFunction: (fn: ((emoji: string) => Promise<void>) | null) => void;\n} {\n let reactFn: ((emoji: string) => Promise<void>) | null = null;\n\n const tool: AgentTool<typeof reactSchema> = {\n name: \"react\",\n label: \"react\",\n description:\n \"Add an emoji reaction to the message you are responding to. Useful for a lightweight acknowledgement (e.g. eyes to show you saw a request, white_check_mark when done) instead of a full reply.\",\n parameters: reactSchema,\n execute: async (_toolCallId: string, { emoji }: { emoji: string }, signal?: AbortSignal) => {\n if (!reactFn) {\n throw new Error(\"Reactions are not supported in this conversation.\");\n }\n if (signal?.aborted) {\n throw new Error(\"Operation aborted\");\n }\n await reactFn(emoji);\n return {\n content: [{ type: \"text\" as const, text: `Reacted with :${emoji.replace(/^:|:$/g, \"\")}:` }],\n details: undefined,\n };\n },\n };\n\n return {\n tool,\n setReactFunction: (fn) => {\n reactFn = fn;\n },\n };\n}\n"]}
1
+ {"version":3,"file":"react.js","sourceRoot":"","sources":["../../src/tools/react.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC;IAC9B,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC;QACjB,WAAW,EACT,0IAA0I;KAC7I,CAAC;CACH,CAAC,CAAC;AAEH;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAI7B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,gBAAgB,CAAuD;QAC7F,IAAI,EAAE,OAAO;QACb,WAAW,EACT,iMAAiM;QACnM,UAAU,EAAE,WAAW;QACvB,WAAW,EAAE,mDAAmD;QAChE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE;YAChC,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC;YACrB,OAAO;gBACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAe,EAAE,IAAI,EAAE,iBAAiB,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,EAAE,CAAC;gBAC3F,OAAO,EAAE,SAAS;aACnB,CAAC;QACJ,CAAC;KACF,CAAC,CAAC;IAEH,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,CAAC;AAC3C,CAAC","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport { Type } from \"@sinclair/typebox\";\nimport { defineHostFnTool } from \"./host-fn-tool.js\";\n\nconst reactSchema = Type.Object({\n emoji: Type.String({\n description:\n \"Emoji to react with. Use a short name without colons (e.g. eyes, white_check_mark, +1) on Slack, or a Unicode emoji on Discord/Telegram.\",\n }),\n});\n\n/**\n * The `react` tool adds an emoji reaction to the message that triggered the\n * current run. The channel and message timestamp are captured per run via\n * {@link createReactTool}'s setter; the model only supplies the emoji.\n */\nexport function createReactTool(): {\n tool: AgentTool<typeof reactSchema>;\n setReactFunction: (fn: ((emoji: string) => Promise<void>) | null) => void;\n} {\n const { tool, setFn } = defineHostFnTool<(emoji: string) => Promise<void>, typeof reactSchema>({\n name: \"react\",\n description:\n \"Add an emoji reaction to the message you are responding to. Useful for a lightweight acknowledgement (e.g. eyes to show you saw a request, white_check_mark when done) instead of a full reply.\",\n parameters: reactSchema,\n unavailable: \"Reactions are not supported in this conversation.\",\n run: async (reactFn, { emoji }) => {\n await reactFn(emoji);\n return {\n content: [{ type: \"text\" as const, text: `Reacted with :${emoji.replace(/^:|:$/g, \"\")}:` }],\n details: undefined,\n };\n },\n });\n\n return { tool, setReactFunction: setFn };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAI/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AA6BpD,QAAA,MAAM,UAAU;;;;;EASd,CAAC;AAMH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CA0H/E"}
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAI/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AA4BpD,QAAA,MAAM,UAAU;;;;;EASd,CAAC;AAMH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC,OAAO,UAAU,CAAC,CAuG/E"}
@@ -1,6 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { extname } from "path";
3
- import { shellEscape } from "../sandbox/utils.js";
4
3
  import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, formatSize, truncateHead, } from "./truncate.js";
5
4
  /**
6
5
  * Map of file extensions to MIME types for common image formats
@@ -36,12 +35,8 @@ export function createReadTool(executor) {
36
35
  execute: async (_toolCallId, { path, offset, limit }, signal) => {
37
36
  const mimeType = isImageFile(path);
38
37
  if (mimeType) {
39
- // Read as image (binary) - use base64
40
- const result = await executor.exec(`base64 < ${shellEscape(path)}`, { signal });
41
- if (result.code !== 0) {
42
- throw new Error(result.stderr || `Failed to read file: ${path}`);
43
- }
44
- const base64 = result.stdout.replace(/\s/g, ""); // Remove whitespace from base64
38
+ // Binary content rides the executor's own transport, never shell argv.
39
+ const base64 = await executor.readFileBase64(path, { signal });
45
40
  return {
46
41
  content: [
47
42
  { type: "text", text: `Read image file [${mimeType}]` },
@@ -50,12 +45,10 @@ export function createReadTool(executor) {
50
45
  details: undefined,
51
46
  };
52
47
  }
53
- // Get total line count first
54
- const countResult = await executor.exec(`wc -l < ${shellEscape(path)}`, { signal });
55
- if (countResult.code !== 0) {
56
- throw new Error(countResult.stderr || `Failed to read file: ${path}`);
57
- }
58
- const totalFileLines = Number.parseInt(countResult.stdout.trim(), 10) + 1; // wc -l counts newlines, not lines
48
+ // One transport read; line selection is plain code, not shell strings.
49
+ const fileText = await executor.readFile(path, { signal });
50
+ const fileLines = fileText.split("\n");
51
+ const totalFileLines = fileLines.length;
59
52
  // Apply offset if specified (1-indexed)
60
53
  const startLine = offset ? Math.max(1, offset) : 1;
61
54
  const startLineDisplay = startLine;
@@ -63,19 +56,7 @@ export function createReadTool(executor) {
63
56
  if (startLine > totalFileLines) {
64
57
  throw new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);
65
58
  }
66
- // Read content with offset
67
- let cmd;
68
- if (startLine === 1) {
69
- cmd = `cat ${shellEscape(path)}`;
70
- }
71
- else {
72
- cmd = `tail -n +${startLine} ${shellEscape(path)}`;
73
- }
74
- const result = await executor.exec(cmd, { signal });
75
- if (result.code !== 0) {
76
- throw new Error(result.stderr || `Failed to read file: ${path}`);
77
- }
78
- let selectedContent = result.stdout;
59
+ let selectedContent = startLine === 1 ? fileText : fileLines.slice(startLine - 1).join("\n");
79
60
  let userLimitedLines;
80
61
  // Apply user limit if specified
81
62
  if (limit !== undefined) {
@@ -1 +1 @@
1
- {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { Type } from \"@sinclair/typebox\";\nimport { extname } from \"path\";\nimport type { Executor } from \"../sandbox/index.js\";\nimport { shellEscape } from \"../sandbox/utils.js\";\nimport {\n DEFAULT_MAX_BYTES,\n DEFAULT_MAX_LINES,\n formatSize,\n type TruncationResult,\n truncateHead,\n} from \"./truncate.js\";\n\n/**\n * Map of file extensions to MIME types for common image formats\n */\nconst IMAGE_MIME_TYPES: Record<string, string> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n};\n\n/**\n * Check if a file is an image based on its extension\n */\nfunction isImageFile(filePath: string): string | null {\n const ext = extname(filePath).toLowerCase();\n return IMAGE_MIME_TYPES[ext] || null;\n}\n\nconst readSchema = Type.Object({\n label: Type.String({\n description: \"Brief description of what you're reading and why (shown to user)\",\n }),\n path: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n offset: Type.Optional(\n Type.Number({ description: \"Line number to start reading from (1-indexed)\" }),\n ),\n limit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\ninterface ReadToolDetails {\n truncation?: TruncationResult;\n}\n\nexport function createReadTool(executor: Executor): AgentTool<typeof readSchema> {\n return {\n name: \"read\",\n label: \"read\",\n description: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files.`,\n parameters: readSchema,\n execute: async (\n _toolCallId: string,\n { path, offset, limit }: { label: string; path: string; offset?: number; limit?: number },\n signal?: AbortSignal,\n ): Promise<{\n content: (TextContent | ImageContent)[];\n details: ReadToolDetails | undefined;\n }> => {\n const mimeType = isImageFile(path);\n\n if (mimeType) {\n // Read as image (binary) - use base64\n const result = await executor.exec(`base64 < ${shellEscape(path)}`, { signal });\n if (result.code !== 0) {\n throw new Error(result.stderr || `Failed to read file: ${path}`);\n }\n const base64 = result.stdout.replace(/\\s/g, \"\"); // Remove whitespace from base64\n\n return {\n content: [\n { type: \"text\", text: `Read image file [${mimeType}]` },\n { type: \"image\", data: base64, mimeType },\n ],\n details: undefined,\n };\n }\n\n // Get total line count first\n const countResult = await executor.exec(`wc -l < ${shellEscape(path)}`, { signal });\n if (countResult.code !== 0) {\n throw new Error(countResult.stderr || `Failed to read file: ${path}`);\n }\n const totalFileLines = Number.parseInt(countResult.stdout.trim(), 10) + 1; // wc -l counts newlines, not lines\n\n // Apply offset if specified (1-indexed)\n const startLine = offset ? Math.max(1, offset) : 1;\n const startLineDisplay = startLine;\n\n // Check if offset is out of bounds\n if (startLine > totalFileLines) {\n throw new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);\n }\n\n // Read content with offset\n let cmd: string;\n if (startLine === 1) {\n cmd = `cat ${shellEscape(path)}`;\n } else {\n cmd = `tail -n +${startLine} ${shellEscape(path)}`;\n }\n\n const result = await executor.exec(cmd, { signal });\n if (result.code !== 0) {\n throw new Error(result.stderr || `Failed to read file: ${path}`);\n }\n\n let selectedContent = result.stdout;\n let userLimitedLines: number | undefined;\n\n // Apply user limit if specified\n if (limit !== undefined) {\n const lines = selectedContent.split(\"\\n\");\n const endLine = Math.min(limit, lines.length);\n selectedContent = lines.slice(0, endLine).join(\"\\n\");\n userLimitedLines = endLine;\n }\n\n // Apply truncation (respects both line and byte limits)\n const truncation = truncateHead(selectedContent);\n\n let outputText: string;\n let details: ReadToolDetails | undefined;\n\n if (truncation.firstLineExceedsLimit) {\n // First line at offset exceeds 50KB - tell model to use bash\n const firstLineSize = formatSize(\n Buffer.byteLength(selectedContent.split(\"\\n\")[0], \"utf-8\"),\n );\n outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n details = { truncation };\n } else if (truncation.truncated) {\n // Truncation occurred - build actionable notice\n const endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n const nextOffset = endLineDisplay + 1;\n\n outputText = truncation.content;\n\n if (truncation.truncatedBy === \"lines\") {\n outputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue]`;\n } else {\n outputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue]`;\n }\n details = { truncation };\n } else if (userLimitedLines !== undefined) {\n // User specified limit, check if there's more content\n const linesFromStart = startLine - 1 + userLimitedLines;\n if (linesFromStart < totalFileLines) {\n const remaining = totalFileLines - linesFromStart;\n const nextOffset = startLine + userLimitedLines;\n\n outputText = truncation.content;\n outputText += `\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`;\n } else {\n outputText = truncation.content;\n }\n } else {\n // No truncation, no user limit exceeded\n outputText = truncation.content;\n }\n\n return {\n content: [{ type: \"text\", text: outputText }],\n details,\n };\n },\n };\n}\n"]}
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/tools/read.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentTool } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { Type } from \"@sinclair/typebox\";\nimport { extname } from \"path\";\nimport type { Executor } from \"../sandbox/index.js\";\nimport {\n DEFAULT_MAX_BYTES,\n DEFAULT_MAX_LINES,\n formatSize,\n type TruncationResult,\n truncateHead,\n} from \"./truncate.js\";\n\n/**\n * Map of file extensions to MIME types for common image formats\n */\nconst IMAGE_MIME_TYPES: Record<string, string> = {\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".png\": \"image/png\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n};\n\n/**\n * Check if a file is an image based on its extension\n */\nfunction isImageFile(filePath: string): string | null {\n const ext = extname(filePath).toLowerCase();\n return IMAGE_MIME_TYPES[ext] || null;\n}\n\nconst readSchema = Type.Object({\n label: Type.String({\n description: \"Brief description of what you're reading and why (shown to user)\",\n }),\n path: Type.String({ description: \"Path to the file to read (relative or absolute)\" }),\n offset: Type.Optional(\n Type.Number({ description: \"Line number to start reading from (1-indexed)\" }),\n ),\n limit: Type.Optional(Type.Number({ description: \"Maximum number of lines to read\" })),\n});\n\ninterface ReadToolDetails {\n truncation?: TruncationResult;\n}\n\nexport function createReadTool(executor: Executor): AgentTool<typeof readSchema> {\n return {\n name: \"read\",\n label: \"read\",\n description: `Read the contents of a file. Supports text files and images (jpg, png, gif, webp). Images are sent as attachments. For text files, output is truncated to ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). Use offset/limit for large files.`,\n parameters: readSchema,\n execute: async (\n _toolCallId: string,\n { path, offset, limit }: { label: string; path: string; offset?: number; limit?: number },\n signal?: AbortSignal,\n ): Promise<{\n content: (TextContent | ImageContent)[];\n details: ReadToolDetails | undefined;\n }> => {\n const mimeType = isImageFile(path);\n\n if (mimeType) {\n // Binary content rides the executor's own transport, never shell argv.\n const base64 = await executor.readFileBase64(path, { signal });\n\n return {\n content: [\n { type: \"text\", text: `Read image file [${mimeType}]` },\n { type: \"image\", data: base64, mimeType },\n ],\n details: undefined,\n };\n }\n\n // One transport read; line selection is plain code, not shell strings.\n const fileText = await executor.readFile(path, { signal });\n const fileLines = fileText.split(\"\\n\");\n const totalFileLines = fileLines.length;\n\n // Apply offset if specified (1-indexed)\n const startLine = offset ? Math.max(1, offset) : 1;\n const startLineDisplay = startLine;\n\n // Check if offset is out of bounds\n if (startLine > totalFileLines) {\n throw new Error(`Offset ${offset} is beyond end of file (${totalFileLines} lines total)`);\n }\n\n let selectedContent = startLine === 1 ? fileText : fileLines.slice(startLine - 1).join(\"\\n\");\n let userLimitedLines: number | undefined;\n\n // Apply user limit if specified\n if (limit !== undefined) {\n const lines = selectedContent.split(\"\\n\");\n const endLine = Math.min(limit, lines.length);\n selectedContent = lines.slice(0, endLine).join(\"\\n\");\n userLimitedLines = endLine;\n }\n\n // Apply truncation (respects both line and byte limits)\n const truncation = truncateHead(selectedContent);\n\n let outputText: string;\n let details: ReadToolDetails | undefined;\n\n if (truncation.firstLineExceedsLimit) {\n // First line at offset exceeds 50KB - tell model to use bash\n const firstLineSize = formatSize(\n Buffer.byteLength(selectedContent.split(\"\\n\")[0], \"utf-8\"),\n );\n outputText = `[Line ${startLineDisplay} is ${firstLineSize}, exceeds ${formatSize(DEFAULT_MAX_BYTES)} limit. Use bash: sed -n '${startLineDisplay}p' ${path} | head -c ${DEFAULT_MAX_BYTES}]`;\n details = { truncation };\n } else if (truncation.truncated) {\n // Truncation occurred - build actionable notice\n const endLineDisplay = startLineDisplay + truncation.outputLines - 1;\n const nextOffset = endLineDisplay + 1;\n\n outputText = truncation.content;\n\n if (truncation.truncatedBy === \"lines\") {\n outputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines}. Use offset=${nextOffset} to continue]`;\n } else {\n outputText += `\\n\\n[Showing lines ${startLineDisplay}-${endLineDisplay} of ${totalFileLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Use offset=${nextOffset} to continue]`;\n }\n details = { truncation };\n } else if (userLimitedLines !== undefined) {\n // User specified limit, check if there's more content\n const linesFromStart = startLine - 1 + userLimitedLines;\n if (linesFromStart < totalFileLines) {\n const remaining = totalFileLines - linesFromStart;\n const nextOffset = startLine + userLimitedLines;\n\n outputText = truncation.content;\n outputText += `\\n\\n[${remaining} more lines in file. Use offset=${nextOffset} to continue]`;\n } else {\n outputText = truncation.content;\n }\n } else {\n // No truncation, no user limit exceeded\n outputText = truncation.content;\n }\n\n return {\n content: [{ type: \"text\", text: outputText }],\n details,\n };\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAkB,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAG7E,QAAA,MAAM,aAAa;;;;EAcjB,CAAC;AAQH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,kBAAkB,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CACtF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,qBAAqB,GAAG;IACpE,IAAI,EAAE,SAAS,CAAC,OAAO,aAAa,CAAC,CAAC;IACtC,iBAAiB,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC1D,CAkDA"}
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../src/tools/sandbox.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAkB,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAG7E,QAAA,MAAM,aAAa;;;;EAcjB,CAAC;AAQH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,UAAU,qBAAqB;IAC7B,OAAO,EAAE,aAAa,CAAC;IACvB,kBAAkB,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,gBAAgB,GAAG,WAAW,CAAC,CAAC;CACtF;AAED,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,qBAAqB,GAAG;IACpE,IAAI,EAAE,SAAS,CAAC,OAAO,aAAa,CAAC,CAAC;IACtC,iBAAiB,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAC1D,CAiDA"}
@@ -1,5 +1,5 @@
1
1
  import { Type } from "@sinclair/typebox";
2
- import { resolveActorVaultKey } from "../vault/routing.js";
2
+ import { actorKey } from "../sandbox/identity.js";
3
3
  const sandboxSchema = Type.Object({
4
4
  action: Type.Union([Type.Literal("status"), Type.Literal("set")], {
5
5
  description: "Use status to inspect current limits, or set to apply temporary limits.",
@@ -29,7 +29,10 @@ export function createSandboxTool(controller) {
29
29
  !controller.resourceController) {
30
30
  throw new Error("The sandbox tool only supports image:* and gondolin:* managed sandboxes");
31
31
  }
32
- const containerKey = resolveActorVaultKey(controller.sandbox, sandboxContext.userId, sandboxContext.conversationId);
32
+ const containerKey = actorKey(controller.sandbox, {
33
+ userId: sandboxContext.userId,
34
+ conversationId: sandboxContext.conversationId,
35
+ });
33
36
  if (params.action === "set") {
34
37
  const limits = normalizeLimits(params);
35
38
  const status = await controller.resourceController.setLimits(containerKey, limits);