@voybio/ace-swarm 0.2.0 → 0.2.1
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/README.md +15 -15
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/STATUS.md +2 -2
- package/dist/ace-autonomy.js +38 -1
- package/dist/ace-context.js +8 -0
- package/dist/ace-server-instructions.js +55 -19
- package/dist/ace-state-resolver.d.ts +18 -0
- package/dist/ace-state-resolver.js +106 -0
- package/dist/cli.js +67 -0
- package/dist/handoff-registry.js +11 -7
- package/dist/helpers.js +74 -8
- package/dist/job-scheduler.js +94 -44
- package/dist/run-ledger.js +3 -4
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/shared.d.ts +1 -1
- package/dist/status-events.js +12 -14
- package/dist/store/bootstrap-store.js +20 -9
- package/dist/store/materializers/context-snapshot-materializer.d.ts +10 -0
- package/dist/store/materializers/context-snapshot-materializer.js +51 -0
- package/dist/store/materializers/host-file-materializer.d.ts +6 -0
- package/dist/store/materializers/host-file-materializer.js +13 -0
- package/dist/store/materializers/projection-manager.d.ts +14 -0
- package/dist/store/materializers/projection-manager.js +73 -0
- package/dist/store/materializers/scheduler-projection-materializer.d.ts +16 -0
- package/dist/store/materializers/scheduler-projection-materializer.js +48 -0
- package/dist/store/repositories/context-snapshot-repository.d.ts +46 -0
- package/dist/store/repositories/context-snapshot-repository.js +105 -0
- package/dist/store/repositories/local-model-runtime-repository.d.ts +98 -0
- package/dist/store/repositories/local-model-runtime-repository.js +165 -0
- package/dist/store/repositories/scheduler-repository.d.ts +21 -39
- package/dist/store/repositories/scheduler-repository.js +123 -93
- package/dist/store/repositories/todo-repository.d.ts +4 -0
- package/dist/store/repositories/todo-repository.js +50 -0
- package/dist/store/state-reader.d.ts +8 -1
- package/dist/store/state-reader.js +12 -1
- package/dist/store/store-artifacts.js +31 -5
- package/dist/store/store-authority-audit.d.ts +30 -0
- package/dist/store/store-authority-audit.js +448 -0
- package/dist/store/types.d.ts +2 -0
- package/dist/store/types.js +1 -0
- package/dist/todo-state.js +179 -11
- package/dist/tools-files.js +2 -1
- package/dist/tools-framework.js +60 -0
- package/dist/tools-memory.js +69 -34
- package/dist/tools-todo.js +1 -1
- package/dist/tui/agent-worker.d.ts +1 -1
- package/dist/tui/agent-worker.js +5 -3
- package/dist/tui/chat.d.ts +19 -0
- package/dist/tui/chat.js +275 -9
- package/dist/tui/commands.d.ts +2 -0
- package/dist/tui/commands.js +62 -0
- package/dist/tui/dashboard.d.ts +5 -0
- package/dist/tui/dashboard.js +38 -2
- package/dist/tui/index.d.ts +5 -0
- package/dist/tui/index.js +146 -2
- package/dist/tui/input.js +5 -0
- package/dist/tui/layout.d.ts +24 -0
- package/dist/tui/layout.js +76 -2
- package/dist/tui/local-model-contract.d.ts +50 -0
- package/dist/tui/local-model-contract.js +272 -0
- package/dist/vericify-bridge.js +3 -4
- package/dist/vericify-context.js +18 -6
- package/package.json +1 -1
package/dist/tui/chat.js
CHANGED
|
@@ -7,9 +7,12 @@
|
|
|
7
7
|
import { diagnoseChatRuntimeConfig, } from "./openai-compatible.js";
|
|
8
8
|
import { estimateTokenCount } from "./telemetry.js";
|
|
9
9
|
import { EventEmitter } from "node:events";
|
|
10
|
-
import {
|
|
10
|
+
import { randomUUID } from "node:crypto";
|
|
11
11
|
import { resolve } from "node:path";
|
|
12
12
|
import { ModelBridge } from "../model-bridge.js";
|
|
13
|
+
import { resolveAceStateLayout } from "../ace-state-resolver.js";
|
|
14
|
+
import { applyEvidenceGuardrail, buildAcePreflightPacket, buildBridgeTaskInput, buildContinuityRecord, buildStartupNudge, mapBridgeResultToRuntimeStatus, nextActivationLedger, } from "./local-model-contract.js";
|
|
15
|
+
import { withLocalModelRuntimeRepository, } from "../store/repositories/local-model-runtime-repository.js";
|
|
13
16
|
export class ChatSession extends EventEmitter {
|
|
14
17
|
clients;
|
|
15
18
|
telemetry;
|
|
@@ -32,6 +35,11 @@ export class ChatSession extends EventEmitter {
|
|
|
32
35
|
aceBridge;
|
|
33
36
|
activeAceBridge = false;
|
|
34
37
|
providerBaseUrls;
|
|
38
|
+
sessionId = randomUUID();
|
|
39
|
+
createdAt = Date.now();
|
|
40
|
+
lastActiveAt = this.createdAt;
|
|
41
|
+
currentRuntimeStatus = null;
|
|
42
|
+
activationLedger = null;
|
|
35
43
|
constructor(clients, telemetry, options) {
|
|
36
44
|
super();
|
|
37
45
|
this.clients = clients;
|
|
@@ -63,6 +71,35 @@ export class ChatSession extends EventEmitter {
|
|
|
63
71
|
getDisplayMessages() {
|
|
64
72
|
return this.displayMessages;
|
|
65
73
|
}
|
|
74
|
+
getSessionId() {
|
|
75
|
+
return this.sessionId;
|
|
76
|
+
}
|
|
77
|
+
getCreatedAt() {
|
|
78
|
+
return this.createdAt;
|
|
79
|
+
}
|
|
80
|
+
getLastActiveAt() {
|
|
81
|
+
return this.lastActiveAt;
|
|
82
|
+
}
|
|
83
|
+
getCurrentRuntimeStatus() {
|
|
84
|
+
return this.currentRuntimeStatus ?? undefined;
|
|
85
|
+
}
|
|
86
|
+
hasMeaningfulTranscript() {
|
|
87
|
+
return this.messages.some((message) => message.role !== "system" && String(message.content ?? "").trim().length > 0);
|
|
88
|
+
}
|
|
89
|
+
getTranscriptExcerpt(maxMessages = 6) {
|
|
90
|
+
const excerpt = this.messages
|
|
91
|
+
.filter((message) => message.role !== "system")
|
|
92
|
+
.slice(-maxMessages)
|
|
93
|
+
.map((message) => `${message.role}: ${oneLine(String(message.content ?? ""))}`)
|
|
94
|
+
.join(" | ");
|
|
95
|
+
return excerpt ? clip(excerpt, 720) : undefined;
|
|
96
|
+
}
|
|
97
|
+
getTranscriptSummary() {
|
|
98
|
+
const lastAssistant = [...this.messages]
|
|
99
|
+
.reverse()
|
|
100
|
+
.find((message) => message.role === "assistant" && String(message.content ?? "").trim().length > 0);
|
|
101
|
+
return lastAssistant ? clip(oneLine(String(lastAssistant.content ?? "")), 240) : undefined;
|
|
102
|
+
}
|
|
66
103
|
/** Is currently streaming a response? */
|
|
67
104
|
isStreaming() {
|
|
68
105
|
return this.streaming;
|
|
@@ -115,6 +152,7 @@ export class ChatSession extends EventEmitter {
|
|
|
115
152
|
timestamp: Date.now(),
|
|
116
153
|
tokens: estimateTokenCount(text),
|
|
117
154
|
});
|
|
155
|
+
this.noteActivity();
|
|
118
156
|
this.emit("updated");
|
|
119
157
|
// Start streaming response
|
|
120
158
|
this.streaming = true;
|
|
@@ -161,6 +199,7 @@ export class ChatSession extends EventEmitter {
|
|
|
161
199
|
timestamp: Date.now(),
|
|
162
200
|
tokens: estimateTokenCount(this.currentStreamText),
|
|
163
201
|
});
|
|
202
|
+
this.noteActivity();
|
|
164
203
|
}
|
|
165
204
|
catch (err) {
|
|
166
205
|
if (err?.name === "AbortError") {
|
|
@@ -210,6 +249,7 @@ export class ChatSession extends EventEmitter {
|
|
|
210
249
|
clear() {
|
|
211
250
|
this.messages = [];
|
|
212
251
|
this.displayMessages = [];
|
|
252
|
+
this.currentRuntimeStatus = null;
|
|
213
253
|
if (this.systemPrompt) {
|
|
214
254
|
this.messages.push({ role: "system", content: this.systemPrompt });
|
|
215
255
|
this.displayMessages.push({
|
|
@@ -274,10 +314,7 @@ export class ChatSession extends EventEmitter {
|
|
|
274
314
|
return false;
|
|
275
315
|
if (!this.workspaceRoot)
|
|
276
316
|
return false;
|
|
277
|
-
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
280
|
-
return true;
|
|
317
|
+
return resolveAceStateLayout(this.workspaceRoot).isAcePresent;
|
|
281
318
|
}
|
|
282
319
|
buildBridgeTask() {
|
|
283
320
|
const recentConversation = this.messages
|
|
@@ -293,39 +330,206 @@ export class ChatSession extends EventEmitter {
|
|
|
293
330
|
content,
|
|
294
331
|
timestamp: Date.now(),
|
|
295
332
|
});
|
|
333
|
+
this.noteActivity();
|
|
296
334
|
this.emit("updated");
|
|
297
335
|
}
|
|
336
|
+
noteActivity() {
|
|
337
|
+
this.lastActiveAt = Date.now();
|
|
338
|
+
}
|
|
339
|
+
buildRuntimeStatusPacket(input) {
|
|
340
|
+
return {
|
|
341
|
+
session_id: this.sessionId,
|
|
342
|
+
process_id: process.pid,
|
|
343
|
+
turn_count: this.messages.filter((message) => message.role === "user").length,
|
|
344
|
+
role: input.role,
|
|
345
|
+
bridge_status: input.bridgeStatus,
|
|
346
|
+
preflight_state: input.preflightState,
|
|
347
|
+
approval_state: this.currentRuntimeStatus?.approval_state ?? "not_required",
|
|
348
|
+
current_task: input.task,
|
|
349
|
+
recommended_next_action: input.recommendedNextAction,
|
|
350
|
+
blocked_reason: input.blockedReason,
|
|
351
|
+
updated_at: Date.now(),
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
setRuntimeStatus(status) {
|
|
355
|
+
this.currentRuntimeStatus = status;
|
|
356
|
+
this.emit("runtime_status", status);
|
|
357
|
+
}
|
|
358
|
+
setApprovalState(state) {
|
|
359
|
+
if (!this.currentRuntimeStatus) {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
const next = {
|
|
363
|
+
...this.currentRuntimeStatus,
|
|
364
|
+
approval_state: state,
|
|
365
|
+
updated_at: Date.now(),
|
|
366
|
+
};
|
|
367
|
+
this.setRuntimeStatus(next);
|
|
368
|
+
this.persistRuntimeStatus(next);
|
|
369
|
+
this.displayMessages.push({
|
|
370
|
+
role: "system",
|
|
371
|
+
content: `Approval state set to: ${state}`,
|
|
372
|
+
timestamp: Date.now(),
|
|
373
|
+
});
|
|
374
|
+
this.emit("updated");
|
|
375
|
+
this.noteActivity();
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
persistRuntimeStatus(status) {
|
|
379
|
+
if (!this.workspaceRoot)
|
|
380
|
+
return;
|
|
381
|
+
const { updated_at: _updatedAt, ...record } = status;
|
|
382
|
+
void withLocalModelRuntimeRepository(this.workspaceRoot, async (repo) => {
|
|
383
|
+
await repo.upsertRuntimeStatus(record);
|
|
384
|
+
}).catch((error) => {
|
|
385
|
+
this.emit("error", `Runtime status persistence failed: ${formatErrorMessage(error)}`);
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
persistRuntimeSessionArtifacts(input) {
|
|
389
|
+
if (!this.workspaceRoot)
|
|
390
|
+
return;
|
|
391
|
+
void withLocalModelRuntimeRepository(this.workspaceRoot, async (repo) => {
|
|
392
|
+
if (input.activationLedger) {
|
|
393
|
+
await repo.upsertActivationLedger(input.activationLedger);
|
|
394
|
+
}
|
|
395
|
+
if (input.runtimeStatus) {
|
|
396
|
+
const { updated_at: _updatedAt, ...statusRecord } = input.runtimeStatus;
|
|
397
|
+
await repo.upsertRuntimeStatus(statusRecord);
|
|
398
|
+
}
|
|
399
|
+
if (input.continuity) {
|
|
400
|
+
await repo.upsertContinuityRecord(input.continuity);
|
|
401
|
+
}
|
|
402
|
+
}).catch((error) => {
|
|
403
|
+
this.emit("error", `ACE runtime persistence failed: ${formatErrorMessage(error)}`);
|
|
404
|
+
});
|
|
405
|
+
}
|
|
298
406
|
async runAceBridge(text, startTime, streamProvider, streamModel) {
|
|
299
407
|
if (!this.workspaceRoot) {
|
|
300
408
|
throw new Error("ACE bridge requires a workspace root.");
|
|
301
409
|
}
|
|
302
410
|
this.activeAceBridge = true;
|
|
411
|
+
const recentConversation = this.buildBridgeTask();
|
|
412
|
+
const preflight = buildAcePreflightPacket({
|
|
413
|
+
sessionId: this.sessionId,
|
|
414
|
+
workspaceRoot: this.workspaceRoot,
|
|
415
|
+
task: recentConversation,
|
|
416
|
+
preferredRole: this.aceRole,
|
|
417
|
+
});
|
|
418
|
+
const executionRole = preflight.recommended_role ?? this.aceRole;
|
|
419
|
+
let activationLedger = nextActivationLedger(this.sessionId, this.activationLedger ?? undefined, preflight.recommended_next_action);
|
|
420
|
+
const startupNudge = buildStartupNudge(preflight, activationLedger);
|
|
421
|
+
if (startupNudge) {
|
|
422
|
+
activationLedger = {
|
|
423
|
+
...activationLedger,
|
|
424
|
+
shown_nudges: uniqueStrings([...activationLedger.shown_nudges, startupNudge.id]),
|
|
425
|
+
};
|
|
426
|
+
this.pushSystemMessage(`Hint: ${startupNudge.text}`);
|
|
427
|
+
this.emit("startup_nudge", startupNudge);
|
|
428
|
+
}
|
|
429
|
+
let runtimeStatus = this.buildRuntimeStatusPacket({
|
|
430
|
+
role: executionRole,
|
|
431
|
+
task: text,
|
|
432
|
+
preflightState: preflight.preflight_state,
|
|
433
|
+
bridgeStatus: preflight.bridge_status,
|
|
434
|
+
recommendedNextAction: preflight.recommended_next_action,
|
|
435
|
+
blockedReason: preflight.blockers[0],
|
|
436
|
+
});
|
|
437
|
+
this.setRuntimeStatus(runtimeStatus);
|
|
438
|
+
this.persistRuntimeSessionArtifacts({
|
|
439
|
+
activationLedger,
|
|
440
|
+
runtimeStatus,
|
|
441
|
+
});
|
|
442
|
+
this.activationLedger = activationLedger;
|
|
443
|
+
if (preflight.preflight_state === "blocked") {
|
|
444
|
+
const blockerText = preflight.blockers.join("; ");
|
|
445
|
+
this.pushSystemMessage(`ACE preflight blocked: ${blockerText}`);
|
|
446
|
+
const blockedContinuity = buildContinuityRecord({
|
|
447
|
+
preflight,
|
|
448
|
+
role: executionRole,
|
|
449
|
+
bridgeStatus: runtimeStatus.bridge_status,
|
|
450
|
+
});
|
|
451
|
+
this.persistRuntimeSessionArtifacts({
|
|
452
|
+
activationLedger,
|
|
453
|
+
runtimeStatus,
|
|
454
|
+
continuity: blockedContinuity,
|
|
455
|
+
});
|
|
456
|
+
this.activationLedger = activationLedger;
|
|
457
|
+
return;
|
|
458
|
+
}
|
|
303
459
|
let bridgeOutput = "";
|
|
460
|
+
const toolNames = [];
|
|
461
|
+
let retryAttempt = 0;
|
|
304
462
|
const result = await this.aceBridge.run({
|
|
305
|
-
task:
|
|
306
|
-
role:
|
|
463
|
+
task: buildBridgeTaskInput(recentConversation, preflight),
|
|
464
|
+
role: executionRole,
|
|
307
465
|
workspace: this.workspaceRoot,
|
|
308
|
-
tier: this.aceTier ?? (
|
|
466
|
+
tier: this.aceTier ?? (executionRole === "orchestrator" ? "compressed" : "brief"),
|
|
309
467
|
maxTurns: this.maxTurns,
|
|
310
468
|
provider: streamProvider,
|
|
311
469
|
model: streamModel,
|
|
312
470
|
numCtx: this.numCtx,
|
|
313
471
|
onThinking: (thinking) => {
|
|
314
472
|
this.pushSystemMessage(`Thinking: ${thinking}`);
|
|
473
|
+
if (/\bretrying\b/i.test(thinking)) {
|
|
474
|
+
retryAttempt += 1;
|
|
475
|
+
runtimeStatus = {
|
|
476
|
+
...runtimeStatus,
|
|
477
|
+
bridge_status: "retrying",
|
|
478
|
+
retry_state: {
|
|
479
|
+
attempt: retryAttempt,
|
|
480
|
+
max_attempts: 2,
|
|
481
|
+
reason: thinking,
|
|
482
|
+
},
|
|
483
|
+
updated_at: Date.now(),
|
|
484
|
+
};
|
|
485
|
+
this.setRuntimeStatus(runtimeStatus);
|
|
486
|
+
this.persistRuntimeStatus(runtimeStatus);
|
|
487
|
+
}
|
|
315
488
|
},
|
|
316
489
|
onToolCall: (tool, args) => {
|
|
490
|
+
toolNames.push(tool);
|
|
491
|
+
activationLedger = {
|
|
492
|
+
...activationLedger,
|
|
493
|
+
updated_at: Date.now(),
|
|
494
|
+
activated_tools: uniqueStrings([...activationLedger.activated_tools, tool]),
|
|
495
|
+
activated_roles: uniqueStrings([...activationLedger.activated_roles, executionRole]),
|
|
496
|
+
accepted_nudges: tool === startupNudge?.recommended_action
|
|
497
|
+
? uniqueStrings([...activationLedger.accepted_nudges, startupNudge.id])
|
|
498
|
+
: activationLedger.accepted_nudges,
|
|
499
|
+
};
|
|
500
|
+
runtimeStatus = {
|
|
501
|
+
...runtimeStatus,
|
|
502
|
+
bridge_status: "running",
|
|
503
|
+
active_tool: tool,
|
|
504
|
+
active_tool_role: executionRole,
|
|
505
|
+
updated_at: Date.now(),
|
|
506
|
+
};
|
|
507
|
+
this.setRuntimeStatus(runtimeStatus);
|
|
508
|
+
this.persistRuntimeSessionArtifacts({ activationLedger, runtimeStatus });
|
|
509
|
+
this.activationLedger = activationLedger;
|
|
317
510
|
this.pushSystemMessage(`Tool call: ${tool} ${safeStringify(args)}`);
|
|
318
511
|
this.emit("tool_call", tool, args);
|
|
319
512
|
},
|
|
320
513
|
onToolResult: (tool, toolResult) => {
|
|
514
|
+
runtimeStatus = {
|
|
515
|
+
...runtimeStatus,
|
|
516
|
+
bridge_status: toolResult.isError ? "needs_input" : "running",
|
|
517
|
+
active_tool: undefined,
|
|
518
|
+
blocked_reason: toolResult.isError ? `${tool}: ${toolResult.summary}` : undefined,
|
|
519
|
+
updated_at: Date.now(),
|
|
520
|
+
};
|
|
521
|
+
this.setRuntimeStatus(runtimeStatus);
|
|
522
|
+
this.persistRuntimeStatus(runtimeStatus);
|
|
321
523
|
this.pushSystemMessage(`Tool ${toolResult.ok ? "ok" : "error"}: ${tool} — ${toolResult.summary}`);
|
|
322
524
|
this.emit("tool_result", tool, toolResult);
|
|
323
525
|
},
|
|
324
526
|
onOutput: (output) => {
|
|
325
527
|
bridgeOutput = output;
|
|
528
|
+
this.noteActivity();
|
|
326
529
|
},
|
|
327
530
|
});
|
|
328
|
-
const
|
|
531
|
+
const rawAssistantText = bridgeOutput.trim() || result.summary.trim() || "ACE bridge completed.";
|
|
532
|
+
const assistantText = applyEvidenceGuardrail(rawAssistantText, toolNames);
|
|
329
533
|
this.messages.push({ role: "assistant", content: assistantText });
|
|
330
534
|
this.displayMessages.push({
|
|
331
535
|
role: "assistant",
|
|
@@ -333,9 +537,57 @@ export class ChatSession extends EventEmitter {
|
|
|
333
537
|
timestamp: Date.now(),
|
|
334
538
|
tokens: estimateTokenCount(assistantText),
|
|
335
539
|
});
|
|
540
|
+
this.noteActivity();
|
|
336
541
|
if (result.status === "needs_input") {
|
|
337
542
|
this.pushSystemMessage("[Awaiting operator follow-up]");
|
|
338
543
|
}
|
|
544
|
+
if (result.status === "failed") {
|
|
545
|
+
runtimeStatus = {
|
|
546
|
+
...runtimeStatus,
|
|
547
|
+
bridge_status: "failed",
|
|
548
|
+
blocked_reason: assistantText,
|
|
549
|
+
active_tool: undefined,
|
|
550
|
+
updated_at: Date.now(),
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
else if (result.status === "max_turns") {
|
|
554
|
+
runtimeStatus = {
|
|
555
|
+
...runtimeStatus,
|
|
556
|
+
bridge_status: "needs_input",
|
|
557
|
+
blocked_reason: "ACE bridge reached max turns before completion.",
|
|
558
|
+
active_tool: undefined,
|
|
559
|
+
updated_at: Date.now(),
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
else if (result.status === "needs_input") {
|
|
563
|
+
runtimeStatus = {
|
|
564
|
+
...runtimeStatus,
|
|
565
|
+
bridge_status: "needs_input",
|
|
566
|
+
active_tool: undefined,
|
|
567
|
+
updated_at: Date.now(),
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
runtimeStatus = mapBridgeResultToRuntimeStatus({
|
|
572
|
+
current: runtimeStatus,
|
|
573
|
+
summary: assistantText,
|
|
574
|
+
toolNames,
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
this.setRuntimeStatus(runtimeStatus);
|
|
578
|
+
const continuity = buildContinuityRecord({
|
|
579
|
+
preflight,
|
|
580
|
+
role: executionRole,
|
|
581
|
+
activeTool: runtimeStatus.active_tool,
|
|
582
|
+
bridgeStatus: runtimeStatus.bridge_status,
|
|
583
|
+
evidenceRefs: toolNames.map((tool) => `tool:${tool}`),
|
|
584
|
+
});
|
|
585
|
+
this.persistRuntimeSessionArtifacts({
|
|
586
|
+
activationLedger,
|
|
587
|
+
runtimeStatus,
|
|
588
|
+
continuity,
|
|
589
|
+
});
|
|
590
|
+
this.activationLedger = activationLedger;
|
|
339
591
|
this.telemetry.recordRequest({
|
|
340
592
|
startTime,
|
|
341
593
|
endTime: Date.now(),
|
|
@@ -365,4 +617,18 @@ function safeStringify(value) {
|
|
|
365
617
|
return "{}";
|
|
366
618
|
}
|
|
367
619
|
}
|
|
620
|
+
function oneLine(input) {
|
|
621
|
+
return input.replace(/\s+/g, " ").trim();
|
|
622
|
+
}
|
|
623
|
+
function clip(input, max) {
|
|
624
|
+
if (input.length <= max)
|
|
625
|
+
return input;
|
|
626
|
+
return `${input.slice(0, Math.max(0, max - 1)).trimEnd()}…`;
|
|
627
|
+
}
|
|
628
|
+
function uniqueStrings(values) {
|
|
629
|
+
return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
|
|
630
|
+
}
|
|
631
|
+
function formatErrorMessage(error) {
|
|
632
|
+
return error instanceof Error ? error.message : String(error);
|
|
633
|
+
}
|
|
368
634
|
//# sourceMappingURL=chat.js.map
|
package/dist/tui/commands.d.ts
CHANGED
|
@@ -34,9 +34,11 @@ export interface TuiController {
|
|
|
34
34
|
createChatTab(): void;
|
|
35
35
|
createLogTab(): void;
|
|
36
36
|
closeCurrentTab(): void;
|
|
37
|
+
archiveCurrentTab(): Promise<void>;
|
|
37
38
|
showMessage(text: string, level?: "info" | "warn" | "error"): void;
|
|
38
39
|
clearView(): void;
|
|
39
40
|
refresh(): void;
|
|
41
|
+
setApprovalState(state: "not_required" | "pending" | "approved" | "rejected"): boolean;
|
|
40
42
|
getStatus(): Record<string, unknown>;
|
|
41
43
|
quit(): void;
|
|
42
44
|
sendChatMessage(text: string): Promise<void>;
|
package/dist/tui/commands.js
CHANGED
|
@@ -304,6 +304,48 @@ export function registerBuiltinCommands(registry) {
|
|
|
304
304
|
ctx.tui.showMessage(lines);
|
|
305
305
|
},
|
|
306
306
|
});
|
|
307
|
+
// /approve — Mark the active runtime approval as approved
|
|
308
|
+
registry.register({
|
|
309
|
+
name: "approve",
|
|
310
|
+
aliases: [],
|
|
311
|
+
description: "Approve the active runtime packet",
|
|
312
|
+
usage: "/approve",
|
|
313
|
+
handler: (_args, ctx) => {
|
|
314
|
+
if (!ctx.tui.setApprovalState("approved")) {
|
|
315
|
+
ctx.tui.showMessage("No active runtime packet to approve.", "warn");
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
ctx.tui.showMessage("Approval state set to: approved");
|
|
319
|
+
},
|
|
320
|
+
});
|
|
321
|
+
// /reject — Mark the active runtime approval as rejected
|
|
322
|
+
registry.register({
|
|
323
|
+
name: "reject",
|
|
324
|
+
aliases: [],
|
|
325
|
+
description: "Reject the active runtime packet",
|
|
326
|
+
usage: "/reject",
|
|
327
|
+
handler: (_args, ctx) => {
|
|
328
|
+
if (!ctx.tui.setApprovalState("rejected")) {
|
|
329
|
+
ctx.tui.showMessage("No active runtime packet to reject.", "warn");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
ctx.tui.showMessage("Approval state set to: rejected");
|
|
333
|
+
},
|
|
334
|
+
});
|
|
335
|
+
// /trust — Keep the active runtime approved until revoked
|
|
336
|
+
registry.register({
|
|
337
|
+
name: "trust",
|
|
338
|
+
aliases: ["remember"],
|
|
339
|
+
description: "Trust the active runtime session until revoked",
|
|
340
|
+
usage: "/trust",
|
|
341
|
+
handler: (_args, ctx) => {
|
|
342
|
+
if (!ctx.tui.setApprovalState("approved")) {
|
|
343
|
+
ctx.tui.showMessage("No active runtime packet to trust.", "warn");
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
ctx.tui.showMessage("Session trusted until /reject.");
|
|
347
|
+
},
|
|
348
|
+
});
|
|
307
349
|
// /skill <name> — Load a skill
|
|
308
350
|
registry.register({
|
|
309
351
|
name: "skill",
|
|
@@ -386,6 +428,26 @@ export function registerBuiltinCommands(registry) {
|
|
|
386
428
|
ctx.tui.createLogTab();
|
|
387
429
|
},
|
|
388
430
|
});
|
|
431
|
+
// /close — Close the current closeable tab
|
|
432
|
+
registry.register({
|
|
433
|
+
name: "close",
|
|
434
|
+
aliases: ["x"],
|
|
435
|
+
description: "Close the current tab",
|
|
436
|
+
usage: "/close",
|
|
437
|
+
handler: (_args, ctx) => {
|
|
438
|
+
ctx.tui.closeCurrentTab();
|
|
439
|
+
},
|
|
440
|
+
});
|
|
441
|
+
// /archive — Archive the current chat tab, then close it
|
|
442
|
+
registry.register({
|
|
443
|
+
name: "archive",
|
|
444
|
+
aliases: ["arc"],
|
|
445
|
+
description: "Archive the current chat tab and close it",
|
|
446
|
+
usage: "/archive",
|
|
447
|
+
handler: async (_args, ctx) => {
|
|
448
|
+
await ctx.tui.archiveCurrentTab();
|
|
449
|
+
},
|
|
450
|
+
});
|
|
389
451
|
// /clear — Clear current view
|
|
390
452
|
registry.register({
|
|
391
453
|
name: "clear",
|
package/dist/tui/dashboard.d.ts
CHANGED
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import { EventEmitter } from "node:events";
|
|
8
8
|
import type { DashboardData } from "./layout.js";
|
|
9
|
+
import type { AceRuntimeStatusPacket } from "../store/repositories/local-model-runtime-repository.js";
|
|
10
|
+
export declare function isProcessAlive(pid: number): boolean;
|
|
11
|
+
export declare function selectLiveRuntimeStatus(statuses: Array<Pick<AceRuntimeStatusPacket, "process_id"> & AceRuntimeStatusPacket>): AceRuntimeStatusPacket | undefined;
|
|
9
12
|
export declare class DashboardState extends EventEmitter {
|
|
10
13
|
private workspaceRoot;
|
|
11
14
|
private readonly storePath;
|
|
@@ -14,10 +17,12 @@ export declare class DashboardState extends EventEmitter {
|
|
|
14
17
|
private persistedEvents;
|
|
15
18
|
private runtimeEvents;
|
|
16
19
|
private phase;
|
|
20
|
+
private latestRuntimeStatus;
|
|
17
21
|
constructor(workspaceRoot: string);
|
|
18
22
|
startWatching(): void;
|
|
19
23
|
stopWatching(): void;
|
|
20
24
|
getData(provider: string, model: string, startTime: number, tokensIn: number, tokensOut: number, tokensPerSec: number, activeAgents: number, totalAgents: number): DashboardData;
|
|
25
|
+
getLatestRuntimeStatus(): AceRuntimeStatusPacket | undefined;
|
|
21
26
|
addEvent(agent: string, message: string): void;
|
|
22
27
|
private applySnapshot;
|
|
23
28
|
}
|
package/dist/tui/dashboard.js
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* The TUI never tails compatibility files directly.
|
|
6
6
|
*/
|
|
7
7
|
import { EventEmitter } from "node:events";
|
|
8
|
-
import { resolve } from "node:path";
|
|
9
8
|
import { pollStore } from "../store/state-reader.js";
|
|
9
|
+
import { getWorkspaceStorePath } from "../store/store-snapshot.js";
|
|
10
10
|
function dedupeEvents(events) {
|
|
11
11
|
const seen = new Set();
|
|
12
12
|
const ordered = [];
|
|
@@ -19,7 +19,37 @@ function dedupeEvents(events) {
|
|
|
19
19
|
}
|
|
20
20
|
return ordered.slice(-500);
|
|
21
21
|
}
|
|
22
|
+
export function isProcessAlive(pid) {
|
|
23
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
24
|
+
return false;
|
|
25
|
+
try {
|
|
26
|
+
process.kill(pid, 0);
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function selectLiveRuntimeStatus(statuses) {
|
|
34
|
+
return statuses.find((status) => isProcessAlive(status.process_id));
|
|
35
|
+
}
|
|
22
36
|
function derivePhase(snapshot) {
|
|
37
|
+
const runtimeStatus = selectLiveRuntimeStatus(snapshot.runtimeStatuses);
|
|
38
|
+
if (runtimeStatus?.bridge_status === "blocked") {
|
|
39
|
+
return "Blocked";
|
|
40
|
+
}
|
|
41
|
+
if (runtimeStatus?.bridge_status === "approval_pending") {
|
|
42
|
+
return "Approval Pending";
|
|
43
|
+
}
|
|
44
|
+
if (runtimeStatus?.bridge_status === "needs_input") {
|
|
45
|
+
return "Needs Input";
|
|
46
|
+
}
|
|
47
|
+
if (runtimeStatus?.bridge_status === "retrying") {
|
|
48
|
+
return "Retrying";
|
|
49
|
+
}
|
|
50
|
+
if (runtimeStatus?.bridge_status === "running") {
|
|
51
|
+
return "Running";
|
|
52
|
+
}
|
|
23
53
|
if (snapshot.jobs.some((job) => String(job.status ?? "") === "running")) {
|
|
24
54
|
return "Running";
|
|
25
55
|
}
|
|
@@ -42,10 +72,11 @@ export class DashboardState extends EventEmitter {
|
|
|
42
72
|
persistedEvents = [];
|
|
43
73
|
runtimeEvents = [];
|
|
44
74
|
phase = "Initializing";
|
|
75
|
+
latestRuntimeStatus;
|
|
45
76
|
constructor(workspaceRoot) {
|
|
46
77
|
super();
|
|
47
78
|
this.workspaceRoot = workspaceRoot;
|
|
48
|
-
this.storePath =
|
|
79
|
+
this.storePath = getWorkspaceStorePath(workspaceRoot);
|
|
49
80
|
}
|
|
50
81
|
startWatching() {
|
|
51
82
|
if (this.stopPolling)
|
|
@@ -74,10 +105,14 @@ export class DashboardState extends EventEmitter {
|
|
|
74
105
|
tokensIn,
|
|
75
106
|
tokensOut,
|
|
76
107
|
tokensPerSec,
|
|
108
|
+
runtimeStatus: this.latestRuntimeStatus,
|
|
77
109
|
tasks: this.tasks,
|
|
78
110
|
events: dedupeEvents([...this.persistedEvents, ...this.runtimeEvents]).slice(-50),
|
|
79
111
|
};
|
|
80
112
|
}
|
|
113
|
+
getLatestRuntimeStatus() {
|
|
114
|
+
return this.latestRuntimeStatus;
|
|
115
|
+
}
|
|
81
116
|
addEvent(agent, message) {
|
|
82
117
|
this.runtimeEvents.push({
|
|
83
118
|
timestamp: Date.now(),
|
|
@@ -105,6 +140,7 @@ export class DashboardState extends EventEmitter {
|
|
|
105
140
|
}));
|
|
106
141
|
this.persistedEvents = dedupeEvents([...trackerEvents, ...ledgerEvents]).slice(-300);
|
|
107
142
|
this.phase = derivePhase(snapshot);
|
|
143
|
+
this.latestRuntimeStatus = selectLiveRuntimeStatus(snapshot.runtimeStatuses);
|
|
108
144
|
// Surface discovered providers to the TUI controller so dashboard controls
|
|
109
145
|
// reflect what's actually available in the store.
|
|
110
146
|
if (snapshot.providers.length > 0) {
|
package/dist/tui/index.d.ts
CHANGED
|
@@ -30,6 +30,8 @@ export declare class AceTui implements TuiController {
|
|
|
30
30
|
private messageQueue;
|
|
31
31
|
private workspaceRoot;
|
|
32
32
|
private providerBaseUrls;
|
|
33
|
+
private latestRuntimeStatus;
|
|
34
|
+
private pendingCloseTabId;
|
|
33
35
|
constructor(options?: {
|
|
34
36
|
provider?: string;
|
|
35
37
|
model?: string;
|
|
@@ -64,6 +66,7 @@ export declare class AceTui implements TuiController {
|
|
|
64
66
|
private syncTabState;
|
|
65
67
|
/** 1-second tick for status bar, message expiry */
|
|
66
68
|
private tick;
|
|
69
|
+
private getActiveRuntimeStatus;
|
|
67
70
|
private normalizeProvider;
|
|
68
71
|
static defaultSystemPrompt(): string;
|
|
69
72
|
private ensureProvider;
|
|
@@ -92,9 +95,11 @@ export declare class AceTui implements TuiController {
|
|
|
92
95
|
createChatTab(): void;
|
|
93
96
|
createLogTab(): void;
|
|
94
97
|
closeCurrentTab(): void;
|
|
98
|
+
archiveCurrentTab(): Promise<void>;
|
|
95
99
|
showMessage(text: string, level?: "info" | "warn" | "error"): void;
|
|
96
100
|
clearView(): void;
|
|
97
101
|
refresh(): void;
|
|
102
|
+
setApprovalState(state: "not_required" | "pending" | "approved" | "rejected"): boolean;
|
|
98
103
|
getStatus(): Record<string, unknown>;
|
|
99
104
|
quit(): void;
|
|
100
105
|
sendChatMessage(text: string): Promise<void>;
|