mulmoterminal 4.5.0 → 4.6.0

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 (144) hide show
  1. package/README.md +33 -3
  2. package/bin/mulmoterminal.js +40 -1
  3. package/common/agentSessionList.ts +24 -0
  4. package/common/sessionContext.ts +24 -0
  5. package/common/sessionRunning.ts +31 -0
  6. package/dist/assets/{abnfDiagram-VRR7QNED-WSmThe6b-DPaEh_gN.js → abnfDiagram-VRR7QNED-WSmThe6b-944Gbk5i.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Cqhmai3f-ByieX7iG.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-Tj3i7Oow-CUD1E04p.js → architectureDiagram-ZJ3FMSHR-Tj3i7Oow-BIeDl9Cb.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-c-hNYBhI-oMocpv9s.js → blockDiagram-677ZJIJ3-c-hNYBhI-CHoEKRcK.js} +1 -1
  10. package/dist/assets/{c4Diagram-LMCZKHZV-WIGVajf5-DCS5Kt2M.js → c4Diagram-LMCZKHZV-WIGVajf5-C3NSXDDO.js} +1 -1
  11. package/dist/assets/channel-OqG5cGJi-9tQa1RJG.js +1 -0
  12. package/dist/assets/{chunk-32BRIVSS-CtSq3QOR-D6jNTV1x.js → chunk-32BRIVSS-CtSq3QOR-P97bmQkL.js} +1 -1
  13. package/dist/assets/{chunk-52WLFC77-2Up0T-6t-qOt9ZLFS.js → chunk-52WLFC77-2Up0T-6t-DQEaQvVy.js} +1 -1
  14. package/dist/assets/{chunk-C7G6YPKG-ivengO-7-7utsyMTv.js → chunk-C7G6YPKG-ivengO-7-j3Jy_MLk.js} +1 -1
  15. package/dist/assets/{chunk-EX3LRPZG-BHoWQ6W8-6OAUxLJG.js → chunk-EX3LRPZG-BHoWQ6W8-DZez9M2G.js} +1 -1
  16. package/dist/assets/{chunk-FWX5IMBZ-PvRXPghr-CoUoZ066.js → chunk-FWX5IMBZ-PvRXPghr-GfoO6luk.js} +2 -2
  17. package/dist/assets/{chunk-HOUHSVGY-A3_zwY4q-Z32157__.js → chunk-HOUHSVGY-A3_zwY4q-nq20Wer2.js} +1 -1
  18. package/dist/assets/{chunk-ICXQ74PX-vzZqgurQ-4S5Kaqhr.js → chunk-ICXQ74PX-vzZqgurQ-D5yDpnEM.js} +1 -1
  19. package/dist/assets/{chunk-MOJQB5TN-CorQ_Pl1-BFdYgLZH.js → chunk-MOJQB5TN-CorQ_Pl1-Bd3mtm2K.js} +1 -1
  20. package/dist/assets/{chunk-OGEWGWER-BVhfs7WG-EE3Qh8NO.js → chunk-OGEWGWER-BVhfs7WG-EBKgCF2u.js} +1 -1
  21. package/dist/assets/{chunk-PUDLZKDR-CJo7KQJ3-D1PA0tz9.js → chunk-PUDLZKDR-CJo7KQJ3-B-xjmG4r.js} +1 -1
  22. package/dist/assets/{chunk-Q4XR5HBZ-DLkq8ZGO-ySWBhGnM.js → chunk-Q4XR5HBZ-DLkq8ZGO-FAAJasw-.js} +1 -1
  23. package/dist/assets/{chunk-V7JOEXUC-DqefuUOb-DhSm2NyP.js → chunk-V7JOEXUC-DqefuUOb-BHac54Dp.js} +1 -1
  24. package/dist/assets/{chunk-VAUOI2AC-BKx35BBi-B60QprFR.js → chunk-VAUOI2AC-BKx35BBi-C-5oHwZ4.js} +1 -1
  25. package/dist/assets/{chunk-VR4S4FIN-DpJgr4Ur-aiw-751p.js → chunk-VR4S4FIN-DpJgr4Ur-DPh5RPem.js} +1 -1
  26. package/dist/assets/{chunk-WYO6CB5R-nS6zX9ct-DWK9p9qg.js → chunk-WYO6CB5R-nS6zX9ct-HZI6iW4p.js} +1 -1
  27. package/dist/assets/{chunk-ZGVPDNZ5-DNtiHxpJ-cQBGQXJU.js → chunk-ZGVPDNZ5-DNtiHxpJ-moQJIMa1.js} +1 -1
  28. package/dist/assets/classDiagram-OUVF2IWQ-C6h0nbT3-Ct0W6XkH.js +1 -0
  29. package/dist/assets/classDiagram-v2-EOCWNBFH-CzsXJ_wA-Ct0W6XkH.js +1 -0
  30. package/dist/assets/cynefin-VYW2F7L2-DvSvuXnG-C2pDg7XE.js +1 -0
  31. package/dist/assets/{cynefinDiagram-TSTJHNR4-969y1qZK-76_lFdlS.js → cynefinDiagram-TSTJHNR4-969y1qZK-AyVAxduh.js} +1 -1
  32. package/dist/assets/{dagre-VKFMJZFB-CZfgAZFk-BIp6P02L.js → dagre-VKFMJZFB-CZfgAZFk-3FlboOqe.js} +1 -1
  33. package/dist/assets/{diagram-FQU43EPY-CSdnleGM-Cfb5V0UI.js → diagram-FQU43EPY-CSdnleGM-XmeOhiBK.js} +1 -1
  34. package/dist/assets/{diagram-G47NLZAW-DKYe-Mwx-DqsHnBaF.js → diagram-G47NLZAW-DKYe-Mwx-oGKIv19o.js} +1 -1
  35. package/dist/assets/{diagram-NH7WQ7WH-DMasKpwa-CNiFWceN.js → diagram-NH7WQ7WH-DMasKpwa-hNtNA4bT.js} +1 -1
  36. package/dist/assets/{diagram-OA4YK3LP-CSyc0BDR-OiAwSIjr.js → diagram-OA4YK3LP-CSyc0BDR-CgkF6797.js} +1 -1
  37. package/dist/assets/{diagram-WEI45ONY-CcJW2fNK-TQf1z8cW.js → diagram-WEI45ONY-CcJW2fNK-Bs6GsUVb.js} +1 -1
  38. package/dist/assets/{dist-BezNTABu.js → dist-BLDGY-kQ.js} +1 -1
  39. package/dist/assets/{dist-RBakV0o4.js → dist-DnpJnWYp.js} +1 -1
  40. package/dist/assets/{dist-BR8nhjbg.js → dist-DsyF0XCw.js} +1 -1
  41. package/dist/assets/{dist-9wGqhibT.js → dist-uMLVGS-I.js} +1 -1
  42. package/dist/assets/{ebnfDiagram-CCIWWBDH-CeBUH2N0-B8mwuH_Q.js → ebnfDiagram-CCIWWBDH-CeBUH2N0-qDGiZqxm.js} +1 -1
  43. package/dist/assets/{erDiagram-Q63AITRT-1OOp34oV-4zV7QoN3.js → erDiagram-Q63AITRT-1OOp34oV-CGCbIXw6.js} +1 -1
  44. package/dist/assets/eventmodeling-45OFAUF4-jdctDg0T-DGj5pagQ.js +1 -0
  45. package/dist/assets/flowDiagram-23GEKE2U-DK0InIny-CYoqs42L.js +1 -0
  46. package/dist/assets/{ganttDiagram-NO4QXBWP-DQT88dUZ-DmKfWKVt.js → ganttDiagram-NO4QXBWP-DQT88dUZ-BcXauXRP.js} +1 -1
  47. package/dist/assets/gitGraph-TEB2WS4Q-C4cKwl5j-hM_pJP8C.js +1 -0
  48. package/dist/assets/{gitGraphDiagram-IHSO6WYX-CeZGS4qi-CPiO74HP.js → gitGraphDiagram-IHSO6WYX-CeZGS4qi-CFqg-BSd.js} +1 -1
  49. package/dist/assets/index-DWpBB5bS.js +619 -0
  50. package/dist/assets/index-DnhwCMuN.css +1 -0
  51. package/dist/assets/info-DKCQHKI2-DPUCBYWS-D2hq_ZvH.js +1 -0
  52. package/dist/assets/{infoDiagram-FWYZ7A6U-BDt8lBTy-BJdFxf7J.js → infoDiagram-FWYZ7A6U-BDt8lBTy-DuWcN2yb.js} +1 -1
  53. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DOHYs_AA-DN4-dfNt.js → ishikawaDiagram-FXEZZL3T-DOHYs_AA-B4h8r5-o.js} +1 -1
  54. package/dist/assets/{journeyDiagram-5HDEW3XC-B33IW9yy-D46iXC-G.js → journeyDiagram-5HDEW3XC-B33IW9yy-BnAHKNKa.js} +1 -1
  55. package/dist/assets/{kanban-definition-HUTT4EX6-Bkdm46eX-DaJbuRjn.js → kanban-definition-HUTT4EX6-Bkdm46eX-BH5Oa5jm.js} +1 -1
  56. package/dist/assets/{lib-BD8LgZAH.js → lib-3N8U0X_t.js} +1 -1
  57. package/dist/assets/{line-Dy_Qlcgo-DWmsY6xO.js → line-Dy_Qlcgo-C2GdKfnD.js} +1 -1
  58. package/dist/assets/{marp-CwNmXt5E.js → marp-DSdA8U3h.js} +1 -1
  59. package/dist/assets/{mermaid-parser.core-C1baW7NG-Bt8E3kO2.js → mermaid-parser.core-C1baW7NG-DSG0yv6A.js} +2 -2
  60. package/dist/assets/{mermaid.core-C09q4TmN-CdpmD79K.js → mermaid.core-C09q4TmN-yteTzMrj.js} +3 -3
  61. package/dist/assets/{mindmap-definition-LN4V7U3C-bfYIhPQ0-C4RQ97Ls.js → mindmap-definition-LN4V7U3C-bfYIhPQ0-CK8OM9lm.js} +1 -1
  62. package/dist/assets/packet-7NZHBO7P-CC7His0F-DBkSyrp5.js +1 -0
  63. package/dist/assets/{pegDiagram-2B236MQR-CZQOKUMC-BGzTvU6v.js → pegDiagram-2B236MQR-CZQOKUMC-BoW13vlD.js} +1 -1
  64. package/dist/assets/pie-RZYD4A2V-CCrcq2sL-DhH_1kd9.js +1 -0
  65. package/dist/assets/{pieDiagram-ENE6RG2P-DiVKPdHE-XKldq1E5.js → pieDiagram-ENE6RG2P-DiVKPdHE-ghK3nyU-.js} +1 -1
  66. package/dist/assets/{quadrantDiagram-ABIIQ3AL-CbPY3kXI-C5r0a9Sf.js → quadrantDiagram-ABIIQ3AL-CbPY3kXI-De-fNZFH.js} +1 -1
  67. package/dist/assets/radar-I7S5WNFK-C5TUrpHf-B2kw7JUI.js +1 -0
  68. package/dist/assets/railroad-3IZDKUUU-CGuuFEd8-DrgAOxGb.js +1 -0
  69. package/dist/assets/railroad-abnf-AHOZXSZD-DuoCSFZY-xKkuEcIV.js +1 -0
  70. package/dist/assets/railroad-ebnf-EBAXGLYW-ig-DG9e_-ax9Y6qHk.js +1 -0
  71. package/dist/assets/railroad-peg-LSFZ7HO6-BkBgpyvu-DI85BUnl.js +1 -0
  72. package/dist/assets/{railroadDiagram-RFXS5EU6-CFRaQzr0-YImSnBwl.js → railroadDiagram-RFXS5EU6-CFRaQzr0-BVCSojzw.js} +1 -1
  73. package/dist/assets/{requirementDiagram-TGXJPOKE-BiIDduje-CLDWdQ6G.js → requirementDiagram-TGXJPOKE-BiIDduje-B_R-QkST.js} +1 -1
  74. package/dist/assets/{sankeyDiagram-HTMAVEWB-DcK87A5Y-CzQDWlB1.js → sankeyDiagram-HTMAVEWB-DcK87A5Y-CAwdVvk0.js} +1 -1
  75. package/dist/assets/{sequenceDiagram-DBY2YBRQ-DgEQYcvc-Cql9GjHP.js → sequenceDiagram-DBY2YBRQ-DgEQYcvc-GvNe1Ov-.js} +1 -1
  76. package/dist/assets/{stateDiagram-2N3HPSRC-Bg7EXtW2-CN3bsM_d.js → stateDiagram-2N3HPSRC-Bg7EXtW2-DzBMtG4d.js} +1 -1
  77. package/dist/assets/stateDiagram-v2-6OUMAXLB-BjhArXP_-CS1Pl40x.js +1 -0
  78. package/dist/assets/{swimlanes-5IMT3BWC-CKORDKr0-cK0-DqfM.js → swimlanes-5IMT3BWC-CKORDKr0-CSoxYOxz.js} +1 -1
  79. package/dist/assets/swimlanesDiagram-G3AALYLV-CyvIfl1o-C573oTpM.js +8 -0
  80. package/dist/assets/{timeline-definition-FHXFAJF6-BM3Iky2r-C6aXUCxm.js → timeline-definition-FHXFAJF6-BM3Iky2r-DJknCJjt.js} +1 -1
  81. package/dist/assets/treeView-QDETBFTQ-CqfT0I1z-DIKDkQMT.js +1 -0
  82. package/dist/assets/treemap-6X3UGDF4-CiJkiGWo-StkZmf8x.js +1 -0
  83. package/dist/assets/{vennDiagram-L72KCM5P-VNmAhEFP-T681nWRy.js → vennDiagram-L72KCM5P-VNmAhEFP-DzdfLEFR.js} +1 -1
  84. package/dist/assets/wardley-OPB4EBWU-D1tuVHAV-D9snqrLP.js +1 -0
  85. package/dist/assets/{wardleyDiagram-EHGQE667-BkHQpyFm-D-AfB-NA.js → wardleyDiagram-EHGQE667-BkHQpyFm-B4K7c047.js} +1 -1
  86. package/dist/assets/{xychartDiagram-FW5EYKEG-Yz-8aLqK-CNzoNtQE.js → xychartDiagram-FW5EYKEG-Yz-8aLqK-BS6GgTLs.js} +1 -1
  87. package/dist/index.html +2 -2
  88. package/package.json +3 -3
  89. package/server/agents/antigravity-proto.ts +168 -0
  90. package/server/agents/antigravity-session.ts +9 -2
  91. package/server/agents/antigravity-sessions.ts +36 -0
  92. package/server/agents/antigravity-usage.ts +184 -0
  93. package/server/agents/codex-events.ts +20 -0
  94. package/server/agents/codex-usage.spec.ts +77 -0
  95. package/server/agents/codex-usage.ts +110 -0
  96. package/server/agents/grok-session.ts +6 -0
  97. package/server/agents/grok-sessions.ts +200 -0
  98. package/server/agents/grok-usage.ts +99 -0
  99. package/server/config/app-config.ts +39 -1
  100. package/server/config/dir-config.ts +6 -19
  101. package/server/config/dir-file.ts +33 -0
  102. package/server/config/dir-icon-detect.ts +7 -2
  103. package/server/config/dir-icon.ts +9 -20
  104. package/server/files/open-dir.ts +53 -15
  105. package/server/files/pick-file.ts +186 -31
  106. package/server/files/ps-quote.ts +7 -0
  107. package/server/files/win-folder-dialog.ts +25 -4
  108. package/server/files/wsl.ts +49 -0
  109. package/server/index.ts +1 -0
  110. package/server/routes/session-routes.ts +97 -13
  111. package/server/routes/ws-routes.ts +136 -133
  112. package/server/session/agent-badges.ts +200 -0
  113. package/server/session/agent-conversations.ts +25 -0
  114. package/server/session/dir-session.ts +20 -0
  115. package/server/session/last-turn.ts +3 -7
  116. package/server/session/spawn-antigravity.ts +24 -50
  117. package/server/session/spawn-deps.ts +4 -0
  118. package/server/session/spawn-directory-mcp.ts +91 -0
  119. package/server/session/spawn-grok.ts +12 -48
  120. package/server/session/transcript.ts +7 -4
  121. package/server/skills/mulmoterminal-model/SKILL.md +15 -3
  122. package/dist/assets/architecture-TIHT7OUA-Cqhmai3f-DCOr0rgu.js +0 -1
  123. package/dist/assets/channel-OqG5cGJi-3dRrNd2x.js +0 -1
  124. package/dist/assets/classDiagram-OUVF2IWQ-C6h0nbT3-he2Qx8Zy.js +0 -1
  125. package/dist/assets/classDiagram-v2-EOCWNBFH-CzsXJ_wA-he2Qx8Zy.js +0 -1
  126. package/dist/assets/cynefin-VYW2F7L2-DvSvuXnG-BM05JO9d.js +0 -1
  127. package/dist/assets/eventmodeling-45OFAUF4-jdctDg0T-D7yLQEKX.js +0 -1
  128. package/dist/assets/flowDiagram-23GEKE2U-DK0InIny-XdeCwjZj.js +0 -1
  129. package/dist/assets/gitGraph-TEB2WS4Q-C4cKwl5j-CNGA-Dxh.js +0 -1
  130. package/dist/assets/index-B7qYhdzG.js +0 -614
  131. package/dist/assets/index-DnimPwKc.css +0 -1
  132. package/dist/assets/info-DKCQHKI2-DPUCBYWS-DggNvHPl.js +0 -1
  133. package/dist/assets/packet-7NZHBO7P-CC7His0F-DIVJjy2R.js +0 -1
  134. package/dist/assets/pie-RZYD4A2V-CCrcq2sL-DbntX_Y9.js +0 -1
  135. package/dist/assets/radar-I7S5WNFK-C5TUrpHf-BjpTp9D5.js +0 -1
  136. package/dist/assets/railroad-3IZDKUUU-CGuuFEd8-BgrykldZ.js +0 -1
  137. package/dist/assets/railroad-abnf-AHOZXSZD-DuoCSFZY-4ECSfnJj.js +0 -1
  138. package/dist/assets/railroad-ebnf-EBAXGLYW-ig-DG9e_-DIGuLSDA.js +0 -1
  139. package/dist/assets/railroad-peg-LSFZ7HO6-BkBgpyvu-WBfEJm_q.js +0 -1
  140. package/dist/assets/stateDiagram-v2-6OUMAXLB-BjhArXP_-PWEWNNBX.js +0 -1
  141. package/dist/assets/swimlanesDiagram-G3AALYLV-CyvIfl1o-DRhfZv9k.js +0 -8
  142. package/dist/assets/treeView-QDETBFTQ-CqfT0I1z-C1kvz6Pn.js +0 -1
  143. package/dist/assets/treemap-6X3UGDF4-CiJkiGWo-CWCrFJlO.js +0 -1
  144. package/dist/assets/wardley-OPB4EBWU-D1tuVHAV-B-2c3CsH.js +0 -1
package/README.md CHANGED
@@ -227,6 +227,7 @@ Needs **Node ≥ 22.9**, plus these CLIs on your `PATH`:
227
227
  | Optional | `codex` | [Codex sessions](#agents-claude--codex) in a cell, alongside Claude | `npm i -g @openai/codex` |
228
228
  | Optional | `ffmpeg` | video rendering from the [mulmo-script panel](#wiki-collections--the-gui-panel) (its plugin ships enabled) | `brew install ffmpeg` · `sudo apt install ffmpeg` · `sudo dnf install ffmpeg` |
229
229
  | Optional | `ollama` | [`claude-ollama`](https://receptron.github.io/mulmoterminal/guide/en/claude-ollama.html) — Claude Code against a fully local model | [ollama.com/download](https://ollama.com/download) |
230
+ | Linux only | a file dialog | the **Choose a folder / Insert a file path** buttons, which open an OS dialog on the machine the server runs on. macOS and Windows have one built in; **WSL** uses the Windows one over interop and needs nothing installed. A Linux desktop needs one of these — without any, the buttons say so and you type the path instead (#1447) | `sudo apt install zenity` · `sudo dnf install zenity` · `kdialog`, `qarma` and `yad` also work |
230
231
 
231
232
  The server starts without any of the non-required rows; you just lose that row's feature,
232
233
  and the header/panel for it says so. `git` and `gh` are marked required because losing them
@@ -1142,6 +1143,33 @@ Each grid cell's header shows two badges for its session, refreshed when a turn
1142
1143
  - **Token badge** — `⇡<in> ⇣<out>`: cumulative input (fresh + cache-read + cache-creation)
1143
1144
  and output tokens for the session, k/M-formatted, with a full breakdown in the tooltip.
1144
1145
 
1146
+ **Both badges are read from the agent's own log**, so what each agent can show differs by
1147
+ what it writes down (`?agent=` on the route picks the reader):
1148
+
1149
+ | Agent | Context badge | Token badge |
1150
+ |---|---|---|
1151
+ | **Claude** | `Opus · ctx 35%` — window from the table above | full |
1152
+ | **Codex** | `gpt-5.5 · ctx 21%` — window from codex's own `model_context_window`, so no table to be out of date | full |
1153
+ | **Grok** | `grok-4.5 · ctx 33%` — window from grok's own `contextWindowTokens`, so no table either | full |
1154
+ | **Antigravity** | `Gemini 3.6 Flash · ctx 78%` — the model from the first step of the conversation's transcript, the reading from agy's own per-generation accounting (a real 256k window, not a table) | full |
1155
+
1156
+ The token badge hides itself when nothing has been counted, and the context badge shows
1157
+ the model alone unless it has **both** a *current-context* token count from the agent and a
1158
+ context window — agent-reported (codex, Grok, Antigravity) or resolved from the built-in table
1159
+ above (Claude, provider models). Either one missing means a name and no percentage. The two
1160
+ badges are independent: an Antigravity session whose accounting cannot be read still shows
1161
+ its model, and one whose cumulative totals are zero still shows a percentage. The context badge is absent
1162
+ entirely until an agent has named a model: codex and Antigravity file their logs under an id
1163
+ the agent mints *after* the session starts, so a brand-new cell shows no model badge until it
1164
+ has been prompted once — a few seconds, not the rest of the session.
1165
+
1166
+ Antigravity's numbers are the one case read from a store with **no published format**: agy keeps
1167
+ its per-generation accounting as protobuf in `~/.gemini/antigravity-cli/conversations/<id>.db`,
1168
+ with no schema on disk, so the fields are identified by measurement (see
1169
+ `server/agents/antigravity-usage.ts`). Every layer of that reader is built to answer *nothing*
1170
+ rather than a number it is unsure of, so if a future agy release moves those fields, an
1171
+ Antigravity cell falls back to showing its model alone — it will not show a wrong percentage.
1172
+
1145
1173
  The **Settings** modal (⚙) shows an **estimated $ cost** — Session / Today / Month — from
1146
1174
  `GET /api/cost`, using a built-in public per-model price table (cache reads billed at
1147
1175
  0.1×, cache writes at 1.25× input). It's an estimate: real billing differs, **flat-plan
@@ -1369,7 +1397,7 @@ quietly answering about the **default workspace** (#1151):
1369
1397
  | --- | --- |
1370
1398
  | `/ws`, `/ws/codex`, `/ws/antigravity`, `/ws/grok`, `/ws/launch`, `/ws/run` | The socket is closed with `{ type: "error", message }`, which the terminal shows as a red banner and does not retry. |
1371
1399
  | A session that is still running (`?session=` names a live PTY or a surviving tmux session) | **Attaches anyway**, with a warning in the server log. Moving or renaming a directory must not shut you out of an agent that is still working in it — and the cwd reported back comes from the running PTY, not from the request. |
1372
- | `GET /api/scripts`, `/api/skills`, `/api/dir-config`, `/api/dir-sound`, `/api/git-status`, `/api/pr-phase`, `/api/header`, `/api/sessions`, `/api/codex/sessions`, `/api/antigravity/sessions`, `/api/session/:id`, `/api/transcript/*`, `/api/cost` | `404 { error, cwd }` — a directory that is not there. |
1400
+ | `GET /api/scripts`, `/api/skills`, `/api/dir-config`, `/api/dir-sound`, `/api/git-status`, `/api/pr-phase`, `/api/header`, `/api/sessions`, `/api/codex/sessions`, `/api/antigravity/sessions`, `/api/grok/sessions`, `/api/session/:id`, `/api/transcript/*`, `/api/cost` | `404 { error, cwd }` — a directory that is not there. |
1373
1401
  | A `?cwd=` that cannot name a directory at all (relative, or repeated as `?cwd=a&cwd=b`) | `400 { error, cwd }`. |
1374
1402
 
1375
1403
  A request that names **no** directory is unaffected: `CLAUDE_CWD` is then the answer it
@@ -1491,6 +1519,7 @@ same-origin-guarded.
1491
1519
  | `GET /api/session/:id?cwd=` | One session's summary — cumulative `usage` and `context` (model + last-turn context tokens). Backs the cell token & ctx% badges. |
1492
1520
  | `GET /api/codex/sessions?cwd=` | Codex sessions for the project (from `~/.codex` rollouts), newest first. |
1493
1521
  | `GET /api/antigravity/sessions?cwd=` | Antigravity conversations for the project, newest first. agy does record a workspace, but never as a complete conversation-to-workspace map (`cache/last_conversations.json` keeps one conversation per directory and is written at exit; `history.jsonl` carries no conversation id), so the project comes from MulmoTerminal's own `~/.mulmoterminal/antigravity-conversations.jsonl`; agy's transcript supplies the title. |
1522
+ | `GET /api/grok/sessions?cwd=` | Grok conversations for the project, newest first. `~/.grok/sessions` is partitioned by working directory (percent-encoded), so this is a directory listing; each conversation's `summary.json` supplies the title and the last-active time, falling back to the directory's `prompt_history.jsonl`. |
1494
1523
  | `GET /api/cost?cwd=&session=` | Estimated $ cost — session / today / month. |
1495
1524
  | `GET /api/transcript/timeline?session=&cwd=` | Per-session activity timeline (tools run). |
1496
1525
  | `GET /api/transcript/last-turn?session=&cwd=&agent=` | A session's last completed exchange (`prompt`, `reply`) plus the `text` to paste into another terminal. `agent=codex` reads the codex rollout instead of the Claude transcript. |
@@ -1541,7 +1570,7 @@ same-origin-guarded.
1541
1570
  | `POST /api/transcribe`(`/model`…) | Voice-input transcription (Whisper, macOS). |
1542
1571
  | `POST /api/translation` | Runtime UI-string translation. |
1543
1572
  | `GET /api/remote-host/status` · `POST /api/remote-host/{connect,disconnect}` | Companion phone-client link. Each response carries the command channel's `health` (`online` / `reconnecting` / `offline`, plus the last listener error), so the toolbar shows a dropped channel instead of the last state it happened to fetch. |
1544
- | `POST /api/open-dir` · `POST /api/pick-file` | Reveal a dir in Finder/Explorer; OS file-picker → path (`{ directory: true }` opens the folder picker — used by the launcher's Working-directory 📁 button). |
1573
+ | `POST /api/open-dir` · `POST /api/pick-file` | Reveal a dir in Finder/Explorer; OS file-picker → path (`{ directory: true }` opens the folder picker — used by the launcher's Working-directory 📁 button). Both run on the SERVER's machine, and both answer **500 with a reason** when it has nothing to open a dialog with, rather than a silent nothing (#1447). The picker tries every dialog the host might have: macOS `osascript`, Windows PowerShell, **WSL** the Windows dialog over interop (`powershell.exe` + `wslpath`), Linux `zenity` → `kdialog` → `qarma` → `yad`. A user cancel is a 200 with `paths: []`. |
1545
1574
  | `POST /api/session/:id/drop` | A dropped file whose path the browser withheld. **Raw bytes**, not JSON, under the file's own content type (base64 in JSON would cap real files near 18 MB, and a dropped `.json` would be parsed as a document); the original name rides percent-encoded in `x-drop-filename` and is used for its **suffix only**. Answers `{ path }` — absolute, inside the private per-session directory the session was granted at launch. 110 MiB cap; 404 for a session this server isn't running. |
1546
1575
 
1547
1576
  The phone itself uses **none** of these routes — it reaches the host over Firestore command
@@ -1857,7 +1886,8 @@ server/
1857
1886
  git/ git, GitHub (gh) and GitLab (glab) + worktrees: git-status.ts, gitRemote.ts,
1858
1887
  gh.ts, prs.ts, issues.ts, pr-for-branch.ts, worktrees.ts, worktree-*.ts
1859
1888
  files/ files-browse.ts (contained tree read/write), pick-file.ts,
1860
- open-dir.ts, scripts.ts (Run-menu script.json loader)
1889
+ open-dir.ts, wsl.ts (interop detection + wslpath),
1890
+ scripts.ts (Run-menu script.json loader)
1861
1891
  infra/ process/transport/misc: tmux.ts, tmux-routes.ts,
1862
1892
  pubsub.ts (socket.io /ws/pubsub), spa-fallback.ts, host-tools.ts,
1863
1893
  plugins-registry.ts, web-push.ts, install-bundled-skills.ts, accounting-tool.ts
@@ -9,6 +9,7 @@ import { execSync, spawn } from "node:child_process";
9
9
  import { existsSync, statSync } from "node:fs";
10
10
  import { createRequire } from "node:module";
11
11
  import { createServer } from "node:net";
12
+ import { release } from "node:os";
12
13
  import { dirname, join, resolve } from "node:path";
13
14
  import { createInterface } from "node:readline";
14
15
  import { fileURLToPath } from "node:url";
@@ -106,6 +107,40 @@ const PATH_TOOLS = [
106
107
  { cmd: "ollama", versionArg: "--version", required: false, why: "a fully local model via claude-ollama", hint: "https://ollama.com/download" },
107
108
  ];
108
109
 
110
+ // The variables WSL exports into a login shell, then the kernel that names itself
111
+ // (`…-microsoft-standard-WSL2`, `…-Microsoft` on WSL1) for the processes those variables never
112
+ // reach. Mirrors `isWsl()` in server/files/wsl.ts, which this file cannot import: bin runs as
113
+ // plain JS, the server runs through tsx. Keep the two in step.
114
+ function isWslHost() {
115
+ if (process.platform !== "linux") return false;
116
+ return Boolean(process.env.WSL_DISTRO_NAME || process.env.WSL_INTEROP) || /microsoft/i.test(release());
117
+ }
118
+
119
+ // The file dialog is the one requirement that differs per host, so it is checked per host rather
120
+ // than listed above: macOS and Windows have one built in and can't be missing it. WSL reaches the
121
+ // Windows dialog over interop, and a Linux desktop has whichever toolkit it was installed with —
122
+ // zenity's absence there is what makes the picker look broken (#1447).
123
+ function fileDialogTool() {
124
+ if (process.platform === "darwin" || process.platform === "win32") return null;
125
+ const why = "the file / folder picker";
126
+ if (isWslHost()) {
127
+ return {
128
+ cmd: "powershell.exe",
129
+ versionArg: "-NoProfile -Command exit",
130
+ required: false,
131
+ why: `${why}, using the Windows dialog`,
132
+ hint: "enable WSL interop (wsl.conf), or: sudo apt install zenity",
133
+ };
134
+ }
135
+ return {
136
+ cmd: "zenity",
137
+ versionArg: "--version",
138
+ required: false,
139
+ why: `${why} (kdialog / qarma / yad also work)`,
140
+ hint: "sudo apt install zenity · sudo dnf install zenity",
141
+ };
142
+ }
143
+
109
144
  function toolCheckLine({ cmd, versionArg, required, why, hint }) {
110
145
  if (hasCommand(cmd, versionArg)) return ` ✓ ${cmd} — ${why}`;
111
146
  const head = required ? ` ✗ ${cmd} — not found, needed for ${why}` : ` ○ ${cmd} — optional (${why})`;
@@ -139,7 +174,7 @@ async function runInit(initArgs) {
139
174
  console.log(" → npm install -g @anthropic-ai/claude-code (then run `claude` and log in)");
140
175
  }
141
176
 
142
- PATH_TOOLS.forEach((tool) => console.log(toolCheckLine(tool)));
177
+ [...PATH_TOOLS, fileDialogTool()].filter(Boolean).forEach((tool) => console.log(toolCheckLine(tool)));
143
178
 
144
179
  // Config half: derive working-dir presets from Claude history + write config.json.
145
180
  console.log("");
@@ -188,9 +223,13 @@ function runGoogle(googleArgs) {
188
223
  });
189
224
  }
190
225
 
226
+ // WSL has no Linux browser to open (and often no `xdg-open` either), so the URL goes to the
227
+ // Windows shell — `start` through cmd.exe, which is what opens the user's real browser. It exits
228
+ // 0, unlike `explorer.exe`, so a failure here still means a failure.
191
229
  function pickOpenCommand() {
192
230
  if (process.platform === "darwin") return "open";
193
231
  if (process.platform === "win32") return "start";
232
+ if (isWslHost()) return 'cmd.exe /c start ""';
194
233
  return "xdg-open";
195
234
  }
196
235
 
@@ -0,0 +1,24 @@
1
+ // Where each agent's own past conversations are listed — one route per agent, keyed by the agent.
2
+ //
3
+ // The launcher's "or resume here" list shows the conversations of whatever the Agent Picker has
4
+ // selected, and each agent keeps its history in its own place: Claude in `~/.claude/projects`,
5
+ // codex in `~/.codex/sessions`, agy in its brain directory (found through our own log), grok in a
6
+ // per-cwd directory under `~/.grok/sessions`. Four stores, four listers, four routes — so the
7
+ // route is chosen, not built.
8
+ //
9
+ // In `common/` because both sides decide from it: the server mounts these paths
10
+ // (server/routes/session-routes.ts) and the UI fetches them (src/composables/useDirLists.ts). A
11
+ // `Record<TerminalAgent, …>` rather than a lookup with a fallback, so a fifth agent is a TYPE
12
+ // ERROR here instead of a picker option that silently lists Claude's history under another name —
13
+ // which is the bug this whole map exists to fix (#1417).
14
+ import type { TerminalAgent } from "./sessionAgent.js";
15
+
16
+ export const AGENT_SESSION_LIST_PATHS: Record<TerminalAgent, string> = {
17
+ claude: "/api/sessions",
18
+ codex: "/api/codex/sessions",
19
+ antigravity: "/api/antigravity/sessions",
20
+ grok: "/api/grok/sessions",
21
+ };
22
+
23
+ /** The listing URL for one agent's conversations in one directory. */
24
+ export const agentSessionListUrl = (agent: TerminalAgent, cwd: string): string => `${AGENT_SESSION_LIST_PATHS[agent]}?cwd=${encodeURIComponent(cwd)}`;
@@ -0,0 +1,24 @@
1
+ // Which model a session is running and how full its context is: the `context` object
2
+ // GET /api/session/:id answers with, and the thing the header's model badge renders.
3
+ //
4
+ // In `common/` because BOTH sides decide from it. The server fills it from whichever log the
5
+ // session's agent keeps, and the client decides from `contextWindow` whether to trust the agent's
6
+ // own window or fall back to its per-model table (`src/components/modelBadge.ts`). It was mirrored
7
+ // as `LatestTurnContext` on the server and `CellContext` in the UI; both are now this.
8
+ export interface SessionContextInfo {
9
+ /** null until a turn has told us — a session that has not answered yet wears no badge. */
10
+ model: string | null;
11
+ /** Tokens re-sent as context for the NEXT turn, off the most recent completed turn. */
12
+ contextTokens: number;
13
+ /**
14
+ * The model's context window, when the AGENT ITSELF reports one. codex writes
15
+ * `model_context_window` into every `token_count` event, grok `contextWindowTokens` into its
16
+ * `signals.json` and agy the window into its accounting store, which is better than any table we
17
+ * can keep: the substring list in
18
+ * modelBadge.ts has already claimed a model whose window it had wrong (#985), and nothing on
19
+ * this side can notice when a provider changes one.
20
+ *
21
+ * Absent (or null) means nobody told us, NOT "no window" — the client falls back to its table.
22
+ */
23
+ contextWindow?: number | null;
24
+ }
@@ -0,0 +1,31 @@
1
+ // Whether a listed conversation still has a session RUNNING for it, and under which key.
2
+ //
3
+ // Deliberately not part of `SessionOccupancy` next door, which answers a different question for a
4
+ // different caller: "is a terminal holding this right now", the fact a second cell must know before
5
+ // offering to open it. That one is computed from `tmux list-clients` — which reports only sessions
6
+ // that HAVE a client, so it is structurally blind to the case here: a session running with nobody
7
+ // attached. Those are what a server restart leaves behind, and they piled up unseen because nothing
8
+ // in the app could see them either (#1467).
9
+ //
10
+ // In `common/` because both sides decide from it: the server fills it from its pty table plus
11
+ // `tmux list-sessions` (routes/session-routes.ts), and the launcher draws the marker and the stop
12
+ // button from it (components/CellLaunchForm.vue).
13
+
14
+ export interface SessionRunning {
15
+ /**
16
+ * The key of the session running for this row, or null when nothing is.
17
+ *
18
+ * A KEY, not the row's id. A codex/agy conversation started from a grid cell runs under a key
19
+ * MulmoTerminal minted, and only that agent's conversation log connects the two — the same reason
20
+ * `attached` consults it. Terminating the row's id there would kill nothing and report success.
21
+ */
22
+ runningKey: string | null;
23
+ }
24
+
25
+ /**
26
+ * The same field as the CLIENT may receive it — optional, like `PartialSessionOccupancy`: a page
27
+ * left open across an upgrade parses rows from a server that never said. Absent must read as
28
+ * "nothing running", so an older server's rows keep their old behaviour (no marker, no button)
29
+ * rather than offering to stop something that was never asked about.
30
+ */
31
+ export type PartialSessionRunning = Partial<SessionRunning>;
@@ -1 +1 @@
1
- import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-CvsWqFln-Tc7ZSrRz.js";import{_ as n,g as r}from"./mermaid-parser.core-C1baW7NG-Bt8E3kO2.js";import"./chunk-WYO6CB5R-nS6zX9ct-DWK9p9qg.js";import"./chunk-VAUOI2AC-BKx35BBi-B60QprFR.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CorQ_Pl1-BFdYgLZH.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
1
+ import{n as e}from"./chunk-Y2CYZVJY-Bdt8pFDJ-DsF7k-Jl.js";import{f as t}from"./src-CvsWqFln-Tc7ZSrRz.js";import{_ as n,g as r}from"./mermaid-parser.core-C1baW7NG-DSG0yv6A.js";import"./chunk-WYO6CB5R-nS6zX9ct-HZI6iW4p.js";import"./chunk-VAUOI2AC-BKx35BBi-C-5oHwZ4.js";import{n as i,r as a,t as o}from"./chunk-MOJQB5TN-CorQ_Pl1-Bd3mtm2K.js";import{t as s}from"./chunk-JWPE2WC7-BQ3zXr2k-DSd3Ct95.js";var c=r().RailroadAbnf.parser.LangiumParser,l=e(e=>{let t=e.alternatives.map(u);return t.length===1?t[0]:{type:`choice`,alternatives:t}},`transformAlternation`),u=e(e=>{let t=e.elements.map(f);return t.length===1?t[0]:{type:`sequence`,elements:t}},`transformConcatenation`),d=e(e=>{if(e.includes(`*`)){let[t,n]=e.split(`*`);return{min:t?parseInt(t,10):0,max:n?parseInt(n,10):1/0}}let t=parseInt(e,10);return{min:t,max:t}},`parseRepeat`),f=e(e=>{let t=p(e.primary);if(!e.repeat)return t;let{min:n,max:r}=d(e.repeat);return n===0&&r===1?{type:`optional`,element:t}:{type:`repetition`,element:t,min:n,max:r}},`transformElement`),p=e(e=>{switch(e.$type){case`AbnfStringLiteral`:return{type:`terminal`,value:e.value};case`AbnfNumVal`:return{type:`terminal`,value:e.value};case`AbnfRuleName`:return{type:`nonterminal`,name:e.name};case`AbnfGroup`:return l(e.element);case`AbnfOptionalGroup`:return{type:`optional`,element:l(e.element)};default:throw Error(`Unsupported ABNF primary node: ${e.$type}`)}},`transformPrimary`),m=e(e=>({name:e.name,definition:l(e.definition)}),`transformRule`),h=e(e=>{s(e,a),e.title&&a.setTitle(e.title),e.rules.map(e=>a.addRule(m(e)))},`populateDb`),g={parser:{parse:e(e=>{a.clear(),t.debug(`[ABNF Parser] Starting Langium parse`);let r=c.parse(e);if(r.lexerErrors.length>0||r.parserErrors.length>0)throw new n(r);let i=r.value;t.debug(`[ABNF Parser] Parsed rules:`,i.rules.length),h(i),t.debug(`[ABNF Parser] Parse complete`)},`parse`),parser:{yy:a}},db:a,renderer:o,styles:i};export{g as diagram};
@@ -0,0 +1 @@
1
+ import{f as e}from"./mermaid-parser.core-C1baW7NG-DSG0yv6A.js";export{e as createArchitectureServices};