@songsid/agend 2.3.3 → 2.3.4-beta.10

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 (368) hide show
  1. package/README.md +3 -3
  2. package/README.zh-TW.md +3 -3
  3. package/THIRD_PARTY_NOTICES.md +37 -0
  4. package/dist/agend-home.d.ts +13 -0
  5. package/dist/agend-home.js +42 -0
  6. package/dist/agend-home.js.map +1 -1
  7. package/dist/agent-cli-instructions.md +2 -2
  8. package/dist/agent-credentials.d.ts +24 -0
  9. package/dist/agent-credentials.js +66 -0
  10. package/dist/agent-credentials.js.map +1 -0
  11. package/dist/agent-endpoint.d.ts +3 -1
  12. package/dist/agent-endpoint.js +13 -0
  13. package/dist/agent-endpoint.js.map +1 -1
  14. package/dist/auth-api.d.ts +16 -3
  15. package/dist/auth-api.js +32 -8
  16. package/dist/auth-api.js.map +1 -1
  17. package/dist/authority/session-authorizations.d.ts +28 -4
  18. package/dist/authority/session-authorizations.js +63 -17
  19. package/dist/authority/session-authorizations.js.map +1 -1
  20. package/dist/authority/web-authority.d.ts +15 -1
  21. package/dist/authority/web-authority.js +27 -10
  22. package/dist/authority/web-authority.js.map +1 -1
  23. package/dist/backend/antigravity.js +3 -1
  24. package/dist/backend/antigravity.js.map +1 -1
  25. package/dist/backend/claude-code.d.ts +3 -0
  26. package/dist/backend/claude-code.js +100 -5
  27. package/dist/backend/claude-code.js.map +1 -1
  28. package/dist/backend/codex-footer-hints.d.ts +4 -0
  29. package/dist/backend/codex-footer-hints.js +27 -0
  30. package/dist/backend/codex-footer-hints.js.map +1 -0
  31. package/dist/backend/codex-session-lookup.d.ts +35 -0
  32. package/dist/backend/codex-session-lookup.js +93 -0
  33. package/dist/backend/codex-session-lookup.js.map +1 -1
  34. package/dist/backend/codex.d.ts +27 -0
  35. package/dist/backend/codex.js +136 -23
  36. package/dist/backend/codex.js.map +1 -1
  37. package/dist/backend/kiro-identity.d.ts +2 -0
  38. package/dist/backend/kiro-identity.js +13 -6
  39. package/dist/backend/kiro-identity.js.map +1 -1
  40. package/dist/backend/kiro.d.ts +3 -0
  41. package/dist/backend/kiro.js +68 -5
  42. package/dist/backend/kiro.js.map +1 -1
  43. package/dist/backend/muse.js +5 -3
  44. package/dist/backend/muse.js.map +1 -1
  45. package/dist/backend/types.d.ts +13 -1
  46. package/dist/backend/types.js +3 -0
  47. package/dist/backend/types.js.map +1 -1
  48. package/dist/backend-availability.d.ts +6 -0
  49. package/dist/backend-availability.js +13 -5
  50. package/dist/backend-availability.js.map +1 -1
  51. package/dist/cache-ledger.d.ts +85 -0
  52. package/dist/cache-ledger.js +236 -1
  53. package/dist/cache-ledger.js.map +1 -1
  54. package/dist/cache-service.d.ts +5 -0
  55. package/dist/cache-service.js +16 -4
  56. package/dist/cache-service.js.map +1 -1
  57. package/dist/channel/adapters/discord.js +27 -4
  58. package/dist/channel/adapters/discord.js.map +1 -1
  59. package/dist/channel/adapters/telegram.d.ts +51 -0
  60. package/dist/channel/adapters/telegram.js +238 -18
  61. package/dist/channel/adapters/telegram.js.map +1 -1
  62. package/dist/channel/inbox-temp-name.d.ts +9 -0
  63. package/dist/channel/inbox-temp-name.js +13 -0
  64. package/dist/channel/inbox-temp-name.js.map +1 -0
  65. package/dist/channel/mcp-tools.js +3 -3
  66. package/dist/channel/mcp-tools.js.map +1 -1
  67. package/dist/channel/types.d.ts +2 -0
  68. package/dist/chat-log-ids.d.ts +48 -0
  69. package/dist/chat-log-ids.js +182 -0
  70. package/dist/chat-log-ids.js.map +1 -0
  71. package/dist/classic-channel-manager.d.ts +8 -1
  72. package/dist/classic-channel-manager.js +24 -2
  73. package/dist/classic-channel-manager.js.map +1 -1
  74. package/dist/cli.js +4 -6
  75. package/dist/cli.js.map +1 -1
  76. package/dist/config-validator.js +18 -1
  77. package/dist/config-validator.js.map +1 -1
  78. package/dist/cross-instance-notice.d.ts +10 -0
  79. package/dist/cross-instance-notice.js +67 -5
  80. package/dist/cross-instance-notice.js.map +1 -1
  81. package/dist/daemon.d.ts +52 -0
  82. package/dist/daemon.js +538 -35
  83. package/dist/daemon.js.map +1 -1
  84. package/dist/delivery-consumed-watch.d.ts +23 -1
  85. package/dist/delivery-consumed-watch.js +30 -3
  86. package/dist/delivery-consumed-watch.js.map +1 -1
  87. package/dist/delivery-reconciliation.d.ts +7 -0
  88. package/dist/delivery-reconciliation.js +9 -1
  89. package/dist/delivery-reconciliation.js.map +1 -1
  90. package/dist/dialog-screen-text.d.ts +16 -0
  91. package/dist/dialog-screen-text.js +51 -0
  92. package/dist/dialog-screen-text.js.map +1 -0
  93. package/dist/env-file.d.ts +2 -0
  94. package/dist/env-file.js +13 -8
  95. package/dist/env-file.js.map +1 -1
  96. package/dist/export-import.d.ts +7 -0
  97. package/dist/export-import.js +44 -10
  98. package/dist/export-import.js.map +1 -1
  99. package/dist/fleet-context.d.ts +2 -0
  100. package/dist/fleet-manager.d.ts +172 -12
  101. package/dist/fleet-manager.js +897 -139
  102. package/dist/fleet-manager.js.map +1 -1
  103. package/dist/fleet-yaml-patch.d.ts +3 -0
  104. package/dist/fleet-yaml-patch.js +103 -0
  105. package/dist/fleet-yaml-patch.js.map +1 -0
  106. package/dist/general-knowledge/skills/concepts/SKILL.md +393 -0
  107. package/dist/general-knowledge/skills/cross-instance-messaging/SKILL.md +2 -1
  108. package/dist/general-knowledge/skills/instance-lifecycle/SKILL.md +3 -1
  109. package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +41 -4
  110. package/dist/general-knowledge/skills/model-discovery/SKILL.md +2 -0
  111. package/dist/general-knowledge/skills/web-appearance/SKILL.md +1 -1
  112. package/dist/general-knowledge/skills/web-dashboard/SKILL.md +14 -4
  113. package/dist/general-knowledge/skills/worker-collaboration/SKILL.md +1 -1
  114. package/dist/install-stream.d.ts +53 -0
  115. package/dist/install-stream.js +134 -0
  116. package/dist/install-stream.js.map +1 -0
  117. package/dist/instance-config-impact.js +1 -0
  118. package/dist/instance-config-impact.js.map +1 -1
  119. package/dist/instance-lifecycle.d.ts +22 -2
  120. package/dist/instance-lifecycle.js +103 -19
  121. package/dist/instance-lifecycle.js.map +1 -1
  122. package/dist/instance-topic-delete.d.ts +77 -0
  123. package/dist/instance-topic-delete.js +123 -0
  124. package/dist/instance-topic-delete.js.map +1 -0
  125. package/dist/instructions.d.ts +2 -2
  126. package/dist/instructions.js +2 -2
  127. package/dist/instructions.js.map +1 -1
  128. package/dist/interaction-observation.d.ts +1 -0
  129. package/dist/interaction-observation.js +1 -1
  130. package/dist/interaction-observation.js.map +1 -1
  131. package/dist/kiro-engine-status.d.ts +11 -0
  132. package/dist/kiro-engine-status.js +6 -0
  133. package/dist/kiro-engine-status.js.map +1 -1
  134. package/dist/kiro-resume-summary.d.ts +37 -0
  135. package/dist/kiro-resume-summary.js +85 -0
  136. package/dist/kiro-resume-summary.js.map +1 -0
  137. package/dist/locale.js +130 -6
  138. package/dist/locale.js.map +1 -1
  139. package/dist/log-service.d.ts +30 -0
  140. package/dist/log-service.js +36 -3
  141. package/dist/log-service.js.map +1 -1
  142. package/dist/log-steps.d.ts +4 -3
  143. package/dist/log-steps.js +32 -7
  144. package/dist/log-steps.js.map +1 -1
  145. package/dist/logger.d.ts +12 -0
  146. package/dist/logger.js +43 -1
  147. package/dist/logger.js.map +1 -1
  148. package/dist/login-controller.js +9 -1
  149. package/dist/login-controller.js.map +1 -1
  150. package/dist/login-flows.d.ts +2 -0
  151. package/dist/login-flows.js +5 -2
  152. package/dist/login-flows.js.map +1 -1
  153. package/dist/login-manager.d.ts +6 -0
  154. package/dist/login-manager.js +10 -1
  155. package/dist/login-manager.js.map +1 -1
  156. package/dist/missed-offline.d.ts +25 -0
  157. package/dist/missed-offline.js +79 -0
  158. package/dist/missed-offline.js.map +1 -0
  159. package/dist/needs-you-hub.d.ts +6 -1
  160. package/dist/needs-you-hub.js +31 -8
  161. package/dist/needs-you-hub.js.map +1 -1
  162. package/dist/needs-you.d.ts +22 -2
  163. package/dist/needs-you.js +17 -3
  164. package/dist/needs-you.js.map +1 -1
  165. package/dist/next-steps.d.ts +12 -2
  166. package/dist/next-steps.js +19 -3
  167. package/dist/next-steps.js.map +1 -1
  168. package/dist/office-share-page.d.ts +1 -1
  169. package/dist/office-share-page.js +6 -2
  170. package/dist/office-share-page.js.map +1 -1
  171. package/dist/office-share-service.d.ts +57 -0
  172. package/dist/office-share-service.js +76 -11
  173. package/dist/office-share-service.js.map +1 -1
  174. package/dist/office-share.d.ts +8 -0
  175. package/dist/office-share.js +34 -0
  176. package/dist/office-share.js.map +1 -1
  177. package/dist/outbound-handlers.js +59 -3
  178. package/dist/outbound-handlers.js.map +1 -1
  179. package/dist/outbound-schemas.d.ts +1 -0
  180. package/dist/outbound-schemas.js +10 -4
  181. package/dist/outbound-schemas.js.map +1 -1
  182. package/dist/package-preimage.js +27 -2
  183. package/dist/package-preimage.js.map +1 -1
  184. package/dist/page-share.d.ts +122 -0
  185. package/dist/page-share.js +199 -0
  186. package/dist/page-share.js.map +1 -0
  187. package/dist/public-preview.d.ts +4 -0
  188. package/dist/public-preview.js +5 -4
  189. package/dist/public-preview.js.map +1 -1
  190. package/dist/public-web-gateway.js +5 -0
  191. package/dist/public-web-gateway.js.map +1 -1
  192. package/dist/queued-handoffs.d.ts +33 -0
  193. package/dist/queued-handoffs.js +75 -0
  194. package/dist/queued-handoffs.js.map +1 -0
  195. package/dist/quickstart-api.d.ts +5 -0
  196. package/dist/quickstart-api.js +1 -1
  197. package/dist/quickstart-api.js.map +1 -1
  198. package/dist/quickstart.d.ts +40 -0
  199. package/dist/quickstart.js +174 -28
  200. package/dist/quickstart.js.map +1 -1
  201. package/dist/remote-card-answer.js +2 -0
  202. package/dist/remote-card-answer.js.map +1 -1
  203. package/dist/remote-card-prompt.js +4 -2
  204. package/dist/remote-card-prompt.js.map +1 -1
  205. package/dist/reply-buttons-controller.js +10 -0
  206. package/dist/reply-buttons-controller.js.map +1 -1
  207. package/dist/scheduler/db.d.ts +2 -1
  208. package/dist/scheduler/db.js +23 -9
  209. package/dist/scheduler/db.js.map +1 -1
  210. package/dist/selection-card.d.ts +20 -0
  211. package/dist/selection-card.js +202 -0
  212. package/dist/selection-card.js.map +1 -0
  213. package/dist/settings-api.d.ts +13 -0
  214. package/dist/settings-api.js +16 -1
  215. package/dist/settings-api.js.map +1 -1
  216. package/dist/settings-change.js +5 -0
  217. package/dist/settings-change.js.map +1 -1
  218. package/dist/settings-effect.js +2 -0
  219. package/dist/settings-effect.js.map +1 -1
  220. package/dist/setup-host.js +2 -2
  221. package/dist/setup-host.js.map +1 -1
  222. package/dist/setup-wizard.js +2 -2
  223. package/dist/setup-wizard.js.map +1 -1
  224. package/dist/stall-reporter.d.ts +21 -0
  225. package/dist/stall-reporter.js +82 -0
  226. package/dist/stall-reporter.js.map +1 -0
  227. package/dist/tmux-control.d.ts +2 -2
  228. package/dist/tmux-control.js +4 -2
  229. package/dist/tmux-control.js.map +1 -1
  230. package/dist/tmux-exec-helper.d.ts +6 -3
  231. package/dist/tmux-exec-helper.js +115 -53
  232. package/dist/tmux-exec-helper.js.map +1 -1
  233. package/dist/tmux-manager.d.ts +6 -0
  234. package/dist/tmux-manager.js +17 -0
  235. package/dist/tmux-manager.js.map +1 -1
  236. package/dist/tool-permissions.d.ts +5 -2
  237. package/dist/tool-permissions.js +6 -5
  238. package/dist/tool-permissions.js.map +1 -1
  239. package/dist/tool-progress.d.ts +2 -0
  240. package/dist/tool-progress.js +19 -3
  241. package/dist/tool-progress.js.map +1 -1
  242. package/dist/topic-commands.js +4 -0
  243. package/dist/topic-commands.js.map +1 -1
  244. package/dist/tunnel/cloudflared-install.d.ts +4 -0
  245. package/dist/tunnel/cloudflared-install.js +20 -1
  246. package/dist/tunnel/cloudflared-install.js.map +1 -1
  247. package/dist/tunnel/cloudflared.d.ts +8 -1
  248. package/dist/tunnel/cloudflared.js +34 -6
  249. package/dist/tunnel/cloudflared.js.map +1 -1
  250. package/dist/tunnel/lease.d.ts +3 -3
  251. package/dist/tunnel/lease.js +23 -19
  252. package/dist/tunnel/lease.js.map +1 -1
  253. package/dist/tunnel/manager.js +6 -7
  254. package/dist/tunnel/manager.js.map +1 -1
  255. package/dist/types.d.ts +10 -0
  256. package/dist/ui/chat-store.js +28 -4
  257. package/dist/ui/chat-strings.js +40 -4
  258. package/dist/ui/chat-thread.js +52 -10
  259. package/dist/ui/fleet-canvas.js +4 -3
  260. package/dist/ui/fleet-office-model.js +55 -22
  261. package/dist/ui/fleet-office-overview.js +6 -38
  262. package/dist/ui/fleet-office-pack.js +4 -3
  263. package/dist/ui/fleet-office-skin.js +114 -21
  264. package/dist/ui/fleet-office-store.js +5 -0
  265. package/dist/ui/fleet-office-zones.js +2 -2
  266. package/dist/ui/fleet-office.js +41 -9
  267. package/dist/ui/fleet-usage.js +90 -27
  268. package/dist/ui/page-share-dialog.js +191 -0
  269. package/dist/ui/panel-chat.js +16 -0
  270. package/dist/ui/panel-log.js +7 -1
  271. package/dist/ui/panel-needs.js +17 -8
  272. package/dist/ui/panel-notices.js +98 -0
  273. package/dist/ui/panel-settings.js +9 -2
  274. package/dist/ui/preview-pages.js +41 -0
  275. package/dist/ui/preview-panel-actions.js +161 -0
  276. package/dist/ui/preview-panel.js +209 -28
  277. package/dist/ui/preview-versions.js +155 -0
  278. package/dist/ui/preview.js +88 -3
  279. package/dist/ui/settings-registry.js +1 -0
  280. package/dist/ui/settings-strings.js +6 -2
  281. package/dist/ui/shared/app-i18n.js +6 -6
  282. package/dist/ui/shared/app-needs.js +1 -1
  283. package/dist/ui/shared/app-route.js +4 -0
  284. package/dist/ui/shared/app-shell.js +9 -5
  285. package/dist/ui/shared/app-store.js +71 -1
  286. package/dist/ui/shared/app-stream.js +27 -1
  287. package/dist/ui/shared/app.css +94 -4
  288. package/dist/ui/shared/app.js +22 -1
  289. package/dist/ui/shared/office/guild.png +0 -0
  290. package/dist/ui/shared/office/school.png +0 -0
  291. package/dist/ui/shared/panel-view.js +339 -17
  292. package/dist/ui/shared/signin.js +12 -1
  293. package/dist/ui/shared/ui-icons.js +3 -0
  294. package/dist/ui/shared/usage-dialog.js +18 -1
  295. package/dist/ui/shared/view-history.css +17 -0
  296. package/dist/ui/shared/view-history.js +438 -0
  297. package/dist/ui/shared/view-strings.js +42 -0
  298. package/dist/ui/shared/web-authorize.js +3 -1
  299. package/dist/unattended-install.d.ts +6 -0
  300. package/dist/unattended-install.js +79 -0
  301. package/dist/unattended-install.js.map +1 -0
  302. package/dist/usage/capacity.d.ts +69 -0
  303. package/dist/usage/capacity.js +119 -0
  304. package/dist/usage/capacity.js.map +1 -0
  305. package/dist/usage/i18n-keys.d.ts +1 -1
  306. package/dist/usage/i18n-keys.js +3 -0
  307. package/dist/usage/i18n-keys.js.map +1 -1
  308. package/dist/usage/profile-timeline.d.ts +16 -0
  309. package/dist/usage/profile-timeline.js +58 -0
  310. package/dist/usage/profile-timeline.js.map +1 -0
  311. package/dist/usage/quota-history.d.ts +43 -0
  312. package/dist/usage/quota-history.js +168 -0
  313. package/dist/usage/quota-history.js.map +1 -0
  314. package/dist/usage/usage-api.d.ts +31 -0
  315. package/dist/usage/usage-api.js +80 -1
  316. package/dist/usage/usage-api.js.map +1 -1
  317. package/dist/usage-report.d.ts +25 -1
  318. package/dist/usage-report.js +55 -4
  319. package/dist/usage-report.js.map +1 -1
  320. package/dist/usage-rollup.d.ts +4 -1
  321. package/dist/usage-rollup.js +12 -3
  322. package/dist/usage-rollup.js.map +1 -1
  323. package/dist/view-api.d.ts +31 -0
  324. package/dist/view-api.js +186 -41
  325. package/dist/view-api.js.map +1 -1
  326. package/dist/view-history.d.ts +84 -0
  327. package/dist/view-history.js +116 -0
  328. package/dist/view-history.js.map +1 -0
  329. package/dist/web-api.d.ts +32 -0
  330. package/dist/web-api.js +195 -5
  331. package/dist/web-api.js.map +1 -1
  332. package/dist/web-auth.js +7 -1
  333. package/dist/web-auth.js.map +1 -1
  334. package/dist/web-chat-history.d.ts +20 -0
  335. package/dist/web-chat-history.js +21 -0
  336. package/dist/web-chat-history.js.map +1 -1
  337. package/dist/web-guest-access.js +5 -1
  338. package/dist/web-guest-access.js.map +1 -1
  339. package/dist/web-login.d.ts +6 -3
  340. package/dist/web-login.js +26 -15
  341. package/dist/web-login.js.map +1 -1
  342. package/dist/web-notices.d.ts +51 -0
  343. package/dist/web-notices.js +118 -0
  344. package/dist/web-notices.js.map +1 -0
  345. package/dist/web-office-pack-tools.d.ts +1 -1
  346. package/dist/web-office-pack-tools.js +1 -1
  347. package/dist/web-office-pack-tools.js.map +1 -1
  348. package/dist/web-only-fleet.d.ts +22 -0
  349. package/dist/web-only-fleet.js +13 -0
  350. package/dist/web-only-fleet.js.map +1 -0
  351. package/dist/web-preview.d.ts +32 -3
  352. package/dist/web-preview.js +52 -19
  353. package/dist/web-preview.js.map +1 -1
  354. package/dist/web-session-authorization.d.ts +13 -2
  355. package/dist/web-session-authorization.js +10 -3
  356. package/dist/web-session-authorization.js.map +1 -1
  357. package/dist/web-session.d.ts +12 -0
  358. package/dist/web-session.js +22 -8
  359. package/dist/web-session.js.map +1 -1
  360. package/dist/web-settings-patch.d.ts +3 -1
  361. package/dist/web-settings-patch.js +4 -1
  362. package/dist/web-settings-patch.js.map +1 -1
  363. package/dist/web-shell-routes.d.ts +2 -0
  364. package/dist/web-shell-routes.js +3 -0
  365. package/dist/web-shell-routes.js.map +1 -1
  366. package/launcher/agend-install.cjs +16 -2
  367. package/launcher/private-home.cjs +1 -1
  368. package/package.json +1 -1
@@ -0,0 +1,438 @@
1
+ // Claude history in the View (the user, hmsg-a8947a2c / hmsg-b4c7ccdf; leader cid-claude-history-build). Claude Code
2
+ // draws on the alternate screen, so tmux keeps nothing above it: scrolling up in its View shows the conversation's
3
+ // earlier part from its transcript (GET /api/pane/<name>?transcript=1, view-history.ts), drawn like Claude's own
4
+ // Ctrl+O view — "❯" prompts on their band, "●" replies and tool calls, "⎿" results, diffs, long output folded — directly
5
+ // above the live screen, under a faint divider.
6
+ //
7
+ // The look is sampled from the live screen the View already shows (samplePalette): the prompt band, the bullet and
8
+ // result glyphs, the tool and diff colours, as this Claude version draws them; Claude 2.1.318's own values when the
9
+ // screen has none of them. A glyph the page's fonts lack falls back (⏺ → ●, ⎿ → └, ❯ → >).
10
+ //
11
+ // Loaded on first use by panel-view.js (a lazy module of its own). A tab opened before an upgrade runs it with the old
12
+ // shell, so it imports nothing but app-i18n.js's register/t (in every shell since #1408), registers its own words and
13
+ // links its own stylesheet (/assets/view-history.css): nothing it needs lives in a long-lived module. A server without
14
+ // the history (an older one answers the pane's text) makes `first()` say `unsupported`: the View stays as it was.
15
+ import { register, t } from "./app-i18n.js";
16
+
17
+ register("viewHistory", {
18
+ en: {
19
+ divider: "History above",
20
+ more: "… +{0} lines (click to expand)",
21
+ moreFailed: "Could not load the full text.",
22
+ cut: "… {0} more characters not shown",
23
+ capped: "Only the latest {0} steps are shown here.",
24
+ },
25
+ "zh-TW": {
26
+ divider: "以上是對話紀錄",
27
+ more: "… 還有 {0} 行(點一下展開)",
28
+ moreFailed: "無法載入完整內容。",
29
+ cut: "… 還有 {0} 個字元未顯示",
30
+ capped: "這裡只顯示最近 {0} 則紀錄。",
31
+ },
32
+ });
33
+ const w = (key, ...values) => t(`viewHistory.${key}`, ...values);
34
+
35
+ /** About this many steps are kept on the page; older pages are not read past it. */
36
+ export const HISTORY_STEP_CAP = 1000;
37
+
38
+ // ── The palette, sampled from the live screen ──
39
+ const BASE = ["#000000", "#cd3131", "#0dbc79", "#e5e510", "#2472c8", "#bc3fbc", "#11a8cd", "#e5e5e5"];
40
+ const BRIGHT = ["#666666", "#f14c4c", "#23d18b", "#f5f543", "#3b8eea", "#d670d6", "#29b8db", "#ffffff"];
41
+ function xterm256(n) {
42
+ if (n < 8) return BASE[n];
43
+ if (n < 16) return BRIGHT[n - 8];
44
+ if (n < 232) { n -= 16; const r = Math.floor(n / 36), g = Math.floor((n % 36) / 6), b = n % 6; const v = (x) => (x ? x * 40 + 55 : 0); return `rgb(${v(r)},${v(g)},${v(b)})`; }
45
+ const l = (n - 232) * 10 + 8; return `rgb(${l},${l},${l})`;
46
+ }
47
+ /** Claude Code 2.1.318's own look (captured from a real pane, dark theme): what is used when the screen shows none. */
48
+ export const DEFAULT_PALETTE = Object.freeze({
49
+ promptGlyph: "❯", promptBand: xterm256(237), promptFg: xterm256(246), promptText: xterm256(231),
50
+ bullet: "●", replyFg: xterm256(231), toolFg: xterm256(114), failedFg: xterm256(203), runningFg: xterm256(246),
51
+ resultGlyph: "⎿", resultFg: xterm256(246),
52
+ delFg: xterm256(167), delBg: xterm256(52), addFg: xterm256(77), addBg: xterm256(22), errorFg: xterm256(203),
53
+ });
54
+
55
+ /** The screen's text as lines of runs: { text, fg, bg, bold } (colours as CSS, null when unset). */
56
+ export function ansiRuns(text) {
57
+ const lines = [[]];
58
+ let fg = null, bg = null, bold = false, reverse = false;
59
+ const push = (chunk) => {
60
+ const parts = chunk.split("\n");
61
+ parts.forEach((part, i) => {
62
+ if (i > 0) lines.push([]);
63
+ if (!part) return;
64
+ let f = typeof fg === "number" ? (bold && fg < 8 ? BRIGHT[fg] : BASE[fg]) : fg;
65
+ let b = typeof bg === "number" ? BASE[bg] : bg;
66
+ if (reverse) [f, b] = [b, f];
67
+ lines[lines.length - 1].push({ text: part, fg: f, bg: b, bold });
68
+ });
69
+ };
70
+ const re = /\x1b\[([0-9;]*)m/g;
71
+ let last = 0, m;
72
+ while ((m = re.exec(text)) !== null) {
73
+ push(text.slice(last, m.index));
74
+ last = re.lastIndex;
75
+ const codes = m[1].split(";").filter((x) => x !== "").map(Number);
76
+ if (codes.length === 0) codes.push(0);
77
+ for (let i = 0; i < codes.length; i++) {
78
+ const c = codes[i];
79
+ if (c === 0) { fg = bg = null; bold = reverse = false; }
80
+ else if (c === 1) bold = true;
81
+ else if (c === 22) bold = false;
82
+ else if (c === 7) reverse = true;
83
+ else if (c === 27) reverse = false;
84
+ else if (c === 39) fg = null;
85
+ else if (c === 49) bg = null;
86
+ else if (c >= 30 && c <= 37) fg = c - 30;
87
+ else if (c >= 90 && c <= 97) fg = BRIGHT[c - 90];
88
+ else if (c >= 40 && c <= 47) bg = c - 40;
89
+ else if (c >= 100 && c <= 107) bg = BRIGHT[c - 100];
90
+ else if (c === 38 || c === 48) {
91
+ let col = null;
92
+ if (codes[i + 1] === 5) { col = xterm256(codes[i + 2]); i += 2; }
93
+ else if (codes[i + 1] === 2) { col = `rgb(${codes[i + 2] || 0},${codes[i + 3] || 0},${codes[i + 4] || 0})`; i += 4; }
94
+ if (c === 38) fg = col; else bg = col;
95
+ }
96
+ }
97
+ }
98
+ push(text.slice(last));
99
+ return lines;
100
+ }
101
+
102
+ const PROMPT_GLYPHS = "❯>›";
103
+ const BULLETS = "●⏺";
104
+ const RESULT_GLYPHS = "⎿└";
105
+ /**
106
+ * How this Claude draws its conversation, read off the live screen (ANSI, as the View fetched it): each value found
107
+ * replaces the default; the most frequent wins where a screen shows several (a failed tool's red bullet among green).
108
+ */
109
+ export function samplePalette(screen) { return sampleScreen(screen).palette; }
110
+ /** samplePalette, and which of its values the screen gave (the rest are the defaults) — for the version audit. */
111
+ export function sampleScreen(screen) {
112
+ const p = { ...DEFAULT_PALETTE };
113
+ const found = new Set();
114
+ const set = (k, v) => { if (v) { p[k] = v; found.add(k); } };
115
+ if (typeof screen !== "string" || !screen) return { palette: p, found: [] };
116
+ const tally = { toolFg: new Map(), replyFg: new Map() };
117
+ const count = (map, v) => { if (v) map.set(v, (map.get(v) || 0) + 1); };
118
+ let banded = false, glyphSeen = false;
119
+ for (const runs of ansiRuns(screen)) {
120
+ const textOf = runs.map((r) => r.text).join("");
121
+ const lead = textOf.length - textOf.trimStart().length;
122
+ // Find the run holding column `col` (and the offset in it).
123
+ const at = (col) => { let n = 0; for (const r of runs) { if (col < n + r.text.length) return r; n += r.text.length; } return null; };
124
+ const first = textOf.charAt(lead);
125
+ // A prompt: its glyph then a space, on a band (a past prompt) — or bare (the composer: the glyph only).
126
+ if (PROMPT_GLYPHS.includes(first) && textOf.charAt(lead + 1) === " " && !banded) {
127
+ const g = at(lead);
128
+ if (g && g.bg) {
129
+ banded = true; glyphSeen = true;
130
+ set("promptGlyph", first); set("promptBand", g.bg); set("promptFg", g.fg);
131
+ const rest = runs.slice(runs.indexOf(g) + 1).find((r) => r.text.trim());
132
+ if (rest) set("promptText", rest.fg);
133
+ } else if (!glyphSeen) { glyphSeen = true; set("promptGlyph", first); }
134
+ continue;
135
+ }
136
+ // A bullet at the line's start: a tool call ("● Name(" with the name bold) or a reply.
137
+ if (lead === 0 && BULLETS.includes(first) && textOf.charAt(1) === " ") {
138
+ set("bullet", first);
139
+ const g = at(0), next = at(2);
140
+ const tool = !!(next && next.bold) && /^. [^(]+\(/.test(textOf);
141
+ count(tool ? tally.toolFg : tally.replyFg, g && g.fg);
142
+ continue;
143
+ }
144
+ // A result: two spaces, its glyph.
145
+ if (lead === 2 && RESULT_GLYPHS.includes(first)) {
146
+ set("resultGlyph", first);
147
+ const g = at(2); if (g) set("resultFg", g.fg);
148
+ continue;
149
+ }
150
+ // A diff row: a line number then - or +, on a coloured background.
151
+ const row = /^\s+\d+\s?([-+])/.exec(textOf);
152
+ if (row) {
153
+ const g = at(textOf.indexOf(row[1], lead));
154
+ if (g && g.bg) { if (row[1] === "-") { set("delBg", g.bg); set("delFg", g.fg); } else { set("addBg", g.bg); set("addFg", g.fg); } }
155
+ }
156
+ }
157
+ const top = (map) => { let best = null, n = 0; for (const [k, v] of map) if (v > n) { best = k; n = v; } return best; };
158
+ set("toolFg", top(tally.toolFg));
159
+ set("replyFg", top(tally.replyFg));
160
+ return { palette: p, found: [...found] };
161
+ }
162
+
163
+ // ── Glyphs the page's fonts lack ──
164
+ const FALLBACK = { "⏺": "●", "●": "*", "⎿": "└", "└": "|", "❯": ">", "›": ">" };
165
+ const glyphCache = new Map();
166
+ /**
167
+ * Whether `ch` draws as itself in `font` (a CSS font-family list): drawn next to a code point no font has, the two must
168
+ * differ. No canvas (a test page): assumed drawn.
169
+ */
170
+ export function hasGlyph(ch, font, doc = typeof document !== "undefined" ? document : null) {
171
+ const key = `${ch}\u0000${font}`;
172
+ if (glyphCache.has(key)) return glyphCache.get(key);
173
+ let ok = true;
174
+ try {
175
+ const canvas = doc && doc.createElement ? doc.createElement("canvas") : null;
176
+ const ctx = canvas && typeof canvas.getContext === "function" ? canvas.getContext("2d") : null;
177
+ if (ctx && typeof ctx.getImageData === "function") {
178
+ canvas.width = 40; canvas.height = 40;
179
+ const draw = (c) => { ctx.clearRect(0, 0, 40, 40); ctx.font = `32px ${font}`; ctx.fillStyle = "#000"; ctx.textBaseline = "top"; ctx.fillText(c, 2, 2); return ctx.getImageData(0, 0, 40, 40).data; };
180
+ const a = draw(ch), none = draw("\u{10FFFD}");
181
+ ok = false;
182
+ for (let i = 3; i < a.length; i += 4) if (a[i] !== none[i]) { ok = true; break; }
183
+ }
184
+ } catch { ok = true; }
185
+ glyphCache.set(key, ok);
186
+ return ok;
187
+ }
188
+ /** The glyph as drawn on this page: itself, or its fallback when the fonts lack it. */
189
+ export function drawable(ch, font, doc) {
190
+ let g = ch;
191
+ for (let i = 0; i < 3 && g && !hasGlyph(g, font, doc); i++) g = FALLBACK[g] || g;
192
+ return g;
193
+ }
194
+
195
+ // ── Which steps the live screen already shows ──
196
+ // Text a needle is compared by: no whitespace (a wrap moves it) and no markdown marks (Claude draws them away).
197
+ const squash = (s) => String(s).replace(/\x1b\[[0-9;]*m/g, "").replace(/[\s*_`#>│]/g, "");
198
+ /**
199
+ * What of a prompt or a reply is looked for on the screen: its WHOLE text, normalised (#3069 review r3, Prism P2-2 — a
200
+ * fixed prefix, however long, is no proof: it can occur inside another message). None for a tool: Claude may draw it
201
+ * summarised.
202
+ */
203
+ function needle(step) {
204
+ if (step.kind !== "user" && step.kind !== "assistant") return null;
205
+ const s = squash(step.body || "");
206
+ return s.length >= 4 ? s : null;
207
+ }
208
+ /**
209
+ * The steps before the live screen, matched conservatively (#3069 review, Prism P2-2): walking back from the newest,
210
+ * each prompt and reply must be found WHOLE on the screen, BEFORE the place the newer one was found — one occurrence
211
+ * never proves two messages, and a shared opening is never taken for the same message. The first one not found that
212
+ * way (a message only partly shown included), and everything older, is history. Tool calls go with their neighbours. Unsure means shown again: a
213
+ * step the screen shows may come twice — never one it does not show not at all.
214
+ */
215
+ export function beforeScreen(steps, screen) {
216
+ const text = squash(screen || "");
217
+ let cut = steps.length, limit = text.length;
218
+ for (let i = steps.length - 1; i >= 0; i--) {
219
+ const n = needle(steps[i]);
220
+ if (n === null) continue;
221
+ const at = limit - n.length >= 0 ? text.lastIndexOf(n, limit - n.length) : -1;
222
+ if (at < 0) break;
223
+ limit = at;
224
+ cut = i;
225
+ }
226
+ return steps.slice(0, cut);
227
+ }
228
+
229
+ // ── Drawing ──
230
+ const TOOL_WORDS = { Edit: "Update", MultiEdit: "Update", Grep: "Search", Glob: "Search", WebFetch: "Fetch", WebSearch: "Web Search", TodoWrite: "Update Todos", Agent: "Task", NotebookEdit: "Edit Notebook" };
231
+ /** Claude's word for a tool, as its screen shows it ("Update" for Edit; "server - tool (MCP)" for an MCP tool). */
232
+ export function toolWord(tool) {
233
+ const mcp = /^mcp__(.+?)__(.+)$/.exec(tool || "");
234
+ if (mcp) return `${mcp[1]} - ${mcp[2]} (MCP)`;
235
+ return TOOL_WORDS[tool] || tool || "?";
236
+ }
237
+
238
+ function el(doc, tag, cls, text) {
239
+ const e = doc.createElement(tag);
240
+ if (cls) e.className = cls;
241
+ if (text != null) e.textContent = text;
242
+ return e;
243
+ }
244
+ function span(doc, text, color, extra) {
245
+ const s = el(doc, "span", extra || null, text);
246
+ if (color) s.style.color = color; // CSSOM, never a style attribute (#1300)
247
+ return s;
248
+ }
249
+ /** A Read result's "12\tcode" lines as the terminal shows them (" 12 code"). */
250
+ const unTab = (text, tool) => (tool === "Read" ? text.replace(/^(\d+)\t/gm, (_, n) => `${n.padStart(4)} `) : text.replace(/\t/g, " "));
251
+
252
+ /** One step's block. `onMore`: the folded step, clicked. */
253
+ export function drawStep(doc, step, pal, glyphs, onMore) {
254
+ const box = el(doc, "div", "vh-step");
255
+ box.dataset.step = step.id;
256
+ const line = (cls) => box.appendChild(el(doc, "div", `vh-line${cls ? ` ${cls}` : ""}`));
257
+ if (step.kind === "user") {
258
+ (step.body || "").split("\n").forEach((text, i) => {
259
+ const l = line("vh-prompt");
260
+ l.style.background = pal.promptBand;
261
+ l.appendChild(span(doc, i === 0 ? `${glyphs.prompt} ` : " ", pal.promptFg));
262
+ l.appendChild(span(doc, text || " ", pal.promptText));
263
+ });
264
+ if (step.cut) line("vh-dim").textContent = ` ${w("cut", step.cut)}`;
265
+ return box;
266
+ }
267
+ if (step.kind === "assistant") {
268
+ (step.body || "").split("\n").forEach((text, i) => {
269
+ const l = line();
270
+ l.appendChild(i === 0 ? span(doc, `${glyphs.bullet} `, pal.replyFg) : span(doc, " "));
271
+ l.appendChild(span(doc, text || " "));
272
+ });
273
+ if (step.cut) line("vh-dim").textContent = ` ${w("cut", step.cut)}`;
274
+ return box;
275
+ }
276
+ if (step.kind === "tool") {
277
+ const head = line();
278
+ const fg = step.status === "failed" ? pal.failedFg : step.status === "running" || step.status === "interrupted" ? pal.runningFg : pal.toolFg;
279
+ head.appendChild(span(doc, `${glyphs.bullet} `, fg));
280
+ head.appendChild(span(doc, toolWord(step.tool), null, "vh-b"));
281
+ head.appendChild(span(doc, step.subject ? `(${step.subject})` : ""));
282
+ }
283
+ // The result: " ⎿ " then its first line; the rest under it — or the edit's diff rows.
284
+ const resultColor = step.kind === "error" || step.status === "failed" ? pal.errorFg : null;
285
+ let firstRow = true;
286
+ const mark = () => { const s = firstRow ? span(doc, ` ${glyphs.result} `, pal.resultFg) : span(doc, " "); firstRow = false; return s; };
287
+ if (step.diff) {
288
+ for (const h of step.diff) {
289
+ for (const [rows, sign, bg, fg] of [[h.del, "-", pal.delBg, pal.delFg], [h.add, "+", pal.addBg, pal.addFg]]) {
290
+ for (const text of rows) {
291
+ const l = line("vh-res");
292
+ l.appendChild(mark());
293
+ const row = span(doc, `${sign} ${text}`, fg, "vh-diff");
294
+ row.style.background = bg;
295
+ l.appendChild(row);
296
+ }
297
+ }
298
+ }
299
+ } else if (step.body != null && step.body !== "") {
300
+ for (const text of unTab(step.body, step.tool).split("\n")) {
301
+ const l = line("vh-res");
302
+ l.appendChild(mark());
303
+ l.appendChild(span(doc, text || " ", resultColor));
304
+ }
305
+ }
306
+ if (step.more) {
307
+ const l = line("vh-res");
308
+ l.appendChild(span(doc, " "));
309
+ const b = el(doc, "button", "vh-more", w("more", step.more));
310
+ b.type = "button";
311
+ b.style.color = pal.resultFg;
312
+ b.addEventListener("click", () => onMore && onMore(step, box, b));
313
+ l.appendChild(b);
314
+ } else if (step.cut) line("vh-res vh-dim").textContent = ` ${w("cut", step.cut)}`;
315
+ return box;
316
+ }
317
+
318
+ /**
319
+ * The history above one agent's live screen. `fetch(url)` reads under the View's lease; `alive()` is false once the
320
+ * View moved on (nothing is drawn then). `root` is the block panel-view.js puts above the screen it shows.
321
+ */
322
+ export function createHistory({ name, fetch, alive = () => true, doc = document, font = "monospace", cols = 0 }) {
323
+ ensureStylesheet(doc);
324
+ const root = el(doc, "div", "vh");
325
+ root.setAttribute("data-trust", "panel-content");
326
+ if (cols > 0) root.style.width = `${cols}ch`;
327
+ const pages = el(doc, "div", "vh-pages");
328
+ const capNote = el(doc, "div", "vh-cap");
329
+ const divider = el(doc, "div", "vh-divider", w("divider"));
330
+ divider.setAttribute("role", "separator");
331
+ root.append(pages, divider);
332
+ let pal = DEFAULT_PALETTE, glyphs = null;
333
+ let key = null, before = null, shown = 0, busy = false;
334
+ const base = `/api/pane/${encodeURIComponent(name)}?transcript=1`;
335
+ const read = async (query) => {
336
+ const r = await fetch(`${base}${query}`);
337
+ // An older server answers the pane's text here: not this history.
338
+ const type = r.headers && typeof r.headers.get === "function" ? String(r.headers.get("Content-Type") || "") : "";
339
+ if (!r.ok) return { failed: true, status: r.status };
340
+ if (!type.includes("application/json")) return { unsupported: true };
341
+ const body = await r.json();
342
+ if (!body || body.view_history !== 1) return { unsupported: true };
343
+ return body;
344
+ };
345
+ const block = (steps, end) => {
346
+ const frag = doc.createDocumentFragment();
347
+ for (const s of steps) {
348
+ const b = drawStep(doc, s, pal, glyphs, more);
349
+ b.dataset.end = String(end);
350
+ frag.appendChild(b);
351
+ }
352
+ return frag;
353
+ };
354
+ async function more(step, box, button) {
355
+ if (button.disabled) return;
356
+ button.disabled = true;
357
+ try {
358
+ const body = await read(`&step=${encodeURIComponent(step.id)}&end=${encodeURIComponent(box.dataset.end)}&key=${encodeURIComponent(key || "")}`);
359
+ if (!alive()) return;
360
+ if (!body.supported || !body.step) throw new Error("gone");
361
+ const whole = drawStep(doc, body.step, pal, glyphs, more);
362
+ whole.dataset.end = box.dataset.end;
363
+ box.replaceWith(whole);
364
+ } catch {
365
+ if (!alive()) return;
366
+ button.disabled = false;
367
+ if (!box.querySelector(".vh-more-failed")) button.parentNode.insertBefore(span(doc, ` ${w("moreFailed")}`, pal.errorFg, "vh-more-failed"), button.nextSibling);
368
+ }
369
+ }
370
+ return {
371
+ root,
372
+ /** Older steps exist and the cap is not reached. */
373
+ hasOlder: () => before !== null && shown < HISTORY_STEP_CAP,
374
+ /**
375
+ * The newest page, minus what the live `screen` (ANSI) shows. "ok" | "empty" (nothing before the screen) |
376
+ * "unsupported" (no history here) | "failed" (with `status`).
377
+ */
378
+ async first(screen) {
379
+ pal = samplePalette(screen);
380
+ glyphs = { prompt: drawable(pal.promptGlyph, font, doc), bullet: drawable(pal.bullet, font, doc), result: drawable(pal.resultGlyph, font, doc) };
381
+ let body;
382
+ try { body = await read(""); } catch { return { kind: "failed", status: 0 }; }
383
+ if (body.failed) return { kind: "failed", status: body.status };
384
+ if (body.unsupported) return { kind: "unsupported" };
385
+ // No history here at all (not Claude, or no reader) — or none YET (a session that has written nothing so far):
386
+ // that one is only nothing earlier, asked again next time.
387
+ if (!body.supported) return { kind: body.reason === "unsupported" || body.reason === "unavailable" ? "unsupported" : "empty" };
388
+ key = body.key; before = body.before;
389
+ let steps = beforeScreen(body.steps || [], screen);
390
+ let end = body.end;
391
+ // Everything in the newest page is on the screen: the page before it is the history.
392
+ if (!steps.length && before !== null) {
393
+ let older;
394
+ try { older = await read(`&before=${before}&key=${encodeURIComponent(key)}`); } catch { older = { failed: true, status: 0 }; }
395
+ if (!older.failed && older.supported && !older.reset) { steps = older.steps || []; before = older.before; end = older.end; }
396
+ }
397
+ if (!alive()) return { kind: "failed", status: 0 };
398
+ if (!steps.length) return { kind: "empty" };
399
+ pages.replaceChildren(block(steps, end));
400
+ shown = steps.length;
401
+ return { kind: "ok" };
402
+ },
403
+ /** The page before what is shown, drawn above it. "ok" | "none" | "capped" | "failed". */
404
+ async older() {
405
+ if (busy) return { kind: "none" };
406
+ if (before === null) return { kind: "none" };
407
+ if (shown >= HISTORY_STEP_CAP) {
408
+ if (!capNote.isConnected) { capNote.textContent = w("capped", HISTORY_STEP_CAP); root.prepend(capNote); }
409
+ return { kind: "capped" };
410
+ }
411
+ busy = true;
412
+ try {
413
+ const body = await read(`&before=${before}&key=${encodeURIComponent(key || "")}`);
414
+ if (!alive()) return { kind: "failed" };
415
+ if (body.failed || body.unsupported || !body.supported) return { kind: "failed" };
416
+ // Another transcript now (a new session): what is shown stays; nothing older of it.
417
+ if (body.reset) { before = null; return { kind: "none" }; }
418
+ before = body.before;
419
+ const steps = body.steps || [];
420
+ if (steps.length) { pages.prepend(block(steps, body.end)); shown += steps.length; }
421
+ return { kind: "ok" };
422
+ } catch { return { kind: "failed" }; }
423
+ finally { busy = false; }
424
+ },
425
+ };
426
+ }
427
+
428
+ /** This module's own stylesheet, linked once (a tab opened before an upgrade has an app.css without these rules). */
429
+ function ensureStylesheet(doc) {
430
+ try {
431
+ if (!doc.head || doc.querySelector('link[data-vh="1"]')) return;
432
+ const link = doc.createElement("link");
433
+ link.setAttribute("rel", "stylesheet");
434
+ link.setAttribute("href", "/assets/view-history.css");
435
+ link.setAttribute("data-vh", "1");
436
+ doc.head.appendChild(link);
437
+ } catch { /* the history still reads without it */ }
438
+ }
@@ -154,6 +154,14 @@ register("view", {
154
154
  "usage.stale.transient": "not updated · cached at {0}",
155
155
  "usage.stale.error": "couldn't read · cached at {0}",
156
156
  "usage.metric.reset_since": "reset since {0}",
157
+ "usage.capacity.line": "100% ≈ {0} ({1}–{2}) · ≈ {3} tokens · from {4} weeks",
158
+ "usage.capacity.line_tokens": "100% ≈ {0} tokens ({1}–{2}) · from {3} weeks",
159
+ "usage.capacity.not_enough": "Not enough usage yet to measure 100% (Δ {0}% of 10%)",
160
+ "usage.capacity.only_agend": "Counts only this fleet's agents; use of the same account elsewhere makes this low.",
161
+ "usage.capacity.mixed": "Bases disagree — possibly used outside this fleet",
162
+ "usage.capacity.best_fit_api": "API-equivalent fits best: ±{0}% across {1} weeks",
163
+ "usage.capacity.best_fit_output": "Output tokens fit best: ±{0}% across {1} weeks",
164
+ "usage.capacity.best_fit_all": "All tokens fit best: ±{0}% across {1} weeks",
157
165
  "usage.error.http": "Usage request failed (HTTP {0}).",
158
166
  "usage.error.billing_http": "Billing request failed (HTTP {0}).",
159
167
  "usage.error.invalid_response": "Invalid response from usage endpoint.",
@@ -215,6 +223,20 @@ register("view", {
215
223
  "guestNeedsTrustedHint": "Your host gave you chat access to this agent. Ask them to make you Trusted in Settings → Guests.",
216
224
  "noInstances": "No instances yet",
217
225
  "paneUnavailable": "This instance's terminal is not available (is it stopped?)",
226
+ // M-2262: the terminal's scrollback.
227
+ "earlier": "Earlier output",
228
+ "earlierHint": "Scroll up to read what has scrolled off. Live output keeps coming in below.",
229
+ "loadingEarlier": "Loading earlier output…",
230
+ "earlierFailed": "Could not load earlier output.",
231
+ "noEarlier": "This CLI redraws its whole screen, so the terminal keeps no earlier output to scroll back to.",
232
+ "nothingEarlier": "Nothing has scrolled off the screen yet.",
233
+ // The words' mark (panel-view.js VIEW_REV, the same in both languages): a tab with older words reads these again.
234
+ "rev": "2",
235
+ "earlierInLog": "Earlier output is in the Log tab",
236
+ "logTabOff": "Earlier output: the Log tab is off. Turn it on in Settings",
237
+ "readingEarlier": "Reading earlier output. Live output continues; scroll to the bottom to follow it again.",
238
+ "jumpLatest": "Jump to latest",
239
+ "newBelow": "New output below",
218
240
  "rosterFailedShort": "The instance list could not be loaded.",
219
241
  "collapseGroup": "Fold {group}",
220
242
  "expandGroup": "Open {group}",
@@ -400,6 +422,14 @@ register("view", {
400
422
  "usage.stale.transient": "暫時無法更新 · 快取於 {0}",
401
423
  "usage.stale.error": "讀取失敗 · 快取於 {0}",
402
424
  "usage.metric.reset_since": "已於 {0} 重置",
425
+ "usage.capacity.line": "100% ≈ {0}({1}–{2})· 約 {3} token · 依 {4} 週",
426
+ "usage.capacity.line_tokens": "100% ≈ {0} token({1}–{2})· 依 {3} 週",
427
+ "usage.capacity.not_enough": "用量還不夠估算 100%(已用 Δ {0}%,需 10%)",
428
+ "usage.capacity.only_agend": "只計算這個 fleet 的 agent;同一帳號在別處的使用會讓這個值偏低。",
429
+ "usage.capacity.mixed": "各換算方式不一致——可能有 fleet 以外的使用",
430
+ "usage.capacity.best_fit_api": "「API 等值」最吻合:{1} 週內 ±{0}%",
431
+ "usage.capacity.best_fit_output": "「輸出 token」最吻合:{1} 週內 ±{0}%",
432
+ "usage.capacity.best_fit_all": "「全部 token」最吻合:{1} 週內 ±{0}%",
403
433
  "usage.error.http": "用量請求失敗(HTTP {0})。",
404
434
  "usage.error.billing_http": "計費請求失敗(HTTP {0})。",
405
435
  "usage.error.invalid_response": "用量端點回傳了無效資料。",
@@ -461,6 +491,18 @@ register("view", {
461
491
  "guestNeedsTrustedHint": "主人給你的是這個 agent 的聊天權限。請他在設定 → 訪客把你改成「信任」。",
462
492
  "noInstances": "還沒有 instance",
463
493
  "paneUnavailable": "無法取得這個 instance 的終端畫面(它是否已停止?)",
494
+ "earlier": "較早的輸出",
495
+ "earlierHint": "往上捲動可以看到已經捲走的內容,新的輸出會繼續出現在下方。",
496
+ "loadingEarlier": "正在載入較早的輸出…",
497
+ "earlierFailed": "無法載入較早的輸出。",
498
+ "noEarlier": "這個 CLI 會重繪整個畫面,終端沒有保留可以往上捲動的較早輸出。",
499
+ "nothingEarlier": "還沒有內容捲出畫面。",
500
+ "rev": "2",
501
+ "earlierInLog": "較早的輸出在「紀錄」分頁",
502
+ "logTabOff": "較早的輸出:「紀錄」分頁目前關閉,請到設定開啟",
503
+ "readingEarlier": "正在看較早的輸出。新的輸出仍會繼續;捲到最下面即可再次跟著最新內容。",
504
+ "jumpLatest": "跳到最新",
505
+ "newBelow": "下方有新的輸出",
464
506
  "rosterFailedShort": "無法載入 instance 清單。",
465
507
  "collapseGroup": "收合 {group}",
466
508
  "expandGroup": "展開 {group}",
@@ -15,6 +15,7 @@ register("webauth", {
15
15
  reason_disabled: "Web authority is turned off.",
16
16
  reason_loopback_only: "Only this computer can authorize, and this browser is not on it.",
17
17
  reason_no_account: "This browser was not signed in as an account (agend web): sign in with /web to authorize.",
18
+ reason_host_sign_in: "To speak as this computer's admin, sign in again on this computer with agend web --code.",
18
19
  reason_not_admin: "Your account is not a fleet admin here.",
19
20
  reason_not_admin_session: "Guests can't authorize.",
20
21
  reason_token_rotated: "The dashboard's token changed: sign in again.",
@@ -43,6 +44,7 @@ register("webauth", {
43
44
  reason_disabled: "網頁授權已關閉。",
44
45
  reason_loopback_only: "只有這台電腦可以授權,而這個瀏覽器不在這台電腦上。",
45
46
  reason_no_account: "這個瀏覽器不是用帳號登入的(agend web):請用 /web 登入後再授權。",
47
+ reason_host_sign_in: "要以這台電腦的管理者身分發言,請在這台電腦上執行 agend web --code 重新登入。",
46
48
  reason_not_admin: "你的帳號在這裡不是 fleet 管理者。",
47
49
  reason_not_admin_session: "訪客無法授權。",
48
50
  reason_token_rotated: "儀表板的 token 已變更:請重新登入。",
@@ -66,7 +68,7 @@ register("webauth", {
66
68
  const tn = (k, ...v) => t(`webauth.${k}`, ...v);
67
69
  // The switch and "Only this computer" are changed in Settings on this computer — never over the public link (#2525 review).
68
70
  const fixable = (reason) => (reason === "disabled" || reason === "loopback_only") && globalThis.document?.body?.dataset?.publicLink !== "1";
69
- const REASONS = ["disabled", "loopback_only", "no_account", "not_admin", "not_admin_session", "token_rotated", "public_link", "named_link_off", "quick_link_off", "not_session"];
71
+ const REASONS = ["disabled", "loopback_only", "no_account", "host_sign_in", "not_admin", "not_admin_session", "token_rotated", "public_link", "named_link_off", "quick_link_off", "not_session"];
70
72
  // Until when: the time, with the day when it isn't today (an authorization lasts up to 48 h).
71
73
  const clock = (ms) => {
72
74
  const d = new Date(ms), today = new Date().toDateString() === d.toDateString();
@@ -0,0 +1,6 @@
1
+ /** Runs `$1` detached from any terminal, at low priority; HUP/INT/TERM on this shell reach its whole process group. */
2
+ export declare const UNATTENDED_INSTALL_SCRIPT: string;
3
+ /** The account's login shell, when it is an absolute path to an executable; else undefined (keep the inherited one). */
4
+ export declare function loginShell(read?: () => string | null | undefined): Promise<string | undefined>;
5
+ /** The window's command for an install command. `shell`: the $SHELL to give it; null keeps the inherited one. */
6
+ export declare function unattendedInstallCommand(command: string, shell?: string | null): string;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * M-2281 (#2980): how `/login` runs a CLI's installer in its window — nobody types there, so nothing may wait for
3
+ * an answer, and the install must not starve the fleet that is still serving every chat.
4
+ *
5
+ * - **No terminal to ask on.** `setsid -w` gives the install a session of its own with no controlling terminal, so
6
+ * `/dev/tty` cannot be opened, and stdin is `/dev/null`. A prompt that asks there (codex's "Start Codex now?",
7
+ * kiro's "replace it?", sudo's password) fails at once instead of waiting out the session timeout. Without
8
+ * `setsid` (macOS has none), stdin is still `/dev/null`. The same wrapper install-agend.ps1 uses (#2583).
9
+ * - **Low priority.** `nice -n 10`, plus `ionice -c2 -n7` where it exists and is permitted: a ~0.5 GB download and
10
+ * unpack (codex 0.166) yields CPU and disk to the fleet and its CLIs.
11
+ * - **Stoppable.** The install's own session is not the window's, so closing the window (cancel, timeout, fleet
12
+ * stop) would not reach it: the window's shell forwards HUP/INT/TERM to the install's whole process group.
13
+ * - **The user's shell.** Installers pick the rc file for their PATH line from `$SHELL`; the window inherits the
14
+ * tmux server's environment, where it may be missing (a service). The account's login shell is passed, so the
15
+ * line lands in the file the user's own terminals read.
16
+ *
17
+ * - **Its verdict is kept.** In a tmux pane it starts once remain-on-exit is on (see the script): an install that ends
18
+ * at once still leaves a dead pane with its exit code and output for the session to judge.
19
+ *
20
+ * The exit code is the install's own: the session judges success by it.
21
+ */
22
+ import { userInfo } from "node:os";
23
+ import { isAbsolute } from "node:path";
24
+ import { shellQuote } from "./backend/types.js";
25
+ import { spawnProbeCommand, PROBE_DEADLINE_MS } from "./binary-probe.js";
26
+ /** Runs `$1` detached from any terminal, at low priority; HUP/INT/TERM on this shell reach its whole process group. */
27
+ export const UNATTENDED_INSTALL_SCRIPT = [
28
+ // In a tmux pane, start only once AgEnD has set remain-on-exit on the window (at most ~10 s): an install that fails
29
+ // at once — a prompt with no terminal does — would otherwise close the window before it is set, taking its exit code
30
+ // and output with it. Outside tmux ($TMUX_PANE unset) there is nothing to wait for.
31
+ 'if [ -n "${TMUX_PANE:-}" ] && command -v tmux >/dev/null 2>&1; then i=0; while [ "$(tmux show-options -wqv -t "$TMUX_PANE" remain-on-exit 2>/dev/null)" != on ] && [ $i -lt 100 ]; do sleep 0.1; i=$((i+1)); done; fi',
32
+ 'P=""',
33
+ 'if command -v ionice >/dev/null 2>&1 && ionice -c2 -n7 true >/dev/null 2>&1; then P="ionice -c2 -n7"; fi',
34
+ 'if command -v setsid >/dev/null 2>&1; then setsid -w nice -n 10 $P sh -c "$1" </dev/null & else nice -n 10 $P sh -c "$1" </dev/null & fi',
35
+ "pid=$!",
36
+ // `kill -TERM -$pid`, not `kill -- -$pid`: dash's kill refuses `--` ("Illegal number"). Before setsid has run there is
37
+ // no such group yet, and the plain pid is what exists.
38
+ "trap 'kill -TERM -$pid 2>/dev/null || kill -TERM $pid 2>/dev/null; exit 129' HUP INT TERM",
39
+ 'wait "$pid"',
40
+ ].join("\n");
41
+ /** The account's login shell, when it is an absolute path to an executable; else undefined (keep the inherited one). */
42
+ export async function loginShell(read = () => userInfo().shell) {
43
+ let shell;
44
+ try {
45
+ shell = read();
46
+ }
47
+ catch {
48
+ return undefined;
49
+ }
50
+ if (!shell || !isAbsolute(shell))
51
+ return undefined;
52
+ // Child `sh` `[ -f && -x ]`, same as resolveBackendBinary's install-dir probe: an accessSync on a dead mount
53
+ // can hang a pool thread forever (#3009); a child can be killed (binary-probe.ts).
54
+ const child = spawnProbeCommand("sh", ["-c", '[ -f "$1" ] && [ -x "$1" ] && echo "$1"', "sh", shell]);
55
+ let timer;
56
+ try {
57
+ const found = await Promise.race([
58
+ child.answer,
59
+ new Promise((r) => {
60
+ timer = setTimeout(() => { child.kill(); r(undefined); }, PROBE_DEADLINE_MS);
61
+ timer.unref?.();
62
+ }),
63
+ ]);
64
+ return found ?? undefined;
65
+ }
66
+ catch {
67
+ return undefined;
68
+ }
69
+ finally {
70
+ if (timer)
71
+ clearTimeout(timer);
72
+ }
73
+ }
74
+ /** The window's command for an install command. `shell`: the $SHELL to give it; null keeps the inherited one. */
75
+ export function unattendedInstallCommand(command, shell = null) {
76
+ const env = shell ? `SHELL=${shellQuote(shell)} ` : "";
77
+ return `${env}sh -c ${shellQuote(UNATTENDED_INSTALL_SCRIPT)} agend-install ${shellQuote(command)}`;
78
+ }
79
+ //# sourceMappingURL=unattended-install.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"unattended-install.js","sourceRoot":"","sources":["../src/unattended-install.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEzE,uHAAuH;AACvH,MAAM,CAAC,MAAM,yBAAyB,GAAG;IACvC,oHAAoH;IACpH,qHAAqH;IACrH,oFAAoF;IACpF,uNAAuN;IACvN,MAAM;IACN,0GAA0G;IAC1G,0IAA0I;IAC1I,QAAQ;IACR,uHAAuH;IACvH,uDAAuD;IACvD,2FAA2F;IAC3F,aAAa;CACd,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,wHAAwH;AACxH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAI,GAAoC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK;IAC7F,IAAI,KAAgC,CAAC;IACrC,IAAI,CAAC;QAAC,KAAK,GAAG,IAAI,EAAE,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,SAAS,CAAC;IAAC,CAAC;IACnD,IAAI,CAAC,KAAK,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACnD,6GAA6G;IAC7G,mFAAmF;IACnF,MAAM,KAAK,GAAG,iBAAiB,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,yCAAyC,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IACtG,IAAI,KAAgD,CAAC;IACrD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;YAC/B,KAAK,CAAC,MAAM;YACZ,IAAI,OAAO,CAAY,CAAC,CAAC,EAAE,EAAE;gBAC3B,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,iBAAiB,CAAC,CAAC;gBAC7E,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC;YAClB,CAAC,CAAC;SACH,CAAC,CAAC;QACH,OAAO,KAAK,IAAI,SAAS,CAAC;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;YAAS,CAAC;QACT,IAAI,KAAK;YAAE,YAAY,CAAC,KAAK,CAAC,CAAC;IACjC,CAAC;AACH,CAAC;AAED,iHAAiH;AACjH,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,KAAK,GAAkB,IAAI;IACnF,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,SAAS,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,OAAO,GAAG,GAAG,SAAS,UAAU,CAAC,yBAAyB,CAAC,kBAAkB,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;AACrG,CAAC"}