@vascend/talos 0.1.4 → 0.1.6

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 (251) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +37 -12
  3. package/dist/core/agent.d.ts +16 -3
  4. package/dist/core/agent.js +170 -70
  5. package/dist/core/background.d.ts +10 -3
  6. package/dist/core/background.js +55 -9
  7. package/dist/core/bootstrap.js +1 -0
  8. package/dist/core/compaction.js +1 -1
  9. package/dist/core/config-storage.d.ts +5 -0
  10. package/dist/core/config-storage.js +83 -0
  11. package/dist/core/config.d.ts +11 -2
  12. package/dist/core/config.js +24 -8
  13. package/dist/core/context-ledger.js +2 -1
  14. package/dist/core/context-policy.d.ts +1 -1
  15. package/dist/core/context-policy.js +10 -7
  16. package/dist/core/credentials.js +29 -30
  17. package/dist/core/effort.d.ts +1 -1
  18. package/dist/core/effort.js +5 -1
  19. package/dist/core/execution-guidance.d.ts +1 -1
  20. package/dist/core/execution-guidance.js +2 -0
  21. package/dist/core/file-coordination.d.ts +3 -0
  22. package/dist/core/file-coordination.js +28 -0
  23. package/dist/core/file-lease-store.d.ts +3 -0
  24. package/dist/core/file-lease-store.js +28 -12
  25. package/dist/core/fusion.d.ts +24 -0
  26. package/dist/core/fusion.js +80 -0
  27. package/dist/core/model-limits.d.ts +1 -0
  28. package/dist/core/model-limits.js +5 -1
  29. package/dist/core/permission-queue.js +15 -15
  30. package/dist/core/request-plan.js +4 -3
  31. package/dist/core/session-naming.d.ts +3 -0
  32. package/dist/core/session-naming.js +47 -0
  33. package/dist/core/session-usage.js +4 -1
  34. package/dist/core/session.d.ts +5 -2
  35. package/dist/core/session.js +22 -7
  36. package/dist/core/settings.d.ts +2 -0
  37. package/dist/core/settings.js +5 -16
  38. package/dist/core/step-policy.d.ts +18 -0
  39. package/dist/core/step-policy.js +6 -0
  40. package/dist/core/storage-health.d.ts +24 -0
  41. package/dist/core/storage-health.js +72 -0
  42. package/dist/core/tool-artifacts.d.ts +2 -0
  43. package/dist/core/tool-artifacts.js +21 -3
  44. package/dist/core/tool-result-index.js +29 -33
  45. package/dist/core/tool-result-store.js +6 -5
  46. package/dist/core/types.d.ts +26 -2
  47. package/dist/core/user-inbox.d.ts +9 -0
  48. package/dist/core/user-inbox.js +22 -3
  49. package/dist/core/user-input.js +1 -1
  50. package/dist/fusion/autopilot-fast.d.ts +59 -0
  51. package/dist/fusion/autopilot-fast.js +213 -0
  52. package/dist/fusion/autopilot.d.ts +75 -0
  53. package/dist/fusion/autopilot.js +308 -0
  54. package/dist/fusion/focus.d.ts +30 -0
  55. package/dist/fusion/focus.js +122 -0
  56. package/dist/fusion/gate.d.ts +30 -0
  57. package/dist/fusion/gate.js +95 -0
  58. package/dist/fusion/jev.d.ts +71 -0
  59. package/dist/fusion/jev.js +164 -0
  60. package/dist/fusion/receipts.d.ts +25 -0
  61. package/dist/fusion/receipts.js +34 -0
  62. package/dist/fusion/route.d.ts +25 -0
  63. package/dist/fusion/route.js +84 -0
  64. package/dist/fusion/run.d.ts +4 -0
  65. package/dist/fusion/run.js +186 -0
  66. package/dist/horizon/engine.d.ts +20 -1
  67. package/dist/horizon/engine.js +57 -0
  68. package/dist/horizon/index.d.ts +2 -2
  69. package/dist/horizon/index.js +1 -1
  70. package/dist/horizon/types.d.ts +29 -0
  71. package/dist/index.js +12 -0
  72. package/dist/providers/anthropic-oauth.d.ts +8 -3
  73. package/dist/providers/anthropic-oauth.js +13 -10
  74. package/dist/providers/anthropic.d.ts +9 -2
  75. package/dist/providers/anthropic.js +64 -21
  76. package/dist/providers/cli-models.d.ts +30 -0
  77. package/dist/providers/cli-models.js +194 -0
  78. package/dist/providers/model-effort.js +1 -1
  79. package/dist/providers/openai-oauth.d.ts +8 -3
  80. package/dist/providers/openai-oauth.js +9 -3
  81. package/dist/providers/openrouter-manual-fallback.d.ts +3 -1
  82. package/dist/providers/openrouter-manual-fallback.js +20 -20
  83. package/dist/providers/registry.js +3 -2
  84. package/dist/providers/retry.d.ts +3 -0
  85. package/dist/providers/retry.js +44 -1
  86. package/dist/remote/cifratura.d.ts +12 -0
  87. package/dist/remote/cifratura.js +66 -0
  88. package/dist/remote/index.d.ts +15 -0
  89. package/dist/remote/index.js +65 -0
  90. package/dist/remote/relay-client.d.ts +58 -0
  91. package/dist/remote/relay-client.js +315 -0
  92. package/dist/remote/sessione-remota.d.ts +60 -0
  93. package/dist/remote/sessione-remota.js +266 -0
  94. package/dist/sdk.d.ts +7 -2
  95. package/dist/sdk.js +3 -1
  96. package/dist/tools/builtin.js +41 -20
  97. package/dist/tools/capabilities.d.ts +3 -1
  98. package/dist/tools/capabilities.js +5 -2
  99. package/dist/tools/computer-use.js +2 -1
  100. package/dist/tools/pagination.js +1 -1
  101. package/dist/tools/program.js +34 -21
  102. package/dist/tools/team-tools.js +11 -6
  103. package/dist/tui/account-entry.js +14 -9
  104. package/dist/tui/agent-label.d.ts +2 -0
  105. package/dist/tui/agent-label.js +5 -0
  106. package/dist/tui/agent-sessions-screen.d.ts +35 -0
  107. package/dist/tui/agent-sessions-screen.js +178 -0
  108. package/dist/tui/agent-sessions.d.ts +53 -0
  109. package/dist/tui/agent-sessions.js +236 -0
  110. package/dist/tui/bottom-input.d.ts +57 -2
  111. package/dist/tui/bottom-input.js +449 -54
  112. package/dist/tui/branding.js +8 -4
  113. package/dist/tui/capability-lists.d.ts +10 -0
  114. package/dist/tui/capability-lists.js +41 -0
  115. package/dist/tui/command-output-registry.js +4 -0
  116. package/dist/tui/command-transcript.d.ts +1 -1
  117. package/dist/tui/command-transcript.js +17 -21
  118. package/dist/tui/commands.js +6 -2
  119. package/dist/tui/compaction-status.d.ts +7 -0
  120. package/dist/tui/compaction-status.js +39 -0
  121. package/dist/tui/config-recovery.d.ts +12 -0
  122. package/dist/tui/config-recovery.js +54 -0
  123. package/dist/tui/context-meter.d.ts +3 -0
  124. package/dist/tui/context-meter.js +35 -8
  125. package/dist/tui/diff.d.ts +9 -0
  126. package/dist/tui/diff.js +43 -19
  127. package/dist/tui/fusion-menu.d.ts +5 -0
  128. package/dist/tui/fusion-menu.js +89 -0
  129. package/dist/tui/i18n-catalog.js +351 -2
  130. package/dist/tui/live-command.d.ts +1 -0
  131. package/dist/tui/live-command.js +9 -0
  132. package/dist/tui/live-output.d.ts +1 -0
  133. package/dist/tui/live-output.js +11 -0
  134. package/dist/tui/logo.js +3 -0
  135. package/dist/tui/model-details.js +13 -12
  136. package/dist/tui/model-picker.d.ts +2 -0
  137. package/dist/tui/model-picker.js +59 -0
  138. package/dist/tui/model-selection.js +19 -10
  139. package/dist/tui/native-scrollback.d.ts +2 -1
  140. package/dist/tui/native-scrollback.js +31 -6
  141. package/dist/tui/prompt-queue-store.d.ts +1 -0
  142. package/dist/tui/prompt-queue-store.js +4 -0
  143. package/dist/tui/prompt-queue.d.ts +3 -0
  144. package/dist/tui/prompt-queue.js +19 -1
  145. package/dist/tui/provider-label.d.ts +2 -0
  146. package/dist/tui/provider-label.js +9 -0
  147. package/dist/tui/provider-menu.js +25 -24
  148. package/dist/tui/queue-screen.js +2 -1
  149. package/dist/tui/queued-questions.js +2 -4
  150. package/dist/tui/quick-help.d.ts +1 -1
  151. package/dist/tui/quick-help.js +3 -0
  152. package/dist/tui/remote-command.d.ts +7 -0
  153. package/dist/tui/remote-command.js +79 -0
  154. package/dist/tui/repl.d.ts +11 -2
  155. package/dist/tui/repl.js +457 -327
  156. package/dist/tui/review-store.d.ts +8 -1
  157. package/dist/tui/review-store.js +64 -11
  158. package/dist/tui/review-team.js +4 -3
  159. package/dist/tui/runtime-error.d.ts +2 -0
  160. package/dist/tui/runtime-error.js +26 -0
  161. package/dist/tui/scrollback-archive.d.ts +16 -0
  162. package/dist/tui/scrollback-archive.js +90 -0
  163. package/dist/tui/selector-mouse.d.ts +1 -0
  164. package/dist/tui/selector-mouse.js +1 -1
  165. package/dist/tui/selector.d.ts +11 -0
  166. package/dist/tui/selector.js +28 -4
  167. package/dist/tui/session-controls.d.ts +26 -0
  168. package/dist/tui/session-controls.js +65 -0
  169. package/dist/tui/session-naming-notice.d.ts +14 -0
  170. package/dist/tui/session-naming-notice.js +59 -0
  171. package/dist/tui/session-picker.d.ts +2 -0
  172. package/dist/tui/session-picker.js +32 -0
  173. package/dist/tui/session-replay.d.ts +1 -0
  174. package/dist/tui/session-replay.js +101 -32
  175. package/dist/tui/session-transcript-layout.d.ts +9 -0
  176. package/dist/tui/session-transcript-layout.js +41 -0
  177. package/dist/tui/startup.js +63 -33
  178. package/dist/tui/status-line.d.ts +1 -1
  179. package/dist/tui/status-line.js +20 -12
  180. package/dist/tui/storage-warning.d.ts +2 -0
  181. package/dist/tui/storage-warning.js +35 -0
  182. package/dist/tui/talos-art-data.d.ts +1 -0
  183. package/dist/tui/talos-art-data.js +2 -0
  184. package/dist/tui/talos-art.d.ts +1 -0
  185. package/dist/tui/talos-art.js +9 -0
  186. package/dist/tui/task-labels.d.ts +1 -0
  187. package/dist/tui/task-labels.js +8 -0
  188. package/dist/tui/terminal-text.d.ts +2 -1
  189. package/dist/tui/terminal-text.js +28 -2
  190. package/dist/tui/theme.js +2 -2
  191. package/dist/tui/tool-presentation.d.ts +3 -0
  192. package/dist/tui/tool-presentation.js +98 -0
  193. package/dist/tui/tool-style.js +4 -0
  194. package/dist/tui/transcript-store.d.ts +25 -0
  195. package/dist/tui/transcript-store.js +151 -0
  196. package/dist/tui/usage-footer.d.ts +1 -1
  197. package/dist/tui/usage-footer.js +3 -1
  198. package/dist/tui/vascend-id.d.ts +49 -0
  199. package/dist/tui/vascend-id.js +321 -0
  200. package/dist/tui/web-login-screen.js +4 -3
  201. package/dist/tui/web-login.d.ts +7 -15
  202. package/dist/tui/web-login.js +38 -78
  203. package/dist/tui/worker-transcripts.d.ts +31 -0
  204. package/dist/tui/worker-transcripts.js +91 -0
  205. package/dist/vascend/candidate-collector.d.ts +6 -5
  206. package/dist/vascend/candidate-collector.js +44 -43
  207. package/dist/vascend/context-feedback-loop.js +2 -0
  208. package/dist/vascend/context-lint.js +32 -2
  209. package/dist/vascend/context-market-selection.js +12 -5
  210. package/dist/vascend/context-pack-render.js +10 -4
  211. package/dist/vascend/context-packet-mirrors.d.ts +8 -0
  212. package/dist/vascend/context-packet-mirrors.js +24 -0
  213. package/dist/vascend/context-plan-events.d.ts +4 -0
  214. package/dist/vascend/context-plan-events.js +44 -0
  215. package/dist/vascend/context-snapshot.d.ts +1 -0
  216. package/dist/vascend/context-snapshot.js +8 -5
  217. package/dist/vascend/context-store-reader.d.ts +20 -0
  218. package/dist/vascend/context-store-reader.js +46 -0
  219. package/dist/vascend/context-window-compiler.d.ts +1 -0
  220. package/dist/vascend/context-window-compiler.js +1 -0
  221. package/dist/vascend/file-lock.d.ts +1 -0
  222. package/dist/vascend/file-lock.js +26 -0
  223. package/dist/vascend/granular-kingdom.d.ts +1 -0
  224. package/dist/vascend/granular-kingdom.js +52 -17
  225. package/dist/vascend/kingdom-bus.d.ts +1 -0
  226. package/dist/vascend/kingdom-bus.js +3 -0
  227. package/dist/vascend/kingdom-channel.d.ts +2 -0
  228. package/dist/vascend/kingdom-channel.js +3 -0
  229. package/dist/vascend/kingdom-live.d.ts +32 -4
  230. package/dist/vascend/kingdom-live.js +149 -13
  231. package/dist/vascend/kingdom-run.d.ts +5 -1
  232. package/dist/vascend/kingdom-run.js +6 -2
  233. package/dist/vascend/kingdom.d.ts +3 -0
  234. package/dist/vascend/kingdom.js +22 -11
  235. package/dist/vascend/memory-packet.d.ts +2 -1
  236. package/dist/vascend/memory-packet.js +31 -44
  237. package/dist/vascend/memory-store.d.ts +1 -0
  238. package/dist/vascend/memory-store.js +12 -4
  239. package/dist/vascend/plan-store.d.ts +3 -1
  240. package/dist/vascend/plan-store.js +6 -4
  241. package/dist/vascend/room-memory.d.ts +13 -2
  242. package/dist/vascend/room-memory.js +48 -6
  243. package/dist/vascend/runtime.d.ts +3 -11
  244. package/dist/vascend/runtime.js +11 -123
  245. package/dist/vascend/session-search.js +2 -0
  246. package/dist/vascend/team-session.js +3 -2
  247. package/dist/vascend/vision.d.ts +3 -1
  248. package/dist/vascend/vision.js +6 -4
  249. package/dist/vascend/worker-session.d.ts +20 -0
  250. package/dist/vascend/worker-session.js +83 -0
  251. package/package.json +2 -2
@@ -1,4 +1,5 @@
1
1
  import type { AgentEvent, Message, ToolCallPart } from "../core/types.js";
2
+ import { AgentSessions } from "./agent-sessions.js";
2
3
  export type ReviewState = "running" | "ok" | "error" | "stopped" | "info";
3
4
  export type ReviewKind = "tool" | "file" | "team" | "notice";
4
5
  export interface ReviewChange {
@@ -27,9 +28,14 @@ export declare function reviewTitle(name: string): string;
27
28
  export declare function callTarget(call: ToolCallPart): string;
28
29
  export declare class ReviewStore {
29
30
  private readonly capacity;
31
+ private readonly maxChars;
32
+ readonly sessions: AgentSessions;
30
33
  private items;
34
+ private sizes;
35
+ private retainedChars;
31
36
  private callIndex;
32
37
  private actorText;
38
+ private rawActors;
33
39
  private sequence;
34
40
  private run;
35
41
  private _revision;
@@ -37,7 +43,7 @@ export declare class ReviewStore {
37
43
  private _running;
38
44
  private _label;
39
45
  private _goal;
40
- constructor(capacity?: number);
46
+ constructor(capacity?: number, maxChars?: number);
41
47
  get revision(): number;
42
48
  get evicted(): number;
43
49
  get running(): boolean;
@@ -57,4 +63,5 @@ export declare class ReviewStore {
57
63
  private callId;
58
64
  private notice;
59
65
  private put;
66
+ private remove;
60
67
  }
@@ -1,4 +1,8 @@
1
1
  import { oneLine } from "./terminal-text.js";
2
+ import { AgentSessions } from "./agent-sessions.js";
3
+ import { agentLabel } from "./agent-label.js";
4
+ import { tr } from "./i18n.js";
5
+ import { runtimeError } from "./runtime-error.js";
2
6
  const LABELS = {
3
7
  read_file: "Lettura", list_dir: "Cartella", grep: "Ricerca", glob: "Ricerca file",
4
8
  write_file: "Scrittura", edit_file: "Modifica", bash: "Comando", bash_bg: "Comando in background",
@@ -8,7 +12,17 @@ const LABELS = {
8
12
  vascend_addrooms: "Nuove attività", vascend_status: "Lista attività",
9
13
  kingdom_send: "Messaggio al team", kingdom_inbox: "Messaggi del team", kingdom_wait: "Attesa collaboratori",
10
14
  };
11
- export function reviewTitle(name) { return LABELS[name] ?? name; }
15
+ export function reviewTitle(name) {
16
+ return name === "bash_output" ? tr("Processo in background", "Background process") : LABELS[name] ?? name;
17
+ }
18
+ function textSize(value, seen = new Set()) {
19
+ if (typeof value === "string")
20
+ return value.length;
21
+ if (!value || typeof value !== "object" || seen.has(value))
22
+ return 0;
23
+ seen.add(value);
24
+ return Object.values(value).reduce((size, child) => size + textSize(child, seen), 0);
25
+ }
12
26
  export function callTarget(call) {
13
27
  const i = call.input;
14
28
  return oneLine(String(i["path"] ?? i["command"] ?? i["pattern"] ?? i["query"] ??
@@ -18,9 +32,14 @@ export function callTarget(call) {
18
32
  // della chat viene compattato. Nessuna lettura del filesystem dal renderer.
19
33
  export class ReviewStore {
20
34
  capacity;
35
+ maxChars;
36
+ sessions = new AgentSessions();
21
37
  items = new Map();
38
+ sizes = new Map();
39
+ retainedChars = 0;
22
40
  callIndex = new Map();
23
41
  actorText = new Map();
42
+ rawActors = new Set();
24
43
  sequence = 0;
25
44
  run = 0;
26
45
  _revision = 0;
@@ -28,17 +47,29 @@ export class ReviewStore {
28
47
  _running = false;
29
48
  _label = "Pronto";
30
49
  _goal = "";
31
- constructor(capacity = 2000) {
50
+ constructor(capacity = 2000, maxChars = 8_000_000) {
32
51
  this.capacity = capacity;
52
+ this.maxChars = maxChars;
33
53
  }
34
54
  get revision() { return this._revision; }
35
55
  get evicted() { return this._evicted; }
36
56
  get running() { return this._running || [...this.items.values()].some(e => e.state === "running"); }
37
- get label() { return !this._running && this.running ? "Collaboratori in esecuzione" : this._label; }
57
+ get label() {
58
+ if (!this._running && this.running)
59
+ return tr("Collaboratori in esecuzione", "Workers running");
60
+ switch (this._label) {
61
+ case "Pronto": return tr("Pronto", "Ready");
62
+ case "Avvio": return tr("Avvio", "Starting");
63
+ case "Interrotto": return tr("Interrotto", "Interrupted");
64
+ case "In attesa del prossimo messaggio": return tr("In attesa del prossimo messaggio", "Waiting for the next message");
65
+ default: return this._label;
66
+ }
67
+ }
38
68
  get goal() { return this._goal; }
39
69
  entries() { return [...this.items.values()]; }
40
70
  get(id) { return this.items.get(id); }
41
71
  begin(goal) {
72
+ this.sessions.begin(goal);
42
73
  this.run += 1;
43
74
  this._running = true;
44
75
  this._label = "Avvio";
@@ -46,6 +77,7 @@ export class ReviewStore {
46
77
  this._revision += 1;
47
78
  }
48
79
  finish(interrupted = false) {
80
+ this.sessions.finish();
49
81
  for (const entry of this.items.values()) {
50
82
  if (entry.state === "running" && entry.actor === "Talos")
51
83
  this.put({ ...entry, state: "stopped", endedAt: Date.now() });
@@ -55,9 +87,13 @@ export class ReviewStore {
55
87
  this._revision += 1;
56
88
  }
57
89
  reset(history = []) {
90
+ this.sessions.reset();
58
91
  this.items.clear();
92
+ this.sizes.clear();
93
+ this.retainedChars = 0;
59
94
  this.callIndex.clear();
60
95
  this.actorText.clear();
96
+ this.rawActors.clear();
61
97
  this.sequence = 0;
62
98
  this.run = 0;
63
99
  this._evicted = 0;
@@ -87,6 +123,11 @@ export class ReviewStore {
87
123
  this.put({ ...entry, change });
88
124
  }
89
125
  record(event, at = Date.now(), actor = "Talos") {
126
+ // Host summaries must not overwrite full raw tool output or duplicate messages.
127
+ if (event.type === "kingdom" && (event.phase === "tool" || event.phase === "say" || event.phase === "inbox")
128
+ && this.rawActors.has(event.slug))
129
+ return;
130
+ this.sessions.record(event, at, actor);
90
131
  switch (event.type) {
91
132
  case "status":
92
133
  this._label = oneLine(event.label);
@@ -132,7 +173,7 @@ export class ReviewStore {
132
173
  break;
133
174
  }
134
175
  case "error":
135
- this.notice("Errore", event.message, "error", at, actor);
176
+ this.notice(tr("Errore", "Error"), runtimeError(event.message), "error", at, actor);
136
177
  break;
137
178
  case "compacted":
138
179
  this.notice("Contesto riassunto", `Stima locale: ${event.tokensBefore} → ${event.tokensAfter} token.\n${event.anchors?.join("\n") ?? ""}`, "info", at);
@@ -149,6 +190,9 @@ export class ReviewStore {
149
190
  }
150
191
  }
151
192
  recordActor(actor, event) {
193
+ this.rawActors.add(actor);
194
+ if (event.type === "status" || event.type === "text")
195
+ this.sessions.record(event, Date.now(), actor);
152
196
  if (event.type === "status") {
153
197
  this.put({ id: `status:${this.run}:${actor}`, kind: "notice", actor, title: "Stato",
154
198
  target: oneLine(event.label), output: event.label, state: "info", startedAt: Date.now() });
@@ -166,17 +210,16 @@ export class ReviewStore {
166
210
  if (text?.trim())
167
211
  this.notice("Aggiornamento", text, "info", Date.now(), actor);
168
212
  this.actorText.delete(actor);
169
- this.items.delete(`stream:${this.run}:${actor}`);
213
+ this.remove(`stream:${this.run}:${actor}`);
170
214
  }
171
215
  if (event.type === "turn_start" || event.type === "done" || event.type === "error") {
172
216
  this.record({ type: "kingdom", phase: "vassal", slug: actor,
173
217
  state: event.type === "turn_start" ? "active" : event.type === "done" ? "done" : "error",
174
- ...(event.type === "error" ? { label: event.message } : {}) });
218
+ ...(event.type === "error" ? { label: runtimeError(event.message) } : {}) });
175
219
  }
176
220
  if (event.type === "done" || event.type === "error")
177
221
  this.finishActor(actor);
178
- if (event.type !== "turn_start")
179
- this.record(event, Date.now(), actor);
222
+ this.record(event, Date.now(), actor);
180
223
  }
181
224
  recordKingdom(e, at) {
182
225
  if (e.phase === "start") {
@@ -227,7 +270,7 @@ export class ReviewStore {
227
270
  }
228
271
  else if (e.phase === "say" || e.phase === "note" || e.phase === "notice" || e.phase === "message" || e.phase === "inbox") {
229
272
  const actor = "slug" in e ? e.slug : "from" in e ? e.from : "Talos";
230
- this.notice(e.phase === "message" ? `${actor} → ${e.to}` : e.phase === "inbox" ? `Messaggio ${e.state === "read" ? "letto" : "in coda"}` : "Aggiornamento", e.text, "info", at, actor);
273
+ this.notice(e.phase === "message" ? `${agentLabel(actor)} → ${agentLabel(e.to)}` : e.phase === "inbox" ? `Messaggio ${e.state === "read" ? "letto" : "in coda"}` : "Aggiornamento", e.text, "info", at, actor);
231
274
  }
232
275
  else if (e.phase === "task") {
233
276
  const id = `task:${this.run}:${e.slug}:${e.workstreamId}:${e.taskRef}`;
@@ -254,17 +297,27 @@ export class ReviewStore {
254
297
  this.put({ id: `notice:${++this.sequence}`, kind: "notice", title, target: oneLine(output).slice(0, 200), actor, state, output, startedAt: at });
255
298
  }
256
299
  put(entry) {
300
+ const size = textSize(entry);
301
+ this.retainedChars += size - (this.sizes.get(entry.id) ?? 0);
302
+ this.sizes.set(entry.id, size);
257
303
  this.items.set(entry.id, entry);
258
- if (this.items.size > this.capacity) {
304
+ while (this.items.size > this.capacity || this.retainedChars > this.maxChars) {
259
305
  const oldest = [...this.items.values()].find(e => e.state !== "running");
260
306
  if (oldest) {
261
- this.items.delete(oldest.id);
307
+ this.remove(oldest.id);
262
308
  for (const [key, id] of this.callIndex)
263
309
  if (id === oldest.id)
264
310
  this.callIndex.delete(key);
265
311
  this._evicted += 1;
266
312
  }
313
+ else
314
+ break;
267
315
  }
268
316
  this._revision += 1;
269
317
  }
318
+ remove(id) {
319
+ this.retainedChars -= this.sizes.get(id) ?? 0;
320
+ this.sizes.delete(id);
321
+ this.items.delete(id);
322
+ }
270
323
  }
@@ -1,3 +1,4 @@
1
+ import { agentLabel } from "./agent-label.js";
1
2
  // Proiezione del registro: una riga stabile per agente, non una per evento.
2
3
  export function teamEntries(store) {
3
4
  const groups = new Map();
@@ -19,9 +20,9 @@ export function teamEntries(store) {
19
20
  const waiting = state === "running" && status && /attes|conferm|permess|domand/i.test(status.target)
20
21
  && (status.startedAt ?? 0) >= (latest.endedAt ?? latest.startedAt ?? 0);
21
22
  const activity = (waiting ? status.target : "") || current || (state !== "running" ? lifecycle?.output : "") || `${latest.title}: ${latest.target}`;
22
- return { id: `agent:${actor}`, actor, kind: "team", title: actor,
23
- state, target: `${actor} · ${state === "running" ? "in corso" : state === "ok" ? "concluso" : state === "error" ? "errore" : state === "stopped" ? "interrotto" : lifecycle?.output?.startsWith("In coda") ? "in coda" : "stato non disponibile"} · ${activity}`,
24
- output: `Incarico: ${goal}\nRuolo: ${lifecycle?.role ?? "non disponibile"}\nRisponde a: ${lifecycle?.parent ?? "orchestratore"}\nAttività: ${activity}\n${active.length} tool attivi · ${files.size} file · ${problems.length} problemi\nEventi disponibili: ${entries.length}`,
23
+ return { id: `agent:${actor}`, actor, kind: "team", title: agentLabel(actor),
24
+ state, target: `${agentLabel(actor)} · ${state === "running" ? "in corso" : state === "ok" ? "concluso" : state === "error" ? "errore" : state === "stopped" ? "interrotto" : lifecycle?.output?.startsWith("In coda") ? "in coda" : "stato non disponibile"} · ${activity}`,
25
+ output: `Incarico: ${goal}\nRuolo: ${lifecycle?.role ?? "non disponibile"}\nRisponde a: ${agentLabel(lifecycle?.parent ?? "main")}\nAttività: ${activity}\n${active.length} tool attivi · ${files.size} file · ${problems.length} problemi\nEventi disponibili: ${entries.length}`,
25
26
  startedAt: lifecycle?.startedAt, endedAt: lifecycle?.endedAt };
26
27
  });
27
28
  }
@@ -0,0 +1,2 @@
1
+ export declare function isUserInterruption(message: string): boolean;
2
+ export declare function runtimeError(message: string): string;
@@ -0,0 +1,26 @@
1
+ import { tr } from "./i18n.js";
2
+ export function isUserInterruption(message) {
3
+ return /^(?:interrotto|interrupted|richiesta interrotta|request interrupted|the operation was aborted\.?|this operation was aborted\.?)$/i.test(message.trim());
4
+ }
5
+ // Localize Talos-owned runtime diagnostics at the display boundary, including
6
+ // events from existing sessions. Unknown provider errors retain their details.
7
+ export function runtimeError(message) {
8
+ switch (message) {
9
+ case "interrotto": return tr("interrotto", "Interrupted");
10
+ case "Il contesto supera la finestra del modello anche dopo la compattazione. La sessione resta recuperabile.": return tr("Il contesto supera la finestra del modello anche dopo la compattazione. La sessione resta recuperabile.", "The context exceeds the model window even after compaction. The session can still be recovered.");
11
+ case "Il provider ha interrotto il turno senza fornire dettagli.": return tr("Il provider ha interrotto il turno senza fornire dettagli.", "The provider interrupted the turn without providing details.");
12
+ case "Il provider ha terminato con tool_use senza fornire una tool call valida. Il task non e' stato completato; riprova mantenendo questa sessione.": return tr("Il provider ha terminato con tool_use senza fornire una tool call valida. Il task non e' stato completato; riprova mantenendo questa sessione.", "The provider ended with tool_use without a valid tool call. The task was not completed; retry in this session.");
13
+ case "Il provider ha chiuso il turno senza una risposta finale. Il task non e' stato dichiarato completato; riprova mantenendo questa sessione.": return tr("Il provider ha chiuso il turno senza una risposta finale. Il task non e' stato dichiarato completato; riprova mantenendo questa sessione.", "The provider ended the turn without a final response. The task was not marked complete; retry in this session.");
14
+ case "La delega richiesta non e' stata autorizzata; il task non viene dichiarato completato.": return tr("La delega richiesta non e' stata autorizzata; il task non viene dichiarato completato.", "The requested delegation was not authorized; the task is not marked complete.");
15
+ }
16
+ const turns = /^pausa al tetto di (\d+) turni per questo giro \(TALOS_MAX_TURNS per alzarlo\); la validazione del piano riprende le attivita rimaste$/.exec(message);
17
+ if (turns)
18
+ return tr("Pausa al limite di {count} turni per questo giro (TALOS_MAX_TURNS per alzarlo); la validazione del piano riprende le attività rimaste.", "Paused at the limit of {count} turns for this run (increase TALOS_MAX_TURNS to raise it); plan validation resumes the remaining tasks.", { count: turns[1] });
19
+ const output = /^Risposta incompleta: raggiunto il limite di output dopo (\d+) continuazion[ei]\. Il task non e' stato marcato completato; riprendi dalla stessa sessione\.$/.exec(message);
20
+ if (output)
21
+ return tr("Risposta incompleta: limite di output raggiunto. Continuazioni eseguite: {count}. Il task non è stato marcato completato; riprendi dalla stessa sessione.", "Incomplete response: output limit reached. Continuations used: {count}. The task was not marked complete; resume in the same session.", { count: output[1] });
22
+ const tasks = /^Task incompleto: restano (.+)\. Il turno e' stato fermato senza dichiarare un falso completamento; riprendilo nella stessa sessione\.$/.exec(message);
23
+ if (tasks)
24
+ return tr("Task incompleto: restano {tasks}. Il turno è stato fermato senza dichiarare un falso completamento; riprendilo nella stessa sessione.", "Incomplete task: {tasks} remain. The turn stopped without claiming completion; resume in the same session.", { tasks: tasks[1] });
25
+ return message;
26
+ }
@@ -0,0 +1,16 @@
1
+ import type { ScrollbackLine } from "./native-scrollback.js";
2
+ export declare class ScrollbackArchive implements Iterable<ScrollbackLine> {
3
+ private readonly maxChars;
4
+ private rows;
5
+ private chars;
6
+ private directory?;
7
+ private fd?;
8
+ private bytes;
9
+ constructor(maxChars?: number);
10
+ get retainedChars(): number;
11
+ push(line: ScrollbackLine): void;
12
+ private write;
13
+ private spill;
14
+ [Symbol.iterator](): Iterator<ScrollbackLine>;
15
+ clear(): void;
16
+ }
@@ -0,0 +1,90 @@
1
+ import { closeSync, mkdtempSync, openSync, readSync, rmdirSync, unlinkSync, writeSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { StringDecoder } from "node:string_decoder";
5
+ // This extra copy is used for resize, not model context. Spill old rows to
6
+ // disk instead of retaining another complete transcript in the JavaScript heap.
7
+ export class ScrollbackArchive {
8
+ maxChars;
9
+ rows = [];
10
+ chars = 0;
11
+ directory;
12
+ fd;
13
+ bytes = 0;
14
+ constructor(maxChars = 1_000_000) {
15
+ this.maxChars = maxChars;
16
+ }
17
+ get retainedChars() { return this.chars; }
18
+ push(line) {
19
+ if (this.rows.length >= 5000 || this.chars + line.text.length > this.maxChars)
20
+ this.spill();
21
+ if (line.text.length > this.maxChars)
22
+ this.write(line);
23
+ else {
24
+ this.rows.push(line);
25
+ this.chars += line.text.length;
26
+ }
27
+ }
28
+ write(line) {
29
+ if (this.fd === undefined) {
30
+ this.directory = mkdtempSync(join(tmpdir(), "talos-scrollback-"));
31
+ this.fd = openSync(join(this.directory, "rows.jsonl"), "w+", 0o600);
32
+ }
33
+ const data = Buffer.from(JSON.stringify(line) + "\n");
34
+ let offset = 0;
35
+ while (offset < data.length) {
36
+ const written = writeSync(this.fd, data, offset, data.length - offset, this.bytes);
37
+ if (!written)
38
+ throw new Error("Unable to write terminal scrollback");
39
+ offset += written;
40
+ this.bytes += written;
41
+ }
42
+ }
43
+ spill() {
44
+ for (const row of this.rows)
45
+ this.write(row);
46
+ this.rows = [];
47
+ this.chars = 0;
48
+ }
49
+ *[Symbol.iterator]() {
50
+ if (this.fd !== undefined) {
51
+ const buffer = Buffer.allocUnsafe(64 * 1024);
52
+ const decoder = new StringDecoder("utf8");
53
+ let position = 0;
54
+ let pending = "";
55
+ while (position < this.bytes) {
56
+ const size = readSync(this.fd, buffer, 0, Math.min(buffer.length, this.bytes - position), position);
57
+ if (!size)
58
+ throw new Error("Unable to read terminal scrollback");
59
+ position += size;
60
+ pending += decoder.write(buffer.subarray(0, size));
61
+ let start = 0;
62
+ let end;
63
+ while ((end = pending.indexOf("\n", start)) >= 0) {
64
+ yield JSON.parse(pending.slice(start, end));
65
+ start = end + 1;
66
+ }
67
+ pending = pending.slice(start);
68
+ }
69
+ }
70
+ yield* this.rows;
71
+ }
72
+ clear() {
73
+ this.rows = [];
74
+ this.chars = 0;
75
+ this.bytes = 0;
76
+ if (this.fd !== undefined) {
77
+ closeSync(this.fd);
78
+ this.fd = undefined;
79
+ }
80
+ if (this.directory) {
81
+ const directory = this.directory;
82
+ this.directory = undefined;
83
+ try {
84
+ unlinkSync(join(directory, "rows.jsonl"));
85
+ rmdirSync(directory);
86
+ }
87
+ catch { /* A scanner may hold the disposable cache open on Windows. */ }
88
+ }
89
+ }
90
+ }
@@ -22,5 +22,6 @@ export declare function selectorMouseIndex(o: {
22
22
  count: number;
23
23
  index: number;
24
24
  pageSize: number;
25
+ headerRows?: number;
25
26
  }): number | null;
26
27
  export {};
@@ -57,7 +57,7 @@ export class SelectorMouseInput {
57
57
  }
58
58
  export function selectorMouseIndex(o) {
59
59
  const top = o.rows - Math.min(o.renderedRows, o.rows - 1) + 1;
60
- const row = o.y - top - 1; // title occupies the first overlay row
60
+ const row = o.y - top - (o.headerRows ?? 1);
61
61
  if (o.x < 1 || o.x > o.columns + 1 || row < 0 || row >= Math.min(o.count, o.pageSize))
62
62
  return null;
63
63
  const start = Math.max(0, Math.min(o.index - Math.floor(o.pageSize / 2), o.count - o.pageSize));
@@ -6,6 +6,12 @@ export interface SelectOption<T> {
6
6
  readonly group?: string;
7
7
  readonly hint?: string;
8
8
  readonly value: T;
9
+ readonly age?: string;
10
+ readonly modelInfo?: {
11
+ readonly context?: number;
12
+ readonly reasoning?: boolean;
13
+ readonly cost: Readonly<Record<string, number>>;
14
+ };
9
15
  }
10
16
  interface SelectorControl {
11
17
  pause(): void;
@@ -14,16 +20,20 @@ interface SelectorControl {
14
20
  setOverlay(lines: string[] | null): void;
15
21
  }
16
22
  interface SelectorOptions<T> {
23
+ readonly signal?: AbortSignal;
17
24
  readonly title: string;
18
25
  readonly hint?: string;
19
26
  readonly options: readonly SelectOption<T>[];
20
27
  readonly current?: T;
21
28
  readonly currentKey?: string;
29
+ readonly onCycleMode?: () => SelectOption<T> | undefined;
22
30
  readonly theme: TuiTheme;
23
31
  readonly wide?: boolean;
24
32
  readonly question?: boolean;
25
33
  readonly selectedDetailsOnly?: boolean;
26
34
  readonly mouse?: boolean;
35
+ readonly sessionList?: boolean;
36
+ readonly modelList?: boolean;
27
37
  }
28
38
  export interface SelectorRenderOptions<T> extends SelectorOptions<T> {
29
39
  readonly index: number;
@@ -33,6 +43,7 @@ export interface SelectorRenderOptions<T> extends SelectorOptions<T> {
33
43
  readonly totalCount?: number;
34
44
  readonly details?: boolean;
35
45
  readonly detailOffset?: number;
46
+ readonly rows?: number;
36
47
  }
37
48
  export declare function pickerPageSize(rows: number, fullHeight?: boolean): number;
38
49
  export declare function filterOptions<T extends {
@@ -6,6 +6,8 @@ import { tr } from "./i18n.js";
6
6
  import { SelectorMouseInput, selectorMouseIndex } from "./selector-mouse.js";
7
7
  import { fitLine, oneLine, padLine, plainText, terminalWidth, wrapText } from "./terminal-text.js";
8
8
  import { renderQuestionChoices } from "./question.js";
9
+ import { renderSessionPicker } from "./session-picker.js";
10
+ import { renderModelPicker } from "./model-picker.js";
9
11
  export function pickerPageSize(rows, fullHeight = false) {
10
12
  const available = Math.max(1, Math.floor(rows) - 6);
11
13
  return Math.min(available, fullHeight ? 26 : 7);
@@ -42,6 +44,10 @@ function detailLines(opts, option, width) {
42
44
  ];
43
45
  }
44
46
  export function renderSelector(opts) {
47
+ if (opts.modelList && !opts.details)
48
+ return renderModelPicker(opts);
49
+ if (opts.sessionList && !opts.details)
50
+ return renderSessionPicker(opts);
45
51
  const t = opts.theme;
46
52
  const width = selectorWidth(opts.columns, opts.wide);
47
53
  const page = Math.max(1, opts.pageSize);
@@ -109,6 +115,8 @@ export function renderSelector(opts) {
109
115
  return lines;
110
116
  }
111
117
  export async function selectOption(opts) {
118
+ if (opts.signal?.aborted)
119
+ return null;
112
120
  if (!stdin.isTTY || !stdout.isTTY || typeof stdin.setRawMode !== "function") {
113
121
  opts.ctl.write(opts.theme.bold(`\n ${opts.title}\n`));
114
122
  if (opts.hint)
@@ -132,7 +140,8 @@ export async function selectOption(opts) {
132
140
  let renderedRows = 0;
133
141
  const wasRaw = stdin.isRaw;
134
142
  const wasPaused = stdin.isPaused();
135
- const pageSize = () => pickerPageSize(stdout.rows || 24, opts.fullHeight);
143
+ const pageSize = () => opts.modelList ? Math.max(1, (stdout.rows || 24) - 13)
144
+ : opts.sessionList ? Math.max(1, (stdout.rows || 24) - 8) : pickerPageSize(stdout.rows || 24, opts.fullHeight);
136
145
  const detailMax = () => Math.max(0, detailLines(opts, filtered[index], selectorWidth(opts.terminalColumns(), opts.wide) - 2).length - pageSize());
137
146
  const applyFilter = () => {
138
147
  const previous = filtered[index];
@@ -152,7 +161,7 @@ export async function selectOption(opts) {
152
161
  detailOffset = Math.min(detailOffset, detailMax());
153
162
  const lines = renderSelector({ ...opts, options: filtered, index,
154
163
  pageSize: pageSize(), columns: opts.terminalColumns(), query: opts.filterable ? query : undefined,
155
- totalCount: opts.options.length, details, detailOffset,
164
+ totalCount: opts.options.length, details, detailOffset, rows: stdout.rows || 24,
156
165
  });
157
166
  renderedRows = lines.length;
158
167
  opts.ctl.setOverlay(lines);
@@ -165,6 +174,7 @@ export async function selectOption(opts) {
165
174
  if (settled)
166
175
  return;
167
176
  settled = true;
177
+ opts.signal?.removeEventListener("abort", abort);
168
178
  stdin.off("data", onData);
169
179
  decoder.dispose();
170
180
  keys.off("keypress", onKeypress);
@@ -190,7 +200,8 @@ export async function selectOption(opts) {
190
200
  if (event.button !== 0 || details || opts.question)
191
201
  return;
192
202
  const hit = selectorMouseIndex({ y: event.y, x: event.x, rows: stdout.rows || 24,
193
- columns: selectorWidth(opts.terminalColumns(), opts.wide), renderedRows,
203
+ columns: opts.modelList ? Math.max(1, opts.terminalColumns() - 2) : selectorWidth(opts.terminalColumns(), opts.wide), renderedRows,
204
+ headerRows: opts.modelList ? 4 : 1,
194
205
  count: filtered.length, index, pageSize: pageSize() });
195
206
  if (hit === null)
196
207
  return;
@@ -205,6 +216,14 @@ export async function selectOption(opts) {
205
216
  const utf8 = new StringDecoder("utf8");
206
217
  const onData = (data) => decoder.push(typeof data === "string" ? data : utf8.write(data));
207
218
  const onKeypress = (chunk, key) => {
219
+ if (key.shift && key.name === "tab" && opts.onCycleMode) {
220
+ const selected = opts.onCycleMode();
221
+ if (selected)
222
+ cleanup(selected);
223
+ else
224
+ draw();
225
+ return;
226
+ }
208
227
  if (key.name === "return") {
209
228
  cleanup(filtered[index] ?? null);
210
229
  return;
@@ -288,12 +307,17 @@ export async function selectOption(opts) {
288
307
  else if (key.name === "q")
289
308
  cleanup(null);
290
309
  };
310
+ const abort = () => cleanup(null);
291
311
  opts.ctl.pause();
292
312
  stdin.setRawMode(true);
293
313
  stdin.resume();
294
314
  stdin.on("data", onData);
295
315
  keys.on("keypress", onKeypress);
296
316
  stdout.on("resize", draw);
297
- draw();
317
+ opts.signal?.addEventListener("abort", abort, { once: true });
318
+ if (opts.signal?.aborted)
319
+ abort();
320
+ else
321
+ draw();
298
322
  });
299
323
  }
@@ -0,0 +1,26 @@
1
+ import type { UserInbox } from "../core/user-inbox.js";
2
+ import type { ContentPart } from "../core/types.js";
3
+ import type { AgentSession } from "./agent-sessions.js";
4
+ import type { TuiTheme } from "./branding.js";
5
+ import { buildUsageFooter } from "./usage-footer.js";
6
+ export declare function workerFooter(session: AgentSession, width: number, theme: TuiTheme, usage?: Parameters<typeof buildUsageFooter>[2], providerId?: (model: string) => string): readonly string[];
7
+ export declare function deleteWorker(session: AgentSession): {
8
+ ok: boolean;
9
+ error?: string;
10
+ };
11
+ export declare function stopWorker(session: AgentSession): {
12
+ ok: boolean;
13
+ error?: string;
14
+ };
15
+ export declare function bindWorkerInbox(session: AgentSession, inbox: UserInbox): {
16
+ ok: boolean;
17
+ error?: string;
18
+ };
19
+ export declare function wakeWorker(session: AgentSession): {
20
+ ok: boolean;
21
+ error?: string;
22
+ };
23
+ export declare function sendWorker(session: AgentSession, text: string, parts: readonly ContentPart[], immediate: boolean): {
24
+ ok: boolean;
25
+ error?: string;
26
+ };
@@ -0,0 +1,65 @@
1
+ import { kingdomLive } from "../vascend/kingdom-live.js";
2
+ import { compactContextTokens } from "./context-bar.js";
3
+ import { buildUsageFooter } from "./usage-footer.js";
4
+ import { tr } from "./i18n.js";
5
+ import { fitLine } from "./terminal-text.js";
6
+ import { ContextMeter } from "./context-meter.js";
7
+ const meters = new WeakMap();
8
+ export function workerFooter(session, width, theme, usage = { limits: new Map(), incomplete: false, missing: 0, unsaved: false }, providerId = model => model.split("/")[0] ?? "") {
9
+ const source = kingdomLive.contextSource(session.kingdomId ?? "", session.actor);
10
+ let reading;
11
+ if (source) {
12
+ let entry = meters.get(source.session);
13
+ if (!entry) {
14
+ entry = { meter: new ContextMeter() };
15
+ meters.set(source.session, entry);
16
+ }
17
+ const input = { ...source, env: source.env ?? process.env };
18
+ if (session.context && entry.observed !== session.context) {
19
+ entry.meter.observe(input, session.context);
20
+ entry.observed = session.context;
21
+ }
22
+ reading = entry.meter.read(input);
23
+ }
24
+ const ctx = reading ?? session.context;
25
+ const model = source?.config.model ?? session.context?.model ?? session.actor;
26
+ const footer = buildUsageFooter({ model, cwd: source?.config.cwd ?? "", width,
27
+ effort: source?.config.effort, fast: source?.config.fast,
28
+ contextTokens: ctx?.tokens, contextWindow: ctx?.window, contextArchive: reading?.archived,
29
+ }, theme, usage, providerId(model), { order: [] });
30
+ return [...footer,
31
+ fitLine(theme.muted(` ${tr("Consumo cumulativo", "Cumulative usage")}: ${session.usageMissing ? "≥" : ""}${session.usageKnown
32
+ ? compactContextTokens(session.inputTokens + session.outputTokens) : "—"} token`), width - 1)];
33
+ }
34
+ // Release works for both active and idle workers; persisted history is kept.
35
+ export function deleteWorker(session) {
36
+ if (session.id === "main")
37
+ return { ok: false, error: tr("La sessione principale non si elimina", "Cannot delete the main session") };
38
+ if (session.kingdomId)
39
+ kingdomLive.release(session.kingdomId, session.actor);
40
+ return { ok: true };
41
+ }
42
+ export function stopWorker(session) {
43
+ if (!session.kingdomId)
44
+ return { ok: false, error: tr("Collaboratore non attivo", "Worker inactive") };
45
+ return kingdomLive.stop(session.kingdomId, session.actor);
46
+ }
47
+ export function bindWorkerInbox(session, inbox) {
48
+ return kingdomLive.bindInbox(session.kingdomId ?? "", session.actor, inbox);
49
+ }
50
+ export function wakeWorker(session) {
51
+ return kingdomLive.wake(session.kingdomId ?? "", session.actor);
52
+ }
53
+ // Session-scoped commands cannot silently fall through to the main session.
54
+ // Global display/preferences commands are still handled by the host explicitly.
55
+ export function sendWorker(session, text, parts, immediate) {
56
+ if (!session.kingdomId)
57
+ return { ok: false, error: tr("Collaboratore non attivo", "Worker inactive") };
58
+ const compact = /^\/compact(?:\s+([\s\S]*))?\s*$/.exec(text.trim());
59
+ if (compact)
60
+ return kingdomLive.compact(session.kingdomId, session.actor, compact[1]);
61
+ if (text.trimStart().startsWith("/"))
62
+ return { ok: false,
63
+ error: tr("Comando non disponibile per questa sessione", "Command unavailable for this session") };
64
+ return kingdomLive.deliver(session.kingdomId, session.actor, text, undefined, parts, immediate);
65
+ }
@@ -0,0 +1,14 @@
1
+ export declare class SessionNamingNotice {
2
+ private readonly refresh;
3
+ private title;
4
+ private startedAt;
5
+ private pending;
6
+ private timer?;
7
+ constructor(refresh: () => void);
8
+ show(title: string): void;
9
+ start(): void;
10
+ finish(title: string): void;
11
+ lines(now?: number): readonly string[];
12
+ line(now?: number): string;
13
+ clear(): void;
14
+ }