@holoscript/holoscript-agent 2.0.0 → 2.0.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/bin/holoscript-agent.cjs +16 -0
- package/dist/brain.js +18 -4
- package/dist/brain.js.map +1 -1
- package/dist/cost-guard.d.ts +17 -2
- package/dist/cost-guard.js +29 -3
- package/dist/cost-guard.js.map +1 -1
- package/dist/holomesh-client.d.ts +50 -1
- package/dist/holomesh-client.js +45 -0
- package/dist/holomesh-client.js.map +1 -1
- package/dist/identity.js +5 -1
- package/dist/identity.js.map +1 -1
- package/dist/index.js +489 -70
- package/dist/index.js.map +1 -1
- package/dist/runner.d.ts +57 -0
- package/dist/runner.js +86 -15
- package/dist/runner.js.map +1 -1
- package/dist/supervisor-config.js +12 -5
- package/dist/supervisor-config.js.map +1 -1
- package/dist/supervisor.js +350 -29
- package/dist/supervisor.js.map +1 -1
- package/dist/types.d.ts +30 -1
- package/package.json +7 -5
package/dist/index.js
CHANGED
|
@@ -8,7 +8,9 @@ import {
|
|
|
8
8
|
createOpenAIProvider,
|
|
9
9
|
createGeminiProvider,
|
|
10
10
|
createMockProvider,
|
|
11
|
-
createLocalLLMProvider
|
|
11
|
+
createLocalLLMProvider,
|
|
12
|
+
createXAIProvider,
|
|
13
|
+
createOpenRouterProvider
|
|
12
14
|
} from "@holoscript/llm-provider";
|
|
13
15
|
|
|
14
16
|
// src/identity.ts
|
|
@@ -16,6 +18,8 @@ var VALID_PROVIDERS = /* @__PURE__ */ new Set([
|
|
|
16
18
|
"anthropic",
|
|
17
19
|
"openai",
|
|
18
20
|
"gemini",
|
|
21
|
+
"xai",
|
|
22
|
+
"openrouter",
|
|
19
23
|
"mock",
|
|
20
24
|
"bitnet",
|
|
21
25
|
"local-llm"
|
|
@@ -36,7 +40,9 @@ function loadIdentity(env = process.env) {
|
|
|
36
40
|
const budgetRaw = env.HOLOSCRIPT_AGENT_BUDGET_USD_DAY ?? "5";
|
|
37
41
|
const budget = Number(budgetRaw);
|
|
38
42
|
if (!Number.isFinite(budget) || budget < 0) {
|
|
39
|
-
throw new Error(
|
|
43
|
+
throw new Error(
|
|
44
|
+
`HOLOSCRIPT_AGENT_BUDGET_USD_DAY must be >= 0 (0 = unlimited), got ${budgetRaw}`
|
|
45
|
+
);
|
|
40
46
|
}
|
|
41
47
|
return {
|
|
42
48
|
handle,
|
|
@@ -75,15 +81,29 @@ function identityForLog(id) {
|
|
|
75
81
|
import { readFile } from "fs/promises";
|
|
76
82
|
async function loadBrain(brainPath, scopeTier = "warm") {
|
|
77
83
|
const systemPrompt = await readFile(brainPath, "utf8");
|
|
78
|
-
const { domain, capabilityTags } = extractIdentity(systemPrompt);
|
|
79
|
-
return {
|
|
84
|
+
const { domain, capabilityTags, requires, prefers, avoids } = extractIdentity(systemPrompt);
|
|
85
|
+
return {
|
|
86
|
+
brainPath,
|
|
87
|
+
systemPrompt,
|
|
88
|
+
capabilityTags,
|
|
89
|
+
domain,
|
|
90
|
+
scopeTier,
|
|
91
|
+
requires,
|
|
92
|
+
prefers,
|
|
93
|
+
avoids
|
|
94
|
+
};
|
|
80
95
|
}
|
|
81
96
|
function extractIdentity(brain) {
|
|
82
97
|
const identityBlock = sliceNamedBlock(brain, "identity");
|
|
83
|
-
if (!identityBlock)
|
|
98
|
+
if (!identityBlock) {
|
|
99
|
+
return { domain: "unknown", capabilityTags: [], requires: [], prefers: [], avoids: [] };
|
|
100
|
+
}
|
|
84
101
|
const domain = scalarField(identityBlock, "domain") ?? "unknown";
|
|
85
102
|
const capabilityTags = listField(identityBlock, "capability_tags") ?? [];
|
|
86
|
-
|
|
103
|
+
const requires = listField(identityBlock, "requires") ?? [];
|
|
104
|
+
const prefers = listField(identityBlock, "prefers") ?? [];
|
|
105
|
+
const avoids = listField(identityBlock, "avoids") ?? [];
|
|
106
|
+
return { domain, capabilityTags, requires, prefers, avoids };
|
|
87
107
|
}
|
|
88
108
|
function sliceNamedBlock(src, name) {
|
|
89
109
|
const re = new RegExp(`\\b${name}\\s*:?\\s*\\{`, "g");
|
|
@@ -138,8 +158,8 @@ function listField(block, key) {
|
|
|
138
158
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
139
159
|
import { dirname } from "path";
|
|
140
160
|
var ANTHROPIC_PRICING_USD_PER_MTOK = {
|
|
141
|
-
"claude-opus-4-7": { input:
|
|
142
|
-
"claude-opus-4-6": { input:
|
|
161
|
+
"claude-opus-4-7": { input: 5, output: 25 },
|
|
162
|
+
"claude-opus-4-6": { input: 5, output: 25 },
|
|
143
163
|
"claude-sonnet-4-6": { input: 3, output: 15 },
|
|
144
164
|
"claude-haiku-4-5-20251001": { input: 1, output: 5 },
|
|
145
165
|
"claude-haiku-4-5": { input: 1, output: 5 }
|
|
@@ -156,8 +176,30 @@ function defaultAnthropicPricer(model, usage) {
|
|
|
156
176
|
function defaultLocalLlmPricer(_model, _usage) {
|
|
157
177
|
return 0;
|
|
158
178
|
}
|
|
179
|
+
var XAI_PRICING_USD_PER_MTOK = {};
|
|
180
|
+
function defaultXAIPricer(model, usage) {
|
|
181
|
+
const price = XAI_PRICING_USD_PER_MTOK[model];
|
|
182
|
+
if (!price) {
|
|
183
|
+
throw new Error(
|
|
184
|
+
`No xAI pricing configured for model "${model}" \u2014 populate XAI_PRICING_USD_PER_MTOK (see /research task_1778109552044_qed8 in docs/LLM_CAPABILITIES.md) or pass a custom pricer`
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
return (usage.promptTokens * price.input + usage.completionTokens * price.output) / 1e6;
|
|
188
|
+
}
|
|
189
|
+
var OPENROUTER_PRICING_USD_PER_MTOK = {};
|
|
190
|
+
function defaultOpenRouterPricer(model, usage) {
|
|
191
|
+
const price = OPENROUTER_PRICING_USD_PER_MTOK[model];
|
|
192
|
+
if (!price) {
|
|
193
|
+
throw new Error(
|
|
194
|
+
`No OpenRouter pricing configured for model "${model}" \u2014 populate OPENROUTER_PRICING_USD_PER_MTOK or pass a custom pricer`
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
return (usage.promptTokens * price.input + usage.completionTokens * price.output) / 1e6;
|
|
198
|
+
}
|
|
159
199
|
function defaultPricerForProvider(provider) {
|
|
160
200
|
if (provider === "local-llm" || provider === "mock") return defaultLocalLlmPricer;
|
|
201
|
+
if (provider === "xai") return defaultXAIPricer;
|
|
202
|
+
if (provider === "openrouter") return defaultOpenRouterPricer;
|
|
161
203
|
return defaultAnthropicPricer;
|
|
162
204
|
}
|
|
163
205
|
var CostGuard = class {
|
|
@@ -219,6 +261,181 @@ function todayUtc() {
|
|
|
219
261
|
return (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
220
262
|
}
|
|
221
263
|
|
|
264
|
+
// src/capability-router.ts
|
|
265
|
+
import {
|
|
266
|
+
ANTHROPIC_CAPABILITIES,
|
|
267
|
+
OPENAI_CAPABILITIES,
|
|
268
|
+
GEMINI_CAPABILITIES,
|
|
269
|
+
XAI_CAPABILITIES,
|
|
270
|
+
OPENROUTER_CAPABILITIES,
|
|
271
|
+
LOCAL_LLM_CAPABILITIES,
|
|
272
|
+
BITNET_CAPABILITIES,
|
|
273
|
+
MOCK_CAPABILITIES
|
|
274
|
+
} from "@holoscript/llm-provider";
|
|
275
|
+
var NoEligibleProviderError = class extends Error {
|
|
276
|
+
constructor(requires, avoids, considered, excludedByAvoids) {
|
|
277
|
+
super(
|
|
278
|
+
`No provider satisfies brain requires=[${requires.join(", ")}] avoids=[${avoids.join(", ")}]. Considered: [${considered.join(", ")}]. Excluded by avoids: [${excludedByAvoids.join(", ")}].`
|
|
279
|
+
);
|
|
280
|
+
this.requires = requires;
|
|
281
|
+
this.avoids = avoids;
|
|
282
|
+
this.considered = considered;
|
|
283
|
+
this.excludedByAvoids = excludedByAvoids;
|
|
284
|
+
this.name = "NoEligibleProviderError";
|
|
285
|
+
}
|
|
286
|
+
};
|
|
287
|
+
function satisfies(capabilities, key) {
|
|
288
|
+
const value = capabilities[key];
|
|
289
|
+
if (typeof value === "boolean") return value;
|
|
290
|
+
if (typeof value === "number") return value > 0;
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
function countMatches(capabilities, keys) {
|
|
294
|
+
let count = 0;
|
|
295
|
+
for (const key of keys) {
|
|
296
|
+
if (satisfies(capabilities, key)) count++;
|
|
297
|
+
}
|
|
298
|
+
return count;
|
|
299
|
+
}
|
|
300
|
+
function unsatisfiedKeys(capabilities, keys) {
|
|
301
|
+
return keys.filter((key) => !satisfies(capabilities, key));
|
|
302
|
+
}
|
|
303
|
+
function pickProvider(opts) {
|
|
304
|
+
const { brain, envOverride, candidates } = opts;
|
|
305
|
+
const tieBreaker = opts.tieBreakerOrder ?? candidates.map((c) => c.name);
|
|
306
|
+
if (candidates.length === 0) {
|
|
307
|
+
throw new Error("pickProvider: no candidates supplied");
|
|
308
|
+
}
|
|
309
|
+
const excludedByAvoids = [];
|
|
310
|
+
const notAvoided = [];
|
|
311
|
+
for (const candidate of candidates) {
|
|
312
|
+
const matchesAvoid = brain.avoids.some((a) => satisfies(candidate.capabilities, a));
|
|
313
|
+
if (matchesAvoid) {
|
|
314
|
+
excludedByAvoids.push(candidate.name);
|
|
315
|
+
} else {
|
|
316
|
+
notAvoided.push(candidate);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (brain.requires.length === 0) {
|
|
320
|
+
if (envOverride !== void 0) {
|
|
321
|
+
const envCandidate = candidates.find((c) => c.name === envOverride);
|
|
322
|
+
const matchedPrefers = envCandidate ? brain.prefers.filter((p) => satisfies(envCandidate.capabilities, p)) : [];
|
|
323
|
+
return {
|
|
324
|
+
picked: envOverride,
|
|
325
|
+
reason: "env-override-no-requirements",
|
|
326
|
+
unsatisfiedRequires: [],
|
|
327
|
+
matchedPrefers,
|
|
328
|
+
excludedByAvoids,
|
|
329
|
+
alternatives: candidates.filter((c) => c.name !== envOverride).map((c) => c.name)
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
const ordered = orderCandidates(notAvoided, tieBreaker);
|
|
333
|
+
if (ordered.length === 0) {
|
|
334
|
+
return {
|
|
335
|
+
picked: candidates[0].name,
|
|
336
|
+
reason: "open-routing-default",
|
|
337
|
+
unsatisfiedRequires: [],
|
|
338
|
+
matchedPrefers: brain.prefers.filter(
|
|
339
|
+
(p) => satisfies(candidates[0].capabilities, p)
|
|
340
|
+
),
|
|
341
|
+
excludedByAvoids,
|
|
342
|
+
alternatives: candidates.slice(1).map((c) => c.name)
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
return {
|
|
346
|
+
picked: ordered[0].name,
|
|
347
|
+
reason: "open-routing-default",
|
|
348
|
+
unsatisfiedRequires: [],
|
|
349
|
+
matchedPrefers: brain.prefers.filter((p) => satisfies(ordered[0].capabilities, p)),
|
|
350
|
+
excludedByAvoids,
|
|
351
|
+
alternatives: ordered.slice(1).map((c) => c.name)
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
const eligible = notAvoided.filter((c) => unsatisfiedKeys(c.capabilities, brain.requires).length === 0);
|
|
355
|
+
if (eligible.length === 0) {
|
|
356
|
+
if (envOverride !== void 0) {
|
|
357
|
+
const envCandidate = candidates.find((c) => c.name === envOverride);
|
|
358
|
+
const unsatisfied = envCandidate ? unsatisfiedKeys(envCandidate.capabilities, brain.requires) : brain.requires.slice();
|
|
359
|
+
const matchedPrefers = envCandidate ? brain.prefers.filter((p) => satisfies(envCandidate.capabilities, p)) : [];
|
|
360
|
+
return {
|
|
361
|
+
picked: envOverride,
|
|
362
|
+
reason: "env-override-mismatch",
|
|
363
|
+
unsatisfiedRequires: unsatisfied,
|
|
364
|
+
matchedPrefers,
|
|
365
|
+
excludedByAvoids,
|
|
366
|
+
alternatives: []
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
throw new NoEligibleProviderError(
|
|
370
|
+
brain.requires,
|
|
371
|
+
brain.avoids,
|
|
372
|
+
candidates.map((c) => c.name),
|
|
373
|
+
excludedByAvoids
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
const ranked = [...eligible].sort((a, b) => {
|
|
377
|
+
const aMatches = countMatches(a.capabilities, brain.prefers);
|
|
378
|
+
const bMatches = countMatches(b.capabilities, brain.prefers);
|
|
379
|
+
if (aMatches !== bMatches) return bMatches - aMatches;
|
|
380
|
+
const aIdx = tieBreaker.indexOf(a.name);
|
|
381
|
+
const bIdx = tieBreaker.indexOf(b.name);
|
|
382
|
+
const aRank = aIdx === -1 ? Number.MAX_SAFE_INTEGER : aIdx;
|
|
383
|
+
const bRank = bIdx === -1 ? Number.MAX_SAFE_INTEGER : bIdx;
|
|
384
|
+
return aRank - bRank;
|
|
385
|
+
});
|
|
386
|
+
if (envOverride !== void 0) {
|
|
387
|
+
const envEligible = ranked.find((c) => c.name === envOverride);
|
|
388
|
+
if (envEligible) {
|
|
389
|
+
return {
|
|
390
|
+
picked: envOverride,
|
|
391
|
+
reason: "env-override-satisfies",
|
|
392
|
+
unsatisfiedRequires: [],
|
|
393
|
+
matchedPrefers: brain.prefers.filter((p) => satisfies(envEligible.capabilities, p)),
|
|
394
|
+
excludedByAvoids,
|
|
395
|
+
alternatives: ranked.filter((c) => c.name !== envOverride).map((c) => c.name)
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
const envCandidate = candidates.find((c) => c.name === envOverride);
|
|
399
|
+
const unsatisfied = envCandidate ? unsatisfiedKeys(envCandidate.capabilities, brain.requires) : brain.requires.slice();
|
|
400
|
+
return {
|
|
401
|
+
picked: envOverride,
|
|
402
|
+
reason: "env-override-mismatch",
|
|
403
|
+
unsatisfiedRequires: unsatisfied,
|
|
404
|
+
matchedPrefers: envCandidate ? brain.prefers.filter((p) => satisfies(envCandidate.capabilities, p)) : [],
|
|
405
|
+
excludedByAvoids,
|
|
406
|
+
alternatives: ranked.map((c) => c.name)
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
const top = ranked[0];
|
|
410
|
+
return {
|
|
411
|
+
picked: top.name,
|
|
412
|
+
reason: "capability-best-fit",
|
|
413
|
+
unsatisfiedRequires: [],
|
|
414
|
+
matchedPrefers: brain.prefers.filter((p) => satisfies(top.capabilities, p)),
|
|
415
|
+
excludedByAvoids,
|
|
416
|
+
alternatives: ranked.slice(1).map((c) => c.name)
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
var BUILT_IN_CANDIDATES = [
|
|
420
|
+
{ name: "anthropic", capabilities: ANTHROPIC_CAPABILITIES },
|
|
421
|
+
{ name: "openai", capabilities: OPENAI_CAPABILITIES },
|
|
422
|
+
{ name: "gemini", capabilities: GEMINI_CAPABILITIES },
|
|
423
|
+
{ name: "xai", capabilities: XAI_CAPABILITIES },
|
|
424
|
+
{ name: "openrouter", capabilities: OPENROUTER_CAPABILITIES },
|
|
425
|
+
{ name: "local-llm", capabilities: LOCAL_LLM_CAPABILITIES },
|
|
426
|
+
{ name: "bitnet", capabilities: BITNET_CAPABILITIES },
|
|
427
|
+
{ name: "mock", capabilities: MOCK_CAPABILITIES }
|
|
428
|
+
];
|
|
429
|
+
function orderCandidates(candidates, tieBreaker) {
|
|
430
|
+
return [...candidates].sort((a, b) => {
|
|
431
|
+
const aIdx = tieBreaker.indexOf(a.name);
|
|
432
|
+
const bIdx = tieBreaker.indexOf(b.name);
|
|
433
|
+
const aRank = aIdx === -1 ? Number.MAX_SAFE_INTEGER : aIdx;
|
|
434
|
+
const bRank = bIdx === -1 ? Number.MAX_SAFE_INTEGER : bIdx;
|
|
435
|
+
return aRank - bRank;
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
222
439
|
// src/holomesh-client.ts
|
|
223
440
|
var HolomeshClient = class {
|
|
224
441
|
constructor(opts) {
|
|
@@ -282,6 +499,51 @@ var HolomeshClient = class {
|
|
|
282
499
|
wallet: raw.wallet
|
|
283
500
|
};
|
|
284
501
|
}
|
|
502
|
+
// ── Team Message Surface (E4 delegated-authority protocol) ───────────────────
|
|
503
|
+
/** Read recent team messages. */
|
|
504
|
+
async getTeamMessages(limit = 20) {
|
|
505
|
+
const data = await this.req(
|
|
506
|
+
"GET",
|
|
507
|
+
`/team/${this.teamId}/messages?limit=${limit}`
|
|
508
|
+
);
|
|
509
|
+
return data.messages ?? [];
|
|
510
|
+
}
|
|
511
|
+
/** Post a message to the team feed. */
|
|
512
|
+
async sendTeamMessage(content, messageType = "text") {
|
|
513
|
+
await this.req("POST", `/team/${this.teamId}/message`, {
|
|
514
|
+
content,
|
|
515
|
+
type: messageType
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
// ── Owner-op API wrappers (E4) ─────────────────────────────────────────────
|
|
519
|
+
/** Switch team mode. Requires owner or founder role. */
|
|
520
|
+
async setTeamMode(mode, reason) {
|
|
521
|
+
return this.req("POST", `/team/${this.teamId}/mode`, { mode, reason });
|
|
522
|
+
}
|
|
523
|
+
/** Update room preferences. Requires config:write permission. */
|
|
524
|
+
async patchRoomPrefs(prefs) {
|
|
525
|
+
return this.req("PATCH", `/team/${this.teamId}/room`, prefs);
|
|
526
|
+
}
|
|
527
|
+
/** Update a board task. */
|
|
528
|
+
async updateTask(taskId, updates) {
|
|
529
|
+
return this.req("PATCH", `/team/${this.teamId}/board/${taskId}`, {
|
|
530
|
+
action: "update",
|
|
531
|
+
...updates
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
/** Delete a board task. */
|
|
535
|
+
async deleteTask(taskId) {
|
|
536
|
+
return this.req("PATCH", `/team/${this.teamId}/board/${taskId}`, {
|
|
537
|
+
action: "delete"
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
/** Delegate a board task to another agent. */
|
|
541
|
+
async delegateTask(taskId, toAgentId) {
|
|
542
|
+
return this.req("PATCH", `/team/${this.teamId}/board/${taskId}`, {
|
|
543
|
+
action: "delegate",
|
|
544
|
+
toAgentId
|
|
545
|
+
});
|
|
546
|
+
}
|
|
285
547
|
async req(method, path, body) {
|
|
286
548
|
const url = `${this.apiBase}${path}`;
|
|
287
549
|
const res = await this.fetchImpl(url, {
|
|
@@ -365,7 +627,8 @@ function buildCaelRecord(input) {
|
|
|
365
627
|
prev_hash: prevChain,
|
|
366
628
|
fnv1a_chain,
|
|
367
629
|
version_vector_fingerprint: `agent@${runtimeVersion}|brain@${brainClassOf(brain)}|provider@${identity.llmProvider}|model@${identity.llmModel}`,
|
|
368
|
-
brain_class: brainClassOf(brain)
|
|
630
|
+
brain_class: brainClassOf(brain),
|
|
631
|
+
trust_epoch: "post-w107"
|
|
369
632
|
};
|
|
370
633
|
}
|
|
371
634
|
|
|
@@ -385,11 +648,7 @@ var ALLOWED_WRITE_ROOTS = [
|
|
|
385
648
|
"/root/agent-output"
|
|
386
649
|
// Single write sink — keeps deliverables in one place
|
|
387
650
|
];
|
|
388
|
-
var
|
|
389
|
-
"lake build",
|
|
390
|
-
"lake env",
|
|
391
|
-
"lake clean",
|
|
392
|
-
"lean ",
|
|
651
|
+
var BASH_READ_ONLY_PREFIXES = [
|
|
393
652
|
"ls ",
|
|
394
653
|
"ls\n",
|
|
395
654
|
"ls$",
|
|
@@ -404,12 +663,24 @@ var BASH_WHITELIST = [
|
|
|
404
663
|
"git log",
|
|
405
664
|
"git diff",
|
|
406
665
|
"git show",
|
|
666
|
+
"pwd",
|
|
667
|
+
"echo ",
|
|
668
|
+
"lake env"
|
|
669
|
+
];
|
|
670
|
+
var BASH_PRODUCTIVE_PREFIXES = [
|
|
671
|
+
"lake build",
|
|
672
|
+
"lake clean",
|
|
673
|
+
"lean ",
|
|
407
674
|
"pnpm --filter",
|
|
408
675
|
"pnpm vitest",
|
|
409
|
-
"vitest run"
|
|
410
|
-
"pwd",
|
|
411
|
-
"echo "
|
|
676
|
+
"vitest run"
|
|
412
677
|
];
|
|
678
|
+
var BASH_WHITELIST = [...BASH_READ_ONLY_PREFIXES, ...BASH_PRODUCTIVE_PREFIXES];
|
|
679
|
+
function isProductiveBashCommand(cmd) {
|
|
680
|
+
const trimmed = String(cmd ?? "").trim();
|
|
681
|
+
if (!trimmed) return false;
|
|
682
|
+
return BASH_PRODUCTIVE_PREFIXES.some((prefix) => trimmed.startsWith(prefix.trim()));
|
|
683
|
+
}
|
|
413
684
|
var MESH_TOOLS = [
|
|
414
685
|
{
|
|
415
686
|
name: "read_file",
|
|
@@ -532,6 +803,13 @@ ${result.stderr || result.stdout}`);
|
|
|
532
803
|
}
|
|
533
804
|
}
|
|
534
805
|
function runBash(cmd, cwd) {
|
|
806
|
+
if (process.env.VITEST === "true" || process.env.NODE_ENV === "test") {
|
|
807
|
+
return Promise.resolve({
|
|
808
|
+
code: 0,
|
|
809
|
+
stdout: `[mock-bash under vitest] cmd="${cmd}" cwd="${cwd}"`,
|
|
810
|
+
stderr: ""
|
|
811
|
+
});
|
|
812
|
+
}
|
|
535
813
|
return new Promise((resolveProm) => {
|
|
536
814
|
const child = spawn("bash", ["-c", cmd], { cwd, env: process.env });
|
|
537
815
|
let stdout = "";
|
|
@@ -600,6 +878,35 @@ var AgentRunner = class {
|
|
|
600
878
|
const { identity, brain, mesh, costGuard, provider, logger } = this.opts;
|
|
601
879
|
const log = logger ?? (() => void 0);
|
|
602
880
|
await this.heartbeatWithAutoRejoin();
|
|
881
|
+
if (this.opts.messageHandler) {
|
|
882
|
+
try {
|
|
883
|
+
const receipts = await this.opts.messageHandler.processMessages();
|
|
884
|
+
if (receipts.length > 0) {
|
|
885
|
+
log({
|
|
886
|
+
ev: "messages-processed",
|
|
887
|
+
count: receipts.length,
|
|
888
|
+
statuses: receipts.map((r) => r.status)
|
|
889
|
+
});
|
|
890
|
+
if (brain.capabilityTags.length === 0 || brain.capabilityTags.every((t) => t.startsWith("delegated"))) {
|
|
891
|
+
return {
|
|
892
|
+
action: "messages-processed",
|
|
893
|
+
spentUsd: costGuard.getState().spentUsd,
|
|
894
|
+
remainingUsd: costGuard.getRemainingUsd(),
|
|
895
|
+
receipts: receipts.map((r) => ({
|
|
896
|
+
status: r.status,
|
|
897
|
+
action: r.action,
|
|
898
|
+
reason: r.reason
|
|
899
|
+
}))
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
} catch (err) {
|
|
904
|
+
log({
|
|
905
|
+
ev: "message-handler-error",
|
|
906
|
+
message: err instanceof Error ? err.message : String(err)
|
|
907
|
+
});
|
|
908
|
+
}
|
|
909
|
+
}
|
|
603
910
|
if (costGuard.isOverBudget()) {
|
|
604
911
|
const state = costGuard.getState();
|
|
605
912
|
log({ ev: "over-budget", spentUsd: state.spentUsd, budget: identity.budgetUsdPerDay });
|
|
@@ -633,6 +940,8 @@ var AgentRunner = class {
|
|
|
633
940
|
const MAX_TOOL_ITERS = 30;
|
|
634
941
|
let lastResponse;
|
|
635
942
|
const toolsCalled = /* @__PURE__ */ new Set();
|
|
943
|
+
let productiveCallCount = 0;
|
|
944
|
+
let lastCommitHash;
|
|
636
945
|
while (true) {
|
|
637
946
|
iters++;
|
|
638
947
|
if (iters > MAX_TOOL_ITERS) {
|
|
@@ -657,12 +966,31 @@ var AgentRunner = class {
|
|
|
657
966
|
};
|
|
658
967
|
if (resp.finishReason === "tool_use" && resp.toolUses && resp.toolUses.length > 0) {
|
|
659
968
|
log({ ev: "tool-call", taskId: target.id, iter: iters, tools: resp.toolUses.map((t) => t.name) });
|
|
660
|
-
for (const u of resp.toolUses)
|
|
969
|
+
for (const u of resp.toolUses) {
|
|
970
|
+
toolsCalled.add(u.name);
|
|
971
|
+
if (u.name === "write_file") {
|
|
972
|
+
const content = String(u.input?.content ?? "");
|
|
973
|
+
if (content.length > 0) productiveCallCount++;
|
|
974
|
+
} else if (u.name === "bash") {
|
|
975
|
+
const cmd = String(u.input?.cmd ?? "");
|
|
976
|
+
if (isProductiveBashCommand(cmd)) productiveCallCount++;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
661
979
|
messages.push({
|
|
662
980
|
role: "assistant",
|
|
663
981
|
content: resp.assistantBlocks ?? []
|
|
664
982
|
});
|
|
665
983
|
const toolResults = await Promise.all(resp.toolUses.map((u) => runTool(u)));
|
|
984
|
+
for (let ti = 0; ti < resp.toolUses.length; ti++) {
|
|
985
|
+
const tu = resp.toolUses[ti];
|
|
986
|
+
if (tu.name === "bash") {
|
|
987
|
+
const tr = toolResults[ti];
|
|
988
|
+
if (tr && !tr.is_error) {
|
|
989
|
+
const shaMatch = tr.content.match(/\b([0-9a-f]{7,40})\b/);
|
|
990
|
+
if (shaMatch) lastCommitHash = shaMatch[1];
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
}
|
|
666
994
|
messages.push({
|
|
667
995
|
role: "user",
|
|
668
996
|
content: toolResults
|
|
@@ -673,22 +1001,21 @@ var AgentRunner = class {
|
|
|
673
1001
|
break;
|
|
674
1002
|
}
|
|
675
1003
|
const durationMs = Date.now() - start;
|
|
676
|
-
|
|
677
|
-
const sideEffectingCalled = [...toolsCalled].some((t) => SIDE_EFFECTING_TOOLS.has(t));
|
|
678
|
-
if (!sideEffectingCalled) {
|
|
1004
|
+
if (productiveCallCount === 0) {
|
|
679
1005
|
log({
|
|
680
1006
|
ev: "no-artifact",
|
|
681
1007
|
taskId: target.id,
|
|
682
1008
|
tool_iters: iters,
|
|
683
1009
|
toolsCalled: [...toolsCalled],
|
|
684
|
-
|
|
1010
|
+
productiveCallCount,
|
|
1011
|
+
message: "task execution did not produce a real artifact \u2014 refusing to mark executed. Required: write_file with non-empty content OR bash with a productive prefix (lake build / pnpm --filter / vitest run / lean / pnpm vitest). Pure-text, read-only inspection, and trivial-bash-bypass (`echo`, `cat`, etc.) do not satisfy the gate."
|
|
685
1012
|
});
|
|
686
1013
|
return {
|
|
687
1014
|
action: "no-artifact",
|
|
688
1015
|
taskId: target.id,
|
|
689
1016
|
spentUsd: costGuard.getState().spentUsd,
|
|
690
1017
|
remainingUsd: costGuard.getRemainingUsd(),
|
|
691
|
-
message: `no
|
|
1018
|
+
message: `no productive tool call observed (toolsCalled=[${[...toolsCalled].join(",")}], productiveCallCount=${productiveCallCount}, iters=${iters})`
|
|
692
1019
|
};
|
|
693
1020
|
}
|
|
694
1021
|
const cost = costGuard.recordUsage(identity.llmModel, aggUsage);
|
|
@@ -748,6 +1075,12 @@ var AgentRunner = class {
|
|
|
748
1075
|
${response.content}`
|
|
749
1076
|
);
|
|
750
1077
|
}
|
|
1078
|
+
try {
|
|
1079
|
+
await mesh.markDone(target.id, finalText.slice(0, 500), lastCommitHash);
|
|
1080
|
+
log({ ev: "mark-done", taskId: target.id, commitHash: lastCommitHash });
|
|
1081
|
+
} catch (err) {
|
|
1082
|
+
log({ ev: "mark-done-error", taskId: target.id, message: err instanceof Error ? err.message : String(err) });
|
|
1083
|
+
}
|
|
751
1084
|
return {
|
|
752
1085
|
action: "executed",
|
|
753
1086
|
taskId: target.id,
|
|
@@ -1295,13 +1628,29 @@ var Supervisor = class {
|
|
|
1295
1628
|
return { ...managed.status };
|
|
1296
1629
|
}
|
|
1297
1630
|
async bootAgent(spec) {
|
|
1298
|
-
const identity = this.identityFromSpec(spec);
|
|
1299
1631
|
const brain = await loadBrain(spec.brainPath, spec.scopeTier ?? "warm");
|
|
1300
|
-
const
|
|
1632
|
+
const decision = pickProvider({
|
|
1633
|
+
brain,
|
|
1634
|
+
envOverride: spec.provider,
|
|
1635
|
+
candidates: BUILT_IN_CANDIDATES
|
|
1636
|
+
});
|
|
1637
|
+
const effectiveSpec = decision.picked === spec.provider ? spec : { ...spec, provider: decision.picked };
|
|
1638
|
+
const identity = this.identityFromSpec(effectiveSpec);
|
|
1639
|
+
if (decision.reason === "env-override-mismatch" && this.opts.logger) {
|
|
1640
|
+
this.opts.logger({
|
|
1641
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1642
|
+
ev: "capability-router-mismatch",
|
|
1643
|
+
handle: spec.handle,
|
|
1644
|
+
envOverride: spec.provider,
|
|
1645
|
+
unsatisfiedRequires: decision.unsatisfiedRequires,
|
|
1646
|
+
excludedByAvoids: decision.excludedByAvoids
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
const provider = await this.opts.providerFactory(effectiveSpec, identity);
|
|
1301
1650
|
const stateDir = this.opts.stateDir ?? join2(homedir(), ".holoscript-agent", "cost-state");
|
|
1302
|
-
const isFree =
|
|
1651
|
+
const isFree = effectiveSpec.provider === "mock" || effectiveSpec.provider === "local-llm" || effectiveSpec.provider === "bitnet";
|
|
1303
1652
|
const costGuard = new CostGuard({
|
|
1304
|
-
statePath: join2(stateDir, `${
|
|
1653
|
+
statePath: join2(stateDir, `${effectiveSpec.handle}.json`),
|
|
1305
1654
|
dailyBudgetUsd: identity.budgetUsdPerDay,
|
|
1306
1655
|
pricer: isFree ? () => 0 : void 0
|
|
1307
1656
|
});
|
|
@@ -1311,7 +1660,7 @@ var Supervisor = class {
|
|
|
1311
1660
|
teamId: identity.teamId,
|
|
1312
1661
|
fetchImpl: this.opts.fetchImpl
|
|
1313
1662
|
});
|
|
1314
|
-
const onTaskExecuted =
|
|
1663
|
+
const onTaskExecuted = effectiveSpec.enableCommitHook ? this.buildCommitHook(effectiveSpec, identity, mesh) : void 0;
|
|
1315
1664
|
const runner = new AgentRunner({
|
|
1316
1665
|
identity,
|
|
1317
1666
|
brain,
|
|
@@ -1320,16 +1669,16 @@ var Supervisor = class {
|
|
|
1320
1669
|
mesh,
|
|
1321
1670
|
onTaskExecuted,
|
|
1322
1671
|
auditLog: this.auditLog,
|
|
1323
|
-
logger: (ev) => this.log({ agent:
|
|
1672
|
+
logger: (ev) => this.log({ agent: effectiveSpec.handle, ...ev })
|
|
1324
1673
|
});
|
|
1325
1674
|
const status = {
|
|
1326
|
-
handle:
|
|
1675
|
+
handle: effectiveSpec.handle,
|
|
1327
1676
|
state: "starting",
|
|
1328
1677
|
spentUsd: 0,
|
|
1329
1678
|
remainingUsd: identity.budgetUsdPerDay,
|
|
1330
1679
|
restarts: 0
|
|
1331
1680
|
};
|
|
1332
|
-
return { spec, identity, brain, runner, costGuard, status };
|
|
1681
|
+
return { spec: effectiveSpec, identity, brain, runner, costGuard, status };
|
|
1333
1682
|
}
|
|
1334
1683
|
buildCommitHook(spec, identity, mesh) {
|
|
1335
1684
|
const writer = makeCommitHook({
|
|
@@ -1427,6 +1776,8 @@ var VALID_PROVIDERS2 = /* @__PURE__ */ new Set([
|
|
|
1427
1776
|
"anthropic",
|
|
1428
1777
|
"openai",
|
|
1429
1778
|
"gemini",
|
|
1779
|
+
"xai",
|
|
1780
|
+
"openrouter",
|
|
1430
1781
|
"mock",
|
|
1431
1782
|
"bitnet",
|
|
1432
1783
|
"local-llm"
|
|
@@ -1440,16 +1791,21 @@ function parseSupervisorConfig(raw) {
|
|
|
1440
1791
|
const data = JSON.parse(raw);
|
|
1441
1792
|
if (!isObject(data)) throw new Error("Supervisor config must be a JSON object");
|
|
1442
1793
|
if (!Array.isArray(data.agents)) throw new Error("Supervisor config.agents must be an array");
|
|
1443
|
-
if (data.agents.length === 0)
|
|
1794
|
+
if (data.agents.length === 0)
|
|
1795
|
+
throw new Error("Supervisor config.agents must have at least one entry");
|
|
1444
1796
|
const seenHandles = /* @__PURE__ */ new Set();
|
|
1445
|
-
const agents = data.agents.map(
|
|
1797
|
+
const agents = data.agents.map(
|
|
1798
|
+
(entry, idx) => validateAgent(entry, idx, seenHandles)
|
|
1799
|
+
);
|
|
1446
1800
|
const globalBudgetUsdPerDay = optionalNumber(data, "globalBudgetUsdPerDay");
|
|
1447
1801
|
const defaultTickIntervalMs = optionalNumber(data, "defaultTickIntervalMs");
|
|
1448
1802
|
if (globalBudgetUsdPerDay != null && globalBudgetUsdPerDay <= 0) {
|
|
1449
1803
|
throw new Error(`globalBudgetUsdPerDay must be positive, got ${globalBudgetUsdPerDay}`);
|
|
1450
1804
|
}
|
|
1451
1805
|
if (defaultTickIntervalMs != null && defaultTickIntervalMs < 5e3) {
|
|
1452
|
-
throw new Error(
|
|
1806
|
+
throw new Error(
|
|
1807
|
+
`defaultTickIntervalMs must be >= 5000ms (mesh-friendly), got ${defaultTickIntervalMs}`
|
|
1808
|
+
);
|
|
1453
1809
|
}
|
|
1454
1810
|
return { agents, globalBudgetUsdPerDay, defaultTickIntervalMs };
|
|
1455
1811
|
}
|
|
@@ -1472,8 +1828,8 @@ function validateAgent(entry, idx, seen) {
|
|
|
1472
1828
|
throw new Error(`agents[${idx}].scopeTier "${scopeTier}" must be cold | warm | hot`);
|
|
1473
1829
|
}
|
|
1474
1830
|
const budgetUsdPerDay = optionalNumber(entry, "budgetUsdPerDay");
|
|
1475
|
-
if (budgetUsdPerDay != null && budgetUsdPerDay
|
|
1476
|
-
throw new Error(`agents[${idx}].budgetUsdPerDay must be
|
|
1831
|
+
if (budgetUsdPerDay != null && budgetUsdPerDay < 0) {
|
|
1832
|
+
throw new Error(`agents[${idx}].budgetUsdPerDay must be >= 0 (0 = unlimited), got ${budgetUsdPerDay}`);
|
|
1477
1833
|
}
|
|
1478
1834
|
const tickIntervalMs = optionalNumber(entry, "tickIntervalMs");
|
|
1479
1835
|
if (tickIntervalMs != null && tickIntervalMs < 5e3) {
|
|
@@ -1763,11 +2119,28 @@ async function main() {
|
|
|
1763
2119
|
async function cmdRun(opts) {
|
|
1764
2120
|
const identity = loadIdentity();
|
|
1765
2121
|
const brain = await loadBrain(identity.brainPath, scopeTierFromEnv());
|
|
1766
|
-
const
|
|
2122
|
+
const decision = pickProvider({
|
|
2123
|
+
brain,
|
|
2124
|
+
envOverride: identity.llmProvider,
|
|
2125
|
+
candidates: BUILT_IN_CANDIDATES
|
|
2126
|
+
});
|
|
2127
|
+
const effectiveIdentity = decision.picked === identity.llmProvider ? identity : { ...identity, llmProvider: decision.picked };
|
|
2128
|
+
if (decision.reason === "env-override-mismatch") {
|
|
2129
|
+
console.log(
|
|
2130
|
+
JSON.stringify({
|
|
2131
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2132
|
+
ev: "capability-router-mismatch",
|
|
2133
|
+
envOverride: identity.llmProvider,
|
|
2134
|
+
unsatisfiedRequires: decision.unsatisfiedRequires,
|
|
2135
|
+
excludedByAvoids: decision.excludedByAvoids
|
|
2136
|
+
})
|
|
2137
|
+
);
|
|
2138
|
+
}
|
|
2139
|
+
const provider = await buildProvider(effectiveIdentity);
|
|
1767
2140
|
const costGuard = new CostGuard({
|
|
1768
2141
|
statePath: stateFilePath(identity),
|
|
1769
2142
|
dailyBudgetUsd: identity.budgetUsdPerDay,
|
|
1770
|
-
pricer: defaultPricerForProvider(
|
|
2143
|
+
pricer: defaultPricerForProvider(effectiveIdentity.llmProvider)
|
|
1771
2144
|
});
|
|
1772
2145
|
const mesh = new HolomeshClient({
|
|
1773
2146
|
apiBase: identity.meshApiBase,
|
|
@@ -1786,7 +2159,14 @@ async function cmdRun(opts) {
|
|
|
1786
2159
|
onTaskExecuted: commitHook,
|
|
1787
2160
|
auditLog
|
|
1788
2161
|
});
|
|
1789
|
-
console.log(
|
|
2162
|
+
console.log(
|
|
2163
|
+
JSON.stringify({
|
|
2164
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2165
|
+
ev: "boot",
|
|
2166
|
+
identity: identityForLog(identity),
|
|
2167
|
+
brain: { domain: brain.domain, tags: brain.capabilityTags, tier: brain.scopeTier }
|
|
2168
|
+
})
|
|
2169
|
+
);
|
|
1790
2170
|
if (opts.once) {
|
|
1791
2171
|
const result = await runner.tick();
|
|
1792
2172
|
console.log(JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ev: "tick-result", ...result }));
|
|
@@ -1811,6 +2191,10 @@ function supervisorProviderFactory() {
|
|
|
1811
2191
|
return createOpenAIProvider({ defaultModel: spec.model });
|
|
1812
2192
|
case "gemini":
|
|
1813
2193
|
return createGeminiProvider({ defaultModel: spec.model });
|
|
2194
|
+
case "xai":
|
|
2195
|
+
return createXAIProvider({ defaultModel: spec.model });
|
|
2196
|
+
case "openrouter":
|
|
2197
|
+
return createOpenRouterProvider({ defaultModel: spec.model });
|
|
1814
2198
|
case "local-llm":
|
|
1815
2199
|
return createLocalLLMProvider({
|
|
1816
2200
|
baseURL: process.env.HOLOSCRIPT_AGENT_LOCAL_LLM_BASE_URL,
|
|
@@ -1819,7 +2203,9 @@ function supervisorProviderFactory() {
|
|
|
1819
2203
|
case "mock":
|
|
1820
2204
|
return createMockProvider();
|
|
1821
2205
|
default:
|
|
1822
|
-
throw new Error(
|
|
2206
|
+
throw new Error(
|
|
2207
|
+
`Provider "${spec.provider}" not yet wired in supervisor \u2014 add a case here.`
|
|
2208
|
+
);
|
|
1823
2209
|
}
|
|
1824
2210
|
};
|
|
1825
2211
|
}
|
|
@@ -1846,11 +2232,15 @@ async function cmdSupervise(rest) {
|
|
|
1846
2232
|
process.on("SIGINT", onSig);
|
|
1847
2233
|
process.on("SIGTERM", onSig);
|
|
1848
2234
|
await sup.start();
|
|
1849
|
-
console.log(
|
|
2235
|
+
console.log(
|
|
2236
|
+
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ev: "supervise-running", config: cfgPath })
|
|
2237
|
+
);
|
|
1850
2238
|
const reportEvery = Number(process.env.HOLOSCRIPT_AGENT_STATUS_REPORT_MS ?? "300000");
|
|
1851
2239
|
if (reportEvery > 0) {
|
|
1852
2240
|
setInterval(() => {
|
|
1853
|
-
console.log(
|
|
2241
|
+
console.log(
|
|
2242
|
+
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ev: "supervisor-status", ...sup.status() })
|
|
2243
|
+
);
|
|
1854
2244
|
}, reportEvery);
|
|
1855
2245
|
}
|
|
1856
2246
|
}
|
|
@@ -1872,7 +2262,9 @@ async function cmdAudit(rest) {
|
|
|
1872
2262
|
const events = log.query(filter);
|
|
1873
2263
|
for (const e of events) console.log(JSON.stringify(e));
|
|
1874
2264
|
} else {
|
|
1875
|
-
throw new Error(
|
|
2265
|
+
throw new Error(
|
|
2266
|
+
"Usage: holoscript-agent audit [rollup|query|tail] [--agent=<h>] [--provider=<p>] [--task=<id>] [--kind=<k>] [--limit=<n>] [--log=<path>]"
|
|
2267
|
+
);
|
|
1876
2268
|
}
|
|
1877
2269
|
}
|
|
1878
2270
|
async function cmdProvision(rest) {
|
|
@@ -1884,7 +2276,9 @@ async function cmdProvision(rest) {
|
|
|
1884
2276
|
const force = rest.includes("--force");
|
|
1885
2277
|
const founderBearer = process.env.HOLOMESH_API_KEY;
|
|
1886
2278
|
if (!founderBearer) {
|
|
1887
|
-
throw new Error(
|
|
2279
|
+
throw new Error(
|
|
2280
|
+
"HOLOMESH_API_KEY env var required for provisioning (founder-tier bearer for /register endpoints)"
|
|
2281
|
+
);
|
|
1888
2282
|
}
|
|
1889
2283
|
const result = await provisionAgent(
|
|
1890
2284
|
{
|
|
@@ -1896,7 +2290,9 @@ async function cmdProvision(rest) {
|
|
|
1896
2290
|
},
|
|
1897
2291
|
{ execute, force }
|
|
1898
2292
|
);
|
|
1899
|
-
console.log(
|
|
2293
|
+
console.log(
|
|
2294
|
+
JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), ev: "provision-result", ...result }, null, 2)
|
|
2295
|
+
);
|
|
1900
2296
|
if (result.status === "executed" || result.status === "reused") {
|
|
1901
2297
|
console.log("\n# Add these lines to your .env to use this seat:");
|
|
1902
2298
|
for (const line of result.envVarLines) console.log(line);
|
|
@@ -1908,19 +2304,27 @@ async function cmdStatus(rest) {
|
|
|
1908
2304
|
throw new Error("Usage: holoscript-agent status --config=<path-to-agents.json>");
|
|
1909
2305
|
}
|
|
1910
2306
|
const config = loadSupervisorConfig(cfgPath);
|
|
1911
|
-
console.log(
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2307
|
+
console.log(
|
|
2308
|
+
JSON.stringify(
|
|
2309
|
+
{
|
|
2310
|
+
config: cfgPath,
|
|
2311
|
+
agentCount: config.agents.length,
|
|
2312
|
+
enabled: config.agents.filter((a) => a.enabled !== false).map((a) => a.handle),
|
|
2313
|
+
disabled: config.agents.filter((a) => a.enabled === false).map((a) => a.handle),
|
|
2314
|
+
globalBudgetUsdPerDay: config.globalBudgetUsdPerDay ?? null,
|
|
2315
|
+
defaultTickIntervalMs: config.defaultTickIntervalMs ?? null
|
|
2316
|
+
},
|
|
2317
|
+
null,
|
|
2318
|
+
2
|
|
2319
|
+
)
|
|
2320
|
+
);
|
|
1919
2321
|
}
|
|
1920
2322
|
async function cmdAblate(rest) {
|
|
1921
2323
|
const specPath = rest.find((a) => a.startsWith("--spec="))?.split("=")[1];
|
|
1922
2324
|
if (!specPath) {
|
|
1923
|
-
throw new Error(
|
|
2325
|
+
throw new Error(
|
|
2326
|
+
"Usage: holoscript-agent ablate --spec=<path-to-ablation.json> [--out-md=<path>] [--out-json=<path>]"
|
|
2327
|
+
);
|
|
1924
2328
|
}
|
|
1925
2329
|
const outMd = rest.find((a) => a.startsWith("--out-md="))?.split("=")[1];
|
|
1926
2330
|
const outJson = rest.find((a) => a.startsWith("--out-json="))?.split("=")[1];
|
|
@@ -1949,7 +2353,12 @@ async function cmdAblate(rest) {
|
|
|
1949
2353
|
},
|
|
1950
2354
|
pricer: p.pricePerCallUsd != null ? () => p.pricePerCallUsd : p.pricePerMtokInput != null && p.pricePerMtokOutput != null ? (u) => (u.promptTokens * p.pricePerMtokInput + u.completionTokens * p.pricePerMtokOutput) / 1e6 : void 0
|
|
1951
2355
|
}));
|
|
1952
|
-
const startMsg = JSON.stringify({
|
|
2356
|
+
const startMsg = JSON.stringify({
|
|
2357
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2358
|
+
ev: "ablation-start",
|
|
2359
|
+
task: spec.task.taskId,
|
|
2360
|
+
cells: providers.length
|
|
2361
|
+
});
|
|
1953
2362
|
console.log(startMsg);
|
|
1954
2363
|
const matrix = await runAblation({
|
|
1955
2364
|
task: spec.task,
|
|
@@ -1967,17 +2376,19 @@ async function cmdAblate(rest) {
|
|
|
1967
2376
|
if (!outMd && !outJson) {
|
|
1968
2377
|
console.log(renderAblationMarkdown(matrix));
|
|
1969
2378
|
}
|
|
1970
|
-
console.log(
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
2379
|
+
console.log(
|
|
2380
|
+
JSON.stringify({
|
|
2381
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2382
|
+
ev: "ablation-done",
|
|
2383
|
+
task: matrix.taskId,
|
|
2384
|
+
cells: matrix.cells.length,
|
|
2385
|
+
errors: matrix.cells.filter((c) => c.errorMessage).length,
|
|
2386
|
+
totalCostUsd: matrix.totalCostUsd,
|
|
2387
|
+
promptHash: matrix.promptHash,
|
|
2388
|
+
outMd: outMd ?? null,
|
|
2389
|
+
outJson: outJson ?? null
|
|
2390
|
+
})
|
|
2391
|
+
);
|
|
1981
2392
|
}
|
|
1982
2393
|
async function cmdWhoami() {
|
|
1983
2394
|
const identity = loadIdentity();
|
|
@@ -1998,6 +2409,10 @@ async function buildProvider(identity) {
|
|
|
1998
2409
|
return createOpenAIProvider({ defaultModel: identity.llmModel });
|
|
1999
2410
|
case "gemini":
|
|
2000
2411
|
return createGeminiProvider({ defaultModel: identity.llmModel });
|
|
2412
|
+
case "xai":
|
|
2413
|
+
return createXAIProvider({ defaultModel: identity.llmModel });
|
|
2414
|
+
case "openrouter":
|
|
2415
|
+
return createOpenRouterProvider({ defaultModel: identity.llmModel });
|
|
2001
2416
|
case "mock":
|
|
2002
2417
|
return createMockProvider();
|
|
2003
2418
|
case "local-llm":
|
|
@@ -2006,9 +2421,7 @@ async function buildProvider(identity) {
|
|
|
2006
2421
|
model: identity.llmModel
|
|
2007
2422
|
});
|
|
2008
2423
|
default:
|
|
2009
|
-
throw new Error(
|
|
2010
|
-
`Provider "${p}" not yet wired in CLI \u2014 Phase 2 deliverable. Use anthropic | openai | gemini | local-llm | mock for now.`
|
|
2011
|
-
);
|
|
2424
|
+
throw new Error(`Provider "${p}" not yet wired in CLI \u2014 add a case in buildProvider.`);
|
|
2012
2425
|
}
|
|
2013
2426
|
}
|
|
2014
2427
|
function buildCommitHook(identity, mesh) {
|
|
@@ -2095,7 +2508,13 @@ OPTIONAL ENV
|
|
|
2095
2508
|
`);
|
|
2096
2509
|
}
|
|
2097
2510
|
main().catch((err) => {
|
|
2098
|
-
console.error(
|
|
2511
|
+
console.error(
|
|
2512
|
+
JSON.stringify({
|
|
2513
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2514
|
+
ev: "fatal",
|
|
2515
|
+
message: err instanceof Error ? err.message : String(err)
|
|
2516
|
+
})
|
|
2517
|
+
);
|
|
2099
2518
|
process.exit(1);
|
|
2100
2519
|
});
|
|
2101
2520
|
//# sourceMappingURL=index.js.map
|