@vascend/talos 0.1.5 → 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.
- package/dist/core/agent.d.ts +16 -3
- package/dist/core/agent.js +114 -34
- package/dist/core/background.d.ts +10 -3
- package/dist/core/background.js +55 -9
- package/dist/core/bootstrap.js +1 -0
- package/dist/core/compaction.js +1 -1
- package/dist/core/config-storage.d.ts +5 -0
- package/dist/core/config-storage.js +83 -0
- package/dist/core/config.d.ts +11 -2
- package/dist/core/config.js +24 -8
- package/dist/core/context-ledger.js +2 -1
- package/dist/core/context-policy.d.ts +1 -1
- package/dist/core/context-policy.js +10 -7
- package/dist/core/credentials.js +29 -30
- package/dist/core/effort.d.ts +1 -1
- package/dist/core/effort.js +5 -1
- package/dist/core/execution-guidance.d.ts +1 -1
- package/dist/core/execution-guidance.js +2 -0
- package/dist/core/file-coordination.d.ts +3 -0
- package/dist/core/file-coordination.js +28 -0
- package/dist/core/file-lease-store.d.ts +3 -0
- package/dist/core/file-lease-store.js +28 -12
- package/dist/core/fusion.d.ts +24 -0
- package/dist/core/fusion.js +80 -0
- package/dist/core/model-limits.d.ts +1 -0
- package/dist/core/model-limits.js +5 -1
- package/dist/core/permission-queue.js +15 -15
- package/dist/core/request-plan.js +4 -3
- package/dist/core/session-naming.js +4 -0
- package/dist/core/session.d.ts +4 -2
- package/dist/core/session.js +13 -4
- package/dist/core/settings.d.ts +2 -0
- package/dist/core/settings.js +5 -16
- package/dist/core/step-policy.d.ts +18 -0
- package/dist/core/step-policy.js +6 -0
- package/dist/core/tool-artifacts.d.ts +2 -0
- package/dist/core/tool-artifacts.js +21 -3
- package/dist/core/tool-result-index.js +29 -33
- package/dist/core/tool-result-store.js +6 -5
- package/dist/core/types.d.ts +10 -3
- package/dist/core/user-inbox.d.ts +9 -0
- package/dist/core/user-inbox.js +22 -3
- package/dist/core/user-input.js +1 -1
- package/dist/fusion/autopilot-fast.d.ts +59 -0
- package/dist/fusion/autopilot-fast.js +213 -0
- package/dist/fusion/autopilot.d.ts +75 -0
- package/dist/fusion/autopilot.js +308 -0
- package/dist/fusion/focus.d.ts +30 -0
- package/dist/fusion/focus.js +122 -0
- package/dist/fusion/gate.d.ts +30 -0
- package/dist/fusion/gate.js +95 -0
- package/dist/fusion/jev.d.ts +71 -0
- package/dist/fusion/jev.js +164 -0
- package/dist/fusion/receipts.d.ts +25 -0
- package/dist/fusion/receipts.js +34 -0
- package/dist/fusion/route.d.ts +25 -0
- package/dist/fusion/route.js +84 -0
- package/dist/fusion/run.d.ts +4 -0
- package/dist/fusion/run.js +186 -0
- package/dist/horizon/engine.d.ts +20 -1
- package/dist/horizon/engine.js +57 -0
- package/dist/horizon/index.d.ts +2 -2
- package/dist/horizon/index.js +1 -1
- package/dist/horizon/types.d.ts +29 -0
- package/dist/index.js +12 -0
- package/dist/providers/anthropic-oauth.d.ts +8 -3
- package/dist/providers/anthropic-oauth.js +13 -10
- package/dist/providers/anthropic.d.ts +9 -2
- package/dist/providers/anthropic.js +64 -21
- package/dist/providers/cli-models.d.ts +30 -0
- package/dist/providers/cli-models.js +194 -0
- package/dist/providers/model-effort.js +1 -1
- package/dist/providers/openai-oauth.d.ts +8 -3
- package/dist/providers/openai-oauth.js +9 -3
- package/dist/providers/openrouter-manual-fallback.d.ts +3 -1
- package/dist/providers/openrouter-manual-fallback.js +20 -20
- package/dist/providers/registry.js +3 -2
- package/dist/providers/retry.d.ts +3 -0
- package/dist/providers/retry.js +44 -1
- package/dist/remote/cifratura.d.ts +12 -0
- package/dist/remote/cifratura.js +66 -0
- package/dist/remote/index.d.ts +15 -0
- package/dist/remote/index.js +65 -0
- package/dist/remote/relay-client.d.ts +58 -0
- package/dist/remote/relay-client.js +315 -0
- package/dist/remote/sessione-remota.d.ts +60 -0
- package/dist/remote/sessione-remota.js +266 -0
- package/dist/sdk.d.ts +7 -2
- package/dist/sdk.js +3 -1
- package/dist/tools/builtin.js +41 -20
- package/dist/tools/capabilities.d.ts +3 -1
- package/dist/tools/capabilities.js +5 -2
- package/dist/tools/computer-use.js +2 -1
- package/dist/tools/pagination.js +1 -1
- package/dist/tools/program.js +34 -21
- package/dist/tools/team-tools.js +11 -6
- package/dist/tui/account-entry.js +14 -9
- package/dist/tui/agent-label.d.ts +2 -0
- package/dist/tui/agent-label.js +5 -0
- package/dist/tui/agent-sessions-screen.d.ts +12 -2
- package/dist/tui/agent-sessions-screen.js +62 -16
- package/dist/tui/agent-sessions.d.ts +14 -0
- package/dist/tui/agent-sessions.js +67 -6
- package/dist/tui/bottom-input.d.ts +46 -2
- package/dist/tui/bottom-input.js +358 -59
- package/dist/tui/branding.js +8 -4
- package/dist/tui/capability-lists.d.ts +10 -0
- package/dist/tui/capability-lists.js +41 -0
- package/dist/tui/command-output-registry.js +4 -0
- package/dist/tui/command-transcript.d.ts +1 -1
- package/dist/tui/command-transcript.js +17 -21
- package/dist/tui/commands.js +4 -1
- package/dist/tui/compaction-status.d.ts +7 -0
- package/dist/tui/compaction-status.js +39 -0
- package/dist/tui/config-recovery.d.ts +12 -0
- package/dist/tui/config-recovery.js +54 -0
- package/dist/tui/context-meter.d.ts +3 -0
- package/dist/tui/context-meter.js +35 -8
- package/dist/tui/diff.d.ts +9 -0
- package/dist/tui/diff.js +43 -19
- package/dist/tui/fusion-menu.d.ts +5 -0
- package/dist/tui/fusion-menu.js +89 -0
- package/dist/tui/i18n-catalog.js +312 -1
- package/dist/tui/live-command.d.ts +1 -0
- package/dist/tui/live-command.js +9 -0
- package/dist/tui/live-output.d.ts +1 -0
- package/dist/tui/live-output.js +11 -0
- package/dist/tui/model-details.js +13 -12
- package/dist/tui/model-picker.js +17 -7
- package/dist/tui/model-selection.js +19 -10
- package/dist/tui/native-scrollback.js +5 -0
- package/dist/tui/prompt-queue.d.ts +3 -0
- package/dist/tui/prompt-queue.js +19 -1
- package/dist/tui/provider-label.d.ts +2 -0
- package/dist/tui/provider-label.js +9 -0
- package/dist/tui/provider-menu.js +25 -24
- package/dist/tui/queue-screen.js +2 -1
- package/dist/tui/remote-command.d.ts +7 -0
- package/dist/tui/remote-command.js +79 -0
- package/dist/tui/repl.d.ts +8 -2
- package/dist/tui/repl.js +355 -311
- package/dist/tui/review-store.d.ts +1 -0
- package/dist/tui/review-store.js +28 -7
- package/dist/tui/review-team.js +4 -3
- package/dist/tui/runtime-error.d.ts +2 -0
- package/dist/tui/runtime-error.js +26 -0
- package/dist/tui/selector-mouse.d.ts +1 -0
- package/dist/tui/selector-mouse.js +1 -1
- package/dist/tui/selector.d.ts +7 -0
- package/dist/tui/selector.js +19 -2
- package/dist/tui/session-controls.d.ts +26 -0
- package/dist/tui/session-controls.js +65 -0
- package/dist/tui/session-naming-notice.js +5 -3
- package/dist/tui/session-replay.d.ts +1 -0
- package/dist/tui/session-replay.js +101 -32
- package/dist/tui/session-transcript-layout.d.ts +9 -0
- package/dist/tui/session-transcript-layout.js +41 -0
- package/dist/tui/startup.js +63 -33
- package/dist/tui/status-line.d.ts +1 -1
- package/dist/tui/status-line.js +20 -12
- package/dist/tui/talos-art-data.d.ts +1 -0
- package/dist/tui/talos-art-data.js +2 -0
- package/dist/tui/talos-art.d.ts +1 -0
- package/dist/tui/talos-art.js +9 -0
- package/dist/tui/task-labels.d.ts +1 -0
- package/dist/tui/task-labels.js +8 -0
- package/dist/tui/terminal-text.d.ts +2 -1
- package/dist/tui/terminal-text.js +28 -2
- package/dist/tui/theme.js +2 -2
- package/dist/tui/tool-presentation.d.ts +3 -0
- package/dist/tui/tool-presentation.js +98 -0
- package/dist/tui/tool-style.js +4 -0
- package/dist/tui/transcript-store.d.ts +25 -0
- package/dist/tui/transcript-store.js +151 -0
- package/dist/tui/usage-footer.d.ts +1 -1
- package/dist/tui/usage-footer.js +3 -1
- package/dist/tui/vascend-id.d.ts +49 -0
- package/dist/tui/vascend-id.js +321 -0
- package/dist/tui/web-login-screen.js +4 -3
- package/dist/tui/web-login.d.ts +7 -15
- package/dist/tui/web-login.js +38 -78
- package/dist/tui/worker-transcripts.d.ts +31 -0
- package/dist/tui/worker-transcripts.js +91 -0
- package/dist/vascend/candidate-collector.d.ts +6 -5
- package/dist/vascend/candidate-collector.js +44 -43
- package/dist/vascend/context-lint.js +32 -2
- package/dist/vascend/context-market-selection.js +12 -5
- package/dist/vascend/context-pack-render.js +10 -4
- package/dist/vascend/context-packet-mirrors.d.ts +8 -0
- package/dist/vascend/context-packet-mirrors.js +24 -0
- package/dist/vascend/context-plan-events.d.ts +4 -0
- package/dist/vascend/context-plan-events.js +44 -0
- package/dist/vascend/context-snapshot.js +4 -2
- package/dist/vascend/context-store-reader.d.ts +20 -0
- package/dist/vascend/context-store-reader.js +46 -0
- package/dist/vascend/file-lock.d.ts +1 -0
- package/dist/vascend/file-lock.js +26 -0
- package/dist/vascend/granular-kingdom.d.ts +1 -0
- package/dist/vascend/granular-kingdom.js +22 -15
- package/dist/vascend/kingdom-bus.d.ts +1 -0
- package/dist/vascend/kingdom-bus.js +3 -0
- package/dist/vascend/kingdom-channel.d.ts +2 -0
- package/dist/vascend/kingdom-channel.js +3 -0
- package/dist/vascend/kingdom-live.d.ts +32 -4
- package/dist/vascend/kingdom-live.js +149 -13
- package/dist/vascend/kingdom-run.d.ts +5 -1
- package/dist/vascend/kingdom-run.js +6 -2
- package/dist/vascend/kingdom.d.ts +3 -0
- package/dist/vascend/kingdom.js +20 -13
- package/dist/vascend/memory-packet.d.ts +2 -1
- package/dist/vascend/memory-packet.js +31 -44
- package/dist/vascend/plan-store.d.ts +3 -1
- package/dist/vascend/plan-store.js +6 -4
- package/dist/vascend/room-memory.d.ts +10 -1
- package/dist/vascend/room-memory.js +42 -2
- package/dist/vascend/runtime.d.ts +3 -11
- package/dist/vascend/runtime.js +11 -123
- package/dist/vascend/session-search.js +2 -0
- package/dist/vascend/team-session.js +3 -2
- package/dist/vascend/vision.d.ts +3 -1
- package/dist/vascend/vision.js +6 -4
- package/dist/vascend/worker-session.d.ts +20 -0
- package/dist/vascend/worker-session.js +83 -0
- package/package.json +1 -1
package/dist/tui/review-store.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { oneLine } from "./terminal-text.js";
|
|
2
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";
|
|
3
6
|
const LABELS = {
|
|
4
7
|
read_file: "Lettura", list_dir: "Cartella", grep: "Ricerca", glob: "Ricerca file",
|
|
5
8
|
write_file: "Scrittura", edit_file: "Modifica", bash: "Comando", bash_bg: "Comando in background",
|
|
@@ -9,7 +12,9 @@ const LABELS = {
|
|
|
9
12
|
vascend_addrooms: "Nuove attività", vascend_status: "Lista attività",
|
|
10
13
|
kingdom_send: "Messaggio al team", kingdom_inbox: "Messaggi del team", kingdom_wait: "Attesa collaboratori",
|
|
11
14
|
};
|
|
12
|
-
export function reviewTitle(name) {
|
|
15
|
+
export function reviewTitle(name) {
|
|
16
|
+
return name === "bash_output" ? tr("Processo in background", "Background process") : LABELS[name] ?? name;
|
|
17
|
+
}
|
|
13
18
|
function textSize(value, seen = new Set()) {
|
|
14
19
|
if (typeof value === "string")
|
|
15
20
|
return value.length;
|
|
@@ -34,6 +39,7 @@ export class ReviewStore {
|
|
|
34
39
|
retainedChars = 0;
|
|
35
40
|
callIndex = new Map();
|
|
36
41
|
actorText = new Map();
|
|
42
|
+
rawActors = new Set();
|
|
37
43
|
sequence = 0;
|
|
38
44
|
run = 0;
|
|
39
45
|
_revision = 0;
|
|
@@ -48,7 +54,17 @@ export class ReviewStore {
|
|
|
48
54
|
get revision() { return this._revision; }
|
|
49
55
|
get evicted() { return this._evicted; }
|
|
50
56
|
get running() { return this._running || [...this.items.values()].some(e => e.state === "running"); }
|
|
51
|
-
get 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
|
+
}
|
|
52
68
|
get goal() { return this._goal; }
|
|
53
69
|
entries() { return [...this.items.values()]; }
|
|
54
70
|
get(id) { return this.items.get(id); }
|
|
@@ -77,6 +93,7 @@ export class ReviewStore {
|
|
|
77
93
|
this.retainedChars = 0;
|
|
78
94
|
this.callIndex.clear();
|
|
79
95
|
this.actorText.clear();
|
|
96
|
+
this.rawActors.clear();
|
|
80
97
|
this.sequence = 0;
|
|
81
98
|
this.run = 0;
|
|
82
99
|
this._evicted = 0;
|
|
@@ -106,6 +123,10 @@ export class ReviewStore {
|
|
|
106
123
|
this.put({ ...entry, change });
|
|
107
124
|
}
|
|
108
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;
|
|
109
130
|
this.sessions.record(event, at, actor);
|
|
110
131
|
switch (event.type) {
|
|
111
132
|
case "status":
|
|
@@ -152,7 +173,7 @@ export class ReviewStore {
|
|
|
152
173
|
break;
|
|
153
174
|
}
|
|
154
175
|
case "error":
|
|
155
|
-
this.notice("Errore", event.message, "error", at, actor);
|
|
176
|
+
this.notice(tr("Errore", "Error"), runtimeError(event.message), "error", at, actor);
|
|
156
177
|
break;
|
|
157
178
|
case "compacted":
|
|
158
179
|
this.notice("Contesto riassunto", `Stima locale: ${event.tokensBefore} → ${event.tokensAfter} token.\n${event.anchors?.join("\n") ?? ""}`, "info", at);
|
|
@@ -169,6 +190,7 @@ export class ReviewStore {
|
|
|
169
190
|
}
|
|
170
191
|
}
|
|
171
192
|
recordActor(actor, event) {
|
|
193
|
+
this.rawActors.add(actor);
|
|
172
194
|
if (event.type === "status" || event.type === "text")
|
|
173
195
|
this.sessions.record(event, Date.now(), actor);
|
|
174
196
|
if (event.type === "status") {
|
|
@@ -193,12 +215,11 @@ export class ReviewStore {
|
|
|
193
215
|
if (event.type === "turn_start" || event.type === "done" || event.type === "error") {
|
|
194
216
|
this.record({ type: "kingdom", phase: "vassal", slug: actor,
|
|
195
217
|
state: event.type === "turn_start" ? "active" : event.type === "done" ? "done" : "error",
|
|
196
|
-
...(event.type === "error" ? { label: event.message } : {}) });
|
|
218
|
+
...(event.type === "error" ? { label: runtimeError(event.message) } : {}) });
|
|
197
219
|
}
|
|
198
220
|
if (event.type === "done" || event.type === "error")
|
|
199
221
|
this.finishActor(actor);
|
|
200
|
-
|
|
201
|
-
this.record(event, Date.now(), actor);
|
|
222
|
+
this.record(event, Date.now(), actor);
|
|
202
223
|
}
|
|
203
224
|
recordKingdom(e, at) {
|
|
204
225
|
if (e.phase === "start") {
|
|
@@ -249,7 +270,7 @@ export class ReviewStore {
|
|
|
249
270
|
}
|
|
250
271
|
else if (e.phase === "say" || e.phase === "note" || e.phase === "notice" || e.phase === "message" || e.phase === "inbox") {
|
|
251
272
|
const actor = "slug" in e ? e.slug : "from" in e ? e.from : "Talos";
|
|
252
|
-
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);
|
|
253
274
|
}
|
|
254
275
|
else if (e.phase === "task") {
|
|
255
276
|
const id = `task:${this.run}:${e.slug}:${e.workstreamId}:${e.taskRef}`;
|
package/dist/tui/review-team.js
CHANGED
|
@@ -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 ?? "
|
|
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,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
|
+
}
|
|
@@ -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;
|
|
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));
|
package/dist/tui/selector.d.ts
CHANGED
|
@@ -7,6 +7,11 @@ export interface SelectOption<T> {
|
|
|
7
7
|
readonly hint?: string;
|
|
8
8
|
readonly value: T;
|
|
9
9
|
readonly age?: string;
|
|
10
|
+
readonly modelInfo?: {
|
|
11
|
+
readonly context?: number;
|
|
12
|
+
readonly reasoning?: boolean;
|
|
13
|
+
readonly cost: Readonly<Record<string, number>>;
|
|
14
|
+
};
|
|
10
15
|
}
|
|
11
16
|
interface SelectorControl {
|
|
12
17
|
pause(): void;
|
|
@@ -15,11 +20,13 @@ interface SelectorControl {
|
|
|
15
20
|
setOverlay(lines: string[] | null): void;
|
|
16
21
|
}
|
|
17
22
|
interface SelectorOptions<T> {
|
|
23
|
+
readonly signal?: AbortSignal;
|
|
18
24
|
readonly title: string;
|
|
19
25
|
readonly hint?: string;
|
|
20
26
|
readonly options: readonly SelectOption<T>[];
|
|
21
27
|
readonly current?: T;
|
|
22
28
|
readonly currentKey?: string;
|
|
29
|
+
readonly onCycleMode?: () => SelectOption<T> | undefined;
|
|
23
30
|
readonly theme: TuiTheme;
|
|
24
31
|
readonly wide?: boolean;
|
|
25
32
|
readonly question?: boolean;
|
package/dist/tui/selector.js
CHANGED
|
@@ -115,6 +115,8 @@ export function renderSelector(opts) {
|
|
|
115
115
|
return lines;
|
|
116
116
|
}
|
|
117
117
|
export async function selectOption(opts) {
|
|
118
|
+
if (opts.signal?.aborted)
|
|
119
|
+
return null;
|
|
118
120
|
if (!stdin.isTTY || !stdout.isTTY || typeof stdin.setRawMode !== "function") {
|
|
119
121
|
opts.ctl.write(opts.theme.bold(`\n ${opts.title}\n`));
|
|
120
122
|
if (opts.hint)
|
|
@@ -172,6 +174,7 @@ export async function selectOption(opts) {
|
|
|
172
174
|
if (settled)
|
|
173
175
|
return;
|
|
174
176
|
settled = true;
|
|
177
|
+
opts.signal?.removeEventListener("abort", abort);
|
|
175
178
|
stdin.off("data", onData);
|
|
176
179
|
decoder.dispose();
|
|
177
180
|
keys.off("keypress", onKeypress);
|
|
@@ -197,7 +200,8 @@ export async function selectOption(opts) {
|
|
|
197
200
|
if (event.button !== 0 || details || opts.question)
|
|
198
201
|
return;
|
|
199
202
|
const hit = selectorMouseIndex({ y: event.y, x: event.x, rows: stdout.rows || 24,
|
|
200
|
-
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,
|
|
201
205
|
count: filtered.length, index, pageSize: pageSize() });
|
|
202
206
|
if (hit === null)
|
|
203
207
|
return;
|
|
@@ -212,6 +216,14 @@ export async function selectOption(opts) {
|
|
|
212
216
|
const utf8 = new StringDecoder("utf8");
|
|
213
217
|
const onData = (data) => decoder.push(typeof data === "string" ? data : utf8.write(data));
|
|
214
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
|
+
}
|
|
215
227
|
if (key.name === "return") {
|
|
216
228
|
cleanup(filtered[index] ?? null);
|
|
217
229
|
return;
|
|
@@ -295,12 +307,17 @@ export async function selectOption(opts) {
|
|
|
295
307
|
else if (key.name === "q")
|
|
296
308
|
cleanup(null);
|
|
297
309
|
};
|
|
310
|
+
const abort = () => cleanup(null);
|
|
298
311
|
opts.ctl.pause();
|
|
299
312
|
stdin.setRawMode(true);
|
|
300
313
|
stdin.resume();
|
|
301
314
|
stdin.on("data", onData);
|
|
302
315
|
keys.on("keypress", onKeypress);
|
|
303
316
|
stdout.on("resize", draw);
|
|
304
|
-
|
|
317
|
+
opts.signal?.addEventListener("abort", abort, { once: true });
|
|
318
|
+
if (opts.signal?.aborted)
|
|
319
|
+
abort();
|
|
320
|
+
else
|
|
321
|
+
draw();
|
|
305
322
|
});
|
|
306
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
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { shimmerText } from "./compaction-status.js";
|
|
1
2
|
// A short, non-scrolling transition from the first prompt to its saved title.
|
|
2
3
|
export class SessionNamingNotice {
|
|
3
4
|
refresh;
|
|
@@ -41,9 +42,10 @@ export class SessionNamingNotice {
|
|
|
41
42
|
return [];
|
|
42
43
|
if (!this.pending && elapsed >= 900)
|
|
43
44
|
return [this.title];
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// Una sola riga stabile: lo shimmer cambia soltanto luminosita', senza
|
|
46
|
+
// griglia 3x3, cubi o glyph che saltano tra un frame e l'altro.
|
|
47
|
+
const frame = Math.floor(elapsed / 100);
|
|
48
|
+
return [shimmerText("Renaming", frame)];
|
|
47
49
|
}
|
|
48
50
|
line(now = Date.now()) { return this.lines(now).join("\n"); }
|
|
49
51
|
clear() {
|
|
@@ -7,4 +7,5 @@ export declare function replaySession(session: Session, control: {
|
|
|
7
7
|
writeConversation?(text: string): void;
|
|
8
8
|
replayConversation?(renderer: (columns: number) => string): void;
|
|
9
9
|
}, theme: TuiTheme, columns: number): void;
|
|
10
|
+
export declare function visibleUserText(text: string): string;
|
|
10
11
|
export declare function resumeReplay(session: Session, theme: TuiTheme, columns: number): string;
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { renderAssistantSegment, renderUserMessage } from "./branding.js";
|
|
2
|
-
import {
|
|
3
|
-
import { plainText, wrapText } from "./terminal-text.js";
|
|
2
|
+
import { commandOutput, commandPreview, commandResultState, commandSource } from "./command-transcript.js";
|
|
4
3
|
import { tr } from "./i18n.js";
|
|
4
|
+
import { livePreview, liveResultSummary, backgroundResultSummary } from "./live-output.js";
|
|
5
|
+
import { mcpName, mcpOutput, mcpPreview } from "./mcp-transcript.js";
|
|
6
|
+
import { loadedSkill, skillPreview } from "./skill-transcript.js";
|
|
7
|
+
import { fitLine, terminalWidth } from "./terminal-text.js";
|
|
8
|
+
import { toolArg, toolLabel } from "./tool-presentation.js";
|
|
5
9
|
// The model's history() is compacted; the visible conversation uses the
|
|
6
10
|
// original nodes on the selected branch, including archived messages.
|
|
7
11
|
export function sessionConversation(session) {
|
|
@@ -13,25 +17,51 @@ export function replaySession(session, control, theme, columns) {
|
|
|
13
17
|
else
|
|
14
18
|
(control.writeConversation ?? control.write).call(control, resumeReplay(session, theme, columns));
|
|
15
19
|
}
|
|
20
|
+
function attachmentLabel(part) {
|
|
21
|
+
const source = part.source && !part.source.startsWith("data:") ? ` · ${part.source}` : "";
|
|
22
|
+
return `[${tr("immagine", "image")} · ${part.mime}${source}]`;
|
|
23
|
+
}
|
|
24
|
+
function genericToolHeader(call, theme, columns) {
|
|
25
|
+
const label = toolLabel(call.name);
|
|
26
|
+
const arg = livePreview(toolArg(call), columns - terminalWidth(label) - 9);
|
|
27
|
+
return fitLine(" " + theme.muted("◇ " + label) + " " + theme.dim(arg), Math.max(1, columns - 1));
|
|
28
|
+
}
|
|
29
|
+
export function visibleUserText(text) {
|
|
30
|
+
// Talos persists the exact request sent to the model. With the Vascend
|
|
31
|
+
// workflow that request contains an internal GOAL/MODE envelope, while the
|
|
32
|
+
// live chat shows only what the user typed. Resume must preserve that same
|
|
33
|
+
// presentation without changing the stored model history.
|
|
34
|
+
if (!text.includes("[VASCEND_GOAL]"))
|
|
35
|
+
return text;
|
|
36
|
+
const markers = [...text.matchAll(/(?:^|\r?\n)INPUT_UTENTE\r?\n/g)];
|
|
37
|
+
const marker = markers.at(-1);
|
|
38
|
+
if (marker?.index === undefined)
|
|
39
|
+
return text;
|
|
40
|
+
return text.slice(marker.index + marker[0].length).trim();
|
|
41
|
+
}
|
|
42
|
+
// A resumed session uses the same compact blocks as a live turn. No resume
|
|
43
|
+
// banner or restored-context footer is inserted into the conversation.
|
|
16
44
|
export function resumeReplay(session, theme, columns) {
|
|
17
45
|
const messages = sessionConversation(session);
|
|
18
|
-
const width = Math.max(2, columns - 5);
|
|
19
46
|
const out = [];
|
|
20
47
|
const calls = new Map();
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
48
|
+
const backgroundPolls = new Map();
|
|
49
|
+
const results = new Map();
|
|
50
|
+
for (const message of messages) {
|
|
51
|
+
for (const part of message.content)
|
|
52
|
+
if (part.type === "tool_result" && !results.has(part.callId))
|
|
53
|
+
results.set(part.callId, part);
|
|
54
|
+
}
|
|
55
|
+
const attachment = (part, role) => {
|
|
56
|
+
const caption = attachmentLabel(part);
|
|
57
|
+
if (role === "user")
|
|
58
|
+
out.push(renderUserMessage(caption, theme, columns));
|
|
59
|
+
else
|
|
60
|
+
out.push(fitLine(theme.dark(" │ ") + theme.dark("⎿ ") + theme.dim(caption), Math.max(1, columns - 1)) + "\n");
|
|
30
61
|
};
|
|
31
|
-
heading(tr("ripresa sessione {name}({id}) · {count} messaggi", "Resumed session {name}({id}) · {count} messages", {
|
|
32
|
-
name: session.name ? `"${session.name}" ` : "", id: session.id, count: messages.length,
|
|
33
|
-
}), theme.bold);
|
|
34
62
|
for (const message of messages) {
|
|
63
|
+
if (message.role === "system")
|
|
64
|
+
continue;
|
|
35
65
|
let text = "";
|
|
36
66
|
const flushText = () => {
|
|
37
67
|
if (!text.trim()) {
|
|
@@ -39,13 +69,9 @@ export function resumeReplay(session, theme, columns) {
|
|
|
39
69
|
return;
|
|
40
70
|
}
|
|
41
71
|
if (message.role === "user")
|
|
42
|
-
out.push(renderUserMessage(text, theme, columns));
|
|
72
|
+
out.push(renderUserMessage(visibleUserText(text), theme, columns));
|
|
43
73
|
else if (message.role === "assistant")
|
|
44
74
|
out.push(renderAssistantSegment(text, theme, columns));
|
|
45
|
-
else {
|
|
46
|
-
heading(message.role === "system" ? tr("Sistema", "System") : tr("Strumento", "Tool"));
|
|
47
|
-
literal(text);
|
|
48
|
-
}
|
|
49
75
|
text = "";
|
|
50
76
|
};
|
|
51
77
|
for (const part of message.content) {
|
|
@@ -54,25 +80,68 @@ export function resumeReplay(session, theme, columns) {
|
|
|
54
80
|
continue;
|
|
55
81
|
}
|
|
56
82
|
flushText();
|
|
83
|
+
if (part.type === "image") {
|
|
84
|
+
attachment(part, message.role);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
57
87
|
if (part.type === "tool_call") {
|
|
58
88
|
calls.set(part.id, part);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
89
|
+
if (part.name === "bash_output")
|
|
90
|
+
continue;
|
|
91
|
+
if (part.name === "vascend_mark")
|
|
92
|
+
continue;
|
|
93
|
+
const result = results.get(part.id);
|
|
94
|
+
const skill = loadedSkill(part);
|
|
95
|
+
if (skill) {
|
|
96
|
+
out.push("\n" + skillPreview(skill, theme, columns, result?.isError ? "error" : result ? "ok" : "running").join("\n") + "\n");
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
if (mcpName(part.name)) {
|
|
100
|
+
out.push("\n" + mcpPreview(part, theme, columns, result?.isError ? "error" : result ? "ok" : "stopped").join("\n") + "\n");
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
const source = part.name === "bash" ? commandSource(part) : undefined;
|
|
104
|
+
if (source !== undefined) {
|
|
105
|
+
out.push("\n" + commandPreview(source, theme, columns, result ? commandResultState(result.output, result.isError) : "stopped").lines.join("\n") + "\n");
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
out.push("\n" + genericToolHeader(part, theme, columns) + "\n");
|
|
109
|
+
continue;
|
|
62
110
|
}
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
|
|
111
|
+
if (part.type === "tool_result") {
|
|
112
|
+
const call = calls.get(part.callId);
|
|
113
|
+
if (call?.name === "bash_output") {
|
|
114
|
+
const id = String(call.input["id"] ?? call.id);
|
|
115
|
+
const snapshot = JSON.stringify([part.isError ?? false, part.output, part.media]);
|
|
116
|
+
if (backgroundPolls.get(id) === snapshot)
|
|
117
|
+
continue;
|
|
118
|
+
backgroundPolls.set(id, snapshot);
|
|
119
|
+
out.push("\n" + fitLine(" " + (part.isError ? theme.red("× ") : theme.muted("◇ "))
|
|
120
|
+
+ theme.muted(toolLabel("bash_output")), Math.max(1, columns - 1)) + "\n");
|
|
121
|
+
}
|
|
122
|
+
if (call?.name === "vascend_mark")
|
|
123
|
+
continue;
|
|
124
|
+
const skill = call && loadedSkill(call);
|
|
125
|
+
if (skill) {
|
|
126
|
+
if (part.isError)
|
|
127
|
+
out.push(commandOutput(part.output, theme, columns, true).join("\n") + "\n");
|
|
128
|
+
}
|
|
129
|
+
else if (call && mcpName(call.name)) {
|
|
130
|
+
out.push(mcpOutput(part.output, part.isError, theme, columns).join("\n") + "\n");
|
|
131
|
+
}
|
|
132
|
+
else if (call?.name === "bash" && commandSource(call) !== undefined) {
|
|
133
|
+
out.push(commandOutput(part.output, theme, columns, Boolean(part.isError)).join("\n") + "\n");
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
const summary = call?.name === "bash_output" ? backgroundResultSummary(part.output, columns) : liveResultSummary(part.output, null, columns);
|
|
137
|
+
out.push(fitLine(theme.dark(" └ ") + (part.isError ? theme.red("× ") : "")
|
|
138
|
+
+ (part.isError ? theme.red(summary) : theme.dim(summary)), Math.max(1, columns - 1)) + "\n");
|
|
139
|
+
}
|
|
67
140
|
for (const media of part.media ?? [])
|
|
68
|
-
attachment(media);
|
|
69
|
-
}
|
|
70
|
-
else if (part.type === "image") {
|
|
71
|
-
attachment(part);
|
|
141
|
+
attachment(media, "tool");
|
|
72
142
|
}
|
|
73
143
|
}
|
|
74
144
|
flushText();
|
|
75
145
|
}
|
|
76
|
-
heading(tr("contesto ripreso: continua pure da qui", "Context restored: continue from here"));
|
|
77
146
|
return out.join("");
|
|
78
147
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Wrap = (text: string, width: number, wordWrap: boolean) => readonly string[];
|
|
2
|
+
export declare class SessionTranscriptLayout {
|
|
3
|
+
private readonly wrap;
|
|
4
|
+
private readonly sessions;
|
|
5
|
+
constructor(wrap?: Wrap);
|
|
6
|
+
retain(ids: ReadonlySet<string>): void;
|
|
7
|
+
lines(id: string, values: readonly string[], width: number): readonly string[];
|
|
8
|
+
}
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { wrapText } from "./terminal-text.js";
|
|
2
|
+
// Reflow only changed blocks (usually the streaming tail), not the complete
|
|
3
|
+
// worker transcript on every token, timer tick or selection change. This is a
|
|
4
|
+
// bounded rendering cache; the session store remains the source of truth.
|
|
5
|
+
export class SessionTranscriptLayout {
|
|
6
|
+
wrap;
|
|
7
|
+
sessions = new Map();
|
|
8
|
+
constructor(wrap = wrapText) {
|
|
9
|
+
this.wrap = wrap;
|
|
10
|
+
}
|
|
11
|
+
retain(ids) {
|
|
12
|
+
for (const id of this.sessions.keys())
|
|
13
|
+
if (!ids.has(id))
|
|
14
|
+
this.sessions.delete(id);
|
|
15
|
+
}
|
|
16
|
+
lines(id, values, width) {
|
|
17
|
+
const blocks = values.filter(Boolean);
|
|
18
|
+
const cached = this.sessions.get(id);
|
|
19
|
+
const previous = cached?.width === width ? cached : undefined;
|
|
20
|
+
if (previous && blocks.length === previous.blocks.length && blocks.every((text, i) => text === previous.blocks[i])) {
|
|
21
|
+
this.sessions.delete(id);
|
|
22
|
+
this.sessions.set(id, previous);
|
|
23
|
+
return previous.lines;
|
|
24
|
+
}
|
|
25
|
+
const wrapped = new Map();
|
|
26
|
+
const lines = [];
|
|
27
|
+
for (const text of blocks) {
|
|
28
|
+
const rows = wrapped.get(text) ?? previous?.wrapped.get(text) ?? this.wrap(text, width, true);
|
|
29
|
+
wrapped.set(text, rows);
|
|
30
|
+
if (lines.length)
|
|
31
|
+
lines.push("");
|
|
32
|
+
for (const row of rows)
|
|
33
|
+
lines.push(row);
|
|
34
|
+
}
|
|
35
|
+
this.sessions.delete(id);
|
|
36
|
+
this.sessions.set(id, { width, blocks, wrapped, lines });
|
|
37
|
+
if (this.sessions.size > 8)
|
|
38
|
+
this.sessions.delete(this.sessions.keys().next().value);
|
|
39
|
+
return lines;
|
|
40
|
+
}
|
|
41
|
+
}
|