@geminixiang/mikan 1.0.0-beta.87 → 1.0.0-beta.89

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 (249) hide show
  1. package/CHANGELOG.md +44 -1370
  2. package/README.md +2 -2
  3. package/dist/adapters/commands/session-view.d.ts.map +1 -1
  4. package/dist/adapters/commands/session-view.js +4 -4
  5. package/dist/adapters/commands/session-view.js.map +1 -1
  6. package/dist/adapters/discord/bot.d.ts +4 -1
  7. package/dist/adapters/discord/bot.d.ts.map +1 -1
  8. package/dist/adapters/discord/bot.js +6 -3
  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 +3 -1
  12. package/dist/adapters/discord/context.js.map +1 -1
  13. package/dist/adapters/discord/types.d.ts +1 -0
  14. package/dist/adapters/discord/types.d.ts.map +1 -1
  15. package/dist/adapters/discord/types.js.map +1 -1
  16. package/dist/adapters/github/activity.d.ts.map +1 -1
  17. package/dist/adapters/github/activity.js +7 -13
  18. package/dist/adapters/github/activity.js.map +1 -1
  19. package/dist/adapters/github/bot.d.ts +4 -1
  20. package/dist/adapters/github/bot.d.ts.map +1 -1
  21. package/dist/adapters/github/bot.js +3 -3
  22. package/dist/adapters/github/bot.js.map +1 -1
  23. package/dist/adapters/github/context.d.ts.map +1 -1
  24. package/dist/adapters/github/context.js +3 -1
  25. package/dist/adapters/github/context.js.map +1 -1
  26. package/dist/adapters/github/types.d.ts +0 -3
  27. package/dist/adapters/github/types.d.ts.map +1 -1
  28. package/dist/adapters/github/types.js.map +1 -1
  29. package/dist/adapters/intake.d.ts.map +1 -1
  30. package/dist/adapters/intake.js +12 -2
  31. package/dist/adapters/intake.js.map +1 -1
  32. package/dist/adapters/shared.d.ts +2 -1
  33. package/dist/adapters/shared.d.ts.map +1 -1
  34. package/dist/adapters/shared.js +7 -2
  35. package/dist/adapters/shared.js.map +1 -1
  36. package/dist/adapters/slack/assistant.d.ts +2 -4
  37. package/dist/adapters/slack/assistant.d.ts.map +1 -1
  38. package/dist/adapters/slack/assistant.js +10 -16
  39. package/dist/adapters/slack/assistant.js.map +1 -1
  40. package/dist/adapters/slack/bot.d.ts +5 -1
  41. package/dist/adapters/slack/bot.d.ts.map +1 -1
  42. package/dist/adapters/slack/bot.js +15 -11
  43. package/dist/adapters/slack/bot.js.map +1 -1
  44. package/dist/adapters/slack/response-lifecycle.d.ts.map +1 -1
  45. package/dist/adapters/slack/response-lifecycle.js +1 -1
  46. package/dist/adapters/slack/response-lifecycle.js.map +1 -1
  47. package/dist/adapters/slack/task-status.d.ts.map +1 -1
  48. package/dist/adapters/slack/task-status.js +1 -2
  49. package/dist/adapters/slack/task-status.js.map +1 -1
  50. package/dist/adapters/telegram/bot.d.ts +4 -1
  51. package/dist/adapters/telegram/bot.d.ts.map +1 -1
  52. package/dist/adapters/telegram/bot.js +3 -3
  53. package/dist/adapters/telegram/bot.js.map +1 -1
  54. package/dist/adapters/telegram/context.d.ts.map +1 -1
  55. package/dist/adapters/telegram/context.js +3 -1
  56. package/dist/adapters/telegram/context.js.map +1 -1
  57. package/dist/adapters/types.d.ts +3 -1
  58. package/dist/adapters/types.d.ts.map +1 -1
  59. package/dist/adapters/types.js.map +1 -1
  60. package/dist/adapters/web/admin/client-assets.js +1 -1
  61. package/dist/adapters/web/admin/client-assets.js.map +1 -1
  62. package/dist/adapters/web/admin/portal.d.ts +0 -1
  63. package/dist/adapters/web/admin/portal.d.ts.map +1 -1
  64. package/dist/adapters/web/admin/portal.js +26 -42
  65. package/dist/adapters/web/admin/portal.js.map +1 -1
  66. package/dist/adapters/web/admin/types.d.ts +0 -2
  67. package/dist/adapters/web/admin/types.d.ts.map +1 -1
  68. package/dist/adapters/web/admin/types.js.map +1 -1
  69. package/dist/adapters/web/login/portal.d.ts.map +1 -1
  70. package/dist/adapters/web/login/portal.js +0 -8
  71. package/dist/adapters/web/login/portal.js.map +1 -1
  72. package/dist/adapters/web/server.d.ts.map +1 -1
  73. package/dist/adapters/web/server.js +0 -1
  74. package/dist/adapters/web/server.js.map +1 -1
  75. package/dist/adapters/web/session-view/portal.d.ts +1 -2
  76. package/dist/adapters/web/session-view/portal.d.ts.map +1 -1
  77. package/dist/adapters/web/session-view/portal.js +38 -328
  78. package/dist/adapters/web/session-view/portal.js.map +1 -1
  79. package/dist/adapters/web/session-view/types.d.ts +4 -6
  80. package/dist/adapters/web/session-view/types.d.ts.map +1 -1
  81. package/dist/adapters/web/session-view/types.js.map +1 -1
  82. package/dist/env-manifest.d.ts +0 -4
  83. package/dist/env-manifest.d.ts.map +1 -1
  84. package/dist/env-manifest.js +5 -8
  85. package/dist/env-manifest.js.map +1 -1
  86. package/dist/events/index.d.ts +2 -9
  87. package/dist/events/index.d.ts.map +1 -1
  88. package/dist/events/index.js +0 -1
  89. package/dist/events/index.js.map +1 -1
  90. package/dist/events/scheduler.d.ts +0 -1
  91. package/dist/events/scheduler.d.ts.map +1 -1
  92. package/dist/events/scheduler.js +2 -7
  93. package/dist/events/scheduler.js.map +1 -1
  94. package/dist/harness/execution-env.d.ts +1 -1
  95. package/dist/harness/execution-env.d.ts.map +1 -1
  96. package/dist/harness/execution-env.js +32 -32
  97. package/dist/harness/execution-env.js.map +1 -1
  98. package/dist/harness/index.d.ts +2 -3
  99. package/dist/harness/index.d.ts.map +1 -1
  100. package/dist/harness/index.js +0 -1
  101. package/dist/harness/index.js.map +1 -1
  102. package/dist/harness/mcp-result.d.ts +4 -3
  103. package/dist/harness/mcp-result.d.ts.map +1 -1
  104. package/dist/harness/mcp-result.js +27 -13
  105. package/dist/harness/mcp-result.js.map +1 -1
  106. package/dist/harness/mcp.js +4 -4
  107. package/dist/harness/mcp.js.map +1 -1
  108. package/dist/harness/presenter.d.ts.map +1 -1
  109. package/dist/harness/presenter.js +20 -25
  110. package/dist/harness/presenter.js.map +1 -1
  111. package/dist/harness/prompt.d.ts +0 -1
  112. package/dist/harness/prompt.d.ts.map +1 -1
  113. package/dist/harness/prompt.js +3 -32
  114. package/dist/harness/prompt.js.map +1 -1
  115. package/dist/harness/run-events.d.ts.map +1 -1
  116. package/dist/harness/run-events.js +4 -12
  117. package/dist/harness/run-events.js.map +1 -1
  118. package/dist/harness/runner.d.ts.map +1 -1
  119. package/dist/harness/runner.js +35 -38
  120. package/dist/harness/runner.js.map +1 -1
  121. package/dist/harness/session.d.ts +37 -38
  122. package/dist/harness/session.d.ts.map +1 -1
  123. package/dist/harness/session.js +387 -388
  124. package/dist/harness/session.js.map +1 -1
  125. package/dist/harness/subagent.d.ts +5 -5
  126. package/dist/harness/subagent.d.ts.map +1 -1
  127. package/dist/harness/subagent.js +40 -45
  128. package/dist/harness/subagent.js.map +1 -1
  129. package/dist/harness/tools/codemode.d.ts +3 -0
  130. package/dist/harness/tools/codemode.d.ts.map +1 -0
  131. package/dist/harness/tools/codemode.js +173 -0
  132. package/dist/harness/tools/codemode.js.map +1 -0
  133. package/dist/harness/tools/history.d.ts +20 -0
  134. package/dist/harness/tools/history.d.ts.map +1 -0
  135. package/dist/harness/tools/history.js +293 -0
  136. package/dist/harness/tools/history.js.map +1 -0
  137. package/dist/harness/tools/pi-tools.d.ts +1 -1
  138. package/dist/harness/tools/pi-tools.d.ts.map +1 -1
  139. package/dist/harness/tools/pi-tools.js +47 -21
  140. package/dist/harness/tools/pi-tools.js.map +1 -1
  141. package/dist/harness/tools/secret-redaction.d.ts.map +1 -1
  142. package/dist/harness/tools/secret-redaction.js +46 -2
  143. package/dist/harness/tools/secret-redaction.js.map +1 -1
  144. package/dist/harness/tools/tool-search.d.ts +5 -0
  145. package/dist/harness/tools/tool-search.d.ts.map +1 -0
  146. package/dist/harness/tools/tool-search.js +84 -0
  147. package/dist/harness/tools/tool-search.js.map +1 -0
  148. package/dist/harness/types.d.ts +75 -15
  149. package/dist/harness/types.d.ts.map +1 -1
  150. package/dist/harness/types.js.map +1 -1
  151. package/dist/index.d.ts +3 -7
  152. package/dist/index.d.ts.map +1 -1
  153. package/dist/index.js +0 -3
  154. package/dist/index.js.map +1 -1
  155. package/dist/log.d.ts +0 -3
  156. package/dist/log.d.ts.map +1 -1
  157. package/dist/log.js +0 -15
  158. package/dist/log.js.map +1 -1
  159. package/dist/main.js +2 -2
  160. package/dist/main.js.map +1 -1
  161. package/dist/memory-capture/index.js +0 -1
  162. package/dist/memory-capture/index.js.map +1 -1
  163. package/dist/migrations/index.d.ts.map +1 -1
  164. package/dist/migrations/index.js +2 -0
  165. package/dist/migrations/index.js.map +1 -1
  166. package/dist/migrations/session-files.d.ts +3 -2
  167. package/dist/migrations/session-files.d.ts.map +1 -1
  168. package/dist/migrations/session-files.js.map +1 -1
  169. package/dist/migrations/session-v4.d.ts +77 -0
  170. package/dist/migrations/session-v4.d.ts.map +1 -0
  171. package/dist/migrations/session-v4.js +177 -0
  172. package/dist/migrations/session-v4.js.map +1 -0
  173. package/dist/migrations/sessions-sqlite.d.ts +3 -0
  174. package/dist/migrations/sessions-sqlite.d.ts.map +1 -0
  175. package/dist/migrations/sessions-sqlite.js +215 -0
  176. package/dist/migrations/sessions-sqlite.js.map +1 -0
  177. package/dist/migrations/sessions-v3.d.ts +2 -2
  178. package/dist/migrations/sessions-v3.d.ts.map +1 -1
  179. package/dist/migrations/sessions-v3.js +8 -19
  180. package/dist/migrations/sessions-v3.js.map +1 -1
  181. package/dist/migrations/types.d.ts +0 -5
  182. package/dist/migrations/types.d.ts.map +1 -1
  183. package/dist/migrations/types.js.map +1 -1
  184. package/dist/observability/sentry.d.ts +2 -9
  185. package/dist/observability/sentry.d.ts.map +1 -1
  186. package/dist/observability/sentry.js +12 -64
  187. package/dist/observability/sentry.js.map +1 -1
  188. package/dist/observability/types.d.ts +0 -16
  189. package/dist/observability/types.d.ts.map +1 -1
  190. package/dist/observability/types.js.map +1 -1
  191. package/dist/office/index.d.ts +1 -4
  192. package/dist/office/index.d.ts.map +1 -1
  193. package/dist/office/index.js +2 -14
  194. package/dist/office/index.js.map +1 -1
  195. package/dist/office/projection.d.ts.map +1 -1
  196. package/dist/office/projection.js +0 -1
  197. package/dist/office/projection.js.map +1 -1
  198. package/dist/office/types.d.ts +1 -2
  199. package/dist/office/types.d.ts.map +1 -1
  200. package/dist/office/types.js.map +1 -1
  201. package/dist/runtime/conversation-runtime.d.ts.map +1 -1
  202. package/dist/runtime/conversation-runtime.js +2 -16
  203. package/dist/runtime/conversation-runtime.js.map +1 -1
  204. package/dist/runtime/session-lifecycle.d.ts +1 -2
  205. package/dist/runtime/session-lifecycle.d.ts.map +1 -1
  206. package/dist/runtime/session-lifecycle.js +7 -16
  207. package/dist/runtime/session-lifecycle.js.map +1 -1
  208. package/dist/runtime/types.d.ts +0 -1
  209. package/dist/runtime/types.d.ts.map +1 -1
  210. package/dist/runtime/types.js.map +1 -1
  211. package/dist/sandbox/provisioner.d.ts.map +1 -1
  212. package/dist/sandbox/provisioner.js +3 -4
  213. package/dist/sandbox/provisioner.js.map +1 -1
  214. package/dist/sessions/chat-history-sync.d.ts +8 -6
  215. package/dist/sessions/chat-history-sync.d.ts.map +1 -1
  216. package/dist/sessions/chat-history-sync.js +70 -143
  217. package/dist/sessions/chat-history-sync.js.map +1 -1
  218. package/dist/sessions/compaction-summary.d.ts +4 -0
  219. package/dist/sessions/compaction-summary.d.ts.map +1 -0
  220. package/dist/sessions/compaction-summary.js +16 -0
  221. package/dist/sessions/compaction-summary.js.map +1 -0
  222. package/dist/sessions/session-key.d.ts +1 -0
  223. package/dist/sessions/session-key.d.ts.map +1 -1
  224. package/dist/sessions/session-key.js +3 -0
  225. package/dist/sessions/session-key.js.map +1 -1
  226. package/dist/sessions/session-store.d.ts +47 -29
  227. package/dist/sessions/session-store.d.ts.map +1 -1
  228. package/dist/sessions/session-store.js +578 -484
  229. package/dist/sessions/session-store.js.map +1 -1
  230. package/dist/sessions/types.d.ts +60 -46
  231. package/dist/sessions/types.d.ts.map +1 -1
  232. package/dist/sessions/types.js +1 -1
  233. package/dist/sessions/types.js.map +1 -1
  234. package/dist/settings/apply.js +2 -2
  235. package/dist/settings/apply.js.map +1 -1
  236. package/dist/types.d.ts +10 -3
  237. package/dist/types.d.ts.map +1 -1
  238. package/dist/types.js.map +1 -1
  239. package/dist/vault/index.d.ts.map +1 -1
  240. package/dist/vault/index.js +0 -3
  241. package/dist/vault/index.js.map +1 -1
  242. package/dist/vault/types.d.ts +0 -3
  243. package/dist/vault/types.d.ts.map +1 -1
  244. package/dist/vault/types.js.map +1 -1
  245. package/package.json +8 -5
  246. package/dist/sessions/store.d.ts +0 -17
  247. package/dist/sessions/store.d.ts.map +0 -1
  248. package/dist/sessions/store.js +0 -211
  249. package/dist/sessions/store.js.map +0 -1
@@ -15,6 +15,5 @@ export declare function parseUserBody(raw: string): {
15
15
  header: string | null;
16
16
  content: string;
17
17
  };
18
- export declare function resolveExistingSessionFile(office: Office, sessionKey: string): string | null;
19
- export declare function loadSessionViewModel(sessionFile: string): Promise<SessionViewModel>;
18
+ export declare function loadSessionViewModel(office: Office, sessionKey: string): Promise<SessionViewModel>;
20
19
  //# sourceMappingURL=portal.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/session-view/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAejE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAGV,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAmB3B,qBAAa,6BAA8B,SAAQ,kBAAkB,CAAC,gBAAgB,CAAC;IACrF,MAAM,CAAC,OAAO,EAAE,6BAA6B,GAAG,gBAAgB,CAS/D;CACF;AA6DD,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAIhE,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,GAAG,EACR,qBAAqB,CAAC,EAAE,6BAA6B,EACrD,WAAW,CAAC,EAAE,6BAA6B,GAC1C,OAAO,CAAC,OAAO,CAAC,CAuElB;AAwLD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAC1C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CA4BA;AAm6CD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAK5F;AAED,wBAAsB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAyDzF"}
1
+ {"version":3,"file":"portal.d.ts","sourceRoot":"","sources":["../../../../src/adapters/web/session-view/portal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAOjE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AAEvD,OAAO,KAAK,EAGV,gBAAgB,EAChB,gBAAgB,EACjB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,KAAK,EAGV,6BAA6B,EAC9B,MAAM,mBAAmB,CAAC;AAmB3B,qBAAa,6BAA8B,SAAQ,kBAAkB,CAAC,gBAAgB,CAAC;IACrF,MAAM,CAAC,OAAO,EAAE,6BAA6B,GAAG,gBAAgB,CAS/D;CACF;AA6DD,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,YAAY,CAAC;AAIhE,wBAAsB,wBAAwB,CAC5C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,GAAG,EACR,qBAAqB,CAAC,EAAE,6BAA6B,EACrD,WAAW,CAAC,EAAE,6BAA6B,GAC1C,OAAO,CAAC,OAAO,CAAC,CAsElB;AA4ID,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;IAC1C,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;CACjB,CA4BA;AAm6CD,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,gBAAgB,CAAC,CA4C3B"}
@@ -1,9 +1,4 @@
1
- import { dirname, join, resolve } from "node:path";
2
- import { existsSync, readdirSync } from "node:fs";
3
1
  import { SessionStore } from "../../../sessions/session-store.js";
4
- import { getThreadSessionFile, tryResolveCurrentSession, tryResolveThreadSession, } from "../../../sessions/store.js";
5
- import { isPlatformHistorySession } from "../../../sessions/store.js";
6
- import { basename } from "node:path";
7
2
  import { InMemoryTokenStore } from "../token-store.js";
8
3
  import MarkdownIt from "markdown-it";
9
4
  import { createConversationEvent, createConversationMessage, createOfficeAddress, } from "../../../office/index.js";
@@ -21,7 +16,7 @@ export class InMemorySessionViewTokenStore extends InMemoryTokenStore {
21
16
  platformUserName: options.platformUserName || undefined,
22
17
  conversationId: options.conversationId,
23
18
  sessionKey: options.sessionKey,
24
- sessionFile: options.sessionFile,
19
+ office: options.office,
25
20
  });
26
21
  }
27
22
  }
@@ -85,9 +80,9 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
85
80
  res.end(renderStatusPage("Session unavailable", target.message));
86
81
  return true;
87
82
  }
88
- const { activeSessionKey, entry, targetSessionFile } = target;
83
+ const { activeSessionKey, entry } = target;
89
84
  try {
90
- const model = await loadSessionViewModel(targetSessionFile);
85
+ const model = await loadSessionViewModel(entry.office, activeSessionKey);
91
86
  const displayedSessionKey = activeSessionKey;
92
87
  const isRunning = interactive?.handler.isRunning(createOfficeAddress(entry.platform, entry.conversationId), displayedSessionKey) ?? false;
93
88
  res.writeHead(200, {
@@ -104,7 +99,7 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
104
99
  }));
105
100
  }
106
101
  catch (error) {
107
- log.logWarning(`[${entry.conversationId}] Failed to render session ${entry.sessionFile}`, errorMessage(error));
102
+ log.logWarning(`[${entry.conversationId}] Failed to render session ${activeSessionKey}`, errorMessage(error));
108
103
  reportUserFacingError(error, {
109
104
  domain: "session_view",
110
105
  surface: "session_view",
@@ -113,8 +108,7 @@ export async function handleSessionViewRequest(req, res, url, sessionViewTokenSt
113
108
  platform: entry.platform,
114
109
  context: {
115
110
  conversationId: entry.conversationId,
116
- sessionKey: entry.sessionKey,
117
- sessionFile: basename(targetSessionFile),
111
+ sessionKey: activeSessionKey,
118
112
  },
119
113
  });
120
114
  res.writeHead(500, { "Content-Type": "text/html; charset=utf-8" });
@@ -131,46 +125,11 @@ async function resolveSessionRequestTarget(sessionViewTokenStore, rawToken, requ
131
125
  if (!entry) {
132
126
  return { ok: false, status: 400, message: "This session link is invalid or has expired." };
133
127
  }
134
- let targetSessionFile;
135
- try {
136
- targetSessionFile = await resolveRequestedSessionFile(entry.sessionFile, requestedSession);
137
- }
138
- catch (error) {
139
- log.logWarning(`[${entry.conversationId}] Corrupted session file referenced for ${entry.sessionFile}`, errorMessage(error));
140
- reportUserFacingError(error, {
141
- domain: "session_view",
142
- surface: "session_view",
143
- operation: "resolve_requested_session",
144
- severity: "error",
145
- platform: entry.platform,
146
- context: {
147
- conversationId: entry.conversationId,
148
- sessionKey: entry.sessionKey,
149
- sessionFile: basename(entry.sessionFile),
150
- requestedSession,
151
- },
152
- });
153
- return { ok: false, status: 500, message: "The selected session file is corrupted." };
154
- }
155
- if (!targetSessionFile) {
128
+ const requested = requestedSession?.trim() || entry.sessionKey;
129
+ if (!(await SessionStore.exists(entry.office, requested))) {
156
130
  return { ok: false, status: 400, message: "The selected session is invalid." };
157
131
  }
158
- return {
159
- ok: true,
160
- entry,
161
- targetSessionFile,
162
- activeSessionKey: resolveDisplayedSessionKey(entry, targetSessionFile),
163
- };
164
- }
165
- function resolveDisplayedSessionKey(entry, sessionFile) {
166
- if (entry.platform === "slack") {
167
- const fileName = basename(sessionFile, ".jsonl");
168
- if (/^\d+\.\d+$/.test(fileName)) {
169
- return `${entry.conversationId}:${fileName}`;
170
- }
171
- return entry.conversationId;
172
- }
173
- return entry.sessionKey;
132
+ return { ok: true, entry, activeSessionKey: requested };
174
133
  }
175
134
  function sessionStreamKey(entry) {
176
135
  return `${entry.platform}:${entry.conversationId}:${entry.sessionKey}`;
@@ -207,7 +166,7 @@ function renderSessionPage(options) {
207
166
 
208
167
  <div class="session-detail-row">
209
168
  <span class="session-detail"><span class="session-detail-label">Session</span><code>${esc(model.sessionId.slice(0, 8))}</code></span>
210
- <span class="session-detail"><span class="session-detail-label">File</span><code>${esc(model.fileName)}</code></span>
169
+ <span class="session-detail"><span class="session-detail-label">Session</span><code>${esc(model.sessionKey)}</code></span>
211
170
  <span class="session-detail"><span class="session-detail-label">Expires</span><span>${esc(formatDate(new Date(expiresAt).toISOString()))}</span></span>
212
171
  </div>
213
172
 
@@ -224,7 +183,7 @@ function renderSessionPage(options) {
224
183
  <section class="composer-card">
225
184
  <form class="composer-form" data-session-composer>
226
185
  <input type="hidden" name="token" value="${esc(token)}">
227
- <input type="hidden" name="session" value="${esc(model.fileName)}">
186
+ <input type="hidden" name="session" value="${esc(model.sessionKey)}">
228
187
  <input type="hidden" name="sessionKey" value="${esc(displayedSessionKey)}">
229
188
  <textarea name="text" rows="1" placeholder="Ask mikan in this session… (replies stay in Session View)" required></textarea>
230
189
  <div class="composer-actions">
@@ -236,13 +195,13 @@ function renderSessionPage(options) {
236
195
  return renderHtmlDocument(`${model.title} · Session Viewer`, body, isRunning);
237
196
  }
238
197
  function renderRelationCard(relation, token) {
239
- const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.fileName)}`;
198
+ const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.sessionKey)}`;
240
199
  const summary = relation.summary ? `<p class="related-summary">${esc(relation.summary)}</p>` : "";
241
200
  return `<a class="related-link" href="${href}">
242
201
  <span class="related-copy">
243
202
  <strong class="related-title">${esc(relation.title)}</strong>
244
203
  ${summary}
245
- <span class="related-meta">${esc(formatDate(relation.updatedAt))} · ${esc(String(relation.entryCount))} entries · ${esc(relation.fileName)}</span>
204
+ <span class="related-meta">${esc(formatDate(relation.updatedAt))} · ${esc(String(relation.entryCount))} entries · ${esc(relation.sessionKey)}</span>
246
205
  </span>
247
206
  <span class="related-arrow" aria-hidden="true">→</span>
248
207
  </a>`;
@@ -252,7 +211,7 @@ function renderThreadLinks(relations, token) {
252
211
  return "";
253
212
  return `<div class="thread-links">${relations
254
213
  .map((relation) => {
255
- const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.fileName)}`;
214
+ const href = `/session?token=${encodeURIComponent(token)}&session=${encodeURIComponent(relation.sessionKey)}`;
256
215
  return `<a class="thread-link" href="${href}" title="Open ${esc(relation.title)}">
257
216
  <span class="thread-dot" aria-hidden="true"></span>
258
217
  <span class="thread-text">Thread</span>
@@ -418,7 +377,7 @@ async function handleSessionStreamRequest(req, res, url, sessionViewTokenStore,
418
377
  const send = (event) => res.write(`data: ${JSON.stringify(event)}\n\n`);
419
378
  const unsubscribe = sessionViewStreamHub.subscribe(streamKey, send);
420
379
  const liveRun = createLiveRunProjection(send, async () => {
421
- const model = await loadSessionViewModel(target.targetSessionFile);
380
+ const model = await loadSessionViewModel(target.entry.office, target.activeSessionKey);
422
381
  return {
423
382
  type: "refresh",
424
383
  timelineHtml: renderTimelineItems(model.items, url.searchParams.get("token") ?? ""),
@@ -1650,29 +1609,24 @@ function entryIsoTime(entry) {
1650
1609
  }
1651
1610
  return new Date(entry.timestamp).toISOString();
1652
1611
  }
1653
- export function resolveExistingSessionFile(office, sessionKey) {
1654
- if (isThreadSessionKey(sessionKey)) {
1655
- return tryResolveThreadSession(getThreadSessionFile(office.sessionsDir, sessionKey));
1656
- }
1657
- return tryResolveCurrentSession(office.sessionsDir);
1658
- }
1659
- export async function loadSessionViewModel(sessionFile) {
1660
- const resolvedFile = resolve(sessionFile);
1661
- const sm = await SessionStore.inspect(resolvedFile);
1612
+ export async function loadSessionViewModel(office, sessionKey) {
1613
+ const sm = await SessionStore.inspect(office, sessionKey);
1614
+ if (!sm)
1615
+ throw new Error(`Session not found: ${sessionKey}`);
1662
1616
  const header = sm.getHeader();
1663
1617
  const entries = await sm.getEntries();
1664
- const updatedAt = entryIsoTime(entries.at(-1)) ?? header.timestamp;
1618
+ const updatedAt = entryIsoTime(entries.at(-1)) ?? new Date(header.createdAt).toISOString();
1665
1619
  const title = (await sm.getSessionName()) || `Session ${header.id.slice(0, 8)}`;
1666
- const parent = await resolveParentRelation(resolvedFile, header);
1667
- const threadCandidates = [];
1668
- for (const candidate of listRelatedSessionFiles(resolvedFile)) {
1669
- if (candidate === resolvedFile)
1670
- continue;
1671
- const relation = await buildSessionRelation(candidate, "thread", resolvedFile, header.id, entries);
1620
+ const listing = await SessionStore.list(office);
1621
+ const parentListing = listing.find((session) => session.id === header.parentSessionId);
1622
+ const parent = parentListing ? await buildSessionRelation(office, parentListing) : undefined;
1623
+ const threads = [];
1624
+ for (const child of listing.filter((session) => session.parentSessionId === header.id)) {
1625
+ const relation = await buildSessionRelation(office, child);
1672
1626
  if (relation)
1673
- threadCandidates.push(relation);
1627
+ threads.push(relation);
1674
1628
  }
1675
- const threads = threadCandidates.toSorted((a, b) => a.updatedAt < b.updatedAt ? -1 : a.updatedAt > b.updatedAt ? 1 : 0);
1629
+ threads.sort((a, b) => (a.updatedAt < b.updatedAt ? -1 : a.updatedAt > b.updatedAt ? 1 : 0));
1676
1630
  const threadsByEntryId = new Map();
1677
1631
  for (const thread of threads) {
1678
1632
  if (!thread.anchorEntryId)
@@ -1685,270 +1639,37 @@ export async function loadSessionViewModel(sessionFile) {
1685
1639
  const item = mapEntryToItem(entry);
1686
1640
  if (!item)
1687
1641
  return [];
1688
- if (item.entryId) {
1689
- const anchoredThreads = threadsByEntryId.get(item.entryId);
1690
- if (anchoredThreads) {
1691
- item.threads = anchoredThreads;
1692
- }
1693
- }
1642
+ const anchoredThreads = item.entryId ? threadsByEntryId.get(item.entryId) : undefined;
1643
+ if (anchoredThreads)
1644
+ item.threads = anchoredThreads;
1694
1645
  return [item];
1695
1646
  });
1696
1647
  return {
1697
1648
  sessionId: header.id,
1698
- fileName: basename(resolvedFile),
1649
+ sessionKey,
1699
1650
  title,
1700
- createdAt: header.timestamp,
1651
+ createdAt: new Date(header.createdAt).toISOString(),
1701
1652
  updatedAt,
1702
1653
  entryCount: entries.length,
1703
1654
  items,
1704
1655
  parent: parent ?? undefined,
1705
- threads,
1706
1656
  };
1707
1657
  }
1708
- async function resolveRequestedSessionFile(baseSessionFile, requestedFileName) {
1709
- const resolvedBase = resolve(baseSessionFile);
1710
- if (!requestedFileName)
1711
- return resolvedBase;
1712
- const trimmed = requestedFileName.trim();
1713
- if (!trimmed)
1714
- return resolvedBase;
1715
- const fileName = basename(trimmed);
1716
- if (fileName !== trimmed || !fileName.endsWith(".jsonl"))
1717
- return null;
1718
- const candidate = join(dirname(resolvedBase), fileName);
1719
- if (!existsSync(candidate))
1658
+ async function buildSessionRelation(office, session) {
1659
+ const sm = await SessionStore.inspect(office, session.key);
1660
+ if (!sm)
1720
1661
  return null;
1721
- try {
1722
- if (!SessionStore.readHeader(candidate))
1723
- throw new Error("Invalid session header");
1724
- }
1725
- catch (err) {
1726
- throw new Error(`Session file is corrupted: ${candidate}: ${errorMessage(err)}`, {
1727
- cause: err,
1728
- });
1729
- }
1730
- return candidate;
1731
- }
1732
- function listRelatedSessionFiles(sessionFile) {
1733
- const dir = dirname(sessionFile);
1734
- if (!existsSync(dir))
1735
- return [];
1736
- return readdirSync(dir)
1737
- .filter((name) => name.endsWith(".jsonl"))
1738
- .map((fileName) => join(dir, fileName));
1739
- }
1740
- async function buildSessionRelation(sessionFile, kind, expectedParent, expectedParentId, expectedParentEntries) {
1741
- let sm;
1742
- try {
1743
- sm = await SessionStore.inspect(sessionFile);
1744
- }
1745
- catch (err) {
1746
- log.logWarning(`Skipping corrupted session file while building ${kind} relation: ${sessionFile}`, errorMessage(err));
1747
- return null;
1748
- }
1749
1662
  const header = sm.getHeader();
1750
- if (kind === "thread" &&
1751
- !isChildThreadSession(sessionFile, expectedParent, expectedParentId, header.parentSessionId)) {
1752
- return null;
1753
- }
1754
1663
  const entries = await sm.getEntries();
1755
- const updatedAt = entryIsoTime(entries.at(-1)) ?? header.timestamp;
1756
- const threadId = kind === "thread" ? getFixedThreadSessionId(sessionFile) : null;
1757
- const anchorEntryId = kind === "thread" && expectedParent
1758
- ? findThreadAnchorEntryId(expectedParentEntries ??
1759
- (await (await SessionStore.inspect(expectedParent)).getEntries()), entries, threadId)
1760
- : undefined;
1761
1664
  return {
1762
- kind,
1763
- fileName: basename(sessionFile),
1764
- sessionId: header.id,
1665
+ sessionKey: session.key,
1765
1666
  title: (await sm.getSessionName()) || `Session ${header.id.slice(0, 8)}`,
1766
- updatedAt,
1667
+ updatedAt: entryIsoTime(entries.at(-1)) ?? new Date(header.createdAt).toISOString(),
1767
1668
  entryCount: entries.length,
1768
1669
  summary: extractSessionSummary(entries),
1769
- anchorEntryId,
1670
+ anchorEntryId: session.forkEntryId,
1770
1671
  };
1771
1672
  }
1772
- async function resolveParentRelation(sessionFile, header) {
1773
- if (header.parentSessionId) {
1774
- const found = findSessionFileById(dirname(sessionFile), header.parentSessionId);
1775
- if (found)
1776
- return (await buildSessionRelation(found, "parent")) ?? undefined;
1777
- }
1778
- return buildInferredThreadParentRelation(sessionFile);
1779
- }
1780
- function findSessionFileById(sessionDir, targetId) {
1781
- if (!existsSync(sessionDir))
1782
- return null;
1783
- for (const name of readdirSync(sessionDir)) {
1784
- if (!name.endsWith(".jsonl"))
1785
- continue;
1786
- try {
1787
- const filePath = join(sessionDir, name);
1788
- if (SessionStore.readHeader(filePath)?.id === targetId)
1789
- return filePath;
1790
- }
1791
- catch {
1792
- continue;
1793
- }
1794
- }
1795
- return null;
1796
- }
1797
- async function buildInferredThreadParentRelation(sessionFile) {
1798
- if (!getFixedThreadSessionId(sessionFile))
1799
- return undefined;
1800
- const parentSession = tryResolveCurrentSession(dirname(sessionFile));
1801
- if (!parentSession || parentSession === sessionFile)
1802
- return undefined;
1803
- return (await buildSessionRelation(parentSession, "parent")) ?? undefined;
1804
- }
1805
- function isChildThreadSession(sessionFile, expectedParent, expectedParentId, threadParentSessionId) {
1806
- if (!expectedParent)
1807
- return false;
1808
- if (threadParentSessionId) {
1809
- if (threadParentSessionId === expectedParentId)
1810
- return true;
1811
- const parent = findSessionFileById(dirname(sessionFile), threadParentSessionId);
1812
- return (parent !== null &&
1813
- isPlatformHistorySession(expectedParent) &&
1814
- isPlatformHistorySession(parent));
1815
- }
1816
- if (!getFixedThreadSessionId(sessionFile))
1817
- return false;
1818
- return tryResolveCurrentSession(dirname(sessionFile)) === expectedParent;
1819
- }
1820
- function getFixedThreadSessionId(sessionFile) {
1821
- const fileName = basename(sessionFile);
1822
- if (!fileName.endsWith(".jsonl"))
1823
- return null;
1824
- if (/^\d{4}-\d{2}-\d{2}T.+_[0-9a-f]{8}\.jsonl$/i.test(fileName))
1825
- return null;
1826
- return fileName.slice(0, -".jsonl".length);
1827
- }
1828
- function findThreadAnchorEntryId(parentEntries, childEntries, threadId) {
1829
- const threadTimestamp = threadId ? Number(threadId) * 1000 : undefined;
1830
- if (threadTimestamp !== undefined && Number.isFinite(threadTimestamp)) {
1831
- const timestampAnchor = findEntryIdByMessageTimestamp(parentEntries, threadTimestamp);
1832
- if (timestampAnchor)
1833
- return timestampAnchor;
1834
- }
1835
- let sharedCount = 0;
1836
- while (sharedCount < parentEntries.length &&
1837
- sharedCount < childEntries.length &&
1838
- parentEntries[sharedCount]?.id === childEntries[sharedCount]?.id) {
1839
- sharedCount += 1;
1840
- }
1841
- if (sharedCount > 0) {
1842
- return parentEntries[sharedCount - 1]?.id;
1843
- }
1844
- const contentAnchor = findSharedContentAnchorEntryId(parentEntries, childEntries);
1845
- if (contentAnchor)
1846
- return contentAnchor;
1847
- const childRoot = findComparableUserMessage(childEntries);
1848
- if (!childRoot)
1849
- return undefined;
1850
- return findParentAnchorByRootMessage(parentEntries, childRoot);
1851
- }
1852
- function findEntryIdByMessageTimestamp(entries, timestamp) {
1853
- for (const entry of entries) {
1854
- if (entry.type !== "message")
1855
- continue;
1856
- const messageTimestamp = entry.message.timestamp;
1857
- if (messageTimestamp === timestamp)
1858
- return entry.id;
1859
- }
1860
- return undefined;
1861
- }
1862
- function findSharedContentAnchorEntryId(parentEntries, childEntries) {
1863
- const parentMessages = parentEntries.flatMap((entry) => {
1864
- const comparable = getComparableSessionMessage(entry);
1865
- return comparable ? [comparable] : [];
1866
- });
1867
- const childMessages = childEntries.flatMap((entry) => {
1868
- const comparable = getComparableSessionMessage(entry);
1869
- return comparable ? [comparable] : [];
1870
- });
1871
- if (parentMessages.length === 0 || childMessages.length === 0)
1872
- return undefined;
1873
- let bestParentEnd = -1;
1874
- let bestLength = 0;
1875
- for (let parentStart = 0; parentStart < parentMessages.length; parentStart++) {
1876
- let length = 0;
1877
- while (parentStart + length < parentMessages.length && length < childMessages.length) {
1878
- const parentMessage = parentMessages[parentStart + length];
1879
- const childMessage = childMessages[length];
1880
- if (parentMessage === undefined ||
1881
- childMessage === undefined ||
1882
- parentMessage.role !== childMessage.role ||
1883
- parentMessage.normalizedText !== childMessage.normalizedText) {
1884
- break;
1885
- }
1886
- length += 1;
1887
- }
1888
- if (length > bestLength) {
1889
- bestLength = length;
1890
- bestParentEnd = parentStart + length - 1;
1891
- }
1892
- }
1893
- return bestLength > 0 ? parentMessages[bestParentEnd]?.entryId : undefined;
1894
- }
1895
- function getComparableSessionMessage(entry) {
1896
- if (entry.type !== "message")
1897
- return null;
1898
- const role = entry.message.role;
1899
- if (role !== "user" && role !== "assistant")
1900
- return null;
1901
- const body = contentToText(entry.message.content);
1902
- const normalizedText = normalizeComparableSessionText(body, role);
1903
- if (!normalizedText)
1904
- return null;
1905
- return { entryId: entry.id, role, normalizedText };
1906
- }
1907
- function normalizeComparableSessionText(text, role) {
1908
- const normalized = role === "user" ? normalizeComparableUserText(text) : text.trim();
1909
- return normalized.replace(/\s+/g, " ").trim();
1910
- }
1911
- function findParentAnchorByRootMessage(parentEntries, childRoot) {
1912
- let textMatchId;
1913
- for (const entry of parentEntries) {
1914
- const comparable = getComparableUserMessage(entry);
1915
- if (!comparable)
1916
- continue;
1917
- if (comparable.normalizedText !== childRoot.normalizedText)
1918
- continue;
1919
- if (childRoot.messageTimestamp !== undefined &&
1920
- comparable.messageTimestamp !== undefined &&
1921
- comparable.messageTimestamp === childRoot.messageTimestamp) {
1922
- return entry.id;
1923
- }
1924
- textMatchId ??= entry.id;
1925
- }
1926
- return textMatchId;
1927
- }
1928
- function findComparableUserMessage(entries) {
1929
- for (const entry of entries) {
1930
- const comparable = getComparableUserMessage(entry);
1931
- if (comparable)
1932
- return comparable;
1933
- }
1934
- return null;
1935
- }
1936
- function getComparableUserMessage(entry) {
1937
- if (entry.type !== "message" || entry.message.role !== "user")
1938
- return null;
1939
- const body = contentToText(entry.message.content);
1940
- const normalizedText = normalizeComparableUserText(body);
1941
- if (!normalizedText)
1942
- return null;
1943
- const messageTimestamp = typeof entry.message.timestamp === "number" ? entry.message.timestamp : undefined;
1944
- return { normalizedText, messageTimestamp };
1945
- }
1946
- function normalizeComparableUserText(text) {
1947
- const withoutTimestamp = text.replace(/^\[[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}[+-][0-9]{2}:[0-9]{2}\]\s+(?=\[[^\]]+\](?:\s+\[in-thread:[^\]]+\])?:\s)/, "");
1948
- return withoutTimestamp
1949
- .replace(/\n*<slack_attachments>\n[\s\S]*?\n<\/slack_attachments>\s*$/g, "")
1950
- .trim();
1951
- }
1952
1673
  function extractSessionSummary(entries) {
1953
1674
  for (const entry of entries) {
1954
1675
  if (entry.type !== "message")
@@ -1970,8 +1691,6 @@ function mapEntryToItem(entry) {
1970
1691
  return mapMessageEntry(entry);
1971
1692
  case "compaction":
1972
1693
  return mapCompactionEntry(entry);
1973
- case "branch_summary":
1974
- return mapSessionSummaryEntry(entry);
1975
1694
  case "custom":
1976
1695
  return {
1977
1696
  kind: "system",
@@ -2077,15 +1796,6 @@ function mapCompactionEntry(entry) {
2077
1796
  kind: "system",
2078
1797
  title: "Context compacted",
2079
1798
  body: entry.summary,
2080
- meta: `${entryIsoTime(entry) ?? ""} · ${entry.tokensBefore} tokens before compaction`,
2081
- tone: "muted",
2082
- };
2083
- }
2084
- function mapSessionSummaryEntry(entry) {
2085
- return {
2086
- kind: "system",
2087
- title: "Session summary",
2088
- body: entry.summary,
2089
1799
  meta: entryIsoTime(entry) ?? "",
2090
1800
  tone: "muted",
2091
1801
  };