@holoscript/holoscript-agent 2.0.7 → 2.1.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/dist/holomesh-client.d.ts +9 -0
- package/dist/holomesh-client.js +28 -0
- package/dist/holomesh-client.js.map +1 -1
- package/dist/index.js +77 -15
- package/dist/index.js.map +1 -1
- package/dist/runner.js +39 -6
- package/dist/runner.js.map +1 -1
- package/dist/supervisor.js +73 -14
- package/dist/supervisor.js.map +1 -1
- package/package.json +1 -1
package/dist/supervisor.js
CHANGED
|
@@ -6,6 +6,8 @@ import { join as join2 } from "path";
|
|
|
6
6
|
import { embedAcrossFleet, cosineSimilarity } from "@holoscript/llm-provider";
|
|
7
7
|
|
|
8
8
|
// src/holomesh-client.ts
|
|
9
|
+
import { readFile, appendFile, mkdir } from "fs/promises";
|
|
10
|
+
import { dirname } from "path";
|
|
9
11
|
var HolomeshClient = class {
|
|
10
12
|
constructor(opts) {
|
|
11
13
|
this.apiBase = opts.apiBase.replace(/\/$/, "");
|
|
@@ -13,6 +15,7 @@ var HolomeshClient = class {
|
|
|
13
15
|
this.teamId = opts.teamId;
|
|
14
16
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
15
17
|
this.signer = opts.signer;
|
|
18
|
+
this.localKnowledgePath = opts.localKnowledgePath;
|
|
16
19
|
}
|
|
17
20
|
/** Wrap body in a signed envelope when a signer is available (strict-mode endpoints). */
|
|
18
21
|
async signBody(body) {
|
|
@@ -141,6 +144,14 @@ var HolomeshClient = class {
|
|
|
141
144
|
* client-side. Returns [] on any failure.
|
|
142
145
|
*/
|
|
143
146
|
async queryPrivateKnowledge() {
|
|
147
|
+
if (this.localKnowledgePath) {
|
|
148
|
+
try {
|
|
149
|
+
const raw = await readFile(this.localKnowledgePath, "utf8");
|
|
150
|
+
return raw.trim().split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
151
|
+
} catch {
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
}
|
|
144
155
|
try {
|
|
145
156
|
const data = await this.req("GET", `/knowledge/private`);
|
|
146
157
|
return data.entries ?? [];
|
|
@@ -178,6 +189,23 @@ var HolomeshClient = class {
|
|
|
178
189
|
}
|
|
179
190
|
async writePrivateKnowledge(entries) {
|
|
180
191
|
if (!entries.length) return false;
|
|
192
|
+
if (this.localKnowledgePath) {
|
|
193
|
+
try {
|
|
194
|
+
await mkdir(dirname(this.localKnowledgePath), { recursive: true });
|
|
195
|
+
const lines = entries.map((e) => JSON.stringify({
|
|
196
|
+
id: `local.${Date.now()}.${Math.random().toString(36).slice(2, 6)}`,
|
|
197
|
+
content: e.content,
|
|
198
|
+
type: e.type ?? "task-outcome",
|
|
199
|
+
tags: e.tags ?? [],
|
|
200
|
+
title: e.title,
|
|
201
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
202
|
+
})).join("\n") + "\n";
|
|
203
|
+
await appendFile(this.localKnowledgePath, lines, "utf8");
|
|
204
|
+
return true;
|
|
205
|
+
} catch {
|
|
206
|
+
return false;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
181
209
|
try {
|
|
182
210
|
await this.req("POST", `/knowledge/private`, { entries });
|
|
183
211
|
return true;
|
|
@@ -285,8 +313,8 @@ function buildCaelRecord(input) {
|
|
|
285
313
|
}
|
|
286
314
|
|
|
287
315
|
// src/tools.ts
|
|
288
|
-
import { readFile, writeFile, readdir, mkdir, stat } from "fs/promises";
|
|
289
|
-
import { resolve, dirname, delimiter, isAbsolute, sep } from "path";
|
|
316
|
+
import { readFile as readFile2, writeFile, readdir, mkdir as mkdir2, stat } from "fs/promises";
|
|
317
|
+
import { resolve, dirname as dirname2, delimiter, isAbsolute, sep } from "path";
|
|
290
318
|
import { spawn } from "child_process";
|
|
291
319
|
import { createHash as createHash2 } from "crypto";
|
|
292
320
|
var FLEET_READ_ROOTS = [
|
|
@@ -577,7 +605,7 @@ async function runTool(use, opts = {}) {
|
|
|
577
605
|
const path = use.input.path;
|
|
578
606
|
const denied = checkReadAllowed(path);
|
|
579
607
|
if (denied) return errResult(use.id, denied);
|
|
580
|
-
const text = await
|
|
608
|
+
const text = await readFile2(path, "utf8");
|
|
581
609
|
const truncated = text.length > 2e5 ? text.slice(0, 2e5) + `
|
|
582
610
|
\u2026[truncated, full file is ${text.length} bytes]` : text;
|
|
583
611
|
return okResult(use.id, truncated);
|
|
@@ -595,7 +623,7 @@ async function runTool(use, opts = {}) {
|
|
|
595
623
|
const content = use.input.content;
|
|
596
624
|
const denied = checkWriteAllowed(path);
|
|
597
625
|
if (denied) return errResult(use.id, denied);
|
|
598
|
-
await
|
|
626
|
+
await mkdir2(dirname2(path), { recursive: true });
|
|
599
627
|
await writeFile(path, content, "utf8");
|
|
600
628
|
const s = await stat(path);
|
|
601
629
|
return okResult(use.id, `wrote ${s.size} bytes to ${path}`);
|
|
@@ -710,7 +738,7 @@ ${truncated}`);
|
|
|
710
738
|
const outPath = resolve(ALLOWED_WRITE_ROOTS[0], `hardware-receipt-${ts}.json`);
|
|
711
739
|
const denied = checkWriteAllowed(outPath);
|
|
712
740
|
if (denied) return errResult(use.id, `Cannot write receipt: ${denied}`);
|
|
713
|
-
await
|
|
741
|
+
await mkdir2(dirname2(outPath), { recursive: true });
|
|
714
742
|
await writeFile(outPath, JSON.stringify(sealed, null, 2), "utf8");
|
|
715
743
|
return okResult(
|
|
716
744
|
use.id,
|
|
@@ -723,7 +751,7 @@ ${truncated}`);
|
|
|
723
751
|
const newStr = use.input.new;
|
|
724
752
|
const denied = checkWriteAllowed(path);
|
|
725
753
|
if (denied) return errResult(use.id, denied);
|
|
726
|
-
const text = await
|
|
754
|
+
const text = await readFile2(path, "utf8");
|
|
727
755
|
const count = text.split(oldStr).length - 1;
|
|
728
756
|
if (count === 0) return errResult(use.id, `str_replace: "old" string not found in ${path} \u2014 0 occurrences`);
|
|
729
757
|
if (count > 1) return errResult(use.id, `str_replace: "old" string is ambiguous in ${path} \u2014 ${count} occurrences; add more surrounding context`);
|
|
@@ -1158,6 +1186,37 @@ var AgentRunner = class {
|
|
|
1158
1186
|
finalText = resp.content;
|
|
1159
1187
|
break;
|
|
1160
1188
|
}
|
|
1189
|
+
if (productiveCallCount === 0 && toolsCalled.size > 0 && iters < MAX_TOOL_ITERS) {
|
|
1190
|
+
iters++;
|
|
1191
|
+
messages.push({
|
|
1192
|
+
role: "user",
|
|
1193
|
+
content: "You gathered data but did not write the task deliverable. Call write_file NOW with the exact output path from the task description. Embed all data you gathered into the write_file content field. Do NOT output text \u2014 your only valid response is a write_file tool call."
|
|
1194
|
+
});
|
|
1195
|
+
const reResp = await provider.complete(
|
|
1196
|
+
{ messages, maxTokens: 8192, temperature: 0.4, tools: activeTools },
|
|
1197
|
+
identity.llmModel
|
|
1198
|
+
);
|
|
1199
|
+
aggUsage = {
|
|
1200
|
+
promptTokens: aggUsage.promptTokens + reResp.usage.promptTokens,
|
|
1201
|
+
completionTokens: aggUsage.completionTokens + reResp.usage.completionTokens,
|
|
1202
|
+
totalTokens: aggUsage.totalTokens + reResp.usage.totalTokens
|
|
1203
|
+
};
|
|
1204
|
+
if (reResp.finishReason === "tool_use" && reResp.toolUses && reResp.toolUses.length > 0) {
|
|
1205
|
+
log({ ev: "reprompt-tool-call", taskId: target.id, iter: iters, tools: reResp.toolUses.map((t) => t.name) });
|
|
1206
|
+
const reProd = summarizeToolProductivity(reResp.toolUses);
|
|
1207
|
+
for (const n of reProd.names) toolsCalled.add(n);
|
|
1208
|
+
productiveCallCount += reProd.productiveCount;
|
|
1209
|
+
messages.push({ role: "assistant", content: reResp.assistantBlocks ?? [] });
|
|
1210
|
+
const reResults = await Promise.all(
|
|
1211
|
+
reResp.toolUses.map(
|
|
1212
|
+
(u) => runTool(u, { signReceipt: this.opts.signReceipt, addTask: (tasks2) => mesh.addTasks(tasks2) })
|
|
1213
|
+
)
|
|
1214
|
+
);
|
|
1215
|
+
messages.push({ role: "user", content: reResults });
|
|
1216
|
+
}
|
|
1217
|
+
finalText = reResp.content;
|
|
1218
|
+
lastResponse = reResp;
|
|
1219
|
+
}
|
|
1161
1220
|
const durationMs = Date.now() - start;
|
|
1162
1221
|
if (productiveCallCount === 0) {
|
|
1163
1222
|
log({
|
|
@@ -1433,7 +1492,7 @@ function jitter(base) {
|
|
|
1433
1492
|
|
|
1434
1493
|
// src/cost-guard.ts
|
|
1435
1494
|
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
1436
|
-
import { dirname as
|
|
1495
|
+
import { dirname as dirname3 } from "path";
|
|
1437
1496
|
var ANTHROPIC_PRICING_USD_PER_MTOK = {
|
|
1438
1497
|
"claude-opus-4-8": { input: 10, output: 50 },
|
|
1439
1498
|
// 3× cheaper than 4.7 on total cost; A-020 2026-06-08
|
|
@@ -1503,7 +1562,7 @@ var CostGuard = class {
|
|
|
1503
1562
|
return { date: todayUtc(), spentUsd: 0, promptTokens: 0, completionTokens: 0, callCount: 0 };
|
|
1504
1563
|
}
|
|
1505
1564
|
persist() {
|
|
1506
|
-
mkdirSync(
|
|
1565
|
+
mkdirSync(dirname3(this.statePath), { recursive: true });
|
|
1507
1566
|
writeFileSync(this.statePath, JSON.stringify(this.state, null, 2), "utf8");
|
|
1508
1567
|
}
|
|
1509
1568
|
};
|
|
@@ -1512,9 +1571,9 @@ function todayUtc() {
|
|
|
1512
1571
|
}
|
|
1513
1572
|
|
|
1514
1573
|
// src/brain.ts
|
|
1515
|
-
import { readFile as
|
|
1574
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
1516
1575
|
async function loadBrain(brainPath, scopeTier = "warm") {
|
|
1517
|
-
const raw = await
|
|
1576
|
+
const raw = await readFile3(brainPath, "utf8");
|
|
1518
1577
|
const { domain, capabilityTags, requires, prefers, avoids } = extractIdentity(raw);
|
|
1519
1578
|
const systemPrompt = extractSystemPromptPreamble(raw);
|
|
1520
1579
|
return {
|
|
@@ -1660,7 +1719,7 @@ function listField(block, key) {
|
|
|
1660
1719
|
|
|
1661
1720
|
// src/commit-hook.ts
|
|
1662
1721
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1663
|
-
import { dirname as
|
|
1722
|
+
import { dirname as dirname4, join, resolve as resolve2 } from "path";
|
|
1664
1723
|
import { spawnSync } from "child_process";
|
|
1665
1724
|
var SAFE_HANDLE = /^[a-z0-9_-]{1,64}$/i;
|
|
1666
1725
|
function makeCommitHook(opts) {
|
|
@@ -1680,7 +1739,7 @@ function makeCommitHook(opts) {
|
|
|
1680
1739
|
const safeTaskId = task.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 80);
|
|
1681
1740
|
const fileName = `${date}_${safeTaskId}_${identity.handle}.md`;
|
|
1682
1741
|
const filePath = join(outputDir, fileName);
|
|
1683
|
-
mkdirSync2(
|
|
1742
|
+
mkdirSync2(dirname4(filePath), { recursive: true });
|
|
1684
1743
|
writeFileSync2(filePath, renderMemo(result, task, identity, date), "utf8");
|
|
1685
1744
|
const relPath = relativeTo(cwd, filePath);
|
|
1686
1745
|
const addRes = spawn2("git", ["add", relPath], { cwd, encoding: "utf8" });
|
|
@@ -1764,12 +1823,12 @@ function relativeTo(base, target) {
|
|
|
1764
1823
|
|
|
1765
1824
|
// src/audit-log.ts
|
|
1766
1825
|
import { mkdirSync as mkdirSync3, appendFileSync, readFileSync as readFileSync2, existsSync as existsSync2, statSync, renameSync } from "fs";
|
|
1767
|
-
import { dirname as
|
|
1826
|
+
import { dirname as dirname5 } from "path";
|
|
1768
1827
|
var AuditLog = class {
|
|
1769
1828
|
constructor(opts) {
|
|
1770
1829
|
this.logPath = opts.logPath;
|
|
1771
1830
|
this.maxBytes = opts.maxBytes ?? 50 * 1024 * 1024;
|
|
1772
|
-
mkdirSync3(
|
|
1831
|
+
mkdirSync3(dirname5(this.logPath), { recursive: true });
|
|
1773
1832
|
}
|
|
1774
1833
|
record(event) {
|
|
1775
1834
|
this.rotateIfFull();
|