@voybio/ace-swarm 0.2.5 → 2.4.0
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/CHANGELOG.md +11 -1
- package/README.md +20 -13
- package/assets/agent-state/EVIDENCE_LOG.md +1 -1
- package/assets/agent-state/MODULES/roles/capability-framework.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-git.json +33 -0
- package/assets/agent-state/MODULES/roles/capability-safety.json +37 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +21 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +43 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +11 -0
- package/assets/agent-state/STATUS.md +2 -2
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +22 -1
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/astgrep-index.d.ts +9 -1
- package/dist/astgrep-index.js +14 -3
- package/dist/cli.js +27 -20
- package/dist/handoff-registry.js +5 -5
- package/dist/helpers/artifacts.d.ts +19 -0
- package/dist/helpers/artifacts.js +152 -0
- package/dist/helpers/bootstrap.d.ts +24 -0
- package/dist/helpers/bootstrap.js +894 -0
- package/dist/helpers/constants.d.ts +53 -0
- package/dist/helpers/constants.js +288 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +17 -0
- package/dist/helpers/path-utils.js +104 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +301 -0
- package/dist/helpers/workspace-root.d.ts +3 -0
- package/dist/helpers/workspace-root.js +80 -0
- package/dist/helpers.d.ts +8 -125
- package/dist/helpers.js +8 -1768
- package/dist/job-scheduler.js +3 -3
- package/dist/local-model-runtime.js +12 -1
- package/dist/model-bridge.d.ts +7 -0
- package/dist/model-bridge.js +75 -5
- package/dist/orchestrator-supervisor.d.ts +14 -0
- package/dist/orchestrator-supervisor.js +72 -1
- package/dist/run-ledger.js +3 -3
- package/dist/runtime-command.d.ts +8 -0
- package/dist/runtime-command.js +38 -6
- package/dist/runtime-executor.d.ts +14 -0
- package/dist/runtime-executor.js +669 -171
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +21 -0
- package/dist/runtime-tool-specs.js +78 -3
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +19 -0
- package/dist/shared.d.ts +2 -2
- package/dist/status-events.js +9 -6
- package/dist/store/ace-packed-store.d.ts +3 -2
- package/dist/store/ace-packed-store.js +188 -110
- package/dist/store/bootstrap-store.d.ts +1 -1
- package/dist/store/bootstrap-store.js +94 -81
- package/dist/store/cache-workspace.js +11 -5
- package/dist/store/materializers/context-snapshot-materializer.js +6 -2
- package/dist/store/materializers/hook-context-materializer.d.ts +6 -9
- package/dist/store/materializers/hook-context-materializer.js +11 -21
- package/dist/store/materializers/host-file-materializer.js +6 -0
- package/dist/store/materializers/projection-manager.d.ts +0 -1
- package/dist/store/materializers/projection-manager.js +5 -13
- package/dist/store/materializers/scheduler-projection-materializer.js +1 -1
- package/dist/store/materializers/vericify-projector.d.ts +7 -7
- package/dist/store/materializers/vericify-projector.js +11 -11
- package/dist/store/repositories/local-model-runtime-repository.d.ts +120 -3
- package/dist/store/repositories/local-model-runtime-repository.js +242 -6
- package/dist/store/skills-install.d.ts +4 -0
- package/dist/store/skills-install.js +21 -12
- package/dist/store/state-reader.d.ts +2 -0
- package/dist/store/state-reader.js +20 -0
- package/dist/store/store-artifacts.d.ts +7 -0
- package/dist/store/store-artifacts.js +27 -1
- package/dist/store/store-authority-audit.d.ts +18 -1
- package/dist/store/store-authority-audit.js +115 -5
- package/dist/store/store-snapshot.d.ts +3 -0
- package/dist/store/store-snapshot.js +22 -2
- package/dist/store/workspace-store-paths.d.ts +39 -0
- package/dist/store/workspace-store-paths.js +94 -0
- package/dist/store/write-coordinator.d.ts +65 -0
- package/dist/store/write-coordinator.js +386 -0
- package/dist/todo-state.js +5 -5
- package/dist/tools-agent.js +268 -14
- package/dist/tools-discovery.js +1 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +299 -10
- package/dist/tools-framework.js +25 -5
- package/dist/tools-handoff.js +2 -2
- package/dist/tools-lifecycle.js +4 -4
- package/dist/tools-memory.js +6 -6
- package/dist/tools-todo.js +2 -2
- package/dist/tracker-adapters.d.ts +1 -1
- package/dist/tracker-adapters.js +13 -18
- package/dist/tracker-sync.js +5 -3
- package/dist/tui/agent-runner.js +3 -1
- package/dist/tui/chat.js +103 -7
- package/dist/tui/dashboard.d.ts +1 -0
- package/dist/tui/dashboard.js +43 -0
- package/dist/tui/layout.d.ts +20 -0
- package/dist/tui/layout.js +31 -1
- package/dist/tui/local-model-contract.d.ts +6 -2
- package/dist/tui/local-model-contract.js +16 -3
- package/dist/vericify-bridge.d.ts +5 -0
- package/dist/vericify-bridge.js +27 -3
- package/dist/workspace-manager.d.ts +30 -3
- package/dist/workspace-manager.js +257 -27
- package/package.json +1 -2
- package/dist/internal-tool-runtime.d.ts +0 -21
- package/dist/internal-tool-runtime.js +0 -136
- package/dist/store/workspace-snapshot.d.ts +0 -26
- package/dist/store/workspace-snapshot.js +0 -107
package/dist/tracker-adapters.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
|
-
import { existsSync,
|
|
4
|
-
import {
|
|
3
|
+
import { existsSync, readFileSync, } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
5
|
import { DEFAULTS_ROOT, resolveWorkspaceArtifactPath as resolveWorkspaceArtifactPathHelper, resolveWorkspaceRoot, } from "./helpers.js";
|
|
6
6
|
import { validateTrackerSnapshotPayload } from "./schemas.js";
|
|
7
|
-
import {
|
|
7
|
+
import { appendStatusEventSafe } from "./status-events.js";
|
|
8
8
|
import { readRuntimeProfile } from "./runtime-profile.js";
|
|
9
|
+
import { safeWriteAsync } from "./helpers.js";
|
|
9
10
|
export const TRACKER_SNAPSHOT_REL_PATH = "agent-state/tracker-snapshot.json";
|
|
10
11
|
export const TRACKER_SNAPSHOT_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/TRACKER_SNAPSHOT.schema.json";
|
|
11
12
|
export const TRACKER_SNAPSHOT_SCHEMA_NAME = "tracker-snapshot@1.0.0";
|
|
@@ -40,14 +41,6 @@ function resolveTrackerSnapshotTarget(explicitPath) {
|
|
|
40
41
|
function resolveWorkspaceArtifactPath(filePath) {
|
|
41
42
|
return resolveWorkspaceArtifactPathHelper(filePath, "write");
|
|
42
43
|
}
|
|
43
|
-
function safeWriteWorkspaceFile(filePath, content) {
|
|
44
|
-
const abs = resolveWorkspaceArtifactPath(filePath);
|
|
45
|
-
mkdirSync(dirname(abs), { recursive: true });
|
|
46
|
-
const tmpPath = `${abs}.${process.pid}.${Date.now()}.tmp`;
|
|
47
|
-
writeFileSync(tmpPath, content, "utf-8");
|
|
48
|
-
renameSync(tmpPath, abs);
|
|
49
|
-
return abs;
|
|
50
|
-
}
|
|
51
44
|
function normalizeTrackerItem(item) {
|
|
52
45
|
return {
|
|
53
46
|
...item,
|
|
@@ -150,12 +143,14 @@ function filterTrackerItems(items, filter) {
|
|
|
150
143
|
}
|
|
151
144
|
function emitTrackerEvent(eventType, summary, payload) {
|
|
152
145
|
try {
|
|
153
|
-
|
|
146
|
+
void appendStatusEventSafe({
|
|
154
147
|
source_module: "capability-framework",
|
|
155
148
|
event_type: eventType,
|
|
156
149
|
status: eventType === "TRACKER_SNAPSHOT_REFRESH_FAILED" ? "fail" : "done",
|
|
157
150
|
summary,
|
|
158
151
|
payload,
|
|
152
|
+
}).catch(() => {
|
|
153
|
+
// Tracker operations must not fail because event logging failed.
|
|
159
154
|
});
|
|
160
155
|
}
|
|
161
156
|
catch {
|
|
@@ -455,7 +450,7 @@ async function githubCreateComment(itemId, body) {
|
|
|
455
450
|
}),
|
|
456
451
|
];
|
|
457
452
|
nextSnapshot.updated_at = now;
|
|
458
|
-
writeTrackerSnapshot(nextSnapshot);
|
|
453
|
+
await writeTrackerSnapshot(nextSnapshot);
|
|
459
454
|
emitTrackerEvent("TRACKER_COMMENT_CREATED", `Tracker comment created for ${issue.item_id}.`, {
|
|
460
455
|
adapter_kind: "external",
|
|
461
456
|
provider: "github",
|
|
@@ -502,7 +497,7 @@ async function githubUpdateItemState(itemId, state) {
|
|
|
502
497
|
try {
|
|
503
498
|
const issue = (await githubGetIssuesById([itemId]))[0];
|
|
504
499
|
const snapshot = upsertTrackerItem(readOrBuildExternalSnapshot(), issue);
|
|
505
|
-
writeTrackerSnapshot(snapshot);
|
|
500
|
+
await writeTrackerSnapshot(snapshot);
|
|
506
501
|
emitTrackerEvent("TRACKER_ITEM_STATE_UPDATED", `Tracker item ${issue.item_id} moved to ${issue.state}.`, {
|
|
507
502
|
adapter_kind: "external",
|
|
508
503
|
provider: "github",
|
|
@@ -569,13 +564,13 @@ export function readTrackerSnapshot() {
|
|
|
569
564
|
export function getTrackerSnapshotPath() {
|
|
570
565
|
return resolveTrackerSnapshotTarget().path;
|
|
571
566
|
}
|
|
572
|
-
export function writeTrackerSnapshot(snapshot) {
|
|
567
|
+
export async function writeTrackerSnapshot(snapshot) {
|
|
573
568
|
const normalized = normalizeTrackerSnapshot(snapshot);
|
|
574
569
|
const validation = validateTrackerSnapshotPayload(normalized);
|
|
575
570
|
if (!validation.ok) {
|
|
576
571
|
throw new Error(`Tracker snapshot failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
|
|
577
572
|
}
|
|
578
|
-
return
|
|
573
|
+
return safeWriteAsync(TRACKER_SNAPSHOT_REL_PATH, JSON.stringify(normalized, null, 2));
|
|
579
574
|
}
|
|
580
575
|
const noneTrackerAdapter = {
|
|
581
576
|
kind: "none",
|
|
@@ -700,7 +695,7 @@ const memoryTrackerAdapter = {
|
|
|
700
695
|
updated_at: now,
|
|
701
696
|
comments: [...snapshot.comments, comment],
|
|
702
697
|
};
|
|
703
|
-
writeTrackerSnapshot(nextSnapshot);
|
|
698
|
+
await writeTrackerSnapshot(nextSnapshot);
|
|
704
699
|
emitTrackerEvent("TRACKER_COMMENT_CREATED", `Tracker comment created for ${comment.item_id}.`, {
|
|
705
700
|
adapter_kind: "memory",
|
|
706
701
|
item_id: comment.item_id,
|
|
@@ -743,7 +738,7 @@ const memoryTrackerAdapter = {
|
|
|
743
738
|
updated_at: now,
|
|
744
739
|
items,
|
|
745
740
|
};
|
|
746
|
-
writeTrackerSnapshot(nextSnapshot);
|
|
741
|
+
await writeTrackerSnapshot(nextSnapshot);
|
|
747
742
|
emitTrackerEvent("TRACKER_ITEM_STATE_UPDATED", `Tracker item ${items[itemIndex].item_id} moved to ${nextState}.`, {
|
|
748
743
|
adapter_kind: "memory",
|
|
749
744
|
item_id: items[itemIndex].item_id,
|
package/dist/tracker-sync.js
CHANGED
|
@@ -2,15 +2,17 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import { resolveWorkspaceRoot } from "./helpers.js";
|
|
3
3
|
import { readRuntimeProfile } from "./runtime-profile.js";
|
|
4
4
|
import { buildDefaultTrackerSnapshot, getTrackerAdapter, writeTrackerSnapshot, readTrackerSnapshot, TRACKER_SNAPSHOT_REL_PATH, } from "./tracker-adapters.js";
|
|
5
|
-
import {
|
|
5
|
+
import { appendStatusEventSafe } from "./status-events.js";
|
|
6
6
|
function emitTrackerSyncEvent(eventType, summary, payload) {
|
|
7
7
|
try {
|
|
8
|
-
|
|
8
|
+
void appendStatusEventSafe({
|
|
9
9
|
source_module: "capability-framework",
|
|
10
10
|
event_type: eventType,
|
|
11
11
|
status: eventType === "TRACKER_SNAPSHOT_REFRESH_FAILED" ? "fail" : "done",
|
|
12
12
|
summary,
|
|
13
13
|
payload,
|
|
14
|
+
}).catch(() => {
|
|
15
|
+
// Snapshot refresh must not fail because observability logging failed.
|
|
14
16
|
});
|
|
15
17
|
}
|
|
16
18
|
catch {
|
|
@@ -52,7 +54,7 @@ export async function refreshTrackerSnapshot(kind) {
|
|
|
52
54
|
items,
|
|
53
55
|
};
|
|
54
56
|
}
|
|
55
|
-
const writtenPath = writeTrackerSnapshot(snapshot);
|
|
57
|
+
const writtenPath = await writeTrackerSnapshot(snapshot);
|
|
56
58
|
emitTrackerSyncEvent("TRACKER_SNAPSHOT_REFRESHED", `Tracker snapshot refreshed using ${snapshot.adapter_kind}.`, {
|
|
57
59
|
adapter_kind: snapshot.adapter_kind,
|
|
58
60
|
snapshot_path: writtenPath,
|
package/dist/tui/agent-runner.js
CHANGED
|
@@ -17,7 +17,9 @@ import { readAgentInstructions } from "../helpers.js";
|
|
|
17
17
|
export class AgentRunner extends EventEmitter {
|
|
18
18
|
agents = new Map();
|
|
19
19
|
workspaceRoot;
|
|
20
|
-
|
|
20
|
+
// Full-suite test runs can heavily contend CPU/IO while workers bootstrap.
|
|
21
|
+
// Keep startup resilient to transient contention before declaring failure.
|
|
22
|
+
startupTimeoutMs = 12000;
|
|
21
23
|
constructor(workspaceRoot) {
|
|
22
24
|
super();
|
|
23
25
|
this.workspaceRoot = workspaceRoot;
|
package/dist/tui/chat.js
CHANGED
|
@@ -380,7 +380,11 @@ export class ChatSession extends EventEmitter {
|
|
|
380
380
|
return;
|
|
381
381
|
const { updated_at: _updatedAt, ...record } = status;
|
|
382
382
|
void withLocalModelRuntimeRepository(this.workspaceRoot, async (repo) => {
|
|
383
|
-
await repo.
|
|
383
|
+
await repo.upsertRuntimeStatusWithTransition(record, {
|
|
384
|
+
from: this.currentRuntimeStatus?.bridge_status,
|
|
385
|
+
reason: "Interactive runtime status updated after provider/tool progress.",
|
|
386
|
+
evidence_refs: [this.sessionId],
|
|
387
|
+
});
|
|
384
388
|
}).catch((error) => {
|
|
385
389
|
this.emit("error", `Runtime status persistence failed: ${formatErrorMessage(error)}`);
|
|
386
390
|
});
|
|
@@ -390,14 +394,26 @@ export class ChatSession extends EventEmitter {
|
|
|
390
394
|
return;
|
|
391
395
|
void withLocalModelRuntimeRepository(this.workspaceRoot, async (repo) => {
|
|
392
396
|
if (input.activationLedger) {
|
|
393
|
-
await repo.
|
|
397
|
+
await repo.upsertActivationLedgerWithTransition(input.activationLedger, {
|
|
398
|
+
from: this.activationLedger ? "updated" : "created",
|
|
399
|
+
reason: "Interactive activation ledger updated after ACE session progress.",
|
|
400
|
+
evidence_refs: [this.sessionId],
|
|
401
|
+
});
|
|
394
402
|
}
|
|
395
403
|
if (input.runtimeStatus) {
|
|
396
404
|
const { updated_at: _updatedAt, ...statusRecord } = input.runtimeStatus;
|
|
397
|
-
await repo.
|
|
405
|
+
await repo.upsertRuntimeStatusWithTransition(statusRecord, {
|
|
406
|
+
from: this.currentRuntimeStatus?.bridge_status,
|
|
407
|
+
reason: "Interactive runtime status updated after ACE session progress.",
|
|
408
|
+
evidence_refs: [this.sessionId],
|
|
409
|
+
});
|
|
398
410
|
}
|
|
399
411
|
if (input.continuity) {
|
|
400
|
-
await repo.
|
|
412
|
+
await repo.upsertContinuityRecordWithTransition(input.continuity, {
|
|
413
|
+
from: this.currentRuntimeStatus?.bridge_status,
|
|
414
|
+
reason: "Interactive continuity record updated after ACE session progress.",
|
|
415
|
+
evidence_refs: [this.sessionId],
|
|
416
|
+
});
|
|
401
417
|
}
|
|
402
418
|
}).catch((error) => {
|
|
403
419
|
this.emit("error", `ACE runtime persistence failed: ${formatErrorMessage(error)}`);
|
|
@@ -416,15 +432,79 @@ export class ChatSession extends EventEmitter {
|
|
|
416
432
|
preferredRole: this.aceRole,
|
|
417
433
|
});
|
|
418
434
|
const executionRole = preflight.recommended_role ?? this.aceRole;
|
|
435
|
+
const prevLedger = this.activationLedger;
|
|
419
436
|
let activationLedger = nextActivationLedger(this.sessionId, this.activationLedger ?? undefined, preflight.recommended_next_action);
|
|
420
|
-
|
|
437
|
+
// Detect previously shown but unaccepted/unauto-executed nudges as ignored
|
|
438
|
+
if (prevLedger) {
|
|
439
|
+
const autoExecuted = prevLedger.auto_executed_nudges ?? [];
|
|
440
|
+
const accepted = prevLedger.accepted_nudges;
|
|
441
|
+
const alreadyIgnored = prevLedger.ignored_nudges ?? [];
|
|
442
|
+
const ignoredNow = prevLedger.shown_nudges.filter((id) => !accepted.includes(id) && !autoExecuted.includes(id) && !alreadyIgnored.includes(id));
|
|
443
|
+
if (ignoredNow.length > 0) {
|
|
444
|
+
activationLedger = {
|
|
445
|
+
...activationLedger,
|
|
446
|
+
ignored_nudges: uniqueStrings([...(activationLedger.ignored_nudges ?? []), ...ignoredNow]),
|
|
447
|
+
};
|
|
448
|
+
if (this.workspaceRoot) {
|
|
449
|
+
const wsr = this.workspaceRoot;
|
|
450
|
+
for (const nudgeId of ignoredNow) {
|
|
451
|
+
void withLocalModelRuntimeRepository(wsr, async (repo) => {
|
|
452
|
+
await repo.appendTransitionRecord({
|
|
453
|
+
session_id: this.sessionId,
|
|
454
|
+
subject_kind: "nudge",
|
|
455
|
+
subject_id: nudgeId,
|
|
456
|
+
from: "shown",
|
|
457
|
+
to: "ignored",
|
|
458
|
+
reason: `Nudge ${nudgeId} ignored: user sent new message without accepting`,
|
|
459
|
+
evidence_refs: [],
|
|
460
|
+
});
|
|
461
|
+
}).catch(() => { });
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
const startupNudge = buildStartupNudge(preflight, activationLedger, this.currentRuntimeStatus);
|
|
421
467
|
if (startupNudge) {
|
|
422
468
|
activationLedger = {
|
|
423
469
|
...activationLedger,
|
|
424
470
|
shown_nudges: uniqueStrings([...activationLedger.shown_nudges, startupNudge.id]),
|
|
471
|
+
...(startupNudge.auto_executable
|
|
472
|
+
? {
|
|
473
|
+
auto_executed_nudges: uniqueStrings([
|
|
474
|
+
...(activationLedger.auto_executed_nudges ?? []),
|
|
475
|
+
startupNudge.id,
|
|
476
|
+
]),
|
|
477
|
+
}
|
|
478
|
+
: {}),
|
|
425
479
|
};
|
|
426
480
|
this.pushSystemMessage(`Hint: ${startupNudge.text}`);
|
|
427
481
|
this.emit("startup_nudge", startupNudge);
|
|
482
|
+
if (this.workspaceRoot) {
|
|
483
|
+
const wsr = this.workspaceRoot;
|
|
484
|
+
const nudge = startupNudge;
|
|
485
|
+
void withLocalModelRuntimeRepository(wsr, async (repo) => {
|
|
486
|
+
await repo.appendTransitionRecord({
|
|
487
|
+
session_id: this.sessionId,
|
|
488
|
+
subject_kind: "nudge",
|
|
489
|
+
subject_id: nudge.id,
|
|
490
|
+
from: "new",
|
|
491
|
+
to: "shown",
|
|
492
|
+
reason: `Startup nudge shown for action: ${nudge.recommended_action}`,
|
|
493
|
+
evidence_refs: [],
|
|
494
|
+
});
|
|
495
|
+
if (nudge.auto_executable) {
|
|
496
|
+
await repo.appendTransitionRecord({
|
|
497
|
+
session_id: this.sessionId,
|
|
498
|
+
subject_kind: "nudge",
|
|
499
|
+
subject_id: nudge.id,
|
|
500
|
+
from: "shown",
|
|
501
|
+
to: "auto_executed",
|
|
502
|
+
reason: `Auto-executed safe action: ${nudge.recommended_action}`,
|
|
503
|
+
evidence_refs: [],
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
}).catch(() => { });
|
|
507
|
+
}
|
|
428
508
|
}
|
|
429
509
|
let runtimeStatus = this.buildRuntimeStatusPacket({
|
|
430
510
|
role: executionRole,
|
|
@@ -488,15 +568,31 @@ export class ChatSession extends EventEmitter {
|
|
|
488
568
|
},
|
|
489
569
|
onToolCall: (tool, args) => {
|
|
490
570
|
toolNames.push(tool);
|
|
571
|
+
const nudgeToAccept = tool === startupNudge?.recommended_action ? startupNudge : undefined;
|
|
491
572
|
activationLedger = {
|
|
492
573
|
...activationLedger,
|
|
493
574
|
updated_at: Date.now(),
|
|
494
575
|
activated_tools: uniqueStrings([...activationLedger.activated_tools, tool]),
|
|
495
576
|
activated_roles: uniqueStrings([...activationLedger.activated_roles, executionRole]),
|
|
496
|
-
accepted_nudges:
|
|
497
|
-
? uniqueStrings([...activationLedger.accepted_nudges,
|
|
577
|
+
accepted_nudges: nudgeToAccept
|
|
578
|
+
? uniqueStrings([...activationLedger.accepted_nudges, nudgeToAccept.id])
|
|
498
579
|
: activationLedger.accepted_nudges,
|
|
499
580
|
};
|
|
581
|
+
if (nudgeToAccept && this.workspaceRoot) {
|
|
582
|
+
const wsr = this.workspaceRoot;
|
|
583
|
+
const nudge = nudgeToAccept;
|
|
584
|
+
void withLocalModelRuntimeRepository(wsr, async (repo) => {
|
|
585
|
+
await repo.appendTransitionRecord({
|
|
586
|
+
session_id: this.sessionId,
|
|
587
|
+
subject_kind: "nudge",
|
|
588
|
+
subject_id: nudge.id,
|
|
589
|
+
from: "shown",
|
|
590
|
+
to: "manually_accepted",
|
|
591
|
+
reason: `Nudge accepted via tool call: ${tool}`,
|
|
592
|
+
evidence_refs: [],
|
|
593
|
+
});
|
|
594
|
+
}).catch(() => { });
|
|
595
|
+
}
|
|
500
596
|
runtimeStatus = {
|
|
501
597
|
...runtimeStatus,
|
|
502
598
|
bridge_status: "running",
|
package/dist/tui/dashboard.d.ts
CHANGED
package/dist/tui/dashboard.js
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import { EventEmitter } from "node:events";
|
|
8
8
|
import { pollStore } from "../store/state-reader.js";
|
|
9
9
|
import { getWorkspaceStorePath } from "../store/store-snapshot.js";
|
|
10
|
+
import { getLivenessDefaults } from "../runtime-profile.js";
|
|
10
11
|
function dedupeEvents(events) {
|
|
11
12
|
const seen = new Set();
|
|
12
13
|
const ordered = [];
|
|
@@ -35,6 +36,18 @@ export function selectLiveRuntimeStatus(statuses) {
|
|
|
35
36
|
}
|
|
36
37
|
function derivePhase(snapshot) {
|
|
37
38
|
const runtimeStatus = selectLiveRuntimeStatus(snapshot.runtimeStatuses);
|
|
39
|
+
if (runtimeStatus?.sleep_state === "stalled") {
|
|
40
|
+
return "Stalled";
|
|
41
|
+
}
|
|
42
|
+
if (runtimeStatus?.sleep_state === "sleeping") {
|
|
43
|
+
return "Sleeping";
|
|
44
|
+
}
|
|
45
|
+
if (runtimeStatus?.sleep_state === "approval_pending") {
|
|
46
|
+
return "Approval Pending";
|
|
47
|
+
}
|
|
48
|
+
if (runtimeStatus?.sleep_state === "dependency_wait") {
|
|
49
|
+
return runtimeStatus.waiting_on ? `Waiting: ${runtimeStatus.waiting_on}` : "Waiting";
|
|
50
|
+
}
|
|
38
51
|
if (runtimeStatus?.bridge_status === "blocked") {
|
|
39
52
|
return "Blocked";
|
|
40
53
|
}
|
|
@@ -48,6 +61,15 @@ function derivePhase(snapshot) {
|
|
|
48
61
|
return "Retrying";
|
|
49
62
|
}
|
|
50
63
|
if (runtimeStatus?.bridge_status === "running") {
|
|
64
|
+
const kind = runtimeStatus.surface_kind;
|
|
65
|
+
if (kind === "unattended_runtime")
|
|
66
|
+
return "Running (Unattended)";
|
|
67
|
+
if (kind === "scheduled_job")
|
|
68
|
+
return "Running (Scheduled)";
|
|
69
|
+
if (kind === "bridge_resumed")
|
|
70
|
+
return "Running (Resumed)";
|
|
71
|
+
if (kind === "supervised_step")
|
|
72
|
+
return "Running (Supervised)";
|
|
51
73
|
return "Running";
|
|
52
74
|
}
|
|
53
75
|
if (snapshot.jobs.some((job) => String(job.status ?? "") === "running")) {
|
|
@@ -73,6 +95,7 @@ export class DashboardState extends EventEmitter {
|
|
|
73
95
|
runtimeEvents = [];
|
|
74
96
|
phase = "Initializing";
|
|
75
97
|
latestRuntimeStatus;
|
|
98
|
+
latestTransitions = [];
|
|
76
99
|
constructor(workspaceRoot) {
|
|
77
100
|
super();
|
|
78
101
|
this.workspaceRoot = workspaceRoot;
|
|
@@ -108,6 +131,7 @@ export class DashboardState extends EventEmitter {
|
|
|
108
131
|
runtimeStatus: this.latestRuntimeStatus,
|
|
109
132
|
tasks: this.tasks,
|
|
110
133
|
events: dedupeEvents([...this.persistedEvents, ...this.runtimeEvents]).slice(-50),
|
|
134
|
+
recentTransitions: this.latestTransitions,
|
|
111
135
|
};
|
|
112
136
|
}
|
|
113
137
|
getLatestRuntimeStatus() {
|
|
@@ -141,6 +165,25 @@ export class DashboardState extends EventEmitter {
|
|
|
141
165
|
this.persistedEvents = dedupeEvents([...trackerEvents, ...ledgerEvents]).slice(-300);
|
|
142
166
|
this.phase = derivePhase(snapshot);
|
|
143
167
|
this.latestRuntimeStatus = selectLiveRuntimeStatus(snapshot.runtimeStatuses);
|
|
168
|
+
// Compute read-only staleness projection — no watcher, computed on each snapshot
|
|
169
|
+
if (this.latestRuntimeStatus?.last_progress_at) {
|
|
170
|
+
const livenessDefaults = getLivenessDefaults(this.latestRuntimeStatus.model_class);
|
|
171
|
+
const effectiveStallWindowMs = this.latestRuntimeStatus.stall_window_ms ?? livenessDefaults.stall_window_ms;
|
|
172
|
+
const stalenessSec = (Date.now() - this.latestRuntimeStatus.last_progress_at) / 1000;
|
|
173
|
+
const stallWindowSec = effectiveStallWindowMs / 1000;
|
|
174
|
+
this.latestRuntimeStatus["staleness_seconds"] = stalenessSec;
|
|
175
|
+
this.latestRuntimeStatus["stall_window_seconds"] = stallWindowSec;
|
|
176
|
+
this.latestRuntimeStatus["is_stale"] = stalenessSec >= stallWindowSec;
|
|
177
|
+
}
|
|
178
|
+
this.latestTransitions = (snapshot.recentTransitions ?? []).map((t) => ({
|
|
179
|
+
subject_kind: t.subject_kind,
|
|
180
|
+
subject_id: t.subject_id,
|
|
181
|
+
from: t.from,
|
|
182
|
+
to: t.to,
|
|
183
|
+
reason: t.reason,
|
|
184
|
+
evidence_refs: t.evidence_refs,
|
|
185
|
+
at: t.at,
|
|
186
|
+
}));
|
|
144
187
|
// Surface discovered providers to the TUI controller so dashboard controls
|
|
145
188
|
// reflect what's actually available in the store.
|
|
146
189
|
if (snapshot.providers.length > 0) {
|
package/dist/tui/layout.d.ts
CHANGED
|
@@ -60,11 +60,20 @@ interface RuntimeStatusSummary {
|
|
|
60
60
|
bridge_status: string;
|
|
61
61
|
preflight_state: string;
|
|
62
62
|
role?: string;
|
|
63
|
+
model_class?: "frontier" | "mid" | "small_local";
|
|
63
64
|
approval_state?: string;
|
|
64
65
|
active_tool?: string;
|
|
65
66
|
active_tool_role?: string;
|
|
66
67
|
recommended_next_action?: string;
|
|
67
68
|
blocked_reason?: string;
|
|
69
|
+
active_workspace_path?: string;
|
|
70
|
+
workspace_hook_health?: "ok" | "degraded" | "failed";
|
|
71
|
+
staleness_seconds?: number;
|
|
72
|
+
stall_window_seconds?: number;
|
|
73
|
+
is_stale?: boolean;
|
|
74
|
+
stall_restart_count?: number;
|
|
75
|
+
current_task?: string;
|
|
76
|
+
surface_kind?: string;
|
|
68
77
|
}
|
|
69
78
|
export declare class LayoutManager {
|
|
70
79
|
private zones;
|
|
@@ -88,6 +97,7 @@ export declare class LayoutManager {
|
|
|
88
97
|
renderStatusBar(state: StatusBarState): void;
|
|
89
98
|
renderInputLine(buffer: string, cursorPos: number, mode: string, prompt?: string): void;
|
|
90
99
|
renderDashboard(data: DashboardData, controls?: DashboardControlState): void;
|
|
100
|
+
private renderTransitionsPanel;
|
|
91
101
|
private renderStatusPanel;
|
|
92
102
|
private renderControlStrip;
|
|
93
103
|
private renderTasksPanel;
|
|
@@ -119,11 +129,21 @@ export interface DashboardData {
|
|
|
119
129
|
runtimeStatus?: RuntimeStatusSummary;
|
|
120
130
|
tasks: TaskItem[];
|
|
121
131
|
events: ActivityEvent[];
|
|
132
|
+
recentTransitions?: Array<{
|
|
133
|
+
subject_kind: string;
|
|
134
|
+
subject_id: string;
|
|
135
|
+
from?: string;
|
|
136
|
+
to: string;
|
|
137
|
+
reason: string;
|
|
138
|
+
evidence_refs?: string[];
|
|
139
|
+
at: number;
|
|
140
|
+
}>;
|
|
122
141
|
}
|
|
123
142
|
export interface TaskItem {
|
|
124
143
|
title: string;
|
|
125
144
|
done: boolean;
|
|
126
145
|
priority?: string;
|
|
146
|
+
upstream_annotation?: string;
|
|
127
147
|
}
|
|
128
148
|
export interface ActivityEvent {
|
|
129
149
|
timestamp: number;
|
package/dist/tui/layout.js
CHANGED
|
@@ -250,6 +250,18 @@ export class LayoutManager {
|
|
|
250
250
|
drawBox(content.x, actY, content.width, actH, { title: "Agent Activity", titleColor: fg.brightGreen, borderColor: fg.gray, rounded: true });
|
|
251
251
|
this.renderActivityLog(content.x + 2, actY + 1, content.width - 4, actH - 2, data.events);
|
|
252
252
|
}
|
|
253
|
+
renderTransitionsPanel(x, y, w, data) {
|
|
254
|
+
const transitions = data.recentTransitions?.slice(-3) ?? [];
|
|
255
|
+
if (transitions.length === 0)
|
|
256
|
+
return;
|
|
257
|
+
writeAt(x, y, `${fg.gray}─── Recent Transitions ───`);
|
|
258
|
+
for (let i = 0; i < transitions.length; i++) {
|
|
259
|
+
const t = transitions[i];
|
|
260
|
+
const age = Math.round((Date.now() - t.at) / 1000);
|
|
261
|
+
const line = `${fg.gray}${age}s ago ${fg.cyan}${t.subject_kind}${fg.gray}:${fg.white}${t.to}${fg.gray} — ${truncate(t.reason, 28)}`;
|
|
262
|
+
writeAt(x, y + 1 + i, truncate(line + style.reset, w));
|
|
263
|
+
}
|
|
264
|
+
}
|
|
253
265
|
renderStatusPanel(x, y, w, h, data) {
|
|
254
266
|
const lines = [
|
|
255
267
|
`${fg.gray}Phase: ${fg.brightWhite}${data.phase}`,
|
|
@@ -266,12 +278,29 @@ export class LayoutManager {
|
|
|
266
278
|
data.runtimeStatus?.role
|
|
267
279
|
? `${fg.gray}Role: ${fg.brightCyan}${data.runtimeStatus.role}`
|
|
268
280
|
: undefined,
|
|
281
|
+
data.runtimeStatus?.model_class
|
|
282
|
+
? `${fg.gray}Model: ${fg.brightCyan}${data.runtimeStatus.model_class}`
|
|
283
|
+
: undefined,
|
|
269
284
|
data.runtimeStatus?.blocked_reason
|
|
270
285
|
? `${fg.gray}Blocker: ${fg.brightRed}${data.runtimeStatus.blocked_reason}`
|
|
271
286
|
: undefined,
|
|
272
287
|
data.runtimeStatus?.recommended_next_action
|
|
273
288
|
? `${fg.gray}Next: ${fg.white}${data.runtimeStatus.recommended_next_action}`
|
|
274
289
|
: undefined,
|
|
290
|
+
data.runtimeStatus?.active_workspace_path
|
|
291
|
+
? `${fg.gray}Wrkspace:${fg.cyan} ${truncate(data.runtimeStatus.active_workspace_path, 32)} ${fg.gray}[hook: ${data.runtimeStatus.workspace_hook_health ?? "?"}]`
|
|
292
|
+
: undefined,
|
|
293
|
+
data.runtimeStatus?.stall_restart_count
|
|
294
|
+
? `${fg.gray}Stalls: ${fg.yellow}${data.runtimeStatus.stall_restart_count} restart(s)`
|
|
295
|
+
: undefined,
|
|
296
|
+
data.runtimeStatus?.is_stale
|
|
297
|
+
? `${fg.brightRed}⚠ STALE ${fg.gray}${(data.runtimeStatus.staleness_seconds ?? 0).toFixed(0)}s since last progress (window: ${(data.runtimeStatus.stall_window_seconds ?? 0).toFixed(0)}s)`
|
|
298
|
+
: data.runtimeStatus?.staleness_seconds !== undefined
|
|
299
|
+
? `${fg.gray}Progress:${fg.brightGreen} ${(data.runtimeStatus.staleness_seconds).toFixed(0)}s ago`
|
|
300
|
+
: undefined,
|
|
301
|
+
data.runtimeStatus?.current_task
|
|
302
|
+
? `${fg.gray}Task: ${fg.white}${truncate(data.runtimeStatus.current_task, 40)}`
|
|
303
|
+
: undefined,
|
|
275
304
|
`${fg.gray}Provider:${fg.brightCyan} ${padRight(data.provider, 8)}`,
|
|
276
305
|
`${fg.gray}Model: ${fg.brightCyan}${data.model}`,
|
|
277
306
|
`${fg.gray}Agents: ${fg.brightGreen}${data.activeAgents}${fg.gray}/${data.totalAgents} active`,
|
|
@@ -304,7 +333,8 @@ export class LayoutManager {
|
|
|
304
333
|
const t = tasks[i];
|
|
305
334
|
const icon = t.done ? `${fg.green}${symbols.check}` : `${fg.gray}${symbols.hollowBullet}`;
|
|
306
335
|
const priority = t.priority ? `${fg.gray}[${t.priority}]` : "";
|
|
307
|
-
const
|
|
336
|
+
const upstream = t.upstream_annotation ? ` ${fg.gray}← ${t.upstream_annotation}` : "";
|
|
337
|
+
const line = `${icon} ${fg.white}${t.title} ${priority}${upstream}${style.reset}`;
|
|
308
338
|
writeAt(x, y + i, truncate(line, w));
|
|
309
339
|
}
|
|
310
340
|
if (tasks.length === 0) {
|
|
@@ -15,11 +15,14 @@ export interface AcePreflightPacket {
|
|
|
15
15
|
recall_summary?: string;
|
|
16
16
|
should_synthesize_plan: boolean;
|
|
17
17
|
}
|
|
18
|
-
export interface
|
|
18
|
+
export interface AceStartupNudge {
|
|
19
19
|
id: string;
|
|
20
20
|
text: string;
|
|
21
21
|
recommended_action: string;
|
|
22
|
+
auto_executable: boolean;
|
|
22
23
|
}
|
|
24
|
+
/** @deprecated Use AceStartupNudge */
|
|
25
|
+
export type StartupNudge = AceStartupNudge;
|
|
23
26
|
export declare function shouldSynthesizeShortPlan(task: string): boolean;
|
|
24
27
|
export declare function buildAcePreflightPacket(input: {
|
|
25
28
|
sessionId: string;
|
|
@@ -28,7 +31,8 @@ export declare function buildAcePreflightPacket(input: {
|
|
|
28
31
|
preferredRole?: string;
|
|
29
32
|
}): AcePreflightPacket;
|
|
30
33
|
export declare function nextActivationLedger(sessionId: string, current: AceSessionActivationLedger | undefined, recommendedAction: string | undefined): AceSessionActivationLedger;
|
|
31
|
-
export declare function
|
|
34
|
+
export declare function shouldAutoExecuteNudge(action: string, surfaceKind: AceRuntimeStatusPacket["surface_kind"] | undefined): boolean;
|
|
35
|
+
export declare function buildStartupNudge(preflight: AcePreflightPacket, ledger: AceSessionActivationLedger, statusPacket?: AceRuntimeStatusPacket | null): AceStartupNudge | undefined;
|
|
32
36
|
export declare function buildBridgeTaskInput(conversation: string, preflight: AcePreflightPacket): string;
|
|
33
37
|
export declare function mapBridgeResultToRuntimeStatus(input: {
|
|
34
38
|
current: AceRuntimeStatusPacket;
|
|
@@ -122,10 +122,18 @@ export function nextActivationLedger(sessionId, current, recommendedAction) {
|
|
|
122
122
|
accepted_nudges: [...(current?.accepted_nudges ?? [])],
|
|
123
123
|
activated_tools: [...(current?.activated_tools ?? [])],
|
|
124
124
|
activated_roles: [...(current?.activated_roles ?? [])],
|
|
125
|
-
|
|
125
|
+
auto_executed_nudges: current?.auto_executed_nudges ? [...current.auto_executed_nudges] : undefined,
|
|
126
|
+
ignored_nudges: current?.ignored_nudges ? [...current.ignored_nudges] : undefined,
|
|
126
127
|
};
|
|
127
128
|
}
|
|
128
|
-
export function
|
|
129
|
+
export function shouldAutoExecuteNudge(action, surfaceKind) {
|
|
130
|
+
const SAFE_TRIO = new Set(["validate_framework", "recall_context", "run_orchestrator"]);
|
|
131
|
+
return (SAFE_TRIO.has(action) &&
|
|
132
|
+
(surfaceKind === "unattended_runtime" ||
|
|
133
|
+
surfaceKind === "scheduled_job" ||
|
|
134
|
+
surfaceKind === "supervised_step"));
|
|
135
|
+
}
|
|
136
|
+
export function buildStartupNudge(preflight, ledger, statusPacket) {
|
|
129
137
|
const action = preflight.recommended_next_action;
|
|
130
138
|
if (!action)
|
|
131
139
|
return undefined;
|
|
@@ -140,7 +148,12 @@ export function buildStartupNudge(preflight, ledger) {
|
|
|
140
148
|
: action === "route_task"
|
|
141
149
|
? "Role selection is still ambiguous. Route the task before deeper work."
|
|
142
150
|
: "Load ACE context first with recall_context before deeper work.";
|
|
143
|
-
return {
|
|
151
|
+
return {
|
|
152
|
+
id: nudgeId,
|
|
153
|
+
text,
|
|
154
|
+
recommended_action: action,
|
|
155
|
+
auto_executable: shouldAutoExecuteNudge(action, statusPacket?.surface_kind),
|
|
156
|
+
};
|
|
144
157
|
}
|
|
145
158
|
function compactTrapPack() {
|
|
146
159
|
return [
|
|
@@ -71,6 +71,11 @@ export interface VericifyBridgeSnapshot {
|
|
|
71
71
|
process_posts: VericifyBridgeSourceState;
|
|
72
72
|
};
|
|
73
73
|
active_run_refs: VericifyBridgeActiveRunRef[];
|
|
74
|
+
ace_runtime_enrichment?: {
|
|
75
|
+
live_session_id?: string;
|
|
76
|
+
last_turn_outcome?: string;
|
|
77
|
+
last_turn_outcome_reason?: string;
|
|
78
|
+
};
|
|
74
79
|
}
|
|
75
80
|
export interface VericifyBridgeSnapshotResult {
|
|
76
81
|
ok: boolean;
|
package/dist/vericify-bridge.js
CHANGED
|
@@ -9,7 +9,7 @@ import { ProjectionManager } from "./store/materializers/projection-manager.js";
|
|
|
9
9
|
import { VericifyRepository } from "./store/repositories/vericify-repository.js";
|
|
10
10
|
import { getWorkspaceStorePath, storeExistsSync } from "./store/store-snapshot.js";
|
|
11
11
|
import { isOperationalArtifactPath, operationalArtifactVirtualPath } from "./store/store-artifacts.js";
|
|
12
|
-
import {
|
|
12
|
+
import { withStoreWriteCoordinator } from "./store/write-coordinator.js";
|
|
13
13
|
export const VERICIFY_PROCESS_POST_LOG_REL_PATH = "agent-state/vericify/process-posts.json";
|
|
14
14
|
export const VERICIFY_PROCESS_POST_LOG_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/VERICIFY_PROCESS_POST_LOG.schema.json";
|
|
15
15
|
export const VERICIFY_PROCESS_POST_LOG_SCHEMA_NAME = "vericify-process-post-log@1.0.0";
|
|
@@ -363,7 +363,7 @@ export async function appendVericifyProcessPost(input) {
|
|
|
363
363
|
const path = await withFileLock(VERICIFY_PROCESS_POST_LOG_REL_PATH, async () => {
|
|
364
364
|
const root = workspaceRoot();
|
|
365
365
|
const storePath = getWorkspaceStorePath(root);
|
|
366
|
-
return
|
|
366
|
+
return withStoreWriteCoordinator(storePath, async () => {
|
|
367
367
|
const store = await openStore(storePath);
|
|
368
368
|
try {
|
|
369
369
|
const repo = new VericifyRepository(store);
|
|
@@ -390,7 +390,7 @@ export async function appendVericifyProcessPost(input) {
|
|
|
390
390
|
await store.close();
|
|
391
391
|
}
|
|
392
392
|
return processPostLogPath();
|
|
393
|
-
});
|
|
393
|
+
}, { operation_label: "appendVericifyProcessPost" });
|
|
394
394
|
});
|
|
395
395
|
return {
|
|
396
396
|
path,
|
|
@@ -544,6 +544,30 @@ export function buildVericifyBridgeSnapshot() {
|
|
|
544
544
|
}),
|
|
545
545
|
},
|
|
546
546
|
active_run_refs: buildActiveRunRefs(),
|
|
547
|
+
ace_runtime_enrichment: (() => {
|
|
548
|
+
const rawSessions = safeRead(runtimeExecutorPath);
|
|
549
|
+
if (!rawSessions)
|
|
550
|
+
return undefined;
|
|
551
|
+
try {
|
|
552
|
+
const parsed = JSON.parse(rawSessions);
|
|
553
|
+
const runningSession = Array.isArray(parsed.sessions)
|
|
554
|
+
? parsed.sessions.find((s) => s.status === "running")
|
|
555
|
+
: undefined;
|
|
556
|
+
if (!runningSession)
|
|
557
|
+
return undefined;
|
|
558
|
+
const lastTurn = Array.isArray(runningSession.turns) && runningSession.turns.length > 0
|
|
559
|
+
? runningSession.turns[runningSession.turns.length - 1]
|
|
560
|
+
: undefined;
|
|
561
|
+
return {
|
|
562
|
+
live_session_id: runningSession.session_id,
|
|
563
|
+
last_turn_outcome: lastTurn?.turn_outcome,
|
|
564
|
+
last_turn_outcome_reason: lastTurn?.outcome_reason,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
catch {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
})(),
|
|
547
571
|
};
|
|
548
572
|
}
|
|
549
573
|
export function refreshVericifyBridgeSnapshot() {
|