@geminixiang/mikan 1.0.0-beta.3 → 1.0.0-beta.30

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 (545) hide show
  1. package/CHANGELOG.md +563 -0
  2. package/README.md +60 -24
  3. package/dist/adapter.d.ts +1 -1
  4. package/dist/adapter.d.ts.map +1 -1
  5. package/dist/adapter.js.map +1 -1
  6. package/dist/adapters/discord/bot.d.ts +1 -0
  7. package/dist/adapters/discord/bot.d.ts.map +1 -1
  8. package/dist/adapters/discord/bot.js +47 -65
  9. package/dist/adapters/discord/bot.js.map +1 -1
  10. package/dist/adapters/discord/context.d.ts.map +1 -1
  11. package/dist/adapters/discord/context.js +46 -211
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/github/bot.d.ts +86 -0
  14. package/dist/adapters/github/bot.d.ts.map +1 -0
  15. package/dist/adapters/github/bot.js +640 -0
  16. package/dist/adapters/github/bot.js.map +1 -0
  17. package/dist/adapters/github/client.d.ts +122 -0
  18. package/dist/adapters/github/client.d.ts.map +1 -0
  19. package/dist/adapters/github/client.js +314 -0
  20. package/dist/adapters/github/client.js.map +1 -0
  21. package/dist/adapters/github/cloudbuild.d.ts +19 -0
  22. package/dist/adapters/github/cloudbuild.d.ts.map +1 -0
  23. package/dist/adapters/github/cloudbuild.js +71 -0
  24. package/dist/adapters/github/cloudbuild.js.map +1 -0
  25. package/dist/adapters/github/context.d.ts +9 -0
  26. package/dist/adapters/github/context.d.ts.map +1 -0
  27. package/dist/adapters/github/context.js +98 -0
  28. package/dist/adapters/github/context.js.map +1 -0
  29. package/dist/adapters/github/gcp-auth.d.ts +28 -0
  30. package/dist/adapters/github/gcp-auth.d.ts.map +1 -0
  31. package/dist/adapters/github/gcp-auth.js +196 -0
  32. package/dist/adapters/github/gcp-auth.js.map +1 -0
  33. package/dist/adapters/github/github-ops.d.ts +90 -0
  34. package/dist/adapters/github/github-ops.d.ts.map +1 -0
  35. package/dist/adapters/github/github-ops.js +405 -0
  36. package/dist/adapters/github/github-ops.js.map +1 -0
  37. package/dist/adapters/github/ids.d.ts +34 -0
  38. package/dist/adapters/github/ids.d.ts.map +1 -0
  39. package/dist/adapters/github/ids.js +53 -0
  40. package/dist/adapters/github/ids.js.map +1 -0
  41. package/dist/adapters/github/repo.d.ts +32 -0
  42. package/dist/adapters/github/repo.d.ts.map +1 -0
  43. package/dist/adapters/github/repo.js +141 -0
  44. package/dist/adapters/github/repo.js.map +1 -0
  45. package/dist/adapters/github/tool-pack.d.ts +14 -0
  46. package/dist/adapters/github/tool-pack.d.ts.map +1 -0
  47. package/dist/adapters/github/tool-pack.js +56 -0
  48. package/dist/adapters/github/tool-pack.js.map +1 -0
  49. package/dist/adapters/github/tools/checks.d.ts +19 -0
  50. package/dist/adapters/github/tools/checks.d.ts.map +1 -0
  51. package/dist/adapters/github/tools/checks.js +105 -0
  52. package/dist/adapters/github/tools/checks.js.map +1 -0
  53. package/dist/adapters/github/tools/issue.d.ts +21 -0
  54. package/dist/adapters/github/tools/issue.d.ts.map +1 -0
  55. package/dist/adapters/github/tools/issue.js +48 -0
  56. package/dist/adapters/github/tools/issue.js.map +1 -0
  57. package/dist/adapters/github/tools/pr.d.ts +22 -0
  58. package/dist/adapters/github/tools/pr.d.ts.map +1 -0
  59. package/dist/adapters/github/tools/pr.js +48 -0
  60. package/dist/adapters/github/tools/pr.js.map +1 -0
  61. package/dist/adapters/github/tools/read.d.ts +21 -0
  62. package/dist/adapters/github/tools/read.d.ts.map +1 -0
  63. package/dist/adapters/github/tools/read.js +141 -0
  64. package/dist/adapters/github/tools/read.js.map +1 -0
  65. package/dist/adapters/github/tools/review-reply.d.ts +17 -0
  66. package/dist/adapters/github/tools/review-reply.d.ts.map +1 -0
  67. package/dist/adapters/github/tools/review-reply.js +38 -0
  68. package/dist/adapters/github/tools/review-reply.js.map +1 -0
  69. package/dist/adapters/github/tools/sync.d.ts +17 -0
  70. package/dist/adapters/github/tools/sync.d.ts.map +1 -0
  71. package/dist/adapters/github/tools/sync.js +34 -0
  72. package/dist/adapters/github/tools/sync.js.map +1 -0
  73. package/dist/adapters/github/types.d.ts +372 -0
  74. package/dist/adapters/github/types.d.ts.map +1 -0
  75. package/dist/adapters/github/types.js +2 -0
  76. package/dist/adapters/github/types.js.map +1 -0
  77. package/dist/adapters/intake.d.ts +16 -5
  78. package/dist/adapters/intake.d.ts.map +1 -1
  79. package/dist/adapters/intake.js +84 -22
  80. package/dist/adapters/intake.js.map +1 -1
  81. package/dist/adapters/progressive-renderer.d.ts +7 -0
  82. package/dist/adapters/progressive-renderer.d.ts.map +1 -0
  83. package/dist/adapters/progressive-renderer.js +383 -0
  84. package/dist/adapters/progressive-renderer.js.map +1 -0
  85. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  86. package/dist/adapters/slack/blocks.js +111 -111
  87. package/dist/adapters/slack/blocks.js.map +1 -1
  88. package/dist/adapters/slack/bot.d.ts +10 -6
  89. package/dist/adapters/slack/bot.d.ts.map +1 -1
  90. package/dist/adapters/slack/bot.js +173 -145
  91. package/dist/adapters/slack/bot.js.map +1 -1
  92. package/dist/adapters/slack/context.d.ts.map +1 -1
  93. package/dist/adapters/slack/context.js +3 -14
  94. package/dist/adapters/slack/context.js.map +1 -1
  95. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  96. package/dist/adapters/slack/response-lifecycle.js +126 -323
  97. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  98. package/dist/adapters/slack/session.d.ts.map +1 -1
  99. package/dist/adapters/slack/session.js +5 -8
  100. package/dist/adapters/slack/session.js.map +1 -1
  101. package/dist/adapters/slack/tool-pack.d.ts +11 -0
  102. package/dist/adapters/slack/tool-pack.d.ts.map +1 -0
  103. package/dist/adapters/slack/tool-pack.js +45 -0
  104. package/dist/adapters/slack/tool-pack.js.map +1 -0
  105. package/dist/adapters/slack/tools/blockkit.d.ts +14 -0
  106. package/dist/adapters/slack/tools/blockkit.d.ts.map +1 -0
  107. package/dist/adapters/slack/tools/blockkit.js +106 -0
  108. package/dist/adapters/slack/tools/blockkit.js.map +1 -0
  109. package/dist/adapters/slack/types.d.ts +37 -0
  110. package/dist/adapters/slack/types.d.ts.map +1 -1
  111. package/dist/adapters/slack/types.js.map +1 -1
  112. package/dist/adapters/telegram/bot.d.ts +1 -2
  113. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  114. package/dist/adapters/telegram/bot.js +51 -63
  115. package/dist/adapters/telegram/bot.js.map +1 -1
  116. package/dist/adapters/telegram/context.d.ts.map +1 -1
  117. package/dist/adapters/telegram/context.js +55 -182
  118. package/dist/adapters/telegram/context.js.map +1 -1
  119. package/dist/adapters/types.d.ts +86 -10
  120. package/dist/adapters/types.d.ts.map +1 -1
  121. package/dist/adapters/types.js.map +1 -1
  122. package/dist/agent.d.ts +14 -19
  123. package/dist/agent.d.ts.map +1 -1
  124. package/dist/agent.js +745 -243
  125. package/dist/agent.js.map +1 -1
  126. package/dist/cli/arg-grammar.d.ts +25 -0
  127. package/dist/cli/arg-grammar.d.ts.map +1 -0
  128. package/dist/cli/arg-grammar.js +59 -0
  129. package/dist/cli/arg-grammar.js.map +1 -0
  130. package/dist/cli/boot.d.ts +5 -0
  131. package/dist/cli/boot.d.ts.map +1 -0
  132. package/dist/cli/boot.js +112 -0
  133. package/dist/cli/boot.js.map +1 -0
  134. package/dist/cli/download.d.ts.map +1 -0
  135. package/dist/cli/download.js.map +1 -0
  136. package/dist/cli/ext-dev.d.ts +2 -0
  137. package/dist/cli/ext-dev.d.ts.map +1 -0
  138. package/dist/cli/ext-dev.js +177 -0
  139. package/dist/cli/ext-dev.js.map +1 -0
  140. package/dist/cli/ext-git.d.ts +17 -0
  141. package/dist/cli/ext-git.d.ts.map +1 -0
  142. package/dist/cli/ext-git.js +107 -0
  143. package/dist/cli/ext-git.js.map +1 -0
  144. package/dist/cli/ext.d.ts +2 -0
  145. package/dist/cli/ext.d.ts.map +1 -0
  146. package/dist/cli/ext.js +222 -0
  147. package/dist/cli/ext.js.map +1 -0
  148. package/dist/cli/types.d.ts +20 -0
  149. package/dist/cli/types.d.ts.map +1 -0
  150. package/dist/cli/types.js +2 -0
  151. package/dist/cli/types.js.map +1 -0
  152. package/dist/commands/admin.d.ts.map +1 -1
  153. package/dist/commands/admin.js +2 -1
  154. package/dist/commands/admin.js.map +1 -1
  155. package/dist/commands/auto-reply.d.ts.map +1 -1
  156. package/dist/commands/auto-reply.js +2 -6
  157. package/dist/commands/auto-reply.js.map +1 -1
  158. package/dist/commands/extensions.d.ts.map +1 -1
  159. package/dist/commands/extensions.js +18 -8
  160. package/dist/commands/extensions.js.map +1 -1
  161. package/dist/commands/login.d.ts +2 -1
  162. package/dist/commands/login.d.ts.map +1 -1
  163. package/dist/commands/login.js +35 -3
  164. package/dist/commands/login.js.map +1 -1
  165. package/dist/commands/manifest.d.ts +30 -0
  166. package/dist/commands/manifest.d.ts.map +1 -0
  167. package/dist/commands/manifest.js +121 -0
  168. package/dist/commands/manifest.js.map +1 -0
  169. package/dist/commands/model.d.ts +2 -3
  170. package/dist/commands/model.d.ts.map +1 -1
  171. package/dist/commands/model.js +57 -20
  172. package/dist/commands/model.js.map +1 -1
  173. package/dist/commands/new.d.ts.map +1 -1
  174. package/dist/commands/new.js +10 -2
  175. package/dist/commands/new.js.map +1 -1
  176. package/dist/commands/registry.d.ts +2 -2
  177. package/dist/commands/registry.d.ts.map +1 -1
  178. package/dist/commands/registry.js +1 -2
  179. package/dist/commands/registry.js.map +1 -1
  180. package/dist/commands/sandbox.d.ts.map +1 -1
  181. package/dist/commands/sandbox.js +23 -17
  182. package/dist/commands/sandbox.js.map +1 -1
  183. package/dist/commands/text.d.ts +2 -0
  184. package/dist/commands/text.d.ts.map +1 -0
  185. package/dist/commands/text.js +16 -0
  186. package/dist/commands/text.js.map +1 -0
  187. package/dist/commands/types.d.ts +70 -2
  188. package/dist/commands/types.d.ts.map +1 -1
  189. package/dist/commands/types.js.map +1 -1
  190. package/dist/config.d.ts +1 -3
  191. package/dist/config.d.ts.map +1 -1
  192. package/dist/config.js +102 -95
  193. package/dist/config.js.map +1 -1
  194. package/dist/env-manifest.d.ts +28 -0
  195. package/dist/env-manifest.d.ts.map +1 -0
  196. package/dist/env-manifest.js +214 -0
  197. package/dist/env-manifest.js.map +1 -0
  198. package/dist/events.d.ts.map +1 -1
  199. package/dist/events.js +9 -73
  200. package/dist/events.js.map +1 -1
  201. package/dist/execution-resolver.d.ts +7 -6
  202. package/dist/execution-resolver.d.ts.map +1 -1
  203. package/dist/execution-resolver.js +105 -110
  204. package/dist/execution-resolver.js.map +1 -1
  205. package/dist/harness/auth.d.ts +2 -1
  206. package/dist/harness/auth.d.ts.map +1 -1
  207. package/dist/harness/auth.js +6 -0
  208. package/dist/harness/auth.js.map +1 -1
  209. package/dist/harness/event-format.d.ts +25 -0
  210. package/dist/harness/event-format.d.ts.map +1 -0
  211. package/dist/harness/event-format.js +159 -0
  212. package/dist/harness/event-format.js.map +1 -0
  213. package/dist/harness/extensions/blockkit.d.ts +19 -0
  214. package/dist/harness/extensions/blockkit.d.ts.map +1 -0
  215. package/dist/harness/extensions/blockkit.js +42 -0
  216. package/dist/harness/extensions/blockkit.js.map +1 -0
  217. package/dist/harness/extensions/loader.d.ts +25 -48
  218. package/dist/harness/extensions/loader.d.ts.map +1 -1
  219. package/dist/harness/extensions/loader.js +373 -113
  220. package/dist/harness/extensions/loader.js.map +1 -1
  221. package/dist/harness/extensions/registry.d.ts +75 -2
  222. package/dist/harness/extensions/registry.d.ts.map +1 -1
  223. package/dist/harness/extensions/registry.js +239 -0
  224. package/dist/harness/extensions/registry.js.map +1 -1
  225. package/dist/harness/extensions/types.d.ts +318 -25
  226. package/dist/harness/extensions/types.d.ts.map +1 -1
  227. package/dist/harness/extensions/types.js.map +1 -1
  228. package/dist/harness/index.d.ts +18 -8
  229. package/dist/harness/index.d.ts.map +1 -1
  230. package/dist/harness/index.js +7 -4
  231. package/dist/harness/index.js.map +1 -1
  232. package/dist/harness/models.d.ts +7 -4
  233. package/dist/harness/models.d.ts.map +1 -1
  234. package/dist/harness/models.js +10 -0
  235. package/dist/harness/models.js.map +1 -1
  236. package/dist/harness/runner.d.ts +50 -54
  237. package/dist/harness/runner.d.ts.map +1 -1
  238. package/dist/harness/runner.js +230 -44
  239. package/dist/harness/runner.js.map +1 -1
  240. package/dist/harness/session-store.d.ts +9 -5
  241. package/dist/harness/session-store.d.ts.map +1 -1
  242. package/dist/harness/session-store.js +131 -30
  243. package/dist/harness/session-store.js.map +1 -1
  244. package/dist/harness/settings.d.ts +2 -20
  245. package/dist/harness/settings.d.ts.map +1 -1
  246. package/dist/harness/settings.js +11 -0
  247. package/dist/harness/settings.js.map +1 -1
  248. package/dist/harness/skills.d.ts +2 -29
  249. package/dist/harness/skills.d.ts.map +1 -1
  250. package/dist/harness/skills.js.map +1 -1
  251. package/dist/harness/subagent-profiles.d.ts +8 -0
  252. package/dist/harness/subagent-profiles.d.ts.map +1 -0
  253. package/dist/harness/subagent-profiles.js +290 -0
  254. package/dist/harness/subagent-profiles.js.map +1 -0
  255. package/dist/harness/subagent-runner.d.ts +39 -0
  256. package/dist/harness/subagent-runner.d.ts.map +1 -0
  257. package/dist/harness/subagent-runner.js +615 -0
  258. package/dist/harness/subagent-runner.js.map +1 -0
  259. package/dist/harness/types.d.ts +263 -1
  260. package/dist/harness/types.d.ts.map +1 -1
  261. package/dist/harness/types.js.map +1 -1
  262. package/dist/harness/usage.d.ts +6 -0
  263. package/dist/harness/usage.d.ts.map +1 -0
  264. package/dist/harness/usage.js +32 -0
  265. package/dist/harness/usage.js.map +1 -0
  266. package/dist/index.d.ts +33 -8
  267. package/dist/index.d.ts.map +1 -1
  268. package/dist/index.js +20 -6
  269. package/dist/index.js.map +1 -1
  270. package/dist/main.js +253 -109
  271. package/dist/main.js.map +1 -1
  272. package/dist/observability/instrument.js +6 -4
  273. package/dist/observability/instrument.js.map +1 -1
  274. package/dist/observability/sentry.d.ts.map +1 -1
  275. package/dist/observability/sentry.js +3 -2
  276. package/dist/observability/sentry.js.map +1 -1
  277. package/dist/packages/admin.d.ts +17 -0
  278. package/dist/packages/admin.d.ts.map +1 -0
  279. package/dist/packages/admin.js +96 -0
  280. package/dist/packages/admin.js.map +1 -0
  281. package/dist/packages/index.d.ts +6 -0
  282. package/dist/packages/index.d.ts.map +1 -0
  283. package/dist/packages/index.js +6 -0
  284. package/dist/packages/index.js.map +1 -0
  285. package/dist/packages/inspect.d.ts +11 -0
  286. package/dist/packages/inspect.d.ts.map +1 -0
  287. package/dist/packages/inspect.js +100 -0
  288. package/dist/packages/inspect.js.map +1 -0
  289. package/dist/packages/materialize.d.ts +39 -0
  290. package/dist/packages/materialize.d.ts.map +1 -0
  291. package/dist/packages/materialize.js +300 -0
  292. package/dist/packages/materialize.js.map +1 -0
  293. package/dist/packages/resolve.d.ts +25 -0
  294. package/dist/packages/resolve.d.ts.map +1 -0
  295. package/dist/packages/resolve.js +212 -0
  296. package/dist/packages/resolve.js.map +1 -0
  297. package/dist/packages/source.d.ts +33 -0
  298. package/dist/packages/source.d.ts.map +1 -0
  299. package/dist/packages/source.js +177 -0
  300. package/dist/packages/source.js.map +1 -0
  301. package/dist/packages/types.d.ts +104 -0
  302. package/dist/packages/types.d.ts.map +1 -0
  303. package/dist/packages/types.js +2 -0
  304. package/dist/packages/types.js.map +1 -0
  305. package/dist/provisioner.d.ts.map +1 -1
  306. package/dist/provisioner.js +6 -6
  307. package/dist/provisioner.js.map +1 -1
  308. package/dist/runtime/conversation-runtime.d.ts +1 -1
  309. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  310. package/dist/runtime/conversation-runtime.js +481 -98
  311. package/dist/runtime/conversation-runtime.js.map +1 -1
  312. package/dist/runtime/session-lifecycle.d.ts +28 -0
  313. package/dist/runtime/session-lifecycle.d.ts.map +1 -0
  314. package/dist/runtime/session-lifecycle.js +161 -0
  315. package/dist/runtime/session-lifecycle.js.map +1 -0
  316. package/dist/runtime/types.d.ts +45 -5
  317. package/dist/runtime/types.d.ts.map +1 -1
  318. package/dist/runtime/types.js.map +1 -1
  319. package/dist/sandbox/cloudflare.d.ts +3 -0
  320. package/dist/sandbox/cloudflare.d.ts.map +1 -1
  321. package/dist/sandbox/cloudflare.js +11 -0
  322. package/dist/sandbox/cloudflare.js.map +1 -1
  323. package/dist/sandbox/container.d.ts +4 -0
  324. package/dist/sandbox/container.d.ts.map +1 -1
  325. package/dist/sandbox/container.js +12 -2
  326. package/dist/sandbox/container.js.map +1 -1
  327. package/dist/sandbox/firecracker.d.ts +3 -0
  328. package/dist/sandbox/firecracker.d.ts.map +1 -1
  329. package/dist/sandbox/firecracker.js +11 -1
  330. package/dist/sandbox/firecracker.js.map +1 -1
  331. package/dist/sandbox/gondolin.d.ts +57 -0
  332. package/dist/sandbox/gondolin.d.ts.map +1 -0
  333. package/dist/sandbox/gondolin.js +764 -0
  334. package/dist/sandbox/gondolin.js.map +1 -0
  335. package/dist/sandbox/host.d.ts +4 -0
  336. package/dist/sandbox/host.d.ts.map +1 -1
  337. package/dist/sandbox/host.js +52 -0
  338. package/dist/sandbox/host.js.map +1 -1
  339. package/dist/sandbox/identity.d.ts +16 -0
  340. package/dist/sandbox/identity.d.ts.map +1 -0
  341. package/dist/sandbox/identity.js +42 -0
  342. package/dist/sandbox/identity.js.map +1 -0
  343. package/dist/sandbox/image.d.ts.map +1 -1
  344. package/dist/sandbox/image.js +1 -0
  345. package/dist/sandbox/image.js.map +1 -1
  346. package/dist/sandbox/index.d.ts +15 -4
  347. package/dist/sandbox/index.d.ts.map +1 -1
  348. package/dist/sandbox/index.js +25 -3
  349. package/dist/sandbox/index.js.map +1 -1
  350. package/dist/sandbox/path-context.js +18 -8
  351. package/dist/sandbox/path-context.js.map +1 -1
  352. package/dist/sandbox/types.d.ts +65 -1
  353. package/dist/sandbox/types.d.ts.map +1 -1
  354. package/dist/sandbox/types.js.map +1 -1
  355. package/dist/sandbox/utils.d.ts +12 -0
  356. package/dist/sandbox/utils.d.ts.map +1 -1
  357. package/dist/sandbox/utils.js +38 -0
  358. package/dist/sandbox/utils.js.map +1 -1
  359. package/dist/sessions/chat-history-sync.d.ts +25 -0
  360. package/dist/sessions/chat-history-sync.d.ts.map +1 -0
  361. package/dist/sessions/{agent-memory-file-manager.js → chat-history-sync.js} +98 -124
  362. package/dist/sessions/chat-history-sync.js.map +1 -0
  363. package/dist/sessions/conversation-log.d.ts +9 -0
  364. package/dist/sessions/conversation-log.d.ts.map +1 -0
  365. package/dist/sessions/conversation-log.js +47 -0
  366. package/dist/sessions/conversation-log.js.map +1 -0
  367. package/dist/sessions/history-line.d.ts +20 -0
  368. package/dist/sessions/history-line.d.ts.map +1 -0
  369. package/dist/sessions/history-line.js +24 -0
  370. package/dist/sessions/history-line.js.map +1 -0
  371. package/dist/sessions/policy.d.ts.map +1 -1
  372. package/dist/sessions/policy.js +5 -3
  373. package/dist/sessions/policy.js.map +1 -1
  374. package/dist/sessions/rotation.d.ts +2 -0
  375. package/dist/sessions/rotation.d.ts.map +1 -0
  376. package/dist/sessions/rotation.js +39 -0
  377. package/dist/sessions/rotation.js.map +1 -0
  378. package/dist/sessions/session-key.d.ts +40 -0
  379. package/dist/sessions/session-key.d.ts.map +1 -0
  380. package/dist/sessions/session-key.js +92 -0
  381. package/dist/sessions/session-key.js.map +1 -0
  382. package/dist/sessions/store.d.ts.map +1 -1
  383. package/dist/sessions/store.js +40 -18
  384. package/dist/sessions/store.js.map +1 -1
  385. package/dist/sessions/types.d.ts +17 -4
  386. package/dist/sessions/types.d.ts.map +1 -1
  387. package/dist/sessions/types.js.map +1 -1
  388. package/dist/settings-mutation.d.ts +8 -0
  389. package/dist/settings-mutation.d.ts.map +1 -0
  390. package/dist/settings-mutation.js +43 -0
  391. package/dist/settings-mutation.js.map +1 -0
  392. package/dist/subagent-progress.d.ts +43 -0
  393. package/dist/subagent-progress.d.ts.map +1 -0
  394. package/dist/subagent-progress.js +220 -0
  395. package/dist/subagent-progress.js.map +1 -0
  396. package/dist/tools/bash.d.ts +3 -1
  397. package/dist/tools/bash.d.ts.map +1 -1
  398. package/dist/tools/bash.js +25 -17
  399. package/dist/tools/bash.js.map +1 -1
  400. package/dist/tools/edit.d.ts.map +1 -1
  401. package/dist/tools/edit.js +10 -12
  402. package/dist/tools/edit.js.map +1 -1
  403. package/dist/tools/event.d.ts +1 -1
  404. package/dist/tools/event.d.ts.map +1 -1
  405. package/dist/tools/event.js +53 -43
  406. package/dist/tools/event.js.map +1 -1
  407. package/dist/tools/generate-image.d.ts +17 -0
  408. package/dist/tools/generate-image.d.ts.map +1 -0
  409. package/dist/tools/generate-image.js +63 -0
  410. package/dist/tools/generate-image.js.map +1 -0
  411. package/dist/tools/host-fn-tool.d.ts +21 -0
  412. package/dist/tools/host-fn-tool.d.ts.map +1 -0
  413. package/dist/tools/host-fn-tool.js +32 -0
  414. package/dist/tools/host-fn-tool.js.map +1 -0
  415. package/dist/tools/index.d.ts +14 -2
  416. package/dist/tools/index.d.ts.map +1 -1
  417. package/dist/tools/index.js +28 -3
  418. package/dist/tools/index.js.map +1 -1
  419. package/dist/tools/react.d.ts +15 -0
  420. package/dist/tools/react.d.ts.map +1 -0
  421. package/dist/tools/react.js +29 -0
  422. package/dist/tools/react.js.map +1 -0
  423. package/dist/tools/read.d.ts +1 -0
  424. package/dist/tools/read.d.ts.map +1 -1
  425. package/dist/tools/read.js +37 -31
  426. package/dist/tools/read.js.map +1 -1
  427. package/dist/tools/sandbox.d.ts +2 -2
  428. package/dist/tools/sandbox.d.ts.map +1 -1
  429. package/dist/tools/sandbox.js +14 -9
  430. package/dist/tools/sandbox.js.map +1 -1
  431. package/dist/tools/subagent-slots.d.ts +28 -0
  432. package/dist/tools/subagent-slots.d.ts.map +1 -0
  433. package/dist/tools/subagent-slots.js +72 -0
  434. package/dist/tools/subagent-slots.js.map +1 -0
  435. package/dist/tools/subagent.d.ts +54 -0
  436. package/dist/tools/subagent.d.ts.map +1 -0
  437. package/dist/tools/subagent.js +401 -0
  438. package/dist/tools/subagent.js.map +1 -0
  439. package/dist/tools/types.d.ts +40 -6
  440. package/dist/tools/types.d.ts.map +1 -1
  441. package/dist/tools/types.js.map +1 -1
  442. package/dist/tools/write.d.ts.map +1 -1
  443. package/dist/tools/write.js +2 -8
  444. package/dist/tools/write.js.map +1 -1
  445. package/dist/trigger.d.ts.map +1 -1
  446. package/dist/trigger.js +1 -2
  447. package/dist/trigger.js.map +1 -1
  448. package/dist/types.d.ts +232 -32
  449. package/dist/types.d.ts.map +1 -1
  450. package/dist/types.js.map +1 -1
  451. package/dist/utils/date.d.ts +1 -1
  452. package/dist/utils/date.js +1 -1
  453. package/dist/utils/date.js.map +1 -1
  454. package/dist/vault/disabled.d.ts +9 -0
  455. package/dist/vault/disabled.d.ts.map +1 -0
  456. package/dist/vault/disabled.js +22 -0
  457. package/dist/vault/disabled.js.map +1 -0
  458. package/dist/vault/index.d.ts +0 -2
  459. package/dist/vault/index.d.ts.map +1 -1
  460. package/dist/vault/index.js +30 -33
  461. package/dist/vault/index.js.map +1 -1
  462. package/dist/vault/injection.d.ts +10 -0
  463. package/dist/vault/injection.d.ts.map +1 -0
  464. package/dist/vault/injection.js +30 -0
  465. package/dist/vault/injection.js.map +1 -0
  466. package/dist/vault/policy.d.ts +20 -0
  467. package/dist/vault/policy.d.ts.map +1 -0
  468. package/dist/vault/policy.js +20 -0
  469. package/dist/vault/policy.js.map +1 -0
  470. package/dist/vault/types.d.ts +4 -2
  471. package/dist/vault/types.d.ts.map +1 -1
  472. package/dist/vault/types.js.map +1 -1
  473. package/dist/web/admin/portal.d.ts +17 -2
  474. package/dist/web/admin/portal.d.ts.map +1 -1
  475. package/dist/web/admin/portal.js +385 -155
  476. package/dist/web/admin/portal.js.map +1 -1
  477. package/dist/web/admin/types.d.ts +18 -0
  478. package/dist/web/admin/types.d.ts.map +1 -1
  479. package/dist/web/admin/types.js.map +1 -1
  480. package/dist/web/agent-events/portal.d.ts.map +1 -1
  481. package/dist/web/agent-events/portal.js +2 -1
  482. package/dist/web/agent-events/portal.js.map +1 -1
  483. package/dist/web/login/oauth.d.ts +2 -3
  484. package/dist/web/login/oauth.d.ts.map +1 -1
  485. package/dist/web/login/oauth.js +0 -31
  486. package/dist/web/login/oauth.js.map +1 -1
  487. package/dist/web/login/portal.js +1 -1
  488. package/dist/web/login/portal.js.map +1 -1
  489. package/dist/web/login/types.d.ts +0 -11
  490. package/dist/web/login/types.d.ts.map +1 -1
  491. package/dist/web/login/types.js.map +1 -1
  492. package/dist/web/portal-shell.d.ts +4 -0
  493. package/dist/web/portal-shell.d.ts.map +1 -0
  494. package/dist/{portal-shell.js → web/portal-shell.js} +2 -2
  495. package/dist/web/portal-shell.js.map +1 -0
  496. package/dist/web/server.d.ts.map +1 -1
  497. package/dist/web/server.js +7 -0
  498. package/dist/web/server.js.map +1 -1
  499. package/dist/web/session-view/command.d.ts.map +1 -1
  500. package/dist/web/session-view/command.js +4 -1
  501. package/dist/web/session-view/command.js.map +1 -1
  502. package/dist/web/session-view/portal.d.ts.map +1 -1
  503. package/dist/web/session-view/portal.js +4 -3
  504. package/dist/web/session-view/portal.js.map +1 -1
  505. package/dist/web/session-view/service.d.ts.map +1 -1
  506. package/dist/web/session-view/service.js +2 -1
  507. package/dist/web/session-view/service.js.map +1 -1
  508. package/dist/web/session-view/types.d.ts +2 -1
  509. package/dist/web/session-view/types.d.ts.map +1 -1
  510. package/dist/web/session-view/types.js.map +1 -1
  511. package/dist/workspace-projection/index.d.ts +5 -0
  512. package/dist/workspace-projection/index.d.ts.map +1 -0
  513. package/dist/workspace-projection/index.js +71 -0
  514. package/dist/workspace-projection/index.js.map +1 -0
  515. package/dist/workspace-projection/types.d.ts +7 -0
  516. package/dist/workspace-projection/types.d.ts.map +1 -0
  517. package/dist/workspace-projection/types.js +2 -0
  518. package/dist/workspace-projection/types.js.map +1 -0
  519. package/package.json +28 -15
  520. package/dist/adapters/streaming.d.ts +0 -18
  521. package/dist/adapters/streaming.d.ts.map +0 -1
  522. package/dist/adapters/streaming.js +0 -44
  523. package/dist/adapters/streaming.js.map +0 -1
  524. package/dist/download.d.ts.map +0 -1
  525. package/dist/download.js.map +0 -1
  526. package/dist/model-registry.d.ts +0 -4
  527. package/dist/model-registry.d.ts.map +0 -1
  528. package/dist/model-registry.js +0 -7
  529. package/dist/model-registry.js.map +0 -1
  530. package/dist/portal-shell.d.ts +0 -4
  531. package/dist/portal-shell.d.ts.map +0 -1
  532. package/dist/portal-shell.js.map +0 -1
  533. package/dist/runtime/agent-run-controller.d.ts +0 -31
  534. package/dist/runtime/agent-run-controller.d.ts.map +0 -1
  535. package/dist/runtime/agent-run-controller.js +0 -195
  536. package/dist/runtime/agent-run-controller.js.map +0 -1
  537. package/dist/sessions/agent-memory-file-manager.d.ts +0 -19
  538. package/dist/sessions/agent-memory-file-manager.d.ts.map +0 -1
  539. package/dist/sessions/agent-memory-file-manager.js.map +0 -1
  540. package/dist/vault/routing.d.ts +0 -3
  541. package/dist/vault/routing.d.ts.map +0 -1
  542. package/dist/vault/routing.js +0 -13
  543. package/dist/vault/routing.js.map +0 -1
  544. /package/dist/{download.d.ts → cli/download.d.ts} +0 -0
  545. /package/dist/{download.js → cli/download.js} +0 -0
@@ -1,12 +1,12 @@
1
1
  import type { AgentMessage } from "@earendil-works/pi-agent-core";
2
2
  import type { ImageContent, TextContent } from "@earendil-works/pi-ai";
3
3
  import { type SessionContext, type SessionEntry, type SessionFileEntry, type SessionHeader } from "./types.js";
4
- /** Parse JSONL content tolerantly: malformed lines are skipped. */
4
+ /** Parse JSONL content tolerantly for compatibility with existing read-only callers. */
5
5
  export declare function parseSessionFileEntries(content: string): SessionFileEntry[];
6
6
  /**
7
- * Load a session file. Returns no entries when the file is missing or its
8
- * first parseable line is not a valid session header (mirrors the tolerant
9
- * read behavior mikan has relied on). Throws only on I/O errors.
7
+ * Load and validate a session file. Missing and whitespace-only files return
8
+ * no entries. A malformed final non-empty line is ignored as a crash tail;
9
+ * all other malformed JSON and invalid tree relationships throw.
10
10
  */
11
11
  export declare function loadSessionFileEntries(filePath: string): SessionFileEntry[];
12
12
  /**
@@ -22,6 +22,7 @@ export declare class SessionStore {
22
22
  private readonly sessionId;
23
23
  private header;
24
24
  private headerOnDisk;
25
+ private rewriteBeforeAppend;
25
26
  private entries;
26
27
  private byId;
27
28
  private leafId;
@@ -39,7 +40,10 @@ export declare class SessionStore {
39
40
  static create(path: string, cwd: string, options?: {
40
41
  id?: string;
41
42
  }): SessionStore;
42
- getSessionFile(): string;
43
+ /** Create an ephemeral session that never writes a session file. */
44
+ static inMemory(cwd?: string): SessionStore;
45
+ getSessionFile(): string | undefined;
46
+ isPersisted(): boolean;
43
47
  getCwd(): string;
44
48
  /** Session header as read from disk, or null when the file had none. */
45
49
  getHeader(): SessionHeader | null;
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEvE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,aAAa,EACnB,MAAM,YAAY,CAAC;AAkBpB,mEAAmE;AACnE,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAmB3E;AAMD;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,EAAE,CAM3E;AAQD;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,YAAY,CAAU;IAC9B,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,IAAI,CAAmC;IAC/C,OAAO,CAAC,MAAM,CAAuB;IAErC,OAAO,eAiBN;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,YAAY,CAW5D;IAED,kFAAkF;IAClF,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY,CAUhF;IAED,cAAc,IAAI,MAAM,CAEvB;IAED,MAAM,IAAI,MAAM,CAEf;IAED,wEAAwE;IACxE,SAAS,IAAI,aAAa,GAAG,IAAI,CAEhC;IAED,8EAA8E;IAC9E,YAAY,IAAI,MAAM,CAErB;IAED,SAAS,IAAI,MAAM,GAAG,IAAI,CAEzB;IAED,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE7C;IAED,qEAAqE;IACrE,UAAU,IAAI,YAAY,EAAE,CAE3B;IAED,iDAAiD;IACjD,cAAc,IAAI,MAAM,GAAG,SAAS,CAMnC;IAED;;;OAGG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,EAAE,CAUzC;IAED;;;OAGG;IACH,mBAAmB,IAAI,cAAc,CAEpC;IAED,kFAAkF;IAClF,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,MAAM,CAE3C;IAED,0EAA0E;IAC1E,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,CAE5D;IAED,sEAAsE;IACtE,wBAAwB,CACtB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,EAChD,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,OAAO,GAChB,MAAM,CAER;IAED,0CAA0C;IAC1C,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEtC;IAED,8DAA8D;IAC9D,gBAAgB,CACd,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,EACxB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,OAAO,EACjB,QAAQ,CAAC,EAAE,OAAO,GACjB,MAAM,CASR;IAED,OAAO,CAAC,WAAW;IAcnB,OAAO,CAAC,OAAO;CAiBhB"}
1
+ {"version":3,"file":"session-store.d.ts","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"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"}
@@ -26,7 +26,7 @@ function generateEntryId(byId) {
26
26
  }
27
27
  return randomUUID();
28
28
  }
29
- /** Parse JSONL content tolerantly: malformed lines are skipped. */
29
+ /** Parse JSONL content tolerantly for compatibility with existing read-only callers. */
30
30
  export function parseSessionFileEntries(content) {
31
31
  const entries = [];
32
32
  for (const line of content.split("\n")) {
@@ -42,7 +42,7 @@ export function parseSessionFileEntries(content) {
42
42
  }
43
43
  }
44
44
  catch {
45
- // Skip malformed lines
45
+ // This compatibility parser is intentionally tolerant. SessionStore.open is not.
46
46
  }
47
47
  }
48
48
  return entries;
@@ -50,26 +50,120 @@ export function parseSessionFileEntries(content) {
50
50
  function isSessionHeader(entry) {
51
51
  return entry.type === "session" && typeof entry.id === "string";
52
52
  }
53
- /**
54
- * Load a session file. Returns no entries when the file is missing or its
55
- * first parseable line is not a valid session header (mirrors the tolerant
56
- * read behavior mikan has relied on). Throws only on I/O errors.
57
- */
58
- export function loadSessionFileEntries(filePath) {
53
+ function corrupted(filePath, detail) {
54
+ return new Error(`Session file is corrupted (${detail}): ${filePath}. Refusing to open for writing.`);
55
+ }
56
+ function parseSessionLine(line, filePath, lineNumber) {
57
+ let parsed;
58
+ try {
59
+ parsed = JSON.parse(line);
60
+ }
61
+ catch {
62
+ throw corrupted(filePath, `line ${lineNumber} is not valid JSON`);
63
+ }
64
+ if (!parsed ||
65
+ typeof parsed !== "object" ||
66
+ typeof parsed.type !== "string") {
67
+ throw corrupted(filePath, `line ${lineNumber} is not a session object`);
68
+ }
69
+ return parsed;
70
+ }
71
+ function validateSessionTree(filePath, fileEntries) {
72
+ const records = fileEntries.slice(1);
73
+ const allIds = new Set();
74
+ const treeEntries = new Map();
75
+ for (const [index, record] of records.entries()) {
76
+ const lineNumber = index + 2;
77
+ if (isSessionHeader(record))
78
+ throw corrupted(filePath, `line ${lineNumber} repeats the header`);
79
+ const entry = record;
80
+ if (typeof entry.id !== "string" || !entry.id) {
81
+ throw corrupted(filePath, `line ${lineNumber} has no entry id`);
82
+ }
83
+ if (allIds.has(entry.id))
84
+ throw corrupted(filePath, `duplicate entry id ${entry.id}`);
85
+ allIds.add(entry.id);
86
+ if (entry.type !== "leaf")
87
+ treeEntries.set(entry.id, entry);
88
+ }
89
+ for (const [index, record] of records.entries()) {
90
+ const lineNumber = index + 2;
91
+ const entry = record;
92
+ if (entry.parentId !== null && typeof entry.parentId !== "string") {
93
+ throw corrupted(filePath, `line ${lineNumber} has an invalid parentId`);
94
+ }
95
+ if (entry.parentId === entry.id)
96
+ throw corrupted(filePath, `entry ${entry.id} is its own parent`);
97
+ if (entry.parentId !== null && !treeEntries.has(entry.parentId)) {
98
+ throw corrupted(filePath, `entry ${entry.id} references missing parent ${entry.parentId}`);
99
+ }
100
+ if (entry.type === "leaf") {
101
+ if (entry.targetId !== null && typeof entry.targetId !== "string") {
102
+ throw corrupted(filePath, `leaf ${entry.id} has an invalid target`);
103
+ }
104
+ if (entry.targetId !== null && !treeEntries.has(entry.targetId)) {
105
+ throw corrupted(filePath, `leaf ${entry.id} references missing target ${entry.targetId}`);
106
+ }
107
+ }
108
+ }
109
+ for (const entry of treeEntries.values()) {
110
+ const seen = new Set();
111
+ let current = entry;
112
+ while (current) {
113
+ if (seen.has(current.id))
114
+ throw corrupted(filePath, `parent cycle includes entry ${current.id}`);
115
+ seen.add(current.id);
116
+ current = current.parentId === null ? undefined : treeEntries.get(current.parentId);
117
+ }
118
+ }
119
+ for (const entry of treeEntries.values()) {
120
+ if (entry.type !== "compaction")
121
+ continue;
122
+ if (typeof entry.firstKeptEntryId !== "string" || !treeEntries.has(entry.firstKeptEntryId)) {
123
+ throw corrupted(filePath, `compaction ${entry.id} references missing first kept entry`);
124
+ }
125
+ let ancestorId = entry.parentId;
126
+ while (ancestorId !== null && ancestorId !== entry.firstKeptEntryId) {
127
+ ancestorId = treeEntries.get(ancestorId)?.parentId ?? null;
128
+ }
129
+ if (ancestorId === null) {
130
+ throw corrupted(filePath, `compaction ${entry.id} first kept entry ${entry.firstKeptEntryId} is not on its branch`);
131
+ }
132
+ }
133
+ }
134
+ function loadValidatedSessionFile(filePath) {
59
135
  if (!existsSync(filePath))
60
- return [];
61
- const entries = parseSessionFileEntries(readFileSync(filePath, "utf-8"));
62
- if (entries.length === 0)
63
- return entries;
136
+ return { entries: [], discardedCrashTail: false };
137
+ const nonEmptyLines = readFileSync(filePath, "utf-8")
138
+ .split("\n")
139
+ .map((line, index) => ({ text: line.trim(), number: index + 1 }))
140
+ .filter(({ text }) => text.length > 0);
141
+ if (nonEmptyLines.length === 0)
142
+ return { entries: [], discardedCrashTail: false };
143
+ const entries = [];
144
+ let discardedCrashTail = false;
145
+ for (const [index, line] of nonEmptyLines.entries()) {
146
+ try {
147
+ entries.push(parseSessionLine(line.text, filePath, line.number));
148
+ }
149
+ catch (error) {
150
+ if (index !== nonEmptyLines.length - 1 || index === 0)
151
+ throw error;
152
+ discardedCrashTail = true;
153
+ }
154
+ }
64
155
  if (!isSessionHeader(entries[0]))
65
- return [];
66
- return entries;
156
+ throw corrupted(filePath, "first non-empty line is not a valid header");
157
+ validateSessionTree(filePath, entries);
158
+ return { entries, discardedCrashTail };
67
159
  }
68
- /** True when the file exists and holds any non-whitespace content on disk. */
69
- function hasSessionFileContent(filePath) {
70
- if (!existsSync(filePath))
71
- return false;
72
- return readFileSync(filePath, "utf-8").trim().length > 0;
160
+ /**
161
+ * Load and validate a session file. Missing and whitespace-only files return
162
+ * no entries. A malformed final non-empty line is ignored as a crash tail;
163
+ * all other malformed JSON and invalid tree relationships throw.
164
+ */
165
+ export function loadSessionFileEntries(filePath) {
166
+ return loadValidatedSessionFile(filePath).entries;
73
167
  }
74
168
  /**
75
169
  * Append-oriented session store over one v3 JSONL file.
@@ -79,13 +173,14 @@ function hasSessionFileContent(filePath) {
79
173
  * without a valid header gets its header materialized on the first append.
80
174
  */
81
175
  export class SessionStore {
82
- constructor(sessionFile, cwd, fileEntries) {
176
+ constructor(sessionFile, cwd, fileEntries, rewriteBeforeAppend = false) {
83
177
  this.entries = [];
84
178
  this.byId = new Map();
85
179
  this.leafId = null;
86
- this.sessionFile = resolve(sessionFile);
180
+ this.sessionFile = sessionFile === null ? null : resolve(sessionFile);
87
181
  this.header = fileEntries.length > 0 && isSessionHeader(fileEntries[0]) ? fileEntries[0] : null;
88
182
  this.headerOnDisk = this.header !== null;
183
+ this.rewriteBeforeAppend = rewriteBeforeAppend;
89
184
  this.sessionId = this.header?.id ?? randomUUID();
90
185
  this.cwd = cwd;
91
186
  for (const entry of fileEntries) {
@@ -110,14 +205,10 @@ export class SessionStore {
110
205
  * @param cwdOverride Working directory override; defaults to the header cwd.
111
206
  */
112
207
  static open(path, cwdOverride) {
113
- const fileEntries = loadSessionFileEntries(path);
114
- if (fileEntries.length === 0 && hasSessionFileContent(path)) {
115
- throw new Error(`Session file is corrupted (no valid session header): ${path}. ` +
116
- `Refusing to open to avoid overwriting existing content.`);
117
- }
118
- const header = fileEntries.find(isSessionHeader);
208
+ const loaded = loadValidatedSessionFile(path);
209
+ const header = loaded.entries.find(isSessionHeader);
119
210
  const cwd = cwdOverride ?? header?.cwd ?? process.cwd();
120
- return new SessionStore(path, cwd, fileEntries);
211
+ return new SessionStore(path, cwd, loaded.entries, loaded.discardedCrashTail);
121
212
  }
122
213
  /** Create a new session file with a fresh header, replacing any existing file. */
123
214
  static create(path, cwd, options) {
@@ -131,8 +222,15 @@ export class SessionStore {
131
222
  atomicWritePrivateFile(path, `${JSON.stringify(header)}\n`);
132
223
  return new SessionStore(path, cwd, [header]);
133
224
  }
225
+ /** Create an ephemeral session that never writes a session file. */
226
+ static inMemory(cwd = process.cwd()) {
227
+ return new SessionStore(null, cwd, []);
228
+ }
134
229
  getSessionFile() {
135
- return this.sessionFile;
230
+ return this.sessionFile ?? undefined;
231
+ }
232
+ isPersisted() {
233
+ return this.sessionFile !== null;
136
234
  }
137
235
  getCwd() {
138
236
  return this.cwd;
@@ -228,7 +326,9 @@ export class SessionStore {
228
326
  return entry.id;
229
327
  }
230
328
  persist(entry) {
231
- if (!this.headerOnDisk) {
329
+ if (this.sessionFile === null)
330
+ return;
331
+ if (!this.headerOnDisk || this.rewriteBeforeAppend) {
232
332
  this.header ??= {
233
333
  type: "session",
234
334
  version: CURRENT_SESSION_VERSION,
@@ -240,6 +340,7 @@ export class SessionStore {
240
340
  mkdirSync(dirname(this.sessionFile), { recursive: true });
241
341
  atomicWritePrivateFile(this.sessionFile, `${lines.join("\n")}\n`);
242
342
  this.headerOnDisk = true;
343
+ this.rewriteBeforeAppend = false;
243
344
  return;
244
345
  }
245
346
  appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`);
@@ -1 +1 @@
1
- {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * mikan-owned session storage.\n *\n * Sessions are append-only trees stored as JSONL files: a `session` header\n * line followed by entries that carry `id`/`parentId` links. The format is\n * the v3 layout mikan has always written, and entry shapes are structurally\n * identical to pi-agent-core's `SessionTreeEntry`, so pi-agent-core's\n * context-building and compaction helpers operate on these entries directly.\n *\n * The store is synchronous by design: every mikan call site (session scope\n * resolution, chat-history sync, session view, admin portal) reads and\n * writes sessions inside synchronous flows.\n */\nimport { randomUUID } from \"crypto\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { buildSessionContext as buildContextFromEntries } from \"@earendil-works/pi-agent-core\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { atomicWritePrivateFile } from \"../utils/fs-atomic.js\";\nimport {\n CURRENT_SESSION_VERSION,\n type SessionContext,\n type SessionEntry,\n type SessionFileEntry,\n type SessionHeader,\n} from \"./types.js\";\n\n/** Distributive omit so each entry variant keeps its own fields. */\ntype PendingSessionEntry = SessionEntry extends infer TEntry\n ? TEntry extends SessionEntry\n ? Omit<TEntry, \"id\" | \"parentId\" | \"timestamp\">\n : never\n : never;\n\n/** Generate a unique short entry id (8 hex chars, collision-checked). */\nfunction generateEntryId(byId: Map<string, SessionEntry>): string {\n for (let i = 0; i < 100; i++) {\n const id = randomUUID().slice(0, 8);\n if (!byId.has(id)) return id;\n }\n return randomUUID();\n}\n\n/** Parse JSONL content tolerantly: malformed lines are skipped. */\nexport function parseSessionFileEntries(content: string): SessionFileEntry[] {\n const entries: SessionFileEntry[] = [];\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (\n parsed &&\n typeof parsed === \"object\" &&\n typeof (parsed as { type?: unknown }).type === \"string\"\n ) {\n entries.push(parsed as SessionFileEntry);\n }\n } catch {\n // Skip malformed lines\n }\n }\n return entries;\n}\n\nfunction isSessionHeader(entry: SessionFileEntry): entry is SessionHeader {\n return entry.type === \"session\" && typeof (entry as SessionHeader).id === \"string\";\n}\n\n/**\n * Load a session file. Returns no entries when the file is missing or its\n * first parseable line is not a valid session header (mirrors the tolerant\n * read behavior mikan has relied on). Throws only on I/O errors.\n */\nexport function loadSessionFileEntries(filePath: string): SessionFileEntry[] {\n if (!existsSync(filePath)) return [];\n const entries = parseSessionFileEntries(readFileSync(filePath, \"utf-8\"));\n if (entries.length === 0) return entries;\n if (!isSessionHeader(entries[0])) return [];\n return entries;\n}\n\n/** True when the file exists and holds any non-whitespace content on disk. */\nfunction hasSessionFileContent(filePath: string): boolean {\n if (!existsSync(filePath)) return false;\n return readFileSync(filePath, \"utf-8\").trim().length > 0;\n}\n\n/**\n * Append-oriented session store over one v3 JSONL file.\n *\n * Reading is tolerant (missing files and malformed lines yield an empty\n * session); appends persist immediately. A session opened from a file\n * without a valid header gets its header materialized on the first append.\n */\nexport class SessionStore {\n private readonly sessionFile: string;\n private readonly cwd: string;\n private readonly sessionId: string;\n private header: SessionHeader | null;\n private headerOnDisk: boolean;\n private entries: SessionEntry[] = [];\n private byId = new Map<string, SessionEntry>();\n private leafId: string | null = null;\n\n private constructor(sessionFile: string, cwd: string, fileEntries: SessionFileEntry[]) {\n this.sessionFile = resolve(sessionFile);\n this.header = fileEntries.length > 0 && isSessionHeader(fileEntries[0]) ? fileEntries[0] : null;\n this.headerOnDisk = this.header !== null;\n this.sessionId = this.header?.id ?? randomUUID();\n this.cwd = cwd;\n for (const entry of fileEntries) {\n if (isSessionHeader(entry)) continue;\n if (entry.type === \"leaf\") {\n // Written by pi-agent-core JSONL sessions; mikan tracks the leaf implicitly.\n this.leafId = entry.targetId;\n continue;\n }\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n }\n }\n\n /**\n * Open a session file. A missing or empty file starts an empty session\n * whose header is written on the first append. A file that has content but\n * no leading session header is treated as corrupted and throws, rather than\n * being silently overwritten on the next append (which would erase the\n * existing history). Callers on read paths already wrap this in try/catch.\n * @param cwdOverride Working directory override; defaults to the header cwd.\n */\n static open(path: string, cwdOverride?: string): SessionStore {\n const fileEntries = loadSessionFileEntries(path);\n if (fileEntries.length === 0 && hasSessionFileContent(path)) {\n throw new Error(\n `Session file is corrupted (no valid session header): ${path}. ` +\n `Refusing to open to avoid overwriting existing content.`,\n );\n }\n const header = fileEntries.find(isSessionHeader);\n const cwd = cwdOverride ?? header?.cwd ?? process.cwd();\n return new SessionStore(path, cwd, fileEntries);\n }\n\n /** Create a new session file with a fresh header, replacing any existing file. */\n static create(path: string, cwd: string, options?: { id?: string }): SessionStore {\n const header: SessionHeader = {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: options?.id ?? randomUUID(),\n timestamp: new Date().toISOString(),\n cwd,\n };\n atomicWritePrivateFile(path, `${JSON.stringify(header)}\\n`);\n return new SessionStore(path, cwd, [header]);\n }\n\n getSessionFile(): string {\n return this.sessionFile;\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n /** Session header as read from disk, or null when the file had none. */\n getHeader(): SessionHeader | null {\n return this.header;\n }\n\n /** Stable session id: the header id, or a generated id for fresh sessions. */\n getSessionId(): string {\n return this.sessionId;\n }\n\n getLeafId(): string | null {\n return this.leafId;\n }\n\n getEntry(id: string): SessionEntry | undefined {\n return this.byId.get(id);\n }\n\n /** All session entries (header excluded). Returns a shallow copy. */\n getEntries(): SessionEntry[] {\n return [...this.entries];\n }\n\n /** Latest user-assigned session name, if any. */\n getSessionName(): string | undefined {\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const entry = this.entries[i];\n if (entry.type === \"session_info\") return entry.name?.trim() || undefined;\n }\n return undefined;\n }\n\n /**\n * Walk from an entry to the root, returning entries in path order\n * (root first). Defaults to the current leaf.\n */\n getBranch(fromId?: string): SessionEntry[] {\n const path: SessionEntry[] = [];\n let currentId = fromId ?? this.leafId;\n while (currentId) {\n const entry = this.byId.get(currentId);\n if (!entry) break;\n path.push(entry);\n currentId = entry.parentId;\n }\n return path.toReversed();\n }\n\n /**\n * Build the LLM context (messages, thinking level, model) from the current\n * branch, resolving compaction and branch summaries along the path.\n */\n buildSessionContext(): SessionContext {\n return buildContextFromEntries(this.getBranch());\n }\n\n /** Append a chat message as a child of the current leaf. Returns the entry id. */\n appendMessage(message: AgentMessage): string {\n return this.appendEntry({ type: \"message\", message });\n }\n\n /** Append an extension/application data entry (never sent to the LLM). */\n appendCustomEntry(customType: string, data?: unknown): string {\n return this.appendEntry({ type: \"custom\", customType, data });\n }\n\n /** Append a custom message entry that participates in LLM context. */\n appendCustomMessageEntry(\n customType: string,\n content: string | (TextContent | ImageContent)[],\n display: boolean,\n details?: unknown,\n ): string {\n return this.appendEntry({ type: \"custom_message\", customType, content, display, details });\n }\n\n /** Record a user-visible session name. */\n appendSessionInfo(name: string): string {\n return this.appendEntry({ type: \"session_info\", name });\n }\n\n /** Persist a compaction summary produced for this session. */\n appendCompaction(\n summary: string,\n firstKeptEntryId: string,\n tokensBefore: number,\n details?: unknown,\n fromHook?: boolean,\n ): string {\n return this.appendEntry({\n type: \"compaction\",\n summary,\n firstKeptEntryId,\n tokensBefore,\n ...(details !== undefined ? { details } : {}),\n ...(fromHook !== undefined ? { fromHook } : {}),\n });\n }\n\n private appendEntry(partial: PendingSessionEntry): string {\n const entry = {\n ...partial,\n id: generateEntryId(this.byId),\n parentId: this.leafId,\n timestamp: new Date().toISOString(),\n } as SessionEntry;\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n this.persist(entry);\n return entry.id;\n }\n\n private persist(entry: SessionEntry): void {\n if (!this.headerOnDisk) {\n this.header ??= {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: this.sessionId,\n timestamp: new Date().toISOString(),\n cwd: this.cwd,\n };\n const lines = [this.header, ...this.entries].map((line) => JSON.stringify(line));\n mkdirSync(dirname(this.sessionFile), { recursive: true });\n atomicWritePrivateFile(this.sessionFile, `${lines.join(\"\\n\")}\\n`);\n this.headerOnDisk = true;\n return;\n }\n appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\\n`);\n }\n}\n"]}
1
+ {"version":3,"file":"session-store.js","sourceRoot":"","sources":["../../src/harness/session-store.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * mikan-owned session storage.\n *\n * Sessions are append-only trees stored as JSONL files: a `session` header\n * line followed by entries that carry `id`/`parentId` links. The format is\n * the v3 layout mikan has always written, and entry shapes are structurally\n * identical to pi-agent-core's `SessionTreeEntry`, so pi-agent-core's\n * context-building and compaction helpers operate on these entries directly.\n *\n * The store is synchronous by design: every mikan call site (session scope\n * resolution, chat-history sync, session view, admin portal) reads and\n * writes sessions inside synchronous flows.\n */\nimport { randomUUID } from \"crypto\";\nimport { appendFileSync, existsSync, mkdirSync, readFileSync } from \"fs\";\nimport { dirname, resolve } from \"path\";\nimport { buildSessionContext as buildContextFromEntries } from \"@earendil-works/pi-agent-core\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { ImageContent, TextContent } from \"@earendil-works/pi-ai\";\nimport { atomicWritePrivateFile } from \"../utils/fs-atomic.js\";\nimport {\n CURRENT_SESSION_VERSION,\n type SessionContext,\n type SessionEntry,\n type SessionFileEntry,\n type SessionHeader,\n} from \"./types.js\";\n\n/** Distributive omit so each entry variant keeps its own fields. */\ntype PendingSessionEntry = SessionEntry extends infer TEntry\n ? TEntry extends SessionEntry\n ? Omit<TEntry, \"id\" | \"parentId\" | \"timestamp\">\n : never\n : never;\n\n/** Generate a unique short entry id (8 hex chars, collision-checked). */\nfunction generateEntryId(byId: Map<string, SessionEntry>): string {\n for (let i = 0; i < 100; i++) {\n const id = randomUUID().slice(0, 8);\n if (!byId.has(id)) return id;\n }\n return randomUUID();\n}\n\n/** Parse JSONL content tolerantly for compatibility with existing read-only callers. */\nexport function parseSessionFileEntries(content: string): SessionFileEntry[] {\n const entries: SessionFileEntry[] = [];\n for (const line of content.split(\"\\n\")) {\n const trimmed = line.trim();\n if (!trimmed) continue;\n try {\n const parsed: unknown = JSON.parse(trimmed);\n if (\n parsed &&\n typeof parsed === \"object\" &&\n typeof (parsed as { type?: unknown }).type === \"string\"\n ) {\n entries.push(parsed as SessionFileEntry);\n }\n } catch {\n // This compatibility parser is intentionally tolerant. SessionStore.open is not.\n }\n }\n return entries;\n}\n\nfunction isSessionHeader(entry: SessionFileEntry): entry is SessionHeader {\n return entry.type === \"session\" && typeof (entry as SessionHeader).id === \"string\";\n}\n\ntype LoadedSessionFile = {\n entries: SessionFileEntry[];\n discardedCrashTail: boolean;\n};\n\nfunction corrupted(filePath: string, detail: string): Error {\n return new Error(\n `Session file is corrupted (${detail}): ${filePath}. Refusing to open for writing.`,\n );\n}\n\nfunction parseSessionLine(line: string, filePath: string, lineNumber: number): SessionFileEntry {\n let parsed: unknown;\n try {\n parsed = JSON.parse(line);\n } catch {\n throw corrupted(filePath, `line ${lineNumber} is not valid JSON`);\n }\n if (\n !parsed ||\n typeof parsed !== \"object\" ||\n typeof (parsed as { type?: unknown }).type !== \"string\"\n ) {\n throw corrupted(filePath, `line ${lineNumber} is not a session object`);\n }\n return parsed as SessionFileEntry;\n}\n\nfunction validateSessionTree(filePath: string, fileEntries: SessionFileEntry[]): void {\n const records = fileEntries.slice(1);\n const allIds = new Set<string>();\n const treeEntries = new Map<string, SessionEntry>();\n\n for (const [index, record] of records.entries()) {\n const lineNumber = index + 2;\n if (isSessionHeader(record)) throw corrupted(filePath, `line ${lineNumber} repeats the header`);\n const entry = record as SessionEntry;\n if (typeof entry.id !== \"string\" || !entry.id) {\n throw corrupted(filePath, `line ${lineNumber} has no entry id`);\n }\n if (allIds.has(entry.id)) throw corrupted(filePath, `duplicate entry id ${entry.id}`);\n allIds.add(entry.id);\n if (entry.type !== \"leaf\") treeEntries.set(entry.id, entry);\n }\n\n for (const [index, record] of records.entries()) {\n const lineNumber = index + 2;\n const entry = record as SessionEntry;\n if (entry.parentId !== null && typeof entry.parentId !== \"string\") {\n throw corrupted(filePath, `line ${lineNumber} has an invalid parentId`);\n }\n if (entry.parentId === entry.id)\n throw corrupted(filePath, `entry ${entry.id} is its own parent`);\n if (entry.parentId !== null && !treeEntries.has(entry.parentId)) {\n throw corrupted(filePath, `entry ${entry.id} references missing parent ${entry.parentId}`);\n }\n if (entry.type === \"leaf\") {\n if (entry.targetId !== null && typeof entry.targetId !== \"string\") {\n throw corrupted(filePath, `leaf ${entry.id} has an invalid target`);\n }\n if (entry.targetId !== null && !treeEntries.has(entry.targetId)) {\n throw corrupted(filePath, `leaf ${entry.id} references missing target ${entry.targetId}`);\n }\n }\n }\n\n for (const entry of treeEntries.values()) {\n const seen = new Set<string>();\n let current: SessionEntry | undefined = entry;\n while (current) {\n if (seen.has(current.id))\n throw corrupted(filePath, `parent cycle includes entry ${current.id}`);\n seen.add(current.id);\n current = current.parentId === null ? undefined : treeEntries.get(current.parentId);\n }\n }\n\n for (const entry of treeEntries.values()) {\n if (entry.type !== \"compaction\") continue;\n if (typeof entry.firstKeptEntryId !== \"string\" || !treeEntries.has(entry.firstKeptEntryId)) {\n throw corrupted(filePath, `compaction ${entry.id} references missing first kept entry`);\n }\n let ancestorId = entry.parentId;\n while (ancestorId !== null && ancestorId !== entry.firstKeptEntryId) {\n ancestorId = treeEntries.get(ancestorId)?.parentId ?? null;\n }\n if (ancestorId === null) {\n throw corrupted(\n filePath,\n `compaction ${entry.id} first kept entry ${entry.firstKeptEntryId} is not on its branch`,\n );\n }\n }\n}\n\nfunction loadValidatedSessionFile(filePath: string): LoadedSessionFile {\n if (!existsSync(filePath)) return { entries: [], discardedCrashTail: false };\n const nonEmptyLines = readFileSync(filePath, \"utf-8\")\n .split(\"\\n\")\n .map((line, index) => ({ text: line.trim(), number: index + 1 }))\n .filter(({ text }) => text.length > 0);\n if (nonEmptyLines.length === 0) return { entries: [], discardedCrashTail: false };\n\n const entries: SessionFileEntry[] = [];\n let discardedCrashTail = false;\n for (const [index, line] of nonEmptyLines.entries()) {\n try {\n entries.push(parseSessionLine(line.text, filePath, line.number));\n } catch (error) {\n if (index !== nonEmptyLines.length - 1 || index === 0) throw error;\n discardedCrashTail = true;\n }\n }\n if (!isSessionHeader(entries[0]))\n throw corrupted(filePath, \"first non-empty line is not a valid header\");\n validateSessionTree(filePath, entries);\n return { entries, discardedCrashTail };\n}\n\n/**\n * Load and validate a session file. Missing and whitespace-only files return\n * no entries. A malformed final non-empty line is ignored as a crash tail;\n * all other malformed JSON and invalid tree relationships throw.\n */\nexport function loadSessionFileEntries(filePath: string): SessionFileEntry[] {\n return loadValidatedSessionFile(filePath).entries;\n}\n\n/**\n * Append-oriented session store over one v3 JSONL file.\n *\n * Reading is tolerant (missing files and malformed lines yield an empty\n * session); appends persist immediately. A session opened from a file\n * without a valid header gets its header materialized on the first append.\n */\nexport class SessionStore {\n private readonly sessionFile: string | null;\n private readonly cwd: string;\n private readonly sessionId: string;\n private header: SessionHeader | null;\n private headerOnDisk: boolean;\n private rewriteBeforeAppend: boolean;\n private entries: SessionEntry[] = [];\n private byId = new Map<string, SessionEntry>();\n private leafId: string | null = null;\n\n private constructor(\n sessionFile: string | null,\n cwd: string,\n fileEntries: SessionFileEntry[],\n rewriteBeforeAppend = false,\n ) {\n this.sessionFile = sessionFile === null ? null : resolve(sessionFile);\n this.header = fileEntries.length > 0 && isSessionHeader(fileEntries[0]) ? fileEntries[0] : null;\n this.headerOnDisk = this.header !== null;\n this.rewriteBeforeAppend = rewriteBeforeAppend;\n this.sessionId = this.header?.id ?? randomUUID();\n this.cwd = cwd;\n for (const entry of fileEntries) {\n if (isSessionHeader(entry)) continue;\n if (entry.type === \"leaf\") {\n // Written by pi-agent-core JSONL sessions; mikan tracks the leaf implicitly.\n this.leafId = entry.targetId;\n continue;\n }\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n }\n }\n\n /**\n * Open a session file. A missing or empty file starts an empty session\n * whose header is written on the first append. A file that has content but\n * no leading session header is treated as corrupted and throws, rather than\n * being silently overwritten on the next append (which would erase the\n * existing history). Callers on read paths already wrap this in try/catch.\n * @param cwdOverride Working directory override; defaults to the header cwd.\n */\n static open(path: string, cwdOverride?: string): SessionStore {\n const loaded = loadValidatedSessionFile(path);\n const header = loaded.entries.find(isSessionHeader);\n const cwd = cwdOverride ?? header?.cwd ?? process.cwd();\n return new SessionStore(path, cwd, loaded.entries, loaded.discardedCrashTail);\n }\n\n /** Create a new session file with a fresh header, replacing any existing file. */\n static create(path: string, cwd: string, options?: { id?: string }): SessionStore {\n const header: SessionHeader = {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: options?.id ?? randomUUID(),\n timestamp: new Date().toISOString(),\n cwd,\n };\n atomicWritePrivateFile(path, `${JSON.stringify(header)}\\n`);\n return new SessionStore(path, cwd, [header]);\n }\n\n /** Create an ephemeral session that never writes a session file. */\n static inMemory(cwd = process.cwd()): SessionStore {\n return new SessionStore(null, cwd, []);\n }\n\n getSessionFile(): string | undefined {\n return this.sessionFile ?? undefined;\n }\n\n isPersisted(): boolean {\n return this.sessionFile !== null;\n }\n\n getCwd(): string {\n return this.cwd;\n }\n\n /** Session header as read from disk, or null when the file had none. */\n getHeader(): SessionHeader | null {\n return this.header;\n }\n\n /** Stable session id: the header id, or a generated id for fresh sessions. */\n getSessionId(): string {\n return this.sessionId;\n }\n\n getLeafId(): string | null {\n return this.leafId;\n }\n\n getEntry(id: string): SessionEntry | undefined {\n return this.byId.get(id);\n }\n\n /** All session entries (header excluded). Returns a shallow copy. */\n getEntries(): SessionEntry[] {\n return [...this.entries];\n }\n\n /** Latest user-assigned session name, if any. */\n getSessionName(): string | undefined {\n for (let i = this.entries.length - 1; i >= 0; i--) {\n const entry = this.entries[i];\n if (entry.type === \"session_info\") return entry.name?.trim() || undefined;\n }\n return undefined;\n }\n\n /**\n * Walk from an entry to the root, returning entries in path order\n * (root first). Defaults to the current leaf.\n */\n getBranch(fromId?: string): SessionEntry[] {\n const path: SessionEntry[] = [];\n let currentId = fromId ?? this.leafId;\n while (currentId) {\n const entry = this.byId.get(currentId);\n if (!entry) break;\n path.push(entry);\n currentId = entry.parentId;\n }\n return path.toReversed();\n }\n\n /**\n * Build the LLM context (messages, thinking level, model) from the current\n * branch, resolving compaction and branch summaries along the path.\n */\n buildSessionContext(): SessionContext {\n return buildContextFromEntries(this.getBranch());\n }\n\n /** Append a chat message as a child of the current leaf. Returns the entry id. */\n appendMessage(message: AgentMessage): string {\n return this.appendEntry({ type: \"message\", message });\n }\n\n /** Append an extension/application data entry (never sent to the LLM). */\n appendCustomEntry(customType: string, data?: unknown): string {\n return this.appendEntry({ type: \"custom\", customType, data });\n }\n\n /** Append a custom message entry that participates in LLM context. */\n appendCustomMessageEntry(\n customType: string,\n content: string | (TextContent | ImageContent)[],\n display: boolean,\n details?: unknown,\n ): string {\n return this.appendEntry({ type: \"custom_message\", customType, content, display, details });\n }\n\n /** Record a user-visible session name. */\n appendSessionInfo(name: string): string {\n return this.appendEntry({ type: \"session_info\", name });\n }\n\n /** Persist a compaction summary produced for this session. */\n appendCompaction(\n summary: string,\n firstKeptEntryId: string,\n tokensBefore: number,\n details?: unknown,\n fromHook?: boolean,\n ): string {\n return this.appendEntry({\n type: \"compaction\",\n summary,\n firstKeptEntryId,\n tokensBefore,\n ...(details !== undefined ? { details } : {}),\n ...(fromHook !== undefined ? { fromHook } : {}),\n });\n }\n\n private appendEntry(partial: PendingSessionEntry): string {\n const entry = {\n ...partial,\n id: generateEntryId(this.byId),\n parentId: this.leafId,\n timestamp: new Date().toISOString(),\n } as SessionEntry;\n this.entries.push(entry);\n this.byId.set(entry.id, entry);\n this.leafId = entry.id;\n this.persist(entry);\n return entry.id;\n }\n\n private persist(entry: SessionEntry): void {\n if (this.sessionFile === null) return;\n if (!this.headerOnDisk || this.rewriteBeforeAppend) {\n this.header ??= {\n type: \"session\",\n version: CURRENT_SESSION_VERSION,\n id: this.sessionId,\n timestamp: new Date().toISOString(),\n cwd: this.cwd,\n };\n const lines = [this.header, ...this.entries].map((line) => JSON.stringify(line));\n mkdirSync(dirname(this.sessionFile), { recursive: true });\n atomicWritePrivateFile(this.sessionFile, `${lines.join(\"\\n\")}\\n`);\n this.headerOnDisk = true;\n this.rewriteBeforeAppend = false;\n return;\n }\n appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\\n`);\n }\n}\n"]}
@@ -1,10 +1,7 @@
1
1
  import type { CompactionSettings } from "@earendil-works/pi-agent-core";
2
+ import type { BudgetSettings, HarnessSettings, RetrySettings } from "./types.js";
2
3
  export type { CompactionSettings };
3
- export interface RetrySettings {
4
- enabled: boolean;
5
- maxRetries: number;
6
- baseDelayMs: number;
7
- }
4
+ export type { BudgetSettings, HarnessSettings, RetrySettings } from "./types.js";
8
5
  export declare const DEFAULT_RETRY_SETTINGS: RetrySettings;
9
6
  /**
10
7
  * Per-run resource ceilings — the harness's external circuit breakers.
@@ -17,16 +14,6 @@ export declare const DEFAULT_RETRY_SETTINGS: RetrySettings;
17
14
  * so they default to uncapped; autonomous runs (scheduled events, triggers)
18
15
  * pass {@link DEFAULT_EVENT_BUDGET}, where nobody is watching the loop.
19
16
  */
20
- export interface BudgetSettings {
21
- /** Max cumulative tokens processed this run (input + output + cache read/write). */
22
- maxTokens?: number;
23
- /** Max cumulative provider cost (USD) this run. Requires a populated model cost table. */
24
- maxCostUsd?: number;
25
- /** Max wall-clock duration for the run, in milliseconds. */
26
- maxDurationMs?: number;
27
- /** Max number of LLM calls (assistant turns) this run. */
28
- maxLlmCalls?: number;
29
- }
30
17
  /** Interactive runs are human-gated per turn — no automatic ceiling by default. */
31
18
  export declare const DEFAULT_BUDGET_SETTINGS: BudgetSettings;
32
19
  /**
@@ -34,11 +21,6 @@ export declare const DEFAULT_BUDGET_SETTINGS: BudgetSettings;
34
21
  * the loop. Deliberately generous — it is a stop-loss, not a target.
35
22
  */
36
23
  export declare const DEFAULT_EVENT_BUDGET: BudgetSettings;
37
- export interface HarnessSettings {
38
- compaction: CompactionSettings;
39
- retry: RetrySettings;
40
- budget: BudgetSettings;
41
- }
42
24
  export declare function resolveHarnessSettings(overrides?: {
43
25
  compaction?: Partial<CompactionSettings>;
44
26
  retry?: Partial<RetrySettings>;
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/harness/settings.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AAExE,YAAY,EAAE,kBAAkB,EAAE,CAAC;AAEnC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,MAAM,WAAW,cAAc;IAC7B,oFAAoF;IACpF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0FAA0F;IAC1F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,EAAE,cAAmB,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAIlC,CAAC;AAEF,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,kBAAkB,CAAC;IAC/B,KAAK,EAAE,aAAa,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;CACxB;AAED,wBAAgB,sBAAsB,CAAC,SAAS,CAAC,EAAE;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAClC,GAAG,eAAe,CAMlB"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/harness/settings.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEjF,YAAY,EAAE,kBAAkB,EAAE,CAAC;AACnC,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEjF,eAAO,MAAM,sBAAsB,EAAE,aAIpC,CAAC;AAEF;;;;;;;;;;GAUG;AACH,mFAAmF;AACnF,eAAO,MAAM,uBAAuB,EAAE,cAAmB,CAAC;AAE1D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,cAIlC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,SAAS,CAAC,EAAE;IACjD,UAAU,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACzC,KAAK,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CAClC,GAAG,eAAe,CAMlB"}
@@ -8,6 +8,17 @@ export const DEFAULT_RETRY_SETTINGS = {
8
8
  maxRetries: 3,
9
9
  baseDelayMs: 2000,
10
10
  };
11
+ /**
12
+ * Per-run resource ceilings — the harness's external circuit breakers.
13
+ *
14
+ * An LLM cannot reliably decide on its own when to stop (the halting problem),
15
+ * so a runaway run has to be stopped from the outside. When any populated cap
16
+ * is exceeded, the run is aborted and a `budget_exceeded` event is emitted.
17
+ * Every field is a cap on a single `prompt()` call; an undefined field means
18
+ * "no cap on that axis". Interactive runs are gated turn-by-turn by a human,
19
+ * so they default to uncapped; autonomous runs (scheduled events, triggers)
20
+ * pass {@link DEFAULT_EVENT_BUDGET}, where nobody is watching the loop.
21
+ */
11
22
  /** Interactive runs are human-gated per turn — no automatic ceiling by default. */
12
23
  export const DEFAULT_BUDGET_SETTINGS = {};
13
24
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/harness/settings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAW5E,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,IAAI;CAClB,CAAC;AAwBF,mFAAmF;AACnF,MAAM,CAAC,MAAM,uBAAuB,GAAmB,EAAE,CAAC;AAE1D;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAmB;IAClD,aAAa,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IAC7B,WAAW,EAAE,EAAE;IACf,UAAU,EAAE,CAAC;CACd,CAAC;AAQF,MAAM,UAAU,sBAAsB,CAAC,SAItC;IACC,OAAO;QACL,UAAU,EAAE,EAAE,GAAG,2BAA2B,EAAE,GAAG,SAAS,EAAE,UAAU,EAAE;QACxE,KAAK,EAAE,EAAE,GAAG,sBAAsB,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE;QACzD,MAAM,EAAE,EAAE,GAAG,uBAAuB,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE;KAC7D,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Runtime settings for the mikan harness. mikan runs headless, so these are\n * plain values with defaults matching the behavior mikan shipped with.\n */\nimport { DEFAULT_COMPACTION_SETTINGS } from \"@earendil-works/pi-agent-core\";\nimport type { CompactionSettings } from \"@earendil-works/pi-agent-core\";\n\nexport type { CompactionSettings };\n\nexport interface RetrySettings {\n enabled: boolean;\n maxRetries: number;\n baseDelayMs: number;\n}\n\nexport const DEFAULT_RETRY_SETTINGS: RetrySettings = {\n enabled: true,\n maxRetries: 3,\n baseDelayMs: 2000,\n};\n\n/**\n * Per-run resource ceilings — the harness's external circuit breakers.\n *\n * An LLM cannot reliably decide on its own when to stop (the halting problem),\n * so a runaway run has to be stopped from the outside. When any populated cap\n * is exceeded, the run is aborted and a `budget_exceeded` event is emitted.\n * Every field is a cap on a single `prompt()` call; an undefined field means\n * \"no cap on that axis\". Interactive runs are gated turn-by-turn by a human,\n * so they default to uncapped; autonomous runs (scheduled events, triggers)\n * pass {@link DEFAULT_EVENT_BUDGET}, where nobody is watching the loop.\n */\nexport interface BudgetSettings {\n /** Max cumulative tokens processed this run (input + output + cache read/write). */\n maxTokens?: number;\n /** Max cumulative provider cost (USD) this run. Requires a populated model cost table. */\n maxCostUsd?: number;\n /** Max wall-clock duration for the run, in milliseconds. */\n maxDurationMs?: number;\n /** Max number of LLM calls (assistant turns) this run. */\n maxLlmCalls?: number;\n}\n\n/** Interactive runs are human-gated per turn — no automatic ceiling by default. */\nexport const DEFAULT_BUDGET_SETTINGS: BudgetSettings = {};\n\n/**\n * Ceiling for autonomous (event / trigger) runs, where no human is watching\n * the loop. Deliberately generous — it is a stop-loss, not a target.\n */\nexport const DEFAULT_EVENT_BUDGET: BudgetSettings = {\n maxDurationMs: 10 * 60 * 1000,\n maxLlmCalls: 50,\n maxCostUsd: 2,\n};\n\nexport interface HarnessSettings {\n compaction: CompactionSettings;\n retry: RetrySettings;\n budget: BudgetSettings;\n}\n\nexport function resolveHarnessSettings(overrides?: {\n compaction?: Partial<CompactionSettings>;\n retry?: Partial<RetrySettings>;\n budget?: Partial<BudgetSettings>;\n}): HarnessSettings {\n return {\n compaction: { ...DEFAULT_COMPACTION_SETTINGS, ...overrides?.compaction },\n retry: { ...DEFAULT_RETRY_SETTINGS, ...overrides?.retry },\n budget: { ...DEFAULT_BUDGET_SETTINGS, ...overrides?.budget },\n };\n}\n"]}
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/harness/settings.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAO5E,MAAM,CAAC,MAAM,sBAAsB,GAAkB;IACnD,OAAO,EAAE,IAAI;IACb,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,IAAI;CAClB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,mFAAmF;AACnF,MAAM,CAAC,MAAM,uBAAuB,GAAmB,EAAE,CAAC;AAE1D;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAmB;IAClD,aAAa,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;IAC7B,WAAW,EAAE,EAAE;IACf,UAAU,EAAE,CAAC;CACd,CAAC;AAEF,MAAM,UAAU,sBAAsB,CAAC,SAItC;IACC,OAAO;QACL,UAAU,EAAE,EAAE,GAAG,2BAA2B,EAAE,GAAG,SAAS,EAAE,UAAU,EAAE;QACxE,KAAK,EAAE,EAAE,GAAG,sBAAsB,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE;QACzD,MAAM,EAAE,EAAE,GAAG,uBAAuB,EAAE,GAAG,SAAS,EAAE,MAAM,EAAE;KAC7D,CAAC;AACJ,CAAC","sourcesContent":["/**\n * Runtime settings for the mikan harness. mikan runs headless, so these are\n * plain values with defaults matching the behavior mikan shipped with.\n */\nimport { DEFAULT_COMPACTION_SETTINGS } from \"@earendil-works/pi-agent-core\";\nimport type { CompactionSettings } from \"@earendil-works/pi-agent-core\";\nimport type { BudgetSettings, HarnessSettings, RetrySettings } from \"./types.js\";\n\nexport type { CompactionSettings };\nexport type { BudgetSettings, HarnessSettings, RetrySettings } from \"./types.js\";\n\nexport const DEFAULT_RETRY_SETTINGS: RetrySettings = {\n enabled: true,\n maxRetries: 3,\n baseDelayMs: 2000,\n};\n\n/**\n * Per-run resource ceilings — the harness's external circuit breakers.\n *\n * An LLM cannot reliably decide on its own when to stop (the halting problem),\n * so a runaway run has to be stopped from the outside. When any populated cap\n * is exceeded, the run is aborted and a `budget_exceeded` event is emitted.\n * Every field is a cap on a single `prompt()` call; an undefined field means\n * \"no cap on that axis\". Interactive runs are gated turn-by-turn by a human,\n * so they default to uncapped; autonomous runs (scheduled events, triggers)\n * pass {@link DEFAULT_EVENT_BUDGET}, where nobody is watching the loop.\n */\n/** Interactive runs are human-gated per turn — no automatic ceiling by default. */\nexport const DEFAULT_BUDGET_SETTINGS: BudgetSettings = {};\n\n/**\n * Ceiling for autonomous (event / trigger) runs, where no human is watching\n * the loop. Deliberately generous — it is a stop-loss, not a target.\n */\nexport const DEFAULT_EVENT_BUDGET: BudgetSettings = {\n maxDurationMs: 10 * 60 * 1000,\n maxLlmCalls: 50,\n maxCostUsd: 2,\n};\n\nexport function resolveHarnessSettings(overrides?: {\n compaction?: Partial<CompactionSettings>;\n retry?: Partial<RetrySettings>;\n budget?: Partial<BudgetSettings>;\n}): HarnessSettings {\n return {\n compaction: { ...DEFAULT_COMPACTION_SETTINGS, ...overrides?.compaction },\n retry: { ...DEFAULT_RETRY_SETTINGS, ...overrides?.retry },\n budget: { ...DEFAULT_BUDGET_SETTINGS, ...overrides?.budget },\n };\n}\n"]}
@@ -1,31 +1,5 @@
1
- /** A loaded skill. `baseDir` is the directory containing the skill file. */
2
- export interface MikanSkill {
3
- name: string;
4
- description: string;
5
- /** Full skill instructions (file content without frontmatter). */
6
- content: string;
7
- filePath: string;
8
- baseDir: string;
9
- /** Where the skill was loaded from (e.g. "workspace", "channel"). */
10
- source: string;
11
- /** Exclude from model-visible skill lists. */
12
- disableModelInvocation?: boolean;
13
- /**
14
- * Embed the skill body in the prompt instead of referencing its file path.
15
- * Used for skills whose files the agent cannot read at runtime (e.g.
16
- * extension skills under the host-only state dir in sandbox modes).
17
- */
18
- inline?: boolean;
19
- }
20
- export interface SkillDiagnostic {
21
- type: "warning";
22
- message: string;
23
- path: string;
24
- }
25
- export interface LoadSkillsResult {
26
- skills: MikanSkill[];
27
- diagnostics: SkillDiagnostic[];
28
- }
1
+ import type { LoadSkillsResult, MikanSkill } from "./types.js";
2
+ export type { LoadSkillsResult, MikanSkill, SkillDiagnostic } from "./types.js";
29
3
  interface Frontmatter {
30
4
  values: Record<string, string>;
31
5
  body: string;
@@ -50,5 +24,4 @@ export declare function loadSkillsFromDir(options: {
50
24
  * Skills with `disableModelInvocation` are excluded.
51
25
  */
52
26
  export declare function formatSkillsForPrompt(skills: MikanSkill[]): string;
53
- export {};
54
27
  //# sourceMappingURL=skills.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/harness/skills.ts"],"names":[],"mappings":"AAeA,4EAA4E;AAC5E,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,qEAAqE;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,8CAA8C;IAC9C,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,WAAW,EAAE,eAAe,EAAE,CAAC;CAChC;AAED,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAoB7D;AAkED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAE5F;AAgFD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAyBlE"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../../src/harness/skills.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,gBAAgB,EAAE,UAAU,EAAmB,MAAM,YAAY,CAAC;AAEhF,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAKhF,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAoB7D;AAkED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,gBAAgB,CAE5F;AAgFD;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,CAyBlE"}
@@ -1 +1 @@
1
- {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/harness/skills.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Skill loading for the mikan harness.\n *\n * Skills follow the Agent Skills layout: a directory containing a `SKILL.md`\n * with YAML frontmatter (`name`, `description`), or standalone `.md` files in\n * the root of a skills directory. Discovery mirrors the behavior mikan's\n * system prompt documents: a directory with a `SKILL.md` is a skill root\n * (no further recursion), other directories are traversed.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join } from \"path\";\n\nconst MAX_NAME_LENGTH = 64;\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\n/** A loaded skill. `baseDir` is the directory containing the skill file. */\nexport interface MikanSkill {\n name: string;\n description: string;\n /** Full skill instructions (file content without frontmatter). */\n content: string;\n filePath: string;\n baseDir: string;\n /** Where the skill was loaded from (e.g. \"workspace\", \"channel\"). */\n source: string;\n /** Exclude from model-visible skill lists. */\n disableModelInvocation?: boolean;\n /**\n * Embed the skill body in the prompt instead of referencing its file path.\n * Used for skills whose files the agent cannot read at runtime (e.g.\n * extension skills under the host-only state dir in sandbox modes).\n */\n inline?: boolean;\n}\n\nexport interface SkillDiagnostic {\n type: \"warning\";\n message: string;\n path: string;\n}\n\nexport interface LoadSkillsResult {\n skills: MikanSkill[];\n diagnostics: SkillDiagnostic[];\n}\n\ninterface Frontmatter {\n values: Record<string, string>;\n body: string;\n}\n\n/**\n * Parse simple `key: value` YAML frontmatter delimited by `---` lines.\n * Quoted values are unquoted; nested structures are not supported.\n */\nexport function parseFrontmatter(content: string): Frontmatter {\n const match = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/);\n if (!match) return { values: {}, body: content };\n\n const values: Record<string, string> = {};\n for (const line of match[1].split(/\\r?\\n/)) {\n const separator = line.indexOf(\":\");\n if (separator === -1) continue;\n const key = line.slice(0, separator).trim();\n if (!key) continue;\n let value = line.slice(separator + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n values[key] = value;\n }\n return { values, body: content.slice(match[0].length) };\n}\n\nfunction validateSkill(name: string, description: string): string[] {\n const errors: string[] = [];\n if (name.length > MAX_NAME_LENGTH) {\n errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n }\n if (\n !/^[a-z0-9-]+$/.test(name) ||\n name.startsWith(\"-\") ||\n name.endsWith(\"-\") ||\n name.includes(\"--\")\n ) {\n errors.push(\"name must be lowercase a-z, 0-9 and single hyphens, not at the edges\");\n }\n if (description.length > MAX_DESCRIPTION_LENGTH) {\n errors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n }\n return errors;\n}\n\nfunction loadSkillFromFile(\n filePath: string,\n source: string,\n): {\n skill: MikanSkill | null;\n diagnostics: SkillDiagnostic[];\n} {\n const diagnostics: SkillDiagnostic[] = [];\n try {\n const raw = readFileSync(filePath, \"utf-8\");\n const { values, body } = parseFrontmatter(raw);\n const skillDir = dirname(filePath);\n const name = values.name || basename(skillDir);\n const description = values.description ?? \"\";\n\n if (!description.trim()) {\n diagnostics.push({ type: \"warning\", message: \"description is required\", path: filePath });\n return { skill: null, diagnostics };\n }\n for (const error of validateSkill(name, description)) {\n diagnostics.push({ type: \"warning\", message: error, path: filePath });\n }\n\n return {\n skill: {\n name,\n description,\n content: body.trim(),\n filePath,\n baseDir: skillDir,\n source,\n disableModelInvocation: values[\"disable-model-invocation\"] === \"true\",\n },\n diagnostics,\n };\n } catch (error) {\n diagnostics.push({\n type: \"warning\",\n message: error instanceof Error ? error.message : \"failed to parse skill file\",\n path: filePath,\n });\n return { skill: null, diagnostics };\n }\n}\n\n/**\n * Load skills from a directory tree.\n * - A directory containing `SKILL.md` is a skill root; no further recursion.\n * - Direct `.md` children of the top-level directory load as skills.\n * - Dot-directories and `node_modules` are skipped.\n */\nexport function loadSkillsFromDir(options: { dir: string; source: string }): LoadSkillsResult {\n return loadSkillsFromDirInternal(options.dir, options.source, true);\n}\n\nfunction loadSkillsFromDirInternal(\n dir: string,\n source: string,\n includeRootFiles: boolean,\n): LoadSkillsResult {\n const skills: MikanSkill[] = [];\n const diagnostics: SkillDiagnostic[] = [];\n if (!existsSync(dir)) return { skills, diagnostics };\n\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return { skills, diagnostics };\n }\n\n const skillFile = entries.find((entry) => entry.name === \"SKILL.md\" && isFileLike(dir, entry));\n if (skillFile) {\n const result = loadSkillFromFile(join(dir, skillFile.name), source);\n if (result.skill) skills.push(result.skill);\n diagnostics.push(...result.diagnostics);\n return { skills, diagnostics };\n }\n\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n const fullPath = join(dir, entry.name);\n\n if (isDirectoryLike(dir, entry)) {\n const subResult = loadSkillsFromDirInternal(fullPath, source, false);\n skills.push(...subResult.skills);\n diagnostics.push(...subResult.diagnostics);\n continue;\n }\n if (!includeRootFiles || !entry.name.endsWith(\".md\") || !isFileLike(dir, entry)) continue;\n\n const result = loadSkillFromFile(fullPath, source);\n if (result.skill) skills.push(result.skill);\n diagnostics.push(...result.diagnostics);\n }\n return { skills, diagnostics };\n}\n\nfunction isFileLike(\n dir: string,\n entry: { name: string; isFile(): boolean; isSymbolicLink(): boolean },\n): boolean {\n if (entry.isFile()) return true;\n if (!entry.isSymbolicLink()) return false;\n try {\n return statSync(join(dir, entry.name)).isFile();\n } catch {\n return false;\n }\n}\n\nfunction isDirectoryLike(\n dir: string,\n entry: { name: string; isDirectory(): boolean; isSymbolicLink(): boolean },\n): boolean {\n if (entry.isDirectory()) return true;\n if (!entry.isSymbolicLink()) return false;\n try {\n return statSync(join(dir, entry.name)).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\n/**\n * Format skills for a system prompt using the Agent Skills XML block.\n * Skills with `disableModelInvocation` are excluded.\n */\nexport function formatSkillsForPrompt(skills: MikanSkill[]): string {\n const visible = skills.filter((skill) => !skill.disableModelInvocation);\n if (visible.length === 0) return \"\";\n\n const lines = [\n \"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n \"Use the read tool to load a skill's file when the task matches its description.\",\n \"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n \"\",\n \"<available_skills>\",\n ];\n for (const skill of visible) {\n lines.push(\" <skill>\");\n lines.push(` <name>${escapeXml(skill.name)}</name>`);\n lines.push(` <description>${escapeXml(skill.description)}</description>`);\n if (skill.inline) {\n // Inline skills carry their body: the agent cannot read their file.\n lines.push(` <instructions>${escapeXml(skill.content)}</instructions>`);\n } else {\n lines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n }\n lines.push(\" </skill>\");\n }\n lines.push(\"</available_skills>\");\n return lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"skills.js","sourceRoot":"","sources":["../../src/harness/skills.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Skill loading for the mikan harness.\n *\n * Skills follow the Agent Skills layout: a directory containing a `SKILL.md`\n * with YAML frontmatter (`name`, `description`), or standalone `.md` files in\n * the root of a skills directory. Discovery mirrors the behavior mikan's\n * system prompt documents: a directory with a `SKILL.md` is a skill root\n * (no further recursion), other directories are traversed.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from \"fs\";\nimport { basename, dirname, join } from \"path\";\nimport type { LoadSkillsResult, MikanSkill, SkillDiagnostic } from \"./types.js\";\n\nexport type { LoadSkillsResult, MikanSkill, SkillDiagnostic } from \"./types.js\";\n\nconst MAX_NAME_LENGTH = 64;\nconst MAX_DESCRIPTION_LENGTH = 1024;\n\ninterface Frontmatter {\n values: Record<string, string>;\n body: string;\n}\n\n/**\n * Parse simple `key: value` YAML frontmatter delimited by `---` lines.\n * Quoted values are unquoted; nested structures are not supported.\n */\nexport function parseFrontmatter(content: string): Frontmatter {\n const match = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/);\n if (!match) return { values: {}, body: content };\n\n const values: Record<string, string> = {};\n for (const line of match[1].split(/\\r?\\n/)) {\n const separator = line.indexOf(\":\");\n if (separator === -1) continue;\n const key = line.slice(0, separator).trim();\n if (!key) continue;\n let value = line.slice(separator + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n values[key] = value;\n }\n return { values, body: content.slice(match[0].length) };\n}\n\nfunction validateSkill(name: string, description: string): string[] {\n const errors: string[] = [];\n if (name.length > MAX_NAME_LENGTH) {\n errors.push(`name exceeds ${MAX_NAME_LENGTH} characters (${name.length})`);\n }\n if (\n !/^[a-z0-9-]+$/.test(name) ||\n name.startsWith(\"-\") ||\n name.endsWith(\"-\") ||\n name.includes(\"--\")\n ) {\n errors.push(\"name must be lowercase a-z, 0-9 and single hyphens, not at the edges\");\n }\n if (description.length > MAX_DESCRIPTION_LENGTH) {\n errors.push(`description exceeds ${MAX_DESCRIPTION_LENGTH} characters (${description.length})`);\n }\n return errors;\n}\n\nfunction loadSkillFromFile(\n filePath: string,\n source: string,\n): {\n skill: MikanSkill | null;\n diagnostics: SkillDiagnostic[];\n} {\n const diagnostics: SkillDiagnostic[] = [];\n try {\n const raw = readFileSync(filePath, \"utf-8\");\n const { values, body } = parseFrontmatter(raw);\n const skillDir = dirname(filePath);\n const name = values.name || basename(skillDir);\n const description = values.description ?? \"\";\n\n if (!description.trim()) {\n diagnostics.push({ type: \"warning\", message: \"description is required\", path: filePath });\n return { skill: null, diagnostics };\n }\n for (const error of validateSkill(name, description)) {\n diagnostics.push({ type: \"warning\", message: error, path: filePath });\n }\n\n return {\n skill: {\n name,\n description,\n content: body.trim(),\n filePath,\n baseDir: skillDir,\n source,\n disableModelInvocation: values[\"disable-model-invocation\"] === \"true\",\n },\n diagnostics,\n };\n } catch (error) {\n diagnostics.push({\n type: \"warning\",\n message: error instanceof Error ? error.message : \"failed to parse skill file\",\n path: filePath,\n });\n return { skill: null, diagnostics };\n }\n}\n\n/**\n * Load skills from a directory tree.\n * - A directory containing `SKILL.md` is a skill root; no further recursion.\n * - Direct `.md` children of the top-level directory load as skills.\n * - Dot-directories and `node_modules` are skipped.\n */\nexport function loadSkillsFromDir(options: { dir: string; source: string }): LoadSkillsResult {\n return loadSkillsFromDirInternal(options.dir, options.source, true);\n}\n\nfunction loadSkillsFromDirInternal(\n dir: string,\n source: string,\n includeRootFiles: boolean,\n): LoadSkillsResult {\n const skills: MikanSkill[] = [];\n const diagnostics: SkillDiagnostic[] = [];\n if (!existsSync(dir)) return { skills, diagnostics };\n\n let entries;\n try {\n entries = readdirSync(dir, { withFileTypes: true });\n } catch {\n return { skills, diagnostics };\n }\n\n const skillFile = entries.find((entry) => entry.name === \"SKILL.md\" && isFileLike(dir, entry));\n if (skillFile) {\n const result = loadSkillFromFile(join(dir, skillFile.name), source);\n if (result.skill) skills.push(result.skill);\n diagnostics.push(...result.diagnostics);\n return { skills, diagnostics };\n }\n\n for (const entry of entries) {\n if (entry.name.startsWith(\".\") || entry.name === \"node_modules\") continue;\n const fullPath = join(dir, entry.name);\n\n if (isDirectoryLike(dir, entry)) {\n const subResult = loadSkillsFromDirInternal(fullPath, source, false);\n skills.push(...subResult.skills);\n diagnostics.push(...subResult.diagnostics);\n continue;\n }\n if (!includeRootFiles || !entry.name.endsWith(\".md\") || !isFileLike(dir, entry)) continue;\n\n const result = loadSkillFromFile(fullPath, source);\n if (result.skill) skills.push(result.skill);\n diagnostics.push(...result.diagnostics);\n }\n return { skills, diagnostics };\n}\n\nfunction isFileLike(\n dir: string,\n entry: { name: string; isFile(): boolean; isSymbolicLink(): boolean },\n): boolean {\n if (entry.isFile()) return true;\n if (!entry.isSymbolicLink()) return false;\n try {\n return statSync(join(dir, entry.name)).isFile();\n } catch {\n return false;\n }\n}\n\nfunction isDirectoryLike(\n dir: string,\n entry: { name: string; isDirectory(): boolean; isSymbolicLink(): boolean },\n): boolean {\n if (entry.isDirectory()) return true;\n if (!entry.isSymbolicLink()) return false;\n try {\n return statSync(join(dir, entry.name)).isDirectory();\n } catch {\n return false;\n }\n}\n\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, \"&amp;\")\n .replace(/</g, \"&lt;\")\n .replace(/>/g, \"&gt;\")\n .replace(/\"/g, \"&quot;\")\n .replace(/'/g, \"&apos;\");\n}\n\n/**\n * Format skills for a system prompt using the Agent Skills XML block.\n * Skills with `disableModelInvocation` are excluded.\n */\nexport function formatSkillsForPrompt(skills: MikanSkill[]): string {\n const visible = skills.filter((skill) => !skill.disableModelInvocation);\n if (visible.length === 0) return \"\";\n\n const lines = [\n \"\\n\\nThe following skills provide specialized instructions for specific tasks.\",\n \"Use the read tool to load a skill's file when the task matches its description.\",\n \"When a skill file references a relative path, resolve it against the skill directory (parent of SKILL.md / dirname of the path) and use that absolute path in tool commands.\",\n \"\",\n \"<available_skills>\",\n ];\n for (const skill of visible) {\n lines.push(\" <skill>\");\n lines.push(` <name>${escapeXml(skill.name)}</name>`);\n lines.push(` <description>${escapeXml(skill.description)}</description>`);\n if (skill.inline) {\n // Inline skills carry their body: the agent cannot read their file.\n lines.push(` <instructions>${escapeXml(skill.content)}</instructions>`);\n } else {\n lines.push(` <location>${escapeXml(skill.filePath)}</location>`);\n }\n lines.push(\" </skill>\");\n }\n lines.push(\"</available_skills>\");\n return lines.join(\"\\n\");\n}\n"]}
@@ -0,0 +1,8 @@
1
+ import type { LoadSubagentProfilesResult } from "./types.js";
2
+ export type { LoadSubagentProfilesResult, SubagentProfileDiagnostic } from "./types.js";
3
+ /**
4
+ * Load the built-in profiles, then patch them with `<workspaceDir>/agents/*.md`.
5
+ * Malformed files are reported and skipped, leaving the built-in in place.
6
+ */
7
+ export declare function loadSubagentProfiles(workspaceDir: string): LoadSubagentProfilesResult;
8
+ //# sourceMappingURL=subagent-profiles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subagent-profiles.d.ts","sourceRoot":"","sources":["../../src/harness/subagent-profiles.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EACV,0BAA0B,EAI3B,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,0BAA0B,EAAE,yBAAyB,EAAE,MAAM,YAAY,CAAC;AAqQxF;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,0BAA0B,CAiCrF"}