@vascend/talos 0.1.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 (620) hide show
  1. package/README.md +202 -0
  2. package/dist/backend/asset-events.d.ts +31 -0
  3. package/dist/backend/asset-events.js +149 -0
  4. package/dist/backend/assets.d.ts +31 -0
  5. package/dist/backend/assets.js +210 -0
  6. package/dist/backend/auth.d.ts +18 -0
  7. package/dist/backend/auth.js +59 -0
  8. package/dist/backend/config.d.ts +14 -0
  9. package/dist/backend/config.js +41 -0
  10. package/dist/backend/conversations.d.ts +21 -0
  11. package/dist/backend/conversations.js +141 -0
  12. package/dist/backend/crypto.d.ts +9 -0
  13. package/dist/backend/crypto.js +68 -0
  14. package/dist/backend/engine.d.ts +13 -0
  15. package/dist/backend/engine.js +78 -0
  16. package/dist/backend/http.d.ts +25 -0
  17. package/dist/backend/http.js +112 -0
  18. package/dist/backend/index.d.ts +1 -0
  19. package/dist/backend/index.js +24 -0
  20. package/dist/backend/routes.d.ts +14 -0
  21. package/dist/backend/routes.js +169 -0
  22. package/dist/backend/sdk.d.ts +12 -0
  23. package/dist/backend/sdk.js +11 -0
  24. package/dist/backend/server.d.ts +12 -0
  25. package/dist/backend/server.js +91 -0
  26. package/dist/backend/temp-image-store.d.ts +46 -0
  27. package/dist/backend/temp-image-store.js +197 -0
  28. package/dist/backend/types.d.ts +103 -0
  29. package/dist/backend/types.js +29 -0
  30. package/dist/backend/users.d.ts +13 -0
  31. package/dist/backend/users.js +76 -0
  32. package/dist/backend/ws-handler.d.ts +14 -0
  33. package/dist/backend/ws-handler.js +72 -0
  34. package/dist/backend/ws.d.ts +19 -0
  35. package/dist/backend/ws.js +146 -0
  36. package/dist/core/agent.d.ts +56 -0
  37. package/dist/core/agent.js +1783 -0
  38. package/dist/core/background.d.ts +45 -0
  39. package/dist/core/background.js +140 -0
  40. package/dist/core/bootstrap.d.ts +45 -0
  41. package/dist/core/bootstrap.js +294 -0
  42. package/dist/core/cli-args.d.ts +41 -0
  43. package/dist/core/cli-args.js +282 -0
  44. package/dist/core/collapsible-output.d.ts +38 -0
  45. package/dist/core/collapsible-output.js +143 -0
  46. package/dist/core/compaction-checkpoint.d.ts +9 -0
  47. package/dist/core/compaction-checkpoint.js +58 -0
  48. package/dist/core/compaction.d.ts +42 -0
  49. package/dist/core/compaction.js +270 -0
  50. package/dist/core/config.d.ts +49 -0
  51. package/dist/core/config.js +417 -0
  52. package/dist/core/context-ledger.d.ts +99 -0
  53. package/dist/core/context-ledger.js +222 -0
  54. package/dist/core/context-policy.d.ts +17 -0
  55. package/dist/core/context-policy.js +81 -0
  56. package/dist/core/continuation-review.d.ts +23 -0
  57. package/dist/core/continuation-review.js +64 -0
  58. package/dist/core/crash-report.d.ts +3 -0
  59. package/dist/core/crash-report.js +27 -0
  60. package/dist/core/credentials.d.ts +11 -0
  61. package/dist/core/credentials.js +183 -0
  62. package/dist/core/doctor.d.ts +24 -0
  63. package/dist/core/doctor.js +125 -0
  64. package/dist/core/effort.d.ts +5 -0
  65. package/dist/core/effort.js +42 -0
  66. package/dist/core/env-paths.d.ts +3 -0
  67. package/dist/core/env-paths.js +38 -0
  68. package/dist/core/execution-guidance.d.ts +9 -0
  69. package/dist/core/execution-guidance.js +73 -0
  70. package/dist/core/execution-target.d.ts +41 -0
  71. package/dist/core/execution-target.js +69 -0
  72. package/dist/core/file-coordination.d.ts +66 -0
  73. package/dist/core/file-coordination.js +176 -0
  74. package/dist/core/file-lease-store.d.ts +14 -0
  75. package/dist/core/file-lease-store.js +89 -0
  76. package/dist/core/image-limits.d.ts +26 -0
  77. package/dist/core/image-limits.js +205 -0
  78. package/dist/core/interruptible-completion.d.ts +3 -0
  79. package/dist/core/interruptible-completion.js +42 -0
  80. package/dist/core/kingdoms-mode-state.d.ts +24 -0
  81. package/dist/core/kingdoms-mode-state.js +139 -0
  82. package/dist/core/licenza.d.ts +38 -0
  83. package/dist/core/licenza.js +125 -0
  84. package/dist/core/model-limits.d.ts +2 -0
  85. package/dist/core/model-limits.js +36 -0
  86. package/dist/core/output-lifecycle.d.ts +44 -0
  87. package/dist/core/output-lifecycle.js +190 -0
  88. package/dist/core/package-update.d.ts +15 -0
  89. package/dist/core/package-update.js +121 -0
  90. package/dist/core/permission-queue.d.ts +2 -0
  91. package/dist/core/permission-queue.js +20 -0
  92. package/dist/core/prompt-attachment.d.ts +21 -0
  93. package/dist/core/prompt-attachment.js +123 -0
  94. package/dist/core/request-plan.d.ts +16 -0
  95. package/dist/core/request-plan.js +36 -0
  96. package/dist/core/run-timing.d.ts +17 -0
  97. package/dist/core/run-timing.js +123 -0
  98. package/dist/core/self-update.d.ts +17 -0
  99. package/dist/core/self-update.js +54 -0
  100. package/dist/core/session-refs.d.ts +27 -0
  101. package/dist/core/session-refs.js +126 -0
  102. package/dist/core/session-storage.d.ts +4 -0
  103. package/dist/core/session-storage.js +48 -0
  104. package/dist/core/session-title.d.ts +1 -0
  105. package/dist/core/session-title.js +21 -0
  106. package/dist/core/session-usage.d.ts +17 -0
  107. package/dist/core/session-usage.js +74 -0
  108. package/dist/core/session.d.ts +58 -0
  109. package/dist/core/session.js +296 -0
  110. package/dist/core/settings.d.ts +17 -0
  111. package/dist/core/settings.js +107 -0
  112. package/dist/core/storage-retention.d.ts +21 -0
  113. package/dist/core/storage-retention.js +227 -0
  114. package/dist/core/text-sanitize.d.ts +1 -0
  115. package/dist/core/text-sanitize.js +35 -0
  116. package/dist/core/tool-artifacts.d.ts +6 -0
  117. package/dist/core/tool-artifacts.js +51 -0
  118. package/dist/core/tool-compress.d.ts +8 -0
  119. package/dist/core/tool-compress.js +111 -0
  120. package/dist/core/tool-executor.d.ts +16 -0
  121. package/dist/core/tool-executor.js +97 -0
  122. package/dist/core/tool-result-index.d.ts +52 -0
  123. package/dist/core/tool-result-index.js +327 -0
  124. package/dist/core/tool-result-store.d.ts +38 -0
  125. package/dist/core/tool-result-store.js +190 -0
  126. package/dist/core/tool-scheduler.d.ts +16 -0
  127. package/dist/core/tool-scheduler.js +35 -0
  128. package/dist/core/trust.d.ts +21 -0
  129. package/dist/core/trust.js +95 -0
  130. package/dist/core/types.d.ts +402 -0
  131. package/dist/core/types.js +1 -0
  132. package/dist/core/update-check.d.ts +26 -0
  133. package/dist/core/update-check.js +89 -0
  134. package/dist/core/update-process.d.ts +10 -0
  135. package/dist/core/update-process.js +31 -0
  136. package/dist/core/user-inbox.d.ts +23 -0
  137. package/dist/core/user-inbox.js +56 -0
  138. package/dist/core/user-input.d.ts +10 -0
  139. package/dist/core/user-input.js +35 -0
  140. package/dist/core/version.d.ts +1 -0
  141. package/dist/core/version.js +10 -0
  142. package/dist/core/vibe-modes.d.ts +38 -0
  143. package/dist/core/vibe-modes.js +383 -0
  144. package/dist/core/workspace-roots.d.ts +13 -0
  145. package/dist/core/workspace-roots.js +91 -0
  146. package/dist/core/workspace-sessions.d.ts +51 -0
  147. package/dist/core/workspace-sessions.js +243 -0
  148. package/dist/cowork/client.d.ts +29 -0
  149. package/dist/cowork/client.js +49 -0
  150. package/dist/cowork/engine-bridge.d.ts +22 -0
  151. package/dist/cowork/engine-bridge.js +98 -0
  152. package/dist/cowork/index.d.ts +11 -0
  153. package/dist/cowork/index.js +16 -0
  154. package/dist/cowork/ipc.d.ts +97 -0
  155. package/dist/cowork/ipc.js +96 -0
  156. package/dist/cowork/manager.d.ts +66 -0
  157. package/dist/cowork/manager.js +325 -0
  158. package/dist/cowork/store.d.ts +23 -0
  159. package/dist/cowork/store.js +182 -0
  160. package/dist/cowork/sync.d.ts +20 -0
  161. package/dist/cowork/sync.js +164 -0
  162. package/dist/cowork/types.d.ts +78 -0
  163. package/dist/cowork/types.js +21 -0
  164. package/dist/extensions/ext-tools.d.ts +3 -0
  165. package/dist/extensions/ext-tools.js +73 -0
  166. package/dist/extensions/loader.d.ts +91 -0
  167. package/dist/extensions/loader.js +474 -0
  168. package/dist/extensions/settings.d.ts +20 -0
  169. package/dist/extensions/settings.js +86 -0
  170. package/dist/extensions/types.d.ts +219 -0
  171. package/dist/extensions/types.js +1 -0
  172. package/dist/horizon/async-queue.d.ts +9 -0
  173. package/dist/horizon/async-queue.js +46 -0
  174. package/dist/horizon/engine.d.ts +85 -0
  175. package/dist/horizon/engine.js +603 -0
  176. package/dist/horizon/index.d.ts +4 -0
  177. package/dist/horizon/index.js +2 -0
  178. package/dist/horizon/main-team.d.ts +163 -0
  179. package/dist/horizon/main-team.js +774 -0
  180. package/dist/horizon/types.d.ts +233 -0
  181. package/dist/horizon/types.js +1 -0
  182. package/dist/index.d.ts +2 -0
  183. package/dist/index.js +233 -0
  184. package/dist/mcp/protocol.d.ts +81 -0
  185. package/dist/mcp/protocol.js +122 -0
  186. package/dist/package.json +1 -0
  187. package/dist/packages/packages.d.ts +12 -0
  188. package/dist/packages/packages.js +323 -0
  189. package/dist/prompts/templates.d.ts +13 -0
  190. package/dist/prompts/templates.js +139 -0
  191. package/dist/providers/anthropic-oauth.d.ts +13 -0
  192. package/dist/providers/anthropic-oauth.js +124 -0
  193. package/dist/providers/anthropic.d.ts +27 -0
  194. package/dist/providers/anthropic.js +526 -0
  195. package/dist/providers/catalog.d.ts +15 -0
  196. package/dist/providers/catalog.js +166 -0
  197. package/dist/providers/effort.d.ts +12 -0
  198. package/dist/providers/effort.js +102 -0
  199. package/dist/providers/model-catalog.d.ts +43 -0
  200. package/dist/providers/model-catalog.js +70 -0
  201. package/dist/providers/model-effort.d.ts +5 -0
  202. package/dist/providers/model-effort.js +43 -0
  203. package/dist/providers/model-registry.d.ts +39 -0
  204. package/dist/providers/model-registry.js +142 -0
  205. package/dist/providers/models-dev.d.ts +9 -0
  206. package/dist/providers/models-dev.js +97 -0
  207. package/dist/providers/oauth-callback-server.d.ts +28 -0
  208. package/dist/providers/oauth-callback-server.js +184 -0
  209. package/dist/providers/oauth-orchestrator.d.ts +58 -0
  210. package/dist/providers/oauth-orchestrator.js +159 -0
  211. package/dist/providers/oauth-store.d.ts +12 -0
  212. package/dist/providers/oauth-store.js +139 -0
  213. package/dist/providers/open-browser.d.ts +20 -0
  214. package/dist/providers/open-browser.js +92 -0
  215. package/dist/providers/openai-compat.d.ts +14 -0
  216. package/dist/providers/openai-compat.js +523 -0
  217. package/dist/providers/openai-oauth.d.ts +15 -0
  218. package/dist/providers/openai-oauth.js +382 -0
  219. package/dist/providers/openrouter-auth.d.ts +73 -0
  220. package/dist/providers/openrouter-auth.js +321 -0
  221. package/dist/providers/openrouter-manual-fallback.d.ts +25 -0
  222. package/dist/providers/openrouter-manual-fallback.js +161 -0
  223. package/dist/providers/openrouter.d.ts +15 -0
  224. package/dist/providers/openrouter.js +107 -0
  225. package/dist/providers/plan-limits.d.ts +6 -0
  226. package/dist/providers/plan-limits.js +175 -0
  227. package/dist/providers/provider-reset.d.ts +4 -0
  228. package/dist/providers/provider-reset.js +83 -0
  229. package/dist/providers/quota-limits.d.ts +4 -0
  230. package/dist/providers/quota-limits.js +33 -0
  231. package/dist/providers/registry.d.ts +33 -0
  232. package/dist/providers/registry.js +213 -0
  233. package/dist/providers/retry.d.ts +37 -0
  234. package/dist/providers/retry.js +347 -0
  235. package/dist/rpc/server.d.ts +23 -0
  236. package/dist/rpc/server.js +222 -0
  237. package/dist/sdk.d.ts +67 -0
  238. package/dist/sdk.js +64 -0
  239. package/dist/skills/skills.d.ts +17 -0
  240. package/dist/skills/skills.js +155 -0
  241. package/dist/text-sanitize.js +28 -0
  242. package/dist/tools/ask-user.d.ts +2 -0
  243. package/dist/tools/ask-user.js +60 -0
  244. package/dist/tools/builtin.d.ts +48 -0
  245. package/dist/tools/builtin.js +2494 -0
  246. package/dist/tools/capabilities.d.ts +5 -0
  247. package/dist/tools/capabilities.js +55 -0
  248. package/dist/tools/computer-capture.d.ts +2 -0
  249. package/dist/tools/computer-capture.js +91 -0
  250. package/dist/tools/computer-posix.d.ts +5 -0
  251. package/dist/tools/computer-posix.js +136 -0
  252. package/dist/tools/computer-use.d.ts +14 -0
  253. package/dist/tools/computer-use.js +108 -0
  254. package/dist/tools/discovery.d.ts +12 -0
  255. package/dist/tools/discovery.js +205 -0
  256. package/dist/tools/execute.d.ts +2 -0
  257. package/dist/tools/execute.js +73 -0
  258. package/dist/tools/normalize-input.d.ts +17 -0
  259. package/dist/tools/normalize-input.js +174 -0
  260. package/dist/tools/output-artifact.d.ts +3 -0
  261. package/dist/tools/output-artifact.js +80 -0
  262. package/dist/tools/pagination.d.ts +15 -0
  263. package/dist/tools/pagination.js +93 -0
  264. package/dist/tools/plan-status.d.ts +2 -0
  265. package/dist/tools/plan-status.js +109 -0
  266. package/dist/tools/program-input.d.ts +3 -0
  267. package/dist/tools/program-input.js +19 -0
  268. package/dist/tools/program.d.ts +3 -0
  269. package/dist/tools/program.js +567 -0
  270. package/dist/tools/protocol-catalog.d.ts +12 -0
  271. package/dist/tools/protocol-catalog.js +46 -0
  272. package/dist/tools/ramification-tools.d.ts +12 -0
  273. package/dist/tools/ramification-tools.js +528 -0
  274. package/dist/tools/regex-compat.d.ts +9 -0
  275. package/dist/tools/regex-compat.js +96 -0
  276. package/dist/tools/registry.d.ts +51 -0
  277. package/dist/tools/registry.js +185 -0
  278. package/dist/tools/resolve.d.ts +2 -0
  279. package/dist/tools/resolve.js +107 -0
  280. package/dist/tools/shell-env.d.ts +2 -0
  281. package/dist/tools/shell-env.js +7 -0
  282. package/dist/tools/team-input.d.ts +3 -0
  283. package/dist/tools/team-input.js +129 -0
  284. package/dist/tools/team-tools.d.ts +7 -0
  285. package/dist/tools/team-tools.js +348 -0
  286. package/dist/tools/workspace-sessions.d.ts +2 -0
  287. package/dist/tools/workspace-sessions.js +35 -0
  288. package/dist/tui/account-entry.d.ts +1 -0
  289. package/dist/tui/account-entry.js +93 -0
  290. package/dist/tui/attachment-highlight.d.ts +1 -0
  291. package/dist/tui/attachment-highlight.js +24 -0
  292. package/dist/tui/attachment-refs.d.ts +2 -0
  293. package/dist/tui/attachment-refs.js +3 -0
  294. package/dist/tui/bottom-input.d.ts +219 -0
  295. package/dist/tui/bottom-input.js +1958 -0
  296. package/dist/tui/branding.d.ts +107 -0
  297. package/dist/tui/branding.js +750 -0
  298. package/dist/tui/clipboard-image.d.ts +10 -0
  299. package/dist/tui/clipboard-image.js +163 -0
  300. package/dist/tui/clipboard-text.d.ts +9 -0
  301. package/dist/tui/clipboard-text.js +51 -0
  302. package/dist/tui/clipboard-windows.d.ts +8 -0
  303. package/dist/tui/clipboard-windows.js +84 -0
  304. package/dist/tui/collapsible-command.d.ts +15 -0
  305. package/dist/tui/collapsible-command.js +48 -0
  306. package/dist/tui/command-feedback.d.ts +10 -0
  307. package/dist/tui/command-feedback.js +46 -0
  308. package/dist/tui/command-output-registry.d.ts +28 -0
  309. package/dist/tui/command-output-registry.js +103 -0
  310. package/dist/tui/command-transcript.d.ts +12 -0
  311. package/dist/tui/command-transcript.js +90 -0
  312. package/dist/tui/commands.d.ts +20 -0
  313. package/dist/tui/commands.js +131 -0
  314. package/dist/tui/context-bar.d.ts +4 -0
  315. package/dist/tui/context-bar.js +74 -0
  316. package/dist/tui/context-meter.d.ts +25 -0
  317. package/dist/tui/context-meter.js +81 -0
  318. package/dist/tui/cost-binding.d.ts +8 -0
  319. package/dist/tui/cost-binding.js +43 -0
  320. package/dist/tui/cost-tracker.d.ts +49 -0
  321. package/dist/tui/cost-tracker.js +122 -0
  322. package/dist/tui/diff.d.ts +9 -0
  323. package/dist/tui/diff.js +119 -0
  324. package/dist/tui/i18n-catalog.d.ts +5 -0
  325. package/dist/tui/i18n-catalog.js +184 -0
  326. package/dist/tui/i18n.d.ts +6 -0
  327. package/dist/tui/i18n.js +10 -0
  328. package/dist/tui/image-attachments.d.ts +46 -0
  329. package/dist/tui/image-attachments.js +167 -0
  330. package/dist/tui/images.d.ts +12 -0
  331. package/dist/tui/images.js +87 -0
  332. package/dist/tui/intro.d.ts +13 -0
  333. package/dist/tui/intro.js +109 -0
  334. package/dist/tui/language-preference.d.ts +6 -0
  335. package/dist/tui/language-preference.js +24 -0
  336. package/dist/tui/live-output.d.ts +2 -0
  337. package/dist/tui/live-output.js +18 -0
  338. package/dist/tui/locales.d.ts +19 -0
  339. package/dist/tui/locales.js +22 -0
  340. package/dist/tui/login-menu.d.ts +17 -0
  341. package/dist/tui/login-menu.js +58 -0
  342. package/dist/tui/logo.d.ts +8 -0
  343. package/dist/tui/logo.js +305 -0
  344. package/dist/tui/markdown-inline.d.ts +3 -0
  345. package/dist/tui/markdown-inline.js +99 -0
  346. package/dist/tui/markdown-table.d.ts +4 -0
  347. package/dist/tui/markdown-table.js +63 -0
  348. package/dist/tui/markdown.d.ts +11 -0
  349. package/dist/tui/markdown.js +257 -0
  350. package/dist/tui/mcp-transcript.d.ts +7 -0
  351. package/dist/tui/mcp-transcript.js +40 -0
  352. package/dist/tui/model-details.d.ts +2 -0
  353. package/dist/tui/model-details.js +21 -0
  354. package/dist/tui/model-selection.d.ts +7 -0
  355. package/dist/tui/model-selection.js +41 -0
  356. package/dist/tui/native-scrollback.d.ts +27 -0
  357. package/dist/tui/native-scrollback.js +134 -0
  358. package/dist/tui/onboarding.d.ts +72 -0
  359. package/dist/tui/onboarding.js +207 -0
  360. package/dist/tui/paste-collapse.d.ts +39 -0
  361. package/dist/tui/paste-collapse.js +117 -0
  362. package/dist/tui/perm-mode.d.ts +8 -0
  363. package/dist/tui/perm-mode.js +41 -0
  364. package/dist/tui/prompt-queue-store.d.ts +26 -0
  365. package/dist/tui/prompt-queue-store.js +87 -0
  366. package/dist/tui/prompt-queue.d.ts +47 -0
  367. package/dist/tui/prompt-queue.js +187 -0
  368. package/dist/tui/provider-menu.d.ts +33 -0
  369. package/dist/tui/provider-menu.js +175 -0
  370. package/dist/tui/question.d.ts +24 -0
  371. package/dist/tui/question.js +32 -0
  372. package/dist/tui/queue-screen.d.ts +25 -0
  373. package/dist/tui/queue-screen.js +152 -0
  374. package/dist/tui/queue-team.d.ts +3 -0
  375. package/dist/tui/queue-team.js +28 -0
  376. package/dist/tui/queued-questions.d.ts +20 -0
  377. package/dist/tui/queued-questions.js +163 -0
  378. package/dist/tui/quick-help.d.ts +1 -0
  379. package/dist/tui/quick-help.js +53 -0
  380. package/dist/tui/repl.d.ts +78 -0
  381. package/dist/tui/repl.js +4130 -0
  382. package/dist/tui/restore-queued-input.d.ts +3 -0
  383. package/dist/tui/restore-queued-input.js +20 -0
  384. package/dist/tui/review-detail.d.ts +6 -0
  385. package/dist/tui/review-detail.js +56 -0
  386. package/dist/tui/review-files.d.ts +3 -0
  387. package/dist/tui/review-files.js +22 -0
  388. package/dist/tui/review-render.d.ts +22 -0
  389. package/dist/tui/review-render.js +98 -0
  390. package/dist/tui/review-screen.d.ts +38 -0
  391. package/dist/tui/review-screen.js +358 -0
  392. package/dist/tui/review-store.d.ts +60 -0
  393. package/dist/tui/review-store.js +270 -0
  394. package/dist/tui/review-team.d.ts +2 -0
  395. package/dist/tui/review-team.js +27 -0
  396. package/dist/tui/selector-mouse.d.ts +26 -0
  397. package/dist/tui/selector-mouse.js +65 -0
  398. package/dist/tui/selector.d.ts +51 -0
  399. package/dist/tui/selector.js +299 -0
  400. package/dist/tui/session-replay.d.ts +10 -0
  401. package/dist/tui/session-replay.js +78 -0
  402. package/dist/tui/session-usage.d.ts +22 -0
  403. package/dist/tui/session-usage.js +55 -0
  404. package/dist/tui/skill-transcript.d.ts +5 -0
  405. package/dist/tui/skill-transcript.js +31 -0
  406. package/dist/tui/startup.d.ts +5 -0
  407. package/dist/tui/startup.js +61 -0
  408. package/dist/tui/status-line-loader.d.ts +8 -0
  409. package/dist/tui/status-line-loader.js +33 -0
  410. package/dist/tui/status-line.d.ts +38 -0
  411. package/dist/tui/status-line.js +213 -0
  412. package/dist/tui/system-language.d.ts +8 -0
  413. package/dist/tui/system-language.js +41 -0
  414. package/dist/tui/task-labels.d.ts +2 -0
  415. package/dist/tui/task-labels.js +7 -0
  416. package/dist/tui/term-bg.d.ts +4 -0
  417. package/dist/tui/term-bg.js +67 -0
  418. package/dist/tui/terminal-screen.d.ts +12 -0
  419. package/dist/tui/terminal-screen.js +1 -0
  420. package/dist/tui/terminal-text.d.ts +7 -0
  421. package/dist/tui/terminal-text.js +123 -0
  422. package/dist/tui/theme.d.ts +25 -0
  423. package/dist/tui/theme.js +383 -0
  424. package/dist/tui/tool-style.d.ts +7 -0
  425. package/dist/tui/tool-style.js +119 -0
  426. package/dist/tui/update-prompt.d.ts +14 -0
  427. package/dist/tui/update-prompt.js +104 -0
  428. package/dist/tui/usage-footer.d.ts +9 -0
  429. package/dist/tui/usage-footer.js +123 -0
  430. package/dist/tui/vascend-login.d.ts +68 -0
  431. package/dist/tui/vascend-login.js +355 -0
  432. package/dist/tui/web-login.d.ts +28 -0
  433. package/dist/tui/web-login.js +115 -0
  434. package/dist/vascend/activity-status.d.ts +12 -0
  435. package/dist/vascend/activity-status.js +68 -0
  436. package/dist/vascend/architecture-map.d.ts +73 -0
  437. package/dist/vascend/architecture-map.js +424 -0
  438. package/dist/vascend/attention-layout.d.ts +19 -0
  439. package/dist/vascend/attention-layout.js +141 -0
  440. package/dist/vascend/budget-allocator.d.ts +31 -0
  441. package/dist/vascend/budget-allocator.js +79 -0
  442. package/dist/vascend/candidate-collector.d.ts +20 -0
  443. package/dist/vascend/candidate-collector.js +220 -0
  444. package/dist/vascend/code-events.d.ts +24 -0
  445. package/dist/vascend/code-events.js +304 -0
  446. package/dist/vascend/code-graph-memory.d.ts +38 -0
  447. package/dist/vascend/code-graph-memory.js +334 -0
  448. package/dist/vascend/code-graph-runtime.d.ts +19 -0
  449. package/dist/vascend/code-graph-runtime.js +190 -0
  450. package/dist/vascend/code-graph-types.d.ts +58 -0
  451. package/dist/vascend/code-graph-types.js +50 -0
  452. package/dist/vascend/code-parser.d.ts +4 -0
  453. package/dist/vascend/code-parser.js +257 -0
  454. package/dist/vascend/competitor-knowledge.d.ts +53 -0
  455. package/dist/vascend/competitor-knowledge.js +262 -0
  456. package/dist/vascend/completeness.d.ts +16 -0
  457. package/dist/vascend/completeness.js +90 -0
  458. package/dist/vascend/context-autotuner.d.ts +45 -0
  459. package/dist/vascend/context-autotuner.js +231 -0
  460. package/dist/vascend/context-candidate.d.ts +45 -0
  461. package/dist/vascend/context-candidate.js +150 -0
  462. package/dist/vascend/context-capsule.d.ts +51 -0
  463. package/dist/vascend/context-capsule.js +133 -0
  464. package/dist/vascend/context-fabric.d.ts +108 -0
  465. package/dist/vascend/context-fabric.js +480 -0
  466. package/dist/vascend/context-feedback-loop.d.ts +56 -0
  467. package/dist/vascend/context-feedback-loop.js +204 -0
  468. package/dist/vascend/context-learning-identity.d.ts +4 -0
  469. package/dist/vascend/context-learning-identity.js +8 -0
  470. package/dist/vascend/context-links.d.ts +14 -0
  471. package/dist/vascend/context-links.js +83 -0
  472. package/dist/vascend/context-lint.d.ts +22 -0
  473. package/dist/vascend/context-lint.js +91 -0
  474. package/dist/vascend/context-market-selection.d.ts +17 -0
  475. package/dist/vascend/context-market-selection.js +50 -0
  476. package/dist/vascend/context-metrics.d.ts +25 -0
  477. package/dist/vascend/context-metrics.js +57 -0
  478. package/dist/vascend/context-pack-render.d.ts +16 -0
  479. package/dist/vascend/context-pack-render.js +52 -0
  480. package/dist/vascend/context-pareto.d.ts +33 -0
  481. package/dist/vascend/context-pareto.js +165 -0
  482. package/dist/vascend/context-pattern-explorer.d.ts +41 -0
  483. package/dist/vascend/context-pattern-explorer.js +276 -0
  484. package/dist/vascend/context-regret.d.ts +32 -0
  485. package/dist/vascend/context-regret.js +93 -0
  486. package/dist/vascend/context-retriever.d.ts +22 -0
  487. package/dist/vascend/context-retriever.js +243 -0
  488. package/dist/vascend/context-scope-index.d.ts +5 -0
  489. package/dist/vascend/context-scope-index.js +96 -0
  490. package/dist/vascend/context-scorer.d.ts +35 -0
  491. package/dist/vascend/context-scorer.js +162 -0
  492. package/dist/vascend/context-search-index.d.ts +12 -0
  493. package/dist/vascend/context-search-index.js +25 -0
  494. package/dist/vascend/context-snapshot.d.ts +22 -0
  495. package/dist/vascend/context-snapshot.js +135 -0
  496. package/dist/vascend/context-window-compiler.d.ts +42 -0
  497. package/dist/vascend/context-window-compiler.js +237 -0
  498. package/dist/vascend/contract-memory.d.ts +61 -0
  499. package/dist/vascend/contract-memory.js +435 -0
  500. package/dist/vascend/council.d.ts +58 -0
  501. package/dist/vascend/council.js +337 -0
  502. package/dist/vascend/decision-promotion.d.ts +48 -0
  503. package/dist/vascend/decision-promotion.js +410 -0
  504. package/dist/vascend/delta-context.d.ts +12 -0
  505. package/dist/vascend/delta-context.js +36 -0
  506. package/dist/vascend/dev-context-compiler.d.ts +27 -0
  507. package/dist/vascend/dev-context-compiler.js +133 -0
  508. package/dist/vascend/dev-context-layout.d.ts +30 -0
  509. package/dist/vascend/dev-context-layout.js +262 -0
  510. package/dist/vascend/evidence-slots.d.ts +14 -0
  511. package/dist/vascend/evidence-slots.js +55 -0
  512. package/dist/vascend/expansion-levels.d.ts +15 -0
  513. package/dist/vascend/expansion-levels.js +178 -0
  514. package/dist/vascend/file-lock.d.ts +7 -0
  515. package/dist/vascend/file-lock.js +95 -0
  516. package/dist/vascend/granular-kingdom.d.ts +25 -0
  517. package/dist/vascend/granular-kingdom.js +259 -0
  518. package/dist/vascend/granular-launcher.d.ts +48 -0
  519. package/dist/vascend/granular-launcher.js +202 -0
  520. package/dist/vascend/kingdom-bus.d.ts +25 -0
  521. package/dist/vascend/kingdom-bus.js +54 -0
  522. package/dist/vascend/kingdom-channel.d.ts +22 -0
  523. package/dist/vascend/kingdom-channel.js +180 -0
  524. package/dist/vascend/kingdom-kpi.d.ts +48 -0
  525. package/dist/vascend/kingdom-kpi.js +172 -0
  526. package/dist/vascend/kingdom-live.d.ts +28 -0
  527. package/dist/vascend/kingdom-live.js +61 -0
  528. package/dist/vascend/kingdom-run.d.ts +18 -0
  529. package/dist/vascend/kingdom-run.js +506 -0
  530. package/dist/vascend/kingdom-scribe.d.ts +20 -0
  531. package/dist/vascend/kingdom-scribe.js +52 -0
  532. package/dist/vascend/kingdom.d.ts +79 -0
  533. package/dist/vascend/kingdom.js +870 -0
  534. package/dist/vascend/lexical-index.d.ts +10 -0
  535. package/dist/vascend/lexical-index.js +99 -0
  536. package/dist/vascend/lexical-search.d.ts +29 -0
  537. package/dist/vascend/lexical-search.js +262 -0
  538. package/dist/vascend/long-term-context.d.ts +51 -0
  539. package/dist/vascend/long-term-context.js +194 -0
  540. package/dist/vascend/mandate.d.ts +33 -0
  541. package/dist/vascend/mandate.js +107 -0
  542. package/dist/vascend/memory-packet.d.ts +13 -0
  543. package/dist/vascend/memory-packet.js +155 -0
  544. package/dist/vascend/memory-status.d.ts +22 -0
  545. package/dist/vascend/memory-status.js +73 -0
  546. package/dist/vascend/memory-store.d.ts +62 -0
  547. package/dist/vascend/memory-store.js +273 -0
  548. package/dist/vascend/objective-revision.d.ts +69 -0
  549. package/dist/vascend/objective-revision.js +319 -0
  550. package/dist/vascend/patch-plan-store.d.ts +33 -0
  551. package/dist/vascend/patch-plan-store.js +240 -0
  552. package/dist/vascend/patch-plan.d.ts +97 -0
  553. package/dist/vascend/patch-plan.js +345 -0
  554. package/dist/vascend/plan-progress.d.ts +11 -0
  555. package/dist/vascend/plan-progress.js +68 -0
  556. package/dist/vascend/plan-store.d.ts +64 -0
  557. package/dist/vascend/plan-store.js +460 -0
  558. package/dist/vascend/proof-chains.d.ts +19 -0
  559. package/dist/vascend/proof-chains.js +120 -0
  560. package/dist/vascend/ramification-store.d.ts +25 -0
  561. package/dist/vascend/ramification-store.js +94 -0
  562. package/dist/vascend/ramification.d.ts +193 -0
  563. package/dist/vascend/ramification.js +583 -0
  564. package/dist/vascend/recall-planner.d.ts +17 -0
  565. package/dist/vascend/recall-planner.js +109 -0
  566. package/dist/vascend/requirements.d.ts +31 -0
  567. package/dist/vascend/requirements.js +151 -0
  568. package/dist/vascend/reviewer.d.ts +9 -0
  569. package/dist/vascend/reviewer.js +93 -0
  570. package/dist/vascend/room-graph.d.ts +29 -0
  571. package/dist/vascend/room-graph.js +242 -0
  572. package/dist/vascend/room-memory.d.ts +57 -0
  573. package/dist/vascend/room-memory.js +631 -0
  574. package/dist/vascend/runtime.d.ts +84 -0
  575. package/dist/vascend/runtime.js +286 -0
  576. package/dist/vascend/self-improve.d.ts +104 -0
  577. package/dist/vascend/self-improve.js +396 -0
  578. package/dist/vascend/session-search.d.ts +52 -0
  579. package/dist/vascend/session-search.js +349 -0
  580. package/dist/vascend/supersession-graph.d.ts +14 -0
  581. package/dist/vascend/supersession-graph.js +70 -0
  582. package/dist/vascend/team-areas.d.ts +9 -0
  583. package/dist/vascend/team-areas.js +28 -0
  584. package/dist/vascend/team-handoff.d.ts +24 -0
  585. package/dist/vascend/team-handoff.js +37 -0
  586. package/dist/vascend/team-scheduler.d.ts +13 -0
  587. package/dist/vascend/team-scheduler.js +74 -0
  588. package/dist/vascend/team-session.d.ts +42 -0
  589. package/dist/vascend/team-session.js +142 -0
  590. package/dist/vascend/test-failure-memory.d.ts +76 -0
  591. package/dist/vascend/test-failure-memory.js +354 -0
  592. package/dist/vascend/token-market.d.ts +42 -0
  593. package/dist/vascend/token-market.js +144 -0
  594. package/dist/vascend/tuner-baseline-store.d.ts +52 -0
  595. package/dist/vascend/tuner-baseline-store.js +446 -0
  596. package/dist/vascend/user-profile.d.ts +44 -0
  597. package/dist/vascend/user-profile.js +131 -0
  598. package/dist/vascend/vision.d.ts +94 -0
  599. package/dist/vascend/vision.js +375 -0
  600. package/dist/vascend/work-agent.d.ts +12 -0
  601. package/dist/vascend/work-agent.js +83 -0
  602. package/dist/vascend/work-queue.d.ts +62 -0
  603. package/dist/vascend/work-queue.js +255 -0
  604. package/dist/vascend/work-runner.d.ts +64 -0
  605. package/dist/vascend/work-runner.js +137 -0
  606. package/dist/vascend/worktree.d.ts +38 -0
  607. package/dist/vascend/worktree.js +206 -0
  608. package/dist/vendor/computer-use/README.md +7 -0
  609. package/dist/vendor/computer-use/computer-desktop.js +211 -0
  610. package/dist/vendor/computer-use/computer-observe.js +315 -0
  611. package/dist/vendor/computer-use/computer-scripts.js +1230 -0
  612. package/dist/vendor/computer-use/computer-tools.js +2136 -0
  613. package/dist/vendor/computer-use/package.json +1 -0
  614. package/dist/vendor/computer-use/rac/atomic-input.js +91 -0
  615. package/dist/vendor/computer-use/rac/grammatica.js +101 -0
  616. package/dist/vendor/computer-use/rac/parser.js +185 -0
  617. package/dist/vendor/computer-use/rac/protocol.js +136 -0
  618. package/install.ps1 +30 -0
  619. package/install.sh +50 -0
  620. package/package.json +56 -0
@@ -0,0 +1,750 @@
1
+ import { renderVascendTerminalLogo } from "./logo.js";
2
+ import { renderMarkdown } from "./markdown.js";
3
+ import { tokenAnsi } from "./theme.js";
4
+ import { fitLine, padLine, plainText, wrapText as wrapTerminalText } from "./terminal-text.js";
5
+ import { createAttachmentHighlighter } from "./attachment-highlight.js";
6
+ const RESET = "\x1b[0m";
7
+ const SILVER = "\x1b[38;2;210;214;220m";
8
+ const DARK_SILVER = "\x1b[38;2;104;111;122m";
9
+ const CHAT_TEXT = "\x1b[38;2;232;235;239m";
10
+ const CHAT_BG = "\x1b[48;2;29;41;56m";
11
+ const ANSI = {
12
+ blue: "\x1b[34m",
13
+ cyan: "\x1b[36m",
14
+ dim: "\x1b[2m",
15
+ gray: "\x1b[90m",
16
+ green: "\x1b[32m",
17
+ magenta: "\x1b[35m",
18
+ red: "\x1b[31m",
19
+ white: "\x1b[37m",
20
+ yellow: "\x1b[33m",
21
+ };
22
+ function paint(color, s) {
23
+ return `${ANSI[color]}${s}${RESET}`;
24
+ }
25
+ // --- Palette "argento gelido" --------------------------------------------
26
+ // Gradiente FREDDO usato dall'intro animata: dal bianco ghiaccio all'argento,
27
+ // poi grafite, con una leggera punta di blu acciaio nei toni piu' scuri (cosi'
28
+ // il metallo sembra "gelido" e non grigio neutro). t in [0,1]: 0 = scuro,
29
+ // 1 = luce. Ritorna una tripla RGB.
30
+ export function frostSilver(t) {
31
+ const x = clamp01(t);
32
+ // Punti di controllo (cool grays con bias blu nei bassi):
33
+ // 0.00 -> grafite blu-acciaio (58, 66, 80)
34
+ // 0.45 -> argento freddo (150, 160, 174)
35
+ // 0.80 -> argento chiaro (208, 215, 224)
36
+ // 1.00 -> bianco ghiaccio (238, 244, 252)
37
+ const stops = [
38
+ [0.0, 58, 66, 80],
39
+ [0.45, 150, 160, 174],
40
+ [0.8, 208, 215, 224],
41
+ [1.0, 238, 244, 252],
42
+ ];
43
+ for (let i = 1; i < stops.length; i += 1) {
44
+ const a = stops[i - 1];
45
+ const b = stops[i];
46
+ if (x <= b[0]) {
47
+ const span = b[0] - a[0] || 1;
48
+ const k = (x - a[0]) / span;
49
+ return [
50
+ Math.round(a[1] + (b[1] - a[1]) * k),
51
+ Math.round(a[2] + (b[2] - a[2]) * k),
52
+ Math.round(a[3] + (b[3] - a[3]) * k),
53
+ ];
54
+ }
55
+ }
56
+ const last = stops[stops.length - 1];
57
+ return [last[1], last[2], last[3]];
58
+ }
59
+ // Sequenza di escape per foreground RGB (truecolor).
60
+ export function fgRgb(rgb) {
61
+ return `\x1b[38;2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
62
+ }
63
+ // Avvolge `s` nel colore argento-gelido al livello `t` (con reset finale).
64
+ export function frostPaint(t, s) {
65
+ return `${fgRgb(frostSilver(t))}${s}${RESET}`;
66
+ }
67
+ function clamp01(v) {
68
+ return v < 0 ? 0 : v > 1 ? 1 : v;
69
+ }
70
+ // Quando e' presente uno ThemeSpec, le funzioni del tema TUI sono derivate dai
71
+ // token del tema (parita' Pi); altrimenti si usa la palette argento/grafite
72
+ // storica e i colori ANSI di accent/muted dal config.
73
+ export function makeTheme(ui, spec) {
74
+ if (spec) {
75
+ const wrap = (code, s) => (code ? `${code}${s}${RESET}` : s);
76
+ const fg = (t) => tokenAnsi(spec, t);
77
+ const bg = (t) => tokenAnsi(spec, t, true);
78
+ const chatBg = tokenAnsi(spec, "userMessageBg", true);
79
+ const composerBg = spec.mode === "light" ? "\x1b[48;2;235;235;235m" : "\x1b[48;2;51;51;51m";
80
+ return {
81
+ markdownHeading: (s) => wrap(fg("mdHeading"), s),
82
+ markdownLink: (s) => wrap(fg("mdLink"), s),
83
+ markdownCode: (s) => wrap(fg("mdCode"), s),
84
+ markdownBullet: (s) => wrap(fg("mdListBullet"), s),
85
+ accent: (s) => wrap(fg("accent"), s),
86
+ bold: (s) => `\x1b[1m${s}${RESET}`,
87
+ chatBorder: (s) => wrap(fg("borderAccent"), s),
88
+ attachmentRef: (s) => wrap(fg("attachmentRef"), s),
89
+ chatText: (s) => {
90
+ const code = `${chatBg}${fg("userMessageText")}`;
91
+ return wrap(code, s.replace(/\x1b\[0m/g, RESET + code));
92
+ },
93
+ composerText: (s) => {
94
+ const code = `${composerBg}${fg("userMessageText")}`;
95
+ return wrap(code, s.replace(/\x1b\[0m/g, RESET + code));
96
+ },
97
+ selected: (s) => wrap(`${bg("selectedBg")}${fg("text")}\x1b[1m`, s),
98
+ dark: (s) => wrap(fg("muted"), s),
99
+ diffAdded: (s) => wrap(`${bg("toolDiffAddedBg")}${fg("toolDiffAdded")}`, s),
100
+ diffBorder: (s) => wrap(fg("toolDiffBorder"), s),
101
+ diffContext: (s) => wrap(`${bg("toolDiffContextBg")}${fg("toolDiffContext")}`, s),
102
+ diffHeader: (s) => wrap(`${bg("toolDiffHeaderBg")}${fg("toolTitle")}`, s),
103
+ diffRemoved: (s) => wrap(`${bg("toolDiffRemovedBg")}${fg("toolDiffRemoved")}`, s),
104
+ dim: (s) => wrap(fg("dim"), s),
105
+ frame: (s) => wrap(fg("border"), s),
106
+ green: (s) => wrap(fg("success"), s),
107
+ muted: (s) => wrap(fg("muted"), s),
108
+ red: (s) => wrap(fg("error"), s),
109
+ silver: (s) => wrap(fg("text"), s),
110
+ yellow: (s) => wrap(fg("warning"), s),
111
+ danTag: (s) => wrap(fg("syntaxFunction"), s),
112
+ danId: (s) => wrap(fg("syntaxType"), s),
113
+ danValue: (s) => wrap(fg("syntaxString"), s),
114
+ danArrow: (s) => wrap(fg("syntaxOperator"), s),
115
+ danNum: (s) => wrap(fg("syntaxNumber"), s),
116
+ danPunct: (s) => wrap(fg("syntaxPunctuation"), s),
117
+ };
118
+ }
119
+ return {
120
+ markdownHeading: (s) => paint("yellow", s),
121
+ markdownLink: (s) => paint("cyan", s),
122
+ markdownCode: (s) => paint("cyan", s),
123
+ markdownBullet: (s) => paint("blue", s),
124
+ accent: (s) => paint(ui.accentColor, s),
125
+ bold: (s) => `\x1b[1m${s}${RESET}`,
126
+ chatBorder: (s) => `${DARK_SILVER}${s}${RESET}`,
127
+ attachmentRef: (s) => paint("cyan", s),
128
+ chatText: (s) => `${CHAT_BG}${CHAT_TEXT}${s.replace(/\x1b\[0m/g, RESET + CHAT_BG + CHAT_TEXT)}${RESET}`,
129
+ composerText: (s) => `\x1b[48;2;51;51;51m${CHAT_TEXT}${s.replace(/\x1b\[0m/g, RESET + "\x1b[48;2;51;51;51m" + CHAT_TEXT)}${RESET}`,
130
+ selected: (s) => `\x1b[48;2;48;68;94m${CHAT_TEXT}\x1b[1m${s}${RESET}`,
131
+ dark: (s) => `${DARK_SILVER}${s}${RESET}`,
132
+ diffAdded: (s) => `\x1b[48;2;16;33;23m\x1b[38;2;127;185;138m${s}${RESET}`,
133
+ diffBorder: (s) => `\x1b[38;2;77;85;97m${s}${RESET}`,
134
+ diffContext: (s) => `\x1b[48;2;13;16;21m${DARK_SILVER}${s}${RESET}`,
135
+ diffHeader: (s) => `\x1b[48;2;21;25;35m${SILVER}${s}${RESET}`,
136
+ diffRemoved: (s) => `\x1b[48;2;37;19;20m\x1b[38;2;217;140;140m${s}${RESET}`,
137
+ dim: (s) => paint("dim", s),
138
+ frame: (s) => `${SILVER}${s}${RESET}`,
139
+ green: (s) => paint("green", s),
140
+ muted: (s) => paint(ui.mutedColor, s),
141
+ red: (s) => paint("red", s),
142
+ silver: (s) => `${SILVER}${s}${RESET}`,
143
+ yellow: (s) => paint("yellow", s),
144
+ danTag: (s) => `\x1b[38;2;169;194;217m${s}${RESET}`,
145
+ danId: (s) => `\x1b[38;2;134;178;198m${s}${RESET}`,
146
+ danValue: (s) => `\x1b[38;2;167;189;170m${s}${RESET}`,
147
+ danArrow: (s) => `\x1b[38;2;127;150;180m${s}${RESET}`,
148
+ danNum: (s) => `\x1b[38;2;200;189;156m${s}${RESET}`,
149
+ danPunct: (s) => `\x1b[38;2;107;116;128m${s}${RESET}`,
150
+ };
151
+ }
152
+ function startupLogo(ui, columns) {
153
+ if (ui.banner === "none" || ui.banner === "minimal")
154
+ return { lines: [], precolored: false };
155
+ if (ui.banner === "custom")
156
+ return { lines: ui.logoLines, precolored: false };
157
+ if (ui.logoLines.length)
158
+ return { lines: ui.logoLines, precolored: false };
159
+ return { lines: renderVascendTerminalLogo(logoSize(columns)), precolored: true };
160
+ }
161
+ export function renderStartup(config, meta = {}, options = {}, spec) {
162
+ return `${renderStartupLines(config, meta, options, spec).join("\n")}\n`;
163
+ }
164
+ // Come renderStartup ma ritorna le RIGHE separate (senza newline finale ne'
165
+ // righe vuote di cornice). Serve all'intro animata, che riscrive le stesse N
166
+ // righe ad ogni frame ricolorandole con il gradiente argento-gelido.
167
+ export function renderStartupLines(config, meta = {}, options = {}, spec) {
168
+ const theme = makeTheme(config.ui, spec);
169
+ const columns = clamp(options.columns ?? 100, 36, 180);
170
+ const contentWidth = Math.max(30, columns - 2);
171
+ if (options.compact)
172
+ return [
173
+ fitLine(theme.silver(` ${config.ui.title}`) + theme.muted(` · ${config.model}`), columns - 1),
174
+ fitLine(" " + commandsHint(theme, columns - 2), columns - 1),
175
+ ];
176
+ const logo = startupLogo(config.ui, columns);
177
+ const logoLines = logo.lines.map((line) => ` ${logo.precolored ? line : theme.accent(line)}`);
178
+ const gap = columns >= 110 ? 6 : 4;
179
+ const logoWidth = Math.max(0, ...logoLines.map(visibleLength));
180
+ const sideCard = renderStartupCard(config, meta, theme, Math.max(30, contentWidth - logoWidth - gap));
181
+ const sideWidth = logoWidth + gap + Math.max(0, ...sideCard.map(visibleLength));
182
+ const canUseSideBySide = logoLines.length > 0 && columns >= 82 && sideWidth <= contentWidth;
183
+ const card = canUseSideBySide
184
+ ? sideCard
185
+ : renderStartupCard(config, meta, theme, Math.min(62, contentWidth));
186
+ const body = canUseSideBySide
187
+ ? beside(logoLines, card, gap)
188
+ : stack(logoLines, card);
189
+ return ["", ...body, ""];
190
+ }
191
+ export function renderAssistantTurnStart(_theme, _columns) {
192
+ return "";
193
+ }
194
+ export function renderAssistantSegment(text, theme, columns, imageCtx, _kind = "start", _sequence = 1) {
195
+ const contentWidth = Math.max(2, columns - 5);
196
+ const lines = renderMarkdown(text.trimEnd(), { width: contentWidth, theme, imageCtx });
197
+ const rows = lines.length ? lines : [""];
198
+ return [
199
+ "",
200
+ ...rows.map((line, index) => (index === 0 ? theme.dark(" • ") : " ") + highlightDanilov(line, theme)),
201
+ ].join("\n") + "\n";
202
+ }
203
+ export function renderAssistantTurnEnd(theme, sequence, status = "completed") {
204
+ return status === "error" ? theme.red(" × Risposta interrotta\n") : "";
205
+ }
206
+ // Compatibilita' con i renderer esterni: una chat box isolata e' una risposta
207
+ // completa, mentre il REPL usa renderAssistantSegment per la gerarchia live.
208
+ export function renderChatBox(text, theme, columns, imageCtx) {
209
+ return renderAssistantSegment(text, theme, columns, imageCtx, "only", 1) +
210
+ renderAssistantTurnEnd(theme, 1);
211
+ }
212
+ // Colora la notazione Danilov nelle righe di chat dando un RUOLO CROMATICO
213
+ // distinto a ciascun pezzo della sintassi, cosi' a colpo d'occhio si separano
214
+ // marcatore, identificatore, contenuto e relazioni:
215
+ // INDICE/DEFINIZIONI/RELAZIONI/OUTPUT -> keyword (accent bold)
216
+ // @tag -> danTag (acciaio-azzurro), il marcatore @ in danPunct
217
+ // [id] -> parentesi in danPunct, id in danId (ciano-acciaio)
218
+ // : / = / · | -> danPunct (punteggiatura tenue)
219
+ // valore dopo : o = -> danValue (salvia) finche' non c'e' altra punteggiatura
220
+ // src -> tgt -> freccia in danArrow (blu freddo)
221
+ // 0x.. e numeri -> danNum (ottone)
222
+ // Tnn (id stanza) -> danId
223
+ // Procede da SINISTRA a DESTRA su una riga PLAIN, costruendo l'output a pezzi:
224
+ // cosi' non ricolora segmenti gia' processati (niente regex su testo con ANSI).
225
+ // True se la riga e' notazione Danilov (da colorare con highlightDanilov):
226
+ // una riga di SEZIONE (INDICE/DEFINIZIONI/RELAZIONI/OUTPUT), una DEFINIZIONE
227
+ // (@tag...) o una RELAZIONE (contiene la freccia ->). Cosi' markdown.ts puo'
228
+ // dipingere il formato alla fonte invece di lasciarlo a renderInline (che lo
229
+ // avvolgerebbe in ANSI, disattivando highlightDanilov a valle).
230
+ export function isDanilovLine(line) {
231
+ const body = line.replace(/^\s+/, "");
232
+ if (!body)
233
+ return false;
234
+ if (/^(INDICE|DEFINIZIONI|RELAZIONI|OUTPUT)\b/.test(body))
235
+ return true;
236
+ if (/^@[A-Za-z_]\w*(\[[^\]]*\])?\s*[:\[]/.test(body))
237
+ return true; // @tag: o @tag[
238
+ if (/\s->\s/.test(body))
239
+ return true; // relazione src -> tgt
240
+ return false;
241
+ }
242
+ export function highlightDanilov(line, theme) {
243
+ if (line.includes("\x1b"))
244
+ return line;
245
+ // Indentazione iniziale conservata.
246
+ const lead = /^(\s*)/.exec(line)?.[1] ?? "";
247
+ let body = line.slice(lead.length);
248
+ // 1) Riga di SEZIONE: INDICE/DEFINIZIONI/RELAZIONI/OUTPUT (eventuale ":" finale).
249
+ const sec = /^(INDICE|DEFINIZIONI|RELAZIONI|OUTPUT)(:?)(.*)$/.exec(body);
250
+ if (sec) {
251
+ const kw = `\x1b[1m${theme.accent(sec[1])}\x1b[22m`;
252
+ const colon = sec[2] ? theme.danPunct(sec[2]) : "";
253
+ return lead + kw + colon + colorizeDanilovRest(sec[3] ?? "", theme);
254
+ }
255
+ return lead + colorizeDanilovRest(body, theme);
256
+ }
257
+ // Colora il "corpo" di una riga Danilov pezzo per pezzo. Riconosce i token in
258
+ // ordine di priorita' e avanza sull'input; il testo non riconosciuto passa
259
+ // inalterato (riga di prosa normale resta normale).
260
+ function colorizeDanilovRest(input, theme) {
261
+ let s = input;
262
+ let out = "";
263
+ // true subito dopo ":" o "=" finche' non si incontra altra punteggiatura:
264
+ // segna il "valore/contenuto" da dipingere in danValue.
265
+ let inValue = false;
266
+ while (s.length > 0) {
267
+ // @tag (con eventuale [id]) — il marcatore di definizione/relazione.
268
+ let m = /^@([A-Za-z_]\w*)(\[[^\]]*\])?/.exec(s);
269
+ if (m) {
270
+ out += theme.danPunct("@") + theme.danTag(m[1]);
271
+ if (m[2]) {
272
+ const id = m[2].slice(1, -1);
273
+ out += theme.danPunct("[") + theme.danId(id) + theme.danPunct("]");
274
+ }
275
+ s = s.slice(m[0].length);
276
+ inValue = false;
277
+ continue;
278
+ }
279
+ // Freccia di relazione src -> tgt.
280
+ m = /^\s*->\s*/.exec(s);
281
+ if (m) {
282
+ out += theme.danArrow(m[0]);
283
+ s = s.slice(m[0].length);
284
+ inValue = false;
285
+ continue;
286
+ }
287
+ // Maschera bit 0x.. o numero isolato.
288
+ m = /^0x[0-9a-fA-F]+|^\b\d+\b/.exec(s);
289
+ if (m) {
290
+ out += theme.danNum(m[0]);
291
+ s = s.slice(m[0].length);
292
+ continue;
293
+ }
294
+ // Id stanza Tnn.
295
+ m = /^T\d{2}\b/.exec(s);
296
+ if (m) {
297
+ out += theme.danId(m[0]);
298
+ s = s.slice(m[0].length);
299
+ continue;
300
+ }
301
+ // Punteggiatura della notazione: avvia (o chiude) la modalita' valore.
302
+ const ch = s[0];
303
+ if (ch === ":" || ch === "=") {
304
+ out += theme.danPunct(ch);
305
+ s = s.slice(1);
306
+ inValue = true;
307
+ continue;
308
+ }
309
+ if (ch === "·" || ch === "|" || ch === "," || ch === ";" || ch === "[" || ch === "]") {
310
+ out += theme.danPunct(ch);
311
+ s = s.slice(1);
312
+ inValue = false;
313
+ continue;
314
+ }
315
+ // Segmento di testo fino al prossimo token significativo.
316
+ m = /^[^@\-:=·|,;\[\]]+|^-(?!>)/.exec(s);
317
+ const chunk = m ? m[0] : ch;
318
+ out += inValue ? theme.danValue(chunk) : chunk;
319
+ s = s.slice(chunk.length);
320
+ }
321
+ return out;
322
+ }
323
+ export function renderUserMessage(text, theme, columns, label) {
324
+ const width = Math.max(1, columns - 2);
325
+ const source = plainText(text.trimEnd());
326
+ const highlight = createAttachmentHighlighter(source, theme.attachmentRef);
327
+ const lines = wrapTerminalText(source, Math.max(2, width - 3), true);
328
+ const rows = lines.length ? lines : [""];
329
+ const receipt = label ? wrapTerminalText(label, Math.max(2, width - 1), true).map(line => " " + theme.accent(line)).join("\n") + "\n" : "";
330
+ let offset = 0;
331
+ return "\n" + receipt + rows.map((line, index) => {
332
+ const start = source.indexOf(line, offset);
333
+ const highlighted = highlight(line, start);
334
+ offset = start + line.length;
335
+ return " " + theme.chatText(padLine((index === 0 ? "› " : " ") + highlighted, width));
336
+ }).join("\n") + "\n";
337
+ }
338
+ // Badge di un'immagine allegata, stile artifact Claude Code: un riquadro con
339
+ // RIEMPIMENTO BIANCO e TESTO NERO, cosi' si distingue subito come oggetto/
340
+ // artifact (non testo normale). Una riga sola: " img #N · label ".
341
+ export function renderImageArtifact(ref, label) {
342
+ const WHITE_BG = "\x1b[47m";
343
+ const BLACK_FG = "\x1b[30m";
344
+ const inner = ` ${ref} · ${label} `;
345
+ const body = `${WHITE_BG}${BLACK_FG}\x1b[1m${inner}${RESET}`;
346
+ return ` ${body}`;
347
+ }
348
+ export function renderVascendStatus(state, theme, columns) {
349
+ // Il riquadro compare SOLO se c'e' almeno un castello (avviato con /castle o
350
+ // pianificato dall'agente con vascend_plan). Niente box automatico sui prompt.
351
+ if (!state.kingdom.castles.length)
352
+ return "";
353
+ const width = clamp(columns - 8, 40, 96);
354
+ const contentWidth = width - 4;
355
+ const title = state.mode === "sticky" ? " lista attività · live " : " lista attività ";
356
+ const top = theme.chatBorder(" ╭─") + theme.silver(`\x1b[1m${title}${RESET}`) +
357
+ theme.chatBorder("─".repeat(Math.max(0, contentWidth + 1 - visibleLength(title))) + "╮");
358
+ const goal = truncate(state.goal ?? "nessun obiettivo", contentWidth - 6);
359
+ const line = (text, color = theme.dark) => theme.chatBorder(" │ ") + color(padRight(truncate(text, contentWidth), contentWidth)) + theme.chatBorder(" │");
360
+ const castles = state.kingdom.castles;
361
+ const totalRooms = castles.reduce((n, c) => n + c.tasks.length, 0);
362
+ // COMPATTO se il regno e' grande (multi-castello o tante stanze): solo sommari
363
+ // per castello, il dettaglio si apre con Ctrl+G. Cosi' il box non esplode.
364
+ const compact = castles.length > 1 || totalRooms > 6;
365
+ const rows = [line(`obiettivo ${goal}`)];
366
+ if (!castles.length)
367
+ rows.push(line("nessuna attività presente"));
368
+ for (const castle of castles) {
369
+ const done = castle.tasks.filter((t) => t.status === "completed").length;
370
+ const failed = castle.tasks.filter((t) => t.status === "failed").length;
371
+ if (compact) {
372
+ const bar = `${done}/${castle.tasks.length} (${pctDone(castle.tasks)}%)${failed ? ` · ${failed} fallit${failed === 1 ? "a" : "e"}` : ""}`;
373
+ // Indicatore di stato del vassallo: glyph dedicato per attivo/in attesa/
374
+ // completato/errore, colore coerente con lo stato aggregato.
375
+ const vstatus = castleStatus(castle.tasks);
376
+ const color = vassalStatusColor(vstatus, theme);
377
+ rows.push(line(`${vassalGlyph(vstatus)} ${castle.slug} · ${truncate(castle.title, contentWidth - 18)} ${bar}`, color));
378
+ }
379
+ else {
380
+ // Intestazione del castello (titolo) + stanze con numero in BOLD, glyph di
381
+ // stato colorato, token se noti e sotto-riga delle skill abbinate.
382
+ if ((castle.title || castle.slug) && castles.length > 0) {
383
+ rows.push(line(`◆ ${castle.title || castle.slug}`, theme.silver));
384
+ }
385
+ for (const task of castle.tasks) {
386
+ const color = taskStatusColor(task.status, theme);
387
+ const tok = task.tokens !== undefined ? theme.dark(` ↓${fmtTokShort(task.tokens)}`) : "";
388
+ const content = color(`${statusGlyph(task.status)} \x1b[1m${task.id}\x1b[22m`) +
389
+ " " + theme.silver(truncate(task.title, contentWidth - 14)) + tok;
390
+ rows.push(theme.chatBorder(" │ ") + padRight(content, contentWidth) + theme.chatBorder(" │"));
391
+ const sub = [];
392
+ if (task.skills?.length)
393
+ sub.push(`@skill ${task.skills.join(", ")}`);
394
+ if (task.notes?.length)
395
+ sub.push(`${task.notes.length} note`);
396
+ if (sub.length) {
397
+ rows.push(theme.chatBorder(" │ ") + padRight(theme.dark(` └ ${truncate(sub.join(" · "), contentWidth - 6)}`), contentWidth) + theme.chatBorder(" │"));
398
+ }
399
+ }
400
+ }
401
+ }
402
+ if (compact)
403
+ rows.push(line("Ctrl+G per le attività", theme.dark));
404
+ const bottom = theme.chatBorder(" ╰" + "─".repeat(contentWidth + 2) + "╯");
405
+ return ["", top, ...rows, bottom].join("\n") + "\n";
406
+ }
407
+ // Status line del REGNO sotto la input bar: castelli e stanze in sintesi, con
408
+ // l'hint del comando di gestione. "" se non c'e' nessun castello (niente rumore
409
+ // sui prompt banali). Pensata come riga extra del footer ancorato in fondo.
410
+ export function vascendFooterLine(state, theme, columns) {
411
+ const castles = state.kingdom.castles;
412
+ if (!castles.length)
413
+ return "";
414
+ const width = inputWidth(columns) + 2;
415
+ const allTasks = castles.flatMap((c) => c.tasks);
416
+ const done = allTasks.filter((t) => t.status === "completed").length;
417
+ const running = allTasks.filter((t) => t.status === "running").length;
418
+ const failed = allTasks.filter((t) => t.status === "failed").length;
419
+ const head = castles.length > 1
420
+ ? `lista attività · ${castles.length} gruppi`
421
+ : `lista «${truncate(castles[0]?.title ?? "", 28)}»`;
422
+ const bits = [`${done}/${allTasks.length} attività (${pctDone(allTasks)}%)`];
423
+ if (running)
424
+ bits.push(`${running} in corso`);
425
+ if (failed)
426
+ bits.push(`${failed} fallit${failed === 1 ? "a" : "e"}`);
427
+ const body = `◆ ${head} · ${bits.join(" · ")} · Ctrl+G dettagli`;
428
+ const summary = theme.silver(` ${truncate(body, width)}`);
429
+ // Con piu' vassalli (multi-castello) aggiungo una riga di indicatori di stato
430
+ // per-vassallo: glyph colorato + slug, cosi' nella vista collassata si vede a
431
+ // colpo d'occhio chi e' attivo/in attesa/completato/in errore.
432
+ if (castles.length < 2)
433
+ return summary;
434
+ const indicators = castles.map((c) => vassalIndicator(c, theme)).join(theme.dark(" "));
435
+ return `${summary}\n${theme.dark(" ")}${truncate(indicators, width + 32)}`;
436
+ }
437
+ // Avviso di chat per gli eventi del regno (stanza avviata/illuminata/fallita):
438
+ // riga marcata, leggibile, in notazione breve. La NASCITA del castello ha invece
439
+ // un design dedicato (renderCastleBorn), per distinguerla nettamente.
440
+ export function renderVascendNotice(kind, text, theme) {
441
+ // Colori DISTINTI per stato della stanza: avviata=accent (cyan), illuminata=
442
+ // giallo (luce accesa), fallita=rosso; nascita castello/regno=accent.
443
+ if (kind === "fail")
444
+ return theme.red(`\n ✗ ${text}\n`);
445
+ if (kind === "done")
446
+ return theme.yellow(`\n ✦ ${text}\n`);
447
+ if (kind === "start")
448
+ return theme.accent(`\n ▸ ${text}\n`);
449
+ const glyph = "◆";
450
+ return theme.accent(`\n ${glyph} ${text}\n`);
451
+ }
452
+ // Nascita di un castello: milestone compatta. Il dettaglio completo delle
453
+ // stanze e' sempre raggiungibile con Ctrl+G, quindi non viene duplicato nella
454
+ // chat consumando una riga per task.
455
+ export function renderCastleBorn(opts, theme, columns) {
456
+ const isKingdom = opts.kingdomCount > 1;
457
+ const available = Math.max(30, columns - 34);
458
+ const title = truncate(opts.title || opts.slug, available);
459
+ const kingdom = isKingdom ? theme.dark(` · ${opts.kingdomCount} gruppi`) : "";
460
+ const slug = opts.slug && opts.slug !== "master" ? theme.dark(` · ${opts.slug}`) : "";
461
+ return "\n " + theme.accent("◆ ") + theme.silver(`\x1b[1m${title}${RESET}`) +
462
+ theme.dark(` · ${opts.tasks.length} attività`) + slug + kingdom + theme.dark(" · Ctrl+G dettagli\n");
463
+ }
464
+ // Avviso di cambio modello: badge UI dedicato, scritto SOLO nello scrollback
465
+ // (ctl.write), MAI nella sessione -> non entra nel context inviato al modello.
466
+ // Mostra il modello nuovo + provider; se warn e' presente, una riga gialla.
467
+ export function renderModelChanged(opts, theme) {
468
+ const badge = theme.accent(`\x1b[1m ⇄ modello${RESET}`);
469
+ const name = theme.silver(`\x1b[1m${opts.model}${RESET}`);
470
+ const prov = opts.provider ? theme.dim(` · ${opts.provider}`) : "";
471
+ const head = `${badge} ${name}${prov}`;
472
+ if (opts.warn) {
473
+ return `\n${head}\n` + theme.yellow(` ! ${opts.warn}`) + "\n";
474
+ }
475
+ return `\n${head}\n`;
476
+ }
477
+ // Blocco prompt unificato: una riga di meta (modello/contesto/cache) e la barra
478
+ // "input", scritte INSIEME subito sopra il cursore. Il numero di righe e' fisso
479
+ // e noto (vedi PROMPT_BLOCK_LINES) cosi' la TUI puo' cancellarlo per intero
480
+ // all'invio, senza lasciare barre duplicate sullo schermo.
481
+ export const PROMPT_BLOCK_LINES = 3; // spacer + footer + barra
482
+ export function renderInputPrompt(config, meta, theme, columns) {
483
+ const footer = inputFooterLine(config, meta, theme, columns);
484
+ const bar = inputBarLine(theme, columns);
485
+ return ["", footer, bar].join("\n") + "\n";
486
+ }
487
+ // Righe del footer per la barra di input ANCORATA in fondo (dall'alto in
488
+ // basso): meta (modello/contesto/cache) + barra "input". Niente spacer ne'
489
+ // newline: le posiziona il chiamante a righe assolute.
490
+ export function inputFooterRows(config, meta, theme, columns) {
491
+ return [inputFooterLine(config, meta, theme, columns), inputBarLine(theme, columns)];
492
+ }
493
+ function inputBarLine(theme, columns) {
494
+ const width = inputWidth(columns);
495
+ const title = " input ";
496
+ const left = Math.floor(Math.max(0, width - visibleLength(title)) / 2);
497
+ const right = Math.max(0, width - visibleLength(title) - left);
498
+ return theme.chatBorder(` ${"─".repeat(left)}`) +
499
+ theme.silver(`\x1b[1m${title}${RESET}`) +
500
+ theme.chatBorder("─".repeat(right));
501
+ }
502
+ function inputFooterLine(config, meta, theme, columns) {
503
+ const width = inputWidth(columns) + 2;
504
+ const provider = meta.provider ?? providerFromModel(config.model);
505
+ const parts = [config.model, provider];
506
+ if (config.effort)
507
+ parts.push(`effort ${config.effort}`);
508
+ if (meta.contextTokens !== undefined && meta.contextWindow) {
509
+ const pct = Math.round((meta.contextTokens / meta.contextWindow) * 100);
510
+ parts.push(`ctx stimato ~${formatTokens(meta.contextTokens)}/${formatTokens(meta.contextWindow)} (${pct}%)`);
511
+ }
512
+ if (meta.cacheHitRate !== undefined) {
513
+ parts.push(`cache ${Math.round(meta.cacheHitRate * 100)}%`);
514
+ }
515
+ return theme.dark(` ${truncate(parts.join(" · "), width)}`);
516
+ }
517
+ // Mantenuto per compatibilita': singola riga di meta con "\n" finale.
518
+ export function renderInputFooter(config, meta, theme, columns) {
519
+ return inputFooterLine(config, meta, theme, columns) + "\n";
520
+ }
521
+ function formatTokens(n) {
522
+ if (n >= 1_000_000) {
523
+ const m = n / 1_000_000;
524
+ return `${Number.isInteger(m) ? m : m.toFixed(1)}M`;
525
+ }
526
+ return n >= 1000 ? `${Math.round(n / 1000)}k` : `${n}`;
527
+ }
528
+ function taskStatusColor(status, theme) {
529
+ if (status === "completed")
530
+ return theme.green;
531
+ if (status === "running")
532
+ return theme.yellow;
533
+ if (status === "failed")
534
+ return theme.red;
535
+ return theme.dark;
536
+ }
537
+ function statusGlyph(status) {
538
+ if (status === "completed")
539
+ return "✦";
540
+ if (status === "running")
541
+ return "▸";
542
+ if (status === "failed")
543
+ return "✗";
544
+ return "·";
545
+ }
546
+ export function castleStatus(tasks) {
547
+ if (tasks.some((t) => t.status === "failed"))
548
+ return "errore";
549
+ if (tasks.some((t) => t.status === "running"))
550
+ return "attivo";
551
+ if (tasks.length > 0 && tasks.every((t) => t.status === "completed"))
552
+ return "completato";
553
+ return "in_attesa";
554
+ }
555
+ // Indicatore visivo (glyph + colore) per lo stato di un vassallo nella vista
556
+ // collassata. Riusa la palette dello stato stanza per coerenza visiva.
557
+ function vassalGlyph(status) {
558
+ if (status === "completato")
559
+ return "✦";
560
+ if (status === "attivo")
561
+ return "▸";
562
+ if (status === "errore")
563
+ return "✗";
564
+ return "·";
565
+ }
566
+ function vassalStatusColor(status, theme) {
567
+ if (status === "completato")
568
+ return theme.green;
569
+ if (status === "attivo")
570
+ return theme.yellow;
571
+ if (status === "errore")
572
+ return theme.red;
573
+ return theme.dark;
574
+ }
575
+ // Indicatore compatto per un vassallo: glyph colorato + nome breve. Usato nella
576
+ // vista collassata del regno per mostrare a colpo d'occhio lo stato di ciascuno.
577
+ export function vassalIndicator(castle, theme, nameWidth = 14) {
578
+ const status = castleStatus(castle.tasks);
579
+ const color = vassalStatusColor(status, theme);
580
+ const pct = pctDone(castle.tasks);
581
+ // La percentuale e' muta (in attesa) finche' non c'e' progresso reale, cosi'
582
+ // la vista collassata non si riempie di "0%" su vassalli ancora fermi.
583
+ const suffix = status === "in_attesa" && pct === 0 ? "" : ` ${pct}%`;
584
+ return color(`${vassalGlyph(status)} ${truncate(castle.slug, nameWidth)}${suffix}`);
585
+ }
586
+ // Percentuale di avanzamento (stanze completate sul totale). 0 se non ci sono
587
+ // stanze, cosi' il chiamante puo' decidere se mostrarla o tenerla muta.
588
+ export function pctDone(tasks) {
589
+ if (!tasks.length)
590
+ return 0;
591
+ const done = tasks.filter((t) => t.status === "completed").length;
592
+ return Math.round((done / tasks.length) * 100);
593
+ }
594
+ function fmtTokShort(n) {
595
+ if (n >= 1_000_000) {
596
+ const v = n / 1_000_000;
597
+ return `${v >= 10 ? Math.round(v) : v.toFixed(1)}M`;
598
+ }
599
+ if (n >= 1000) {
600
+ const v = n / 1000;
601
+ return `${v >= 10 ? Math.round(v) : v.toFixed(1)}k`;
602
+ }
603
+ return `${n}`;
604
+ }
605
+ function inputWidth(columns) {
606
+ return clamp(columns - 8, 34, 78);
607
+ }
608
+ // Hyperlink OSC 8: rende `label` cliccabile verso `url` nei terminali che lo
609
+ // supportano (parita' Pi clickable file hyperlinks). Negli altri appare come
610
+ // testo normale.
611
+ export function osc8(url, label) {
612
+ return `\x1b]8;;${url}\x1b\\${label}\x1b]8;;\x1b\\`;
613
+ }
614
+ // Costruisce un URL file:// assoluto (forward slash, prefisso / su Windows).
615
+ export function fileUrl(absPath) {
616
+ const norm = absPath.replace(/\\/g, "/");
617
+ return norm.startsWith("/") ? `file://${norm}` : `file:///${norm}`;
618
+ }
619
+ // Avvolge un path in un hyperlink OSC 8 file://, mantenendo il testo originale.
620
+ // Molti terminali sottolineano gli hyperlink OSC 8 (i "trattini sotto al
621
+ // path"): forziamo no-underline (SGR 24) attorno alla label e lo ripristiniamo
622
+ // (SGR 4) appena prima della chiusura del link, cosi' il path resta cliccabile
623
+ // ma non appare sottolineato.
624
+ export function fileLink(absPath, label) {
625
+ const text = label ?? absPath;
626
+ return osc8(fileUrl(absPath), `\x1b[24m${text}\x1b[24m`);
627
+ }
628
+ // Hint dei comandi reso come "tasti": ogni voce in un riquadro chiaro
629
+ // [ /help ] separato da una sottile barra verticale, cosi' la riga finale
630
+ // della card sembra una pulsantiera invece di testo piatto.
631
+ function commandsHint(theme, width) {
632
+ const key = (s) => theme.silver(`\x1b[1m${s}${RESET}`);
633
+ // Forma piena se c'e' spazio, altrimenti solo i tasti separati dal punto
634
+ // mediano. Restiamo entro `width` colonne visibili (allineamento del bordo).
635
+ const full = `${key("Ctrl+R")}${theme.dark(" revisione · ")}${key("/help")}${theme.dark(" guida")}`;
636
+ const compact = `${key("Ctrl+R")}${theme.dark(" revisione · ")}${key("/help")}`;
637
+ return visibleLength(full) <= width ? full : compact;
638
+ }
639
+ function renderStartupCard(config, meta, theme, maxWidth) {
640
+ // Design APERTO, stessa lingua dell'avvio dev di Horizon: wordmark col rombo
641
+ // del marchio, sottotitolo in didascalia, righe dato allineate con marcatore
642
+ // pieno, pulsantiera dei comandi in coda. Il box che c'era prima appesantiva
643
+ // il banner e stringeva i valori dentro la cornice.
644
+ const width = Math.max(26, maxWidth);
645
+ const rows = config.ui.showMeta
646
+ ? [
647
+ ["modello", config.model, true],
648
+ ["provider", meta.provider ?? providerFromModel(config.model), false],
649
+ ["sessione", sessionLabel(meta), false],
650
+ ["cwd", compactPath(config.cwd, Math.min(46, width - 12)), false],
651
+ ]
652
+ : [];
653
+ const labelWidth = rows.length ? Math.max(...rows.map(([label]) => label.length)) : 0;
654
+ const out = [];
655
+ const brand = theme.silver(`\x1b[1m◆ ${truncate(config.ui.title, Math.max(8, width - 4))}${RESET}`);
656
+ const subtitle = config.ui.subtitle
657
+ ? theme.dark(" · ") + theme.dark(`\x1b[3m${truncate(config.ui.subtitle, Math.max(8, width - 2))}\x1b[23m`)
658
+ : "";
659
+ out.push(visibleLength(brand + subtitle) <= width + 6 ? brand + subtitle : brand);
660
+ if (rows.length) {
661
+ out.push("");
662
+ for (const [label, value, emphasis] of rows) {
663
+ const testo = truncate(value, Math.max(10, width - labelWidth - 4));
664
+ const valore = emphasis ? theme.accent(`\x1b[1m${testo}${RESET}`) : theme.silver(testo);
665
+ out.push(theme.accent("▪ ") + theme.dark(padRight(label, labelWidth)) + " " + valore);
666
+ }
667
+ }
668
+ out.push("");
669
+ out.push(commandsHint(theme, width));
670
+ return out;
671
+ }
672
+ // Stessa taglia dell'avvio dev di Horizon (scripts/dev.js: 30x14) quando lo
673
+ // spazio c'e': il marchio deve essere IDENTICO nei due avvii.
674
+ function logoSize(columns) {
675
+ if (columns >= 92)
676
+ return { columns: 30, rows: 14, samples: 4, ansi: true };
677
+ if (columns >= 64)
678
+ return { columns: 20, rows: 10, samples: 4, ansi: true };
679
+ return { columns: 16, rows: 8, samples: 4, ansi: true };
680
+ }
681
+ function beside(left, right, gap) {
682
+ const width = Math.max(0, ...left.map(visibleLength));
683
+ const rows = Math.max(left.length, right.length);
684
+ const out = [];
685
+ for (let i = 0; i < rows; i += 1) {
686
+ const l = left[i] ?? "";
687
+ const r = right[i] ?? "";
688
+ out.push(`${padRight(l, width)}${" ".repeat(gap)}${r}`.replace(/\s+$/g, ""));
689
+ }
690
+ return out;
691
+ }
692
+ function stack(logo, card) {
693
+ if (!logo.length)
694
+ return card.map((line) => ` ${line}`);
695
+ return [...logo, "", ...card.map((line) => ` ${line}`)];
696
+ }
697
+ function providerFromModel(model) {
698
+ const index = model.indexOf("/");
699
+ return index > 0 ? model.slice(0, index) : "auto";
700
+ }
701
+ function sessionLabel(meta) {
702
+ const id = meta.sessionId ?? "nuova";
703
+ const nodes = meta.sessionNodes ?? 0;
704
+ const head = meta.headId ? ` · head ${meta.headId.slice(0, 8)}` : "";
705
+ return `${id} · ${nodes} nodi${head}`;
706
+ }
707
+ function compactPath(path, max) {
708
+ if (path.length <= max)
709
+ return path;
710
+ const parts = path.split(/[\\/]/).filter(Boolean);
711
+ const tail = parts.slice(-2).join("/");
712
+ const prefix = path.startsWith("\\\\") ? "\\\\" : path[1] === ":" ? `${path.slice(0, 2)}/` : ".../";
713
+ const compact = `${prefix}.../${tail}`;
714
+ return compact.length <= max ? compact : `.../${tail}`.slice(0, max);
715
+ }
716
+ function truncate(value, max) {
717
+ if (value.length <= max)
718
+ return value;
719
+ if (max <= 1)
720
+ return value.slice(0, max);
721
+ return `${value.slice(0, max - 1)}…`;
722
+ }
723
+ function wrapText(text, width) {
724
+ return text.split("\n").flatMap((line) => wrapLine(line, width));
725
+ }
726
+ function wrapLine(line, width) {
727
+ if (!line)
728
+ return [""];
729
+ const out = [];
730
+ let rest = line;
731
+ while (visibleLength(rest) > width) {
732
+ const slice = rest.slice(0, width + 1);
733
+ const breakAt = Math.max(slice.lastIndexOf(" "), slice.lastIndexOf("\t"));
734
+ const cut = breakAt > Math.floor(width * 0.55) ? breakAt : width;
735
+ out.push(rest.slice(0, cut).trimEnd());
736
+ rest = rest.slice(cut).trimStart();
737
+ }
738
+ out.push(rest);
739
+ return out;
740
+ }
741
+ function clamp(value, min, max) {
742
+ return Math.max(min, Math.min(max, value));
743
+ }
744
+ function padRight(value, width) {
745
+ const pad = width - visibleLength(value);
746
+ return pad > 0 ? `${value}${" ".repeat(pad)}` : value;
747
+ }
748
+ export function visibleLength(value) {
749
+ return value.replace(/\x1b\[[0-9;]*m/g, "").length;
750
+ }