@geminixiang/mikan 1.0.0-beta.4 → 1.0.0-beta.41

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 (661) hide show
  1. package/CHANGELOG.md +714 -0
  2. package/README.md +99 -36
  3. package/dist/adapter.d.ts +11 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js +18 -1
  6. package/dist/adapter.js.map +1 -1
  7. package/dist/adapters/discord/bot.d.ts +6 -7
  8. package/dist/adapters/discord/bot.d.ts.map +1 -1
  9. package/dist/adapters/discord/bot.js +88 -122
  10. package/dist/adapters/discord/bot.js.map +1 -1
  11. package/dist/adapters/discord/components.d.ts +33 -0
  12. package/dist/adapters/discord/components.d.ts.map +1 -0
  13. package/dist/adapters/discord/components.js +32 -0
  14. package/dist/adapters/discord/components.js.map +1 -0
  15. package/dist/adapters/discord/context.d.ts +3 -1
  16. package/dist/adapters/discord/context.d.ts.map +1 -1
  17. package/dist/adapters/discord/context.js +66 -217
  18. package/dist/adapters/discord/context.js.map +1 -1
  19. package/dist/adapters/discord/format.d.ts +3 -0
  20. package/dist/adapters/discord/format.d.ts.map +1 -0
  21. package/dist/adapters/discord/format.js +86 -0
  22. package/dist/adapters/discord/format.js.map +1 -0
  23. package/dist/adapters/github/bot.d.ts +89 -0
  24. package/dist/adapters/github/bot.d.ts.map +1 -0
  25. package/dist/adapters/github/bot.js +653 -0
  26. package/dist/adapters/github/bot.js.map +1 -0
  27. package/dist/adapters/github/client.d.ts +122 -0
  28. package/dist/adapters/github/client.d.ts.map +1 -0
  29. package/dist/adapters/github/client.js +314 -0
  30. package/dist/adapters/github/client.js.map +1 -0
  31. package/dist/adapters/github/cloudbuild.d.ts +19 -0
  32. package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
  33. package/dist/adapters/github/cloudbuild.js +71 -0
  34. package/dist/adapters/github/cloudbuild.js.map +1 -0
  35. package/dist/adapters/github/context.d.ts +12 -0
  36. package/dist/adapters/github/context.d.ts.map +1 -0
  37. package/dist/adapters/github/context.js +101 -0
  38. package/dist/adapters/github/context.js.map +1 -0
  39. package/dist/adapters/github/gcp-auth.d.ts +28 -0
  40. package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
  41. package/dist/adapters/github/gcp-auth.js +196 -0
  42. package/dist/adapters/github/gcp-auth.js.map +1 -0
  43. package/dist/adapters/github/github-ops.d.ts +91 -0
  44. package/dist/adapters/github/github-ops.d.ts.map +1 -0
  45. package/dist/adapters/github/github-ops.js +406 -0
  46. package/dist/adapters/github/github-ops.js.map +1 -0
  47. package/dist/adapters/github/ids.d.ts +34 -0
  48. package/dist/adapters/github/ids.d.ts.map +1 -0
  49. package/dist/adapters/github/ids.js +57 -0
  50. package/dist/adapters/github/ids.js.map +1 -0
  51. package/dist/adapters/github/repo.d.ts +33 -0
  52. package/dist/adapters/github/repo.d.ts.map +1 -0
  53. package/dist/adapters/github/repo.js +141 -0
  54. package/dist/adapters/github/repo.js.map +1 -0
  55. package/dist/adapters/github/tool-pack.d.ts +14 -0
  56. package/dist/adapters/github/tool-pack.d.ts.map +1 -0
  57. package/dist/adapters/github/tool-pack.js +56 -0
  58. package/dist/adapters/github/tool-pack.js.map +1 -0
  59. package/dist/adapters/github/tools/checks.d.ts +19 -0
  60. package/dist/adapters/github/tools/checks.d.ts.map +1 -0
  61. package/dist/adapters/github/tools/checks.js +105 -0
  62. package/dist/adapters/github/tools/checks.js.map +1 -0
  63. package/dist/adapters/github/tools/issue.d.ts +21 -0
  64. package/dist/adapters/github/tools/issue.d.ts.map +1 -0
  65. package/dist/adapters/github/tools/issue.js +48 -0
  66. package/dist/adapters/github/tools/issue.js.map +1 -0
  67. package/dist/adapters/github/tools/pr.d.ts +22 -0
  68. package/dist/adapters/github/tools/pr.d.ts.map +1 -0
  69. package/dist/adapters/github/tools/pr.js +48 -0
  70. package/dist/adapters/github/tools/pr.js.map +1 -0
  71. package/dist/adapters/github/tools/read.d.ts +21 -0
  72. package/dist/adapters/github/tools/read.d.ts.map +1 -0
  73. package/dist/adapters/github/tools/read.js +141 -0
  74. package/dist/adapters/github/tools/read.js.map +1 -0
  75. package/dist/adapters/github/tools/review-reply.d.ts +17 -0
  76. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
  77. package/dist/adapters/github/tools/review-reply.js +38 -0
  78. package/dist/adapters/github/tools/review-reply.js.map +1 -0
  79. package/dist/adapters/github/tools/sync.d.ts +17 -0
  80. package/dist/adapters/github/tools/sync.d.ts.map +1 -0
  81. package/dist/adapters/github/tools/sync.js +34 -0
  82. package/dist/adapters/github/tools/sync.js.map +1 -0
  83. package/dist/adapters/github/types.d.ts +373 -0
  84. package/dist/adapters/github/types.d.ts.map +1 -0
  85. package/dist/adapters/github/types.js +2 -0
  86. package/dist/adapters/github/types.js.map +1 -0
  87. package/dist/adapters/intake.d.ts +16 -5
  88. package/dist/adapters/intake.d.ts.map +1 -1
  89. package/dist/adapters/intake.js +86 -23
  90. package/dist/adapters/intake.js.map +1 -1
  91. package/dist/adapters/markdown-tables.d.ts +37 -0
  92. package/dist/adapters/markdown-tables.d.ts.map +1 -0
  93. package/dist/adapters/markdown-tables.js +164 -0
  94. package/dist/adapters/markdown-tables.js.map +1 -0
  95. package/dist/adapters/progressive-renderer.d.ts +7 -0
  96. package/dist/adapters/progressive-renderer.d.ts.map +1 -0
  97. package/dist/adapters/progressive-renderer.js +440 -0
  98. package/dist/adapters/progressive-renderer.js.map +1 -0
  99. package/dist/adapters/shared.d.ts +23 -10
  100. package/dist/adapters/shared.d.ts.map +1 -1
  101. package/dist/adapters/shared.js +60 -22
  102. package/dist/adapters/shared.js.map +1 -1
  103. package/dist/adapters/slack/assistant.d.ts +101 -0
  104. package/dist/adapters/slack/assistant.d.ts.map +1 -0
  105. package/dist/adapters/slack/assistant.js +204 -0
  106. package/dist/adapters/slack/assistant.js.map +1 -0
  107. package/dist/adapters/slack/blocks.d.ts +14 -0
  108. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  109. package/dist/adapters/slack/blocks.js +105 -147
  110. package/dist/adapters/slack/blocks.js.map +1 -1
  111. package/dist/adapters/slack/bot.d.ts +95 -13
  112. package/dist/adapters/slack/bot.d.ts.map +1 -1
  113. package/dist/adapters/slack/bot.js +545 -211
  114. package/dist/adapters/slack/bot.js.map +1 -1
  115. package/dist/adapters/slack/context.d.ts +3 -1
  116. package/dist/adapters/slack/context.d.ts.map +1 -1
  117. package/dist/adapters/slack/context.js +11 -17
  118. package/dist/adapters/slack/context.js.map +1 -1
  119. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  120. package/dist/adapters/slack/response-lifecycle.js +197 -323
  121. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  122. package/dist/adapters/slack/session.d.ts.map +1 -1
  123. package/dist/adapters/slack/session.js +7 -11
  124. package/dist/adapters/slack/session.js.map +1 -1
  125. package/dist/adapters/slack/stream-limits.d.ts +35 -0
  126. package/dist/adapters/slack/stream-limits.d.ts.map +1 -0
  127. package/dist/adapters/slack/stream-limits.js +56 -0
  128. package/dist/adapters/slack/stream-limits.js.map +1 -0
  129. package/dist/adapters/slack/tool-pack.d.ts +11 -0
  130. package/dist/adapters/slack/tool-pack.d.ts.map +1 -0
  131. package/dist/adapters/slack/tool-pack.js +45 -0
  132. package/dist/adapters/slack/tool-pack.js.map +1 -0
  133. package/dist/adapters/slack/tools/attach.js +1 -1
  134. package/dist/adapters/slack/tools/attach.js.map +1 -1
  135. package/dist/adapters/slack/tools/blockkit.d.ts +14 -0
  136. package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -0
  137. package/dist/adapters/slack/tools/blockkit.js +106 -0
  138. package/dist/adapters/slack/tools/blockkit.js.map +1 -0
  139. package/dist/adapters/slack/types.d.ts +40 -2
  140. package/dist/adapters/slack/types.d.ts.map +1 -1
  141. package/dist/adapters/slack/types.js.map +1 -1
  142. package/dist/adapters/telegram/bot.d.ts +8 -9
  143. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  144. package/dist/adapters/telegram/bot.js +115 -163
  145. package/dist/adapters/telegram/bot.js.map +1 -1
  146. package/dist/adapters/telegram/context.d.ts +2 -0
  147. package/dist/adapters/telegram/context.d.ts.map +1 -1
  148. package/dist/adapters/telegram/context.js +53 -188
  149. package/dist/adapters/telegram/context.js.map +1 -1
  150. package/dist/adapters/types.d.ts +132 -12
  151. package/dist/adapters/types.d.ts.map +1 -1
  152. package/dist/adapters/types.js.map +1 -1
  153. package/dist/agent-events.d.ts +3 -2
  154. package/dist/agent-events.d.ts.map +1 -1
  155. package/dist/agent-events.js +19 -1
  156. package/dist/agent-events.js.map +1 -1
  157. package/dist/agent.d.ts +14 -19
  158. package/dist/agent.d.ts.map +1 -1
  159. package/dist/agent.js +841 -298
  160. package/dist/agent.js.map +1 -1
  161. package/dist/cli/arg-grammar.d.ts +25 -0
  162. package/dist/cli/arg-grammar.d.ts.map +1 -0
  163. package/dist/cli/arg-grammar.js +61 -0
  164. package/dist/cli/arg-grammar.js.map +1 -0
  165. package/dist/cli/boot.d.ts +5 -0
  166. package/dist/cli/boot.d.ts.map +1 -0
  167. package/dist/cli/boot.js +117 -0
  168. package/dist/cli/boot.js.map +1 -0
  169. package/dist/cli/download.d.ts.map +1 -0
  170. package/dist/{download.js → cli/download.js} +2 -0
  171. package/dist/cli/download.js.map +1 -0
  172. package/dist/cli/ext-dev.d.ts +2 -0
  173. package/dist/cli/ext-dev.d.ts.map +1 -0
  174. package/dist/cli/ext-dev.js +188 -0
  175. package/dist/cli/ext-dev.js.map +1 -0
  176. package/dist/cli/ext.d.ts +2 -0
  177. package/dist/cli/ext.d.ts.map +1 -0
  178. package/dist/cli/ext.js +324 -0
  179. package/dist/cli/ext.js.map +1 -0
  180. package/dist/cli/office.d.ts +2 -0
  181. package/dist/cli/office.d.ts.map +1 -0
  182. package/dist/cli/office.js +91 -0
  183. package/dist/cli/office.js.map +1 -0
  184. package/dist/cli/types.d.ts +16 -0
  185. package/dist/cli/types.d.ts.map +1 -0
  186. package/dist/cli/types.js +2 -0
  187. package/dist/cli/types.js.map +1 -0
  188. package/dist/commands/admin.d.ts.map +1 -1
  189. package/dist/commands/admin.js +3 -2
  190. package/dist/commands/admin.js.map +1 -1
  191. package/dist/commands/auto-reply.d.ts.map +1 -1
  192. package/dist/commands/auto-reply.js +5 -9
  193. package/dist/commands/auto-reply.js.map +1 -1
  194. package/dist/commands/extensions.d.ts +18 -6
  195. package/dist/commands/extensions.d.ts.map +1 -1
  196. package/dist/commands/extensions.js +64 -14
  197. package/dist/commands/extensions.js.map +1 -1
  198. package/dist/commands/login.d.ts +2 -1
  199. package/dist/commands/login.d.ts.map +1 -1
  200. package/dist/commands/login.js +40 -5
  201. package/dist/commands/login.js.map +1 -1
  202. package/dist/commands/manifest.d.ts +37 -0
  203. package/dist/commands/manifest.d.ts.map +1 -0
  204. package/dist/commands/manifest.js +151 -0
  205. package/dist/commands/manifest.js.map +1 -0
  206. package/dist/commands/model.d.ts +2 -3
  207. package/dist/commands/model.d.ts.map +1 -1
  208. package/dist/commands/model.js +63 -24
  209. package/dist/commands/model.js.map +1 -1
  210. package/dist/commands/new.d.ts.map +1 -1
  211. package/dist/commands/new.js +15 -3
  212. package/dist/commands/new.js.map +1 -1
  213. package/dist/commands/registry.d.ts +2 -2
  214. package/dist/commands/registry.d.ts.map +1 -1
  215. package/dist/commands/registry.js +1 -2
  216. package/dist/commands/registry.js.map +1 -1
  217. package/dist/commands/sandbox.d.ts.map +1 -1
  218. package/dist/commands/sandbox.js +83 -33
  219. package/dist/commands/sandbox.js.map +1 -1
  220. package/dist/commands/session-view.d.ts.map +1 -1
  221. package/dist/commands/session-view.js +6 -3
  222. package/dist/commands/session-view.js.map +1 -1
  223. package/dist/commands/types.d.ts +79 -7
  224. package/dist/commands/types.d.ts.map +1 -1
  225. package/dist/commands/types.js.map +1 -1
  226. package/dist/config.d.ts +23 -8
  227. package/dist/config.d.ts.map +1 -1
  228. package/dist/config.js +222 -135
  229. package/dist/config.js.map +1 -1
  230. package/dist/env-manifest.d.ts +30 -0
  231. package/dist/env-manifest.d.ts.map +1 -0
  232. package/dist/env-manifest.js +233 -0
  233. package/dist/env-manifest.js.map +1 -0
  234. package/dist/events.d.ts.map +1 -1
  235. package/dist/events.js +20 -81
  236. package/dist/events.js.map +1 -1
  237. package/dist/execution-resolver.d.ts +7 -8
  238. package/dist/execution-resolver.d.ts.map +1 -1
  239. package/dist/execution-resolver.js +106 -110
  240. package/dist/execution-resolver.js.map +1 -1
  241. package/dist/extension-schedules.d.ts +31 -0
  242. package/dist/extension-schedules.d.ts.map +1 -0
  243. package/dist/extension-schedules.js +229 -0
  244. package/dist/extension-schedules.js.map +1 -0
  245. package/dist/harness/auth.d.ts +2 -1
  246. package/dist/harness/auth.d.ts.map +1 -1
  247. package/dist/harness/auth.js +11 -5
  248. package/dist/harness/auth.js.map +1 -1
  249. package/dist/harness/event-format.d.ts +27 -0
  250. package/dist/harness/event-format.d.ts.map +1 -0
  251. package/dist/harness/event-format.js +160 -0
  252. package/dist/harness/event-format.js.map +1 -0
  253. package/dist/harness/extensions/loader.d.ts +27 -49
  254. package/dist/harness/extensions/loader.d.ts.map +1 -1
  255. package/dist/harness/extensions/loader.js +486 -130
  256. package/dist/harness/extensions/loader.js.map +1 -1
  257. package/dist/harness/extensions/registry.d.ts +107 -2
  258. package/dist/harness/extensions/registry.d.ts.map +1 -1
  259. package/dist/harness/extensions/registry.js +316 -0
  260. package/dist/harness/extensions/registry.js.map +1 -1
  261. package/dist/harness/extensions/types.d.ts +482 -42
  262. package/dist/harness/extensions/types.d.ts.map +1 -1
  263. package/dist/harness/extensions/types.js.map +1 -1
  264. package/dist/harness/index.d.ts +18 -8
  265. package/dist/harness/index.d.ts.map +1 -1
  266. package/dist/harness/index.js +7 -4
  267. package/dist/harness/index.js.map +1 -1
  268. package/dist/harness/models.d.ts +7 -4
  269. package/dist/harness/models.d.ts.map +1 -1
  270. package/dist/harness/models.js +46 -24
  271. package/dist/harness/models.js.map +1 -1
  272. package/dist/harness/runner.d.ts +50 -54
  273. package/dist/harness/runner.d.ts.map +1 -1
  274. package/dist/harness/runner.js +232 -46
  275. package/dist/harness/runner.js.map +1 -1
  276. package/dist/harness/session-store.d.ts +11 -5
  277. package/dist/harness/session-store.d.ts.map +1 -1
  278. package/dist/harness/session-store.js +141 -36
  279. package/dist/harness/session-store.js.map +1 -1
  280. package/dist/harness/settings.d.ts +2 -20
  281. package/dist/harness/settings.d.ts.map +1 -1
  282. package/dist/harness/settings.js +12 -1
  283. package/dist/harness/settings.js.map +1 -1
  284. package/dist/harness/skills.d.ts +10 -29
  285. package/dist/harness/skills.d.ts.map +1 -1
  286. package/dist/harness/skills.js +48 -6
  287. package/dist/harness/skills.js.map +1 -1
  288. package/dist/harness/subagent-profiles.d.ts +8 -0
  289. package/dist/harness/subagent-profiles.d.ts.map +1 -0
  290. package/dist/harness/subagent-profiles.js +299 -0
  291. package/dist/harness/subagent-profiles.js.map +1 -0
  292. package/dist/harness/subagent-runner.d.ts +45 -0
  293. package/dist/harness/subagent-runner.d.ts.map +1 -0
  294. package/dist/harness/subagent-runner.js +680 -0
  295. package/dist/harness/subagent-runner.js.map +1 -0
  296. package/dist/harness/types.d.ts +267 -1
  297. package/dist/harness/types.d.ts.map +1 -1
  298. package/dist/harness/types.js.map +1 -1
  299. package/dist/harness/usage.d.ts +6 -0
  300. package/dist/harness/usage.d.ts.map +1 -0
  301. package/dist/harness/usage.js +32 -0
  302. package/dist/harness/usage.js.map +1 -0
  303. package/dist/index.d.ts +36 -8
  304. package/dist/index.d.ts.map +1 -1
  305. package/dist/index.js +22 -6
  306. package/dist/index.js.map +1 -1
  307. package/dist/main.js +391 -127
  308. package/dist/main.js.map +1 -1
  309. package/dist/observability/instrument.js +7 -5
  310. package/dist/observability/instrument.js.map +1 -1
  311. package/dist/observability/sentry.d.ts +2 -0
  312. package/dist/observability/sentry.d.ts.map +1 -1
  313. package/dist/observability/sentry.js +10 -2
  314. package/dist/observability/sentry.js.map +1 -1
  315. package/dist/office/address.d.ts +31 -0
  316. package/dist/office/address.d.ts.map +1 -0
  317. package/dist/office/address.js +113 -0
  318. package/dist/office/address.js.map +1 -0
  319. package/dist/office/index.d.ts +11 -0
  320. package/dist/office/index.d.ts.map +1 -0
  321. package/dist/office/index.js +11 -0
  322. package/dist/office/index.js.map +1 -0
  323. package/dist/office/layout.d.ts +27 -0
  324. package/dist/office/layout.d.ts.map +1 -0
  325. package/dist/office/layout.js +97 -0
  326. package/dist/office/layout.js.map +1 -0
  327. package/dist/office/migration.d.ts +44 -0
  328. package/dist/office/migration.d.ts.map +1 -0
  329. package/dist/office/migration.js +255 -0
  330. package/dist/office/migration.js.map +1 -0
  331. package/dist/office/registry.d.ts +75 -0
  332. package/dist/office/registry.d.ts.map +1 -0
  333. package/dist/office/registry.js +646 -0
  334. package/dist/office/registry.js.map +1 -0
  335. package/dist/office/types.d.ts +71 -0
  336. package/dist/office/types.d.ts.map +1 -0
  337. package/dist/office/types.js +2 -0
  338. package/dist/office/types.js.map +1 -0
  339. package/dist/packages/admin.d.ts +17 -0
  340. package/dist/packages/admin.d.ts.map +1 -0
  341. package/dist/packages/admin.js +98 -0
  342. package/dist/packages/admin.js.map +1 -0
  343. package/dist/packages/index.d.ts +6 -0
  344. package/dist/packages/index.d.ts.map +1 -0
  345. package/dist/packages/index.js +6 -0
  346. package/dist/packages/index.js.map +1 -0
  347. package/dist/packages/inspect.d.ts +11 -0
  348. package/dist/packages/inspect.d.ts.map +1 -0
  349. package/dist/packages/inspect.js +98 -0
  350. package/dist/packages/inspect.js.map +1 -0
  351. package/dist/packages/materialize.d.ts +40 -0
  352. package/dist/packages/materialize.d.ts.map +1 -0
  353. package/dist/packages/materialize.js +297 -0
  354. package/dist/packages/materialize.js.map +1 -0
  355. package/dist/packages/resolve.d.ts +25 -0
  356. package/dist/packages/resolve.d.ts.map +1 -0
  357. package/dist/packages/resolve.js +208 -0
  358. package/dist/packages/resolve.js.map +1 -0
  359. package/dist/packages/source.d.ts +39 -0
  360. package/dist/packages/source.d.ts.map +1 -0
  361. package/dist/packages/source.js +189 -0
  362. package/dist/packages/source.js.map +1 -0
  363. package/dist/packages/types.d.ts +104 -0
  364. package/dist/packages/types.d.ts.map +1 -0
  365. package/dist/packages/types.js +2 -0
  366. package/dist/packages/types.js.map +1 -0
  367. package/dist/provisioner.d.ts +66 -0
  368. package/dist/provisioner.d.ts.map +1 -1
  369. package/dist/provisioner.js +335 -13
  370. package/dist/provisioner.js.map +1 -1
  371. package/dist/runtime/conversation-runtime.d.ts +1 -1
  372. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  373. package/dist/runtime/conversation-runtime.js +562 -118
  374. package/dist/runtime/conversation-runtime.js.map +1 -1
  375. package/dist/runtime/session-lifecycle.d.ts +38 -0
  376. package/dist/runtime/session-lifecycle.d.ts.map +1 -0
  377. package/dist/runtime/session-lifecycle.js +193 -0
  378. package/dist/runtime/session-lifecycle.js.map +1 -0
  379. package/dist/runtime/types.d.ts +66 -7
  380. package/dist/runtime/types.d.ts.map +1 -1
  381. package/dist/runtime/types.js.map +1 -1
  382. package/dist/sandbox/cloudflare.d.ts +3 -0
  383. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  384. package/dist/sandbox/cloudflare.js +13 -1
  385. package/dist/sandbox/cloudflare.js.map +1 -1
  386. package/dist/sandbox/container.d.ts +4 -0
  387. package/dist/sandbox/container.d.ts.map +1 -1
  388. package/dist/sandbox/container.js +13 -3
  389. package/dist/sandbox/container.js.map +1 -1
  390. package/dist/sandbox/firecracker.d.ts +3 -0
  391. package/dist/sandbox/firecracker.d.ts.map +1 -1
  392. package/dist/sandbox/firecracker.js +13 -2
  393. package/dist/sandbox/firecracker.js.map +1 -1
  394. package/dist/sandbox/gondolin.d.ts +57 -0
  395. package/dist/sandbox/gondolin.d.ts.map +1 -0
  396. package/dist/sandbox/gondolin.js +764 -0
  397. package/dist/sandbox/gondolin.js.map +1 -0
  398. package/dist/sandbox/host.d.ts +3 -0
  399. package/dist/sandbox/host.d.ts.map +1 -1
  400. package/dist/sandbox/host.js +18 -3
  401. package/dist/sandbox/host.js.map +1 -1
  402. package/dist/sandbox/identity.d.ts +44 -0
  403. package/dist/sandbox/identity.d.ts.map +1 -0
  404. package/dist/sandbox/identity.js +68 -0
  405. package/dist/sandbox/identity.js.map +1 -0
  406. package/dist/sandbox/image.d.ts.map +1 -1
  407. package/dist/sandbox/image.js +2 -0
  408. package/dist/sandbox/image.js.map +1 -1
  409. package/dist/sandbox/index.d.ts +17 -4
  410. package/dist/sandbox/index.d.ts.map +1 -1
  411. package/dist/sandbox/index.js +37 -3
  412. package/dist/sandbox/index.js.map +1 -1
  413. package/dist/sandbox/types.d.ts +65 -1
  414. package/dist/sandbox/types.d.ts.map +1 -1
  415. package/dist/sandbox/types.js.map +1 -1
  416. package/dist/sandbox/utils.d.ts +14 -0
  417. package/dist/sandbox/utils.d.ts.map +1 -1
  418. package/dist/sandbox/utils.js +68 -1
  419. package/dist/sandbox/utils.js.map +1 -1
  420. package/dist/sessions/chat-history-sync.d.ts +25 -0
  421. package/dist/sessions/chat-history-sync.d.ts.map +1 -0
  422. package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +135 -131
  423. package/dist/sessions/chat-history-sync.js.map +1 -0
  424. package/dist/sessions/conversation-log.d.ts +9 -0
  425. package/dist/sessions/conversation-log.d.ts.map +1 -0
  426. package/dist/sessions/conversation-log.js +50 -0
  427. package/dist/sessions/conversation-log.js.map +1 -0
  428. package/dist/sessions/history-line.d.ts +29 -0
  429. package/dist/sessions/history-line.d.ts.map +1 -0
  430. package/dist/sessions/history-line.js +45 -0
  431. package/dist/sessions/history-line.js.map +1 -0
  432. package/dist/sessions/rotation.d.ts +2 -0
  433. package/dist/sessions/rotation.d.ts.map +1 -0
  434. package/dist/sessions/rotation.js +39 -0
  435. package/dist/sessions/rotation.js.map +1 -0
  436. package/dist/sessions/session-key.d.ts +33 -0
  437. package/dist/sessions/session-key.d.ts.map +1 -0
  438. package/dist/sessions/session-key.js +115 -0
  439. package/dist/sessions/session-key.js.map +1 -0
  440. package/dist/sessions/store.d.ts +15 -7
  441. package/dist/sessions/store.d.ts.map +1 -1
  442. package/dist/sessions/store.js +127 -36
  443. package/dist/sessions/store.js.map +1 -1
  444. package/dist/sessions/types.d.ts +19 -5
  445. package/dist/sessions/types.d.ts.map +1 -1
  446. package/dist/sessions/types.js.map +1 -1
  447. package/dist/settings-mutation.d.ts +40 -0
  448. package/dist/settings-mutation.d.ts.map +1 -0
  449. package/dist/settings-mutation.js +61 -0
  450. package/dist/settings-mutation.js.map +1 -0
  451. package/dist/subagent-progress.d.ts +39 -0
  452. package/dist/subagent-progress.d.ts.map +1 -0
  453. package/dist/subagent-progress.js +225 -0
  454. package/dist/subagent-progress.js.map +1 -0
  455. package/dist/tools/bash.d.ts +3 -1
  456. package/dist/tools/bash.d.ts.map +1 -1
  457. package/dist/tools/bash.js +26 -18
  458. package/dist/tools/bash.js.map +1 -1
  459. package/dist/tools/edit.d.ts.map +1 -1
  460. package/dist/tools/edit.js +14 -13
  461. package/dist/tools/edit.js.map +1 -1
  462. package/dist/tools/event.d.ts +1 -1
  463. package/dist/tools/event.d.ts.map +1 -1
  464. package/dist/tools/event.js +58 -45
  465. package/dist/tools/event.js.map +1 -1
  466. package/dist/tools/generate-image.d.ts +23 -0
  467. package/dist/tools/generate-image.d.ts.map +1 -0
  468. package/dist/tools/generate-image.js +65 -0
  469. package/dist/tools/generate-image.js.map +1 -0
  470. package/dist/tools/host-fn-tool.d.ts +21 -0
  471. package/dist/tools/host-fn-tool.d.ts.map +1 -0
  472. package/dist/tools/host-fn-tool.js +32 -0
  473. package/dist/tools/host-fn-tool.js.map +1 -0
  474. package/dist/tools/index.d.ts +19 -2
  475. package/dist/tools/index.d.ts.map +1 -1
  476. package/dist/tools/index.js +25 -2
  477. package/dist/tools/index.js.map +1 -1
  478. package/dist/tools/react.d.ts +15 -0
  479. package/dist/tools/react.d.ts.map +1 -0
  480. package/dist/tools/react.js +29 -0
  481. package/dist/tools/react.js.map +1 -0
  482. package/dist/tools/read.d.ts +1 -0
  483. package/dist/tools/read.d.ts.map +1 -1
  484. package/dist/tools/read.js +39 -33
  485. package/dist/tools/read.js.map +1 -1
  486. package/dist/tools/sandbox.d.ts +2 -2
  487. package/dist/tools/sandbox.d.ts.map +1 -1
  488. package/dist/tools/sandbox.js +14 -9
  489. package/dist/tools/sandbox.js.map +1 -1
  490. package/dist/tools/subagent-slots.d.ts +28 -0
  491. package/dist/tools/subagent-slots.d.ts.map +1 -0
  492. package/dist/tools/subagent-slots.js +72 -0
  493. package/dist/tools/subagent-slots.js.map +1 -0
  494. package/dist/tools/subagent.d.ts +61 -0
  495. package/dist/tools/subagent.d.ts.map +1 -0
  496. package/dist/tools/subagent.js +434 -0
  497. package/dist/tools/subagent.js.map +1 -0
  498. package/dist/tools/types.d.ts +40 -32
  499. package/dist/tools/types.d.ts.map +1 -1
  500. package/dist/tools/types.js.map +1 -1
  501. package/dist/tools/write.d.ts.map +1 -1
  502. package/dist/tools/write.js +2 -8
  503. package/dist/tools/write.js.map +1 -1
  504. package/dist/trigger.d.ts +2 -1
  505. package/dist/trigger.d.ts.map +1 -1
  506. package/dist/trigger.js +6 -9
  507. package/dist/trigger.js.map +1 -1
  508. package/dist/types.d.ts +411 -59
  509. package/dist/types.d.ts.map +1 -1
  510. package/dist/types.js +2 -2
  511. package/dist/types.js.map +1 -1
  512. package/dist/utils/file-guards.d.ts +9 -0
  513. package/dist/utils/file-guards.d.ts.map +1 -1
  514. package/dist/utils/file-guards.js +44 -1
  515. package/dist/utils/file-guards.js.map +1 -1
  516. package/dist/vault/disabled.d.ts +9 -0
  517. package/dist/vault/disabled.d.ts.map +1 -0
  518. package/dist/vault/disabled.js +25 -0
  519. package/dist/vault/disabled.js.map +1 -0
  520. package/dist/vault/index.d.ts +45 -2
  521. package/dist/vault/index.d.ts.map +1 -1
  522. package/dist/vault/index.js +127 -36
  523. package/dist/vault/index.js.map +1 -1
  524. package/dist/vault/types.d.ts +6 -2
  525. package/dist/vault/types.d.ts.map +1 -1
  526. package/dist/vault/types.js.map +1 -1
  527. package/dist/web/admin/portal.d.ts +31 -3
  528. package/dist/web/admin/portal.d.ts.map +1 -1
  529. package/dist/web/admin/portal.js +850 -345
  530. package/dist/web/admin/portal.js.map +1 -1
  531. package/dist/web/admin/provider-models.js +1 -1
  532. package/dist/web/admin/provider-models.js.map +1 -1
  533. package/dist/web/admin/types.d.ts +23 -3
  534. package/dist/web/admin/types.d.ts.map +1 -1
  535. package/dist/web/admin/types.js.map +1 -1
  536. package/dist/web/login/oauth.d.ts +2 -3
  537. package/dist/web/login/oauth.d.ts.map +1 -1
  538. package/dist/web/login/oauth.js +1 -32
  539. package/dist/web/login/oauth.js.map +1 -1
  540. package/dist/web/login/portal.d.ts +1 -1
  541. package/dist/web/login/portal.d.ts.map +1 -1
  542. package/dist/web/login/portal.js +13 -36
  543. package/dist/web/login/portal.js.map +1 -1
  544. package/dist/web/login/types.d.ts +0 -11
  545. package/dist/web/login/types.d.ts.map +1 -1
  546. package/dist/web/login/types.js.map +1 -1
  547. package/dist/web/portal-shell.d.ts +21 -0
  548. package/dist/web/portal-shell.d.ts.map +1 -0
  549. package/dist/{portal-shell.js → web/portal-shell.js} +67 -2
  550. package/dist/web/portal-shell.js.map +1 -0
  551. package/dist/web/server.d.ts +3 -2
  552. package/dist/web/server.d.ts.map +1 -1
  553. package/dist/web/server.js +11 -4
  554. package/dist/web/server.js.map +1 -1
  555. package/dist/web/session-view/portal.d.ts +1 -1
  556. package/dist/web/session-view/portal.d.ts.map +1 -1
  557. package/dist/web/session-view/portal.js +37 -28
  558. package/dist/web/session-view/portal.js.map +1 -1
  559. package/dist/web/session-view/service.d.ts +1 -1
  560. package/dist/web/session-view/service.d.ts.map +1 -1
  561. package/dist/web/session-view/service.js +61 -18
  562. package/dist/web/session-view/service.js.map +1 -1
  563. package/dist/web/session-view/types.d.ts +0 -3
  564. package/dist/web/session-view/types.d.ts.map +1 -1
  565. package/dist/web/session-view/types.js.map +1 -1
  566. package/dist/web/token-store.js +1 -1
  567. package/dist/web/token-store.js.map +1 -1
  568. package/dist/workspace-projection/index.d.ts +10 -0
  569. package/dist/workspace-projection/index.d.ts.map +1 -0
  570. package/dist/workspace-projection/index.js +120 -0
  571. package/dist/workspace-projection/index.js.map +1 -0
  572. package/dist/workspace-projection/types.d.ts +16 -0
  573. package/dist/workspace-projection/types.d.ts.map +1 -0
  574. package/dist/workspace-projection/types.js +2 -0
  575. package/dist/workspace-projection/types.js.map +1 -0
  576. package/package.json +68 -37
  577. package/dist/adapters/streaming.d.ts +0 -18
  578. package/dist/adapters/streaming.d.ts.map +0 -1
  579. package/dist/adapters/streaming.js +0 -44
  580. package/dist/adapters/streaming.js.map +0 -1
  581. package/dist/adapters/telegram/html.d.ts +0 -3
  582. package/dist/adapters/telegram/html.d.ts.map +0 -1
  583. package/dist/adapters/telegram/html.js +0 -98
  584. package/dist/adapters/telegram/html.js.map +0 -1
  585. package/dist/commands/parse.d.ts +0 -7
  586. package/dist/commands/parse.d.ts.map +0 -1
  587. package/dist/commands/parse.js +0 -14
  588. package/dist/commands/parse.js.map +0 -1
  589. package/dist/download.d.ts.map +0 -1
  590. package/dist/download.js.map +0 -1
  591. package/dist/model-registry.d.ts +0 -4
  592. package/dist/model-registry.d.ts.map +0 -1
  593. package/dist/model-registry.js +0 -7
  594. package/dist/model-registry.js.map +0 -1
  595. package/dist/portal-shell.d.ts +0 -4
  596. package/dist/portal-shell.d.ts.map +0 -1
  597. package/dist/portal-shell.js.map +0 -1
  598. package/dist/runtime/agent-run-controller.d.ts +0 -31
  599. package/dist/runtime/agent-run-controller.d.ts.map +0 -1
  600. package/dist/runtime/agent-run-controller.js +0 -195
  601. package/dist/runtime/agent-run-controller.js.map +0 -1
  602. package/dist/sandbox/path-context.d.ts +0 -3
  603. package/dist/sandbox/path-context.d.ts.map +0 -1
  604. package/dist/sandbox/path-context.js +0 -20
  605. package/dist/sandbox/path-context.js.map +0 -1
  606. package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
  607. package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
  608. package/dist/sessions/agent-memory-file-manager.js.map +0 -1
  609. package/dist/sessions/metadata.d.ts +0 -3
  610. package/dist/sessions/metadata.d.ts.map +0 -1
  611. package/dist/sessions/metadata.js +0 -11
  612. package/dist/sessions/metadata.js.map +0 -1
  613. package/dist/sessions/policy.d.ts +0 -6
  614. package/dist/sessions/policy.d.ts.map +0 -1
  615. package/dist/sessions/policy.js +0 -23
  616. package/dist/sessions/policy.js.map +0 -1
  617. package/dist/store.d.ts +0 -46
  618. package/dist/store.d.ts.map +0 -1
  619. package/dist/store.js +0 -177
  620. package/dist/store.js.map +0 -1
  621. package/dist/tools/truncate.d.ts +0 -33
  622. package/dist/tools/truncate.d.ts.map +0 -1
  623. package/dist/tools/truncate.js +0 -184
  624. package/dist/tools/truncate.js.map +0 -1
  625. package/dist/utils/date.d.ts +0 -10
  626. package/dist/utils/date.d.ts.map +0 -1
  627. package/dist/utils/date.js +0 -23
  628. package/dist/utils/date.js.map +0 -1
  629. package/dist/utils/env.d.ts +0 -3
  630. package/dist/utils/env.d.ts.map +0 -1
  631. package/dist/utils/env.js +0 -12
  632. package/dist/utils/env.js.map +0 -1
  633. package/dist/utils/fs-atomic.d.ts +0 -10
  634. package/dist/utils/fs-atomic.d.ts.map +0 -1
  635. package/dist/utils/fs-atomic.js +0 -45
  636. package/dist/utils/fs-atomic.js.map +0 -1
  637. package/dist/utils/html.d.ts +0 -2
  638. package/dist/utils/html.d.ts.map +0 -1
  639. package/dist/utils/html.js +0 -4
  640. package/dist/utils/html.js.map +0 -1
  641. package/dist/utils/http-body.d.ts +0 -10
  642. package/dist/utils/http-body.d.ts.map +0 -1
  643. package/dist/utils/http-body.js +0 -34
  644. package/dist/utils/http-body.js.map +0 -1
  645. package/dist/vault/routing.d.ts +0 -3
  646. package/dist/vault/routing.d.ts.map +0 -1
  647. package/dist/vault/routing.js +0 -13
  648. package/dist/vault/routing.js.map +0 -1
  649. package/dist/web/agent-events/portal.d.ts +0 -3
  650. package/dist/web/agent-events/portal.d.ts.map +0 -1
  651. package/dist/web/agent-events/portal.js +0 -18
  652. package/dist/web/agent-events/portal.js.map +0 -1
  653. package/dist/web/request.d.ts +0 -3
  654. package/dist/web/request.d.ts.map +0 -1
  655. package/dist/web/request.js +0 -13
  656. package/dist/web/request.js.map +0 -1
  657. package/dist/web/session-view/command.d.ts +0 -4
  658. package/dist/web/session-view/command.d.ts.map +0 -1
  659. package/dist/web/session-view/command.js +0 -7
  660. package/dist/web/session-view/command.js.map +0 -1
  661. /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
package/dist/agent.js CHANGED
@@ -1,24 +1,104 @@
1
- import { DEFAULT_EVENT_BUDGET, defaultExtensionDirs, formatSkillsForPrompt, loadExtensions, loadSkillsFromDir, MikanAgentSession, MikanModels, } from "./harness/index.js";
2
- import { createHash } from "crypto";
3
- import { existsSync, readFileSync } from "fs";
4
- import { mkdir, readFile, writeFile } from "fs/promises";
5
- import { join, posix } from "path";
1
+ /**
2
+ * Agent runner: prompt construction, path translation, run lifecycle, and createRunner.
3
+ * Single module — do not split helpers that only this file uses.
4
+ */
5
+ import {} from "@earendil-works/pi-ai";
6
+ import { DEFAULT_EVENT_BUDGET, formatSkillsForPrompt, loadExtensions, loadSkillsFromDir, MikanAgentSession, MikanModels, parseCommandInput, } from "./harness/index.js";
7
+ import { runSubagent } from "./harness/subagent-runner.js";
8
+ import { loadSubagentProfiles } from "./harness/subagent-profiles.js";
9
+ import { mergeSubagentProgress, parseSubagentProgressSnapshot, renderSubagentDashboard, settleSubagentProgress, } from "./subagent-progress.js";
10
+ import { createHash } from "node:crypto";
11
+ import { existsSync, lstatSync } from "node:fs";
12
+ import { chmod, mkdtemp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
13
+ import { tmpdir } from "node:os";
14
+ import { basename, isAbsolute, join, posix, relative, resolve, sep } from "node:path";
6
15
  import { resolveConversationSettings } from "./config.js";
7
- import { readEnv } from "./utils/env.js";
16
+ import { resolveWorkspaceProjection } from "./workspace-projection/index.js";
17
+ import { packageSkillRuntimeDir, resolveConversationPackages } from "./packages/index.js";
8
18
  import { ActorExecutionResolver } from "./execution-resolver.js";
9
19
  import * as log from "./log.js";
10
- import { reportUserFacingError } from "./observability/sentry.js";
11
- import { createExecutor, } from "./sandbox/index.js";
12
- import { createMountedRuntimePathContext } from "./sandbox/path-context.js";
13
- import { addLifecycleBreadcrumb, metricAttributes, updateActiveSpanAttribution, } from "./observability/sentry.js";
14
- import { AgentMemoryFileManager } from "./sessions/agent-memory-file-manager.js";
20
+ import { assertSandboxSupportsWorkspacePolicy, createExecutor, getUnresolvedSandboxPathContext, } from "./sandbox/index.js";
21
+ import { addLifecycleBreadcrumb, metricAttributes, reportUserFacingError, updateActiveSpanAttribution, } from "./observability/sentry.js";
22
+ import { ChatHistorySync } from "./sessions/chat-history-sync.js";
23
+ import { formatHistoryLine } from "./sessions/history-line.js";
24
+ import { conversationIdOf, isThreadSessionKey } from "./sessions/session-key.js";
15
25
  import { extractSessionUuid, openManagedSession, } from "./sessions/store.js";
16
26
  import { HostEventStore } from "./tools/event.js";
17
- import { createMikanTools } from "./tools/index.js";
27
+ import { createMikanTools, createSubagentTool, DEFAULT_GLOBAL_SUBAGENT_SLOTS, SubagentSlotPool, } from "./tools/index.js";
28
+ // One process-wide fan-out account: per-conversation queues serialize runs,
29
+ // but each run can fan out up to the per-run cap — without this shared
30
+ // ceiling, N busy conversations hold N × cap live subagent sessions.
31
+ const globalSubagentSlots = new SubagentSlotPool(DEFAULT_GLOBAL_SUBAGENT_SLOTS);
18
32
  import * as Sentry from "@sentry/node";
19
- import { formatLocalTimestamp } from "./utils/date.js";
20
- import { resolveConfiguredModel } from "./model-registry.js";
21
33
  import { emitAgentEvent } from "./agent-events.js";
34
+ export function translateRuntimePathToHost(runtimePath, pathContext) {
35
+ return pathContext.runtimeToHostPath?.(runtimePath) ?? runtimePath;
36
+ }
37
+ function isWithinPathRoot(path, root) {
38
+ const pathRelative = relative(root, path);
39
+ return (pathRelative === "" ||
40
+ (pathRelative !== ".." && !pathRelative.startsWith(`..${sep}`) && !isAbsolute(pathRelative)));
41
+ }
42
+ function hasParentTraversal(path) {
43
+ return path.split(/[\\/]/).some((segment) => segment === "..");
44
+ }
45
+ export function translateAttachPathToHost(filePath, pathContext) {
46
+ if (!pathContext.runtimeToHostPath) {
47
+ throw new Error("Cannot attach files: this sandbox has no host-backed runtime path mapping; attachments are unavailable for remote sandboxes such as Cloudflare or Firecracker");
48
+ }
49
+ if (hasParentTraversal(filePath)) {
50
+ throw new Error("Cannot attach files: parent-directory traversal is not allowed");
51
+ }
52
+ const runtimeRoot = resolve(pathContext.runtimeWorkspaceRoot);
53
+ const runtimePath = posix.isAbsolute(filePath)
54
+ ? filePath
55
+ : posix.join(pathContext.runtimeWorkspaceRoot, filePath);
56
+ const normalizedRuntimePath = resolve(runtimePath);
57
+ if (!isWithinPathRoot(normalizedRuntimePath, runtimeRoot)) {
58
+ throw new Error("Cannot attach files: path must be within the runtime workspace");
59
+ }
60
+ const hostRoot = resolve(pathContext.hostWorkspaceRoot);
61
+ const translatedPath = pathContext.runtimeToHostPath(runtimePath);
62
+ const hostPath = resolve(translatedPath);
63
+ if (!isWithinPathRoot(hostPath, hostRoot)) {
64
+ throw new Error("Cannot attach files: path must be within the host workspace");
65
+ }
66
+ return hostPath;
67
+ }
68
+ /**
69
+ * Normalize an attachment path using runtime lexical semantics only. The
70
+ * executor remains the authority for reading the resulting runtime path.
71
+ */
72
+ function normalizeAttachRuntimePath(filePath, runtimeWorkspaceRoot) {
73
+ if (hasParentTraversal(filePath)) {
74
+ throw new Error("Cannot attach files: parent-directory traversal is not allowed");
75
+ }
76
+ const runtimeRoot = posix.resolve(runtimeWorkspaceRoot);
77
+ const runtimePath = posix.resolve(runtimeRoot, filePath);
78
+ const runtimeRelativePath = posix.relative(runtimeRoot, runtimePath);
79
+ if (runtimeRelativePath === ".." ||
80
+ runtimeRelativePath.startsWith("../") ||
81
+ posix.isAbsolute(runtimeRelativePath)) {
82
+ throw new Error("Cannot attach files: path must be within the runtime workspace");
83
+ }
84
+ return runtimePath;
85
+ }
86
+ async function withStagedRuntimeFile(executor, runtimePath, upload) {
87
+ const content = Buffer.from(await executor.readFileBase64(runtimePath), "base64");
88
+ let stagingDir;
89
+ try {
90
+ stagingDir = await mkdtemp(join(tmpdir(), "mikan-upload-"));
91
+ await chmod(stagingDir, 0o700);
92
+ const stagedPath = join(stagingDir, basename(runtimePath));
93
+ await writeFile(stagedPath, content, { mode: 0o600, flag: "wx" });
94
+ await chmod(stagedPath, 0o600);
95
+ await upload(stagedPath);
96
+ }
97
+ finally {
98
+ if (stagingDir)
99
+ await rm(stagingDir, { recursive: true, force: true });
100
+ }
101
+ }
22
102
  const IMAGE_MIME_TYPES = {
23
103
  jpg: "image/jpeg",
24
104
  jpeg: "image/jpeg",
@@ -29,18 +109,51 @@ const IMAGE_MIME_TYPES = {
29
109
  function getImageMimeType(filename) {
30
110
  return IMAGE_MIME_TYPES[filename.toLowerCase().split(".").pop() || ""];
31
111
  }
32
- function buildThreadSessionName(message) {
33
- const text = message?.text?.trim();
34
- if (!text)
35
- return undefined;
36
- const userLabel = message?.userName || message?.user || "unknown";
37
- return `[${userLabel}]: ${text}`;
112
+ function formatTimestampedUserMessage(message) {
113
+ return formatHistoryLine({
114
+ date: new Date(),
115
+ userName: message.userName,
116
+ threadTs: message.threadTs,
117
+ text: message.text,
118
+ });
119
+ }
120
+ async function collectMessageAttachments(message, workspacePath, pathContext, readAttachment) {
121
+ const imageAttachments = [];
122
+ const nonImagePaths = [];
123
+ for (const attachment of message.attachments || []) {
124
+ const runtimePath = `${workspacePath}/${attachment.localPath}`;
125
+ const hostPath = pathContext?.runtimeToHostPath?.(runtimePath) ?? runtimePath;
126
+ const mimeType = getImageMimeType(attachment.localPath);
127
+ if (mimeType && existsSync(hostPath) && readAttachment) {
128
+ try {
129
+ imageAttachments.push({
130
+ type: "image",
131
+ mimeType,
132
+ data: await readAttachment(runtimePath),
133
+ });
134
+ }
135
+ catch {
136
+ nonImagePaths.push(runtimePath);
137
+ }
138
+ }
139
+ else {
140
+ nonImagePaths.push(runtimePath);
141
+ }
142
+ }
143
+ return { imageAttachments, nonImagePaths };
144
+ }
145
+ export async function buildPromptPayload(message, workspacePath, pathContext, readAttachment) {
146
+ let userMessage = formatTimestampedUserMessage(message);
147
+ const { imageAttachments, nonImagePaths } = await collectMessageAttachments(message, workspacePath, pathContext, readAttachment);
148
+ if (nonImagePaths.length > 0) {
149
+ userMessage += `\n\n<slack_attachments>\n${nonImagePaths.join("\n")}\n</slack_attachments>`;
150
+ }
151
+ return { userMessage, imageAttachments };
38
152
  }
39
- async function getMemory(conversationDir) {
153
+ async function getMemory(projection) {
40
154
  const parts = [];
41
- // Read workspace-level memory (shared across all conversations)
42
- const workspaceMemoryPath = join(conversationDir, "..", "MEMORY.md");
43
- if (existsSync(workspaceMemoryPath)) {
155
+ const workspaceMemoryPath = projection.promptSources.globalMemoryPath;
156
+ if (workspaceMemoryPath && isRegularFile(workspaceMemoryPath)) {
44
157
  try {
45
158
  const content = (await readFile(workspaceMemoryPath, "utf-8")).trim();
46
159
  if (content) {
@@ -51,9 +164,8 @@ async function getMemory(conversationDir) {
51
164
  log.logWarning("Failed to read workspace memory", `${workspaceMemoryPath}: ${error}`);
52
165
  }
53
166
  }
54
- // Read conversation-specific memory
55
- const conversationMemoryPath = join(conversationDir, "MEMORY.md");
56
- if (existsSync(conversationMemoryPath)) {
167
+ const conversationMemoryPath = projection.promptSources.conversationMemoryPath;
168
+ if (isRegularFile(conversationMemoryPath)) {
57
169
  try {
58
170
  const content = (await readFile(conversationMemoryPath, "utf-8")).trim();
59
171
  if (content) {
@@ -69,12 +181,20 @@ async function getMemory(conversationDir) {
69
181
  }
70
182
  return parts.join("\n\n");
71
183
  }
72
- function loadMikanSkills(conversationDir, workspacePath) {
184
+ function isRegularFile(path) {
185
+ try {
186
+ const stats = lstatSync(path);
187
+ return stats.isFile() && !stats.isSymbolicLink();
188
+ }
189
+ catch {
190
+ return false;
191
+ }
192
+ }
193
+ function loadMikanSkills(office, workspacePath, projection) {
73
194
  const skillMap = new Map();
74
- // conversationDir is the host path (e.g., /Users/.../data/C0A34FL8PMH)
75
- // hostWorkspacePath is the parent directory on host
76
- // workspacePath is the container path (e.g., /workspace)
77
- const hostWorkspacePath = join(conversationDir, "..");
195
+ // workspacePath is the runtime-side root (e.g. /workspace); host paths under
196
+ // the workspace root translate onto it for prompt references.
197
+ const hostWorkspacePath = office.workspace.root;
78
198
  // Helper to translate host paths to container paths
79
199
  const translatePath = (hostPath) => {
80
200
  if (hostPath.startsWith(hostWorkspacePath)) {
@@ -82,26 +202,66 @@ function loadMikanSkills(conversationDir, workspacePath) {
82
202
  }
83
203
  return hostPath;
84
204
  };
85
- // Load workspace-level skills (global)
86
- const workspaceSkillsDir = join(hostWorkspacePath, "skills");
87
- for (const skill of loadSkillsFromDir({ dir: workspaceSkillsDir, source: "workspace" }).skills) {
88
- // Translate paths to container paths for system prompt
89
- skill.filePath = translatePath(skill.filePath);
90
- skill.baseDir = translatePath(skill.baseDir);
91
- skillMap.set(skill.name, skill);
205
+ // Package skills first: they are the lowest precedence, so a workspace or
206
+ // conversation skill of the same name below overwrites them. A package can
207
+ // offer a capability without taking the name away from whoever wants to
208
+ // replace it locally.
209
+ //
210
+ // Unlike extension-shipped skills, these are NOT inlined into the prompt:
211
+ // conversationPackageSkillMounts exposes them read-only at a runtime path,
212
+ // so the agent can read the files — including any scripts or templates the
213
+ // skill ships alongside its SKILL.md, which inlining could never carry.
214
+ const mounted = workspacePath !== hostWorkspacePath;
215
+ for (const { slug, dir } of resolveConversationPackages({ office }).skillDirs) {
216
+ const runtimeDir = packageSkillRuntimeDir(slug);
217
+ for (const skill of loadSkillsFromDir({ dir, source: `package:${slug}` }).skills) {
218
+ // In host mode there is no mount, so the host path is already the path
219
+ // the agent will use.
220
+ if (mounted) {
221
+ skill.filePath = runtimeDir + skill.filePath.slice(dir.length);
222
+ skill.baseDir = runtimeDir + skill.baseDir.slice(dir.length);
223
+ }
224
+ skillMap.set(skill.name, skill);
225
+ }
92
226
  }
93
- // Load conversation-specific skills (override workspace skills on collision)
94
- const conversationSkillsDir = join(conversationDir, "skills");
95
- for (const skill of loadSkillsFromDir({ dir: conversationSkillsDir, source: "channel" }).skills) {
227
+ // Load workspace-level skills only when the office projection authorizes it.
228
+ const workspaceSkillsDir = projection.promptSources.globalSkillsDir;
229
+ if (workspaceSkillsDir) {
230
+ for (const skill of loadSkillsFromDir({ dir: workspaceSkillsDir, source: "workspace" })
231
+ .skills) {
232
+ skill.filePath = translatePath(skill.filePath);
233
+ skill.baseDir = translatePath(skill.baseDir);
234
+ skillMap.set(skill.name, skill);
235
+ }
236
+ }
237
+ // Load conversation-specific skills (override workspace skills on collision).
238
+ // Host prompt construction must never follow an agent-created symlink out of
239
+ // the conversation office, so this load rejects symlinks per entry — on
240
+ // exactly the paths it reads. Vendored node_modules and dot directories are
241
+ // never read and never disqualify anything.
242
+ const conversationSkillsDir = projection.promptSources.conversationSkillsDir;
243
+ const conversationSkills = loadSkillsFromDir({
244
+ dir: conversationSkillsDir,
245
+ source: "channel",
246
+ rejectSymlinks: true,
247
+ });
248
+ const skippedSkillLinks = [];
249
+ for (const diagnostic of conversationSkills.diagnostics) {
250
+ if (diagnostic.code !== "symlink")
251
+ continue;
252
+ log.logWarning("Skipping conversation skill entry (symlink)", diagnostic.path);
253
+ skippedSkillLinks.push(translatePath(diagnostic.path));
254
+ }
255
+ for (const skill of conversationSkills.skills) {
96
256
  skill.filePath = translatePath(skill.filePath);
97
257
  skill.baseDir = translatePath(skill.baseDir);
98
258
  skillMap.set(skill.name, skill);
99
259
  }
100
- return Array.from(skillMap.values());
260
+ return { skills: Array.from(skillMap.values()), skippedSkillLinks };
101
261
  }
102
- function buildRuntimePaths(runtimeWorkspaceRoot, conversationId) {
262
+ function buildRuntimePaths(runtimeWorkspaceRoot, office) {
103
263
  const workspaceRoot = runtimeWorkspaceRoot.replace(/\/+$/, "") || "/";
104
- const conversationPath = posix.join(workspaceRoot, conversationId);
264
+ const conversationPath = posix.join(workspaceRoot, office.key);
105
265
  return {
106
266
  workspaceRoot,
107
267
  conversationPath,
@@ -111,7 +271,7 @@ function buildRuntimePaths(runtimeWorkspaceRoot, conversationId) {
111
271
  function buildEnvDescription(sandboxType, workspaceRoot) {
112
272
  switch (sandboxType) {
113
273
  case "image":
114
- return `You are running inside a managed per-user container.
274
+ return `You are running inside a managed per-conversation container.
115
275
  - Runtime workspace root: ${workspaceRoot}
116
276
  - Bash commands start in: ${workspaceRoot}
117
277
  - Install tools with the image's package manager
@@ -152,8 +312,8 @@ export function resolveTriggerAttribution(message) {
152
312
  return `@${message.userName}`;
153
313
  return undefined;
154
314
  }
155
- function buildSystemPrompt(workspacePath, conversationId, conversationKind, currentUserId, memory, sandboxConfig, platform, skills) {
156
- const { workspaceRoot, conversationPath, scratchPath } = buildRuntimePaths(workspacePath, conversationId);
315
+ function buildSystemPrompt(workspacePath, office, conversationKind, currentUserId, memory, sandboxConfig, platform, skills, projection, skippedSkillLinks = []) {
316
+ const { workspaceRoot, conversationPath, scratchPath } = buildRuntimePaths(workspacePath, office);
157
317
  const sandboxType = sandboxConfig.type;
158
318
  const isContainerLike = sandboxType === "container" || sandboxType === "image";
159
319
  const isFirecracker = sandboxType === "firecracker";
@@ -171,11 +331,26 @@ function buildSystemPrompt(workspacePath, conversationId, conversationKind, curr
171
331
  // system prompt stays byte-stable across a conversation's turns and keeps the
172
332
  // provider prompt cache warm (a changing system prefix invalidates the cache
173
333
  // for the whole request, including the far larger conversation history).
334
+ const workspaceLayout = projection.layout === "full"
335
+ ? `${workspaceRoot}/ contains the complete trusted workspace.`
336
+ : projection.layout === "shared-support"
337
+ ? `${workspaceRoot}/ contains shared MEMORY.md, skills/, events/, and this conversation's directory.`
338
+ : `${conversationPath}/ is the only conversation workspace mounted; global memory, skills, and events are not available.`;
339
+ const skillStorageGuidance = projection.doorPolicy === "trusted"
340
+ ? `Store shared skills in \`${workspaceRoot}/skills/<name>/\` or conversation-specific skills in \`${conversationPath}/skills/<name>/\`.`
341
+ : `Store skills in \`${conversationPath}/skills/<name>/\`; this office cannot access workspace-global skills.`;
342
+ const memoryGuidance = projection.doorPolicy === "trusted"
343
+ ? `Write important shared knowledge to \`${workspaceRoot}/MEMORY.md\` and conversation-specific knowledge to \`${conversationPath}/MEMORY.md\`.`
344
+ : `Write durable knowledge only to \`${conversationPath}/MEMORY.md\`; this office cannot access workspace-global memory.`;
345
+ const systemLogPath = projection.layout === "conversation"
346
+ ? `${conversationPath}/SYSTEM.md`
347
+ : `${workspaceRoot}/SYSTEM.md`;
174
348
  const slackBlockKitInstructions = platform.name === "slack"
175
349
  ? `
176
350
  ## Slack Rendering
177
- - The Slack adapter renders responses with Block Kit automatically. Answer normally using Slack mrkdwn.
178
- - Do not emit markdown pipe tables; Slack does not render them as tables. Prefer short sections or bullet lists.
351
+ - The Slack adapter renders responses natively from standard Markdown. Answer in normal Markdown/GFM.
352
+ - Markdown pipe tables are rendered as native Slack tables.
353
+ - For interactive elements (buttons, select menus), use the slack_blockkit tool; user interactions arrive as "[Slack action] <action_id>: <value>" messages.
179
354
  `
180
355
  : "";
181
356
  return `You are mikan, a ${platform.name} bot assistant. Be concise. No emojis.
@@ -196,7 +371,7 @@ Channels: ${channelMappings}
196
371
 
197
372
  Users: ${userMappings}
198
373
 
199
- When mentioning users, use <@username> format (e.g., <@mario>).
374
+ When mentioning users, write <@userName> using the exact userName from the Users table above (e.g., <@mario>). Never invent handles from other platforms (GitHub, email); the platform adapter converts <@userName> to the platform's native mention form.
200
375
 
201
376
  ## Environment
202
377
  ${envDescription}
@@ -204,10 +379,13 @@ ${envDescription}
204
379
  - Do not use host-only paths unless you are running in host mode and verified they exist.
205
380
 
206
381
  ## Workspace Layout
207
- ${workspaceRoot}/
382
+ ${workspaceLayout}
383
+ ${projection.layout === "conversation"
384
+ ? `${conversationPath}/ # This conversation`
385
+ : `${workspaceRoot}/
208
386
  ├── MEMORY.md # Global memory (all conversations)
209
387
  ├── skills/ # Global CLI tools you create
210
- └── ${conversationId}/ # This conversation
388
+ └── ${office.key}/ # This conversation`}
211
389
  ├── MEMORY.md # Conversation-specific memory
212
390
  ├── log.jsonl # Human-readable message history (no tool results)
213
391
  ├── sessions/ # Structured session history used for context reconstruction
@@ -222,7 +400,7 @@ ${workspaceRoot}/
222
400
  You can create reusable CLI tools for recurring tasks (email, APIs, data processing, etc.).
223
401
 
224
402
  ### Creating Skills
225
- Store in \`${workspaceRoot}/skills/<name>/\` (global) or \`${conversationPath}/skills/<name>/\` (conversation-specific).
403
+ ${skillStorageGuidance}
226
404
  Each skill directory needs a \`SKILL.md\` with YAML frontmatter:
227
405
 
228
406
  \`\`\`markdown
@@ -240,7 +418,9 @@ Scripts are in: {baseDir}/
240
418
  \`name\` and \`description\` are required. Use \`{baseDir}\` as placeholder for the skill's directory path.
241
419
 
242
420
  ### Available Skills
243
- ${skills.length > 0 ? formatSkillsForPrompt(skills) : "(no skills installed yet)"}
421
+ ${skills.length > 0 ? formatSkillsForPrompt(skills) : "(no skills installed yet)"}${skippedSkillLinks.length > 0
422
+ ? `\n\nNote: these skill entries were skipped because they are symlinks, which the host never follows when reading this conversation's skills: ${skippedSkillLinks.join(", ")}. Replace each with a real file or directory (for example \`cp -rL\`) to load it.`
423
+ : ""}
244
424
 
245
425
  ## Events
246
426
  Use the \`event\` tool to schedule immediate, one-shot, or periodic follow-ups. It writes to the host-side mikan control plane and fills routing fields for the current conversation automatically.
@@ -254,16 +434,14 @@ When events trigger, messages are prefixed like \`[EVENT:filename:type:time]\`.
254
434
  For periodic events where there's nothing to report, respond with exactly \`[SILENT]\`. Debounce external triggers; prefer one summarized event over many.
255
435
 
256
436
  ## Memory
257
- Write to MEMORY.md files to persist context across conversations.
258
- - Global (${workspaceRoot}/MEMORY.md): skills, preferences, project info
259
- - Conversation (${conversationPath}/MEMORY.md): conversation-specific decisions, ongoing work
260
- Update when you learn something important or when asked to remember something.
437
+ ${memoryGuidance}
438
+ Update it when you learn something important or when asked to remember something.
261
439
 
262
440
  ### Current Memory
263
441
  ${memory}
264
442
 
265
443
  ## System Configuration Log
266
- Maintain ${workspaceRoot}/SYSTEM.md to log all environment modifications:
444
+ Maintain ${systemLogPath} to log all environment modifications:
267
445
  - Installed packages (apt install, npm install, uv pip install)
268
446
  - Environment variables set
269
447
  - Config files modified (~/.gitconfig, cron jobs, etc.)
@@ -329,81 +507,21 @@ Do not add this to \`[SILENT]\` responses.`);
329
507
  }
330
508
  return parts.join("\n\n");
331
509
  }
332
- export function getUnresolvedSandboxPathContext(sandboxConfig, hostWorkspaceRoot) {
333
- if (sandboxConfig.type === "image") {
334
- return createMountedRuntimePathContext(hostWorkspaceRoot, "/workspace");
335
- }
336
- return createExecutor(sandboxConfig).getPathContext(hostWorkspaceRoot);
337
- }
338
- function createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspaceDir, hostWorkspacePath) {
339
- const executionResolver = vaultManager && sandboxConfig.type !== "host"
340
- ? new ActorExecutionResolver(sandboxConfig, vaultManager, provisioner, workspaceDir)
341
- : undefined;
342
- // activeExecutor is replaced at the start of each run() call when executionResolver
343
- // is present, so the stable `executor` wrapper always delegates to the latest resolved value.
344
- let activeExecutor = executionResolver !== undefined
345
- ? createExecutor({ type: "host" })
346
- : createExecutor(sandboxConfig);
347
- const executor = {
348
- exec(command, options) {
349
- return activeExecutor.exec(command, options);
350
- },
351
- getWorkspacePath(hostPath) {
352
- return activeExecutor.getWorkspacePath(hostPath);
353
- },
354
- getSandboxConfig() {
355
- return activeExecutor.getSandboxConfig();
356
- },
357
- getPathContext(hostWorkspaceRoot) {
358
- return activeExecutor.getPathContext(hostWorkspaceRoot);
359
- },
360
- };
361
- return {
362
- executionResolver,
363
- executor,
364
- getPathContext: () => executor.getPathContext(hostWorkspacePath),
365
- async resolveExecutorForRun(context) {
366
- if (!executionResolver)
367
- return;
368
- activeExecutor = await executionResolver.resolve(context);
369
- },
370
- };
371
- }
372
- /**
373
- * Extension host services over mikan's runtime infrastructure: schedules
374
- * become event files under `<workspaceDir>/events` (picked up live by
375
- * EventsWatcher), secrets come from `vaults/extensions/<slug>/env`, and
376
- * notify posts through the platform bots when main.ts provides a notifier.
377
- */
378
- function buildExtensionHostServices(params) {
379
- const { workspaceDir, vaultManager, platformNotifier } = params;
380
- const eventStore = HostEventStore.fromWorkspaceDir(workspaceDir);
381
- return {
382
- stateDir: readEnv("STATE_DIR"),
383
- scheduleStore: {
384
- write: async (filename, payload) => {
385
- // Event files tolerate a missing platform (single-platform default),
386
- // so the harness payload is a valid event payload as written.
387
- await eventStore.write(filename, payload);
388
- },
389
- delete: async (filename) => (await eventStore.delete(filename)).deleted,
390
- list: async () => (await eventStore.list()).map((entry) => ({
391
- filename: entry.filename,
392
- payload: entry.payload,
393
- })),
394
- },
395
- ...(platformNotifier ? { postMessage: platformNotifier } : {}),
396
- ...(vaultManager
397
- ? {
398
- resolveSecrets: (slug) => vaultManager.resolve(`extensions/${slug}`)?.env ?? {},
399
- }
400
- : {}),
401
- };
510
+ export function appendTriggerAttribution(text, triggerAttribution, sessionLink) {
511
+ if (!triggerAttribution)
512
+ return text;
513
+ const trimmed = text.trimEnd();
514
+ const legacySuffix = `_Triggered by ${triggerAttribution}_`;
515
+ // Slack mrkdwn italics cannot span a URL — a `_..._` wrapping a link renders
516
+ // the underscores literally. Keep the session link outside the italic span.
517
+ const suffix = sessionLink ? `${legacySuffix} · session: ${sessionLink}` : legacySuffix;
518
+ if (trimmed.endsWith(suffix))
519
+ return text;
520
+ const body = trimmed.endsWith(legacySuffix)
521
+ ? trimmed.slice(0, -legacySuffix.length).trimEnd()
522
+ : trimmed;
523
+ return `${body}\n\n${suffix}`;
402
524
  }
403
- /**
404
- * Local (workspace/conversation) skills override extension skills on name
405
- * collision: an admin's on-disk skill always beats an extension's.
406
- */
407
525
  function mergeExtensionSkills(local, extension) {
408
526
  if (extension.length === 0)
409
527
  return local;
@@ -414,37 +532,6 @@ function mergeExtensionSkills(local, extension) {
414
532
  byName.set(skill.name, skill);
415
533
  return [...byName.values()];
416
534
  }
417
- async function createConfiguredAgentSession(params) {
418
- const { conversationId, workspaceDir, systemPrompt, model, thinkingLevel, tools, sessionStore, models, vaultManager, platformNotifier, } = params;
419
- // Host-only dirs under the state dir: extension code runs in the mikan
420
- // process, so it must never load from workspace paths — those are mounted
421
- // into sandbox containers and agent-writable (sandbox escape otherwise).
422
- const extensionsResult = await loadExtensions({
423
- dirs: defaultExtensionDirs(conversationId, readEnv("STATE_DIR")),
424
- context: { conversationId, workspaceDir, model, thinkingLevel },
425
- services: buildExtensionHostServices({ workspaceDir, vaultManager, platformNotifier }),
426
- });
427
- for (const err of extensionsResult.errors) {
428
- log.logWarning(`[${conversationId}] Extension load error: ${err.path}`, err.error);
429
- }
430
- if (extensionsResult.extensions.length > 0) {
431
- log.logInfo(`[${conversationId}] Loaded ${extensionsResult.extensions.length} extension(s): ${extensionsResult.extensions.map((extension) => extension.name).join(", ")}`);
432
- }
433
- const session = new MikanAgentSession({
434
- systemPrompt,
435
- model,
436
- thinkingLevel,
437
- tools,
438
- models,
439
- sessionStore,
440
- extensions: extensionsResult.registry,
441
- });
442
- const reloaded = session.reloadFromSession();
443
- if (reloaded > 0) {
444
- log.logInfo(`[${conversationId}] Reloaded ${reloaded} messages from session context`);
445
- }
446
- return { session, extensionSkills: extensionsResult.skills };
447
- }
448
535
  function createEmptyUsageTotals() {
449
536
  return {
450
537
  input: 0,
@@ -461,6 +548,13 @@ function createRunState() {
461
548
  queue: null,
462
549
  pendingTools: new Map(),
463
550
  toolProgress: new Map(),
551
+ subagentProgress: new Map(),
552
+ completedSubagentProgress: [],
553
+ subagentToolCalls: new Set(),
554
+ subagentProgressShown: false,
555
+ suppressResponseDeltas: false,
556
+ lastSubagentProgressAt: 0,
557
+ toolProgressTimer: undefined,
464
558
  totalUsage: createEmptyUsageTotals(),
465
559
  llmCallCount: 0,
466
560
  stopReason: "stop",
@@ -480,6 +574,15 @@ function resetRunState(runState, responder, sessionConversation, userName, sessi
480
574
  };
481
575
  runState.pendingTools.clear();
482
576
  runState.toolProgress.clear();
577
+ runState.subagentProgress.clear();
578
+ runState.completedSubagentProgress = [];
579
+ runState.subagentToolCalls.clear();
580
+ runState.subagentProgressShown = false;
581
+ runState.suppressResponseDeltas = false;
582
+ runState.lastSubagentProgressAt = 0;
583
+ if (runState.toolProgressTimer)
584
+ clearTimeout(runState.toolProgressTimer);
585
+ runState.toolProgressTimer = undefined;
483
586
  runState.totalUsage = createEmptyUsageTotals();
484
587
  runState.llmCallCount = 0;
485
588
  runState.stopReason = "stop";
@@ -515,53 +618,6 @@ function createRunQueue(responder, runState) {
515
618
  wait: () => queueChain,
516
619
  };
517
620
  }
518
- function formatTimestampedUserMessage(message) {
519
- const timestamp = formatLocalTimestamp(new Date());
520
- const threadContext = message.threadTs ? ` [in-thread:${message.threadTs}]` : "";
521
- return `[${timestamp}] [${message.userName || "unknown"}]${threadContext}: ${message.text}`;
522
- }
523
- function collectMessageAttachments(message, workspacePath, pathContext) {
524
- const imageAttachments = [];
525
- const nonImagePaths = [];
526
- for (const attachment of message.attachments || []) {
527
- const runtimePath = `${workspacePath}/${attachment.localPath}`;
528
- const hostPath = pathContext?.runtimeToHostPath?.(runtimePath) ?? runtimePath;
529
- const mimeType = getImageMimeType(attachment.localPath);
530
- if (mimeType && existsSync(hostPath)) {
531
- try {
532
- imageAttachments.push({
533
- type: "image",
534
- mimeType,
535
- data: readFileSync(hostPath).toString("base64"),
536
- });
537
- }
538
- catch {
539
- nonImagePaths.push(runtimePath);
540
- }
541
- }
542
- else {
543
- nonImagePaths.push(runtimePath);
544
- }
545
- }
546
- return { imageAttachments, nonImagePaths };
547
- }
548
- export function buildPromptPayload(message, workspacePath, pathContext) {
549
- let userMessage = formatTimestampedUserMessage(message);
550
- const { imageAttachments, nonImagePaths } = collectMessageAttachments(message, workspacePath, pathContext);
551
- if (nonImagePaths.length > 0) {
552
- userMessage += `\n\n<slack_attachments>\n${nonImagePaths.join("\n")}\n</slack_attachments>`;
553
- }
554
- return { userMessage, imageAttachments };
555
- }
556
- async function writePromptDebugContext(conversationDir, systemPrompt, session, userMessage, imageAttachmentCount) {
557
- const debugContext = {
558
- systemPrompt,
559
- messages: session.messages,
560
- newUserMessage: userMessage,
561
- imageAttachmentCount,
562
- };
563
- await writeFile(join(conversationDir, "last_prompt.jsonl"), JSON.stringify(debugContext, null, 2));
564
- }
565
621
  function getFinalAssistantText(session) {
566
622
  const lastAssistant = session.messages.findLast((message) => message.role === "assistant");
567
623
  return (lastAssistant?.content
@@ -569,21 +625,6 @@ function getFinalAssistantText(session) {
569
625
  .map((content) => content.text)
570
626
  .join("\n") || "");
571
627
  }
572
- export function appendTriggerAttribution(text, triggerAttribution, sessionLink) {
573
- if (!triggerAttribution)
574
- return text;
575
- const trimmed = text.trimEnd();
576
- const legacySuffix = `_Triggered by ${triggerAttribution}_`;
577
- const suffix = sessionLink
578
- ? `_Triggered by ${triggerAttribution} · session: ${sessionLink}_`
579
- : legacySuffix;
580
- if (trimmed.endsWith(suffix))
581
- return text;
582
- const body = trimmed.endsWith(legacySuffix)
583
- ? trimmed.slice(0, -legacySuffix.length).trimEnd()
584
- : trimmed;
585
- return `${body}\n\n${suffix}`;
586
- }
587
628
  function isEventTriggerAttribution(triggerAttribution) {
588
629
  return triggerAttribution?.startsWith("[event:") === true;
589
630
  }
@@ -593,22 +634,86 @@ function extractToolLabel(toolName, args) {
593
634
  return toolName;
594
635
  return label.trim() || toolName;
595
636
  }
637
+ /** Non-subagent tool activity lines; subagent calls render as the dashboard. */
596
638
  function formatToolProgress(runState) {
597
- const lines = Array.from(runState.toolProgress.values()).map((item) => {
639
+ const lines = Array.from(runState.toolProgress.entries()).flatMap(([toolCallId, item]) => {
640
+ if (runState.subagentToolCalls.has(toolCallId))
641
+ return [];
598
642
  const marker = item.status === "running" ? "•" : item.status === "error" ? "✗" : "✓";
599
- return `${marker} ${item.label}`;
643
+ return [`${marker} ${item.label}`];
600
644
  });
601
645
  return lines.join("\n");
602
646
  }
603
647
  function formatResponseWithToolProgress(text, runState) {
604
648
  const progress = formatToolProgress(runState);
605
- return progress ? `${progress}\n\n${text}` : text;
649
+ return [progress, text].filter(Boolean).join("\n\n");
606
650
  }
607
- async function replaceResponseWithToolProgress(responder, runState) {
651
+ /**
652
+ * The one dashboard render path. A responder that overrides
653
+ * `replaceSubagentProgress` converts the snapshot for a pipeline that is not
654
+ * response-source Markdown (Telegram HTML); every other platform receives the
655
+ * Markdown dashboard — "dashboard, blank line, answer" — through
656
+ * `replaceResponse`, the same conversion as any response.
657
+ */
658
+ async function replaceWithSubagentDashboard(responder, snapshot, finalText, options) {
659
+ if (responder.replaceSubagentProgress) {
660
+ await responder.replaceSubagentProgress(snapshot, finalText);
661
+ return;
662
+ }
663
+ const dashboard = renderSubagentDashboard(snapshot);
664
+ await responder.replaceResponse(finalText ? `${dashboard}\n\n${finalText}` : dashboard, options);
665
+ }
666
+ async function replaceResponseWithToolProgress(responder, runState, subagentProgress = mergeSubagentProgress([...runState.subagentProgress.values()])) {
667
+ if (subagentProgress) {
668
+ await replaceWithSubagentDashboard(responder, subagentProgress);
669
+ return;
670
+ }
608
671
  const progress = formatToolProgress(runState);
609
672
  if (progress)
610
673
  await responder.replaceResponse(progress);
611
674
  }
675
+ const TOOL_PROGRESS_DEBOUNCE_MS = 500;
676
+ const SUBAGENT_PROGRESS_THROTTLE_MS = 2000;
677
+ function subagentProgressDelay(runState) {
678
+ if (!runState.subagentProgressShown)
679
+ return 0;
680
+ return Math.max(0, runState.lastSubagentProgressAt + SUBAGENT_PROGRESS_THROTTLE_MS - Date.now());
681
+ }
682
+ function scheduleToolProgressUpdate(responder, runState) {
683
+ if (runState.toolProgressTimer)
684
+ return;
685
+ const subagentProgress = mergeSubagentProgress([...runState.subagentProgress.values()]);
686
+ const delay = subagentProgress ? subagentProgressDelay(runState) : TOOL_PROGRESS_DEBOUNCE_MS;
687
+ runState.toolProgressTimer = setTimeout(() => {
688
+ runState.toolProgressTimer = undefined;
689
+ const snapshot = mergeSubagentProgress([...runState.subagentProgress.values()]);
690
+ if (snapshot) {
691
+ runState.subagentProgressShown = true;
692
+ runState.lastSubagentProgressAt = Date.now();
693
+ }
694
+ runState.queue?.enqueue(() => replaceResponseWithToolProgress(responder, runState, snapshot), "tool progress update");
695
+ }, delay);
696
+ runState.toolProgressTimer.unref();
697
+ }
698
+ function flushToolProgressUpdate(responder, runState) {
699
+ if (runState.toolProgressTimer)
700
+ clearTimeout(runState.toolProgressTimer);
701
+ runState.toolProgressTimer = undefined;
702
+ const subagentProgress = mergeSubagentProgress([...runState.subagentProgress.values()]);
703
+ if (subagentProgress) {
704
+ runState.subagentProgressShown = true;
705
+ runState.lastSubagentProgressAt = Date.now();
706
+ }
707
+ runState.queue?.enqueue(() => replaceResponseWithToolProgress(responder, runState, subagentProgress), "tool progress update");
708
+ }
709
+ function extractSubagentProgress(partialResult) {
710
+ if (!partialResult || typeof partialResult !== "object")
711
+ return undefined;
712
+ const details = partialResult.details;
713
+ if (!details || typeof details !== "object")
714
+ return undefined;
715
+ return parseSubagentProgressSnapshot(details.progress);
716
+ }
612
717
  async function finalizeRunResponse(responder, session, runState, options) {
613
718
  if (runState.stopReason === "error" && runState.errorMessage) {
614
719
  if (!runState.reportedLlmError) {
@@ -673,7 +778,17 @@ async function finalizeRunResponse(responder, session, runState, options) {
673
778
  if (!finalText.trim())
674
779
  return;
675
780
  try {
676
- await responder.replaceResponse(appendTriggerAttribution(formatResponseWithToolProgress(finalText, runState), options?.triggerAttribution, options?.triggerSessionLink), { createOverflowLink: options?.createOverflowLink });
781
+ const finalResponse = appendTriggerAttribution(finalText, options?.triggerAttribution, options?.triggerSessionLink);
782
+ const finalDashboard = mergeSubagentProgress(runState.completedSubagentProgress);
783
+ if (finalDashboard) {
784
+ await replaceWithSubagentDashboard(responder, finalDashboard, finalResponse, {
785
+ createOverflowLink: options?.createOverflowLink,
786
+ });
787
+ return;
788
+ }
789
+ await responder.replaceResponse(formatResponseWithToolProgress(finalResponse, runState), {
790
+ createOverflowLink: options?.createOverflowLink,
791
+ });
677
792
  }
678
793
  catch (err) {
679
794
  const errMsg = err instanceof Error ? err.message : String(err);
@@ -739,6 +854,226 @@ async function reportUsageSummary(ctx) {
739
854
  await waitForQueue();
740
855
  }
741
856
  }
857
+ function extractToolResultText(result) {
858
+ if (typeof result === "string") {
859
+ return result;
860
+ }
861
+ if (result &&
862
+ typeof result === "object" &&
863
+ "content" in result &&
864
+ Array.isArray(result.content)) {
865
+ const content = result.content;
866
+ const textParts = [];
867
+ for (const part of content) {
868
+ if (part.type === "text" && part.text) {
869
+ textParts.push(part.text);
870
+ }
871
+ }
872
+ if (textParts.length > 0) {
873
+ return textParts.join("\n");
874
+ }
875
+ }
876
+ return JSON.stringify(result);
877
+ }
878
+ function buildThreadSessionName(message) {
879
+ const text = message?.text?.trim();
880
+ if (!text)
881
+ return undefined;
882
+ const userLabel = message?.userName || message?.user || "unknown";
883
+ return `[${userLabel}]: ${text}`;
884
+ }
885
+ function createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspace) {
886
+ const executionResolver = vaultManager && sandboxConfig.type !== "host"
887
+ ? new ActorExecutionResolver(sandboxConfig, vaultManager, provisioner, workspace)
888
+ : undefined;
889
+ // activeExecutor is replaced at the start of each run() call when executionResolver
890
+ // is present, so the stable `executor` wrapper always delegates to the latest resolved value.
891
+ let activeExecutor = executionResolver !== undefined
892
+ ? createExecutor({ type: "host" })
893
+ : createExecutor(sandboxConfig);
894
+ const executor = {
895
+ exec(command, options) {
896
+ return activeExecutor.exec(command, options);
897
+ },
898
+ readFile(path, options) {
899
+ return activeExecutor.readFile(path, options);
900
+ },
901
+ readFileBase64(path, options) {
902
+ return activeExecutor.readFileBase64(path, options);
903
+ },
904
+ writeFile(path, content, options) {
905
+ return activeExecutor.writeFile(path, content, options);
906
+ },
907
+ getWorkspacePath(hostPath) {
908
+ return activeExecutor.getWorkspacePath(hostPath);
909
+ },
910
+ getSandboxConfig() {
911
+ return activeExecutor.getSandboxConfig();
912
+ },
913
+ getPathContext(hostWorkspaceRoot) {
914
+ return activeExecutor.getPathContext(hostWorkspaceRoot);
915
+ },
916
+ };
917
+ return {
918
+ executionResolver,
919
+ executor,
920
+ getPathContext: () => executor.getPathContext(workspace.root),
921
+ async resolveExecutorForRun(context) {
922
+ if (!executionResolver)
923
+ return;
924
+ activeExecutor = await executionResolver.resolve(context);
925
+ },
926
+ };
927
+ }
928
+ /**
929
+ * Extension host services over mikan's runtime infrastructure: schedules
930
+ * become event files under `<workspaceDir>/events` (picked up live by
931
+ * EventsWatcher), secrets come from `vaults/extensions/<slug>/env`, and
932
+ * notify posts through the platform bots when main.ts provides a notifier.
933
+ */
934
+ function buildExtensionHostServices(params) {
935
+ const { workspace, address, vaultManager, platformNotifier, platformReactor, platformUploader, platformBlockKit, platformDmOpener, platformHistoryFetcher, platformUserLister, extensionScheduleEngine, runSubagentService, } = params;
936
+ const eventStore = HostEventStore.fromWorkspaceDir(workspace.root);
937
+ return {
938
+ stateDir: workspace.stateDir,
939
+ scheduleStore: {
940
+ write: async (filename, payload) => {
941
+ await eventStore.write(filename, payload);
942
+ },
943
+ delete: async (filename) => (await eventStore.delete(filename)).deleted,
944
+ list: async () => (await eventStore.list()).flatMap((entry) => entry.payload ? [{ filename: entry.filename, payload: entry.payload }] : []),
945
+ },
946
+ ...(platformNotifier ? { postMessage: platformNotifier } : {}),
947
+ ...(platformReactor ? { addReaction: platformReactor } : {}),
948
+ ...(platformUploader ? { uploadFile: platformUploader } : {}),
949
+ ...(platformDmOpener ? { openDirectConversation: platformDmOpener } : {}),
950
+ ...(platformHistoryFetcher ? { fetchHistory: platformHistoryFetcher } : {}),
951
+ ...(platformUserLister ? { listUsers: platformUserLister } : {}),
952
+ ...(extensionScheduleEngine
953
+ ? {
954
+ callbackScheduleStore: {
955
+ upsert: (slug, name, spec) => extensionScheduleEngine.upsert(address, slug, name, spec),
956
+ delete: (slug, name) => extensionScheduleEngine.delete(address, slug, name),
957
+ list: (slug) => extensionScheduleEngine.list(address, slug),
958
+ },
959
+ }
960
+ : {}),
961
+ ...(platformBlockKit
962
+ ? { postBlocks: platformBlockKit.postBlocks, updateBlocks: platformBlockKit.updateBlocks }
963
+ : {}),
964
+ ...(runSubagentService ? { runSubagent: runSubagentService } : {}),
965
+ ...(vaultManager
966
+ ? {
967
+ resolveSecrets: (slug) => vaultManager.resolve(`extensions/${slug}`)?.env ?? {},
968
+ }
969
+ : {}),
970
+ };
971
+ }
972
+ async function createConfiguredAgentSession(params) {
973
+ const { office, systemPrompt, model, thinkingLevel, tools, sessionStore, models, vaultManager, platformNotifier, platformReactor, platformUploader, platformBlockKit, platformDmOpener, platformHistoryFetcher, platformUserLister, extensionScheduleEngine, } = params;
974
+ const { address } = office;
975
+ const conversationId = address.conversationId;
976
+ const workspaceDir = office.workspace.root;
977
+ // Host-only dirs under the state dir: extension code runs in the mikan
978
+ // process, so it must never load from workspace paths — those are mounted
979
+ // into sandbox containers and agent-writable (sandbox escape otherwise).
980
+ // resolveConversationPackages only ever returns state-dir paths (and host
981
+ // paths an administrator named explicitly in settings); it never reaches
982
+ // the network on this path, so a slow remote cannot delay a reply.
983
+ let session;
984
+ const loadedProfiles = loadSubagentProfiles(workspaceDir);
985
+ for (const diagnostic of loadedProfiles.diagnostics) {
986
+ log.logWarning(`[${conversationId}] Subagent profile ignored: ${diagnostic.path}`, diagnostic.message);
987
+ }
988
+ // Narrowed to the profiles whose tools actually exist once extensions have
989
+ // contributed theirs. Both subagent entry points read this at call time, so
990
+ // they always agree on which profiles are launchable.
991
+ let runnableSubagentProfiles = loadedProfiles.profiles;
992
+ const resolvedPackages = resolveConversationPackages({ office });
993
+ for (const error of resolvedPackages.errors) {
994
+ log.logWarning(`Package unavailable: ${error.source}`, error.message);
995
+ }
996
+ const extensionsResult = await loadExtensions({
997
+ dirs: resolvedPackages.extensionDirs,
998
+ roots: resolvedPackages.extensionRoots,
999
+ context: { address, conversationId, workspaceDir, model, thinkingLevel },
1000
+ services: buildExtensionHostServices({
1001
+ workspace: office.workspace,
1002
+ address,
1003
+ vaultManager,
1004
+ platformNotifier,
1005
+ platformReactor,
1006
+ platformUploader,
1007
+ platformBlockKit,
1008
+ platformDmOpener,
1009
+ platformHistoryFetcher,
1010
+ platformUserLister,
1011
+ extensionScheduleEngine,
1012
+ runSubagentService: (request, extensionTools) => {
1013
+ const activeParent = session?.isActiveRun ? session : undefined;
1014
+ return runSubagent({
1015
+ request,
1016
+ defaultModel: model,
1017
+ thinkingLevel,
1018
+ models,
1019
+ workspaceDir,
1020
+ availableTools: [...tools, ...extensionTools],
1021
+ profiles: runnableSubagentProfiles,
1022
+ slots: globalSubagentSlots,
1023
+ ...(activeParent
1024
+ ? {
1025
+ parentMessages: [...activeParent.messages],
1026
+ onUsage: activeParent.captureExternalUsageSink(),
1027
+ }
1028
+ : {}),
1029
+ });
1030
+ },
1031
+ }),
1032
+ });
1033
+ const contributedTools = extensionsResult.registry.getContributedTools();
1034
+ const subagentAvailableTools = [...tools, ...contributedTools];
1035
+ const availableToolNames = new Set(subagentAvailableTools.map((tool) => tool.name));
1036
+ runnableSubagentProfiles = new Map([...loadedProfiles.profiles].filter(([, profile]) => profile.tools.every((tool) => availableToolNames.has(tool))));
1037
+ for (const err of extensionsResult.errors) {
1038
+ log.logWarning(`[${conversationId}] Extension load error: ${err.path}`, err.error);
1039
+ }
1040
+ if (extensionsResult.extensions.length > 0) {
1041
+ log.logInfo(`[${conversationId}] Loaded ${extensionsResult.extensions.length} extension(s): ${extensionsResult.extensions.map((extension) => extension.name).join(", ")}`);
1042
+ }
1043
+ const subagentTool = createSubagentTool((request, hooks) => runSubagent({
1044
+ request,
1045
+ ...(hooks?.onActivity ? { onActivity: hooks.onActivity } : {}),
1046
+ defaultModel: model,
1047
+ thinkingLevel,
1048
+ models,
1049
+ workspaceDir,
1050
+ availableTools: subagentAvailableTools,
1051
+ profiles: runnableSubagentProfiles,
1052
+ slots: globalSubagentSlots,
1053
+ parentMessages: [...session.messages],
1054
+ onUsage: session.captureExternalUsageSink(),
1055
+ }), runnableSubagentProfiles);
1056
+ session = new MikanAgentSession({
1057
+ systemPrompt,
1058
+ model,
1059
+ thinkingLevel,
1060
+ tools: [...tools, subagentTool],
1061
+ models,
1062
+ sessionStore,
1063
+ extensions: extensionsResult.registry,
1064
+ });
1065
+ const activeSession = session;
1066
+ const reloaded = activeSession.reloadFromSession();
1067
+ if (reloaded > 0) {
1068
+ log.logInfo(`[${conversationId}] Reloaded ${reloaded} messages from session context`);
1069
+ }
1070
+ return {
1071
+ session: activeSession,
1072
+ extensionSkills: extensionsResult.skills,
1073
+ extensionRegistry: extensionsResult.registry,
1074
+ disposeExtensions: extensionsResult.dispose,
1075
+ };
1076
+ }
742
1077
  function reloadSessionMessages(session, conversationId) {
743
1078
  const reloaded = session.reloadFromSession();
744
1079
  if (reloaded > 0) {
@@ -746,31 +1081,36 @@ function reloadSessionMessages(session, conversationId) {
746
1081
  }
747
1082
  }
748
1083
  async function prepareRunContext(params) {
749
- const { message, responder, platform, conversationId, conversationDir, sessionUuid, runState, executor, executionResolver, resolveExecutorForRun, getPathContext, session, setEventContext, setSandboxContext, setUploadFunction, } = params;
1084
+ const { message, responder, platform, office, sessionUuid, runState, executor, executionResolver, resolveExecutorForRun, getPathContext, session, setEventContext, setSandboxContext, setUploadFunction, setImageUploadFunction, setReactFunction, bindPlatformToolPacks, } = params;
1085
+ const conversationId = office.address.conversationId;
750
1086
  let pathContext = params.pathContext;
751
- const sessionConversation = message.sessionKey.split(":")[0];
752
- await mkdir(join(conversationDir, "scratch"), { recursive: true });
1087
+ const sessionConversation = conversationIdOf(message.sessionKey);
1088
+ await mkdir(join(office.dir, "scratch"), { recursive: true });
753
1089
  if (executionResolver) {
754
1090
  await resolveExecutorForRun({
755
- platform: platform.name,
1091
+ address: message.address,
756
1092
  userId: message.userId,
757
- conversationId,
1093
+ trustModel: platform.trustModel,
758
1094
  });
759
1095
  pathContext = getPathContext();
760
1096
  }
761
1097
  reloadSessionMessages(session, conversationId);
762
- const memory = await getMemory(conversationDir);
763
- const skills = mergeExtensionSkills(loadMikanSkills(conversationDir, pathContext.runtimeWorkspaceRoot), params.extensionSkills ?? []);
1098
+ const projection = resolveWorkspaceProjection(office);
1099
+ const memory = await getMemory(projection);
1100
+ const conversationSkillLoad = loadMikanSkills(office, pathContext.runtimeWorkspaceRoot, projection);
1101
+ const skills = mergeExtensionSkills(conversationSkillLoad.skills, params.extensionSkills ?? []);
764
1102
  const triggerAttribution = resolveTriggerAttribution(message);
765
- const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, conversationId, message.conversationKind, message.userId, memory, executor.getSandboxConfig(), platform, skills);
1103
+ const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, office, message.conversationKind, message.userId, memory, executor.getSandboxConfig(), platform, skills, projection, conversationSkillLoad.skippedSkillLinks);
766
1104
  session.agent.state.systemPrompt = systemPrompt;
767
1105
  // Cache diagnosis: a byte-stable system prompt is the precondition for
768
1106
  // provider prompt caching. If this hash changes between turns of one
769
1107
  // conversation, something turn-varying leaked into the prompt; if it is
770
1108
  // stable and cacheRead stays 0, the miss is provider-side (e.g. OpenRouter
771
- // routing the model across upstream hosts).
1109
+ // routing the model across upstream hosts). This is the base prompt mikan
1110
+ // builds; a `before_agent_start` extension may still rewrite it per turn
1111
+ // (logged separately by the runner).
772
1112
  const promptHash = createHash("sha256").update(systemPrompt).digest("hex").slice(0, 8);
773
- log.logInfo(`[${conversationId}] System prompt: ${systemPrompt.length} chars, sha ${promptHash}`);
1113
+ log.logInfo(`[${conversationId}] System prompt (base): ${systemPrompt.length} chars, sha ${promptHash}`);
774
1114
  setEventContext({
775
1115
  platform: platform.name,
776
1116
  conversationId,
@@ -779,18 +1119,33 @@ async function prepareRunContext(params) {
779
1119
  });
780
1120
  setSandboxContext({ conversationId, userId: message.userId });
781
1121
  setUploadFunction(async (filePath, title) => {
782
- const hostPath = translateAttachPathToHost(filePath, pathContext);
1122
+ const runtimePath = normalizeAttachRuntimePath(filePath, pathContext.runtimeWorkspaceRoot);
1123
+ await withStagedRuntimeFile(executor, runtimePath, (stagedPath) => responder.uploadFile(stagedPath, title));
1124
+ });
1125
+ // generate_image writes its file host-side, so it uploads by host path —
1126
+ // no staging through the sandbox, where the file may not be mounted.
1127
+ setImageUploadFunction(async (hostPath, title) => {
783
1128
  await responder.uploadFile(hostPath, title);
784
1129
  });
1130
+ // The react tool is available only when the responder supports reactions
1131
+ // (interactive turns on platforms with reaction support); otherwise unset
1132
+ // so the tool reports it is unavailable rather than silently no-op'ing.
1133
+ setReactFunction(responder.react ? async (emoji) => responder.react(emoji) : null);
1134
+ // Platform capability packs (e.g. GitHub PR/CI) enable themselves per run;
1135
+ // core does not branch on platform name here.
1136
+ bindPlatformToolPacks({
1137
+ conversationId,
1138
+ platformName: platform.name,
1139
+ threadTs: message.threadTs,
1140
+ });
785
1141
  resetRunState(runState, responder, sessionConversation, message.userName, sessionUuid, triggerAttribution);
786
1142
  const runQueue = createRunQueue(responder, runState);
787
1143
  runState.queue = runQueue.queue;
788
1144
  log.logInfo(`Context sizes - system: ${systemPrompt.length} chars, memory: ${memory.length} chars`);
789
1145
  log.logInfo(`Channels: ${platform.channels.length}, Users: ${platform.users.length}`);
790
- const { userMessage, imageAttachments } = buildPromptPayload(message, pathContext.runtimeWorkspaceRoot, pathContext);
1146
+ const { userMessage, imageAttachments } = await buildPromptPayload(message, pathContext.runtimeWorkspaceRoot, pathContext, (runtimePath) => executor.readFileBase64(runtimePath));
791
1147
  const turnInstructions = buildTurnInstructions(message.id.startsWith("event:"), triggerAttribution, platform.name);
792
1148
  const finalUserMessage = turnInstructions ? `${turnInstructions}\n\n${userMessage}` : userMessage;
793
- await writePromptDebugContext(conversationDir, systemPrompt, session, finalUserMessage, imageAttachments.length);
794
1149
  return {
795
1150
  sessionConversation,
796
1151
  runQueue,
@@ -800,6 +1155,62 @@ async function prepareRunContext(params) {
800
1155
  pathContext,
801
1156
  };
802
1157
  }
1158
+ /**
1159
+ * The dream run reads a transcript in which the agent held its full tool set,
1160
+ * but `sessionDreamTools` has since narrowed it. Left to infer its grant from
1161
+ * the transcript, the model calls a tool it no longer holds and burns one of
1162
+ * its five turns on the failure — so the grant is stated, and derived from the
1163
+ * tools actually handed to the run rather than restated by hand.
1164
+ */
1165
+ function sessionDreamPrompt(conversationMemoryPath, toolNames) {
1166
+ // read/edit/write are core tools, so an empty grant does not arise today;
1167
+ // the fallback only keeps this sentence coherent if that ever changes.
1168
+ const grant = toolNames.length === 0
1169
+ ? "You have no tools in this run and cannot change memory. Say so and stop."
1170
+ : `Your tools this run: ${toolNames.join(", ")}. Nothing else will execute, including tools used earlier in this transcript. ${conversationMemoryPath} is the only path you may write.`;
1171
+ return [
1172
+ "Before this conversation is reset or rotated, preserve only durable information worth carrying into future sessions of this same conversation.",
1173
+ `Review the existing transcript and update only the conversation-specific MEMORY.md at ${conversationMemoryPath}.`,
1174
+ grant,
1175
+ "Do not modify the workspace-level global MEMORY.md. Information from one DM or channel must not be promoted into memory shared with other conversations by this maintenance run.",
1176
+ "Preserve durable decisions, preferences, facts, and ongoing work specific to this conversation. Preserve the concrete values and details needed to resume the work; do not replace them with abstract categories, summaries of retention rules, or statements that merely say a kind of information is durable. An explicit user statement that a fact, preference, or decision should persist is strong evidence that its exact content is worth preserving.",
1177
+ "Deduplicate against existing conversation memory. Do not preserve transient discussion, tool noise, secrets, speculative details, or test scaffolding. If there is nothing new worth preserving, make no changes. Do no unrelated work.",
1178
+ ].join("\n");
1179
+ }
1180
+ const SESSION_DREAM_BUDGET = {
1181
+ maxDurationMs: 2 * 60 * 1000,
1182
+ maxLlmCalls: 10,
1183
+ };
1184
+ function sessionDreamTools(tools, conversationMemoryPath) {
1185
+ return tools
1186
+ .filter((tool) => tool.name === "read" || tool.name === "edit" || tool.name === "write")
1187
+ .map((tool) => {
1188
+ if (tool.name === "read")
1189
+ return tool;
1190
+ return Object.assign({}, tool, {
1191
+ execute: async (...args) => {
1192
+ const params = args[1];
1193
+ if (params.path !== conversationMemoryPath) {
1194
+ throw new Error(`Session Dream may only modify ${conversationMemoryPath}`);
1195
+ }
1196
+ return tool.execute(...args);
1197
+ },
1198
+ });
1199
+ });
1200
+ }
1201
+ async function noopResponderMethod() { }
1202
+ function createNoopResponder() {
1203
+ return {
1204
+ respond: noopResponderMethod,
1205
+ replaceResponse: noopResponderMethod,
1206
+ respondDiagnostic: noopResponderMethod,
1207
+ respondToolResult: noopResponderMethod,
1208
+ setTyping: noopResponderMethod,
1209
+ setWorking: noopResponderMethod,
1210
+ uploadFile: noopResponderMethod,
1211
+ deleteResponse: noopResponderMethod,
1212
+ };
1213
+ }
803
1214
  function formatAgentActorName(userName, fallback) {
804
1215
  return userName ? `DM:${userName}` : fallback;
805
1216
  }
@@ -837,7 +1248,12 @@ function attachSessionEventHandlers(params) {
837
1248
  label: extractToolLabel(event.toolName, event.args),
838
1249
  status: "running",
839
1250
  });
840
- queue.enqueue(() => replaceResponseWithToolProgress(responder, runState), "tool progress update");
1251
+ if (event.toolName === "subagent") {
1252
+ runState.subagentToolCalls.add(event.toolCallId);
1253
+ }
1254
+ else {
1255
+ queue.enqueue(() => replaceResponseWithToolProgress(responder, runState), "tool progress update");
1256
+ }
841
1257
  addLifecycleBreadcrumb("agent.tool.started", {
842
1258
  tool: event.toolName,
843
1259
  ...baseAttrs,
@@ -845,6 +1261,16 @@ function attachSessionEventHandlers(params) {
845
1261
  log.logToolStart(logCtx, event.toolName, label, event.args);
846
1262
  return;
847
1263
  }
1264
+ if (event.type === "tool_execution_update") {
1265
+ const subagentProgress = extractSubagentProgress(event.partialResult);
1266
+ if (subagentProgress) {
1267
+ runState.subagentProgress.set(event.toolCallId, subagentProgress);
1268
+ runState.subagentToolCalls.add(event.toolCallId);
1269
+ runState.suppressResponseDeltas = true;
1270
+ scheduleToolProgressUpdate(responder, runState);
1271
+ }
1272
+ return;
1273
+ }
848
1274
  if (event.type === "tool_execution_end") {
849
1275
  const resultStr = extractToolResultText(event.result);
850
1276
  const pending = pendingTools.get(event.toolCallId);
@@ -856,7 +1282,15 @@ function attachSessionEventHandlers(params) {
856
1282
  const progress = runState.toolProgress.get(event.toolCallId);
857
1283
  if (progress)
858
1284
  progress.status = event.isError ? "error" : "done";
859
- queue.enqueue(() => replaceResponseWithToolProgress(responder, runState), "tool progress update");
1285
+ const subagentProgress = runState.subagentProgress.get(event.toolCallId);
1286
+ if (subagentProgress) {
1287
+ runState.subagentProgress.set(event.toolCallId, settleSubagentProgress(subagentProgress, event.isError));
1288
+ }
1289
+ flushToolProgressUpdate(responder, runState);
1290
+ const completedProgress = runState.subagentProgress.get(event.toolCallId);
1291
+ if (completedProgress)
1292
+ runState.completedSubagentProgress.push(completedProgress);
1293
+ runState.subagentProgress.delete(event.toolCallId);
860
1294
  pendingTools.delete(event.toolCallId);
861
1295
  const durationMs = pending ? Date.now() - pending.startTime : 0;
862
1296
  Sentry.metrics.count("agent.tool.calls", 1, {
@@ -884,6 +1318,9 @@ function attachSessionEventHandlers(params) {
884
1318
  }
885
1319
  else {
886
1320
  log.logToolSuccess(logCtx, event.toolName, durationMs, resultStr);
1321
+ if (event.toolName === "slack_blockkit") {
1322
+ runState.finalResponseHandledByTool = true;
1323
+ }
887
1324
  }
888
1325
  return;
889
1326
  }
@@ -913,7 +1350,7 @@ function attachSessionEventHandlers(params) {
913
1350
  actorName: formatAgentActorName(logCtx.userName, logCtx.conversationId),
914
1351
  event: { kind: "responseDelta", delta: assistantMessageEvent.delta },
915
1352
  });
916
- if (responder.appendResponseDelta) {
1353
+ if (responder.appendResponseDelta && !runState.suppressResponseDeltas) {
917
1354
  queue.enqueue(async () => {
918
1355
  await responder.appendResponseDelta?.(assistantMessageEvent.delta ?? "");
919
1356
  }, "response delta");
@@ -1010,6 +1447,12 @@ function attachSessionEventHandlers(params) {
1010
1447
  actorName: formatAgentActorName(logCtx.userName, logCtx.conversationId),
1011
1448
  event: { kind: "turnEnd" },
1012
1449
  });
1450
+ // A run that produced a subagent dashboard finalizes through it:
1451
+ // finalizeRunResponse renders "dashboard, blank line, answer", and a
1452
+ // finish here would first overwrite the dashboard with the bare
1453
+ // answer on every platform.
1454
+ if (runState.completedSubagentProgress.length > 0)
1455
+ return;
1013
1456
  if (responder.finishResponse) {
1014
1457
  queue.enqueue(async () => {
1015
1458
  await responder.finishResponse?.(finalText);
@@ -1069,27 +1512,6 @@ function attachSessionEventHandlers(params) {
1069
1512
  }
1070
1513
  });
1071
1514
  }
1072
- function extractToolResultText(result) {
1073
- if (typeof result === "string") {
1074
- return result;
1075
- }
1076
- if (result &&
1077
- typeof result === "object" &&
1078
- "content" in result &&
1079
- Array.isArray(result.content)) {
1080
- const content = result.content;
1081
- const textParts = [];
1082
- for (const part of content) {
1083
- if (part.type === "text" && part.text) {
1084
- textParts.push(part.text);
1085
- }
1086
- }
1087
- if (textParts.length > 0) {
1088
- return textParts.join("\n");
1089
- }
1090
- }
1091
- return JSON.stringify(result);
1092
- }
1093
1515
  /**
1094
1516
  * Create a new PiAgentWrapper for a channel.
1095
1517
  * Sets up the session and subscribes to events once.
@@ -1097,32 +1519,45 @@ function extractToolResultText(result) {
1097
1519
  * Runner caching is handled by the caller (channelStates in main.ts).
1098
1520
  * This is a stateless factory function.
1099
1521
  */
1100
- export async function createRunner(sandboxConfig, sessionKey, conversationId, conversationDir, workspaceDir, sessionScope, vaultManager, provisioner, sessionView, platformNotifier) {
1101
- const agentConfig = resolveConversationSettings(conversationDir);
1102
- const workspaceBase = join(conversationDir, "..");
1103
- const { executionResolver, executor, getPathContext, resolveExecutorForRun } = createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, workspaceDir, workspaceBase);
1104
- let pathContext = getUnresolvedSandboxPathContext(sandboxConfig, workspaceBase);
1105
- // Create tools (per-runner, with per-runner upload function setter)
1106
- const { tools, setUploadFunction, setEventContext, setSandboxContext } = createMikanTools(executor, workspaceDir, { sandbox: sandboxConfig, provisioner });
1107
- const modelRegistry = MikanModels.create();
1522
+ export async function createRunner(options) {
1523
+ const { sandboxConfig, sessionKey, office, sessionScope, vaultManager, provisioner, resourceController, sessionView, platformNotifier, platformReactor, platformUploader, platformBlockKit, platformDmOpener, platformHistoryFetcher, platformUserLister, extensionScheduleEngine, platformToolPackFactories, } = options;
1524
+ const conversationId = office.address.conversationId;
1525
+ const conversationDir = office.dir;
1526
+ const workspaceDir = office.workspace.root;
1527
+ const agentConfig = resolveConversationSettings(office);
1528
+ const projection = resolveWorkspaceProjection(office);
1529
+ assertSandboxSupportsWorkspacePolicy(sandboxConfig, projection.doorPolicy);
1530
+ const { executionResolver, executor, getPathContext, resolveExecutorForRun } = createRunnerExecutionContext(sandboxConfig, vaultManager, provisioner, office.workspace);
1531
+ let pathContext = getUnresolvedSandboxPathContext(sandboxConfig, workspaceDir);
1532
+ const modelRegistry = options.models ?? MikanModels.create();
1108
1533
  if (modelRegistry.getError()) {
1109
1534
  log.logWarning("models.json load error", modelRegistry.getError());
1110
1535
  }
1111
- const model = resolveConfiguredModel(modelRegistry, agentConfig.provider, agentConfig.model);
1536
+ const model = modelRegistry.resolve(agentConfig.provider, agentConfig.model);
1537
+ // Create tools (per-runner, with per-runner upload function setter)
1538
+ const { tools, setUploadFunction, setImageUploadFunction, setReactFunction, bindPlatformToolPacks, setEventContext, setSandboxContext, } = createMikanTools(executor, workspaceDir, { sandbox: sandboxConfig, resourceController: resourceController ?? provisioner }, platformToolPackFactories ?? [], {
1539
+ model,
1540
+ getApiKey: () => modelRegistry.getApiKeyForProvider(model.provider),
1541
+ // The conversation's own office dir: mounted into the sandbox, unlike
1542
+ // the workspace base — generated images must land somewhere the agent
1543
+ // (and a future run) can still reach.
1544
+ outputDir: conversationDir,
1545
+ });
1112
1546
  // Initial system prompt (will be updated each run with fresh memory/channels/users/skills)
1113
- const memory = await getMemory(conversationDir);
1114
- const skills = loadMikanSkills(conversationDir, pathContext.runtimeWorkspaceRoot);
1547
+ const memory = await getMemory(projection);
1548
+ const { skills, skippedSkillLinks } = loadMikanSkills(office, pathContext.runtimeWorkspaceRoot, projection);
1115
1549
  const emptyPlatform = {
1116
1550
  name: "chat",
1117
1551
  formattingGuide: "",
1118
1552
  channels: [],
1119
1553
  users: [],
1554
+ trustModel: "membership",
1120
1555
  };
1121
- const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, conversationId, "shared", undefined, memory, sandboxConfig, emptyPlatform, skills);
1556
+ const systemPrompt = buildSystemPrompt(pathContext.runtimeWorkspaceRoot, office, "shared", undefined, memory, sandboxConfig, emptyPlatform, skills, projection, skippedSkillLinks);
1122
1557
  // Create session manager and settings manager. Top-level/private sessions
1123
1558
  // use the conversation's current pointer; scoped sessions use fixed files.
1124
1559
  // Platform-specific scope behavior is resolved before runner creation.
1125
- const isThread = sessionKey.includes(":");
1560
+ const isThread = isThreadSessionKey(sessionKey);
1126
1561
  const { contextFile, threadRootMessage } = sessionScope;
1127
1562
  const sessionManager = openManagedSession(contextFile, pathContext.runtimeWorkspaceRoot);
1128
1563
  const threadSessionName = buildThreadSessionName(threadRootMessage);
@@ -1130,10 +1565,9 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1130
1565
  sessionManager.appendSessionInfo(threadSessionName);
1131
1566
  }
1132
1567
  const sessionUuid = extractSessionUuid(contextFile);
1133
- const chatSessionManager = new AgentMemoryFileManager();
1134
- const { session, extensionSkills } = await createConfiguredAgentSession({
1135
- conversationId,
1136
- workspaceDir,
1568
+ const chatSessionManager = new ChatHistorySync();
1569
+ const { session, extensionSkills, extensionRegistry, disposeExtensions } = await createConfiguredAgentSession({
1570
+ office,
1137
1571
  systemPrompt,
1138
1572
  model,
1139
1573
  thinkingLevel: agentConfig.thinkingLevel,
@@ -1142,6 +1576,13 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1142
1576
  models: modelRegistry,
1143
1577
  vaultManager,
1144
1578
  platformNotifier,
1579
+ platformReactor,
1580
+ platformUploader,
1581
+ platformBlockKit,
1582
+ platformDmOpener,
1583
+ platformHistoryFetcher,
1584
+ platformUserLister,
1585
+ extensionScheduleEngine,
1145
1586
  });
1146
1587
  // Mutable per-run state - event handler references this
1147
1588
  const runState = createRunState();
@@ -1160,8 +1601,7 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1160
1601
  message,
1161
1602
  responder,
1162
1603
  platform,
1163
- conversationId,
1164
- conversationDir,
1604
+ office,
1165
1605
  sessionUuid,
1166
1606
  runState,
1167
1607
  executor,
@@ -1173,6 +1613,9 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1173
1613
  setEventContext,
1174
1614
  setSandboxContext,
1175
1615
  setUploadFunction,
1616
+ setImageUploadFunction,
1617
+ setReactFunction,
1618
+ bindPlatformToolPacks,
1176
1619
  pathContext,
1177
1620
  });
1178
1621
  pathContext = prepared.pathContext;
@@ -1201,10 +1644,39 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1201
1644
  // Autonomous (event/trigger) runs get an explicit resource ceiling since
1202
1645
  // no human is watching the loop; interactive turns stay human-gated.
1203
1646
  const isEventRun = message.id.startsWith("event:");
1204
- await session.prompt(prepared.userMessage, {
1647
+ // Event runs have no triggering platform message, so identity fields
1648
+ // stay unset and extensions must null-check them.
1649
+ const origin = isEventRun
1650
+ ? { kind: "event", platform: platform.name }
1651
+ : {
1652
+ kind: "interactive",
1653
+ platform: platform.name,
1654
+ messageTs: message.id,
1655
+ userId: message.userId,
1656
+ userName: message.userName,
1657
+ threadTs: message.threadTs,
1658
+ attachments: message.attachments,
1659
+ };
1660
+ const outcome = await session.prompt(prepared.userMessage, {
1661
+ origin,
1205
1662
  ...(prepared.imageAttachments.length > 0 ? { images: prepared.imageAttachments } : {}),
1206
1663
  ...(isEventRun ? { budget: DEFAULT_EVENT_BUDGET } : {}),
1207
1664
  });
1665
+ if (outcome?.blocked) {
1666
+ const text = outcome.reason
1667
+ ? `_Turn blocked by an extension: ${outcome.reason}_`
1668
+ : "_Turn blocked by an extension._";
1669
+ sendAgentEvent({
1670
+ sessionId: sessionUuid,
1671
+ actorName: formatAgentActorName(message.userName, prepared.sessionConversation),
1672
+ event: { kind: "diagnostic", text },
1673
+ });
1674
+ await responder.respondDiagnostic(text, { style: "muted" });
1675
+ runState.responder = null;
1676
+ runState.logCtx = null;
1677
+ runState.queue = null;
1678
+ return { stopReason: "blocked" };
1679
+ }
1208
1680
  // Wait for queued messages
1209
1681
  await prepared.runQueue.wait();
1210
1682
  const sessionViewTokenStore = sessionView?.tokenStore;
@@ -1247,9 +1719,89 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1247
1719
  runState.queue = null;
1248
1720
  return { stopReason: runState.stopReason, errorMessage: runState.errorMessage };
1249
1721
  },
1722
+ async dreamSessionMemory(message, platform) {
1723
+ const responder = createNoopResponder();
1724
+ const prepared = await prepareRunContext({
1725
+ message,
1726
+ responder,
1727
+ platform,
1728
+ office,
1729
+ sessionUuid,
1730
+ runState,
1731
+ executor,
1732
+ executionResolver,
1733
+ resolveExecutorForRun,
1734
+ getPathContext,
1735
+ session,
1736
+ extensionSkills,
1737
+ setEventContext,
1738
+ setSandboxContext,
1739
+ setUploadFunction,
1740
+ setImageUploadFunction,
1741
+ setReactFunction,
1742
+ bindPlatformToolPacks,
1743
+ pathContext,
1744
+ });
1745
+ pathContext = prepared.pathContext;
1746
+ try {
1747
+ const conversationMemoryPath = posix.join(prepared.pathContext.runtimeWorkspaceRoot, office.key, "MEMORY.md");
1748
+ const dreamTools = sessionDreamTools(session.agent.state.tools, conversationMemoryPath);
1749
+ const outcome = await session.prompt(sessionDreamPrompt(conversationMemoryPath, dreamTools.map((tool) => tool.name)), {
1750
+ origin: {
1751
+ kind: "interactive",
1752
+ platform: platform.name,
1753
+ messageTs: message.id,
1754
+ userId: message.userId,
1755
+ userName: message.userName,
1756
+ threadTs: message.threadTs,
1757
+ attachments: message.attachments,
1758
+ },
1759
+ budget: SESSION_DREAM_BUDGET,
1760
+ tools: dreamTools,
1761
+ });
1762
+ if (outcome?.blocked) {
1763
+ return { stopReason: "blocked", errorMessage: outcome.reason };
1764
+ }
1765
+ await prepared.runQueue.wait();
1766
+ // A budget abort carries its reason in the tally, not in errorMessage,
1767
+ // and that reason is the only thing that tells the user why memory was
1768
+ // not preserved.
1769
+ return {
1770
+ stopReason: runState.stopReason,
1771
+ errorMessage: runState.errorMessage ?? session.getLastRunStats().budgetExceededReason,
1772
+ };
1773
+ }
1774
+ finally {
1775
+ runState.responder = null;
1776
+ runState.logCtx = null;
1777
+ runState.queue = null;
1778
+ }
1779
+ },
1250
1780
  abort() {
1251
1781
  session.abort();
1252
1782
  },
1783
+ async tryExtensionCommand(message, responder) {
1784
+ const parsed = parseCommandInput(message.text);
1785
+ if (!parsed)
1786
+ return false;
1787
+ return extensionRegistry.dispatchCommand(parsed.name, {
1788
+ args: parsed.args,
1789
+ conversationId,
1790
+ userId: message.userId,
1791
+ userName: message.userName,
1792
+ threadTs: message.threadTs,
1793
+ respond: (text) => responder.respond(text),
1794
+ });
1795
+ },
1796
+ async tryExtensionAction(slug, action) {
1797
+ return extensionRegistry.dispatchAction(slug, action);
1798
+ },
1799
+ async tryExtensionScheduleCallback(slug, callback, event) {
1800
+ return extensionRegistry.dispatchScheduleCallback(slug, callback, event);
1801
+ },
1802
+ async dispose() {
1803
+ await disposeExtensions();
1804
+ },
1253
1805
  getCurrentStep() {
1254
1806
  const pending = runState.pendingTools;
1255
1807
  if (pending.size === 0)
@@ -1265,13 +1817,4 @@ export async function createRunner(sandboxConfig, sessionKey, conversationId, co
1265
1817
  },
1266
1818
  };
1267
1819
  }
1268
- export function translateRuntimePathToHost(runtimePath, pathContext) {
1269
- return pathContext.runtimeToHostPath?.(runtimePath) ?? runtimePath;
1270
- }
1271
- export function translateAttachPathToHost(filePath, pathContext) {
1272
- const runtimePath = posix.isAbsolute(filePath)
1273
- ? filePath
1274
- : posix.join(pathContext.runtimeWorkspaceRoot, filePath);
1275
- return translateRuntimePathToHost(runtimePath, pathContext);
1276
- }
1277
1820
  //# sourceMappingURL=agent.js.map