@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/bottom-input.js
CHANGED
|
@@ -8,6 +8,7 @@ import { NativeScrollback } from "./native-scrollback.js";
|
|
|
8
8
|
import { QueuedQuestions } from "./queued-questions.js";
|
|
9
9
|
import { AgentSessionsScreen } from "./agent-sessions-screen.js";
|
|
10
10
|
import { SessionNamingNotice } from "./session-naming-notice.js";
|
|
11
|
+
import { renderCompactionStatus, shimmerText } from "./compaction-status.js";
|
|
11
12
|
const SYNC_ON = "\x1b[?2026h";
|
|
12
13
|
const SYNC_OFF = "\x1b[?2026l";
|
|
13
14
|
const ALT_ON = "\x1b[?1049h";
|
|
@@ -23,7 +24,6 @@ export const NATIVE_SCROLL_MODE = "\x1b[?1000l\x1b[?1002l\x1b[?1003l\x1b[?1006l\
|
|
|
23
24
|
// crudo nel buffer (parita' Claude Code).
|
|
24
25
|
const PASTE_ON = "\x1b[?2004h";
|
|
25
26
|
const PASTE_OFF = "\x1b[?2004l";
|
|
26
|
-
const SPINNER = ["✶", "✸", "✺", "✹", "✷", "✦"];
|
|
27
27
|
const MAX_LOG = 5000;
|
|
28
28
|
const SCROLL_FRAME_MS = 16;
|
|
29
29
|
const RESIZE_SETTLE_MS = 80;
|
|
@@ -49,6 +49,8 @@ export class BottomInput {
|
|
|
49
49
|
buffer = "";
|
|
50
50
|
cursor = 0;
|
|
51
51
|
history = [];
|
|
52
|
+
composerSession = "main";
|
|
53
|
+
sessionDrafts = new Map();
|
|
52
54
|
histIdx = -1;
|
|
53
55
|
draft = "";
|
|
54
56
|
active = false;
|
|
@@ -56,9 +58,35 @@ export class BottomInput {
|
|
|
56
58
|
ctrlCConfirmation = null;
|
|
57
59
|
ctrlCTimer;
|
|
58
60
|
running = false;
|
|
61
|
+
commandPending = false;
|
|
59
62
|
questions = new QueuedQuestions(() => this.refresh());
|
|
60
63
|
runAbort;
|
|
61
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
|
+
}
|
|
62
90
|
queueEditor = null;
|
|
63
91
|
// Log della chat (righe gia' formattate) + scroll del viewport (0 = in fondo).
|
|
64
92
|
log = [];
|
|
@@ -90,12 +118,15 @@ export class BottomInput {
|
|
|
90
118
|
screen = null;
|
|
91
119
|
agentSessions;
|
|
92
120
|
sessionsUnsubscribe;
|
|
121
|
+
messagesUnsubscribe;
|
|
93
122
|
sessionsTimer;
|
|
94
123
|
// Domanda a risposta libera (tool ask_user): quando attiva, l'editing va alla
|
|
95
124
|
// risposta e Invio/Esc risolvono la promise invece di inviare un prompt.
|
|
96
125
|
modal = null;
|
|
97
126
|
statusLabel = null;
|
|
127
|
+
sessionLimitNotice = null;
|
|
98
128
|
compactionStartedAt = null;
|
|
129
|
+
compactionPreviousStatus = null;
|
|
99
130
|
// Nome GREZZO del tool attualmente in corso (es. "read_file"): pilota colore e
|
|
100
131
|
// glyph animati nella status-line (pulse abbinato alla categoria). null quando
|
|
101
132
|
// l'attivita' non e' un singolo tool (es. "elaboro", ondata mista).
|
|
@@ -146,16 +177,24 @@ export class BottomInput {
|
|
|
146
177
|
this.nativeScroll = h.nativeScroll !== false;
|
|
147
178
|
this.queue = this.createQueue();
|
|
148
179
|
if (h.review)
|
|
149
|
-
this.agentSessions = new AgentSessionsScreen(h.review.sessions, h.queueStyle);
|
|
180
|
+
this.agentSessions = new AgentSessionsScreen(h.review.sessions, h.queueStyle, h.onSessionDelete);
|
|
150
181
|
}
|
|
151
182
|
createQueue() {
|
|
152
|
-
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);
|
|
153
184
|
}
|
|
154
185
|
restoreQueue() {
|
|
155
186
|
this.inputGeneration++;
|
|
156
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();
|
|
157
196
|
this.queueEditor = null;
|
|
158
|
-
if (this.screen instanceof QueueScreen)
|
|
197
|
+
if (this.screen instanceof QueueScreen || this.screen === this.agentSessions)
|
|
159
198
|
this.screen = null;
|
|
160
199
|
this.render();
|
|
161
200
|
}
|
|
@@ -164,7 +203,7 @@ export class BottomInput {
|
|
|
164
203
|
acceptQueuedMessage(id) {
|
|
165
204
|
const text = this.queue.acknowledge(id);
|
|
166
205
|
if (text)
|
|
167
|
-
this.history.push(text);
|
|
206
|
+
(this.composerSession === "main" ? this.history : this.sessionDrafts.get("main")?.history)?.push(text);
|
|
168
207
|
return text;
|
|
169
208
|
}
|
|
170
209
|
pauseQueuedMessages() { if (this.queue.length) {
|
|
@@ -180,19 +219,21 @@ export class BottomInput {
|
|
|
180
219
|
this.arrowTimer = undefined;
|
|
181
220
|
}
|
|
182
221
|
this.pendingArrows = [];
|
|
183
|
-
this.
|
|
184
|
-
this.screen = new QueueScreen(this.
|
|
222
|
+
this.composerQueue.held = true;
|
|
223
|
+
this.screen = new QueueScreen(this.composerQueue, id => this.editQueuedMessage(id), this.h.queueStyle);
|
|
185
224
|
this.render();
|
|
186
225
|
}
|
|
187
226
|
closeScreen() {
|
|
188
227
|
if (this.screen instanceof QueueScreen)
|
|
189
|
-
this.
|
|
190
|
-
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);
|
|
191
232
|
this.startNextQueued();
|
|
192
233
|
this.render();
|
|
193
234
|
}
|
|
194
235
|
editQueuedMessage(id) {
|
|
195
|
-
const entry = this.
|
|
236
|
+
const entry = this.composerQueue.get(id);
|
|
196
237
|
if (!entry || !(this.screen instanceof QueueScreen))
|
|
197
238
|
return;
|
|
198
239
|
this.inputGeneration++;
|
|
@@ -209,7 +250,7 @@ export class BottomInput {
|
|
|
209
250
|
return;
|
|
210
251
|
if (save) {
|
|
211
252
|
try {
|
|
212
|
-
if (!this.
|
|
253
|
+
if (!this.composerQueue.update(editor.id, this.buffer))
|
|
213
254
|
throw new Error("Il messaggio non è più in attesa.");
|
|
214
255
|
}
|
|
215
256
|
catch (error) {
|
|
@@ -222,7 +263,7 @@ export class BottomInput {
|
|
|
222
263
|
this.histIdx = editor.draft.histIdx;
|
|
223
264
|
this.queueEditor = null;
|
|
224
265
|
this.screen = editor.screen;
|
|
225
|
-
if (immediate && this.
|
|
266
|
+
if (immediate && this.composerQueue.sendNow(editor.id)) {
|
|
226
267
|
this.closeScreen();
|
|
227
268
|
return;
|
|
228
269
|
}
|
|
@@ -240,7 +281,36 @@ export class BottomInput {
|
|
|
240
281
|
this.screen = this.h.createReviewScreen(section);
|
|
241
282
|
this.render();
|
|
242
283
|
}
|
|
243
|
-
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
|
+
}
|
|
244
314
|
showAgents() {
|
|
245
315
|
if (!this.agentSessions?.available || this.modal || this.overlay || this.queueEditor || this.queue.held)
|
|
246
316
|
return;
|
|
@@ -260,7 +330,18 @@ export class BottomInput {
|
|
|
260
330
|
if (!BottomInput.supported())
|
|
261
331
|
return Promise.resolve();
|
|
262
332
|
this.active = true;
|
|
263
|
-
|
|
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
|
+
});
|
|
264
345
|
this.sessionsTimer = setInterval(() => {
|
|
265
346
|
if (this.agentSessions?.available || this.screen === this.agentSessions)
|
|
266
347
|
this.refresh();
|
|
@@ -345,6 +426,7 @@ export class BottomInput {
|
|
|
345
426
|
this.vassalOpen = false;
|
|
346
427
|
this.kingdomBoard = null;
|
|
347
428
|
this.statusLabel = null;
|
|
429
|
+
this.sessionLimitNotice = null;
|
|
348
430
|
this.statusTool = null;
|
|
349
431
|
this.roomStatus = null;
|
|
350
432
|
this.tokensNew = this.tokensCache = this.tokensOut = 0;
|
|
@@ -422,6 +504,13 @@ export class BottomInput {
|
|
|
422
504
|
if (label === null)
|
|
423
505
|
this.compactionStartedAt = null;
|
|
424
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();
|
|
425
514
|
if (label !== null) {
|
|
426
515
|
if (!this.spinnerTimer)
|
|
427
516
|
this.spinnerTimer = setInterval(() => { this.frame += 1; this.render(); }, 140);
|
|
@@ -434,13 +523,27 @@ export class BottomInput {
|
|
|
434
523
|
this.render();
|
|
435
524
|
}
|
|
436
525
|
setCompacting(active) {
|
|
437
|
-
|
|
438
|
-
|
|
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);
|
|
439
538
|
}
|
|
440
539
|
showSessionNaming(title) {
|
|
441
540
|
this.namingNotice.show(title);
|
|
442
541
|
}
|
|
443
542
|
setSessionNaming(phase, title) {
|
|
543
|
+
if (this.sessionLimitNotice) {
|
|
544
|
+
this.namingNotice.clear();
|
|
545
|
+
return;
|
|
546
|
+
}
|
|
444
547
|
if (phase === "start")
|
|
445
548
|
this.namingNotice.start();
|
|
446
549
|
else
|
|
@@ -452,6 +555,28 @@ export class BottomInput {
|
|
|
452
555
|
isBusy() {
|
|
453
556
|
return this.running || this.statusLabel !== null;
|
|
454
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
|
+
}
|
|
455
580
|
// Nome del tool in corso per l'animazione della status-line. null = attivita'
|
|
456
581
|
// generica (spinner neutro). Non forza un timer: lo gestisce gia' setStatus.
|
|
457
582
|
setStatusTool(name) {
|
|
@@ -714,6 +839,7 @@ export class BottomInput {
|
|
|
714
839
|
touchLog() {
|
|
715
840
|
this.logRevision += 1;
|
|
716
841
|
}
|
|
842
|
+
cyclePermissionMode() { this.h.onCycleMode?.(); }
|
|
717
843
|
pause() {
|
|
718
844
|
if (this.paused)
|
|
719
845
|
return;
|
|
@@ -825,9 +951,18 @@ export class BottomInput {
|
|
|
825
951
|
clearTimeout(this.scrollRenderTimer);
|
|
826
952
|
this.scrollRenderTimer = undefined;
|
|
827
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);
|
|
828
963
|
const H = this.rows();
|
|
829
964
|
const W = this.cols();
|
|
830
|
-
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;
|
|
831
966
|
if (alternate !== this.alternateScreen) {
|
|
832
967
|
stdout.write(alternate ? ALT_ON + "\x1b[2J\x1b[H" + NATIVE_SELECTION_MODE : ALT_OFF + NATIVE_SCROLL_MODE);
|
|
833
968
|
this.alternateScreen = alternate;
|
|
@@ -872,10 +1007,17 @@ export class BottomInput {
|
|
|
872
1007
|
return;
|
|
873
1008
|
}
|
|
874
1009
|
const liveH = Math.min(lay.lines.length, H - 1);
|
|
875
|
-
const requestedHeader = this.h.headerLines?.() ?? [];
|
|
1010
|
+
const requestedHeader = this.workerVisible ? [] : this.h.headerLines?.() ?? [];
|
|
876
1011
|
const headerH = Math.min(requestedHeader.length, Math.max(0, H - liveH - 1));
|
|
877
1012
|
const header = requestedHeader.slice(0, headerH);
|
|
878
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
|
+
}
|
|
879
1021
|
const rendered = this.renderedLog();
|
|
880
1022
|
const total = rendered.length;
|
|
881
1023
|
const maxOffset = Math.max(0, total - chatRows);
|
|
@@ -922,6 +1064,11 @@ export class BottomInput {
|
|
|
922
1064
|
this.lastPaintAt = Date.now();
|
|
923
1065
|
}
|
|
924
1066
|
scrollBy(delta) {
|
|
1067
|
+
if (this.workerVisible) {
|
|
1068
|
+
this.agentSessions.scroll(-delta);
|
|
1069
|
+
this.queueScrollRender();
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
925
1072
|
const next = Math.max(0, this.scrollOffset + delta);
|
|
926
1073
|
if (next === this.scrollOffset)
|
|
927
1074
|
return;
|
|
@@ -945,25 +1092,55 @@ export class BottomInput {
|
|
|
945
1092
|
layout() {
|
|
946
1093
|
const cols = this.cols();
|
|
947
1094
|
const lines = [];
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
if (
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
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
|
+
}
|
|
956
1130
|
}
|
|
957
1131
|
// La riga live descrive gia' il team; la board completa si consulta a parte.
|
|
958
|
-
if (this.kingdomBoard && this.statusLabel === null)
|
|
1132
|
+
if (!worker && this.kingdomBoard && this.statusLabel === null)
|
|
959
1133
|
lines.push(fitLine(this.kingdomBoard[0] ?? "", cols - 1));
|
|
960
1134
|
if (!this.modal && !this.queueEditor && !this.questions.focused) {
|
|
961
1135
|
lines.push(...(this.agentSessions?.preview(cols, Math.min(5, Math.max(2, this.rows() - 12))) ?? []));
|
|
962
1136
|
}
|
|
963
|
-
|
|
1137
|
+
const footerLines = worker ? this.h.sessionFooter?.(worker, cols) ?? [] : this.h.footerLines();
|
|
1138
|
+
for (const f of footerLines)
|
|
964
1139
|
lines.push(f);
|
|
1140
|
+
if (hasRunningStatus && footerLines.length)
|
|
1141
|
+
lines.push("");
|
|
965
1142
|
if (!this.modal && !this.queueEditor)
|
|
966
|
-
lines.push(...queuePreview(this.
|
|
1143
|
+
lines.push(...queuePreview(this.composerQueue, cols, this.h.queueStyle, worker ? !worker.running : !this.running));
|
|
967
1144
|
const questionStart = lines.length;
|
|
968
1145
|
const questions = !this.modal && !this.queueEditor
|
|
969
1146
|
? this.questions.render(cols, Math.max(5, this.rows() - lines.length - 6), this.h.queueStyle)
|
|
@@ -998,7 +1175,7 @@ export class BottomInput {
|
|
|
998
1175
|
const inputCap = Math.max(1, Math.min(3, Math.floor(this.rows() / 4)));
|
|
999
1176
|
const rowOffset = Math.max(0, Math.min(crow - Math.floor(inputCap / 2), rows.length - inputCap));
|
|
1000
1177
|
const visibleRows = rows.slice(rowOffset, rowOffset + inputCap);
|
|
1001
|
-
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?.();
|
|
1002
1179
|
const blank = " " + (this.h.composerStyle?.(" ".repeat(cell)) ?? " ".repeat(cell));
|
|
1003
1180
|
if (padding)
|
|
1004
1181
|
lines.push(blank);
|
|
@@ -1017,11 +1194,13 @@ export class BottomInput {
|
|
|
1017
1194
|
const notice = ` ${this.roomStatus.label}`;
|
|
1018
1195
|
lines.push(fitLine(this.h.statusStyle?.(notice) ?? notice, cols - 1));
|
|
1019
1196
|
}
|
|
1197
|
+
if (this.sessionLimitNotice && this.sessionLimitNotice.model !== this.h.currentModel?.())
|
|
1198
|
+
this.sessionLimitNotice = null;
|
|
1020
1199
|
if (!this.modal) {
|
|
1021
1200
|
for (const naming of this.namingNotice.lines()) {
|
|
1022
1201
|
lines.push(fitLine(" " + (this.h.statusStyle?.(naming) ?? naming), cols - 1));
|
|
1023
1202
|
}
|
|
1024
|
-
for (const row of this.h.belowInputLines?.() ?? [])
|
|
1203
|
+
for (const row of (worker ? this.h.sessionBelowInput?.(worker, cols) ?? [] : this.h.belowInputLines?.() ?? []))
|
|
1025
1204
|
lines.push(fitLine(row, cols - 1));
|
|
1026
1205
|
}
|
|
1027
1206
|
for (const row of this.h.warningLines?.() ?? [])
|
|
@@ -1046,43 +1225,37 @@ export class BottomInput {
|
|
|
1046
1225
|
rowLengths: visibleRows.map((r) => r.length),
|
|
1047
1226
|
};
|
|
1048
1227
|
}
|
|
1049
|
-
// Segmento VIVO della stanza Danilov
|
|
1050
|
-
//
|
|
1228
|
+
// Segmento VIVO della stanza Danilov. Durante un run resta testuale: un solo
|
|
1229
|
+
// shimmer viene applicato all'intera frase dalla status-line.
|
|
1051
1230
|
roomSegment() {
|
|
1052
1231
|
if (!this.roomStatus || this.roomStatus.state === "done")
|
|
1053
1232
|
return "";
|
|
1054
1233
|
const style = this.h.statusStyle ?? ((s) => s);
|
|
1055
1234
|
const { label, state } = this.roomStatus;
|
|
1056
|
-
if (state === "running")
|
|
1057
|
-
|
|
1058
|
-
return `${g} ${label}`;
|
|
1059
|
-
}
|
|
1235
|
+
if (state === "running")
|
|
1236
|
+
return label;
|
|
1060
1237
|
const glyph = state === "failed" ? "✗" : "✦";
|
|
1061
1238
|
return style(`${glyph} ${label}`);
|
|
1062
1239
|
}
|
|
1063
1240
|
statusLine() {
|
|
1064
|
-
if (this.compactionStartedAt !== null) {
|
|
1065
|
-
const seconds = Math.floor((Date.now() - this.compactionStartedAt) / 1000);
|
|
1066
|
-
return fitLine(` ${SPINNER[this.frame % SPINNER.length]} Compacting context (${seconds}s · esc to interrupt)`, this.cols() - 1);
|
|
1067
|
-
}
|
|
1068
1241
|
const elapsed = Math.round((Date.now() - this.startedAt) / 1000);
|
|
1069
1242
|
const style = this.h.statusStyle ?? ((s) => s);
|
|
1070
1243
|
const label = this.statusLabel ?? tr("Elaborazione", "Working");
|
|
1071
|
-
const glyph = style(SPINNER[this.frame % SPINNER.length]);
|
|
1072
|
-
const name = style(label);
|
|
1073
1244
|
const meta = [this.h.runningHint?.() ?? "", this.roomSegment()].filter(Boolean);
|
|
1074
|
-
const shortcut = this.cols() >= 100 && (!this.
|
|
1245
|
+
const shortcut = this.cols() >= 100 && (!this.composerQueue.length || this.modal || this.queueEditor)
|
|
1075
1246
|
? tr("Alt+S subito · Esc ferma", "Alt+S send now · Esc stop") : tr("Esc ferma", "Esc stop");
|
|
1076
1247
|
const right = style(`${fmtElapsed(elapsed).padStart(7)} · ${shortcut}`);
|
|
1077
1248
|
const budget = Math.max(0, this.cols() - terminalWidth(right) - 5);
|
|
1078
|
-
let
|
|
1249
|
+
let text = label;
|
|
1079
1250
|
for (const segment of meta) {
|
|
1080
|
-
const candidate =
|
|
1081
|
-
if (terminalWidth(candidate) > budget)
|
|
1251
|
+
const candidate = text + ` · ${plainText(segment)}`;
|
|
1252
|
+
if (terminalWidth(candidate) + 2 > budget)
|
|
1082
1253
|
break;
|
|
1083
|
-
|
|
1254
|
+
text = candidate;
|
|
1084
1255
|
}
|
|
1085
|
-
|
|
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);
|
|
1086
1259
|
return fitLine(left + " ".repeat(Math.max(2, this.cols() - 2 - terminalWidth(left) - terminalWidth(right))) + right, this.cols() - 1);
|
|
1087
1260
|
}
|
|
1088
1261
|
// ── input ─────────────────────────────────────────────────────────────────
|
|
@@ -1194,6 +1367,9 @@ export class BottomInput {
|
|
|
1194
1367
|
this.scrollOffset = 0;
|
|
1195
1368
|
this.render();
|
|
1196
1369
|
}
|
|
1370
|
+
else if (this.workerVisible) {
|
|
1371
|
+
this.stopRun();
|
|
1372
|
+
}
|
|
1197
1373
|
else if (this.running) {
|
|
1198
1374
|
this.stopRun();
|
|
1199
1375
|
}
|
|
@@ -1346,16 +1522,21 @@ export class BottomInput {
|
|
|
1346
1522
|
return;
|
|
1347
1523
|
}
|
|
1348
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
|
+
}
|
|
1349
1530
|
if (k.name === "return" || k.name === "enter") {
|
|
1350
1531
|
this.agentSessions.key(s, k);
|
|
1351
|
-
|
|
1352
|
-
|
|
1532
|
+
this.screen = this.agentSessions.activeId !== "main" ? this.agentSessions : null;
|
|
1533
|
+
this.agentSessions.focused = this.agentSessions.activeId !== "main";
|
|
1353
1534
|
this.render();
|
|
1354
1535
|
return;
|
|
1355
1536
|
}
|
|
1356
1537
|
this.agentSessions.focused = false;
|
|
1357
1538
|
}
|
|
1358
|
-
if (k.meta && k.name === "t" && !this.modal && !this.overlay && !this.queueEditor) {
|
|
1539
|
+
if (!this.workerVisible && k.meta && k.name === "t" && !this.modal && !this.overlay && !this.queueEditor) {
|
|
1359
1540
|
if (this.queue.sendToTeam())
|
|
1360
1541
|
this.startNextQueued();
|
|
1361
1542
|
else
|
|
@@ -1373,6 +1554,10 @@ export class BottomInput {
|
|
|
1373
1554
|
}
|
|
1374
1555
|
if (k.ctrl && k.name === "c")
|
|
1375
1556
|
return this.onCtrlC();
|
|
1557
|
+
if (k.ctrl && k.name === "g" && this.agentSessions?.available) {
|
|
1558
|
+
this.showAgents();
|
|
1559
|
+
return;
|
|
1560
|
+
}
|
|
1376
1561
|
if (k.ctrl && k.name === "g") {
|
|
1377
1562
|
// Con un regno attivo: Ctrl+G entra nel viewer e cicla i vassalli (poi esce).
|
|
1378
1563
|
if (this.vassals && this.vassals.length) {
|
|
@@ -1392,7 +1577,15 @@ export class BottomInput {
|
|
|
1392
1577
|
return;
|
|
1393
1578
|
}
|
|
1394
1579
|
if (k.ctrl && k.name === "b") {
|
|
1395
|
-
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?.();
|
|
1396
1589
|
return;
|
|
1397
1590
|
}
|
|
1398
1591
|
// Ctrl+V (e Alt+V come alias): incolla un'immagine dalla clipboard di sistema
|
|
@@ -1409,6 +1602,11 @@ export class BottomInput {
|
|
|
1409
1602
|
return;
|
|
1410
1603
|
}
|
|
1411
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
|
+
}
|
|
1412
1610
|
if (k.name === "pageup")
|
|
1413
1611
|
return this.scrollBy(this.pageStep());
|
|
1414
1612
|
if (k.name === "pagedown")
|
|
@@ -1611,6 +1809,20 @@ export class BottomInput {
|
|
|
1611
1809
|
this.edit(() => { this.cursor = Math.max(0, this.cursor - tw); });
|
|
1612
1810
|
return;
|
|
1613
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
|
+
}
|
|
1614
1826
|
this.historyPrev();
|
|
1615
1827
|
}
|
|
1616
1828
|
onDown() {
|
|
@@ -1667,7 +1879,8 @@ export class BottomInput {
|
|
|
1667
1879
|
this.resolveModal(null);
|
|
1668
1880
|
return;
|
|
1669
1881
|
}
|
|
1670
|
-
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";
|
|
1671
1884
|
if (this.ctrlCConfirmation?.action === action && Date.now() < this.ctrlCConfirmation.until) {
|
|
1672
1885
|
this.cancelCtrlC();
|
|
1673
1886
|
if (action === "stop") {
|
|
@@ -1683,7 +1896,13 @@ export class BottomInput {
|
|
|
1683
1896
|
this.render();
|
|
1684
1897
|
return;
|
|
1685
1898
|
}
|
|
1686
|
-
this.
|
|
1899
|
+
if (this.workerVisible) {
|
|
1900
|
+
this.agentSessions.returnToMain();
|
|
1901
|
+
this.screen = null;
|
|
1902
|
+
this.render();
|
|
1903
|
+
}
|
|
1904
|
+
else
|
|
1905
|
+
this.quit();
|
|
1687
1906
|
return;
|
|
1688
1907
|
}
|
|
1689
1908
|
this.cancelCtrlC();
|
|
@@ -1700,11 +1919,18 @@ export class BottomInput {
|
|
|
1700
1919
|
}
|
|
1701
1920
|
stopRun() {
|
|
1702
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
|
+
}
|
|
1703
1929
|
const queued = this.queue.length;
|
|
1704
1930
|
this.queue.paused = true;
|
|
1705
1931
|
this.runAbort?.abort();
|
|
1706
1932
|
if (queued)
|
|
1707
|
-
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");
|
|
1708
1934
|
}
|
|
1709
1935
|
// Domanda a risposta libera: l'editing finisce nel buffer, Invio risolve.
|
|
1710
1936
|
queueQuestion(title, options, signal) {
|
|
@@ -1764,6 +1990,17 @@ export class BottomInput {
|
|
|
1764
1990
|
this.cursor = 0;
|
|
1765
1991
|
this.histIdx = -1;
|
|
1766
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
|
+
}
|
|
1767
2004
|
if (immediate) {
|
|
1768
2005
|
const next = this.queue.items().find(entry => !entry.text.trimStart().startsWith("/"));
|
|
1769
2006
|
if (next)
|
|
@@ -1788,16 +2025,52 @@ export class BottomInput {
|
|
|
1788
2025
|
this.showReview(review[1]);
|
|
1789
2026
|
return;
|
|
1790
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
|
+
}
|
|
1791
2060
|
this.dispatch(line, immediate);
|
|
1792
2061
|
}
|
|
1793
2062
|
dispatch(line, immediate = false) {
|
|
2063
|
+
if (line.trimStart().startsWith("/") && (this.running || this.commandPending)) {
|
|
2064
|
+
this.runLiveCommand(line.trim());
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
1794
2067
|
// Una coda ripristinata in pausa non deve bloccare /new, /resume, /help
|
|
1795
2068
|
// o /quit. A riposo i comandi controllano la UI senza riprendere la coda.
|
|
1796
2069
|
if (!this.running && line.trimStart().startsWith("/")) {
|
|
1797
2070
|
this.runLine(line);
|
|
1798
2071
|
return;
|
|
1799
2072
|
}
|
|
1800
|
-
if (this.running || this.queue.length) {
|
|
2073
|
+
if (this.running || this.commandPending || this.queue.length) {
|
|
1801
2074
|
try {
|
|
1802
2075
|
this.queue.add(line, immediate ? "immediate" : "after-turn");
|
|
1803
2076
|
// Aprire una chat non avvia lavoro da solo. Un nuovo Invio a riposo
|
|
@@ -1820,7 +2093,7 @@ export class BottomInput {
|
|
|
1820
2093
|
this.runLine(line);
|
|
1821
2094
|
}
|
|
1822
2095
|
startNextQueued() {
|
|
1823
|
-
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)
|
|
1824
2097
|
return;
|
|
1825
2098
|
const next = this.queue.takeNext();
|
|
1826
2099
|
if (next) {
|
|
@@ -1828,9 +2101,34 @@ export class BottomInput {
|
|
|
1828
2101
|
this.runLine(next.text, true, next);
|
|
1829
2102
|
}
|
|
1830
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
|
+
}
|
|
1831
2129
|
runLine(line, fromQueue = false, queued) {
|
|
1832
2130
|
this.setRoomStatus(null);
|
|
1833
|
-
this.history.push(line);
|
|
2131
|
+
(this.composerSession === "main" ? this.history : this.sessionDrafts.get("main")?.history)?.push(line);
|
|
1834
2132
|
this.running = true;
|
|
1835
2133
|
this.startedAt = Date.now();
|
|
1836
2134
|
this.tokensNew = 0;
|
|
@@ -1861,6 +2159,7 @@ export class BottomInput {
|
|
|
1861
2159
|
this.active = false;
|
|
1862
2160
|
this.namingNotice.clear();
|
|
1863
2161
|
this.sessionsUnsubscribe?.();
|
|
2162
|
+
this.messagesUnsubscribe?.();
|
|
1864
2163
|
if (this.sessionsTimer)
|
|
1865
2164
|
clearInterval(this.sessionsTimer);
|
|
1866
2165
|
this.questions.clear();
|