@voybio/ace-swarm 2.4.1 → 2.4.2
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 +8 -0
- package/README.md +501 -56
- package/dist/cli.js +282 -2
- package/dist/helpers/constants.d.ts +2 -0
- package/dist/helpers/constants.js +1 -0
- package/dist/hermes/bridge-protocol.d.ts +41 -0
- package/dist/hermes/bridge-protocol.js +70 -0
- package/dist/hermes/launch-profile.d.ts +19 -0
- package/dist/hermes/launch-profile.js +81 -0
- package/dist/hermes/session-manager.d.ts +42 -0
- package/dist/hermes/session-manager.js +187 -0
- package/dist/local-model-runtime.d.ts +11 -0
- package/dist/local-model-runtime.js +58 -2
- package/dist/schemas.js +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.js +22 -4
- package/dist/store/materializers/vericify-projector.js +3 -0
- package/dist/store/repositories/local-model-runtime-repository.d.ts +12 -0
- package/dist/store/repositories/local-model-runtime-repository.js +3 -0
- package/dist/tools-agent.js +6 -1
- package/dist/tools.d.ts +4 -1
- package/dist/tools.js +35 -13
- package/dist/tui/chat.d.ts +8 -0
- package/dist/tui/chat.js +74 -0
- package/dist/tui/index.d.ts +7 -0
- package/dist/tui/index.js +35 -1
- package/dist/tui/layout.d.ts +1 -0
- package/dist/tui/layout.js +4 -1
- package/dist/tui/provider-discovery.js +15 -13
- package/dist/vericify-bridge.d.ts +3 -0
- package/dist/vericify-bridge.js +3 -0
- package/package.json +2 -1
- package/scripts/hermes_bridge_worker.py +136 -0
package/dist/tui/chat.js
CHANGED
|
@@ -13,6 +13,7 @@ import { ModelBridge } from "../model-bridge.js";
|
|
|
13
13
|
import { resolveAceStateLayout } from "../ace-state-resolver.js";
|
|
14
14
|
import { applyEvidenceGuardrail, buildAcePreflightPacket, buildBridgeTaskInput, buildContinuityRecord, buildStartupNudge, mapBridgeResultToRuntimeStatus, nextActivationLedger, } from "./local-model-contract.js";
|
|
15
15
|
import { withLocalModelRuntimeRepository, } from "../store/repositories/local-model-runtime-repository.js";
|
|
16
|
+
import { parseExecutionEngine, runLocalModelTask } from "../local-model-runtime.js";
|
|
16
17
|
export class ChatSession extends EventEmitter {
|
|
17
18
|
clients;
|
|
18
19
|
telemetry;
|
|
@@ -32,7 +33,10 @@ export class ChatSession extends EventEmitter {
|
|
|
32
33
|
aceRole;
|
|
33
34
|
aceTier;
|
|
34
35
|
maxTurns;
|
|
36
|
+
executionEngine;
|
|
35
37
|
aceBridge;
|
|
38
|
+
hermesExecutor;
|
|
39
|
+
hermesLaunchProfile;
|
|
36
40
|
activeAceBridge = false;
|
|
37
41
|
providerBaseUrls;
|
|
38
42
|
sessionId = randomUUID();
|
|
@@ -56,7 +60,10 @@ export class ChatSession extends EventEmitter {
|
|
|
56
60
|
this.aceRole = options.aceRole?.trim() || "orchestrator";
|
|
57
61
|
this.aceTier = options.aceTier;
|
|
58
62
|
this.maxTurns = options.maxTurns ?? 6;
|
|
63
|
+
this.executionEngine = parseExecutionEngine(options.engine) ?? "direct";
|
|
59
64
|
this.aceBridge = options.bridge ?? new ModelBridge(clients);
|
|
65
|
+
this.hermesExecutor = options.hermesExecutor;
|
|
66
|
+
this.hermesLaunchProfile = options.hermesLaunchProfile;
|
|
60
67
|
// Add system prompt if provided
|
|
61
68
|
if (this.systemPrompt) {
|
|
62
69
|
this.messages.push({ role: "system", content: this.systemPrompt });
|
|
@@ -349,6 +356,9 @@ export class ChatSession extends EventEmitter {
|
|
|
349
356
|
recommended_next_action: input.recommendedNextAction,
|
|
350
357
|
blocked_reason: input.blockedReason,
|
|
351
358
|
updated_at: Date.now(),
|
|
359
|
+
execution_engine: this.executionEngine,
|
|
360
|
+
underlying_provider: this.provider,
|
|
361
|
+
underlying_model: this.model,
|
|
352
362
|
};
|
|
353
363
|
}
|
|
354
364
|
setRuntimeStatus(status) {
|
|
@@ -536,6 +546,70 @@ export class ChatSession extends EventEmitter {
|
|
|
536
546
|
this.activationLedger = activationLedger;
|
|
537
547
|
return;
|
|
538
548
|
}
|
|
549
|
+
if (this.executionEngine === "hermes_local") {
|
|
550
|
+
const delegated = await runLocalModelTask({
|
|
551
|
+
task: buildBridgeTaskInput(recentConversation, preflight),
|
|
552
|
+
role: executionRole,
|
|
553
|
+
workspaceRoot: this.workspaceRoot,
|
|
554
|
+
provider: streamProvider,
|
|
555
|
+
model: streamModel,
|
|
556
|
+
baseUrl: this.providerBaseUrls[streamProvider],
|
|
557
|
+
ollamaUrl: this.providerBaseUrls.ollama,
|
|
558
|
+
engine: "hermes_local",
|
|
559
|
+
maxTurns: this.maxTurns,
|
|
560
|
+
tier: this.aceTier ?? (executionRole === "orchestrator" ? "compressed" : "brief"),
|
|
561
|
+
hermesExecutor: this.hermesExecutor,
|
|
562
|
+
hermesLaunchProfile: this.hermesLaunchProfile,
|
|
563
|
+
});
|
|
564
|
+
const assistantText = delegated.result.summary.trim() || "Hermes-local turn completed.";
|
|
565
|
+
this.messages.push({ role: "assistant", content: assistantText });
|
|
566
|
+
this.displayMessages.push({
|
|
567
|
+
role: "assistant",
|
|
568
|
+
content: assistantText,
|
|
569
|
+
timestamp: Date.now(),
|
|
570
|
+
tokens: estimateTokenCount(assistantText),
|
|
571
|
+
});
|
|
572
|
+
runtimeStatus = {
|
|
573
|
+
...runtimeStatus,
|
|
574
|
+
bridge_status: delegated.result.status === "completed"
|
|
575
|
+
? "done"
|
|
576
|
+
: delegated.result.status === "failed"
|
|
577
|
+
? "failed"
|
|
578
|
+
: delegated.result.status === "max_turns"
|
|
579
|
+
? "needs_input"
|
|
580
|
+
: delegated.result.status,
|
|
581
|
+
blocked_reason: delegated.result.status === "failed" ? assistantText : undefined,
|
|
582
|
+
hermes_bridge_protocol_version: delegated.hermes?.bridge_protocol_version,
|
|
583
|
+
hermes_session_id: delegated.hermes?.hermes_session_id,
|
|
584
|
+
shadow_mcp_session_id: delegated.hermes?.shadow_mcp_session_id,
|
|
585
|
+
updated_at: Date.now(),
|
|
586
|
+
};
|
|
587
|
+
this.setRuntimeStatus(runtimeStatus);
|
|
588
|
+
const continuity = {
|
|
589
|
+
...buildContinuityRecord({
|
|
590
|
+
preflight,
|
|
591
|
+
role: executionRole,
|
|
592
|
+
bridgeStatus: runtimeStatus.bridge_status,
|
|
593
|
+
evidenceRefs: delegated.result.evidence_refs ?? [],
|
|
594
|
+
}),
|
|
595
|
+
execution_engine: "hermes_local",
|
|
596
|
+
underlying_provider: streamProvider,
|
|
597
|
+
underlying_model: streamModel,
|
|
598
|
+
hermes_bridge_protocol_version: delegated.hermes?.bridge_protocol_version,
|
|
599
|
+
hermes_session_id: delegated.hermes?.hermes_session_id,
|
|
600
|
+
shadow_mcp_session_id: delegated.hermes?.shadow_mcp_session_id,
|
|
601
|
+
};
|
|
602
|
+
this.persistRuntimeSessionArtifacts({ activationLedger, runtimeStatus, continuity });
|
|
603
|
+
this.activationLedger = activationLedger;
|
|
604
|
+
this.telemetry.recordRequest({
|
|
605
|
+
startTime,
|
|
606
|
+
endTime: Date.now(),
|
|
607
|
+
promptTokens: estimateTokenCount(text),
|
|
608
|
+
completionTokens: estimateTokenCount(assistantText),
|
|
609
|
+
model: streamModel,
|
|
610
|
+
});
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
539
613
|
let bridgeOutput = "";
|
|
540
614
|
const toolNames = [];
|
|
541
615
|
let retryAttempt = 0;
|
package/dist/tui/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* This is the process entry when `ace tui` is invoked.
|
|
6
6
|
*/
|
|
7
7
|
import { type TuiController } from "./commands.js";
|
|
8
|
+
import type { HermesLaunchProfile } from "../hermes/launch-profile.js";
|
|
8
9
|
export declare class AceTui implements TuiController {
|
|
9
10
|
private input;
|
|
10
11
|
private layout;
|
|
@@ -18,6 +19,7 @@ export declare class AceTui implements TuiController {
|
|
|
18
19
|
private chatSessions;
|
|
19
20
|
private activeChatSession;
|
|
20
21
|
private provider;
|
|
22
|
+
private executionEngine;
|
|
21
23
|
private model;
|
|
22
24
|
private ollamaAvailable;
|
|
23
25
|
private providers;
|
|
@@ -32,6 +34,7 @@ export declare class AceTui implements TuiController {
|
|
|
32
34
|
private providerBaseUrls;
|
|
33
35
|
private latestRuntimeStatus;
|
|
34
36
|
private pendingCloseTabId;
|
|
37
|
+
private hermesLaunchProfile?;
|
|
35
38
|
constructor(options?: {
|
|
36
39
|
provider?: string;
|
|
37
40
|
model?: string;
|
|
@@ -40,6 +43,8 @@ export declare class AceTui implements TuiController {
|
|
|
40
43
|
baseUrl?: string;
|
|
41
44
|
ollamaUrl?: string;
|
|
42
45
|
providerBaseUrls?: Record<string, string>;
|
|
46
|
+
engine?: string;
|
|
47
|
+
hermesLaunchProfile?: HermesLaunchProfile;
|
|
43
48
|
workspaceRoot?: string;
|
|
44
49
|
systemPrompt?: string;
|
|
45
50
|
});
|
|
@@ -114,6 +119,8 @@ export declare function runTui(options?: {
|
|
|
114
119
|
baseUrl?: string;
|
|
115
120
|
ollamaUrl?: string;
|
|
116
121
|
providerBaseUrls?: Record<string, string>;
|
|
122
|
+
engine?: string;
|
|
123
|
+
hermesLaunchProfile?: HermesLaunchProfile;
|
|
117
124
|
workspaceRoot?: string;
|
|
118
125
|
}): Promise<void>;
|
|
119
126
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/tui/index.js
CHANGED
|
@@ -16,8 +16,9 @@ import { ChatSession } from "./chat.js";
|
|
|
16
16
|
import { OpenAICompatibleClient, diagnoseChatRuntimeConfig, } from "./openai-compatible.js";
|
|
17
17
|
import { detectColorLevel, write, cursor, screen, fg, style } from "./renderer.js";
|
|
18
18
|
import { ALL_AGENTS, WORKSPACE_ROOT } from "../helpers.js";
|
|
19
|
+
import { parseExecutionEngine } from "../local-model-runtime.js";
|
|
19
20
|
import { backfillHandoffsIntoScheduler } from "../tools-handoff.js";
|
|
20
|
-
import { DEFAULT_OLLAMA_MODEL, defaultModelForProvider, inferProviderFromModel, normalizeLocalBaseUrl, } from "./provider-discovery.js";
|
|
21
|
+
import { DEFAULT_OLLAMA_MODEL, defaultModelForProvider, inferProviderFromModel, normalizeLocalBaseUrl, scanLocalModelRuntimes, } from "./provider-discovery.js";
|
|
21
22
|
import { resolveAceStateLayout } from "../ace-state-resolver.js";
|
|
22
23
|
import { withLocalModelRuntimeRepository, } from "../store/repositories/local-model-runtime-repository.js";
|
|
23
24
|
const DASHBOARD_CONTROLS = ["provider", "model", "chat", "logs", "refresh"];
|
|
@@ -38,6 +39,7 @@ export class AceTui {
|
|
|
38
39
|
activeChatSession = null;
|
|
39
40
|
// State
|
|
40
41
|
provider;
|
|
42
|
+
executionEngine;
|
|
41
43
|
model;
|
|
42
44
|
ollamaAvailable = false;
|
|
43
45
|
providers = [];
|
|
@@ -52,10 +54,13 @@ export class AceTui {
|
|
|
52
54
|
providerBaseUrls = new Map();
|
|
53
55
|
latestRuntimeStatus = null;
|
|
54
56
|
pendingCloseTabId = null;
|
|
57
|
+
hermesLaunchProfile;
|
|
55
58
|
constructor(options = {}) {
|
|
56
59
|
const workspaceRoot = options.workspaceRoot ?? WORKSPACE_ROOT;
|
|
57
60
|
this.workspaceRoot = workspaceRoot;
|
|
61
|
+
this.hermesLaunchProfile = options.hermesLaunchProfile;
|
|
58
62
|
this.provider = this.normalizeProvider(options.provider ?? inferProviderFromModel(options.model) ?? "ollama") ?? "ollama";
|
|
63
|
+
this.executionEngine = parseExecutionEngine(options.engine) ?? "direct";
|
|
59
64
|
const initialModel = options.model ??
|
|
60
65
|
(this.provider === "ollama"
|
|
61
66
|
? defaultModelForProvider(this.provider)
|
|
@@ -92,6 +97,7 @@ export class AceTui {
|
|
|
92
97
|
this.config.set("ollama_url", this.providerBaseUrls.get("ollama"));
|
|
93
98
|
}
|
|
94
99
|
this.config.set("provider", this.provider);
|
|
100
|
+
this.config.set("engine", this.executionEngine);
|
|
95
101
|
this.config.set("model", this.model);
|
|
96
102
|
this.config.set("temperature", "0.7");
|
|
97
103
|
this.config.set("top_p", "0.9");
|
|
@@ -148,6 +154,31 @@ export class AceTui {
|
|
|
148
154
|
this.showMessage(`Provider '${this.provider}' loaded from session/config. Switch with /provider or dashboard controls.`, "info");
|
|
149
155
|
this.checkProviderRuntimeConfig(this.provider, this.model, "startup");
|
|
150
156
|
}
|
|
157
|
+
// Additionally probe local runtimes (llama.cpp / Ollama) and merge discoveries into TUI so users can explicitly pick either.
|
|
158
|
+
try {
|
|
159
|
+
const scan = await scanLocalModelRuntimes({ workspaceRoot: this.workspaceRoot, timeoutMs: 800 });
|
|
160
|
+
for (const c of scan.candidates) {
|
|
161
|
+
this.ensureProvider(c.provider);
|
|
162
|
+
this.setProviderBaseUrl(c.provider, c.baseUrl);
|
|
163
|
+
if (c.models && c.models.length > 0)
|
|
164
|
+
this.setProviderModels(c.provider, c.models, false);
|
|
165
|
+
}
|
|
166
|
+
// If the current model appears served by a discovered runtime and the provider wasn't explicitly forced, prefer the local runtime.
|
|
167
|
+
if (!process.env.ACE_TUI_PROVIDER) {
|
|
168
|
+
for (const c of scan.candidates) {
|
|
169
|
+
if (c.models.includes(this.model) && this.provider !== c.provider) {
|
|
170
|
+
this.provider = c.provider;
|
|
171
|
+
this.config.set("provider", this.provider);
|
|
172
|
+
this.telemetry.setModel(this.model);
|
|
173
|
+
this.showMessage(`Discovered local runtime '${c.provider}' serving model '${this.model}'. Provider set to '${this.provider}'.`, "info");
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
catch {
|
|
180
|
+
// Ignore scan failures — discovery is best-effort.
|
|
181
|
+
}
|
|
151
182
|
// Initial render
|
|
152
183
|
this.fullRender();
|
|
153
184
|
// Start 1-second tick for status bar updates
|
|
@@ -498,6 +529,7 @@ export class AceTui {
|
|
|
498
529
|
const state = {
|
|
499
530
|
taskState: this.getTaskState(),
|
|
500
531
|
provider: this.provider,
|
|
532
|
+
engine: this.executionEngine,
|
|
501
533
|
model: this.model,
|
|
502
534
|
tokensIn: snap.tokensIn + agentTokens.input,
|
|
503
535
|
tokensOut: snap.tokensOut + agentTokens.output,
|
|
@@ -928,6 +960,7 @@ export class AceTui {
|
|
|
928
960
|
// Create a chat session for this tab
|
|
929
961
|
const session = new ChatSession({ ollama: this.ollama, openai: this.openai }, this.telemetry, {
|
|
930
962
|
provider: this.provider,
|
|
963
|
+
engine: this.executionEngine,
|
|
931
964
|
model: this.model,
|
|
932
965
|
providerBaseUrls: Object.fromEntries(this.providerBaseUrls.entries()),
|
|
933
966
|
systemPrompt: this.config.get("system_prompt"),
|
|
@@ -935,6 +968,7 @@ export class AceTui {
|
|
|
935
968
|
topP: parseFloat(this.config.get("top_p") ?? "0.9"),
|
|
936
969
|
numCtx: parseInt(this.config.get("num_ctx") ?? "8192", 10),
|
|
937
970
|
workspaceRoot: this.workspaceRoot,
|
|
971
|
+
hermesLaunchProfile: this.hermesLaunchProfile,
|
|
938
972
|
});
|
|
939
973
|
// Wire chat session events
|
|
940
974
|
session.on("updated", () => {
|
package/dist/tui/layout.d.ts
CHANGED
package/dist/tui/layout.js
CHANGED
|
@@ -160,6 +160,9 @@ export class LayoutManager {
|
|
|
160
160
|
? `${fg.gray}tab: ${fg.white}${state.activeTabLabel}${style.reset}`
|
|
161
161
|
: undefined;
|
|
162
162
|
const providerSeg = `${fg.cyan}provider: ${fg.brightCyan}${state.provider}${style.reset}`;
|
|
163
|
+
const engineSeg = state.engine
|
|
164
|
+
? `${fg.cyan}engine: ${fg.brightCyan}${state.engine}${style.reset}`
|
|
165
|
+
: undefined;
|
|
163
166
|
const modelSeg = `${fg.cyan}model: ${fg.brightCyan}${state.model}${style.reset}`;
|
|
164
167
|
const runtimeSummary = formatRuntimeStatusSummary(state);
|
|
165
168
|
const runtimeSeg = runtimeSummary
|
|
@@ -175,7 +178,7 @@ export class LayoutManager {
|
|
|
175
178
|
: state.bridgeStatus
|
|
176
179
|
? `${fg.gray}${state.bridgeStatus}${style.reset}`
|
|
177
180
|
: undefined;
|
|
178
|
-
const segments = [stateIndicator, tabSeg, providerSeg, modelSeg, runtimeSeg, contextSeg, tokenSeg, timeSeg, clockSeg]
|
|
181
|
+
const segments = [stateIndicator, tabSeg, providerSeg, engineSeg, modelSeg, runtimeSeg, contextSeg, tokenSeg, timeSeg, clockSeg]
|
|
179
182
|
.filter((segment) => Boolean(segment));
|
|
180
183
|
const sep = ` ${fg.gray}│${style.reset} `;
|
|
181
184
|
const line = ` ${segments.join(sep)} `;
|
|
@@ -72,22 +72,19 @@ export function inferProviderFromModel(model) {
|
|
|
72
72
|
const value = model.trim().toLowerCase();
|
|
73
73
|
if (!value)
|
|
74
74
|
return undefined;
|
|
75
|
-
|
|
75
|
+
// explicit prefixes
|
|
76
|
+
if (value.startsWith("llama.cpp/") || value.startsWith("llamacpp/"))
|
|
76
77
|
return "llama.cpp";
|
|
77
|
-
|
|
78
|
-
if (value.startsWith("ollama/") ||
|
|
79
|
-
value.includes("llama") ||
|
|
80
|
-
value.includes("qwen") ||
|
|
81
|
-
value.includes("mistral") ||
|
|
82
|
-
value.includes("deepseek") ||
|
|
83
|
-
value.includes("phi") ||
|
|
84
|
-
value.includes(":") ||
|
|
85
|
-
value.includes("mixtral")) {
|
|
78
|
+
if (value.startsWith("ollama/"))
|
|
86
79
|
return "ollama";
|
|
87
|
-
|
|
88
|
-
if (value.startsWith("copilot/")) {
|
|
80
|
+
if (value.startsWith("copilot/"))
|
|
89
81
|
return "copilot";
|
|
90
|
-
|
|
82
|
+
// file-like or gguf artifacts -> prefer llama.cpp
|
|
83
|
+
if (value.endsWith(".gguf") || value.includes(".gguf"))
|
|
84
|
+
return "llama.cpp";
|
|
85
|
+
if (value.endsWith(".bin"))
|
|
86
|
+
return "llama.cpp";
|
|
87
|
+
// common hosted provider hints
|
|
91
88
|
if (value.includes("claude"))
|
|
92
89
|
return "claude";
|
|
93
90
|
if (value.includes("gemini"))
|
|
@@ -100,6 +97,11 @@ export function inferProviderFromModel(model) {
|
|
|
100
97
|
value.startsWith("o5")) {
|
|
101
98
|
return "codex";
|
|
102
99
|
}
|
|
100
|
+
// model families often served via Ollama
|
|
101
|
+
if (value.includes("qwen") || value.includes("mistral") || value.includes("mixtral") || value.includes("deepseek") || value.includes("phi")) {
|
|
102
|
+
return "ollama";
|
|
103
|
+
}
|
|
104
|
+
// Ambiguous cases (e.g., hf.co/owner/name:ref) should not default to Ollama; allow runtime discovery to decide.
|
|
103
105
|
return undefined;
|
|
104
106
|
}
|
|
105
107
|
export function isLocalLlmProvider(providerInput) {
|
|
@@ -14,6 +14,7 @@ export interface VericifyProcessPost {
|
|
|
14
14
|
agent_id: string;
|
|
15
15
|
kind: VericifyProcessPostKind;
|
|
16
16
|
summary: string;
|
|
17
|
+
blocker_category?: string;
|
|
17
18
|
tool_refs: string[];
|
|
18
19
|
evidence_refs: string[];
|
|
19
20
|
checkpoint_ref?: string;
|
|
@@ -109,6 +110,7 @@ export declare function appendVericifyProcessPost(input: {
|
|
|
109
110
|
agent_id: string;
|
|
110
111
|
kind: VericifyProcessPostKind;
|
|
111
112
|
summary: string;
|
|
113
|
+
blocker_category?: string;
|
|
112
114
|
tool_refs?: string[];
|
|
113
115
|
evidence_refs?: string[];
|
|
114
116
|
checkpoint_ref?: string;
|
|
@@ -124,6 +126,7 @@ export declare function appendVericifyProcessPostSafe(input: {
|
|
|
124
126
|
agent_id: string;
|
|
125
127
|
kind: VericifyProcessPostKind;
|
|
126
128
|
summary: string;
|
|
129
|
+
blocker_category?: string;
|
|
127
130
|
tool_refs?: string[];
|
|
128
131
|
evidence_refs?: string[];
|
|
129
132
|
checkpoint_ref?: string;
|
package/dist/vericify-bridge.js
CHANGED
|
@@ -50,6 +50,7 @@ function normalizePost(input) {
|
|
|
50
50
|
agent_id: input.agent_id.trim(),
|
|
51
51
|
kind: input.kind,
|
|
52
52
|
summary: input.summary.trim(),
|
|
53
|
+
blocker_category: input.blocker_category?.trim() || undefined,
|
|
53
54
|
tool_refs: [...new Set(input.tool_refs.map((entry) => entry.trim()).filter(Boolean))],
|
|
54
55
|
evidence_refs: [
|
|
55
56
|
...new Set(input.evidence_refs.map((entry) => entry.trim()).filter(Boolean)),
|
|
@@ -354,6 +355,7 @@ export async function appendVericifyProcessPost(input) {
|
|
|
354
355
|
agent_id: agentId,
|
|
355
356
|
kind: input.kind,
|
|
356
357
|
summary,
|
|
358
|
+
blocker_category: input.blocker_category,
|
|
357
359
|
tool_refs: input.tool_refs ?? [],
|
|
358
360
|
evidence_refs: input.evidence_refs ?? [],
|
|
359
361
|
checkpoint_ref: input.checkpoint_ref,
|
|
@@ -375,6 +377,7 @@ export async function appendVericifyProcessPost(input) {
|
|
|
375
377
|
metadata: {
|
|
376
378
|
branch_id: post.branch_id,
|
|
377
379
|
lane_id: post.lane_id,
|
|
380
|
+
blocker_category: post.blocker_category,
|
|
378
381
|
checkpoint_ref: post.checkpoint_ref,
|
|
379
382
|
tool_refs: post.tool_refs,
|
|
380
383
|
evidence_refs: post.evidence_refs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voybio/ace-swarm",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.2",
|
|
4
4
|
"description": "ACE Framework MCP server and CLI — single-file ACEPACK state, local-model serving, agent orchestration, and host compliance enforcement.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"files": [
|
|
22
22
|
"dist/**/*.js",
|
|
23
23
|
"dist/**/*.d.ts",
|
|
24
|
+
"scripts/hermes_bridge_worker.py",
|
|
24
25
|
"assets",
|
|
25
26
|
"README.md",
|
|
26
27
|
"CHANGELOG.md"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""ACE-owned Hermes bridge worker.
|
|
3
|
+
|
|
4
|
+
The worker reads one JSON request from stdin and writes newline-delimited
|
|
5
|
+
bridge events to stdout. All Hermes stdout chatter is redirected to stderr so
|
|
6
|
+
ACE never has to guess whether an unframed line is assistant text or authority.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import contextlib
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import traceback
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
ORIGINAL_STDOUT = sys.stdout
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def emit(event: dict[str, Any]) -> None:
|
|
24
|
+
ORIGINAL_STDOUT.write(json.dumps(event, ensure_ascii=False, separators=(",", ":")) + "\n")
|
|
25
|
+
ORIGINAL_STDOUT.flush()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def load_request() -> dict[str, Any]:
|
|
29
|
+
raw = sys.stdin.read()
|
|
30
|
+
if not raw.strip():
|
|
31
|
+
raise ValueError("empty bridge request")
|
|
32
|
+
parsed = json.loads(raw)
|
|
33
|
+
if not isinstance(parsed, dict):
|
|
34
|
+
raise ValueError("bridge request must be a JSON object")
|
|
35
|
+
return parsed
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def provider_for_hermes(provider: str) -> str:
|
|
39
|
+
if provider in {"llama.cpp", "ollama"}:
|
|
40
|
+
return "openai"
|
|
41
|
+
return provider
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def base_url_for_hermes(provider: str, base_url: Any) -> str:
|
|
45
|
+
raw = str(base_url or "").rstrip("/")
|
|
46
|
+
if provider == "ollama" and raw and not raw.endswith("/v1"):
|
|
47
|
+
return f"{raw}/v1"
|
|
48
|
+
return raw
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def main() -> int:
|
|
52
|
+
try:
|
|
53
|
+
request = load_request()
|
|
54
|
+
hermes_root = Path(request["hermes_root"]).resolve()
|
|
55
|
+
sys.path.insert(0, str(hermes_root))
|
|
56
|
+
|
|
57
|
+
session_id = str(request["session_id"])
|
|
58
|
+
turn_id = str(request.get("turn_id") or session_id)
|
|
59
|
+
emit({"type": "session_open", "session_id": session_id, "turn_id": turn_id})
|
|
60
|
+
|
|
61
|
+
with contextlib.redirect_stdout(sys.stderr):
|
|
62
|
+
from run_agent import AIAgent # type: ignore
|
|
63
|
+
|
|
64
|
+
def status_callback(kind: str, message: str) -> None:
|
|
65
|
+
emit({"type": "status" if kind == "lifecycle" else "status", "session_id": session_id, "turn_id": turn_id, "kind": kind, "message": str(message)})
|
|
66
|
+
|
|
67
|
+
def delta_callback(text: str) -> None:
|
|
68
|
+
emit({"type": "delta", "session_id": session_id, "turn_id": turn_id, "text": str(text)})
|
|
69
|
+
|
|
70
|
+
def reasoning_callback(text: str) -> None:
|
|
71
|
+
emit({"type": "reasoning", "session_id": session_id, "turn_id": turn_id, "text": str(text)})
|
|
72
|
+
|
|
73
|
+
def interim_callback(text: str, **kwargs: Any) -> None:
|
|
74
|
+
emit({"type": "assistant_interim", "session_id": session_id, "turn_id": turn_id, "text": str(text), **kwargs})
|
|
75
|
+
|
|
76
|
+
def thinking_callback(text: str) -> None:
|
|
77
|
+
emit({"type": "reasoning", "session_id": session_id, "turn_id": turn_id, "text": str(text)})
|
|
78
|
+
|
|
79
|
+
def tool_start_callback(tool_name: str, *args: Any, **kwargs: Any) -> None:
|
|
80
|
+
emit({"type": "tool_start", "session_id": session_id, "turn_id": turn_id, "tool": str(tool_name), "input": kwargs.get("args") or (args[0] if args else {})})
|
|
81
|
+
|
|
82
|
+
def tool_progress_callback(tool_name: str, message: str = "", *args: Any, **kwargs: Any) -> None:
|
|
83
|
+
emit({"type": "tool_progress", "session_id": session_id, "turn_id": turn_id, "tool": str(tool_name), "message": str(message), **kwargs})
|
|
84
|
+
|
|
85
|
+
def tool_complete_callback(tool_name: str, result: Any = None, *args: Any, **kwargs: Any) -> None:
|
|
86
|
+
emit({"type": "tool_complete", "session_id": session_id, "turn_id": turn_id, "tool": str(tool_name), "result": result, **kwargs})
|
|
87
|
+
|
|
88
|
+
agent = AIAgent(
|
|
89
|
+
base_url=base_url_for_hermes(str(request.get("provider") or ""), request.get("base_url")),
|
|
90
|
+
api_key=str(request.get("api_key") or "no-key-required"),
|
|
91
|
+
provider=provider_for_hermes(str(request.get("provider") or "")),
|
|
92
|
+
model=str(request.get("model") or ""),
|
|
93
|
+
max_iterations=int(request.get("max_turns") or 6),
|
|
94
|
+
enabled_toolsets=["mcp-ace_shadow"],
|
|
95
|
+
disabled_toolsets=["terminal", "filesystem", "web", "vision", "creative", "reasoning"],
|
|
96
|
+
quiet_mode=True,
|
|
97
|
+
verbose_logging=False,
|
|
98
|
+
ephemeral_system_prompt=str(request.get("system_prompt") or ""),
|
|
99
|
+
session_id=session_id,
|
|
100
|
+
platform="ace",
|
|
101
|
+
skip_context_files=True,
|
|
102
|
+
skip_memory=True,
|
|
103
|
+
status_callback=status_callback,
|
|
104
|
+
stream_delta_callback=delta_callback,
|
|
105
|
+
reasoning_callback=reasoning_callback,
|
|
106
|
+
thinking_callback=thinking_callback,
|
|
107
|
+
interim_assistant_callback=interim_callback,
|
|
108
|
+
tool_start_callback=tool_start_callback,
|
|
109
|
+
tool_progress_callback=tool_progress_callback,
|
|
110
|
+
tool_complete_callback=tool_complete_callback,
|
|
111
|
+
)
|
|
112
|
+
emit({"type": "turn_run", "session_id": session_id, "turn_id": turn_id})
|
|
113
|
+
result = agent.run_conversation(str(request.get("task") or ""), task_id=turn_id)
|
|
114
|
+
|
|
115
|
+
final = result.get("final_response") if isinstance(result, dict) else str(result)
|
|
116
|
+
emit({
|
|
117
|
+
"type": "final",
|
|
118
|
+
"session_id": session_id,
|
|
119
|
+
"turn_id": turn_id,
|
|
120
|
+
"text": final or "",
|
|
121
|
+
"completed": bool(result.get("completed", True)) if isinstance(result, dict) else True,
|
|
122
|
+
"turns": int(result.get("api_calls", 1) or 1) if isinstance(result, dict) else 1,
|
|
123
|
+
})
|
|
124
|
+
emit({"type": "session_close", "session_id": session_id, "turn_id": turn_id})
|
|
125
|
+
return 0
|
|
126
|
+
except Exception as exc:
|
|
127
|
+
emit({
|
|
128
|
+
"type": "error",
|
|
129
|
+
"message": str(exc),
|
|
130
|
+
"traceback": traceback.format_exc(limit=8),
|
|
131
|
+
})
|
|
132
|
+
return 1
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
if __name__ == "__main__":
|
|
136
|
+
raise SystemExit(main())
|