@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.
- package/LICENSE +22 -0
- package/README.md +37 -12
- package/dist/core/agent.d.ts +16 -3
- package/dist/core/agent.js +170 -70
- 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.d.ts +3 -0
- package/dist/core/session-naming.js +47 -0
- package/dist/core/session-usage.js +4 -1
- package/dist/core/session.d.ts +5 -2
- package/dist/core/session.js +22 -7
- 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/storage-health.d.ts +24 -0
- package/dist/core/storage-health.js +72 -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 +26 -2
- 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 +35 -0
- package/dist/tui/agent-sessions-screen.js +178 -0
- package/dist/tui/agent-sessions.d.ts +53 -0
- package/dist/tui/agent-sessions.js +236 -0
- package/dist/tui/bottom-input.d.ts +57 -2
- package/dist/tui/bottom-input.js +449 -54
- 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 +6 -2
- 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 +351 -2
- 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/logo.js +3 -0
- package/dist/tui/model-details.js +13 -12
- package/dist/tui/model-picker.d.ts +2 -0
- package/dist/tui/model-picker.js +59 -0
- package/dist/tui/model-selection.js +19 -10
- package/dist/tui/native-scrollback.d.ts +2 -1
- package/dist/tui/native-scrollback.js +31 -6
- package/dist/tui/prompt-queue-store.d.ts +1 -0
- package/dist/tui/prompt-queue-store.js +4 -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/queued-questions.js +2 -4
- package/dist/tui/quick-help.d.ts +1 -1
- package/dist/tui/quick-help.js +3 -0
- package/dist/tui/remote-command.d.ts +7 -0
- package/dist/tui/remote-command.js +79 -0
- package/dist/tui/repl.d.ts +11 -2
- package/dist/tui/repl.js +457 -327
- package/dist/tui/review-store.d.ts +8 -1
- package/dist/tui/review-store.js +64 -11
- 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/scrollback-archive.d.ts +16 -0
- package/dist/tui/scrollback-archive.js +90 -0
- package/dist/tui/selector-mouse.d.ts +1 -0
- package/dist/tui/selector-mouse.js +1 -1
- package/dist/tui/selector.d.ts +11 -0
- package/dist/tui/selector.js +28 -4
- package/dist/tui/session-controls.d.ts +26 -0
- package/dist/tui/session-controls.js +65 -0
- package/dist/tui/session-naming-notice.d.ts +14 -0
- package/dist/tui/session-naming-notice.js +59 -0
- package/dist/tui/session-picker.d.ts +2 -0
- package/dist/tui/session-picker.js +32 -0
- 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/storage-warning.d.ts +2 -0
- package/dist/tui/storage-warning.js +35 -0
- 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-feedback-loop.js +2 -0
- 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.d.ts +1 -0
- package/dist/vascend/context-snapshot.js +8 -5
- package/dist/vascend/context-store-reader.d.ts +20 -0
- package/dist/vascend/context-store-reader.js +46 -0
- package/dist/vascend/context-window-compiler.d.ts +1 -0
- package/dist/vascend/context-window-compiler.js +1 -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 +52 -17
- 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 +22 -11
- package/dist/vascend/memory-packet.d.ts +2 -1
- package/dist/vascend/memory-packet.js +31 -44
- package/dist/vascend/memory-store.d.ts +1 -0
- package/dist/vascend/memory-store.js +12 -4
- package/dist/vascend/plan-store.d.ts +3 -1
- package/dist/vascend/plan-store.js +6 -4
- package/dist/vascend/room-memory.d.ts +13 -2
- package/dist/vascend/room-memory.js +48 -6
- 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 +2 -2
package/dist/tui/bottom-input.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { tr } from "./i18n.js";
|
|
2
2
|
import { stdin, stdout } from "node:process";
|
|
3
|
-
import { pulseGlyph, pulseToolName } from "./tool-style.js";
|
|
4
3
|
import { fitLine, plainText, terminalWidth, wrapText } from "./terminal-text.js";
|
|
5
4
|
import { PromptQueue } from "./prompt-queue.js";
|
|
6
5
|
import { QueueScreen, queuePreview } from "./queue-screen.js";
|
|
7
6
|
import { createAttachmentHighlighter } from "./attachment-highlight.js";
|
|
8
7
|
import { NativeScrollback } from "./native-scrollback.js";
|
|
9
8
|
import { QueuedQuestions } from "./queued-questions.js";
|
|
9
|
+
import { AgentSessionsScreen } from "./agent-sessions-screen.js";
|
|
10
|
+
import { SessionNamingNotice } from "./session-naming-notice.js";
|
|
11
|
+
import { renderCompactionStatus, shimmerText } from "./compaction-status.js";
|
|
10
12
|
const SYNC_ON = "\x1b[?2026h";
|
|
11
13
|
const SYNC_OFF = "\x1b[?2026l";
|
|
12
14
|
const ALT_ON = "\x1b[?1049h";
|
|
@@ -22,7 +24,6 @@ export const NATIVE_SCROLL_MODE = "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\
|
|
|
22
24
|
// crudo nel buffer (parita' Claude Code).
|
|
23
25
|
const PASTE_ON = "\x1b[?2004h";
|
|
24
26
|
const PASTE_OFF = "\x1b[?2004l";
|
|
25
|
-
const SPINNER = ["✶", "✸", "✺", "✹", "✷", "✦"];
|
|
26
27
|
const MAX_LOG = 5000;
|
|
27
28
|
const SCROLL_FRAME_MS = 16;
|
|
28
29
|
const RESIZE_SETTLE_MS = 80;
|
|
@@ -48,6 +49,8 @@ export class BottomInput {
|
|
|
48
49
|
buffer = "";
|
|
49
50
|
cursor = 0;
|
|
50
51
|
history = [];
|
|
52
|
+
composerSession = "main";
|
|
53
|
+
sessionDrafts = new Map();
|
|
51
54
|
histIdx = -1;
|
|
52
55
|
draft = "";
|
|
53
56
|
active = false;
|
|
@@ -55,9 +58,35 @@ export class BottomInput {
|
|
|
55
58
|
ctrlCConfirmation = null;
|
|
56
59
|
ctrlCTimer;
|
|
57
60
|
running = false;
|
|
61
|
+
commandPending = false;
|
|
58
62
|
questions = new QueuedQuestions(() => this.refresh());
|
|
59
63
|
runAbort;
|
|
60
64
|
queue;
|
|
65
|
+
workerQueues = new Map();
|
|
66
|
+
get composerQueue() {
|
|
67
|
+
if (this.composerSession === "main")
|
|
68
|
+
return this.queue;
|
|
69
|
+
let queue = this.workerQueues.get(this.composerSession);
|
|
70
|
+
if (!queue) {
|
|
71
|
+
queue = new PromptQueue(this.h.prepareQueuedInput, () => this.queueScrollRender());
|
|
72
|
+
this.workerQueues.set(this.composerSession, queue);
|
|
73
|
+
}
|
|
74
|
+
return queue;
|
|
75
|
+
}
|
|
76
|
+
ensureWorkerQueue() {
|
|
77
|
+
const session = this.agentSessions?.session;
|
|
78
|
+
if (!session || session.id === "main" || !this.h.bindSessionInbox)
|
|
79
|
+
return undefined;
|
|
80
|
+
const queue = this.workerQueues.get(session.id) ?? new PromptQueue(this.h.prepareQueuedInput, () => this.queueScrollRender());
|
|
81
|
+
// A footer/queue preview may have allocated it before the runtime was reachable.
|
|
82
|
+
const result = this.h.bindSessionInbox(session, queue.inbox);
|
|
83
|
+
if (!result.ok) {
|
|
84
|
+
this.h.review?.sessions.notify(session.id, result.error ?? "Coda non disponibile");
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
this.workerQueues.set(session.id, queue);
|
|
88
|
+
return queue;
|
|
89
|
+
}
|
|
61
90
|
queueEditor = null;
|
|
62
91
|
// Log della chat (righe gia' formattate) + scroll del viewport (0 = in fondo).
|
|
63
92
|
log = [];
|
|
@@ -87,10 +116,17 @@ export class BottomInput {
|
|
|
87
116
|
kingdomBoard = null;
|
|
88
117
|
overlay = null;
|
|
89
118
|
screen = null;
|
|
119
|
+
agentSessions;
|
|
120
|
+
sessionsUnsubscribe;
|
|
121
|
+
messagesUnsubscribe;
|
|
122
|
+
sessionsTimer;
|
|
90
123
|
// Domanda a risposta libera (tool ask_user): quando attiva, l'editing va alla
|
|
91
124
|
// risposta e Invio/Esc risolvono la promise invece di inviare un prompt.
|
|
92
125
|
modal = null;
|
|
93
126
|
statusLabel = null;
|
|
127
|
+
sessionLimitNotice = null;
|
|
128
|
+
compactionStartedAt = null;
|
|
129
|
+
compactionPreviousStatus = null;
|
|
94
130
|
// Nome GREZZO del tool attualmente in corso (es. "read_file"): pilota colore e
|
|
95
131
|
// glyph animati nella status-line (pulse abbinato alla categoria). null quando
|
|
96
132
|
// l'attivita' non e' un singolo tool (es. "elaboro", ondata mista).
|
|
@@ -107,6 +143,8 @@ export class BottomInput {
|
|
|
107
143
|
tokensOut = 0;
|
|
108
144
|
frame = 0;
|
|
109
145
|
spinnerTimer;
|
|
146
|
+
namingNotice = new SessionNamingNotice(() => { if (this.active)
|
|
147
|
+
this.render(); });
|
|
110
148
|
lastLayout;
|
|
111
149
|
liveStartRow = 1;
|
|
112
150
|
paintedRows = [];
|
|
@@ -138,15 +176,25 @@ export class BottomInput {
|
|
|
138
176
|
this.h = h;
|
|
139
177
|
this.nativeScroll = h.nativeScroll !== false;
|
|
140
178
|
this.queue = this.createQueue();
|
|
179
|
+
if (h.review)
|
|
180
|
+
this.agentSessions = new AgentSessionsScreen(h.review.sessions, h.queueStyle, h.onSessionDelete);
|
|
141
181
|
}
|
|
142
182
|
createQueue() {
|
|
143
|
-
return new PromptQueue(this.h.prepareQueuedInput, () => this.render(), this.h.queuePersistence?.(), this.h.restoreQueuedInput);
|
|
183
|
+
return new PromptQueue(this.h.prepareQueuedInput, () => this.workerVisible ? this.queueScrollRender() : this.render(), this.h.queuePersistence?.(), this.h.restoreQueuedInput);
|
|
144
184
|
}
|
|
145
185
|
restoreQueue() {
|
|
146
186
|
this.inputGeneration++;
|
|
147
187
|
this.queue = this.createQueue();
|
|
188
|
+
this.workerQueues.clear();
|
|
189
|
+
this.sessionDrafts.clear();
|
|
190
|
+
this.composerSession = "main";
|
|
191
|
+
this.buffer = "";
|
|
192
|
+
this.cursor = 0;
|
|
193
|
+
this.history = [];
|
|
194
|
+
this.histIdx = -1;
|
|
195
|
+
this.agentSessions?.returnToMain();
|
|
148
196
|
this.queueEditor = null;
|
|
149
|
-
if (this.screen instanceof QueueScreen)
|
|
197
|
+
if (this.screen instanceof QueueScreen || this.screen === this.agentSessions)
|
|
150
198
|
this.screen = null;
|
|
151
199
|
this.render();
|
|
152
200
|
}
|
|
@@ -155,7 +203,7 @@ export class BottomInput {
|
|
|
155
203
|
acceptQueuedMessage(id) {
|
|
156
204
|
const text = this.queue.acknowledge(id);
|
|
157
205
|
if (text)
|
|
158
|
-
this.history.push(text);
|
|
206
|
+
(this.composerSession === "main" ? this.history : this.sessionDrafts.get("main")?.history)?.push(text);
|
|
159
207
|
return text;
|
|
160
208
|
}
|
|
161
209
|
pauseQueuedMessages() { if (this.queue.length) {
|
|
@@ -171,19 +219,21 @@ export class BottomInput {
|
|
|
171
219
|
this.arrowTimer = undefined;
|
|
172
220
|
}
|
|
173
221
|
this.pendingArrows = [];
|
|
174
|
-
this.
|
|
175
|
-
this.screen = new QueueScreen(this.
|
|
222
|
+
this.composerQueue.held = true;
|
|
223
|
+
this.screen = new QueueScreen(this.composerQueue, id => this.editQueuedMessage(id), this.h.queueStyle);
|
|
176
224
|
this.render();
|
|
177
225
|
}
|
|
178
226
|
closeScreen() {
|
|
179
227
|
if (this.screen instanceof QueueScreen)
|
|
180
|
-
this.
|
|
181
|
-
this.screen = null;
|
|
228
|
+
this.composerQueue.held = false;
|
|
229
|
+
this.screen = this.agentSessions?.activeId !== "main" && this.agentSessions ? this.agentSessions : null;
|
|
230
|
+
if (this.composerSession !== "main" && this.agentSessions?.activeId !== "main" && !this.composerQueue.blocked)
|
|
231
|
+
this.h.wakeSession?.(this.agentSessions.session);
|
|
182
232
|
this.startNextQueued();
|
|
183
233
|
this.render();
|
|
184
234
|
}
|
|
185
235
|
editQueuedMessage(id) {
|
|
186
|
-
const entry = this.
|
|
236
|
+
const entry = this.composerQueue.get(id);
|
|
187
237
|
if (!entry || !(this.screen instanceof QueueScreen))
|
|
188
238
|
return;
|
|
189
239
|
this.inputGeneration++;
|
|
@@ -200,7 +250,7 @@ export class BottomInput {
|
|
|
200
250
|
return;
|
|
201
251
|
if (save) {
|
|
202
252
|
try {
|
|
203
|
-
if (!this.
|
|
253
|
+
if (!this.composerQueue.update(editor.id, this.buffer))
|
|
204
254
|
throw new Error("Il messaggio non è più in attesa.");
|
|
205
255
|
}
|
|
206
256
|
catch (error) {
|
|
@@ -213,7 +263,7 @@ export class BottomInput {
|
|
|
213
263
|
this.histIdx = editor.draft.histIdx;
|
|
214
264
|
this.queueEditor = null;
|
|
215
265
|
this.screen = editor.screen;
|
|
216
|
-
if (immediate && this.
|
|
266
|
+
if (immediate && this.composerQueue.sendNow(editor.id)) {
|
|
217
267
|
this.closeScreen();
|
|
218
268
|
return;
|
|
219
269
|
}
|
|
@@ -231,7 +281,48 @@ export class BottomInput {
|
|
|
231
281
|
this.screen = this.h.createReviewScreen(section);
|
|
232
282
|
this.render();
|
|
233
283
|
}
|
|
234
|
-
get screenVisible() { return this.screen !== null && !this.modal && !this.overlay; }
|
|
284
|
+
get screenVisible() { return this.screen !== null && this.screen !== this.agentSessions && !this.modal && !this.overlay; }
|
|
285
|
+
get workerVisible() { return !!this.agentSessions && this.screen === this.agentSessions; }
|
|
286
|
+
syncSessionComposer() {
|
|
287
|
+
const id = this.agentSessions?.activeId ?? "main";
|
|
288
|
+
const retired = this.composerSession !== "main" && !this.h.review?.sessions.get(this.composerSession)?.live;
|
|
289
|
+
if (retired) {
|
|
290
|
+
if (this.queueEditor) {
|
|
291
|
+
this.queueEditor = null;
|
|
292
|
+
this.inputGeneration++;
|
|
293
|
+
}
|
|
294
|
+
if (this.screen instanceof QueueScreen)
|
|
295
|
+
this.screen = null;
|
|
296
|
+
}
|
|
297
|
+
if (id === this.composerSession || this.modal || this.overlay || this.queueEditor)
|
|
298
|
+
return;
|
|
299
|
+
if (!retired)
|
|
300
|
+
this.sessionDrafts.set(this.composerSession, { buffer: this.buffer, cursor: this.cursor,
|
|
301
|
+
history: this.history, histIdx: this.histIdx, draft: this.draft });
|
|
302
|
+
const saved = this.sessionDrafts.get(id);
|
|
303
|
+
this.buffer = saved?.buffer ?? "";
|
|
304
|
+
this.cursor = saved?.cursor ?? 0;
|
|
305
|
+
this.history = saved?.history ?? [];
|
|
306
|
+
this.histIdx = saved?.histIdx ?? -1;
|
|
307
|
+
this.draft = saved?.draft ?? "";
|
|
308
|
+
this.composerSession = id;
|
|
309
|
+
if (id !== "main")
|
|
310
|
+
this.ensureWorkerQueue();
|
|
311
|
+
this.inputGeneration++;
|
|
312
|
+
this.cancelCtrlC();
|
|
313
|
+
}
|
|
314
|
+
showAgents() {
|
|
315
|
+
if (!this.agentSessions?.available || this.modal || this.overlay || this.queueEditor || this.queue.held)
|
|
316
|
+
return;
|
|
317
|
+
this.agentSessions.focused = true;
|
|
318
|
+
this.scrollOffset = 0;
|
|
319
|
+
this.render();
|
|
320
|
+
}
|
|
321
|
+
sessionArrow(name) {
|
|
322
|
+
return !!this.agentSessions?.available && !this.modal && !this.overlay && !this.queueEditor
|
|
323
|
+
&& !this.questions.focused && this.scrollOffset === 0
|
|
324
|
+
&& (this.agentSessions.focused || (!this.buffer && name === "down"));
|
|
325
|
+
}
|
|
235
326
|
static supported() {
|
|
236
327
|
return Boolean(stdout.isTTY && stdin.isTTY && typeof stdin.setRawMode === "function");
|
|
237
328
|
}
|
|
@@ -239,6 +330,23 @@ export class BottomInput {
|
|
|
239
330
|
if (!BottomInput.supported())
|
|
240
331
|
return Promise.resolve();
|
|
241
332
|
this.active = true;
|
|
333
|
+
// A burst of worker deltas must yield to stdin and timers before painting.
|
|
334
|
+
// Reuse the bounded frame scheduler; keyboard navigation still paints now.
|
|
335
|
+
this.sessionsUnsubscribe = this.h.review?.sessions.subscribe(() => this.queueScrollRender());
|
|
336
|
+
this.messagesUnsubscribe = this.h.review?.sessions.onMessage((id, messageId) => {
|
|
337
|
+
const text = this.workerQueues.get(id)?.acknowledge(messageId);
|
|
338
|
+
if (!text)
|
|
339
|
+
return;
|
|
340
|
+
if (this.composerSession === id)
|
|
341
|
+
this.history.push(text);
|
|
342
|
+
else
|
|
343
|
+
this.sessionDrafts.get(id)?.history.push(text);
|
|
344
|
+
});
|
|
345
|
+
this.sessionsTimer = setInterval(() => {
|
|
346
|
+
if (this.agentSessions?.available || this.screen === this.agentSessions)
|
|
347
|
+
this.refresh();
|
|
348
|
+
}, 500);
|
|
349
|
+
this.sessionsTimer.unref();
|
|
242
350
|
this.alternateScreen = !this.nativeScroll;
|
|
243
351
|
stdout.write(this.nativeScroll ? "\x1b[2J\x1b[H" + NATIVE_SCROLL_MODE + PASTE_ON
|
|
244
352
|
: ALT_ON + "\x1b[2J\x1b[H" + NATIVE_SELECTION_MODE + PASTE_ON);
|
|
@@ -300,6 +408,7 @@ export class BottomInput {
|
|
|
300
408
|
}
|
|
301
409
|
// Forget the previous session's display and editor state before any replay.
|
|
302
410
|
clearConversation() {
|
|
411
|
+
this.namingNotice.clear();
|
|
303
412
|
this.questions.clear();
|
|
304
413
|
this.inputGeneration++;
|
|
305
414
|
this.buffer = "";
|
|
@@ -317,6 +426,7 @@ export class BottomInput {
|
|
|
317
426
|
this.vassalOpen = false;
|
|
318
427
|
this.kingdomBoard = null;
|
|
319
428
|
this.statusLabel = null;
|
|
429
|
+
this.sessionLimitNotice = null;
|
|
320
430
|
this.statusTool = null;
|
|
321
431
|
this.roomStatus = null;
|
|
322
432
|
this.tokensNew = this.tokensCache = this.tokensOut = 0;
|
|
@@ -391,7 +501,16 @@ export class BottomInput {
|
|
|
391
501
|
this.render();
|
|
392
502
|
}
|
|
393
503
|
setStatus(label) {
|
|
504
|
+
if (label === null)
|
|
505
|
+
this.compactionStartedAt = null;
|
|
394
506
|
this.statusLabel = label;
|
|
507
|
+
const limit = label?.match(/^Limite sessione di 5 ore raggiunto · reset alle (.+?) · riprendo automaticamente al reset · Esc per annullare$/);
|
|
508
|
+
this.sessionLimitNotice = limit ? {
|
|
509
|
+
model: this.h.currentModel?.(),
|
|
510
|
+
text: tr("Limite della sessione raggiunto · reset alle {time}\nRipresa automatica alle {clock} · Esc per annullare", "You've hit your session limit · resets {time}\nContinuing automatically at {clock} · esc to cancel", { time: limit[1], clock: limit[1].replace(/\s*\([^)]*\)$/, "") }),
|
|
511
|
+
} : null;
|
|
512
|
+
if (this.sessionLimitNotice)
|
|
513
|
+
this.namingNotice.clear();
|
|
395
514
|
if (label !== null) {
|
|
396
515
|
if (!this.spinnerTimer)
|
|
397
516
|
this.spinnerTimer = setInterval(() => { this.frame += 1; this.render(); }, 140);
|
|
@@ -403,12 +522,61 @@ export class BottomInput {
|
|
|
403
522
|
if (this.active)
|
|
404
523
|
this.render();
|
|
405
524
|
}
|
|
525
|
+
setCompacting(active) {
|
|
526
|
+
if (active) {
|
|
527
|
+
if (this.compactionStartedAt === null) {
|
|
528
|
+
this.compactionStartedAt = Date.now();
|
|
529
|
+
this.compactionPreviousStatus = this.statusLabel;
|
|
530
|
+
}
|
|
531
|
+
this.setStatus(this.statusLabel ?? "Compacting context");
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
const previous = this.compactionPreviousStatus;
|
|
535
|
+
this.compactionStartedAt = null;
|
|
536
|
+
this.compactionPreviousStatus = null;
|
|
537
|
+
this.setStatus(previous);
|
|
538
|
+
}
|
|
539
|
+
showSessionNaming(title) {
|
|
540
|
+
this.namingNotice.show(title);
|
|
541
|
+
}
|
|
542
|
+
setSessionNaming(phase, title) {
|
|
543
|
+
if (this.sessionLimitNotice) {
|
|
544
|
+
this.namingNotice.clear();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
547
|
+
if (phase === "start")
|
|
548
|
+
this.namingNotice.start();
|
|
549
|
+
else
|
|
550
|
+
this.namingNotice.finish(title);
|
|
551
|
+
}
|
|
406
552
|
getLogLine(index) { return this.log[index]; }
|
|
407
553
|
// Stato leggibile dall'host per evitare una seconda telemetria statica mentre
|
|
408
554
|
// la riga animata del run sta gia' mostrando token, tempo e coda.
|
|
409
555
|
isBusy() {
|
|
410
556
|
return this.running || this.statusLabel !== null;
|
|
411
557
|
}
|
|
558
|
+
// Controllo remoto: un messaggio dal telefono entra come se fosse scritto
|
|
559
|
+
// qui (a riposo parte, durante un giro va in coda o, con `subito`, viene
|
|
560
|
+
// consegnato al primo confine sicuro). Mai un comando "/": dal telefono non
|
|
561
|
+
// si cambiano modello, permessi o account.
|
|
562
|
+
submitExternal(line, immediate = false) {
|
|
563
|
+
const text = line.trim();
|
|
564
|
+
if (!this.active || !text || text.startsWith("/"))
|
|
565
|
+
return false;
|
|
566
|
+
this.dispatch(text, immediate);
|
|
567
|
+
this.render();
|
|
568
|
+
return true;
|
|
569
|
+
}
|
|
570
|
+
// Ferma il lavoro della chat principale, come Esc.
|
|
571
|
+
requestStop() {
|
|
572
|
+
if (!this.running)
|
|
573
|
+
return false;
|
|
574
|
+
this.cancelCtrlC();
|
|
575
|
+
this.queue.paused = true;
|
|
576
|
+
this.runAbort?.abort();
|
|
577
|
+
this.render();
|
|
578
|
+
return true;
|
|
579
|
+
}
|
|
412
580
|
// Nome del tool in corso per l'animazione della status-line. null = attivita'
|
|
413
581
|
// generica (spinner neutro). Non forza un timer: lo gestisce gia' setStatus.
|
|
414
582
|
setStatusTool(name) {
|
|
@@ -671,6 +839,7 @@ export class BottomInput {
|
|
|
671
839
|
touchLog() {
|
|
672
840
|
this.logRevision += 1;
|
|
673
841
|
}
|
|
842
|
+
cyclePermissionMode() { this.h.onCycleMode?.(); }
|
|
674
843
|
pause() {
|
|
675
844
|
if (this.paused)
|
|
676
845
|
return;
|
|
@@ -775,13 +944,25 @@ export class BottomInput {
|
|
|
775
944
|
render(force = false) {
|
|
776
945
|
if (!this.active || this.resizeTimer || (this.paused && !this.overlay))
|
|
777
946
|
return;
|
|
947
|
+
this.agentSessions?.sync();
|
|
948
|
+
if (this.agentSessions && this.screen === this.agentSessions && this.agentSessions.activeId === "main")
|
|
949
|
+
this.screen = null;
|
|
778
950
|
if (this.scrollRenderTimer) {
|
|
779
951
|
clearTimeout(this.scrollRenderTimer);
|
|
780
952
|
this.scrollRenderTimer = undefined;
|
|
781
953
|
}
|
|
954
|
+
this.syncSessionComposer();
|
|
955
|
+
for (const [id, queue] of this.workerQueues)
|
|
956
|
+
if (!this.h.review?.sessions.get(id)?.live) {
|
|
957
|
+
queue.dispose();
|
|
958
|
+
this.workerQueues.delete(id);
|
|
959
|
+
}
|
|
960
|
+
for (const id of this.sessionDrafts.keys())
|
|
961
|
+
if (id !== "main" && !this.h.review?.sessions.get(id)?.live)
|
|
962
|
+
this.sessionDrafts.delete(id);
|
|
782
963
|
const H = this.rows();
|
|
783
964
|
const W = this.cols();
|
|
784
|
-
const alternate = !this.nativeScroll || this.screenVisible || this.overlay !== null || this.vassalOpen || this.scrollOffset > 0;
|
|
965
|
+
const alternate = !this.nativeScroll || this.screenVisible || this.workerVisible || this.overlay !== null || this.vassalOpen || this.scrollOffset > 0;
|
|
785
966
|
if (alternate !== this.alternateScreen) {
|
|
786
967
|
stdout.write(alternate ? ALT_ON + "\x1b[2J\x1b[H" + NATIVE_SELECTION_MODE : ALT_OFF + NATIVE_SCROLL_MODE);
|
|
787
968
|
this.alternateScreen = alternate;
|
|
@@ -790,8 +971,10 @@ export class BottomInput {
|
|
|
790
971
|
force = true;
|
|
791
972
|
}
|
|
792
973
|
if (this.screenVisible) {
|
|
793
|
-
const
|
|
794
|
-
const
|
|
974
|
+
const warnings = (this.h.warningLines?.() ?? []).slice(0, Math.max(0, H - 3));
|
|
975
|
+
const bodyHeight = H - warnings.length;
|
|
976
|
+
const content = this.screen.render(W, bodyHeight);
|
|
977
|
+
const rows = Array.from({ length: H }, (_, i) => fitLine((i < bodyHeight ? content[i] : warnings[i - bodyHeight]) ?? "", Math.max(1, W - 1)));
|
|
795
978
|
this.paint(rows, W, H, 1, 1, force, true);
|
|
796
979
|
return;
|
|
797
980
|
}
|
|
@@ -809,9 +992,14 @@ export class BottomInput {
|
|
|
809
992
|
}
|
|
810
993
|
const lay = this.liveContent();
|
|
811
994
|
this.lastLayout = lay;
|
|
995
|
+
if (this.overlay && lay.lines.length >= H - 1) {
|
|
996
|
+
const frame = Array.from({ length: H }, (_, i) => fitLine(lay.lines[i] ?? "", W - 1));
|
|
997
|
+
this.paint(frame, W, H, Math.min(H, lay.cursorRow + 1), lay.cursorCol, force, true);
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
812
1000
|
if (this.nativeScroll && !alternate) {
|
|
813
1001
|
this.renderedLog();
|
|
814
|
-
const output = this.nativeOutput.render(this.renderedEntries, lay, W, H, false, this.h.startupLines?.() ?? this.h.headerLines?.() ?? []);
|
|
1002
|
+
const output = this.nativeOutput.render(this.renderedEntries, lay, W, H, false, this.h.startupLines?.() ?? this.h.headerLines?.() ?? [], chunk => { stdout.write(chunk); });
|
|
815
1003
|
if (output)
|
|
816
1004
|
stdout.write(output);
|
|
817
1005
|
this.lastPaintAt = Date.now();
|
|
@@ -819,10 +1007,17 @@ export class BottomInput {
|
|
|
819
1007
|
return;
|
|
820
1008
|
}
|
|
821
1009
|
const liveH = Math.min(lay.lines.length, H - 1);
|
|
822
|
-
const requestedHeader = this.h.headerLines?.() ?? [];
|
|
1010
|
+
const requestedHeader = this.workerVisible ? [] : this.h.headerLines?.() ?? [];
|
|
823
1011
|
const headerH = Math.min(requestedHeader.length, Math.max(0, H - liveH - 1));
|
|
824
1012
|
const header = requestedHeader.slice(0, headerH);
|
|
825
1013
|
const chatRows = Math.max(1, H - liveH - headerH);
|
|
1014
|
+
if (this.workerVisible && !this.overlay && !this.modal) {
|
|
1015
|
+
const visible = this.agentSessions.conversation(W, chatRows);
|
|
1016
|
+
const frame = [...header, ...Array.from({ length: chatRows }, (_, i) => visible[i] ?? ""), ...lay.lines.slice(0, liveH)];
|
|
1017
|
+
this.liveStartRow = headerH + chatRows + 1;
|
|
1018
|
+
this.paint(frame.map(line => fitLine(line, W - 1)), W, H, Math.min(H, this.liveStartRow + lay.cursorRow), lay.cursorCol, force);
|
|
1019
|
+
return;
|
|
1020
|
+
}
|
|
826
1021
|
const rendered = this.renderedLog();
|
|
827
1022
|
const total = rendered.length;
|
|
828
1023
|
const maxOffset = Math.max(0, total - chatRows);
|
|
@@ -869,6 +1064,11 @@ export class BottomInput {
|
|
|
869
1064
|
this.lastPaintAt = Date.now();
|
|
870
1065
|
}
|
|
871
1066
|
scrollBy(delta) {
|
|
1067
|
+
if (this.workerVisible) {
|
|
1068
|
+
this.agentSessions.scroll(-delta);
|
|
1069
|
+
this.queueScrollRender();
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
872
1072
|
const next = Math.max(0, this.scrollOffset + delta);
|
|
873
1073
|
if (next === this.scrollOffset)
|
|
874
1074
|
return;
|
|
@@ -892,20 +1092,55 @@ export class BottomInput {
|
|
|
892
1092
|
layout() {
|
|
893
1093
|
const cols = this.cols();
|
|
894
1094
|
const lines = [];
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
if (
|
|
898
|
-
const
|
|
899
|
-
lines.push(
|
|
900
|
-
|
|
1095
|
+
const worker = this.workerVisible ? this.agentSessions.session : undefined;
|
|
1096
|
+
const hasRunningStatus = worker ? worker.running : this.compactionStartedAt !== null || this.statusLabel !== null;
|
|
1097
|
+
if (worker) {
|
|
1098
|
+
const style = this.h.statusStyle ?? ((s) => s);
|
|
1099
|
+
lines.push(fitLine(style(` ${worker.actor} · ${worker.compacting ? tr("Compattazione in corso", "Compacting") : worker.activity}`), cols - 1));
|
|
1100
|
+
if (worker.notice)
|
|
1101
|
+
lines.push(fitLine(style(" " + worker.notice), cols - 1));
|
|
1102
|
+
if (worker.compacting)
|
|
1103
|
+
lines.push(...renderCompactionStatus({ frame: this.frame,
|
|
1104
|
+
elapsedSeconds: (Date.now() - (worker.compactingAt ?? Date.now())) / 1000, columns: cols, style: this.h.statusStyle }));
|
|
1105
|
+
}
|
|
1106
|
+
if (!worker) {
|
|
1107
|
+
if (this.compactionStartedAt !== null) {
|
|
1108
|
+
// A blank row plus a framed block keeps compaction separate from the
|
|
1109
|
+
// transcript. The renderer diffs rows, so the shimmer cannot flicker the
|
|
1110
|
+
// surrounding chat, context meter or composer.
|
|
1111
|
+
lines.push("");
|
|
1112
|
+
lines.push(...renderCompactionStatus({
|
|
1113
|
+
frame: this.frame,
|
|
1114
|
+
elapsedSeconds: (Date.now() - this.compactionStartedAt) / 1000,
|
|
1115
|
+
columns: cols,
|
|
1116
|
+
style: this.h.statusStyle,
|
|
1117
|
+
}));
|
|
1118
|
+
}
|
|
1119
|
+
else if (this.statusLabel !== null && !this.sessionLimitNotice)
|
|
1120
|
+
lines.push(this.statusLine());
|
|
1121
|
+
// L'attivita', il misuratore di contesto e il composer sono tre superfici
|
|
1122
|
+
// diverse: una riga vuota impedisce che si fondano visivamente durante i run.
|
|
1123
|
+
if (hasRunningStatus)
|
|
1124
|
+
lines.push("");
|
|
1125
|
+
if (this.statusLabel === null && this.roomStatus?.state === "failed") {
|
|
1126
|
+
const notice = this.roomSegment().replace(/\s+/g, " ").trim();
|
|
1127
|
+
lines.push(...wrapText(notice, Math.max(8, cols - 3)).slice(0, 3).map(line => ` ${line}`));
|
|
1128
|
+
lines.push(fitLine(tr(" Ctrl+R dettagli", " Ctrl+R details"), cols - 1));
|
|
1129
|
+
}
|
|
901
1130
|
}
|
|
902
1131
|
// La riga live descrive gia' il team; la board completa si consulta a parte.
|
|
903
|
-
if (this.kingdomBoard && this.statusLabel === null)
|
|
1132
|
+
if (!worker && this.kingdomBoard && this.statusLabel === null)
|
|
904
1133
|
lines.push(fitLine(this.kingdomBoard[0] ?? "", cols - 1));
|
|
905
|
-
|
|
1134
|
+
if (!this.modal && !this.queueEditor && !this.questions.focused) {
|
|
1135
|
+
lines.push(...(this.agentSessions?.preview(cols, Math.min(5, Math.max(2, this.rows() - 12))) ?? []));
|
|
1136
|
+
}
|
|
1137
|
+
const footerLines = worker ? this.h.sessionFooter?.(worker, cols) ?? [] : this.h.footerLines();
|
|
1138
|
+
for (const f of footerLines)
|
|
906
1139
|
lines.push(f);
|
|
1140
|
+
if (hasRunningStatus && footerLines.length)
|
|
1141
|
+
lines.push("");
|
|
907
1142
|
if (!this.modal && !this.queueEditor)
|
|
908
|
-
lines.push(...queuePreview(this.
|
|
1143
|
+
lines.push(...queuePreview(this.composerQueue, cols, this.h.queueStyle, worker ? !worker.running : !this.running));
|
|
909
1144
|
const questionStart = lines.length;
|
|
910
1145
|
const questions = !this.modal && !this.queueEditor
|
|
911
1146
|
? this.questions.render(cols, Math.max(5, this.rows() - lines.length - 6), this.h.queueStyle)
|
|
@@ -940,7 +1175,7 @@ export class BottomInput {
|
|
|
940
1175
|
const inputCap = Math.max(1, Math.min(3, Math.floor(this.rows() / 4)));
|
|
941
1176
|
const rowOffset = Math.max(0, Math.min(crow - Math.floor(inputCap / 2), rows.length - inputCap));
|
|
942
1177
|
const visibleRows = rows.slice(rowOffset, rowOffset + inputCap);
|
|
943
|
-
const padding = !this.modal && !this.queueEditor && this.rows() >= 16 && this.h.composerPadding?.();
|
|
1178
|
+
const padding = !worker && !this.modal && !this.queueEditor && this.rows() >= 16 && this.h.composerPadding?.();
|
|
944
1179
|
const blank = " " + (this.h.composerStyle?.(" ".repeat(cell)) ?? " ".repeat(cell));
|
|
945
1180
|
if (padding)
|
|
946
1181
|
lines.push(blank);
|
|
@@ -959,10 +1194,17 @@ export class BottomInput {
|
|
|
959
1194
|
const notice = ` ${this.roomStatus.label}`;
|
|
960
1195
|
lines.push(fitLine(this.h.statusStyle?.(notice) ?? notice, cols - 1));
|
|
961
1196
|
}
|
|
1197
|
+
if (this.sessionLimitNotice && this.sessionLimitNotice.model !== this.h.currentModel?.())
|
|
1198
|
+
this.sessionLimitNotice = null;
|
|
962
1199
|
if (!this.modal) {
|
|
963
|
-
for (const
|
|
1200
|
+
for (const naming of this.namingNotice.lines()) {
|
|
1201
|
+
lines.push(fitLine(" " + (this.h.statusStyle?.(naming) ?? naming), cols - 1));
|
|
1202
|
+
}
|
|
1203
|
+
for (const row of (worker ? this.h.sessionBelowInput?.(worker, cols) ?? [] : this.h.belowInputLines?.() ?? []))
|
|
964
1204
|
lines.push(fitLine(row, cols - 1));
|
|
965
1205
|
}
|
|
1206
|
+
for (const row of this.h.warningLines?.() ?? [])
|
|
1207
|
+
lines.push(fitLine(row, cols - 1));
|
|
966
1208
|
const action = this.ctrlCConfirmation?.action;
|
|
967
1209
|
const warning = action === "stop" ? tr("fermare il lavoro e mettere in pausa la coda", "stop work and pause the queue") : action === "clear" ? tr("svuotare la bozza", "clear the draft") : tr("uscire", "exit");
|
|
968
1210
|
const hint = action
|
|
@@ -983,17 +1225,15 @@ export class BottomInput {
|
|
|
983
1225
|
rowLengths: visibleRows.map((r) => r.length),
|
|
984
1226
|
};
|
|
985
1227
|
}
|
|
986
|
-
// Segmento VIVO della stanza Danilov
|
|
987
|
-
//
|
|
1228
|
+
// Segmento VIVO della stanza Danilov. Durante un run resta testuale: un solo
|
|
1229
|
+
// shimmer viene applicato all'intera frase dalla status-line.
|
|
988
1230
|
roomSegment() {
|
|
989
1231
|
if (!this.roomStatus || this.roomStatus.state === "done")
|
|
990
1232
|
return "";
|
|
991
1233
|
const style = this.h.statusStyle ?? ((s) => s);
|
|
992
1234
|
const { label, state } = this.roomStatus;
|
|
993
|
-
if (state === "running")
|
|
994
|
-
|
|
995
|
-
return `${g} ${label}`;
|
|
996
|
-
}
|
|
1235
|
+
if (state === "running")
|
|
1236
|
+
return label;
|
|
997
1237
|
const glyph = state === "failed" ? "✗" : "✦";
|
|
998
1238
|
return style(`${glyph} ${label}`);
|
|
999
1239
|
}
|
|
@@ -1001,20 +1241,21 @@ export class BottomInput {
|
|
|
1001
1241
|
const elapsed = Math.round((Date.now() - this.startedAt) / 1000);
|
|
1002
1242
|
const style = this.h.statusStyle ?? ((s) => s);
|
|
1003
1243
|
const label = this.statusLabel ?? tr("Elaborazione", "Working");
|
|
1004
|
-
const
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
const right = style(
|
|
1008
|
-
? tr("Alt+S subito · Esc ferma", "Alt+S send now · Esc stop") : tr("Esc ferma", "Esc stop"));
|
|
1244
|
+
const meta = [this.h.runningHint?.() ?? "", this.roomSegment()].filter(Boolean);
|
|
1245
|
+
const shortcut = this.cols() >= 100 && (!this.composerQueue.length || this.modal || this.queueEditor)
|
|
1246
|
+
? tr("Alt+S subito · Esc ferma", "Alt+S send now · Esc stop") : tr("Esc ferma", "Esc stop");
|
|
1247
|
+
const right = style(`${fmtElapsed(elapsed).padStart(7)} · ${shortcut}`);
|
|
1009
1248
|
const budget = Math.max(0, this.cols() - terminalWidth(right) - 5);
|
|
1010
|
-
let
|
|
1249
|
+
let text = label;
|
|
1011
1250
|
for (const segment of meta) {
|
|
1012
|
-
const candidate =
|
|
1013
|
-
if (terminalWidth(candidate) > budget)
|
|
1251
|
+
const candidate = text + ` · ${plainText(segment)}`;
|
|
1252
|
+
if (terminalWidth(candidate) + 2 > budget)
|
|
1014
1253
|
break;
|
|
1015
|
-
|
|
1254
|
+
text = candidate;
|
|
1016
1255
|
}
|
|
1017
|
-
|
|
1256
|
+
// La geometria non cambia tra i frame: si muove soltanto una sottile banda
|
|
1257
|
+
// di luce sull'intero stato, evitando due o tre spinner concorrenti.
|
|
1258
|
+
let left = fitLine(` ${shimmerText(text, this.frame)}`, budget);
|
|
1018
1259
|
return fitLine(left + " ".repeat(Math.max(2, this.cols() - 2 - terminalWidth(left) - terminalWidth(right))) + right, this.cols() - 1);
|
|
1019
1260
|
}
|
|
1020
1261
|
// ── input ─────────────────────────────────────────────────────────────────
|
|
@@ -1126,6 +1367,9 @@ export class BottomInput {
|
|
|
1126
1367
|
this.scrollOffset = 0;
|
|
1127
1368
|
this.render();
|
|
1128
1369
|
}
|
|
1370
|
+
else if (this.workerVisible) {
|
|
1371
|
+
this.stopRun();
|
|
1372
|
+
}
|
|
1129
1373
|
else if (this.running) {
|
|
1130
1374
|
this.stopRun();
|
|
1131
1375
|
}
|
|
@@ -1188,7 +1432,7 @@ export class BottomInput {
|
|
|
1188
1432
|
}
|
|
1189
1433
|
// Freccia su/giu' NUDA: disambigua scroll(touchpad) vs pressione (timer).
|
|
1190
1434
|
if ((name === "up" || name === "down") && !ctrl && !meta && !shift && !this.screenVisible
|
|
1191
|
-
&& !this.questions.focused && (!this.nativeScroll || this.scrollOffset > 0)) {
|
|
1435
|
+
&& !this.questions.focused && !this.sessionArrow(name) && (!this.nativeScroll || this.scrollOffset > 0)) {
|
|
1192
1436
|
this.onArrow(name);
|
|
1193
1437
|
return;
|
|
1194
1438
|
}
|
|
@@ -1268,7 +1512,31 @@ export class BottomInput {
|
|
|
1268
1512
|
this.showQueue();
|
|
1269
1513
|
return;
|
|
1270
1514
|
}
|
|
1271
|
-
if (k.meta && k.name === "
|
|
1515
|
+
if (k.meta && k.name === "a") {
|
|
1516
|
+
this.showAgents();
|
|
1517
|
+
return;
|
|
1518
|
+
}
|
|
1519
|
+
if (!k.ctrl && !k.meta && !k.shift && this.sessionArrow(k.name ?? "") && (k.name === "up" || k.name === "down")) {
|
|
1520
|
+
this.agentSessions.key(s, k);
|
|
1521
|
+
this.render();
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
if (this.agentSessions?.focused && !this.modal && !this.overlay && !this.queueEditor) {
|
|
1525
|
+
if (k.name === "delete" && !k.ctrl && !k.meta && !k.shift) {
|
|
1526
|
+
this.agentSessions.key(s, k);
|
|
1527
|
+
this.render();
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
if (k.name === "return" || k.name === "enter") {
|
|
1531
|
+
this.agentSessions.key(s, k);
|
|
1532
|
+
this.screen = this.agentSessions.activeId !== "main" ? this.agentSessions : null;
|
|
1533
|
+
this.agentSessions.focused = this.agentSessions.activeId !== "main";
|
|
1534
|
+
this.render();
|
|
1535
|
+
return;
|
|
1536
|
+
}
|
|
1537
|
+
this.agentSessions.focused = false;
|
|
1538
|
+
}
|
|
1539
|
+
if (!this.workerVisible && k.meta && k.name === "t" && !this.modal && !this.overlay && !this.queueEditor) {
|
|
1272
1540
|
if (this.queue.sendToTeam())
|
|
1273
1541
|
this.startNextQueued();
|
|
1274
1542
|
else
|
|
@@ -1286,6 +1554,10 @@ export class BottomInput {
|
|
|
1286
1554
|
}
|
|
1287
1555
|
if (k.ctrl && k.name === "c")
|
|
1288
1556
|
return this.onCtrlC();
|
|
1557
|
+
if (k.ctrl && k.name === "g" && this.agentSessions?.available) {
|
|
1558
|
+
this.showAgents();
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1289
1561
|
if (k.ctrl && k.name === "g") {
|
|
1290
1562
|
// Con un regno attivo: Ctrl+G entra nel viewer e cicla i vassalli (poi esce).
|
|
1291
1563
|
if (this.vassals && this.vassals.length) {
|
|
@@ -1305,7 +1577,15 @@ export class BottomInput {
|
|
|
1305
1577
|
return;
|
|
1306
1578
|
}
|
|
1307
1579
|
if (k.ctrl && k.name === "b") {
|
|
1308
|
-
this.
|
|
1580
|
+
if (this.workerVisible) {
|
|
1581
|
+
const session = this.agentSessions.session;
|
|
1582
|
+
const result = this.h.onSessionBackground?.(session);
|
|
1583
|
+
this.h.review?.sessions.notify(session.id, result?.ok ? tr("Comando in background", "Command backgrounded")
|
|
1584
|
+
: result?.error ?? tr("Nessun comando in primo piano", "No foreground command"));
|
|
1585
|
+
this.render();
|
|
1586
|
+
}
|
|
1587
|
+
else
|
|
1588
|
+
this.h.onBackground?.();
|
|
1309
1589
|
return;
|
|
1310
1590
|
}
|
|
1311
1591
|
// Ctrl+V (e Alt+V come alias): incolla un'immagine dalla clipboard di sistema
|
|
@@ -1322,6 +1602,11 @@ export class BottomInput {
|
|
|
1322
1602
|
return;
|
|
1323
1603
|
}
|
|
1324
1604
|
// Scroll della CHAT (input resta fisso): PageUp/Down, Shift+frecce.
|
|
1605
|
+
if (this.workerVisible && k.ctrl && (k.name === "home" || k.name === "end")) {
|
|
1606
|
+
this.agentSessions.key("", { name: k.name });
|
|
1607
|
+
this.render();
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1325
1610
|
if (k.name === "pageup")
|
|
1326
1611
|
return this.scrollBy(this.pageStep());
|
|
1327
1612
|
if (k.name === "pagedown")
|
|
@@ -1524,6 +1809,20 @@ export class BottomInput {
|
|
|
1524
1809
|
this.edit(() => { this.cursor = Math.max(0, this.cursor - tw); });
|
|
1525
1810
|
return;
|
|
1526
1811
|
}
|
|
1812
|
+
// Sul composer vuoto la voce piu recente ancora in coda torna modificabile.
|
|
1813
|
+
// Un buffer gia compilato conserva invece la normale navigazione verticale
|
|
1814
|
+
// e della cronologia, senza perdere la bozza dell'utente.
|
|
1815
|
+
if (!this.buffer && this.histIdx === -1) {
|
|
1816
|
+
const queued = this.composerQueue.recallLast();
|
|
1817
|
+
if (queued) {
|
|
1818
|
+
this.inputGeneration += 1;
|
|
1819
|
+
this.buffer = queued.text;
|
|
1820
|
+
this.cursor = queued.text.length;
|
|
1821
|
+
this.draft = "";
|
|
1822
|
+
this.render();
|
|
1823
|
+
return;
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1527
1826
|
this.historyPrev();
|
|
1528
1827
|
}
|
|
1529
1828
|
onDown() {
|
|
@@ -1580,7 +1879,8 @@ export class BottomInput {
|
|
|
1580
1879
|
this.resolveModal(null);
|
|
1581
1880
|
return;
|
|
1582
1881
|
}
|
|
1583
|
-
const action = this.running ? "stop" : this.buffer ? "clear" : "quit"
|
|
1882
|
+
const action = this.workerVisible ? (this.agentSessions.session.running ? "stop" : this.buffer ? "clear" : "quit")
|
|
1883
|
+
: this.running ? "stop" : this.buffer ? "clear" : "quit";
|
|
1584
1884
|
if (this.ctrlCConfirmation?.action === action && Date.now() < this.ctrlCConfirmation.until) {
|
|
1585
1885
|
this.cancelCtrlC();
|
|
1586
1886
|
if (action === "stop") {
|
|
@@ -1596,7 +1896,13 @@ export class BottomInput {
|
|
|
1596
1896
|
this.render();
|
|
1597
1897
|
return;
|
|
1598
1898
|
}
|
|
1599
|
-
this.
|
|
1899
|
+
if (this.workerVisible) {
|
|
1900
|
+
this.agentSessions.returnToMain();
|
|
1901
|
+
this.screen = null;
|
|
1902
|
+
this.render();
|
|
1903
|
+
}
|
|
1904
|
+
else
|
|
1905
|
+
this.quit();
|
|
1600
1906
|
return;
|
|
1601
1907
|
}
|
|
1602
1908
|
this.cancelCtrlC();
|
|
@@ -1613,11 +1919,18 @@ export class BottomInput {
|
|
|
1613
1919
|
}
|
|
1614
1920
|
stopRun() {
|
|
1615
1921
|
this.cancelCtrlC();
|
|
1922
|
+
if (this.workerVisible) {
|
|
1923
|
+
const session = this.agentSessions.session;
|
|
1924
|
+
this.composerQueue.paused = true;
|
|
1925
|
+
const result = this.h.onSessionStop?.(session);
|
|
1926
|
+
this.h.review?.sessions.notify(session.id, result?.ok ? tr("Interruzione richiesta", "Stop requested") : result?.error ?? tr("Interruzione non disponibile", "Stop unavailable"));
|
|
1927
|
+
return;
|
|
1928
|
+
}
|
|
1616
1929
|
const queued = this.queue.length;
|
|
1617
1930
|
this.queue.paused = true;
|
|
1618
1931
|
this.runAbort?.abort();
|
|
1619
1932
|
if (queued)
|
|
1620
|
-
this.setRoomStatus(tr("Lavoro interrotto. {queued} messaggi conservati in pausa · Alt+Q o /queue per gestirli.", "Work interrupted. {queued} messages paused · Alt+Q or /queue to manage.", { queued }), "
|
|
1933
|
+
this.setRoomStatus(tr("Lavoro interrotto. {queued} messaggi conservati in pausa · Alt+Q o /queue per gestirli.", "Work interrupted. {queued} messages paused · Alt+Q or /queue to manage.", { queued }), "done");
|
|
1621
1934
|
}
|
|
1622
1935
|
// Domanda a risposta libera: l'editing finisce nel buffer, Invio risolve.
|
|
1623
1936
|
queueQuestion(title, options, signal) {
|
|
@@ -1677,6 +1990,17 @@ export class BottomInput {
|
|
|
1677
1990
|
this.cursor = 0;
|
|
1678
1991
|
this.histIdx = -1;
|
|
1679
1992
|
if (!line.trim()) {
|
|
1993
|
+
if (this.workerVisible) {
|
|
1994
|
+
const queue = this.composerQueue;
|
|
1995
|
+
const next = queue.items()[0];
|
|
1996
|
+
if (immediate && next)
|
|
1997
|
+
queue.sendNow(next.id);
|
|
1998
|
+
else
|
|
1999
|
+
queue.paused = false;
|
|
2000
|
+
this.h.wakeSession?.(this.agentSessions.session);
|
|
2001
|
+
this.render();
|
|
2002
|
+
return;
|
|
2003
|
+
}
|
|
1680
2004
|
if (immediate) {
|
|
1681
2005
|
const next = this.queue.items().find(entry => !entry.text.trimStart().startsWith("/"));
|
|
1682
2006
|
if (next)
|
|
@@ -1692,21 +2016,61 @@ export class BottomInput {
|
|
|
1692
2016
|
this.showQueue();
|
|
1693
2017
|
return;
|
|
1694
2018
|
}
|
|
2019
|
+
if (/^\/(?:agents|agenti)\s*$/i.test(line.trim())) {
|
|
2020
|
+
this.showAgents();
|
|
2021
|
+
return;
|
|
2022
|
+
}
|
|
1695
2023
|
const review = /^\/(?:review|revisione)(?:\s+(\S+))?\s*$/i.exec(line.trim());
|
|
1696
2024
|
if (review && this.h.createReviewScreen) {
|
|
1697
2025
|
this.showReview(review[1]);
|
|
1698
2026
|
return;
|
|
1699
2027
|
}
|
|
2028
|
+
if (this.workerVisible) {
|
|
2029
|
+
const session = this.agentSessions.session;
|
|
2030
|
+
const queue = !line.trimStart().startsWith("/") ? this.ensureWorkerQueue() : undefined;
|
|
2031
|
+
if (queue) {
|
|
2032
|
+
try {
|
|
2033
|
+
queue.add(line, immediate ? "immediate" : "during-turn");
|
|
2034
|
+
if (!session.running)
|
|
2035
|
+
queue.paused = false;
|
|
2036
|
+
const result = this.h.wakeSession?.(session);
|
|
2037
|
+
if (result && !result.ok)
|
|
2038
|
+
this.h.review?.sessions.notify(session.id, result.error ?? "Invio non disponibile");
|
|
2039
|
+
}
|
|
2040
|
+
catch (error) {
|
|
2041
|
+
this.buffer = line;
|
|
2042
|
+
this.cursor = line.length;
|
|
2043
|
+
this.h.review?.sessions.notify(session.id, String(error));
|
|
2044
|
+
}
|
|
2045
|
+
this.render();
|
|
2046
|
+
return;
|
|
2047
|
+
}
|
|
2048
|
+
const result = this.h.onSessionSubmit?.(session, line, this.h.prepareQueuedInput?.(line) ?? [{ type: "text", text: line }], immediate);
|
|
2049
|
+
if (result?.ok)
|
|
2050
|
+
this.history.push(line);
|
|
2051
|
+
else {
|
|
2052
|
+
this.buffer = line;
|
|
2053
|
+
this.cursor = line.length;
|
|
2054
|
+
}
|
|
2055
|
+
this.h.review?.sessions.notify(session.id, result?.ok ? tr("Messaggio in attesa di consegna", "Message awaiting delivery")
|
|
2056
|
+
: result?.error ?? tr("Invio non disponibile", "Send unavailable"));
|
|
2057
|
+
this.render();
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
1700
2060
|
this.dispatch(line, immediate);
|
|
1701
2061
|
}
|
|
1702
2062
|
dispatch(line, immediate = false) {
|
|
2063
|
+
if (line.trimStart().startsWith("/") && (this.running || this.commandPending)) {
|
|
2064
|
+
this.runLiveCommand(line.trim());
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
1703
2067
|
// Una coda ripristinata in pausa non deve bloccare /new, /resume, /help
|
|
1704
2068
|
// o /quit. A riposo i comandi controllano la UI senza riprendere la coda.
|
|
1705
2069
|
if (!this.running && line.trimStart().startsWith("/")) {
|
|
1706
2070
|
this.runLine(line);
|
|
1707
2071
|
return;
|
|
1708
2072
|
}
|
|
1709
|
-
if (this.running || this.queue.length) {
|
|
2073
|
+
if (this.running || this.commandPending || this.queue.length) {
|
|
1710
2074
|
try {
|
|
1711
2075
|
this.queue.add(line, immediate ? "immediate" : "after-turn");
|
|
1712
2076
|
// Aprire una chat non avvia lavoro da solo. Un nuovo Invio a riposo
|
|
@@ -1729,7 +2093,7 @@ export class BottomInput {
|
|
|
1729
2093
|
this.runLine(line);
|
|
1730
2094
|
}
|
|
1731
2095
|
startNextQueued() {
|
|
1732
|
-
if (!this.active || this.running || this.modal || this.overlay || this.queueEditor)
|
|
2096
|
+
if (!this.active || this.running || this.commandPending || this.modal || this.overlay || this.queueEditor)
|
|
1733
2097
|
return;
|
|
1734
2098
|
const next = this.queue.takeNext();
|
|
1735
2099
|
if (next) {
|
|
@@ -1737,9 +2101,34 @@ export class BottomInput {
|
|
|
1737
2101
|
this.runLine(next.text, true, next);
|
|
1738
2102
|
}
|
|
1739
2103
|
}
|
|
2104
|
+
runLiveCommand(line) {
|
|
2105
|
+
if (this.commandPending) {
|
|
2106
|
+
this.buffer = line;
|
|
2107
|
+
this.cursor = line.length;
|
|
2108
|
+
this.setRoomStatus(tr("Chiudi prima il comando aperto.", "Close the open command first."), "done");
|
|
2109
|
+
return;
|
|
2110
|
+
}
|
|
2111
|
+
if (!this.h.onCommandDuringRun) {
|
|
2112
|
+
this.setRoomStatus(tr("Comando non disponibile durante il lavoro. Riprova a fine turno.", "Command unavailable while working. Retry after the turn."), "done");
|
|
2113
|
+
return;
|
|
2114
|
+
}
|
|
2115
|
+
this.history.push(line);
|
|
2116
|
+
this.commandPending = true;
|
|
2117
|
+
// A UI command must not replace the agent's abort controller or reset its metrics.
|
|
2118
|
+
const signal = new AbortController().signal;
|
|
2119
|
+
Promise.resolve().then(() => this.h.onCommandDuringRun(line, signal))
|
|
2120
|
+
.catch(error => this.reportFailure(`${tr("Errore", "Error")}: ${error instanceof Error ? error.message : String(error)}`))
|
|
2121
|
+
.finally(() => {
|
|
2122
|
+
this.commandPending = false;
|
|
2123
|
+
if (!this.active)
|
|
2124
|
+
return;
|
|
2125
|
+
this.startNextQueued();
|
|
2126
|
+
this.render();
|
|
2127
|
+
});
|
|
2128
|
+
}
|
|
1740
2129
|
runLine(line, fromQueue = false, queued) {
|
|
1741
2130
|
this.setRoomStatus(null);
|
|
1742
|
-
this.history.push(line);
|
|
2131
|
+
(this.composerSession === "main" ? this.history : this.sessionDrafts.get("main")?.history)?.push(line);
|
|
1743
2132
|
this.running = true;
|
|
1744
2133
|
this.startedAt = Date.now();
|
|
1745
2134
|
this.tokensNew = 0;
|
|
@@ -1768,6 +2157,11 @@ export class BottomInput {
|
|
|
1768
2157
|
if (!this.active)
|
|
1769
2158
|
return;
|
|
1770
2159
|
this.active = false;
|
|
2160
|
+
this.namingNotice.clear();
|
|
2161
|
+
this.sessionsUnsubscribe?.();
|
|
2162
|
+
this.messagesUnsubscribe?.();
|
|
2163
|
+
if (this.sessionsTimer)
|
|
2164
|
+
clearInterval(this.sessionsTimer);
|
|
1771
2165
|
this.questions.clear();
|
|
1772
2166
|
this.clearPendingEscape();
|
|
1773
2167
|
this.cancelCtrlC();
|
|
@@ -1802,6 +2196,7 @@ export class BottomInput {
|
|
|
1802
2196
|
if (this.nativeScroll)
|
|
1803
2197
|
stdout.write(this.nativeOutput.release());
|
|
1804
2198
|
stdout.write(PASTE_OFF + NATIVE_SCROLL_MODE + "\x1b[?25h");
|
|
2199
|
+
this.nativeOutput.dispose();
|
|
1805
2200
|
if (typeof stdin.setRawMode === "function")
|
|
1806
2201
|
stdin.setRawMode(false);
|
|
1807
2202
|
this.h.onQuit();
|