@geminixiang/mikan 1.0.0-beta.84 → 1.0.0-beta.86

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 (289) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +10 -10
  3. package/dist/adapters/commands/session-view.js +1 -1
  4. package/dist/adapters/commands/session-view.js.map +1 -1
  5. package/dist/adapters/discord/bot.d.ts.map +1 -1
  6. package/dist/adapters/discord/bot.js +1 -8
  7. package/dist/adapters/discord/bot.js.map +1 -1
  8. package/dist/adapters/github/activity.d.ts +3 -0
  9. package/dist/adapters/github/activity.d.ts.map +1 -0
  10. package/dist/adapters/github/activity.js +154 -0
  11. package/dist/adapters/github/activity.js.map +1 -0
  12. package/dist/adapters/github/bot.d.ts +10 -20
  13. package/dist/adapters/github/bot.d.ts.map +1 -1
  14. package/dist/adapters/github/bot.js +119 -342
  15. package/dist/adapters/github/bot.js.map +1 -1
  16. package/dist/adapters/github/client.d.ts +9 -19
  17. package/dist/adapters/github/client.d.ts.map +1 -1
  18. package/dist/adapters/github/client.js +28 -104
  19. package/dist/adapters/github/client.js.map +1 -1
  20. package/dist/adapters/github/context.d.ts.map +1 -1
  21. package/dist/adapters/github/context.js +29 -20
  22. package/dist/adapters/github/context.js.map +1 -1
  23. package/dist/adapters/github/github-ops.d.ts +2 -10
  24. package/dist/adapters/github/github-ops.d.ts.map +1 -1
  25. package/dist/adapters/github/github-ops.js +28 -91
  26. package/dist/adapters/github/github-ops.js.map +1 -1
  27. package/dist/adapters/github/policy.d.ts +19 -0
  28. package/dist/adapters/github/policy.d.ts.map +1 -0
  29. package/dist/adapters/github/policy.js +79 -0
  30. package/dist/adapters/github/policy.js.map +1 -0
  31. package/dist/adapters/github/tool-pack.d.ts +2 -2
  32. package/dist/adapters/github/tool-pack.d.ts.map +1 -1
  33. package/dist/adapters/github/tool-pack.js +4 -9
  34. package/dist/adapters/github/tool-pack.js.map +1 -1
  35. package/dist/adapters/github/tools/checks.d.ts.map +1 -1
  36. package/dist/adapters/github/tools/checks.js +6 -8
  37. package/dist/adapters/github/tools/checks.js.map +1 -1
  38. package/dist/adapters/github/tools/pr.d.ts.map +1 -1
  39. package/dist/adapters/github/tools/pr.js +7 -9
  40. package/dist/adapters/github/tools/pr.js.map +1 -1
  41. package/dist/adapters/github/types.d.ts +66 -67
  42. package/dist/adapters/github/types.d.ts.map +1 -1
  43. package/dist/adapters/github/types.js.map +1 -1
  44. package/dist/adapters/github/webhook.d.ts +2 -1
  45. package/dist/adapters/github/webhook.d.ts.map +1 -1
  46. package/dist/adapters/github/webhook.js +46 -4
  47. package/dist/adapters/github/webhook.js.map +1 -1
  48. package/dist/adapters/progressive-renderer.d.ts.map +1 -1
  49. package/dist/adapters/progressive-renderer.js +3 -0
  50. package/dist/adapters/progressive-renderer.js.map +1 -1
  51. package/dist/adapters/shared.d.ts +3 -1
  52. package/dist/adapters/shared.d.ts.map +1 -1
  53. package/dist/adapters/shared.js +45 -23
  54. package/dist/adapters/shared.js.map +1 -1
  55. package/dist/adapters/slack/blocks.d.ts.map +1 -1
  56. package/dist/adapters/slack/blocks.js +65 -2
  57. package/dist/adapters/slack/blocks.js.map +1 -1
  58. package/dist/adapters/slack/bot.d.ts +4 -0
  59. package/dist/adapters/slack/bot.d.ts.map +1 -1
  60. package/dist/adapters/slack/bot.js +84 -36
  61. package/dist/adapters/slack/bot.js.map +1 -1
  62. package/dist/adapters/slack/jev-context.js +2 -2
  63. package/dist/adapters/slack/jev-context.js.map +1 -1
  64. package/dist/adapters/slack/response-lifecycle.js +2 -2
  65. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  66. package/dist/adapters/slack/task-status.d.ts +3 -2
  67. package/dist/adapters/slack/task-status.d.ts.map +1 -1
  68. package/dist/adapters/slack/task-status.js +12 -15
  69. package/dist/adapters/slack/task-status.js.map +1 -1
  70. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  71. package/dist/adapters/telegram/bot.js +1 -8
  72. package/dist/adapters/telegram/bot.js.map +1 -1
  73. package/dist/adapters/web/admin/portal.js +1 -1
  74. package/dist/adapters/web/admin/portal.js.map +1 -1
  75. package/dist/adapters/web/admin/provider-models.d.ts.map +1 -1
  76. package/dist/adapters/web/admin/provider-models.js +10 -3
  77. package/dist/adapters/web/admin/provider-models.js.map +1 -1
  78. package/dist/adapters/web/login/portal.js +6 -1
  79. package/dist/adapters/web/login/portal.js.map +1 -1
  80. package/dist/adapters/web/server.d.ts.map +1 -1
  81. package/dist/adapters/web/server.js +5 -3
  82. package/dist/adapters/web/server.js.map +1 -1
  83. package/dist/adapters/web/session-view/portal.d.ts +2 -1
  84. package/dist/adapters/web/session-view/portal.d.ts.map +1 -1
  85. package/dist/adapters/web/session-view/portal.js +18 -27
  86. package/dist/adapters/web/session-view/portal.js.map +1 -1
  87. package/dist/cli/boot.d.ts.map +1 -1
  88. package/dist/cli/boot.js +4 -6
  89. package/dist/cli/boot.js.map +1 -1
  90. package/dist/cli/migrate.d.ts +3 -0
  91. package/dist/cli/migrate.d.ts.map +1 -0
  92. package/dist/cli/migrate.js +74 -0
  93. package/dist/cli/migrate.js.map +1 -0
  94. package/dist/cli/office.d.ts.map +1 -1
  95. package/dist/cli/office.js +8 -129
  96. package/dist/cli/office.js.map +1 -1
  97. package/dist/cli/onboard.d.ts +0 -1
  98. package/dist/cli/onboard.d.ts.map +1 -1
  99. package/dist/cli/onboard.js +4 -7
  100. package/dist/cli/onboard.js.map +1 -1
  101. package/dist/cli/types.d.ts +2 -3
  102. package/dist/cli/types.d.ts.map +1 -1
  103. package/dist/cli/types.js.map +1 -1
  104. package/dist/env-manifest.d.ts.map +1 -1
  105. package/dist/env-manifest.js +24 -22
  106. package/dist/env-manifest.js.map +1 -1
  107. package/dist/events/index.d.ts +3 -13
  108. package/dist/events/index.d.ts.map +1 -1
  109. package/dist/events/index.js +12 -58
  110. package/dist/events/index.js.map +1 -1
  111. package/dist/events/scheduler.d.ts.map +1 -1
  112. package/dist/events/scheduler.js +3 -0
  113. package/dist/events/scheduler.js.map +1 -1
  114. package/dist/file-guards.d.ts +7 -0
  115. package/dist/file-guards.d.ts.map +1 -1
  116. package/dist/file-guards.js +63 -1
  117. package/dist/file-guards.js.map +1 -1
  118. package/dist/harness/budget-stop.d.ts +3 -0
  119. package/dist/harness/budget-stop.d.ts.map +1 -0
  120. package/dist/harness/budget-stop.js +36 -0
  121. package/dist/harness/budget-stop.js.map +1 -0
  122. package/dist/harness/execution-resolver.d.ts.map +1 -1
  123. package/dist/harness/execution-resolver.js +5 -9
  124. package/dist/harness/execution-resolver.js.map +1 -1
  125. package/dist/harness/mcp.d.ts.map +1 -1
  126. package/dist/harness/mcp.js +75 -31
  127. package/dist/harness/mcp.js.map +1 -1
  128. package/dist/harness/models.js +2 -2
  129. package/dist/harness/models.js.map +1 -1
  130. package/dist/harness/open-connector.d.ts +0 -9
  131. package/dist/harness/open-connector.d.ts.map +1 -1
  132. package/dist/harness/open-connector.js +15 -69
  133. package/dist/harness/open-connector.js.map +1 -1
  134. package/dist/harness/presenter.d.ts.map +1 -1
  135. package/dist/harness/presenter.js +33 -2
  136. package/dist/harness/presenter.js.map +1 -1
  137. package/dist/harness/prompt.d.ts +1 -1
  138. package/dist/harness/prompt.d.ts.map +1 -1
  139. package/dist/harness/prompt.js +14 -32
  140. package/dist/harness/prompt.js.map +1 -1
  141. package/dist/harness/run-events.d.ts.map +1 -1
  142. package/dist/harness/run-events.js +6 -1
  143. package/dist/harness/run-events.js.map +1 -1
  144. package/dist/harness/runner.d.ts.map +1 -1
  145. package/dist/harness/runner.js +6 -9
  146. package/dist/harness/runner.js.map +1 -1
  147. package/dist/harness/session.d.ts.map +1 -1
  148. package/dist/harness/session.js +6 -0
  149. package/dist/harness/session.js.map +1 -1
  150. package/dist/harness/tools/event.d.ts.map +1 -1
  151. package/dist/harness/tools/event.js +19 -2
  152. package/dist/harness/tools/event.js.map +1 -1
  153. package/dist/harness/tools/generate-image.d.ts.map +1 -1
  154. package/dist/harness/tools/generate-image.js +17 -3
  155. package/dist/harness/tools/generate-image.js.map +1 -1
  156. package/dist/harness/tools/jev-browser.d.ts +1 -2
  157. package/dist/harness/tools/jev-browser.d.ts.map +1 -1
  158. package/dist/harness/tools/jev-browser.js +75 -50
  159. package/dist/harness/tools/jev-browser.js.map +1 -1
  160. package/dist/harness/types.d.ts +16 -0
  161. package/dist/harness/types.d.ts.map +1 -1
  162. package/dist/harness/types.js.map +1 -1
  163. package/dist/index.d.ts +2 -3
  164. package/dist/index.d.ts.map +1 -1
  165. package/dist/index.js +2 -3
  166. package/dist/index.js.map +1 -1
  167. package/dist/main.js +40 -93
  168. package/dist/main.js.map +1 -1
  169. package/dist/memory-capture/index.d.ts.map +1 -1
  170. package/dist/memory-capture/index.js +3 -3
  171. package/dist/memory-capture/index.js.map +1 -1
  172. package/dist/migrations/actor-vaults.d.ts +3 -0
  173. package/dist/migrations/actor-vaults.d.ts.map +1 -0
  174. package/dist/migrations/actor-vaults.js +43 -0
  175. package/dist/migrations/actor-vaults.js.map +1 -0
  176. package/dist/migrations/conversation-settings.d.ts +3 -0
  177. package/dist/migrations/conversation-settings.d.ts.map +1 -0
  178. package/dist/migrations/conversation-settings.js +31 -0
  179. package/dist/migrations/conversation-settings.js.map +1 -0
  180. package/dist/migrations/index.d.ts +14 -0
  181. package/dist/migrations/index.d.ts.map +1 -0
  182. package/dist/migrations/index.js +99 -0
  183. package/dist/migrations/index.js.map +1 -0
  184. package/dist/migrations/models-json.d.ts +3 -0
  185. package/dist/migrations/models-json.d.ts.map +1 -0
  186. package/dist/migrations/models-json.js +18 -0
  187. package/dist/migrations/models-json.js.map +1 -0
  188. package/dist/migrations/office-layout.d.ts +4 -0
  189. package/dist/migrations/office-layout.d.ts.map +1 -0
  190. package/dist/migrations/office-layout.js +94 -0
  191. package/dist/migrations/office-layout.js.map +1 -0
  192. package/dist/migrations/office-sessions.d.ts +3 -0
  193. package/dist/migrations/office-sessions.d.ts.map +1 -0
  194. package/dist/migrations/office-sessions.js +70 -0
  195. package/dist/migrations/office-sessions.js.map +1 -0
  196. package/dist/migrations/sandbox-containers.d.ts +3 -0
  197. package/dist/migrations/sandbox-containers.d.ts.map +1 -0
  198. package/dist/migrations/sandbox-containers.js +36 -0
  199. package/dist/migrations/sandbox-containers.js.map +1 -0
  200. package/dist/{sessions/migrate-common.d.ts → migrations/session-files.d.ts} +1 -2
  201. package/dist/migrations/session-files.d.ts.map +1 -0
  202. package/dist/{sessions/migrate-common.js → migrations/session-files.js} +2 -2
  203. package/dist/migrations/session-files.js.map +1 -0
  204. package/dist/{sessions/migrate-v3.d.ts → migrations/sessions-v3.d.ts} +4 -3
  205. package/dist/migrations/sessions-v3.d.ts.map +1 -0
  206. package/dist/{sessions/migrate-v3.js → migrations/sessions-v3.js} +14 -4
  207. package/dist/migrations/sessions-v3.js.map +1 -0
  208. package/dist/migrations/types.d.ts +30 -0
  209. package/dist/migrations/types.d.ts.map +1 -0
  210. package/dist/migrations/types.js +2 -0
  211. package/dist/migrations/types.js.map +1 -0
  212. package/dist/migrations/workspace-events.d.ts +3 -0
  213. package/dist/migrations/workspace-events.d.ts.map +1 -0
  214. package/dist/migrations/workspace-events.js +43 -0
  215. package/dist/migrations/workspace-events.js.map +1 -0
  216. package/dist/office/index.d.ts +2 -24
  217. package/dist/office/index.d.ts.map +1 -1
  218. package/dist/office/index.js +15 -579
  219. package/dist/office/index.js.map +1 -1
  220. package/dist/office/types.d.ts +0 -10
  221. package/dist/office/types.d.ts.map +1 -1
  222. package/dist/office/types.js.map +1 -1
  223. package/dist/runtime/conversation-runtime.js +12 -12
  224. package/dist/runtime/conversation-runtime.js.map +1 -1
  225. package/dist/sandbox/identity.d.ts +2 -3
  226. package/dist/sandbox/identity.d.ts.map +1 -1
  227. package/dist/sandbox/identity.js +6 -13
  228. package/dist/sandbox/identity.js.map +1 -1
  229. package/dist/sandbox/layout.d.ts +0 -2
  230. package/dist/sandbox/layout.d.ts.map +1 -1
  231. package/dist/sandbox/layout.js +2 -2
  232. package/dist/sandbox/layout.js.map +1 -1
  233. package/dist/sandbox/provisioner.d.ts +4 -38
  234. package/dist/sandbox/provisioner.d.ts.map +1 -1
  235. package/dist/sandbox/provisioner.js +32 -456
  236. package/dist/sandbox/provisioner.js.map +1 -1
  237. package/dist/sessions/chat-history-sync.d.ts.map +1 -1
  238. package/dist/sessions/chat-history-sync.js +28 -40
  239. package/dist/sessions/chat-history-sync.js.map +1 -1
  240. package/dist/sessions/session-store.d.ts.map +1 -1
  241. package/dist/sessions/session-store.js +1 -8
  242. package/dist/sessions/session-store.js.map +1 -1
  243. package/dist/sessions/store.d.ts +2 -3
  244. package/dist/sessions/store.d.ts.map +1 -1
  245. package/dist/sessions/store.js +8 -14
  246. package/dist/sessions/store.js.map +1 -1
  247. package/dist/sessions/types.d.ts +6 -15
  248. package/dist/sessions/types.d.ts.map +1 -1
  249. package/dist/sessions/types.js.map +1 -1
  250. package/dist/settings/index.d.ts +0 -48
  251. package/dist/settings/index.d.ts.map +1 -1
  252. package/dist/settings/index.js +6 -37
  253. package/dist/settings/index.js.map +1 -1
  254. package/dist/types.d.ts +0 -29
  255. package/dist/types.d.ts.map +1 -1
  256. package/dist/types.js.map +1 -1
  257. package/dist/vault/index.d.ts +0 -7
  258. package/dist/vault/index.d.ts.map +1 -1
  259. package/dist/vault/index.js +1 -36
  260. package/dist/vault/index.js.map +1 -1
  261. package/package.json +8 -6
  262. package/dist/adapters/github/repo.d.ts +0 -8
  263. package/dist/adapters/github/repo.d.ts.map +0 -1
  264. package/dist/adapters/github/repo.js +0 -92
  265. package/dist/adapters/github/repo.js.map +0 -1
  266. package/dist/adapters/github/tools/sync.d.ts +0 -13
  267. package/dist/adapters/github/tools/sync.d.ts.map +0 -1
  268. package/dist/adapters/github/tools/sync.js +0 -29
  269. package/dist/adapters/github/tools/sync.js.map +0 -1
  270. package/dist/cli/sandbox.d.ts +0 -3
  271. package/dist/cli/sandbox.d.ts.map +0 -1
  272. package/dist/cli/sandbox.js +0 -80
  273. package/dist/cli/sandbox.js.map +0 -1
  274. package/dist/cli/sessions.d.ts +0 -2
  275. package/dist/cli/sessions.d.ts.map +0 -1
  276. package/dist/cli/sessions.js +0 -78
  277. package/dist/cli/sessions.js.map +0 -1
  278. package/dist/sessions/migrate-common.d.ts.map +0 -1
  279. package/dist/sessions/migrate-common.js.map +0 -1
  280. package/dist/sessions/migrate-pi-084.d.ts +0 -7
  281. package/dist/sessions/migrate-pi-084.d.ts.map +0 -1
  282. package/dist/sessions/migrate-pi-084.js +0 -293
  283. package/dist/sessions/migrate-pi-084.js.map +0 -1
  284. package/dist/sessions/migrate-v3.d.ts.map +0 -1
  285. package/dist/sessions/migrate-v3.js.map +0 -1
  286. package/dist/settings/migrate.d.ts +0 -16
  287. package/dist/settings/migrate.d.ts.map +0 -1
  288. package/dist/settings/migrate.js +0 -70
  289. package/dist/settings/migrate.js.map +0 -1
@@ -0,0 +1,18 @@
1
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { atomicWritePrivateFile } from "../file-guards.js";
4
+ export const modelsJsonMigration = Object.freeze({
5
+ id: "0007-models-json",
6
+ summary: "copy custom model providers from Pi's models.json to mikan's own",
7
+ async run(context) {
8
+ const source = join(context.piAgentDir, "models.json");
9
+ if (existsSync(context.modelsPath) || !existsSync(source))
10
+ return;
11
+ context.report(` models.json ${source} -> ${context.modelsPath}`);
12
+ if (context.dryRun)
13
+ return;
14
+ mkdirSync(dirname(context.modelsPath), { recursive: true, mode: 0o700 });
15
+ atomicWritePrivateFile(context.modelsPath, readFileSync(source, "utf-8"));
16
+ },
17
+ });
18
+ //# sourceMappingURL=models-json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models-json.js","sourceRoot":"","sources":["../../src/migrations/models-json.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAG3D,MAAM,CAAC,MAAM,mBAAmB,GAAc,MAAM,CAAC,MAAM,CAAC;IAC1D,EAAE,EAAE,kBAAkB;IACtB,OAAO,EAAE,kEAAkE;IAC3E,KAAK,CAAC,GAAG,CAAC,OAAyB;QACjC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;QACvD,IAAI,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,OAAO;QAClE,OAAO,CAAC,MAAM,CAAC,iBAAiB,MAAM,OAAO,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;QACnE,IAAI,OAAO,CAAC,MAAM;YAAE,OAAO;QAC3B,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;QACzE,sBAAsB,CAAC,OAAO,CAAC,UAAU,EAAE,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5E,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { existsSync, mkdirSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { atomicWritePrivateFile } from \"../file-guards.js\";\nimport type { Migration, MigrationContext } from \"./types.js\";\n\nexport const modelsJsonMigration: Migration = Object.freeze({\n id: \"0007-models-json\",\n summary: \"copy custom model providers from Pi's models.json to mikan's own\",\n async run(context: MigrationContext): Promise<void> {\n const source = join(context.piAgentDir, \"models.json\");\n if (existsSync(context.modelsPath) || !existsSync(source)) return;\n context.report(` models.json ${source} -> ${context.modelsPath}`);\n if (context.dryRun) return;\n mkdirSync(dirname(context.modelsPath), { recursive: true, mode: 0o700 });\n atomicWritePrivateFile(context.modelsPath, readFileSync(source, \"utf-8\"));\n },\n});\n"]}
@@ -0,0 +1,4 @@
1
+ import type { Migration } from "./types.js";
2
+ export declare function legacyResourceKey(conversationId: string): string;
3
+ export declare const officeLayoutMigration: Migration;
4
+ //# sourceMappingURL=office-layout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"office-layout.d.ts","sourceRoot":"","sources":["../../src/migrations/office-layout.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AAS9D,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAMhE;AA0ED,eAAO,MAAM,qBAAqB,EAAE,SAOlC,CAAC"}
@@ -0,0 +1,94 @@
1
+ import { existsSync, lstatSync, readdirSync, renameSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { createOfficeAddress, isOfficeKey, OFFICE_LOG_FILENAME, officeDir, officeKey, OfficeRegistry, RESERVED_WORKSPACE_NAMES, } from "../office/index.js";
4
+ const LEGACY_ID_FORMATS = Object.freeze({
5
+ slack: /^[A-Z][A-Z0-9]*$/,
6
+ telegram: /^-?\d+$/,
7
+ discord: /^\d+$/,
8
+ github: /^$/,
9
+ });
10
+ export function legacyResourceKey(conversationId) {
11
+ const sanitized = conversationId
12
+ .toLowerCase()
13
+ .replace(/[^a-z0-9]+/g, "-")
14
+ .replace(/^-+|-+$/g, "");
15
+ return sanitized || "unknown";
16
+ }
17
+ function listLegacyOfficeDirs(workspaceRoot) {
18
+ if (!existsSync(workspaceRoot))
19
+ return [];
20
+ return readdirSync(workspaceRoot, { withFileTypes: true })
21
+ .filter((entry) => entry.isDirectory() &&
22
+ !entry.name.startsWith(".") &&
23
+ !RESERVED_WORKSPACE_NAMES.has(entry.name) &&
24
+ !isOfficeKey(entry.name) &&
25
+ (existsSync(join(workspaceRoot, entry.name, OFFICE_LOG_FILENAME)) ||
26
+ existsSync(join(workspaceRoot, entry.name, "sessions"))))
27
+ .map((entry) => entry.name)
28
+ .toSorted();
29
+ }
30
+ function resolveOwner(conversationId, context) {
31
+ const explicit = context.owners.get(conversationId);
32
+ if (explicit)
33
+ return explicit;
34
+ const candidates = context.enabledPlatforms.filter((platform) => LEGACY_ID_FORMATS[platform].test(conversationId));
35
+ return candidates.length === 1 ? candidates[0] : undefined;
36
+ }
37
+ function resolveAddresses(ids, context) {
38
+ const addresses = [];
39
+ const unowned = [];
40
+ for (const id of ids) {
41
+ const platform = resolveOwner(id, context);
42
+ if (platform)
43
+ addresses.push(createOfficeAddress(platform, id));
44
+ else
45
+ unowned.push(id);
46
+ }
47
+ if (unowned.length > 0) {
48
+ throw new Error([
49
+ "These conversation directories match no single enabled platform:",
50
+ ...unowned.map((id) => ` - ${id}`),
51
+ "Enable the owning platform's token, or name it with --owner <conversationId>=<platform>.",
52
+ ].join("\n"));
53
+ }
54
+ return addresses;
55
+ }
56
+ function moveVault(context, address) {
57
+ const vaultsDir = join(context.stateDir, "vaults");
58
+ const source = join(vaultsDir, legacyResourceKey(address.conversationId));
59
+ if (!existsSync(source))
60
+ return;
61
+ const target = join(vaultsDir, officeKey(address));
62
+ if (existsSync(target)) {
63
+ throw new Error(`Both a legacy and an office vault exist; merge ${source} into ${target}`);
64
+ }
65
+ context.report(` vault ${legacyResourceKey(address.conversationId)} -> ${officeKey(address)}`);
66
+ if (!context.dryRun)
67
+ renameSync(source, target);
68
+ }
69
+ function moveOffice(context, address) {
70
+ const source = join(context.workspaceRoot, address.conversationId);
71
+ const target = officeDir(context.workspaceRoot, address);
72
+ if (lstatSync(source).isSymbolicLink()) {
73
+ throw new Error(`Conversation directory must not be a symlink: ${source}`);
74
+ }
75
+ if (existsSync(target)) {
76
+ throw new Error(`Both a legacy and an office directory exist; merge ${source} into ${target}`);
77
+ }
78
+ context.report(` office ${address.conversationId} -> ${officeKey(address)}`);
79
+ if (!context.dryRun)
80
+ new OfficeRegistry(context.stateDir).recordOffice(address);
81
+ moveVault(context, address);
82
+ if (!context.dryRun)
83
+ renameSync(source, target);
84
+ }
85
+ export const officeLayoutMigration = Object.freeze({
86
+ id: "0001-office-layout",
87
+ summary: "move conversation directories and their vaults to office keys",
88
+ async run(context) {
89
+ const addresses = resolveAddresses(listLegacyOfficeDirs(context.workspaceRoot), context);
90
+ for (const address of addresses)
91
+ moveOffice(context, address);
92
+ },
93
+ });
94
+ //# sourceMappingURL=office-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"office-layout.js","sourceRoot":"","sources":["../../src/migrations/office-layout.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, lstatSync, readdirSync, renameSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport {\n createOfficeAddress,\n isOfficeKey,\n OFFICE_LOG_FILENAME,\n officeDir,\n officeKey,\n OfficeRegistry,\n RESERVED_WORKSPACE_NAMES,\n} from \"../office/index.js\";\nimport type { OfficeAddress, PlatformName } from \"../types.js\";\nimport type { Migration, MigrationContext } from \"./types.js\";\n\nconst LEGACY_ID_FORMATS: Readonly<Record<PlatformName, RegExp>> = Object.freeze({\n slack: /^[A-Z][A-Z0-9]*$/,\n telegram: /^-?\\d+$/,\n discord: /^\\d+$/,\n github: /^$/,\n});\n\nexport function legacyResourceKey(conversationId: string): string {\n const sanitized = conversationId\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"unknown\";\n}\n\nfunction listLegacyOfficeDirs(workspaceRoot: string): string[] {\n if (!existsSync(workspaceRoot)) return [];\n return readdirSync(workspaceRoot, { withFileTypes: true })\n .filter(\n (entry) =>\n entry.isDirectory() &&\n !entry.name.startsWith(\".\") &&\n !RESERVED_WORKSPACE_NAMES.has(entry.name) &&\n !isOfficeKey(entry.name) &&\n (existsSync(join(workspaceRoot, entry.name, OFFICE_LOG_FILENAME)) ||\n existsSync(join(workspaceRoot, entry.name, \"sessions\"))),\n )\n .map((entry) => entry.name)\n .toSorted();\n}\n\nfunction resolveOwner(conversationId: string, context: MigrationContext): PlatformName | undefined {\n const explicit = context.owners.get(conversationId);\n if (explicit) return explicit;\n const candidates = context.enabledPlatforms.filter((platform) =>\n LEGACY_ID_FORMATS[platform].test(conversationId),\n );\n return candidates.length === 1 ? candidates[0] : undefined;\n}\n\nfunction resolveAddresses(ids: readonly string[], context: MigrationContext): OfficeAddress[] {\n const addresses: OfficeAddress[] = [];\n const unowned: string[] = [];\n for (const id of ids) {\n const platform = resolveOwner(id, context);\n if (platform) addresses.push(createOfficeAddress(platform, id));\n else unowned.push(id);\n }\n if (unowned.length > 0) {\n throw new Error(\n [\n \"These conversation directories match no single enabled platform:\",\n ...unowned.map((id) => ` - ${id}`),\n \"Enable the owning platform's token, or name it with --owner <conversationId>=<platform>.\",\n ].join(\"\\n\"),\n );\n }\n return addresses;\n}\n\nfunction moveVault(context: MigrationContext, address: OfficeAddress): void {\n const vaultsDir = join(context.stateDir, \"vaults\");\n const source = join(vaultsDir, legacyResourceKey(address.conversationId));\n if (!existsSync(source)) return;\n const target = join(vaultsDir, officeKey(address));\n if (existsSync(target)) {\n throw new Error(`Both a legacy and an office vault exist; merge ${source} into ${target}`);\n }\n context.report(` vault ${legacyResourceKey(address.conversationId)} -> ${officeKey(address)}`);\n if (!context.dryRun) renameSync(source, target);\n}\n\nfunction moveOffice(context: MigrationContext, address: OfficeAddress): void {\n const source = join(context.workspaceRoot, address.conversationId);\n const target = officeDir(context.workspaceRoot, address);\n if (lstatSync(source).isSymbolicLink()) {\n throw new Error(`Conversation directory must not be a symlink: ${source}`);\n }\n if (existsSync(target)) {\n throw new Error(`Both a legacy and an office directory exist; merge ${source} into ${target}`);\n }\n context.report(` office ${address.conversationId} -> ${officeKey(address)}`);\n if (!context.dryRun) new OfficeRegistry(context.stateDir).recordOffice(address);\n moveVault(context, address);\n if (!context.dryRun) renameSync(source, target);\n}\n\nexport const officeLayoutMigration: Migration = Object.freeze({\n id: \"0001-office-layout\",\n summary: \"move conversation directories and their vaults to office keys\",\n async run(context: MigrationContext): Promise<void> {\n const addresses = resolveAddresses(listLegacyOfficeDirs(context.workspaceRoot), context);\n for (const address of addresses) moveOffice(context, address);\n },\n});\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Migration } from "./types.js";
2
+ export declare const officeSessionsMigration: Migration;
3
+ //# sourceMappingURL=office-sessions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"office-sessions.d.ts","sourceRoot":"","sources":["../../src/migrations/office-sessions.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AA8C9D,eAAO,MAAM,uBAAuB,EAAE,SAcpC,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { constants as fsConstants, copyFileSync, existsSync, lstatSync, mkdirSync, readdirSync, renameSync, rmdirSync, unlinkSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { listRegisteredOffices, officeDir, officeStateDir } from "../office/index.js";
4
+ const SESSIONS_DIRNAME = "sessions";
5
+ function moveFile(source, target) {
6
+ try {
7
+ renameSync(source, target);
8
+ }
9
+ catch (error) {
10
+ if (!(error instanceof Error && "code" in error && error.code === "EXDEV"))
11
+ throw error;
12
+ copyFileSync(source, target, fsConstants.COPYFILE_EXCL);
13
+ unlinkSync(source);
14
+ }
15
+ }
16
+ function dropLink(context, path) {
17
+ context.report(` skip link ${path} (removed, never followed)`);
18
+ if (!context.dryRun)
19
+ unlinkSync(path);
20
+ }
21
+ function moveSessions(context, source, target) {
22
+ if (lstatSync(source).isSymbolicLink()) {
23
+ dropLink(context, source);
24
+ return;
25
+ }
26
+ if (!lstatSync(source).isDirectory()) {
27
+ throw new Error(`Expected a sessions directory: ${source}`);
28
+ }
29
+ if (existsSync(target) && readdirSync(target).length > 0) {
30
+ throw new Error(`Both ${source} and ${target} exist; merge them into ${target}`);
31
+ }
32
+ context.report(` sessions ${source} -> ${target}`);
33
+ const entries = readdirSync(source, { withFileTypes: true });
34
+ const unexpected = entries.find((entry) => !entry.isFile() && !entry.isSymbolicLink());
35
+ if (unexpected) {
36
+ throw new Error(`Unexpected entry in sessions directory: ${join(source, unexpected.name)}`);
37
+ }
38
+ if (context.dryRun)
39
+ return;
40
+ mkdirSync(target, { recursive: true, mode: 0o700 });
41
+ for (const entry of entries) {
42
+ const from = join(source, entry.name);
43
+ if (entry.isSymbolicLink())
44
+ dropLink(context, from);
45
+ else
46
+ moveFile(from, join(target, entry.name));
47
+ }
48
+ rmdirSync(source);
49
+ }
50
+ export const officeSessionsMigration = Object.freeze({
51
+ id: "0008-office-sessions",
52
+ summary: "move session files out of the sandbox-visible office directory",
53
+ async run(context) {
54
+ for (const office of listRegisteredOffices(context.stateDir)) {
55
+ const source = join(officeDir(context.workspaceRoot, office), SESSIONS_DIRNAME);
56
+ if (!existsSync(source) && !isLink(source))
57
+ continue;
58
+ moveSessions(context, source, join(officeStateDir(context.stateDir, office), SESSIONS_DIRNAME));
59
+ }
60
+ },
61
+ });
62
+ function isLink(path) {
63
+ try {
64
+ return lstatSync(path).isSymbolicLink();
65
+ }
66
+ catch {
67
+ return false;
68
+ }
69
+ }
70
+ //# sourceMappingURL=office-sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"office-sessions.js","sourceRoot":"","sources":["../../src/migrations/office-sessions.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n constants as fsConstants,\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n renameSync,\n rmdirSync,\n unlinkSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { listRegisteredOffices, officeDir, officeStateDir } from \"../office/index.js\";\nimport type { Migration, MigrationContext } from \"./types.js\";\n\nconst SESSIONS_DIRNAME = \"sessions\";\n\nfunction moveFile(source: string, target: string): void {\n try {\n renameSync(source, target);\n } catch (error) {\n if (!(error instanceof Error && \"code\" in error && error.code === \"EXDEV\")) throw error;\n copyFileSync(source, target, fsConstants.COPYFILE_EXCL);\n unlinkSync(source);\n }\n}\n\nfunction dropLink(context: MigrationContext, path: string): void {\n context.report(` skip link ${path} (removed, never followed)`);\n if (!context.dryRun) unlinkSync(path);\n}\n\nfunction moveSessions(context: MigrationContext, source: string, target: string): void {\n if (lstatSync(source).isSymbolicLink()) {\n dropLink(context, source);\n return;\n }\n if (!lstatSync(source).isDirectory()) {\n throw new Error(`Expected a sessions directory: ${source}`);\n }\n if (existsSync(target) && readdirSync(target).length > 0) {\n throw new Error(`Both ${source} and ${target} exist; merge them into ${target}`);\n }\n context.report(` sessions ${source} -> ${target}`);\n const entries = readdirSync(source, { withFileTypes: true });\n const unexpected = entries.find((entry) => !entry.isFile() && !entry.isSymbolicLink());\n if (unexpected) {\n throw new Error(`Unexpected entry in sessions directory: ${join(source, unexpected.name)}`);\n }\n if (context.dryRun) return;\n mkdirSync(target, { recursive: true, mode: 0o700 });\n for (const entry of entries) {\n const from = join(source, entry.name);\n if (entry.isSymbolicLink()) dropLink(context, from);\n else moveFile(from, join(target, entry.name));\n }\n rmdirSync(source);\n}\n\nexport const officeSessionsMigration: Migration = Object.freeze({\n id: \"0008-office-sessions\",\n summary: \"move session files out of the sandbox-visible office directory\",\n async run(context: MigrationContext): Promise<void> {\n for (const office of listRegisteredOffices(context.stateDir)) {\n const source = join(officeDir(context.workspaceRoot, office), SESSIONS_DIRNAME);\n if (!existsSync(source) && !isLink(source)) continue;\n moveSessions(\n context,\n source,\n join(officeStateDir(context.stateDir, office), SESSIONS_DIRNAME),\n );\n }\n },\n});\n\nfunction isLink(path: string): boolean {\n try {\n return lstatSync(path).isSymbolicLink();\n } catch {\n return false;\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Migration } from "./types.js";
2
+ export declare const sandboxContainersMigration: Migration;
3
+ //# sourceMappingURL=sandbox-containers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-containers.d.ts","sourceRoot":"","sources":["../../src/migrations/sandbox-containers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AAgB9D,eAAO,MAAM,0BAA0B,EAAE,SAiBvC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { listRegisteredOffices } from "../office/index.js";
2
+ import { legacyResourceKey } from "./office-layout.js";
3
+ function isMissingExecutable(error) {
4
+ return error instanceof Error && "code" in error && error.code === "ENOENT";
5
+ }
6
+ async function listContainerNames(context) {
7
+ try {
8
+ const stdout = await context.docker(["ps", "-a", "--format", "{{.Names}}"]);
9
+ return new Set(stdout.split("\n").map((name) => name.trim()));
10
+ }
11
+ catch (error) {
12
+ if (isMissingExecutable(error))
13
+ return undefined;
14
+ throw error;
15
+ }
16
+ }
17
+ export const sandboxContainersMigration = Object.freeze({
18
+ id: "0006-sandbox-containers",
19
+ summary: "remove sandbox containers named by raw conversation IDs; offices recreate their own",
20
+ async run(context) {
21
+ const existing = await listContainerNames(context);
22
+ if (!existing) {
23
+ context.report(" docker is not installed; no containers to remove");
24
+ return;
25
+ }
26
+ const legacyNames = listRegisteredOffices(context.stateDir)
27
+ .map((office) => `mikan-sandbox-${legacyResourceKey(office.conversationId)}`)
28
+ .filter((name) => existing.has(name));
29
+ for (const name of new Set(legacyNames)) {
30
+ context.report(` container ${name}`);
31
+ if (!context.dryRun)
32
+ await context.docker(["rm", "-f", name]);
33
+ }
34
+ },
35
+ });
36
+ //# sourceMappingURL=sandbox-containers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sandbox-containers.js","sourceRoot":"","sources":["../../src/migrations/sandbox-containers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGvD,SAAS,mBAAmB,CAAC,KAAc;IACzC,OAAO,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC9E,CAAC;AAED,KAAK,UAAU,kBAAkB,CAAC,OAAyB;IACzD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;QAC5E,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,mBAAmB,CAAC,KAAK,CAAC;YAAE,OAAO,SAAS,CAAC;QACjD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,0BAA0B,GAAc,MAAM,CAAC,MAAM,CAAC;IACjE,EAAE,EAAE,yBAAyB;IAC7B,OAAO,EAAE,qFAAqF;IAC9F,KAAK,CAAC,GAAG,CAAC,OAAyB;QACjC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,MAAM,CAAC,oDAAoD,CAAC,CAAC;YACrE,OAAO;QACT,CAAC;QACD,MAAM,WAAW,GAAG,qBAAqB,CAAC,OAAO,CAAC,QAAQ,CAAC;aACxD,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,iBAAiB,iBAAiB,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,CAAC;aAC5E,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;QACxC,KAAK,MAAM,IAAI,IAAI,IAAI,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;YACxC,OAAO,CAAC,MAAM,CAAC,eAAe,IAAI,EAAE,CAAC,CAAC;YACtC,IAAI,CAAC,OAAO,CAAC,MAAM;gBAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;CACF,CAAC,CAAC","sourcesContent":["import { listRegisteredOffices } from \"../office/index.js\";\nimport { legacyResourceKey } from \"./office-layout.js\";\nimport type { Migration, MigrationContext } from \"./types.js\";\n\nfunction isMissingExecutable(error: unknown): boolean {\n return error instanceof Error && \"code\" in error && error.code === \"ENOENT\";\n}\n\nasync function listContainerNames(context: MigrationContext): Promise<Set<string> | undefined> {\n try {\n const stdout = await context.docker([\"ps\", \"-a\", \"--format\", \"{{.Names}}\"]);\n return new Set(stdout.split(\"\\n\").map((name) => name.trim()));\n } catch (error) {\n if (isMissingExecutable(error)) return undefined;\n throw error;\n }\n}\n\nexport const sandboxContainersMigration: Migration = Object.freeze({\n id: \"0006-sandbox-containers\",\n summary: \"remove sandbox containers named by raw conversation IDs; offices recreate their own\",\n async run(context: MigrationContext): Promise<void> {\n const existing = await listContainerNames(context);\n if (!existing) {\n context.report(\" docker is not installed; no containers to remove\");\n return;\n }\n const legacyNames = listRegisteredOffices(context.stateDir)\n .map((office) => `mikan-sandbox-${legacyResourceKey(office.conversationId)}`)\n .filter((name) => existing.has(name));\n for (const name of new Set(legacyNames)) {\n context.report(` container ${name}`);\n if (!context.dryRun) await context.docker([\"rm\", \"-f\", name]);\n }\n },\n});\n"]}
@@ -1,6 +1,5 @@
1
1
  import type { Entry as PiEntry, JsonValue } from "@earendil-works/pi-agent-core";
2
2
  import type { Usage } from "@earendil-works/pi-ai";
3
- export declare function jsonValue(value: unknown): JsonValue;
4
3
  export declare function optionalAnnotations(source: {
5
4
  details?: unknown;
6
5
  usage?: unknown;
@@ -25,4 +24,4 @@ export declare function commitMigration(options: {
25
24
  backupPath: string;
26
25
  verify: (candidatePath: string) => Promise<void>;
27
26
  }): Promise<void>;
28
- //# sourceMappingURL=migrate-common.d.ts.map
27
+ //# sourceMappingURL=session-files.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-files.d.ts","sourceRoot":"","sources":["../../src/migrations/session-files.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,IAAI,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AACjF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAOnD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG;IACnF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,CAKA;AAED,qBAAa,YAAY;;IAIvB,YAAY,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAE1C;IAED,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAE1B;IAED,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAExD;IAED,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAE3D;IAMD,QAAQ,IAAI,MAAM,CAEjB;CACF;AAeD,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,MAAM,EAAE,CAK/F;AAUD,wBAAsB,eAAe,CAAC,OAAO,EAAE;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAClD,GAAG,OAAO,CAAC,IAAI,CAAC,CAehB"}
@@ -1,7 +1,7 @@
1
1
  import { existsSync, linkSync, readFileSync, readdirSync, renameSync, rmSync, statSync, } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { atomicWritePrivateFile } from "../file-guards.js";
4
- export function jsonValue(value) {
4
+ function jsonValue(value) {
5
5
  return JSON.parse(JSON.stringify(value));
6
6
  }
7
7
  export function optionalAnnotations(source) {
@@ -77,4 +77,4 @@ export async function commitMigration(options) {
77
77
  throw new Error(`Migration failed for ${filePath}`, { cause: error });
78
78
  }
79
79
  }
80
- //# sourceMappingURL=migrate-common.js.map
80
+ //# sourceMappingURL=session-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-files.js","sourceRoot":"","sources":["../../src/migrations/session-files.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n existsSync,\n linkSync,\n readFileSync,\n readdirSync,\n renameSync,\n rmSync,\n statSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { Entry as PiEntry, JsonValue } from \"@earendil-works/pi-agent-core\";\nimport type { Usage } from \"@earendil-works/pi-ai\";\nimport { atomicWritePrivateFile } from \"../file-guards.js\";\n\nfunction jsonValue(value: unknown): JsonValue {\n return JSON.parse(JSON.stringify(value)) as JsonValue;\n}\n\nexport function optionalAnnotations(source: { details?: unknown; usage?: unknown }): {\n details?: JsonValue;\n usage?: Usage;\n} {\n return {\n details: source.details !== undefined ? jsonValue(source.details) : undefined,\n usage: source.usage as Usage | undefined,\n };\n}\n\nexport class V4FileWriter {\n readonly #lines: string[];\n #seq = 0;\n\n constructor(header: Record<string, unknown>) {\n this.#lines = [JSON.stringify(header)];\n }\n\n entry(entry: PiEntry): void {\n this.#lines.push(JSON.stringify({ kind: \"entry\", ...entry, seq: ++this.#seq }));\n }\n\n set(namespace: string, key: string, value: unknown): void {\n this.#write(\"value\", \"set\", namespace, key, value);\n }\n\n append(namespace: string, key: string, value: unknown): void {\n this.#write(\"list\", \"append\", namespace, key, value);\n }\n\n #write(kind: string, op: string, namespace: string, key: string, value: unknown): void {\n this.#lines.push(JSON.stringify({ kind, op, seq: ++this.#seq, namespace, key, value }));\n }\n\n toString(): string {\n return `${this.#lines.join(\"\\n\")}\\n`;\n }\n}\n\nfunction collectSessionFiles(\n dir: string,\n isCandidate: (path: string) => boolean,\n found: string[],\n): void {\n for (const entry of readdirSync(dir, { withFileTypes: true })) {\n const path = join(dir, entry.name);\n if (entry.isSymbolicLink()) continue;\n if (entry.isDirectory()) collectSessionFiles(path, isCandidate, found);\n else if (entry.name.endsWith(\".jsonl\") && isCandidate(path)) found.push(path);\n }\n}\n\nexport function findSessionFiles(root: string, isCandidate: (path: string) => boolean): string[] {\n if (!existsSync(root)) return [];\n const found: string[] = [];\n collectSessionFiles(root, isCandidate, found);\n return found;\n}\n\nfunction backupAlreadyLinked(sourcePath: string, backupPath: string): boolean {\n if (!existsSync(backupPath)) return false;\n const source = statSync(sourcePath);\n const backup = statSync(backupPath);\n if (source.dev === backup.dev && source.ino === backup.ino) return true;\n throw new Error(`Backup already exists: ${backupPath}`);\n}\n\nexport async function commitMigration(options: {\n filePath: string;\n sourceBytes: Buffer;\n encoded: string;\n tempPath: string;\n backupPath: string;\n verify: (candidatePath: string) => Promise<void>;\n}): Promise<void> {\n const { filePath, tempPath, backupPath } = options;\n const backupLinked = backupAlreadyLinked(filePath, backupPath);\n atomicWritePrivateFile(tempPath, options.encoded);\n try {\n await options.verify(tempPath);\n if (!readFileSync(filePath).equals(options.sourceBytes)) {\n throw new Error(\"source changed during migration\");\n }\n if (!backupLinked) linkSync(filePath, backupPath);\n renameSync(tempPath, filePath);\n } catch (error) {\n rmSync(tempPath, { force: true });\n throw new Error(`Migration failed for ${filePath}`, { cause: error });\n }\n}\n"]}
@@ -1,7 +1,8 @@
1
- import type { MigrateResult } from "./types.js";
1
+ import type { Migration, SessionMigrationResult } from "./types.js";
2
2
  export declare function isV3SessionFile(filePath: string): boolean;
3
3
  export declare function migrateSessionFile(filePath: string, options?: {
4
4
  dryRun?: boolean;
5
- }): Promise<MigrateResult>;
5
+ }): Promise<SessionMigrationResult>;
6
6
  export declare function findV3SessionFiles(root: string): string[];
7
- //# sourceMappingURL=migrate-v3.d.ts.map
7
+ export declare const sessionsV3Migration: Migration;
8
+ //# sourceMappingURL=sessions-v3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions-v3.d.ts","sourceRoot":"","sources":["../../src/migrations/sessions-v3.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,SAAS,EAAoB,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAmEtF,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAUzD;AAoVD,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAC7B,OAAO,CAAC,sBAAsB,CAAC,CAejC;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAEzD;AAED,eAAO,MAAM,mBAAmB,EAAE,SAShC,CAAC"}
@@ -1,7 +1,7 @@
1
1
  import { readFileSync } from "node:fs";
2
2
  import { createBranchSummaryMessage, createCompactionSummaryMessage, createCustomMessage, } from "@earendil-works/pi-agent-core";
3
- import { SessionStore } from "./session-store.js";
4
- import { commitMigration, findSessionFiles, optionalAnnotations, V4FileWriter, } from "./migrate-common.js";
3
+ import { SessionStore } from "../sessions/session-store.js";
4
+ import { commitMigration, findSessionFiles, optionalAnnotations, V4FileWriter, } from "./session-files.js";
5
5
  function isFactEntry(entry) {
6
6
  return entry.type === "session_info" || entry.type === "label" || entry.type === "leaf";
7
7
  }
@@ -330,7 +330,7 @@ async function verifyMigratedFile(v4Path, source) {
330
330
  }
331
331
  export async function migrateSessionFile(filePath, options) {
332
332
  if (!isV3SessionFile(filePath))
333
- return { file: filePath, status: "already-v4" };
333
+ return { file: filePath, status: "already-current" };
334
334
  const sourceBytes = readFileSync(filePath);
335
335
  const source = readV3SessionFile(filePath);
336
336
  if (options?.dryRun)
@@ -348,4 +348,14 @@ export async function migrateSessionFile(filePath, options) {
348
348
  export function findV3SessionFiles(root) {
349
349
  return findSessionFiles(root, isV3SessionFile);
350
350
  }
351
- //# sourceMappingURL=migrate-v3.js.map
351
+ export const sessionsV3Migration = Object.freeze({
352
+ id: "0004-sessions-v3",
353
+ summary: "convert v3 session files to the current Pi session format, keeping *.v3.bak",
354
+ async run(context) {
355
+ for (const file of findV3SessionFiles(context.workspaceRoot)) {
356
+ context.report(` session ${file}`);
357
+ await migrateSessionFile(file, { dryRun: context.dryRun });
358
+ }
359
+ },
360
+ });
361
+ //# sourceMappingURL=sessions-v3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions-v3.js","sourceRoot":"","sources":["../../src/migrations/sessions-v3.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFileSync } from \"node:fs\";\nimport {\n createBranchSummaryMessage,\n createCompactionSummaryMessage,\n createCustomMessage,\n type AgentMessage,\n type Entry as PiEntry,\n type JsonValue,\n} from \"@earendil-works/pi-agent-core\";\nimport { SessionStore } from \"../sessions/session-store.js\";\nimport {\n commitMigration,\n findSessionFiles,\n optionalAnnotations,\n V4FileWriter,\n} from \"./session-files.js\";\nimport type { Migration, MigrationContext, SessionMigrationResult } from \"./types.js\";\n\ninterface V3EntryBase {\n type: string;\n id: string;\n parentId: string | null;\n timestamp: string;\n}\n\ntype V3Entry = V3EntryBase &\n (\n | { type: \"message\"; message: AgentMessage }\n | { type: \"thinking_level_change\"; thinkingLevel: string }\n | { type: \"model_change\"; provider: string; modelId: string }\n | { type: \"active_tools_change\"; activeToolNames: string[] }\n | {\n type: \"compaction\";\n summary: string;\n firstKeptEntryId?: string;\n tokensBefore: number;\n retainedTail?: AgentMessage[];\n details?: unknown;\n usage?: unknown;\n }\n | {\n type: \"branch_summary\";\n fromId: string;\n summary: string;\n details?: unknown;\n usage?: unknown;\n }\n | { type: \"custom\"; customType: string; data?: unknown }\n | {\n type: \"custom_message\";\n customType: string;\n content: Parameters<typeof createCustomMessage>[1];\n display: boolean;\n details?: unknown;\n }\n | { type: \"label\"; targetId: string; label: string | undefined }\n | { type: \"session_info\"; name?: string }\n | { type: \"leaf\"; targetId: string | null }\n );\n\ntype V3Compaction = Extract<V3Entry, { type: \"compaction\" }>;\ntype V3CustomMessage = Extract<V3Entry, { type: \"custom_message\" }>;\n\ninterface V3SessionHeader {\n type: \"session\";\n version?: number;\n id: string;\n timestamp: string;\n cwd: string;\n parentSession?: string;\n parentSessionId?: string;\n [extra: string]: unknown;\n}\n\ninterface V3SessionFile {\n header: V3SessionHeader;\n entries: V3Entry[];\n}\n\nfunction isFactEntry(entry: V3Entry): boolean {\n return entry.type === \"session_info\" || entry.type === \"label\" || entry.type === \"leaf\";\n}\n\nexport function isV3SessionFile(filePath: string): boolean {\n try {\n const content = readFileSync(filePath, \"utf-8\");\n const firstLine = content.split(\"\\n\").find((line) => line.trim().length > 0);\n if (!firstLine) return false;\n const parsed = JSON.parse(firstLine) as { type?: unknown };\n return parsed.type === \"session\";\n } catch {\n return false;\n }\n}\n\nfunction parseJsonLines(filePath: string): Record<string, unknown>[] {\n const lines = readFileSync(filePath, \"utf-8\")\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n const records: Record<string, unknown>[] = [];\n for (const [index, line] of lines.entries()) {\n try {\n records.push(JSON.parse(line) as Record<string, unknown>);\n } catch (error) {\n if (index === lines.length - 1 && index > 0) continue;\n throw new Error(`Invalid JSON on line ${index + 1} of ${filePath}`, { cause: error });\n }\n }\n return records;\n}\n\nfunction dedupeEntries(records: Record<string, unknown>[]): V3Entry[] {\n const entriesById = new Map<string, V3Entry>();\n for (const record of records) {\n if (typeof record.type !== \"string\" || typeof record.id !== \"string\") continue;\n if (record.type === \"session\") continue;\n entriesById.set(record.id, record as unknown as V3Entry);\n }\n return [...entriesById.values()];\n}\n\nfunction readV3SessionFile(filePath: string): V3SessionFile {\n const records = parseJsonLines(filePath);\n const header = records[0];\n if (!header || header.type !== \"session\" || typeof header.id !== \"string\") {\n throw new Error(`Not a v3 session file: ${filePath}`);\n }\n return {\n header: header as unknown as V3SessionHeader,\n entries: dedupeEntries(records.slice(1)),\n };\n}\n\nfunction toEpochMillis(timestamp: string): number {\n const parsed = Date.parse(timestamp);\n return Number.isNaN(parsed) ? 0 : parsed;\n}\n\nfunction v3Branch(entries: V3Entry[], fromId: string | null): V3Entry[] {\n const byId = new Map(entries.map((entry) => [entry.id, entry]));\n const path: V3Entry[] = [];\n let currentId = fromId;\n while (currentId) {\n const entry = byId.get(currentId);\n if (!entry) break;\n path.push(entry);\n currentId = entry.parentId;\n }\n return path.toReversed();\n}\n\nfunction v3LeafId(entries: V3Entry[]): string | null {\n let leafId: string | null = null;\n for (const entry of entries) {\n leafId = entry.type === \"leaf\" ? entry.targetId : entry.id;\n }\n return leafId;\n}\n\nfunction customMessageOf(entry: V3CustomMessage): AgentMessage {\n return createCustomMessage(\n entry.customType,\n entry.content,\n entry.display,\n entry.details,\n toEpochMillis(entry.timestamp),\n );\n}\n\nfunction convertEntry(entry: V3Entry, entries: V3Entry[]): PiEntry | null {\n const base = {\n id: entry.id,\n seq: 0,\n parentId: resolveV4Parent(entry, entries),\n timestamp: toEpochMillis(entry.timestamp),\n };\n switch (entry.type) {\n case \"message\":\n return { ...base, type: \"message\", message: entry.message };\n case \"custom_message\":\n return { ...base, type: \"message\", message: customMessageOf(entry) };\n case \"thinking_level_change\":\n case \"model_change\":\n case \"active_tools_change\": {\n const { id: _id, parentId: _parentId, timestamp: _timestamp, type, ...data } = entry;\n return {\n ...base,\n type: \"custom\",\n customType: `mikan.legacy.${type}`,\n data: data as JsonValue,\n };\n }\n case \"compaction\":\n return {\n ...base,\n type: \"compaction\",\n summary: entry.summary,\n retainedTail: entry.retainedTail ?? compactionKeptMessages(entry, entries),\n tokensBefore: entry.tokensBefore,\n fromHook: false,\n ...optionalAnnotations(entry),\n } as unknown as PiEntry;\n case \"branch_summary\":\n return {\n ...base,\n type: \"branch_summary\",\n fromId: entry.fromId ?? null,\n summary: entry.summary,\n fromHook: false,\n ...optionalAnnotations(entry),\n } as unknown as PiEntry;\n case \"custom\":\n return {\n ...base,\n type: \"custom\",\n customType: entry.customType,\n data: entry.data as JsonValue | undefined,\n };\n default:\n return null;\n }\n}\n\nfunction resolveV4Parent(entry: V3Entry, entries: V3Entry[]): string | null {\n const byId = new Map(entries.map((item) => [item.id, item]));\n let parentId = entry.parentId;\n while (parentId !== null) {\n const parent = byId.get(parentId);\n if (!parent) return null;\n if (!isFactEntry(parent)) return parentId;\n parentId = parent.parentId;\n }\n return null;\n}\n\nfunction compactionKeptMessages(compaction: V3Compaction, entries: V3Entry[]): AgentMessage[] {\n if (!compaction.firstKeptEntryId) return [];\n const ancestors = v3Branch(entries, compaction.parentId);\n const keptStart = ancestors.findIndex((entry) => entry.id === compaction.firstKeptEntryId);\n if (keptStart === -1) return [];\n const messages: AgentMessage[] = [];\n for (const entry of ancestors.slice(keptStart)) {\n if (entry.type === \"message\") messages.push(entry.message);\n else if (entry.type === \"custom_message\") messages.push(customMessageOf(entry));\n }\n return messages;\n}\n\nfunction orderForReference(branch: V3Entry[]): {\n ordered: V3Entry[];\n repositioned?: V3Compaction;\n} {\n const lastIndex = branch.findLastIndex((entry) => entry.type === \"compaction\");\n const lastCompaction = branch[lastIndex] as V3Compaction | undefined;\n const firstKeptId = lastCompaction?.retainedTail ? undefined : lastCompaction?.firstKeptEntryId;\n const firstKeptIndex =\n firstKeptId === undefined ? -1 : branch.findIndex((entry) => entry.id === firstKeptId);\n if (firstKeptIndex === -1 || !lastCompaction) return { ordered: branch };\n return {\n ordered: [\n ...branch.slice(0, firstKeptIndex),\n lastCompaction,\n ...branch.filter((_, index) => index >= firstKeptIndex && index !== lastIndex),\n ],\n repositioned: lastCompaction,\n };\n}\n\nfunction emptiedCompaction(entry: V3Compaction): PiEntry {\n return {\n type: \"compaction\",\n id: entry.id,\n seq: 0,\n parentId: null,\n timestamp: toEpochMillis(entry.timestamp),\n summary: entry.summary,\n retainedTail: [],\n tokensBefore: entry.tokensBefore,\n fromHook: false,\n };\n}\n\nfunction referenceContextEntries(branch: V3Entry[]): PiEntry[] {\n const { ordered, repositioned } = orderForReference(branch);\n const converted: PiEntry[] = [];\n for (const entry of ordered) {\n const piEntry =\n repositioned === entry ? emptiedCompaction(repositioned) : convertEntry(entry, []);\n if (piEntry) converted.push(piEntry);\n }\n let previousId: string | null = null;\n for (const [index, entry] of converted.entries()) {\n entry.seq = index;\n entry.parentId = previousId;\n previousId = entry.id;\n }\n return converted;\n}\n\nfunction isCurrentContextMessage(message: AgentMessage): boolean {\n return (\n message.role !== \"assistant\" ||\n (message.stopReason !== \"error\" &&\n message.stopReason !== \"aborted\" &&\n message.stopReason !== \"deferred\")\n );\n}\n\nfunction contextMessagesOf(entry: PiEntry): AgentMessage[] {\n if (entry.type === \"message\") {\n return isCurrentContextMessage(entry.message) ? [entry.message] : [];\n }\n if (entry.type === \"compaction\") {\n return [\n createCompactionSummaryMessage(entry.summary, entry.tokensBefore, entry.timestamp),\n ...entry.retainedTail.filter(isCurrentContextMessage),\n ];\n }\n if (entry.type === \"branch_summary\" && entry.summary) {\n return [createBranchSummaryMessage(entry.summary, entry.fromId, entry.timestamp)];\n }\n return [];\n}\n\nfunction referenceContextMessages(entries: PiEntry[]): AgentMessage[] {\n const compactionIndex = entries.findLastIndex((entry) => entry.type === \"compaction\");\n const visible = compactionIndex === -1 ? entries : entries.slice(compactionIndex);\n return visible.flatMap(contextMessagesOf);\n}\n\nfunction buildV4Header(header: V3SessionHeader): Record<string, unknown> {\n return {\n v: 4,\n kind: \"header\",\n id: header.id,\n storageVersion: 1,\n createdAt: toEpochMillis(header.timestamp),\n cwd: header.cwd,\n parentSessionId: header.parentSessionId,\n legacyParentSessionPath: header.parentSession,\n };\n}\n\nfunction partitionEntries(entries: V3Entry[]): {\n tree: V3Entry[];\n name?: string;\n labels: Array<{ targetId: string; label: string | undefined }>;\n} {\n const tree: V3Entry[] = [];\n const labels: Array<{ targetId: string; label: string | undefined }> = [];\n let name: string | undefined;\n for (const entry of entries) {\n switch (entry.type) {\n case \"session_info\":\n name = entry.name?.trim() || undefined;\n break;\n case \"label\":\n labels.push({ targetId: entry.targetId, label: entry.label });\n break;\n case \"leaf\":\n break;\n default:\n tree.push(entry);\n }\n }\n return { tree, name, labels };\n}\n\nfunction resolveV4Leaf(entries: V3Entry[]): string | null {\n const byId = new Map(entries.map((entry) => [entry.id, entry]));\n let leafId = v3LeafId(entries);\n while (leafId !== null) {\n const target = byId.get(leafId);\n if (!target) return null;\n if (!isFactEntry(target)) return leafId;\n leafId = target.parentId;\n }\n return null;\n}\n\nfunction headerMetadata(header: V3SessionHeader): Record<string, JsonValue> | undefined {\n const {\n type: _type,\n version: _version,\n id: _id,\n timestamp: _timestamp,\n cwd: _cwd,\n parentSession,\n parentSessionId: _parentSessionId,\n ...extras\n } = header;\n const metadata: Record<string, JsonValue> = extras as Record<string, JsonValue>;\n if (parentSession !== undefined) metadata.parentSessionPath = parentSession;\n return Object.keys(metadata).length > 0 ? metadata : undefined;\n}\n\nfunction encodeV4File(file: V3SessionFile): string {\n const writer = new V4FileWriter(buildV4Header(file.header));\n const { tree, name, labels } = partitionEntries(file.entries);\n for (const entry of tree) {\n const converted = convertEntry(entry, file.entries);\n if (converted) writer.entry(converted);\n }\n writer.set(\"pi.branch.tip\", \"main\", resolveV4Leaf(file.entries));\n if (name !== undefined) writer.set(\"pi.session.name\", \"\", name);\n for (const { targetId, label } of labels) {\n if (label !== undefined) writer.set(\"pi.entry.label\", targetId, label);\n }\n const metadata = headerMetadata(file.header);\n if (metadata) writer.set(\"mikan\", \"metadata\", metadata);\n return writer.toString();\n}\n\nasync function verifyMigratedFile(v4Path: string, source: V3SessionFile): Promise<void> {\n const store = await SessionStore.inspect(v4Path);\n const migratedContext = await store.buildSessionContext();\n const branch = v3Branch(source.entries, v3LeafId(source.entries));\n const migrated = JSON.stringify(migratedContext.messages);\n const reference = JSON.stringify(referenceContextMessages(referenceContextEntries(branch)));\n if (migrated !== reference) {\n throw new Error(\"migrated context does not match the v3 reference context\");\n }\n const migratedName = await store.getSessionName();\n const v3Name = [...source.entries]\n .toReversed()\n .find((entry) => entry.type === \"session_info\")\n ?.name?.trim();\n if ((migratedName ?? \"\") !== (v3Name || \"\")) {\n throw new Error(\"migrated session name does not match the v3 session name\");\n }\n}\n\nexport async function migrateSessionFile(\n filePath: string,\n options?: { dryRun?: boolean },\n): Promise<SessionMigrationResult> {\n if (!isV3SessionFile(filePath)) return { file: filePath, status: \"already-current\" };\n const sourceBytes = readFileSync(filePath);\n const source = readV3SessionFile(filePath);\n if (options?.dryRun) return { file: filePath, status: \"migrated\", detail: \"dry run\" };\n\n await commitMigration({\n filePath,\n sourceBytes,\n encoded: encodeV4File(source),\n tempPath: `${filePath}.v4.tmp`,\n backupPath: `${filePath}.v3.bak`,\n verify: (candidatePath) => verifyMigratedFile(candidatePath, source),\n });\n return { file: filePath, status: \"migrated\" };\n}\n\nexport function findV3SessionFiles(root: string): string[] {\n return findSessionFiles(root, isV3SessionFile);\n}\n\nexport const sessionsV3Migration: Migration = Object.freeze({\n id: \"0004-sessions-v3\",\n summary: \"convert v3 session files to the current Pi session format, keeping *.v3.bak\",\n async run(context: MigrationContext): Promise<void> {\n for (const file of findV3SessionFiles(context.workspaceRoot)) {\n context.report(` session ${file}`);\n await migrateSessionFile(file, { dryRun: context.dryRun });\n }\n },\n});\n"]}
@@ -0,0 +1,30 @@
1
+ import type { SandboxConfig } from "../sandbox/types.js";
2
+ import type { PlatformName } from "../types.js";
3
+ export type DockerCli = (args: readonly string[]) => Promise<string>;
4
+ export interface MigrationContext {
5
+ readonly workspaceRoot: string;
6
+ readonly stateDir: string;
7
+ readonly dryRun: boolean;
8
+ readonly owners: ReadonlyMap<string, PlatformName>;
9
+ readonly enabledPlatforms: readonly PlatformName[];
10
+ readonly sandbox: SandboxConfig;
11
+ readonly piAgentDir: string;
12
+ readonly modelsPath: string;
13
+ readonly docker: DockerCli;
14
+ report(line: string): void;
15
+ }
16
+ export interface Migration {
17
+ readonly id: string;
18
+ readonly summary: string;
19
+ run(context: MigrationContext): Promise<void>;
20
+ }
21
+ export interface AppliedMigration {
22
+ readonly id: string;
23
+ readonly appliedAt: string;
24
+ }
25
+ export interface SessionMigrationResult {
26
+ file: string;
27
+ status: "migrated" | "already-current";
28
+ detail?: string;
29
+ }
30
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/migrations/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,MAAM,SAAS,GAAG,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACnD,QAAQ,CAAC,gBAAgB,EAAE,SAAS,YAAY,EAAE,CAAC;IACnD,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,GAAG,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAAC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/migrations/types.ts"],"names":[],"mappings":"","sourcesContent":["import type { SandboxConfig } from \"../sandbox/types.js\";\nimport type { PlatformName } from \"../types.js\";\n\nexport type DockerCli = (args: readonly string[]) => Promise<string>;\n\nexport interface MigrationContext {\n readonly workspaceRoot: string;\n readonly stateDir: string;\n readonly dryRun: boolean;\n readonly owners: ReadonlyMap<string, PlatformName>;\n readonly enabledPlatforms: readonly PlatformName[];\n readonly sandbox: SandboxConfig;\n readonly piAgentDir: string;\n readonly modelsPath: string;\n readonly docker: DockerCli;\n report(line: string): void;\n}\n\nexport interface Migration {\n readonly id: string;\n readonly summary: string;\n run(context: MigrationContext): Promise<void>;\n}\n\nexport interface AppliedMigration {\n readonly id: string;\n readonly appliedAt: string;\n}\n\nexport interface SessionMigrationResult {\n file: string;\n status: \"migrated\" | \"already-current\";\n detail?: string;\n}\n"]}
@@ -0,0 +1,3 @@
1
+ import type { Migration } from "./types.js";
2
+ export declare const workspaceEventsMigration: Migration;
3
+ //# sourceMappingURL=workspace-events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-events.d.ts","sourceRoot":"","sources":["../../src/migrations/workspace-events.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,SAAS,EAAoB,MAAM,YAAY,CAAC;AAU9D,eAAO,MAAM,wBAAwB,EAAE,SAyBrC,CAAC"}
@@ -0,0 +1,43 @@
1
+ import { existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, rmdirSync, rmSync, } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { officeEventsDir, parseEventPayload } from "../events/index.js";
4
+ import { atomicWritePrivateFile } from "../file-guards.js";
5
+ import { assertPlatformName, createOfficeAddress, createWorkspace } from "../office/index.js";
6
+ function eventAddress(source, filename) {
7
+ const payload = parseEventPayload(readFileSync(source, "utf-8"), filename);
8
+ if (!payload.platform) {
9
+ throw new Error(`Event has no platform, so its conversation is unknown: ${source}`);
10
+ }
11
+ return createOfficeAddress(assertPlatformName(payload.platform), payload.conversationId);
12
+ }
13
+ export const workspaceEventsMigration = Object.freeze({
14
+ id: "0005-workspace-events",
15
+ summary: "move scheduled events from <workspace>/events into each office's host state",
16
+ async run(context) {
17
+ const legacyDir = join(context.workspaceRoot, "events");
18
+ if (!existsSync(legacyDir))
19
+ return;
20
+ const workspace = createWorkspace({ root: context.workspaceRoot, stateDir: context.stateDir });
21
+ const filenames = readdirSync(legacyDir)
22
+ .filter((name) => name.endsWith(".json") && lstatSync(join(legacyDir, name)).isFile())
23
+ .toSorted();
24
+ for (const filename of filenames) {
25
+ const source = join(legacyDir, filename);
26
+ const office = workspace.office(eventAddress(source, filename));
27
+ const targetDir = officeEventsDir(office);
28
+ const target = join(targetDir, filename);
29
+ if (existsSync(target))
30
+ throw new Error(`Event already exists in its office: ${target}`);
31
+ context.report(` event ${filename} -> ${office.key}`);
32
+ if (context.dryRun)
33
+ continue;
34
+ office.ensure();
35
+ mkdirSync(targetDir, { recursive: true, mode: 0o700 });
36
+ atomicWritePrivateFile(target, readFileSync(source, "utf-8"));
37
+ rmSync(source);
38
+ }
39
+ if (!context.dryRun && readdirSync(legacyDir).length === 0)
40
+ rmdirSync(legacyDir);
41
+ },
42
+ });
43
+ //# sourceMappingURL=workspace-events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-events.js","sourceRoot":"","sources":["../../src/migrations/workspace-events.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,SAAS,EACT,SAAS,EACT,WAAW,EACX,YAAY,EACZ,SAAS,EACT,MAAM,GACP,MAAM,SAAS,CAAC;AACjB,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAI9F,SAAS,YAAY,CAAC,MAAc,EAAE,QAAgB;IACpD,MAAM,OAAO,GAAG,iBAAiB,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC3E,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CAAC,0DAA0D,MAAM,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,OAAO,mBAAmB,CAAC,kBAAkB,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;AAC3F,CAAC;AAED,MAAM,CAAC,MAAM,wBAAwB,GAAc,MAAM,CAAC,MAAM,CAAC;IAC/D,EAAE,EAAE,uBAAuB;IAC3B,OAAO,EAAE,6EAA6E;IACtF,KAAK,CAAC,GAAG,CAAC,OAAyB;QACjC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;QACxD,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;YAAE,OAAO;QACnC,MAAM,SAAS,GAAG,eAAe,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,aAAa,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC/F,MAAM,SAAS,GAAG,WAAW,CAAC,SAAS,CAAC;aACrC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;aACrF,QAAQ,EAAE,CAAC;QACd,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACzC,MAAM,MAAM,GAAG,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;YAChE,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;YAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;YACzC,IAAI,UAAU,CAAC,MAAM,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,uCAAuC,MAAM,EAAE,CAAC,CAAC;YACzF,OAAO,CAAC,MAAM,CAAC,WAAW,QAAQ,OAAO,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC;YACvD,IAAI,OAAO,CAAC,MAAM;gBAAE,SAAS;YAC7B,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YACvD,sBAAsB,CAAC,MAAM,EAAE,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;YAC9D,MAAM,CAAC,MAAM,CAAC,CAAC;QACjB,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,SAAS,CAAC,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IACnF,CAAC;CACF,CAAC,CAAC","sourcesContent":["import {\n existsSync,\n lstatSync,\n mkdirSync,\n readdirSync,\n readFileSync,\n rmdirSync,\n rmSync,\n} from \"node:fs\";\nimport { join } from \"node:path\";\nimport { officeEventsDir, parseEventPayload } from \"../events/index.js\";\nimport { atomicWritePrivateFile } from \"../file-guards.js\";\nimport { assertPlatformName, createOfficeAddress, createWorkspace } from \"../office/index.js\";\nimport type { OfficeAddress } from \"../types.js\";\nimport type { Migration, MigrationContext } from \"./types.js\";\n\nfunction eventAddress(source: string, filename: string): OfficeAddress {\n const payload = parseEventPayload(readFileSync(source, \"utf-8\"), filename);\n if (!payload.platform) {\n throw new Error(`Event has no platform, so its conversation is unknown: ${source}`);\n }\n return createOfficeAddress(assertPlatformName(payload.platform), payload.conversationId);\n}\n\nexport const workspaceEventsMigration: Migration = Object.freeze({\n id: \"0005-workspace-events\",\n summary: \"move scheduled events from <workspace>/events into each office's host state\",\n async run(context: MigrationContext): Promise<void> {\n const legacyDir = join(context.workspaceRoot, \"events\");\n if (!existsSync(legacyDir)) return;\n const workspace = createWorkspace({ root: context.workspaceRoot, stateDir: context.stateDir });\n const filenames = readdirSync(legacyDir)\n .filter((name) => name.endsWith(\".json\") && lstatSync(join(legacyDir, name)).isFile())\n .toSorted();\n for (const filename of filenames) {\n const source = join(legacyDir, filename);\n const office = workspace.office(eventAddress(source, filename));\n const targetDir = officeEventsDir(office);\n const target = join(targetDir, filename);\n if (existsSync(target)) throw new Error(`Event already exists in its office: ${target}`);\n context.report(` event ${filename} -> ${office.key}`);\n if (context.dryRun) continue;\n office.ensure();\n mkdirSync(targetDir, { recursive: true, mode: 0o700 });\n atomicWritePrivateFile(target, readFileSync(source, \"utf-8\"));\n rmSync(source);\n }\n if (!context.dryRun && readdirSync(legacyDir).length === 0) rmdirSync(legacyDir);\n },\n});\n"]}