@voybio/ace-swarm 2.4.0 → 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 +16 -0
- package/README.md +502 -56
- package/assets/.agents/ACE/agent-qa/instructions.md +11 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +43 -0
- 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/dist/ace-context.js +70 -11
- package/dist/ace-internal-tools.d.ts +3 -1
- package/dist/ace-internal-tools.js +10 -2
- 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 +48 -0
- package/dist/astgrep-index.js +126 -1
- package/dist/cli.js +487 -17
- package/dist/discovery-runtime-wrappers.d.ts +108 -0
- package/dist/discovery-runtime-wrappers.js +615 -0
- package/dist/helpers/bootstrap.js +1 -1
- package/dist/helpers/constants.d.ts +4 -2
- package/dist/helpers/constants.js +8 -0
- package/dist/helpers/path-utils.d.ts +8 -1
- package/dist/helpers/path-utils.js +27 -8
- package/dist/helpers/store-resolution.js +7 -3
- 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/job-scheduler.js +30 -4
- 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 +17 -0
- package/dist/local-model-runtime.js +77 -20
- package/dist/model-bridge.d.ts +6 -1
- package/dist/model-bridge.js +338 -21
- package/dist/orchestrator-supervisor.d.ts +42 -0
- package/dist/orchestrator-supervisor.js +110 -3
- package/dist/plan-proposal.d.ts +115 -0
- package/dist/plan-proposal.js +1073 -0
- package/dist/runtime-executor.d.ts +6 -1
- package/dist/runtime-executor.js +72 -5
- package/dist/runtime-tool-specs.d.ts +19 -1
- package/dist/runtime-tool-specs.js +67 -26
- package/dist/schemas.js +30 -1
- package/dist/server.d.ts +3 -0
- package/dist/server.js +73 -4
- package/dist/shared.d.ts +1 -0
- package/dist/shared.js +2 -0
- package/dist/store/bootstrap-store.d.ts +1 -0
- package/dist/store/bootstrap-store.js +8 -2
- package/dist/store/materializers/vericify-projector.js +3 -0
- package/dist/store/repositories/local-model-runtime-repository.d.ts +13 -1
- package/dist/store/repositories/local-model-runtime-repository.js +4 -1
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +544 -29
- package/dist/tools-discovery.js +135 -0
- package/dist/tools-files.js +768 -66
- package/dist/tools-framework.js +80 -61
- 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 +45 -2
- package/dist/tui/layout.d.ts +1 -0
- package/dist/tui/layout.js +4 -1
- 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 +50 -24
- package/dist/vericify-bridge.d.ts +4 -1
- package/dist/vericify-bridge.js +3 -0
- package/package.json +2 -1
- package/scripts/hermes_bridge_worker.py +136 -0
package/dist/tools-framework.js
CHANGED
|
@@ -21,6 +21,7 @@ import { auditStoreAuthority, writeStoreAuthorityAuditReport, } from "./store/st
|
|
|
21
21
|
import { PROVENANCE_CRITICAL_EVENT_TYPES, validateArtifactManifestPayload, validateProvenanceLogContent, validateTealConfigContent, } from "./schemas.js";
|
|
22
22
|
import { readAceTaskContractAssessment } from "./ace-autonomy.js";
|
|
23
23
|
import { listStoreKeysSync, readStoreBlobSync } from "./store/store-snapshot.js";
|
|
24
|
+
import { parseJsonLikeText } from "./json-sanitizer.js";
|
|
24
25
|
function executionRoleForDomain(domain) {
|
|
25
26
|
switch (domain) {
|
|
26
27
|
case "venture":
|
|
@@ -75,6 +76,14 @@ function parseGateManifest(raw, sourceRef) {
|
|
|
75
76
|
return undefined;
|
|
76
77
|
}
|
|
77
78
|
}
|
|
79
|
+
function parseJsonForValidation(raw) {
|
|
80
|
+
return parseJsonLikeText(raw);
|
|
81
|
+
}
|
|
82
|
+
function jsonValidationDetail(parsed, validDetail = "valid JSON") {
|
|
83
|
+
return parsed.sanitized.removed_control_bytes > 0
|
|
84
|
+
? `control bytes removed before parse (${parsed.sanitized.removed_control_bytes}); source requires repair`
|
|
85
|
+
: validDetail;
|
|
86
|
+
}
|
|
78
87
|
function readGateManifests(gatesDir) {
|
|
79
88
|
const files = readdirSync(gatesDir).filter((f) => f.endsWith(".json"));
|
|
80
89
|
const allGates = [];
|
|
@@ -737,7 +746,10 @@ export function registerFrameworkTools(server) {
|
|
|
737
746
|
"",
|
|
738
747
|
"## LLM Runtime Profile",
|
|
739
748
|
`- provider: ${llm_provider}`,
|
|
740
|
-
`- model: ${resolvedLlmModel ??
|
|
749
|
+
`- model: ${resolvedLlmModel ??
|
|
750
|
+
(llm_provider === "llama.cpp"
|
|
751
|
+
? "(set via ace connect or ace doctor --scan)"
|
|
752
|
+
: defaultModelForProvider(llm_provider))}`,
|
|
741
753
|
`- base_url: ${resolvedLlmBaseUrl ??
|
|
742
754
|
(llm_provider === "ollama" || llm_provider === "llama.cpp"
|
|
743
755
|
? "discover via ace doctor --scan or set explicitly"
|
|
@@ -1010,58 +1022,60 @@ export function registerFrameworkTools(server) {
|
|
|
1010
1022
|
// Check: handoff registry parseable
|
|
1011
1023
|
const hrRaw = safeRead("agent-state/handoff-registry.json");
|
|
1012
1024
|
const hrOk = !hrRaw.startsWith("[FILE NOT FOUND]");
|
|
1025
|
+
let hrParsed;
|
|
1013
1026
|
if (hrOk) {
|
|
1014
|
-
|
|
1015
|
-
|
|
1027
|
+
const parsed = parseJsonForValidation(hrRaw);
|
|
1028
|
+
if (parsed.ok) {
|
|
1029
|
+
hrParsed = parsed.value;
|
|
1016
1030
|
checks.push({
|
|
1017
1031
|
name: "handoff-registry:parse",
|
|
1018
|
-
ok:
|
|
1019
|
-
detail:
|
|
1032
|
+
ok: parsed.sanitized.removed_control_bytes === 0,
|
|
1033
|
+
detail: jsonValidationDetail(parsed),
|
|
1020
1034
|
});
|
|
1021
1035
|
}
|
|
1022
|
-
|
|
1036
|
+
else {
|
|
1023
1037
|
checks.push({
|
|
1024
1038
|
name: "handoff-registry:parse",
|
|
1025
1039
|
ok: false,
|
|
1026
|
-
detail:
|
|
1040
|
+
detail: `agent-state/handoff-registry.json: ${parsed.error}`,
|
|
1027
1041
|
});
|
|
1028
1042
|
}
|
|
1029
1043
|
}
|
|
1030
1044
|
// Check: run-ledger parseable
|
|
1031
1045
|
const rlRaw = safeRead("agent-state/run-ledger.json");
|
|
1032
1046
|
if (!rlRaw.startsWith("[FILE NOT FOUND]")) {
|
|
1033
|
-
|
|
1034
|
-
|
|
1047
|
+
const parsed = parseJsonForValidation(rlRaw);
|
|
1048
|
+
if (parsed.ok) {
|
|
1035
1049
|
checks.push({
|
|
1036
1050
|
name: "run-ledger:parse",
|
|
1037
|
-
ok:
|
|
1038
|
-
detail:
|
|
1051
|
+
ok: parsed.sanitized.removed_control_bytes === 0,
|
|
1052
|
+
detail: jsonValidationDetail(parsed),
|
|
1039
1053
|
});
|
|
1040
1054
|
}
|
|
1041
|
-
|
|
1055
|
+
else {
|
|
1042
1056
|
checks.push({
|
|
1043
1057
|
name: "run-ledger:parse",
|
|
1044
1058
|
ok: false,
|
|
1045
|
-
detail:
|
|
1059
|
+
detail: `agent-state/run-ledger.json: ${parsed.error}`,
|
|
1046
1060
|
});
|
|
1047
1061
|
}
|
|
1048
1062
|
}
|
|
1049
1063
|
// Check: todo-state parseable
|
|
1050
1064
|
const tsRaw = safeRead("agent-state/todo-state.json");
|
|
1051
1065
|
if (!tsRaw.startsWith("[FILE NOT FOUND]")) {
|
|
1052
|
-
|
|
1053
|
-
|
|
1066
|
+
const parsed = parseJsonForValidation(tsRaw);
|
|
1067
|
+
if (parsed.ok) {
|
|
1054
1068
|
checks.push({
|
|
1055
1069
|
name: "todo-state:parse",
|
|
1056
|
-
ok:
|
|
1057
|
-
detail:
|
|
1070
|
+
ok: parsed.sanitized.removed_control_bytes === 0,
|
|
1071
|
+
detail: jsonValidationDetail(parsed),
|
|
1058
1072
|
});
|
|
1059
1073
|
}
|
|
1060
|
-
|
|
1074
|
+
else {
|
|
1061
1075
|
checks.push({
|
|
1062
1076
|
name: "todo-state:parse",
|
|
1063
1077
|
ok: false,
|
|
1064
|
-
detail:
|
|
1078
|
+
detail: `agent-state/todo-state.json: ${parsed.error}`,
|
|
1065
1079
|
});
|
|
1066
1080
|
}
|
|
1067
1081
|
}
|
|
@@ -1081,19 +1095,19 @@ export function registerFrameworkTools(server) {
|
|
|
1081
1095
|
});
|
|
1082
1096
|
continue;
|
|
1083
1097
|
}
|
|
1084
|
-
|
|
1085
|
-
|
|
1098
|
+
const parsed = parseJsonForValidation(raw);
|
|
1099
|
+
if (parsed.ok) {
|
|
1086
1100
|
checks.push({
|
|
1087
1101
|
name: check.name,
|
|
1088
|
-
ok:
|
|
1089
|
-
detail:
|
|
1102
|
+
ok: parsed.sanitized.removed_control_bytes === 0,
|
|
1103
|
+
detail: jsonValidationDetail(parsed),
|
|
1090
1104
|
});
|
|
1091
1105
|
}
|
|
1092
|
-
|
|
1106
|
+
else {
|
|
1093
1107
|
checks.push({
|
|
1094
1108
|
name: check.name,
|
|
1095
1109
|
ok: false,
|
|
1096
|
-
detail:
|
|
1110
|
+
detail: `${check.rel}: ${parsed.error}`,
|
|
1097
1111
|
});
|
|
1098
1112
|
}
|
|
1099
1113
|
}
|
|
@@ -1147,25 +1161,28 @@ export function registerFrameworkTools(server) {
|
|
|
1147
1161
|
const artifactManifestRaw = safeRead("agent-state/ARTIFACT_MANIFEST.json");
|
|
1148
1162
|
let artifactManifestEntries = [];
|
|
1149
1163
|
if (!artifactManifestRaw.startsWith("[FILE NOT FOUND]")) {
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
const validation = validateArtifactManifestPayload(parsed);
|
|
1164
|
+
const parsed = parseJsonForValidation(artifactManifestRaw);
|
|
1165
|
+
if (parsed.ok) {
|
|
1166
|
+
const validation = validateArtifactManifestPayload(parsed.value);
|
|
1167
|
+
const controlBytesOk = parsed.sanitized.removed_control_bytes === 0;
|
|
1153
1168
|
checks.push({
|
|
1154
1169
|
name: "artifact-manifest:schema",
|
|
1155
|
-
ok: validation.ok,
|
|
1156
|
-
detail:
|
|
1157
|
-
?
|
|
1158
|
-
: validation.
|
|
1170
|
+
ok: validation.ok && controlBytesOk,
|
|
1171
|
+
detail: !controlBytesOk
|
|
1172
|
+
? jsonValidationDetail(parsed)
|
|
1173
|
+
: validation.ok
|
|
1174
|
+
? `valid (${validation.schema})`
|
|
1175
|
+
: validation.errors.join("; "),
|
|
1159
1176
|
});
|
|
1160
1177
|
if (validation.ok) {
|
|
1161
|
-
artifactManifestEntries = getArtifactManifestEntries(parsed);
|
|
1178
|
+
artifactManifestEntries = getArtifactManifestEntries(parsed.value);
|
|
1162
1179
|
}
|
|
1163
1180
|
}
|
|
1164
|
-
|
|
1181
|
+
else {
|
|
1165
1182
|
checks.push({
|
|
1166
1183
|
name: "artifact-manifest:parse",
|
|
1167
1184
|
ok: false,
|
|
1168
|
-
detail:
|
|
1185
|
+
detail: `agent-state/ARTIFACT_MANIFEST.json: ${parsed.error}`,
|
|
1169
1186
|
});
|
|
1170
1187
|
}
|
|
1171
1188
|
}
|
|
@@ -1203,8 +1220,16 @@ export function registerFrameworkTools(server) {
|
|
|
1203
1220
|
// Check: module registry role coverage vs event emitters
|
|
1204
1221
|
const registryRaw = safeRead("agent-state/MODULES/registry.json");
|
|
1205
1222
|
if (!registryRaw.startsWith("[FILE NOT FOUND]")) {
|
|
1206
|
-
|
|
1207
|
-
|
|
1223
|
+
const parsed = parseJsonForValidation(registryRaw);
|
|
1224
|
+
if (parsed.ok) {
|
|
1225
|
+
if (parsed.sanitized.removed_control_bytes > 0) {
|
|
1226
|
+
checks.push({
|
|
1227
|
+
name: "registry:parse",
|
|
1228
|
+
ok: false,
|
|
1229
|
+
detail: jsonValidationDetail(parsed),
|
|
1230
|
+
});
|
|
1231
|
+
}
|
|
1232
|
+
const registry = parsed.value;
|
|
1208
1233
|
const registeredRoles = new Set(registry.roles ?? []);
|
|
1209
1234
|
const expectedEmitters = [
|
|
1210
1235
|
"capability-safety",
|
|
@@ -1223,11 +1248,11 @@ export function registerFrameworkTools(server) {
|
|
|
1223
1248
|
});
|
|
1224
1249
|
}
|
|
1225
1250
|
}
|
|
1226
|
-
|
|
1251
|
+
else {
|
|
1227
1252
|
checks.push({
|
|
1228
1253
|
name: "registry:parse",
|
|
1229
1254
|
ok: false,
|
|
1230
|
-
detail:
|
|
1255
|
+
detail: `agent-state/MODULES/registry.json: ${parsed.error}`,
|
|
1231
1256
|
});
|
|
1232
1257
|
}
|
|
1233
1258
|
}
|
|
@@ -1248,30 +1273,24 @@ export function registerFrameworkTools(server) {
|
|
|
1248
1273
|
});
|
|
1249
1274
|
}
|
|
1250
1275
|
// Check: handoff consistency (open handoffs have source files)
|
|
1251
|
-
if (hrOk) {
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
if (fileContent.startsWith("[FILE NOT FOUND]"))
|
|
1261
|
-
openWithoutFile++;
|
|
1262
|
-
}
|
|
1276
|
+
if (hrOk && hrParsed) {
|
|
1277
|
+
const handoffs = Object.values(hrParsed.handoffs ?? {});
|
|
1278
|
+
let openWithoutFile = 0;
|
|
1279
|
+
for (const h of handoffs) {
|
|
1280
|
+
if ((h.status === "open" || h.status === "accepted") &&
|
|
1281
|
+
h.source_file) {
|
|
1282
|
+
const fileContent = safeRead(h.source_file);
|
|
1283
|
+
if (fileContent.startsWith("[FILE NOT FOUND]"))
|
|
1284
|
+
openWithoutFile++;
|
|
1263
1285
|
}
|
|
1264
|
-
checks.push({
|
|
1265
|
-
name: "handoff:source-file-integrity",
|
|
1266
|
-
ok: openWithoutFile === 0,
|
|
1267
|
-
detail: openWithoutFile === 0
|
|
1268
|
-
? "all open handoffs have source files"
|
|
1269
|
-
: `${openWithoutFile} open handoffs reference missing source files`,
|
|
1270
|
-
});
|
|
1271
|
-
}
|
|
1272
|
-
catch {
|
|
1273
|
-
/* already caught above */
|
|
1274
1286
|
}
|
|
1287
|
+
checks.push({
|
|
1288
|
+
name: "handoff:source-file-integrity",
|
|
1289
|
+
ok: openWithoutFile === 0,
|
|
1290
|
+
detail: openWithoutFile === 0
|
|
1291
|
+
? "all open handoffs have source files"
|
|
1292
|
+
: `${openWithoutFile} open handoffs reference missing source files`,
|
|
1293
|
+
});
|
|
1275
1294
|
}
|
|
1276
1295
|
// Check: event provenance gaps in recent events.
|
|
1277
1296
|
// Scans for provenance-critical event types that are missing
|
package/dist/tools.d.ts
CHANGED
|
@@ -5,5 +5,8 @@
|
|
|
5
5
|
* This keeps the registration entry-point small and each domain testable.
|
|
6
6
|
*/
|
|
7
7
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
8
|
-
export
|
|
8
|
+
export interface RegisterToolsOptions {
|
|
9
|
+
toolAllowlist?: readonly string[];
|
|
10
|
+
}
|
|
11
|
+
export declare function registerTools(server: McpServer, options?: RegisterToolsOptions): void;
|
|
9
12
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.js
CHANGED
|
@@ -16,18 +16,40 @@ import { registerMemoryTools } from "./tools-memory.js";
|
|
|
16
16
|
import { registerDriftTools } from "./tools-drift.js";
|
|
17
17
|
import { registerSchedulerTools } from "./tools-scheduler.js";
|
|
18
18
|
import { registerSkillTools } from "./tools-skills.js";
|
|
19
|
-
|
|
20
|
-
registerAgentTools
|
|
21
|
-
registerHandoffTools
|
|
22
|
-
registerTodoTools
|
|
23
|
-
registerDiscoveryTools
|
|
24
|
-
registerLifecycleTools
|
|
25
|
-
registerFileTools
|
|
26
|
-
registerFrameworkTools
|
|
27
|
-
registerGitTools
|
|
28
|
-
registerMemoryTools
|
|
29
|
-
registerDriftTools
|
|
30
|
-
registerSchedulerTools
|
|
31
|
-
registerSkillTools
|
|
19
|
+
const TOOL_REGISTRARS = [
|
|
20
|
+
registerAgentTools,
|
|
21
|
+
registerHandoffTools,
|
|
22
|
+
registerTodoTools,
|
|
23
|
+
registerDiscoveryTools,
|
|
24
|
+
registerLifecycleTools,
|
|
25
|
+
registerFileTools,
|
|
26
|
+
registerFrameworkTools,
|
|
27
|
+
registerGitTools,
|
|
28
|
+
registerMemoryTools,
|
|
29
|
+
registerDriftTools,
|
|
30
|
+
registerSchedulerTools,
|
|
31
|
+
registerSkillTools,
|
|
32
|
+
];
|
|
33
|
+
function createAllowlistedServer(server, allowed) {
|
|
34
|
+
return new Proxy(server, {
|
|
35
|
+
get(target, property, receiver) {
|
|
36
|
+
if (property !== "tool")
|
|
37
|
+
return Reflect.get(target, property, receiver);
|
|
38
|
+
const registerTool = Reflect.get(target, property, receiver);
|
|
39
|
+
return (name, ...args) => {
|
|
40
|
+
if (!allowed.has(name))
|
|
41
|
+
return undefined;
|
|
42
|
+
return Reflect.apply(registerTool, target, [name, ...args]);
|
|
43
|
+
};
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
export function registerTools(server, options = {}) {
|
|
48
|
+
const target = options.toolAllowlist === undefined
|
|
49
|
+
? server
|
|
50
|
+
: createAllowlistedServer(server, new Set(options.toolAllowlist));
|
|
51
|
+
for (const register of TOOL_REGISTRARS) {
|
|
52
|
+
register(target);
|
|
53
|
+
}
|
|
32
54
|
}
|
|
33
55
|
//# sourceMappingURL=tools.js.map
|
package/dist/tui/chat.d.ts
CHANGED
|
@@ -12,6 +12,8 @@ import type { ChatMessage } from "./layout.js";
|
|
|
12
12
|
import { ModelBridge } from "../model-bridge.js";
|
|
13
13
|
import type { AceContextTier } from "../ace-context.js";
|
|
14
14
|
import { type AceRuntimeStatusPacket } from "../store/repositories/local-model-runtime-repository.js";
|
|
15
|
+
import type { HermesLocalExecutor } from "../hermes/session-manager.js";
|
|
16
|
+
import type { HermesLaunchProfile } from "../hermes/launch-profile.js";
|
|
15
17
|
export interface ChatSessionOptions {
|
|
16
18
|
provider: string;
|
|
17
19
|
model: string;
|
|
@@ -25,7 +27,10 @@ export interface ChatSessionOptions {
|
|
|
25
27
|
aceRole?: string;
|
|
26
28
|
aceTier?: AceContextTier;
|
|
27
29
|
maxTurns?: number;
|
|
30
|
+
engine?: string;
|
|
28
31
|
bridge?: Pick<ModelBridge, "run" | "interrupt">;
|
|
32
|
+
hermesExecutor?: HermesLocalExecutor;
|
|
33
|
+
hermesLaunchProfile?: HermesLaunchProfile;
|
|
29
34
|
}
|
|
30
35
|
export interface ChatSessionClients {
|
|
31
36
|
ollama: Pick<OllamaClient, "chat" | "abort">;
|
|
@@ -51,7 +56,10 @@ export declare class ChatSession extends EventEmitter {
|
|
|
51
56
|
private aceRole;
|
|
52
57
|
private aceTier?;
|
|
53
58
|
private maxTurns;
|
|
59
|
+
private executionEngine;
|
|
54
60
|
private aceBridge;
|
|
61
|
+
private hermesExecutor?;
|
|
62
|
+
private hermesLaunchProfile?;
|
|
55
63
|
private activeAceBridge;
|
|
56
64
|
private providerBaseUrls;
|
|
57
65
|
private sessionId;
|
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,11 +54,20 @@ 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";
|
|
59
|
-
this.
|
|
63
|
+
this.executionEngine = parseExecutionEngine(options.engine) ?? "direct";
|
|
64
|
+
const initialModel = options.model ??
|
|
65
|
+
(this.provider === "ollama"
|
|
66
|
+
? defaultModelForProvider(this.provider)
|
|
67
|
+
: this.provider === "llama.cpp"
|
|
68
|
+
? ""
|
|
69
|
+
: defaultModelForProvider(this.provider));
|
|
70
|
+
this.model = initialModel.trim();
|
|
60
71
|
// Initialize modules
|
|
61
72
|
const colorLevel = detectColorLevel();
|
|
62
73
|
for (const [provider, baseUrl] of Object.entries(options.providerBaseUrls ?? {})) {
|
|
@@ -86,6 +97,7 @@ export class AceTui {
|
|
|
86
97
|
this.config.set("ollama_url", this.providerBaseUrls.get("ollama"));
|
|
87
98
|
}
|
|
88
99
|
this.config.set("provider", this.provider);
|
|
100
|
+
this.config.set("engine", this.executionEngine);
|
|
89
101
|
this.config.set("model", this.model);
|
|
90
102
|
this.config.set("temperature", "0.7");
|
|
91
103
|
this.config.set("top_p", "0.9");
|
|
@@ -142,6 +154,31 @@ export class AceTui {
|
|
|
142
154
|
this.showMessage(`Provider '${this.provider}' loaded from session/config. Switch with /provider or dashboard controls.`, "info");
|
|
143
155
|
this.checkProviderRuntimeConfig(this.provider, this.model, "startup");
|
|
144
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
|
+
}
|
|
145
182
|
// Initial render
|
|
146
183
|
this.fullRender();
|
|
147
184
|
// Start 1-second tick for status bar updates
|
|
@@ -492,6 +529,7 @@ export class AceTui {
|
|
|
492
529
|
const state = {
|
|
493
530
|
taskState: this.getTaskState(),
|
|
494
531
|
provider: this.provider,
|
|
532
|
+
engine: this.executionEngine,
|
|
495
533
|
model: this.model,
|
|
496
534
|
tokensIn: snap.tokensIn + agentTokens.input,
|
|
497
535
|
tokensOut: snap.tokensOut + agentTokens.output,
|
|
@@ -757,6 +795,9 @@ export class AceTui {
|
|
|
757
795
|
else if (this.provider === "ollama") {
|
|
758
796
|
this.model = DEFAULT_OLLAMA_MODEL;
|
|
759
797
|
}
|
|
798
|
+
else if (this.provider === "llama.cpp") {
|
|
799
|
+
this.model = "";
|
|
800
|
+
}
|
|
760
801
|
else {
|
|
761
802
|
this.model = defaultModelForProvider(this.provider);
|
|
762
803
|
}
|
|
@@ -919,6 +960,7 @@ export class AceTui {
|
|
|
919
960
|
// Create a chat session for this tab
|
|
920
961
|
const session = new ChatSession({ ollama: this.ollama, openai: this.openai }, this.telemetry, {
|
|
921
962
|
provider: this.provider,
|
|
963
|
+
engine: this.executionEngine,
|
|
922
964
|
model: this.model,
|
|
923
965
|
providerBaseUrls: Object.fromEntries(this.providerBaseUrls.entries()),
|
|
924
966
|
systemPrompt: this.config.get("system_prompt"),
|
|
@@ -926,6 +968,7 @@ export class AceTui {
|
|
|
926
968
|
topP: parseFloat(this.config.get("top_p") ?? "0.9"),
|
|
927
969
|
numCtx: parseInt(this.config.get("num_ctx") ?? "8192", 10),
|
|
928
970
|
workspaceRoot: this.workspaceRoot,
|
|
971
|
+
hermesLaunchProfile: this.hermesLaunchProfile,
|
|
929
972
|
});
|
|
930
973
|
// Wire chat session events
|
|
931
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)} `;
|
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
|