@voybio/ace-swarm 0.2.5 → 2.4.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/CHANGELOG.md +19 -1
- package/README.md +21 -13
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- 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/RUNTIME_TOOL_SPEC_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/agent-state/runtime-tool-specs.json +70 -2
- package/assets/instructions/ACE_Coder.instructions.md +13 -0
- package/assets/instructions/ACE_UI.instructions.md +11 -0
- package/assets/scripts/ace-hook-dispatch.mjs +70 -6
- package/assets/scripts/render-mcp-configs.sh +19 -5
- package/dist/ace-context.js +91 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- package/dist/ace-server-instructions.js +3 -3
- package/dist/ace-state-resolver.js +5 -3
- package/dist/agent-runtime/role-adapters.d.ts +18 -1
- package/dist/agent-runtime/role-adapters.js +49 -5
- package/dist/astgrep-index.d.ts +57 -1
- package/dist/astgrep-index.js +140 -4
- package/dist/cli.js +232 -35
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- 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 +295 -0
- package/dist/helpers/drift.d.ts +13 -0
- package/dist/helpers/drift.js +45 -0
- package/dist/helpers/path-utils.d.ts +24 -0
- package/dist/helpers/path-utils.js +123 -0
- package/dist/helpers/store-resolution.d.ts +19 -0
- package/dist/helpers/store-resolution.js +305 -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 +33 -7
- package/dist/json-sanitizer.d.ts +16 -0
- package/dist/json-sanitizer.js +26 -0
- package/dist/local-model-policy.d.ts +27 -0
- package/dist/local-model-policy.js +84 -0
- package/dist/local-model-runtime.d.ts +6 -0
- package/dist/local-model-runtime.js +33 -21
- package/dist/model-bridge.d.ts +13 -1
- package/dist/model-bridge.js +410 -23
- package/dist/orchestrator-supervisor.d.ts +56 -0
- package/dist/orchestrator-supervisor.js +179 -1
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- 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 +20 -1
- package/dist/runtime-executor.js +737 -172
- package/dist/runtime-profile.d.ts +32 -0
- package/dist/runtime-profile.js +89 -13
- package/dist/runtime-tool-specs.d.ts +39 -0
- package/dist/runtime-tool-specs.js +144 -28
- package/dist/safe-edit.d.ts +7 -0
- package/dist/safe-edit.js +163 -37
- package/dist/schemas.js +48 -1
- package/dist/server.js +51 -0
- package/dist/shared.d.ts +3 -2
- package/dist/shared.js +2 -0
- 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 +2 -1
- package/dist/store/bootstrap-store.js +102 -83
- 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/repositories/vericify-repository.d.ts +1 -1
- 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.d.ts +20 -0
- package/dist/tools-agent.js +789 -25
- package/dist/tools-discovery.js +136 -1
- package/dist/tools-files.d.ts +7 -0
- package/dist/tools-files.js +1002 -11
- package/dist/tools-framework.js +105 -66
- 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/index.js +10 -1
- 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/tui/ollama.d.ts +8 -1
- package/dist/tui/ollama.js +53 -12
- package/dist/tui/openai-compatible.d.ts +13 -0
- package/dist/tui/openai-compatible.js +305 -5
- package/dist/tui/provider-discovery.d.ts +1 -0
- package/dist/tui/provider-discovery.js +35 -11
- package/dist/vericify-bridge.d.ts +6 -1
- 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/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/index.js
CHANGED
|
@@ -56,7 +56,13 @@ export class AceTui {
|
|
|
56
56
|
const workspaceRoot = options.workspaceRoot ?? WORKSPACE_ROOT;
|
|
57
57
|
this.workspaceRoot = workspaceRoot;
|
|
58
58
|
this.provider = this.normalizeProvider(options.provider ?? inferProviderFromModel(options.model) ?? "ollama") ?? "ollama";
|
|
59
|
-
|
|
59
|
+
const initialModel = options.model ??
|
|
60
|
+
(this.provider === "ollama"
|
|
61
|
+
? defaultModelForProvider(this.provider)
|
|
62
|
+
: this.provider === "llama.cpp"
|
|
63
|
+
? ""
|
|
64
|
+
: defaultModelForProvider(this.provider));
|
|
65
|
+
this.model = initialModel.trim();
|
|
60
66
|
// Initialize modules
|
|
61
67
|
const colorLevel = detectColorLevel();
|
|
62
68
|
for (const [provider, baseUrl] of Object.entries(options.providerBaseUrls ?? {})) {
|
|
@@ -757,6 +763,9 @@ export class AceTui {
|
|
|
757
763
|
else if (this.provider === "ollama") {
|
|
758
764
|
this.model = DEFAULT_OLLAMA_MODEL;
|
|
759
765
|
}
|
|
766
|
+
else if (this.provider === "llama.cpp") {
|
|
767
|
+
this.model = "";
|
|
768
|
+
}
|
|
760
769
|
else {
|
|
761
770
|
this.model = defaultModelForProvider(this.provider);
|
|
762
771
|
}
|
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 [
|
package/dist/tui/ollama.d.ts
CHANGED
|
@@ -111,6 +111,13 @@ export declare class OllamaClient {
|
|
|
111
111
|
export declare class OllamaError extends Error {
|
|
112
112
|
statusCode: number;
|
|
113
113
|
responseBody: string;
|
|
114
|
-
|
|
114
|
+
kind?: string;
|
|
115
|
+
meta?: Record<string, unknown>;
|
|
116
|
+
suggested_remediation?: string;
|
|
117
|
+
constructor(message: string, statusCode: number, responseBody: string, options?: {
|
|
118
|
+
kind?: string;
|
|
119
|
+
model?: string;
|
|
120
|
+
suggested_remediation?: string;
|
|
121
|
+
});
|
|
115
122
|
}
|
|
116
123
|
//# sourceMappingURL=ollama.d.ts.map
|
package/dist/tui/ollama.js
CHANGED
|
@@ -5,6 +5,24 @@
|
|
|
5
5
|
* Zero dependencies — uses Node.js built-in fetch/http.
|
|
6
6
|
* Supports model listing, pulling, chat streaming, and health checks.
|
|
7
7
|
*/
|
|
8
|
+
function sleep(ms) {
|
|
9
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
10
|
+
}
|
|
11
|
+
function requestedModelFromOptions(options) {
|
|
12
|
+
if (typeof options?.body !== "string")
|
|
13
|
+
return undefined;
|
|
14
|
+
try {
|
|
15
|
+
const parsed = JSON.parse(options.body);
|
|
16
|
+
return typeof parsed.model === "string"
|
|
17
|
+
? parsed.model
|
|
18
|
+
: typeof parsed.name === "string"
|
|
19
|
+
? parsed.name
|
|
20
|
+
: undefined;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
8
26
|
// ── Client ───────────────────────────────────────────────────────────
|
|
9
27
|
export class OllamaClient {
|
|
10
28
|
baseUrl;
|
|
@@ -124,19 +142,36 @@ export class OllamaClient {
|
|
|
124
142
|
throw new OllamaError("Ollama base URL is not configured. Set one explicitly or run `ace doctor --scan`.", 0, "");
|
|
125
143
|
}
|
|
126
144
|
const url = `${this.baseUrl}${path}`;
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
145
|
+
const maxAttempts = 3;
|
|
146
|
+
let lastError;
|
|
147
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
148
|
+
const res = await fetch(url, {
|
|
149
|
+
...options,
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
Accept: "application/json",
|
|
153
|
+
...(options?.headers ?? {}),
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (res.ok)
|
|
157
|
+
return res;
|
|
136
158
|
const text = await res.text().catch(() => "");
|
|
137
|
-
|
|
159
|
+
const requestedModel = requestedModelFromOptions(options);
|
|
160
|
+
if (res.status >= 500 && /unable to load model/i.test(text)) {
|
|
161
|
+
throw new OllamaError(`Ollama was unable to load model${requestedModel ? ` '${requestedModel}'` : ""}. ` +
|
|
162
|
+
`Run \`ollama pull ${requestedModel ?? "<model>"}\` or \`ace doctor --repair-ollama\`.`, res.status, text, {
|
|
163
|
+
kind: "ollama_model_load_error",
|
|
164
|
+
model: requestedModel,
|
|
165
|
+
suggested_remediation: `run ollama pull ${requestedModel ?? "<model>"} or ace doctor --repair-ollama`,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
lastError = new OllamaError(`Ollama API error: ${res.status} ${res.statusText}`, res.status, text);
|
|
169
|
+
if (res.status < 500 || attempt === maxAttempts) {
|
|
170
|
+
throw lastError;
|
|
171
|
+
}
|
|
172
|
+
await sleep(100 * attempt);
|
|
138
173
|
}
|
|
139
|
-
|
|
174
|
+
throw lastError ?? new OllamaError("Ollama API request failed", 0, "");
|
|
140
175
|
}
|
|
141
176
|
/** Stream newline-delimited JSON from a ReadableStream */
|
|
142
177
|
async *streamJsonLines(body) {
|
|
@@ -182,11 +217,17 @@ export class OllamaClient {
|
|
|
182
217
|
export class OllamaError extends Error {
|
|
183
218
|
statusCode;
|
|
184
219
|
responseBody;
|
|
185
|
-
|
|
220
|
+
kind;
|
|
221
|
+
meta;
|
|
222
|
+
suggested_remediation;
|
|
223
|
+
constructor(message, statusCode, responseBody, options) {
|
|
186
224
|
super(message);
|
|
187
225
|
this.statusCode = statusCode;
|
|
188
226
|
this.responseBody = responseBody;
|
|
189
227
|
this.name = "OllamaError";
|
|
228
|
+
this.kind = options?.kind;
|
|
229
|
+
this.meta = options?.model ? { model: options.model } : undefined;
|
|
230
|
+
this.suggested_remediation = options?.suggested_remediation;
|
|
190
231
|
}
|
|
191
232
|
}
|
|
192
233
|
//# sourceMappingURL=ollama.js.map
|
|
@@ -15,6 +15,7 @@ export interface OpenAICompatibleChatRequest {
|
|
|
15
15
|
messages: OpenAICompatibleChatMessage[];
|
|
16
16
|
temperature?: number;
|
|
17
17
|
topP?: number;
|
|
18
|
+
onProviderEvent?: (event: OpenAICompatibleProviderEvent) => void;
|
|
18
19
|
}
|
|
19
20
|
export interface OpenAICompatibleChatChunk {
|
|
20
21
|
text: string;
|
|
@@ -22,6 +23,18 @@ export interface OpenAICompatibleChatChunk {
|
|
|
22
23
|
promptTokens?: number;
|
|
23
24
|
completionTokens?: number;
|
|
24
25
|
}
|
|
26
|
+
export type OpenAICompatibleAdapterStage = "streaming_chat" | "non_streaming_chat" | "completions";
|
|
27
|
+
export interface OpenAICompatibleProviderEvent {
|
|
28
|
+
provider: string;
|
|
29
|
+
stage: OpenAICompatibleAdapterStage;
|
|
30
|
+
event: "attempt" | "success" | "fallback" | "parse_error";
|
|
31
|
+
reason?: string;
|
|
32
|
+
statusCode?: number;
|
|
33
|
+
next_stage?: OpenAICompatibleAdapterStage;
|
|
34
|
+
fallback_count?: number;
|
|
35
|
+
sample_hex?: string;
|
|
36
|
+
sample_text?: string;
|
|
37
|
+
}
|
|
25
38
|
export interface ProviderConfigOverride {
|
|
26
39
|
baseUrl?: string;
|
|
27
40
|
apiKey?: string;
|