@voybio/ace-swarm 2.4.0 → 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 +8 -0
- package/README.md +1 -0
- 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 +205 -15
- 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 +2 -2
- package/dist/helpers/constants.js +7 -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/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 +6 -0
- package/dist/local-model-runtime.js +21 -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 +29 -1
- package/dist/server.js +51 -0
- 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/repositories/local-model-runtime-repository.d.ts +1 -1
- package/dist/store/repositories/local-model-runtime-repository.js +1 -1
- package/dist/store/repositories/vericify-repository.d.ts +1 -1
- package/dist/tools-agent.d.ts +20 -0
- package/dist/tools-agent.js +538 -28
- package/dist/tools-discovery.js +135 -0
- package/dist/tools-files.js +768 -66
- package/dist/tools-framework.js +80 -61
- package/dist/tui/index.js +10 -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 +35 -11
- package/dist/vericify-bridge.d.ts +1 -1
- package/package.json +1 -1
|
@@ -5,7 +5,104 @@
|
|
|
5
5
|
* - codex: defaults to OpenAI API endpoint/key env vars
|
|
6
6
|
* - others: require provider-specific base URL + API key env vars
|
|
7
7
|
*/
|
|
8
|
+
import { appendFileSync } from "node:fs";
|
|
8
9
|
import { buildOpenAiCompatibleBaseUrl } from "./provider-discovery.js";
|
|
10
|
+
function sanitizeTransportText(input) {
|
|
11
|
+
return input
|
|
12
|
+
.replace(/^\uFEFF/, "")
|
|
13
|
+
.replace(/\r\n?/g, "\n")
|
|
14
|
+
.replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, "");
|
|
15
|
+
}
|
|
16
|
+
function sampleBytes(bytes, decoded) {
|
|
17
|
+
const buffer = Buffer.from(bytes);
|
|
18
|
+
return {
|
|
19
|
+
sample_hex: buffer.toString("hex").slice(0, 512),
|
|
20
|
+
sample_text: sanitizeTransportText(decoded).slice(0, 512),
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
function captureRawBytes(label, bytes) {
|
|
24
|
+
const target = process.env.ACE_RAW_RESPONSE_LOG?.trim();
|
|
25
|
+
if (!target)
|
|
26
|
+
return;
|
|
27
|
+
const decoded = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
28
|
+
const sample = sampleBytes(bytes, decoded);
|
|
29
|
+
try {
|
|
30
|
+
appendFileSync(target, `${JSON.stringify({
|
|
31
|
+
at: new Date().toISOString(),
|
|
32
|
+
label,
|
|
33
|
+
byte_length: bytes.byteLength,
|
|
34
|
+
...sample,
|
|
35
|
+
})}\n`, "utf8");
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Raw capture is opt-in diagnostics; never fail the provider call because logging failed.
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function readResponseText(response, label) {
|
|
42
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
43
|
+
captureRawBytes(label, bytes);
|
|
44
|
+
const decoded = new TextDecoder("utf-8", { fatal: false }).decode(bytes);
|
|
45
|
+
const text = sanitizeTransportText(decoded);
|
|
46
|
+
const sample = sampleBytes(bytes, decoded);
|
|
47
|
+
return {
|
|
48
|
+
text,
|
|
49
|
+
byteLength: bytes.byteLength,
|
|
50
|
+
...sample,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function sanitizeChatMessages(messages) {
|
|
54
|
+
return (Array.isArray(messages) ? messages : [])
|
|
55
|
+
.map((msg) => {
|
|
56
|
+
const role = msg?.role === "system" || msg?.role === "assistant" || msg?.role === "user"
|
|
57
|
+
? msg.role
|
|
58
|
+
: "user";
|
|
59
|
+
const content = typeof msg?.content === "string"
|
|
60
|
+
? sanitizeTransportText(msg.content)
|
|
61
|
+
: sanitizeTransportText(String(msg?.content ?? ""));
|
|
62
|
+
return { role, content };
|
|
63
|
+
})
|
|
64
|
+
.filter((msg) => msg.role === "assistant" || msg.content.trim().length > 0);
|
|
65
|
+
}
|
|
66
|
+
function extractChoiceText(payload) {
|
|
67
|
+
if (!payload || typeof payload !== "object")
|
|
68
|
+
return "";
|
|
69
|
+
const choices = Array.isArray(payload.choices)
|
|
70
|
+
? (payload.choices ?? [])
|
|
71
|
+
: [];
|
|
72
|
+
const firstChoice = choices[0];
|
|
73
|
+
if (!firstChoice || typeof firstChoice !== "object")
|
|
74
|
+
return "";
|
|
75
|
+
const choice = firstChoice;
|
|
76
|
+
if (typeof choice.message?.content === "string")
|
|
77
|
+
return sanitizeTransportText(choice.message.content);
|
|
78
|
+
if (typeof choice.text === "string")
|
|
79
|
+
return sanitizeTransportText(choice.text);
|
|
80
|
+
if (typeof choice.delta?.content === "string")
|
|
81
|
+
return sanitizeTransportText(choice.delta.content);
|
|
82
|
+
return "";
|
|
83
|
+
}
|
|
84
|
+
function extractProviderBodyText(bodyText) {
|
|
85
|
+
const trimmed = sanitizeTransportText(bodyText).trim();
|
|
86
|
+
if (!trimmed)
|
|
87
|
+
return { text: "", parsedJson: false };
|
|
88
|
+
try {
|
|
89
|
+
const payload = JSON.parse(trimmed);
|
|
90
|
+
const extracted = extractChoiceText(payload);
|
|
91
|
+
return {
|
|
92
|
+
text: extracted || trimmed,
|
|
93
|
+
parsedJson: true,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return {
|
|
98
|
+
text: trimmed,
|
|
99
|
+
parsedJson: false,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function formatErrorMessage(error) {
|
|
104
|
+
return error instanceof Error ? error.message : String(error);
|
|
105
|
+
}
|
|
9
106
|
export class OpenAICompatibleError extends Error {
|
|
10
107
|
statusCode;
|
|
11
108
|
responseBody;
|
|
@@ -46,14 +143,216 @@ export class OpenAICompatibleClient {
|
|
|
46
143
|
const resolvedModel = provider === "copilot"
|
|
47
144
|
? normalizeCopilotModelName(request.model)
|
|
48
145
|
: request.model;
|
|
146
|
+
if (provider === "llama.cpp") {
|
|
147
|
+
const emitProviderEvent = (event) => {
|
|
148
|
+
request.onProviderEvent?.({ provider, ...event });
|
|
149
|
+
};
|
|
150
|
+
let fallbackCount = 0;
|
|
151
|
+
const emitFallback = (stage, nextStage, reason, extra = {}) => {
|
|
152
|
+
fallbackCount += 1;
|
|
153
|
+
emitProviderEvent({
|
|
154
|
+
event: "fallback",
|
|
155
|
+
stage,
|
|
156
|
+
next_stage: nextStage,
|
|
157
|
+
reason,
|
|
158
|
+
fallback_count: fallbackCount,
|
|
159
|
+
statusCode: extra.statusCode,
|
|
160
|
+
sample_hex: extra.sample_hex,
|
|
161
|
+
sample_text: extra.sample_text,
|
|
162
|
+
});
|
|
163
|
+
};
|
|
164
|
+
const messagesToSend = sanitizeChatMessages(request.messages);
|
|
165
|
+
const messagesToPrompt = messagesToSend.map((m) => `${m.role}: ${m.content}`).join("\n");
|
|
166
|
+
// Attempt 1: streaming chat (SSE)
|
|
167
|
+
try {
|
|
168
|
+
emitProviderEvent({ event: "attempt", stage: "streaming_chat" });
|
|
169
|
+
const streamBody = {
|
|
170
|
+
model: resolvedModel,
|
|
171
|
+
messages: messagesToSend,
|
|
172
|
+
stream: true,
|
|
173
|
+
temperature: request.temperature,
|
|
174
|
+
top_p: request.topP,
|
|
175
|
+
};
|
|
176
|
+
const streamRes = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
177
|
+
method: "POST",
|
|
178
|
+
signal: controller.signal,
|
|
179
|
+
headers: {
|
|
180
|
+
"Content-Type": "application/json",
|
|
181
|
+
Accept: "text/event-stream",
|
|
182
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
183
|
+
...(config.headers ?? {}),
|
|
184
|
+
},
|
|
185
|
+
body: JSON.stringify(streamBody),
|
|
186
|
+
});
|
|
187
|
+
if (streamRes.ok && streamRes.body) {
|
|
188
|
+
let streamContentSeen = false;
|
|
189
|
+
let streamParsedFrames = 0;
|
|
190
|
+
let streamParseErrors = 0;
|
|
191
|
+
for await (const data of this.streamSseData(streamRes.body)) {
|
|
192
|
+
if (data === "[DONE]") {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
let parsed;
|
|
196
|
+
try {
|
|
197
|
+
parsed = JSON.parse(data);
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
streamParseErrors += 1;
|
|
201
|
+
emitProviderEvent({
|
|
202
|
+
event: "parse_error",
|
|
203
|
+
stage: "streaming_chat",
|
|
204
|
+
reason: "sse_data_json_parse_error",
|
|
205
|
+
sample_text: data.slice(0, 512),
|
|
206
|
+
});
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
streamParsedFrames += 1;
|
|
210
|
+
const usage = (parsed.usage ?? {});
|
|
211
|
+
if (typeof usage.prompt_tokens === "number")
|
|
212
|
+
promptTokens = usage.prompt_tokens;
|
|
213
|
+
if (typeof usage.completion_tokens === "number")
|
|
214
|
+
completionTokens = usage.completion_tokens;
|
|
215
|
+
const choices = Array.isArray(parsed.choices) ? parsed.choices : [];
|
|
216
|
+
const firstChoice = (choices[0] ?? {});
|
|
217
|
+
const delta = firstChoice.delta;
|
|
218
|
+
const text = typeof delta?.content === "string" ? sanitizeTransportText(delta.content) : "";
|
|
219
|
+
if (text.length > 0) {
|
|
220
|
+
streamContentSeen = true;
|
|
221
|
+
yield { text, done: false };
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (streamContentSeen) {
|
|
225
|
+
if (!doneSent) {
|
|
226
|
+
doneSent = true;
|
|
227
|
+
yield {
|
|
228
|
+
text: "",
|
|
229
|
+
done: true,
|
|
230
|
+
promptTokens,
|
|
231
|
+
completionTokens,
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
emitProviderEvent({ event: "success", stage: "streaming_chat" });
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
emitFallback("streaming_chat", "non_streaming_chat", streamParseErrors > 0
|
|
238
|
+
? "stream_parse_error_no_content"
|
|
239
|
+
: streamParsedFrames > 0
|
|
240
|
+
? "stream_no_text_content"
|
|
241
|
+
: "stream_no_parseable_frames");
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
const sample = await readResponseText(streamRes, `${provider}:streaming_chat:error`);
|
|
245
|
+
emitFallback("streaming_chat", "non_streaming_chat", "stream_http_error", {
|
|
246
|
+
statusCode: streamRes.status,
|
|
247
|
+
sample_hex: sample.sample_hex,
|
|
248
|
+
sample_text: sample.sample_text,
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
if (err instanceof Error && /abort/i.test(err.name))
|
|
254
|
+
throw err;
|
|
255
|
+
emitFallback("streaming_chat", "non_streaming_chat", formatErrorMessage(err));
|
|
256
|
+
}
|
|
257
|
+
// Attempt 2: non-streaming chat (JSON or direct text)
|
|
258
|
+
try {
|
|
259
|
+
emitProviderEvent({ event: "attempt", stage: "non_streaming_chat" });
|
|
260
|
+
const chatBody = {
|
|
261
|
+
model: resolvedModel,
|
|
262
|
+
messages: messagesToSend,
|
|
263
|
+
stream: false,
|
|
264
|
+
temperature: request.temperature,
|
|
265
|
+
top_p: request.topP,
|
|
266
|
+
};
|
|
267
|
+
const res = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
268
|
+
method: "POST",
|
|
269
|
+
signal: controller.signal,
|
|
270
|
+
headers: {
|
|
271
|
+
"Content-Type": "application/json",
|
|
272
|
+
Accept: "application/json, text/plain;q=0.9, */*;q=0.1",
|
|
273
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
274
|
+
...(config.headers ?? {}),
|
|
275
|
+
},
|
|
276
|
+
body: JSON.stringify(chatBody),
|
|
277
|
+
});
|
|
278
|
+
const sample = await readResponseText(res, `${provider}:non_streaming_chat`);
|
|
279
|
+
if (res.ok) {
|
|
280
|
+
const extracted = extractProviderBodyText(sample.text);
|
|
281
|
+
if (extracted.text.length > 0) {
|
|
282
|
+
yield { text: extracted.text, done: false };
|
|
283
|
+
yield {
|
|
284
|
+
text: "",
|
|
285
|
+
done: true,
|
|
286
|
+
promptTokens,
|
|
287
|
+
completionTokens,
|
|
288
|
+
};
|
|
289
|
+
doneSent = true;
|
|
290
|
+
emitProviderEvent({ event: "success", stage: "non_streaming_chat" });
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
emitFallback("non_streaming_chat", "completions", "empty_non_streaming_response", {
|
|
294
|
+
statusCode: res.status,
|
|
295
|
+
sample_hex: sample.sample_hex,
|
|
296
|
+
sample_text: sample.sample_text,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
emitFallback("non_streaming_chat", "completions", "non_streaming_http_error", {
|
|
301
|
+
statusCode: res.status,
|
|
302
|
+
sample_hex: sample.sample_hex,
|
|
303
|
+
sample_text: sample.sample_text,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
catch (err) {
|
|
308
|
+
if (err instanceof Error && /abort/i.test(err.name))
|
|
309
|
+
throw err;
|
|
310
|
+
emitFallback("non_streaming_chat", "completions", formatErrorMessage(err));
|
|
311
|
+
}
|
|
312
|
+
// Attempt 3: completions endpoint with `prompt` (legacy shape, JSON or direct text)
|
|
313
|
+
emitProviderEvent({ event: "attempt", stage: "completions" });
|
|
314
|
+
const prompt = messagesToPrompt || "";
|
|
315
|
+
const compBody = {
|
|
316
|
+
model: resolvedModel,
|
|
317
|
+
prompt,
|
|
318
|
+
temperature: request.temperature,
|
|
319
|
+
top_p: request.topP,
|
|
320
|
+
};
|
|
321
|
+
const compRes = await fetch(`${config.baseUrl}/completions`, {
|
|
322
|
+
method: "POST",
|
|
323
|
+
signal: controller.signal,
|
|
324
|
+
headers: {
|
|
325
|
+
"Content-Type": "application/json",
|
|
326
|
+
Accept: "application/json",
|
|
327
|
+
...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
|
|
328
|
+
...(config.headers ?? {}),
|
|
329
|
+
},
|
|
330
|
+
body: JSON.stringify(compBody),
|
|
331
|
+
});
|
|
332
|
+
const compSample = await readResponseText(compRes, `${provider}:completions`);
|
|
333
|
+
if (!compRes.ok) {
|
|
334
|
+
throw new OpenAICompatibleError(`Provider '${provider}' API error: ${compRes.status} ${compRes.statusText}`, compRes.status, compSample.text);
|
|
335
|
+
}
|
|
336
|
+
const extracted = extractProviderBodyText(compSample.text);
|
|
337
|
+
const text = extracted.text;
|
|
338
|
+
if (text.length > 0)
|
|
339
|
+
yield { text, done: false };
|
|
340
|
+
yield { text: "", done: true, promptTokens, completionTokens };
|
|
341
|
+
emitProviderEvent({ event: "success", stage: "completions" });
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
// default streaming path for other providers
|
|
49
345
|
const body = {
|
|
50
346
|
model: resolvedModel,
|
|
51
|
-
messages: request.messages,
|
|
347
|
+
messages: sanitizeChatMessages(request.messages),
|
|
52
348
|
stream: true,
|
|
53
349
|
temperature: request.temperature,
|
|
54
350
|
top_p: request.topP,
|
|
55
|
-
stream_options: { include_usage: true },
|
|
56
351
|
};
|
|
352
|
+
// llama.cpp rejects custom stream_options; only include for non-llama providers
|
|
353
|
+
if (provider !== 'llama.cpp') {
|
|
354
|
+
body.stream_options = { include_usage: true };
|
|
355
|
+
}
|
|
57
356
|
const res = await fetch(`${config.baseUrl}/chat/completions`, {
|
|
58
357
|
method: "POST",
|
|
59
358
|
signal: controller.signal,
|
|
@@ -202,7 +501,8 @@ export class OpenAICompatibleClient {
|
|
|
202
501
|
const { done, value } = await reader.read();
|
|
203
502
|
if (done)
|
|
204
503
|
break;
|
|
205
|
-
|
|
504
|
+
captureRawBytes("openai-compatible:sse_chunk", value);
|
|
505
|
+
buffer += sanitizeTransportText(decoder.decode(value, { stream: true }));
|
|
206
506
|
const lines = buffer.split("\n");
|
|
207
507
|
buffer = lines.pop() ?? "";
|
|
208
508
|
for (const line of lines) {
|
|
@@ -211,14 +511,14 @@ export class OpenAICompatibleClient {
|
|
|
211
511
|
continue;
|
|
212
512
|
if (!trimmed.startsWith("data:"))
|
|
213
513
|
continue;
|
|
214
|
-
const data = trimmed.slice(5).trim();
|
|
514
|
+
const data = sanitizeTransportText(trimmed.slice(5).trim());
|
|
215
515
|
if (!data)
|
|
216
516
|
continue;
|
|
217
517
|
yield data;
|
|
218
518
|
}
|
|
219
519
|
}
|
|
220
520
|
if (buffer.trim().startsWith("data:")) {
|
|
221
|
-
const data = buffer.trim().slice(5).trim();
|
|
521
|
+
const data = sanitizeTransportText(buffer.trim().slice(5).trim());
|
|
222
522
|
if (data)
|
|
223
523
|
yield data;
|
|
224
524
|
}
|
|
@@ -57,6 +57,7 @@ export declare function buildOpenAiCompatibleBaseUrl(baseUrl: string): string;
|
|
|
57
57
|
export declare function inferProviderFromModel(model: string | undefined): string | undefined;
|
|
58
58
|
export declare function isLocalLlmProvider(providerInput: string | undefined): providerInput is LocalLlmProvider;
|
|
59
59
|
export declare function defaultModelForProvider(providerInput: string | undefined): string;
|
|
60
|
+
export declare function normalizeLlamaCppHfModelName(modelInput: string | undefined): string | undefined;
|
|
60
61
|
export declare function providerEnvPrefix(providerInput: string | undefined): string;
|
|
61
62
|
export declare function buildProviderDoctorCommands(providerInput: string | undefined, modelInput: string | undefined, baseUrlInput?: string): string[];
|
|
62
63
|
export declare function parseJsoncLoose(raw: string): unknown;
|
|
@@ -114,6 +114,22 @@ export function defaultModelForProvider(providerInput) {
|
|
|
114
114
|
return DEFAULT_LLAMA_CPP_MODEL;
|
|
115
115
|
return DEFAULT_HOSTED_MODELS[provider] ?? DEFAULT_HOSTED_MODELS.codex;
|
|
116
116
|
}
|
|
117
|
+
export function normalizeLlamaCppHfModelName(modelInput) {
|
|
118
|
+
const value = modelInput?.trim();
|
|
119
|
+
if (!value)
|
|
120
|
+
return undefined;
|
|
121
|
+
if (value.endsWith(".gguf"))
|
|
122
|
+
return value;
|
|
123
|
+
const normalized = value.replace(/^(?:https?:\/\/)?(?:hf\.co|huggingface\.co)\//i, "");
|
|
124
|
+
const match = normalized.match(/^([^/]+)\/([^:]+?)(?::([^:]+))?$/);
|
|
125
|
+
if (!match)
|
|
126
|
+
return value;
|
|
127
|
+
const repoName = match[2].replace(/\.gguf$/i, "").replace(/-GGUF$/i, "");
|
|
128
|
+
const quant = match[3]?.trim();
|
|
129
|
+
if (!quant)
|
|
130
|
+
return value;
|
|
131
|
+
return `${repoName}-${quant}.gguf`;
|
|
132
|
+
}
|
|
117
133
|
export function providerEnvPrefix(providerInput) {
|
|
118
134
|
const provider = normalizeProvider(providerInput);
|
|
119
135
|
if (!provider)
|
|
@@ -124,46 +140,51 @@ export function providerEnvPrefix(providerInput) {
|
|
|
124
140
|
}
|
|
125
141
|
export function buildProviderDoctorCommands(providerInput, modelInput, baseUrlInput) {
|
|
126
142
|
const provider = normalizeProvider(providerInput) ?? "ollama";
|
|
127
|
-
const model = modelInput?.trim()
|
|
143
|
+
const model = modelInput?.trim();
|
|
128
144
|
const baseUrl = normalizeLocalBaseUrl(baseUrlInput);
|
|
129
145
|
if (provider === "ollama") {
|
|
146
|
+
const resolvedModel = model || defaultModelForProvider(provider);
|
|
130
147
|
return [
|
|
131
148
|
"ollama serve",
|
|
132
|
-
`ollama pull ${
|
|
149
|
+
`ollama pull ${resolvedModel}`,
|
|
133
150
|
...(baseUrl ? [`curl -s ${baseUrl}/api/tags`] : []),
|
|
134
151
|
baseUrl
|
|
135
|
-
? `ace doctor --llm ${provider} --model ${
|
|
136
|
-
: `ace doctor --llm ${provider} --model ${
|
|
152
|
+
? `ace doctor --llm ${provider} --model ${resolvedModel} --base-url ${baseUrl}`
|
|
153
|
+
: `ace doctor --llm ${provider} --model ${resolvedModel} --scan`,
|
|
137
154
|
];
|
|
138
155
|
}
|
|
139
156
|
if (provider === "llama.cpp") {
|
|
157
|
+
const resolvedModel = model || "<hf-model-or-gguf>";
|
|
140
158
|
return [
|
|
141
159
|
"# Start llama-server separately, for example:",
|
|
142
|
-
|
|
160
|
+
`# llama-server -hf ${resolvedModel} --port 8080`,
|
|
143
161
|
...(baseUrl ? [`curl -s ${buildOpenAiCompatibleBaseUrl(baseUrl)}/models`] : []),
|
|
144
162
|
baseUrl
|
|
145
|
-
? `ace doctor --llm ${provider} --model ${
|
|
146
|
-
: `ace doctor --llm ${provider} --model ${
|
|
163
|
+
? `ace doctor --llm ${provider} --model ${resolvedModel} --base-url ${baseUrl}`
|
|
164
|
+
: `ace doctor --llm ${provider} --model ${resolvedModel} --scan`,
|
|
147
165
|
];
|
|
148
166
|
}
|
|
149
167
|
if (provider === "codex") {
|
|
168
|
+
const resolvedModel = model || defaultModelForProvider(provider);
|
|
150
169
|
return [
|
|
151
170
|
"export OPENAI_API_KEY=<token>",
|
|
152
171
|
...(baseUrl ? [`export OPENAI_BASE_URL=${baseUrl}`] : []),
|
|
153
|
-
`ace doctor --llm ${provider} --model ${
|
|
172
|
+
`ace doctor --llm ${provider} --model ${resolvedModel}${baseUrl ? ` --base-url ${baseUrl}` : ""}`,
|
|
154
173
|
];
|
|
155
174
|
}
|
|
156
175
|
if (provider === "copilot") {
|
|
176
|
+
const resolvedModel = model || defaultModelForProvider(provider);
|
|
157
177
|
return [
|
|
158
178
|
"gh auth login # or export GITHUB_TOKEN=<token>",
|
|
159
|
-
`ace doctor --llm ${provider} --model ${
|
|
179
|
+
`ace doctor --llm ${provider} --model ${resolvedModel}`,
|
|
160
180
|
];
|
|
161
181
|
}
|
|
162
182
|
const prefix = providerEnvPrefix(provider);
|
|
183
|
+
const resolvedModel = model || defaultModelForProvider(provider);
|
|
163
184
|
return [
|
|
164
185
|
`export ${prefix}_BASE_URL=${baseUrl ?? "<openai-compatible-base-url>"}`,
|
|
165
186
|
`export ${prefix}_API_KEY=<token>`,
|
|
166
|
-
`ace doctor --llm ${provider} --model ${
|
|
187
|
+
`ace doctor --llm ${provider} --model ${resolvedModel}${baseUrl ? ` --base-url ${baseUrl}` : ""}`,
|
|
167
188
|
];
|
|
168
189
|
}
|
|
169
190
|
function looksLikeModel(value) {
|
|
@@ -458,7 +479,10 @@ export function discoverProviderContext(options) {
|
|
|
458
479
|
}
|
|
459
480
|
}
|
|
460
481
|
if (!model) {
|
|
461
|
-
model = defaultModelForProvider(provider);
|
|
482
|
+
model = provider === "llama.cpp" ? "" : defaultModelForProvider(provider);
|
|
483
|
+
if (provider === "llama.cpp") {
|
|
484
|
+
notes.push("runtime_default=llama.cpp (model not set; run ace connect or ace doctor --scan)");
|
|
485
|
+
}
|
|
462
486
|
}
|
|
463
487
|
addModel(modelsByProvider, provider, model);
|
|
464
488
|
const allProviders = sortProviders([
|
|
@@ -5,7 +5,7 @@ export declare const VERICIFY_PROCESS_POST_LOG_SCHEMA_NAME = "vericify-process-p
|
|
|
5
5
|
export declare const VERICIFY_BRIDGE_SNAPSHOT_REL_PATH = "agent-state/vericify/ace-bridge.json";
|
|
6
6
|
export declare const VERICIFY_BRIDGE_SNAPSHOT_SCHEMA_REL_PATH = "agent-state/MODULES/schemas/VERICIFY_BRIDGE_SNAPSHOT.schema.json";
|
|
7
7
|
export declare const VERICIFY_BRIDGE_SNAPSHOT_SCHEMA_NAME = "vericify-bridge-snapshot@1.0.0";
|
|
8
|
-
type VericifyProcessPostKind = "intent" | "progress" | "blocker" | "handoff_note" | "stale_ack" | "completion";
|
|
8
|
+
type VericifyProcessPostKind = "intent" | "progress" | "blocker" | "handoff_note" | "stale_ack" | "completion" | "plan_proposal" | "plan_quality_assessment";
|
|
9
9
|
export interface VericifyProcessPost {
|
|
10
10
|
process_post_id: string;
|
|
11
11
|
run_id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voybio/ace-swarm",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.1",
|
|
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",
|