@holoscript/holoscript-agent 2.0.2 → 2.0.4
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 +0 -0
- package/dist/brain.js +49 -1
- package/dist/brain.js.map +1 -1
- package/dist/holomesh-client.d.ts +21 -1
- package/dist/holomesh-client.js +31 -0
- package/dist/holomesh-client.js.map +1 -1
- package/dist/identity.js +2 -2
- package/dist/identity.js.map +1 -1
- package/dist/index.js +301 -24
- package/dist/index.js.map +1 -1
- package/dist/runner.js +119 -1
- package/dist/runner.js.map +1 -1
- package/dist/supervisor.js +199 -2
- package/dist/supervisor.js.map +1 -1
- package/dist/types.d.ts +16 -1
- package/package.json +11 -11
- package/LICENSE +0 -21
package/dist/supervisor.js
CHANGED
|
@@ -109,6 +109,37 @@ var HolomeshClient = class {
|
|
|
109
109
|
async delegateTask(taskId, toAgentId) {
|
|
110
110
|
return this.req("PATCH", `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: "delegate", toAgentId }));
|
|
111
111
|
}
|
|
112
|
+
// ── Cognitive-verb knowledge surface (Phase 2.2 — recall / rag_query) ────────
|
|
113
|
+
/**
|
|
114
|
+
* Query the TEAM knowledge base (the `rag_query` cognitive verb). Bearer-only
|
|
115
|
+
* GET; `q` is the server-side search filter. Returns [] on any failure so a
|
|
116
|
+
* retrieval miss never breaks a tick.
|
|
117
|
+
*/
|
|
118
|
+
async queryTeamKnowledge(query, limit = 5) {
|
|
119
|
+
const qs = new URLSearchParams({ q: query, limit: String(limit) }).toString();
|
|
120
|
+
try {
|
|
121
|
+
const data = await this.req(
|
|
122
|
+
"GET",
|
|
123
|
+
`/team/${this.teamId}/knowledge?${qs}`
|
|
124
|
+
);
|
|
125
|
+
return data.entries ?? [];
|
|
126
|
+
} catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Query this agent's PRIVATE workspace knowledge (the `recall` cognitive verb).
|
|
132
|
+
* The endpoint has no server-side search param, so the caller filters by query
|
|
133
|
+
* client-side. Returns [] on any failure.
|
|
134
|
+
*/
|
|
135
|
+
async queryPrivateKnowledge() {
|
|
136
|
+
try {
|
|
137
|
+
const data = await this.req("GET", `/knowledge/private`);
|
|
138
|
+
return data.entries ?? [];
|
|
139
|
+
} catch {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
}
|
|
112
143
|
async req(method, path, body) {
|
|
113
144
|
const url = `${this.apiBase}${path}`;
|
|
114
145
|
const res = await this.fetchImpl(url, {
|
|
@@ -569,6 +600,109 @@ function errResult(id, message) {
|
|
|
569
600
|
return { type: "tool_result", tool_use_id: id, content: message, is_error: true };
|
|
570
601
|
}
|
|
571
602
|
|
|
603
|
+
// src/cognitive-verbs.ts
|
|
604
|
+
var DEFAULT_LIMIT = 5;
|
|
605
|
+
var MAX_ENTRY_CHARS = 320;
|
|
606
|
+
var MAX_INJECTED_CHARS = 2400;
|
|
607
|
+
function strField(config, ...keys) {
|
|
608
|
+
for (const k of keys) {
|
|
609
|
+
const v = config[k];
|
|
610
|
+
if (typeof v === "string" && v.trim()) return v.trim();
|
|
611
|
+
}
|
|
612
|
+
return "";
|
|
613
|
+
}
|
|
614
|
+
function numField(config, key, fallback) {
|
|
615
|
+
const v = config[key];
|
|
616
|
+
return typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
617
|
+
}
|
|
618
|
+
function formatEntries(entries) {
|
|
619
|
+
let out = "";
|
|
620
|
+
for (const e of entries) {
|
|
621
|
+
const line = `- ${(e.content ?? "").replace(/\s+/g, " ").trim().slice(0, MAX_ENTRY_CHARS)}`;
|
|
622
|
+
if (out.length + line.length > MAX_INJECTED_CHARS) break;
|
|
623
|
+
out += (out ? "\n" : "") + line;
|
|
624
|
+
}
|
|
625
|
+
return out;
|
|
626
|
+
}
|
|
627
|
+
async function augmentWithOnTaskCognition(deps) {
|
|
628
|
+
let content = deps.systemPrompt;
|
|
629
|
+
if (!deps.onTaskActions || deps.onTaskActions.length === 0) return content;
|
|
630
|
+
for (const action of deps.onTaskActions) {
|
|
631
|
+
try {
|
|
632
|
+
switch (action.verb) {
|
|
633
|
+
case "llm_call": {
|
|
634
|
+
const prompt = strField(action.config, "prompt");
|
|
635
|
+
if (prompt) {
|
|
636
|
+
content += `
|
|
637
|
+
|
|
638
|
+
[Brain on_task directive]
|
|
639
|
+
${prompt}`;
|
|
640
|
+
deps.log({ ev: "on-task-llm-call", taskId: deps.task.id, promptLen: prompt.length });
|
|
641
|
+
}
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
case "rag_query": {
|
|
645
|
+
const query = strField(action.config, "query", "q") || deps.task.title;
|
|
646
|
+
const limit = numField(action.config, "limit", DEFAULT_LIMIT);
|
|
647
|
+
const entries = await deps.queryTeamKnowledge(query, limit);
|
|
648
|
+
if (entries.length > 0) {
|
|
649
|
+
content += `
|
|
650
|
+
|
|
651
|
+
[Retrieved knowledge for "${query}"]
|
|
652
|
+
${formatEntries(entries)}`;
|
|
653
|
+
}
|
|
654
|
+
deps.log({ ev: "on-task-rag-query", taskId: deps.task.id, query, retrieved: entries.length });
|
|
655
|
+
break;
|
|
656
|
+
}
|
|
657
|
+
case "recall": {
|
|
658
|
+
const query = strField(action.config, "query", "q");
|
|
659
|
+
const limit = numField(action.config, "limit", DEFAULT_LIMIT);
|
|
660
|
+
const all = await deps.queryPrivateKnowledge();
|
|
661
|
+
const needle = query.toLowerCase();
|
|
662
|
+
const matched = (needle ? all.filter((e) => `${e.id ?? ""} ${e.content ?? ""}`.toLowerCase().includes(needle)) : all).slice(0, limit);
|
|
663
|
+
if (matched.length > 0) {
|
|
664
|
+
content += `
|
|
665
|
+
|
|
666
|
+
[Recalled memory${query ? ` for "${query}"` : ""}]
|
|
667
|
+
${formatEntries(matched)}`;
|
|
668
|
+
}
|
|
669
|
+
deps.log({ ev: "on-task-recall", taskId: deps.task.id, query, recalled: matched.length });
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
case "plan": {
|
|
673
|
+
if (!deps.plan) break;
|
|
674
|
+
const goal = strField(action.config, "goal", "prompt", "of") || deps.task.title;
|
|
675
|
+
const planText = await deps.plan(
|
|
676
|
+
`Produce a short numbered plan (max 6 steps) to accomplish this task. Be concrete and specific to the goal; do not execute it.
|
|
677
|
+
|
|
678
|
+
Goal: ${goal}`
|
|
679
|
+
);
|
|
680
|
+
const trimmed = planText.trim().slice(0, MAX_INJECTED_CHARS);
|
|
681
|
+
if (trimmed) {
|
|
682
|
+
content += `
|
|
683
|
+
|
|
684
|
+
[Plan]
|
|
685
|
+
${trimmed}`;
|
|
686
|
+
deps.log({ ev: "on-task-plan", taskId: deps.task.id, planLen: trimmed.length });
|
|
687
|
+
}
|
|
688
|
+
break;
|
|
689
|
+
}
|
|
690
|
+
// `reflect` is handled post-artifact in runner.ts, not here.
|
|
691
|
+
default:
|
|
692
|
+
break;
|
|
693
|
+
}
|
|
694
|
+
} catch (err) {
|
|
695
|
+
deps.log({
|
|
696
|
+
ev: "on-task-verb-error",
|
|
697
|
+
taskId: deps.task.id,
|
|
698
|
+
verb: action.verb,
|
|
699
|
+
message: err instanceof Error ? err.message : String(err)
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
return content;
|
|
704
|
+
}
|
|
705
|
+
|
|
572
706
|
// src/runner.ts
|
|
573
707
|
var RUNTIME_VERSION = "1.0.0";
|
|
574
708
|
var AgentRunner = class {
|
|
@@ -651,8 +785,23 @@ var AgentRunner = class {
|
|
|
651
785
|
log({ ev: "claim", taskId: target.id, title: target.title });
|
|
652
786
|
await mesh.claim(target.id);
|
|
653
787
|
const start = Date.now();
|
|
788
|
+
const systemContent = await augmentWithOnTaskCognition({
|
|
789
|
+
systemPrompt: brain.systemPrompt,
|
|
790
|
+
onTaskActions: brain.onTaskActions ?? [],
|
|
791
|
+
task: { id: target.id, title: target.title },
|
|
792
|
+
queryTeamKnowledge: (q, limit) => mesh.queryTeamKnowledge(q, limit),
|
|
793
|
+
queryPrivateKnowledge: () => mesh.queryPrivateKnowledge(),
|
|
794
|
+
plan: async (prompt) => {
|
|
795
|
+
const resp = await provider.complete(
|
|
796
|
+
{ messages: [{ role: "user", content: prompt }], maxTokens: 512, temperature: 0.3 },
|
|
797
|
+
identity.llmModel
|
|
798
|
+
);
|
|
799
|
+
return resp.content;
|
|
800
|
+
},
|
|
801
|
+
log
|
|
802
|
+
});
|
|
654
803
|
const messages = [
|
|
655
|
-
{ role: "system", content:
|
|
804
|
+
{ role: "system", content: systemContent },
|
|
656
805
|
{ role: "user", content: buildTaskPrompt(target) }
|
|
657
806
|
];
|
|
658
807
|
let aggUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
@@ -1092,7 +1241,8 @@ async function loadBrain(brainPath, scopeTier = "warm") {
|
|
|
1092
1241
|
requires,
|
|
1093
1242
|
prefers,
|
|
1094
1243
|
avoids,
|
|
1095
|
-
reflect: extractReflect(raw)
|
|
1244
|
+
reflect: extractReflect(raw),
|
|
1245
|
+
onTaskActions: extractOnTaskActions(raw)
|
|
1096
1246
|
};
|
|
1097
1247
|
}
|
|
1098
1248
|
function extractReflect(brain) {
|
|
@@ -1127,6 +1277,53 @@ function extractIdentity(brain) {
|
|
|
1127
1277
|
const avoids = listField(identityBlock, "avoids") ?? [];
|
|
1128
1278
|
return { domain, capabilityTags, requires, prefers, avoids };
|
|
1129
1279
|
}
|
|
1280
|
+
function extractOnTaskActions(brain) {
|
|
1281
|
+
const block = sliceNamedBlock(brain, "on_task");
|
|
1282
|
+
if (!block) return [];
|
|
1283
|
+
const VERBS = ["recall", "rag_query", "llm_call", "plan", "reflect"];
|
|
1284
|
+
const entries = [];
|
|
1285
|
+
for (const verb of VERBS) {
|
|
1286
|
+
const re = new RegExp(`\\b${verb}\\s*\\{`, "g");
|
|
1287
|
+
let m;
|
|
1288
|
+
while ((m = re.exec(block)) !== null) {
|
|
1289
|
+
const start = m.index + m[0].length;
|
|
1290
|
+
let depth = 1;
|
|
1291
|
+
let end = -1;
|
|
1292
|
+
for (let i = start; i < block.length; i++) {
|
|
1293
|
+
if (block[i] === "{") depth++;
|
|
1294
|
+
else if (block[i] === "}") {
|
|
1295
|
+
depth--;
|
|
1296
|
+
if (depth === 0) {
|
|
1297
|
+
end = i;
|
|
1298
|
+
break;
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
}
|
|
1302
|
+
if (end < 0) continue;
|
|
1303
|
+
entries.push({ verb, config: parseKVBlock(block.slice(start, end)), _pos: m.index });
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return entries.sort((a, b) => a._pos - b._pos).map(({ _pos: _ignored, ...rest }) => rest);
|
|
1307
|
+
}
|
|
1308
|
+
function parseKVBlock(block) {
|
|
1309
|
+
const out = {};
|
|
1310
|
+
const strRe = /\b(\w+)\s*:\s*"([^"]*)"/g;
|
|
1311
|
+
let m;
|
|
1312
|
+
while ((m = strRe.exec(block)) !== null) out[m[1]] = m[2];
|
|
1313
|
+
const arrRe = /\b(\w+)\s*:\s*\[([^\]]*)\]/g;
|
|
1314
|
+
while ((m = arrRe.exec(block)) !== null) {
|
|
1315
|
+
out[m[1]] = m[2].split(",").map((s) => s.trim().replace(/^["']|["']$/g, "")).filter((s) => s.length > 0);
|
|
1316
|
+
}
|
|
1317
|
+
const boolRe = /\b(\w+)\s*:\s*(true|false)\b/g;
|
|
1318
|
+
while ((m = boolRe.exec(block)) !== null) {
|
|
1319
|
+
if (!(m[1] in out)) out[m[1]] = m[2] === "true";
|
|
1320
|
+
}
|
|
1321
|
+
const numRe = /\b(\w+)\s*:\s*(-?\d+(?:\.\d+)?)\b/g;
|
|
1322
|
+
while ((m = numRe.exec(block)) !== null) {
|
|
1323
|
+
if (!(m[1] in out)) out[m[1]] = parseFloat(m[2]);
|
|
1324
|
+
}
|
|
1325
|
+
return out;
|
|
1326
|
+
}
|
|
1130
1327
|
function sliceNamedBlock(src, name) {
|
|
1131
1328
|
const re = new RegExp(`\\b${name}\\s*:?\\s*\\{`, "g");
|
|
1132
1329
|
const match = re.exec(src);
|