@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
|
@@ -36,6 +36,14 @@ interface HolomeshClientOptions {
|
|
|
36
36
|
fetchImpl?: typeof fetch;
|
|
37
37
|
/** EIP-191 signing function. When present, used on strict endpoints like joinTeam(). */
|
|
38
38
|
signer?: RequestSigner;
|
|
39
|
+
/**
|
|
40
|
+
* Local JSONL path for private knowledge (sovereign edge store).
|
|
41
|
+
* When set, writePrivateKnowledge appends to this file and queryPrivateKnowledge
|
|
42
|
+
* reads from it instead of routing through the remote orchestrator.
|
|
43
|
+
* Bypasses the mcp-orchestrator /knowledge/sync 401 gap for edge nodes.
|
|
44
|
+
* Set via HOLOSCRIPT_AGENT_LOCAL_KNOWLEDGE_PATH env var (index.ts).
|
|
45
|
+
*/
|
|
46
|
+
localKnowledgePath?: string;
|
|
39
47
|
}
|
|
40
48
|
interface TeamMessage {
|
|
41
49
|
id: string;
|
|
@@ -59,6 +67,7 @@ declare class HolomeshClient {
|
|
|
59
67
|
private readonly teamId;
|
|
60
68
|
private readonly fetchImpl;
|
|
61
69
|
private readonly signer?;
|
|
70
|
+
private readonly localKnowledgePath?;
|
|
62
71
|
constructor(opts: HolomeshClientOptions);
|
|
63
72
|
/** Wrap body in a signed envelope when a signer is available (strict-mode endpoints). */
|
|
64
73
|
private signBody;
|
package/dist/holomesh-client.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
// src/holomesh-client.ts
|
|
2
|
+
import { readFile, appendFile, mkdir } from "fs/promises";
|
|
3
|
+
import { dirname } from "path";
|
|
2
4
|
var HolomeshClient = class {
|
|
3
5
|
constructor(opts) {
|
|
4
6
|
this.apiBase = opts.apiBase.replace(/\/$/, "");
|
|
@@ -6,6 +8,7 @@ var HolomeshClient = class {
|
|
|
6
8
|
this.teamId = opts.teamId;
|
|
7
9
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
8
10
|
this.signer = opts.signer;
|
|
11
|
+
this.localKnowledgePath = opts.localKnowledgePath;
|
|
9
12
|
}
|
|
10
13
|
/** Wrap body in a signed envelope when a signer is available (strict-mode endpoints). */
|
|
11
14
|
async signBody(body) {
|
|
@@ -134,6 +137,14 @@ var HolomeshClient = class {
|
|
|
134
137
|
* client-side. Returns [] on any failure.
|
|
135
138
|
*/
|
|
136
139
|
async queryPrivateKnowledge() {
|
|
140
|
+
if (this.localKnowledgePath) {
|
|
141
|
+
try {
|
|
142
|
+
const raw = await readFile(this.localKnowledgePath, "utf8");
|
|
143
|
+
return raw.trim().split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
144
|
+
} catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
137
148
|
try {
|
|
138
149
|
const data = await this.req("GET", `/knowledge/private`);
|
|
139
150
|
return data.entries ?? [];
|
|
@@ -171,6 +182,23 @@ var HolomeshClient = class {
|
|
|
171
182
|
}
|
|
172
183
|
async writePrivateKnowledge(entries) {
|
|
173
184
|
if (!entries.length) return false;
|
|
185
|
+
if (this.localKnowledgePath) {
|
|
186
|
+
try {
|
|
187
|
+
await mkdir(dirname(this.localKnowledgePath), { recursive: true });
|
|
188
|
+
const lines = entries.map((e) => JSON.stringify({
|
|
189
|
+
id: `local.${Date.now()}.${Math.random().toString(36).slice(2, 6)}`,
|
|
190
|
+
content: e.content,
|
|
191
|
+
type: e.type ?? "task-outcome",
|
|
192
|
+
tags: e.tags ?? [],
|
|
193
|
+
title: e.title,
|
|
194
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
195
|
+
})).join("\n") + "\n";
|
|
196
|
+
await appendFile(this.localKnowledgePath, lines, "utf8");
|
|
197
|
+
return true;
|
|
198
|
+
} catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
174
202
|
try {
|
|
175
203
|
await this.req("POST", `/knowledge/private`, { entries });
|
|
176
204
|
return true;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/holomesh-client.ts"],"sourcesContent":["import type { BoardTask } from './types.js';\nimport type { CaelAuditRecord } from './cael-builder.js';\n\n/** Wraps a request body in a signed envelope for strict-mode endpoints (e.g. /team/:id/join). */\nexport type RequestSigner = (\n body: Record<string, unknown>\n) => Promise<Record<string, unknown>>;\n\nexport interface HolomeshClientOptions {\n apiBase: string;\n bearer: string;\n teamId: string;\n fetchImpl?: typeof fetch;\n /** EIP-191 signing function. When present, used on strict endpoints like joinTeam(). */\n signer?: RequestSigner;\n}\n\nexport interface TeamMessage {\n id: string;\n fromAgentId: string;\n fromAgentName: string;\n content: string;\n messageType: string;\n createdAt: string;\n}\n\n/** Minimal knowledge-entry shape returned by the mesh knowledge endpoints. */\nexport interface KnowledgeEntry {\n id: string;\n content: string;\n domain?: string;\n type?: string;\n createdAt?: string;\n}\n\nexport class HolomeshClient {\n private readonly apiBase: string;\n private readonly bearer: string;\n private readonly teamId: string;\n private readonly fetchImpl: typeof fetch;\n private readonly signer?: RequestSigner;\n\n constructor(opts: HolomeshClientOptions) {\n this.apiBase = opts.apiBase.replace(/\\/$/, '');\n this.bearer = opts.bearer;\n this.teamId = opts.teamId;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n this.signer = opts.signer;\n }\n\n /** Wrap body in a signed envelope when a signer is available (strict-mode endpoints). */\n private async signBody(body: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.signer ? await this.signer(body) : body;\n }\n\n async heartbeat(payload: { agentName: string; surface: string; capabilityTags?: string[] }): Promise<void> {\n await this.req('POST', `/team/${this.teamId}/presence`, await this.signBody(payload as Record<string, unknown>));\n }\n\n async getOpenTasks(): Promise<BoardTask[]> {\n const data = await this.req<{ tasks?: BoardTask[]; open?: BoardTask[] }>(\n 'GET',\n `/team/${this.teamId}/board`\n );\n return data.tasks ?? data.open ?? [];\n }\n\n async claim(taskId: string): Promise<BoardTask> {\n return this.req<BoardTask>('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'claim' }));\n }\n\n async joinTeam(): Promise<{ success: boolean; role?: string; members?: number }> {\n return this.req<{ success: boolean; role?: string; members?: number }>(\n 'POST',\n `/team/${this.teamId}/join`,\n await this.signBody({})\n );\n }\n\n async sendMessageOnTask(taskId: string, body: string): Promise<void> {\n await this.req('POST', `/team/${this.teamId}/message`, await this.signBody({\n to: 'team',\n subject: `task:${taskId}`,\n content: body,\n }));\n }\n\n async markDone(taskId: string, summary: string, commitHash?: string): Promise<void> {\n await this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({\n action: 'done',\n summary,\n // verification_evidence required by server before task can be closed.\n verification_evidence: summary,\n // Exclude commitHash when undefined — JSON.stringify drops undefined but\n // canonicalizeSigning preserves it as the literal string \"undefined\",\n // causing a signature-mismatch vs what the server sees after JSON.parse.\n ...(commitHash !== undefined ? { commitHash } : {}),\n }));\n }\n\n // POST CAEL audit records for this agent. Server validator at\n // packages/mcp-server/src/holomesh/routes/core-routes.ts:472-533 requires\n // bearer == handle owner OR founder; the per-surface x402 bearer is the\n // handle owner so this resolves correctly. Records that fail shape\n // validation (layer_hashes != 7 elements, missing tick_iso/operation/\n // fnv1a_chain) are silently dropped server-side, not rejected as a batch.\n async postAuditRecords(\n handle: string,\n records: CaelAuditRecord[]\n ): Promise<{ appended: number; rejected: number }> {\n // Audit endpoint uses bearer-only auth — no signed envelope wrapper.\n return this.req<{ appended: number; rejected: number }>(\n 'POST',\n `/agent/${encodeURIComponent(handle)}/audit`,\n { records } as unknown as Record<string, unknown>\n );\n }\n\n async whoAmI(): Promise<{ agentId: string; surface: string; wallet?: string }> {\n // GET /api/holomesh/me returns { agentId, name, wallet, isFounder, teamId, teams, permissions }\n // (see packages/mcp-server/src/holomesh/routes/core-routes.ts §/me handler).\n // It does NOT return a `surface` field — derive it from the seat name on the\n // client side. Seat naming convention (set by the provisioning admin path):\n // claudecode-claude-x402 → claude-code\n // cursor-claude-x402 → claude-cursor\n // gemini-antigravity → gemini-antigravity\n // copilot-vscode → copilot-vscode\n // Founder → unknown (shared key, no surface attribution)\n const raw = await this.req<{\n agentId: string;\n name?: string;\n wallet?: string;\n }>('GET', '/me');\n return {\n agentId: raw.agentId,\n surface: deriveSurface(raw.name),\n wallet: raw.wallet,\n };\n }\n\n // ── Team Message Surface (E4 delegated-authority protocol) ───────────────────\n\n /** Read recent team messages. */\n async getTeamMessages(limit = 20): Promise<TeamMessage[]> {\n const data = await this.req<{ messages?: TeamMessage[]; success?: boolean }>(\n 'GET',\n `/team/${this.teamId}/messages?limit=${limit}`\n );\n return data.messages ?? [];\n }\n\n /** Post a message to the team feed. */\n async sendTeamMessage(content: string, messageType = 'text'): Promise<void> {\n await this.req('POST', `/team/${this.teamId}/message`, await this.signBody({ content, type: messageType }));\n }\n\n // ── Owner-op API wrappers (E4) ─────────────────────────────────────────────\n\n /** Switch team mode. Requires owner or founder role. */\n async setTeamMode(mode: string, reason?: string): Promise<{ mode: string; unchanged?: boolean }> {\n return this.req('POST', `/team/${this.teamId}/mode`, await this.signBody({ mode, reason } as Record<string, unknown>));\n }\n\n /** Update room preferences. Requires config:write permission. */\n async patchRoomPrefs(prefs: { communicationStyle?: string; objective?: string }): Promise<{\n communicationStyle: string;\n objective: string;\n }> {\n return this.req('PATCH', `/team/${this.teamId}/room`, await this.signBody(prefs as Record<string, unknown>));\n }\n\n /** Update a board task. */\n async updateTask(\n taskId: string,\n updates: {\n title?: string;\n description?: string;\n priority?: number;\n tags?: string[];\n }\n ): Promise<unknown> {\n return this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'update', ...updates } as Record<string, unknown>));\n }\n\n /** Delete a board task. */\n async deleteTask(taskId: string): Promise<unknown> {\n return this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'delete' }));\n }\n\n /** Delegate a board task to another agent. */\n async delegateTask(taskId: string, toAgentId: string): Promise<unknown> {\n return this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'delegate', toAgentId }));\n }\n\n /** Add new tasks to the board (used by the delegate_task tool to spawn sub-work). */\n async addTasks(\n tasks: Array<{ title: string; description?: string; priority?: number; source?: string; role?: string; tags?: string[] }>\n ): Promise<{ added: number }> {\n const result = await this.req<{ added?: number }>('POST', `/team/${this.teamId}/board`, await this.signBody({ tasks } as Record<string, unknown>));\n return { added: result.added ?? tasks.length };\n }\n\n // ── Cognitive-verb knowledge surface (Phase 2.2 — recall / rag_query) ────────\n\n /**\n * Query the TEAM knowledge base (the `rag_query` cognitive verb). Bearer-only\n * GET; `q` is the server-side search filter. Returns [] on any failure so a\n * retrieval miss never breaks a tick.\n */\n async queryTeamKnowledge(query: string, limit = 5): Promise<KnowledgeEntry[]> {\n const qs = new URLSearchParams({ q: query, limit: String(limit) }).toString();\n try {\n const data = await this.req<{ entries?: KnowledgeEntry[] }>(\n 'GET',\n `/team/${this.teamId}/knowledge?${qs}`\n );\n return data.entries ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * Query this agent's PRIVATE workspace knowledge (the `recall` cognitive verb).\n * The endpoint has no server-side search param, so the caller filters by query\n * client-side. Returns [] on any failure.\n */\n async queryPrivateKnowledge(): Promise<KnowledgeEntry[]> {\n try {\n const data = await this.req<{ entries?: KnowledgeEntry[] }>('GET', `/knowledge/private`);\n return data.entries ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * Persist facts to this agent's PRIVATE workspace — the WRITE side of `recall`\n * (POST /knowledge/private). The server stamps id / workspaceId (`private:<wallet>`)\n * / author / timestamps; the caller supplies `content` (+ optional type/tags/title).\n * This is what gives `recall` something to recall: without it the private store\n * stays empty and every `recall` returns [] (the W.752 loop-gap). Best-effort —\n * returns false on any failure so a write miss never breaks a tick (the task is\n * already done by the time this runs).\n */\n /**\n * Query the server-side in-process codebase GraphRAG index via the mesh bearer-gated\n * route (POST /api/holomesh/codebase/search). Called by the `rag_query` cognitive verb\n * (Phase 2.3 W.753) so edge agents can reach HoloEmbed semantic search without a direct\n * Absorb-service dep. Returns [] (not an error) when the graph isn't loaded yet — the\n * caller falls back to team-knowledge search.\n *\n * @param query Natural-language search string.\n * @param topK Max results to return (1–20, server-capped).\n */\n async queryCodebase(query: string, topK = 8): Promise<Array<{ name: string; file: string; line?: number; type: string; score: number; signature?: string | null }>> {\n try {\n const data = await this.req<{\n success?: boolean;\n result?: {\n results?: Array<{ name: string; file: string; line?: number; type: string; score: number; signature?: string | null }>;\n error?: string;\n };\n error?: string;\n }>('POST', `/codebase/search`, { query, topK });\n if (data.error || data.result?.error) return [];\n return data.result?.results ?? [];\n } catch {\n return [];\n }\n }\n\n async writePrivateKnowledge(\n entries: Array<{ content: string; type?: string; tags?: string[]; title?: string }>\n ): Promise<boolean> {\n if (!entries.length) return false;\n try {\n await this.req('POST', `/knowledge/private`, { entries });\n return true;\n } catch {\n return false;\n }\n }\n\n private async req<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.apiBase}${path}`;\n // HoloMesh REST auth: server (packages/mcp-server/src/holomesh/auth-utils.ts\n // resolveRequestingAgent) accepts EITHER `Authorization: Bearer <token>`\n // (HTTP-standard, used here) OR `x-mcp-api-key: <token>` (orchestrator\n // convention). Both resolve through the same key-registry / agent-store /\n // env-fallback chain. Bearer is preferred for new code (W.087 vertex B,\n // task_1777073616424_klls).\n const res = await this.fetchImpl(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.bearer}`,\n 'content-type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n const txt = await res.text().catch(() => '');\n throw new Error(`HoloMesh ${method} ${path} ${res.status}: ${txt.slice(0, 300)}`);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n}\n\n/**\n * Derive a surface tag from a seat name returned by /me. Mirrors the surface\n * detection in scripts/probe-surface-bearers.mjs and hooks/lib/holomesh-env.mjs\n * so a single agent's surface attribution is consistent across read and write\n * paths. Returns 'unknown' when the seat name doesn't encode a surface\n * (e.g. shared-key resolution to \"Founder\").\n */\nexport function deriveSurface(seatName: string | undefined): string {\n if (!seatName) return 'unknown';\n const n = seatName.toLowerCase();\n if (n.startsWith('claudecode')) return 'claude-code';\n if (n.startsWith('cursor')) return 'claude-cursor';\n if (n.startsWith('claudedesktop')) return 'claude-desktop';\n if (n.startsWith('vscode-claude') || n.startsWith('claude-vscode')) return 'claude-vscode';\n if (n.startsWith('gemini')) return 'gemini-antigravity';\n if (n.startsWith('copilot')) return 'copilot-vscode';\n return 'unknown';\n}\n\nexport function pickClaimableTask(\n tasks: BoardTask[],\n brainCapabilityTags: string[]\n): BoardTask | undefined {\n const wanted = new Set(brainCapabilityTags.map((t) => t.toLowerCase()));\n const open = tasks.filter((t) => t.status === 'open' && !t.claimedBy);\n const scored = open\n .map((t) => ({ task: t, score: scoreTask(t, wanted) }))\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score || priority(a.task) - priority(b.task));\n return scored[0]?.task;\n}\n\nfunction scoreTask(task: BoardTask, wanted: Set<string>): number {\n const tags = (task.tags ?? []).map((t) => t.toLowerCase());\n const text = `${task.title} ${task.description ?? ''}`.toLowerCase();\n let score = 0;\n for (const tag of tags) if (wanted.has(tag)) score += 2;\n for (const w of wanted) if (text.includes(w)) score += 1;\n return score;\n}\n\nfunction priority(t: BoardTask): number {\n if (typeof t.priority === 'number') return t.priority;\n const map: Record<string, number> = { critical: 1, high: 2, medium: 4, low: 6 };\n return map[String(t.priority).toLowerCase()] ?? 5;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/holomesh-client.ts"],"sourcesContent":["import { readFile, appendFile, mkdir } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport type { BoardTask } from './types.js';\nimport type { CaelAuditRecord } from './cael-builder.js';\n\n/** Wraps a request body in a signed envelope for strict-mode endpoints (e.g. /team/:id/join). */\nexport type RequestSigner = (\n body: Record<string, unknown>\n) => Promise<Record<string, unknown>>;\n\nexport interface HolomeshClientOptions {\n apiBase: string;\n bearer: string;\n teamId: string;\n fetchImpl?: typeof fetch;\n /** EIP-191 signing function. When present, used on strict endpoints like joinTeam(). */\n signer?: RequestSigner;\n /**\n * Local JSONL path for private knowledge (sovereign edge store).\n * When set, writePrivateKnowledge appends to this file and queryPrivateKnowledge\n * reads from it instead of routing through the remote orchestrator.\n * Bypasses the mcp-orchestrator /knowledge/sync 401 gap for edge nodes.\n * Set via HOLOSCRIPT_AGENT_LOCAL_KNOWLEDGE_PATH env var (index.ts).\n */\n localKnowledgePath?: string;\n}\n\nexport interface TeamMessage {\n id: string;\n fromAgentId: string;\n fromAgentName: string;\n content: string;\n messageType: string;\n createdAt: string;\n}\n\n/** Minimal knowledge-entry shape returned by the mesh knowledge endpoints. */\nexport interface KnowledgeEntry {\n id: string;\n content: string;\n domain?: string;\n type?: string;\n createdAt?: string;\n}\n\nexport class HolomeshClient {\n private readonly apiBase: string;\n private readonly bearer: string;\n private readonly teamId: string;\n private readonly fetchImpl: typeof fetch;\n private readonly signer?: RequestSigner;\n private readonly localKnowledgePath?: string;\n\n constructor(opts: HolomeshClientOptions) {\n this.apiBase = opts.apiBase.replace(/\\/$/, '');\n this.bearer = opts.bearer;\n this.teamId = opts.teamId;\n this.fetchImpl = opts.fetchImpl ?? fetch;\n this.signer = opts.signer;\n this.localKnowledgePath = opts.localKnowledgePath;\n }\n\n /** Wrap body in a signed envelope when a signer is available (strict-mode endpoints). */\n private async signBody(body: Record<string, unknown>): Promise<Record<string, unknown>> {\n return this.signer ? await this.signer(body) : body;\n }\n\n async heartbeat(payload: { agentName: string; surface: string; capabilityTags?: string[] }): Promise<void> {\n await this.req('POST', `/team/${this.teamId}/presence`, await this.signBody(payload as Record<string, unknown>));\n }\n\n async getOpenTasks(): Promise<BoardTask[]> {\n const data = await this.req<{ tasks?: BoardTask[]; open?: BoardTask[] }>(\n 'GET',\n `/team/${this.teamId}/board`\n );\n return data.tasks ?? data.open ?? [];\n }\n\n async claim(taskId: string): Promise<BoardTask> {\n return this.req<BoardTask>('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'claim' }));\n }\n\n async joinTeam(): Promise<{ success: boolean; role?: string; members?: number }> {\n return this.req<{ success: boolean; role?: string; members?: number }>(\n 'POST',\n `/team/${this.teamId}/join`,\n await this.signBody({})\n );\n }\n\n async sendMessageOnTask(taskId: string, body: string): Promise<void> {\n await this.req('POST', `/team/${this.teamId}/message`, await this.signBody({\n to: 'team',\n subject: `task:${taskId}`,\n content: body,\n }));\n }\n\n async markDone(taskId: string, summary: string, commitHash?: string): Promise<void> {\n await this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({\n action: 'done',\n summary,\n // verification_evidence required by server before task can be closed.\n verification_evidence: summary,\n // Exclude commitHash when undefined — JSON.stringify drops undefined but\n // canonicalizeSigning preserves it as the literal string \"undefined\",\n // causing a signature-mismatch vs what the server sees after JSON.parse.\n ...(commitHash !== undefined ? { commitHash } : {}),\n }));\n }\n\n // POST CAEL audit records for this agent. Server validator at\n // packages/mcp-server/src/holomesh/routes/core-routes.ts:472-533 requires\n // bearer == handle owner OR founder; the per-surface x402 bearer is the\n // handle owner so this resolves correctly. Records that fail shape\n // validation (layer_hashes != 7 elements, missing tick_iso/operation/\n // fnv1a_chain) are silently dropped server-side, not rejected as a batch.\n async postAuditRecords(\n handle: string,\n records: CaelAuditRecord[]\n ): Promise<{ appended: number; rejected: number }> {\n // Audit endpoint uses bearer-only auth — no signed envelope wrapper.\n return this.req<{ appended: number; rejected: number }>(\n 'POST',\n `/agent/${encodeURIComponent(handle)}/audit`,\n { records } as unknown as Record<string, unknown>\n );\n }\n\n async whoAmI(): Promise<{ agentId: string; surface: string; wallet?: string }> {\n // GET /api/holomesh/me returns { agentId, name, wallet, isFounder, teamId, teams, permissions }\n // (see packages/mcp-server/src/holomesh/routes/core-routes.ts §/me handler).\n // It does NOT return a `surface` field — derive it from the seat name on the\n // client side. Seat naming convention (set by the provisioning admin path):\n // claudecode-claude-x402 → claude-code\n // cursor-claude-x402 → claude-cursor\n // gemini-antigravity → gemini-antigravity\n // copilot-vscode → copilot-vscode\n // Founder → unknown (shared key, no surface attribution)\n const raw = await this.req<{\n agentId: string;\n name?: string;\n wallet?: string;\n }>('GET', '/me');\n return {\n agentId: raw.agentId,\n surface: deriveSurface(raw.name),\n wallet: raw.wallet,\n };\n }\n\n // ── Team Message Surface (E4 delegated-authority protocol) ───────────────────\n\n /** Read recent team messages. */\n async getTeamMessages(limit = 20): Promise<TeamMessage[]> {\n const data = await this.req<{ messages?: TeamMessage[]; success?: boolean }>(\n 'GET',\n `/team/${this.teamId}/messages?limit=${limit}`\n );\n return data.messages ?? [];\n }\n\n /** Post a message to the team feed. */\n async sendTeamMessage(content: string, messageType = 'text'): Promise<void> {\n await this.req('POST', `/team/${this.teamId}/message`, await this.signBody({ content, type: messageType }));\n }\n\n // ── Owner-op API wrappers (E4) ─────────────────────────────────────────────\n\n /** Switch team mode. Requires owner or founder role. */\n async setTeamMode(mode: string, reason?: string): Promise<{ mode: string; unchanged?: boolean }> {\n return this.req('POST', `/team/${this.teamId}/mode`, await this.signBody({ mode, reason } as Record<string, unknown>));\n }\n\n /** Update room preferences. Requires config:write permission. */\n async patchRoomPrefs(prefs: { communicationStyle?: string; objective?: string }): Promise<{\n communicationStyle: string;\n objective: string;\n }> {\n return this.req('PATCH', `/team/${this.teamId}/room`, await this.signBody(prefs as Record<string, unknown>));\n }\n\n /** Update a board task. */\n async updateTask(\n taskId: string,\n updates: {\n title?: string;\n description?: string;\n priority?: number;\n tags?: string[];\n }\n ): Promise<unknown> {\n return this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'update', ...updates } as Record<string, unknown>));\n }\n\n /** Delete a board task. */\n async deleteTask(taskId: string): Promise<unknown> {\n return this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'delete' }));\n }\n\n /** Delegate a board task to another agent. */\n async delegateTask(taskId: string, toAgentId: string): Promise<unknown> {\n return this.req('PATCH', `/team/${this.teamId}/board/${taskId}`, await this.signBody({ action: 'delegate', toAgentId }));\n }\n\n /** Add new tasks to the board (used by the delegate_task tool to spawn sub-work). */\n async addTasks(\n tasks: Array<{ title: string; description?: string; priority?: number; source?: string; role?: string; tags?: string[] }>\n ): Promise<{ added: number }> {\n const result = await this.req<{ added?: number }>('POST', `/team/${this.teamId}/board`, await this.signBody({ tasks } as Record<string, unknown>));\n return { added: result.added ?? tasks.length };\n }\n\n // ── Cognitive-verb knowledge surface (Phase 2.2 — recall / rag_query) ────────\n\n /**\n * Query the TEAM knowledge base (the `rag_query` cognitive verb). Bearer-only\n * GET; `q` is the server-side search filter. Returns [] on any failure so a\n * retrieval miss never breaks a tick.\n */\n async queryTeamKnowledge(query: string, limit = 5): Promise<KnowledgeEntry[]> {\n const qs = new URLSearchParams({ q: query, limit: String(limit) }).toString();\n try {\n const data = await this.req<{ entries?: KnowledgeEntry[] }>(\n 'GET',\n `/team/${this.teamId}/knowledge?${qs}`\n );\n return data.entries ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * Query this agent's PRIVATE workspace knowledge (the `recall` cognitive verb).\n * The endpoint has no server-side search param, so the caller filters by query\n * client-side. Returns [] on any failure.\n */\n async queryPrivateKnowledge(): Promise<KnowledgeEntry[]> {\n if (this.localKnowledgePath) {\n try {\n const raw = await readFile(this.localKnowledgePath, 'utf8');\n return raw.trim().split('\\n').filter(Boolean).map(l => JSON.parse(l) as KnowledgeEntry);\n } catch {\n return [];\n }\n }\n try {\n const data = await this.req<{ entries?: KnowledgeEntry[] }>('GET', `/knowledge/private`);\n return data.entries ?? [];\n } catch {\n return [];\n }\n }\n\n /**\n * Persist facts to this agent's PRIVATE workspace — the WRITE side of `recall`\n * (POST /knowledge/private). The server stamps id / workspaceId (`private:<wallet>`)\n * / author / timestamps; the caller supplies `content` (+ optional type/tags/title).\n * This is what gives `recall` something to recall: without it the private store\n * stays empty and every `recall` returns [] (the W.752 loop-gap). Best-effort —\n * returns false on any failure so a write miss never breaks a tick (the task is\n * already done by the time this runs).\n */\n /**\n * Query the server-side in-process codebase GraphRAG index via the mesh bearer-gated\n * route (POST /api/holomesh/codebase/search). Called by the `rag_query` cognitive verb\n * (Phase 2.3 W.753) so edge agents can reach HoloEmbed semantic search without a direct\n * Absorb-service dep. Returns [] (not an error) when the graph isn't loaded yet — the\n * caller falls back to team-knowledge search.\n *\n * @param query Natural-language search string.\n * @param topK Max results to return (1–20, server-capped).\n */\n async queryCodebase(query: string, topK = 8): Promise<Array<{ name: string; file: string; line?: number; type: string; score: number; signature?: string | null }>> {\n try {\n const data = await this.req<{\n success?: boolean;\n result?: {\n results?: Array<{ name: string; file: string; line?: number; type: string; score: number; signature?: string | null }>;\n error?: string;\n };\n error?: string;\n }>('POST', `/codebase/search`, { query, topK });\n if (data.error || data.result?.error) return [];\n return data.result?.results ?? [];\n } catch {\n return [];\n }\n }\n\n async writePrivateKnowledge(\n entries: Array<{ content: string; type?: string; tags?: string[]; title?: string }>\n ): Promise<boolean> {\n if (!entries.length) return false;\n if (this.localKnowledgePath) {\n try {\n await mkdir(dirname(this.localKnowledgePath), { recursive: true });\n const lines = entries.map(e => JSON.stringify({\n id: `local.${Date.now()}.${Math.random().toString(36).slice(2, 6)}`,\n content: e.content,\n type: e.type ?? 'task-outcome',\n tags: e.tags ?? [],\n title: e.title,\n createdAt: new Date().toISOString(),\n })).join('\\n') + '\\n';\n await appendFile(this.localKnowledgePath, lines, 'utf8');\n return true;\n } catch {\n return false;\n }\n }\n try {\n await this.req('POST', `/knowledge/private`, { entries });\n return true;\n } catch {\n return false;\n }\n }\n\n private async req<T>(method: string, path: string, body?: unknown): Promise<T> {\n const url = `${this.apiBase}${path}`;\n // HoloMesh REST auth: server (packages/mcp-server/src/holomesh/auth-utils.ts\n // resolveRequestingAgent) accepts EITHER `Authorization: Bearer <token>`\n // (HTTP-standard, used here) OR `x-mcp-api-key: <token>` (orchestrator\n // convention). Both resolve through the same key-registry / agent-store /\n // env-fallback chain. Bearer is preferred for new code (W.087 vertex B,\n // task_1777073616424_klls).\n const res = await this.fetchImpl(url, {\n method,\n headers: {\n Authorization: `Bearer ${this.bearer}`,\n 'content-type': 'application/json',\n },\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n const txt = await res.text().catch(() => '');\n throw new Error(`HoloMesh ${method} ${path} ${res.status}: ${txt.slice(0, 300)}`);\n }\n if (res.status === 204) return undefined as T;\n return (await res.json()) as T;\n }\n}\n\n/**\n * Derive a surface tag from a seat name returned by /me. Mirrors the surface\n * detection in scripts/probe-surface-bearers.mjs and hooks/lib/holomesh-env.mjs\n * so a single agent's surface attribution is consistent across read and write\n * paths. Returns 'unknown' when the seat name doesn't encode a surface\n * (e.g. shared-key resolution to \"Founder\").\n */\nexport function deriveSurface(seatName: string | undefined): string {\n if (!seatName) return 'unknown';\n const n = seatName.toLowerCase();\n if (n.startsWith('claudecode')) return 'claude-code';\n if (n.startsWith('cursor')) return 'claude-cursor';\n if (n.startsWith('claudedesktop')) return 'claude-desktop';\n if (n.startsWith('vscode-claude') || n.startsWith('claude-vscode')) return 'claude-vscode';\n if (n.startsWith('gemini')) return 'gemini-antigravity';\n if (n.startsWith('copilot')) return 'copilot-vscode';\n return 'unknown';\n}\n\nexport function pickClaimableTask(\n tasks: BoardTask[],\n brainCapabilityTags: string[]\n): BoardTask | undefined {\n const wanted = new Set(brainCapabilityTags.map((t) => t.toLowerCase()));\n const open = tasks.filter((t) => t.status === 'open' && !t.claimedBy);\n const scored = open\n .map((t) => ({ task: t, score: scoreTask(t, wanted) }))\n .filter((s) => s.score > 0)\n .sort((a, b) => b.score - a.score || priority(a.task) - priority(b.task));\n return scored[0]?.task;\n}\n\nfunction scoreTask(task: BoardTask, wanted: Set<string>): number {\n const tags = (task.tags ?? []).map((t) => t.toLowerCase());\n const text = `${task.title} ${task.description ?? ''}`.toLowerCase();\n let score = 0;\n for (const tag of tags) if (wanted.has(tag)) score += 2;\n for (const w of wanted) if (text.includes(w)) score += 1;\n return score;\n}\n\nfunction priority(t: BoardTask): number {\n if (typeof t.priority === 'number') return t.priority;\n const map: Record<string, number> = { critical: 1, high: 2, medium: 4, low: 6 };\n return map[String(t.priority).toLowerCase()] ?? 5;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -512,6 +512,8 @@ function orderCandidates(candidates, tieBreaker) {
|
|
|
512
512
|
}
|
|
513
513
|
|
|
514
514
|
// src/holomesh-client.ts
|
|
515
|
+
import { readFile as readFile2, appendFile, mkdir } from "fs/promises";
|
|
516
|
+
import { dirname as dirname2 } from "path";
|
|
515
517
|
var HolomeshClient = class {
|
|
516
518
|
constructor(opts) {
|
|
517
519
|
this.apiBase = opts.apiBase.replace(/\/$/, "");
|
|
@@ -519,6 +521,7 @@ var HolomeshClient = class {
|
|
|
519
521
|
this.teamId = opts.teamId;
|
|
520
522
|
this.fetchImpl = opts.fetchImpl ?? fetch;
|
|
521
523
|
this.signer = opts.signer;
|
|
524
|
+
this.localKnowledgePath = opts.localKnowledgePath;
|
|
522
525
|
}
|
|
523
526
|
/** Wrap body in a signed envelope when a signer is available (strict-mode endpoints). */
|
|
524
527
|
async signBody(body) {
|
|
@@ -647,6 +650,14 @@ var HolomeshClient = class {
|
|
|
647
650
|
* client-side. Returns [] on any failure.
|
|
648
651
|
*/
|
|
649
652
|
async queryPrivateKnowledge() {
|
|
653
|
+
if (this.localKnowledgePath) {
|
|
654
|
+
try {
|
|
655
|
+
const raw = await readFile2(this.localKnowledgePath, "utf8");
|
|
656
|
+
return raw.trim().split("\n").filter(Boolean).map((l) => JSON.parse(l));
|
|
657
|
+
} catch {
|
|
658
|
+
return [];
|
|
659
|
+
}
|
|
660
|
+
}
|
|
650
661
|
try {
|
|
651
662
|
const data = await this.req("GET", `/knowledge/private`);
|
|
652
663
|
return data.entries ?? [];
|
|
@@ -684,6 +695,23 @@ var HolomeshClient = class {
|
|
|
684
695
|
}
|
|
685
696
|
async writePrivateKnowledge(entries) {
|
|
686
697
|
if (!entries.length) return false;
|
|
698
|
+
if (this.localKnowledgePath) {
|
|
699
|
+
try {
|
|
700
|
+
await mkdir(dirname2(this.localKnowledgePath), { recursive: true });
|
|
701
|
+
const lines = entries.map((e) => JSON.stringify({
|
|
702
|
+
id: `local.${Date.now()}.${Math.random().toString(36).slice(2, 6)}`,
|
|
703
|
+
content: e.content,
|
|
704
|
+
type: e.type ?? "task-outcome",
|
|
705
|
+
tags: e.tags ?? [],
|
|
706
|
+
title: e.title,
|
|
707
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
708
|
+
})).join("\n") + "\n";
|
|
709
|
+
await appendFile(this.localKnowledgePath, lines, "utf8");
|
|
710
|
+
return true;
|
|
711
|
+
} catch {
|
|
712
|
+
return false;
|
|
713
|
+
}
|
|
714
|
+
}
|
|
687
715
|
try {
|
|
688
716
|
await this.req("POST", `/knowledge/private`, { entries });
|
|
689
717
|
return true;
|
|
@@ -832,8 +860,8 @@ function buildCaelRecord(input) {
|
|
|
832
860
|
}
|
|
833
861
|
|
|
834
862
|
// src/tools.ts
|
|
835
|
-
import { readFile as
|
|
836
|
-
import { resolve, dirname as
|
|
863
|
+
import { readFile as readFile3, writeFile, readdir, mkdir as mkdir2, stat } from "fs/promises";
|
|
864
|
+
import { resolve, dirname as dirname3, delimiter, isAbsolute, sep } from "path";
|
|
837
865
|
import { spawn } from "child_process";
|
|
838
866
|
import { createHash as createHash2 } from "crypto";
|
|
839
867
|
var FLEET_READ_ROOTS = [
|
|
@@ -1124,7 +1152,7 @@ async function runTool(use, opts = {}) {
|
|
|
1124
1152
|
const path = use.input.path;
|
|
1125
1153
|
const denied = checkReadAllowed(path);
|
|
1126
1154
|
if (denied) return errResult(use.id, denied);
|
|
1127
|
-
const text = await
|
|
1155
|
+
const text = await readFile3(path, "utf8");
|
|
1128
1156
|
const truncated = text.length > 2e5 ? text.slice(0, 2e5) + `
|
|
1129
1157
|
\u2026[truncated, full file is ${text.length} bytes]` : text;
|
|
1130
1158
|
return okResult(use.id, truncated);
|
|
@@ -1142,7 +1170,7 @@ async function runTool(use, opts = {}) {
|
|
|
1142
1170
|
const content = use.input.content;
|
|
1143
1171
|
const denied = checkWriteAllowed(path);
|
|
1144
1172
|
if (denied) return errResult(use.id, denied);
|
|
1145
|
-
await
|
|
1173
|
+
await mkdir2(dirname3(path), { recursive: true });
|
|
1146
1174
|
await writeFile(path, content, "utf8");
|
|
1147
1175
|
const s = await stat(path);
|
|
1148
1176
|
return okResult(use.id, `wrote ${s.size} bytes to ${path}`);
|
|
@@ -1257,7 +1285,7 @@ ${truncated}`);
|
|
|
1257
1285
|
const outPath = resolve(ALLOWED_WRITE_ROOTS[0], `hardware-receipt-${ts}.json`);
|
|
1258
1286
|
const denied = checkWriteAllowed(outPath);
|
|
1259
1287
|
if (denied) return errResult(use.id, `Cannot write receipt: ${denied}`);
|
|
1260
|
-
await
|
|
1288
|
+
await mkdir2(dirname3(outPath), { recursive: true });
|
|
1261
1289
|
await writeFile(outPath, JSON.stringify(sealed, null, 2), "utf8");
|
|
1262
1290
|
return okResult(
|
|
1263
1291
|
use.id,
|
|
@@ -1270,7 +1298,7 @@ ${truncated}`);
|
|
|
1270
1298
|
const newStr = use.input.new;
|
|
1271
1299
|
const denied = checkWriteAllowed(path);
|
|
1272
1300
|
if (denied) return errResult(use.id, denied);
|
|
1273
|
-
const text = await
|
|
1301
|
+
const text = await readFile3(path, "utf8");
|
|
1274
1302
|
const count = text.split(oldStr).length - 1;
|
|
1275
1303
|
if (count === 0) return errResult(use.id, `str_replace: "old" string not found in ${path} \u2014 0 occurrences`);
|
|
1276
1304
|
if (count > 1) return errResult(use.id, `str_replace: "old" string is ambiguous in ${path} \u2014 ${count} occurrences; add more surrounding context`);
|
|
@@ -1705,6 +1733,37 @@ var AgentRunner = class {
|
|
|
1705
1733
|
finalText = resp.content;
|
|
1706
1734
|
break;
|
|
1707
1735
|
}
|
|
1736
|
+
if (productiveCallCount === 0 && toolsCalled.size > 0 && iters < MAX_TOOL_ITERS) {
|
|
1737
|
+
iters++;
|
|
1738
|
+
messages.push({
|
|
1739
|
+
role: "user",
|
|
1740
|
+
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."
|
|
1741
|
+
});
|
|
1742
|
+
const reResp = await provider.complete(
|
|
1743
|
+
{ messages, maxTokens: 8192, temperature: 0.4, tools: activeTools },
|
|
1744
|
+
identity.llmModel
|
|
1745
|
+
);
|
|
1746
|
+
aggUsage = {
|
|
1747
|
+
promptTokens: aggUsage.promptTokens + reResp.usage.promptTokens,
|
|
1748
|
+
completionTokens: aggUsage.completionTokens + reResp.usage.completionTokens,
|
|
1749
|
+
totalTokens: aggUsage.totalTokens + reResp.usage.totalTokens
|
|
1750
|
+
};
|
|
1751
|
+
if (reResp.finishReason === "tool_use" && reResp.toolUses && reResp.toolUses.length > 0) {
|
|
1752
|
+
log({ ev: "reprompt-tool-call", taskId: target.id, iter: iters, tools: reResp.toolUses.map((t) => t.name) });
|
|
1753
|
+
const reProd = summarizeToolProductivity(reResp.toolUses);
|
|
1754
|
+
for (const n of reProd.names) toolsCalled.add(n);
|
|
1755
|
+
productiveCallCount += reProd.productiveCount;
|
|
1756
|
+
messages.push({ role: "assistant", content: reResp.assistantBlocks ?? [] });
|
|
1757
|
+
const reResults = await Promise.all(
|
|
1758
|
+
reResp.toolUses.map(
|
|
1759
|
+
(u) => runTool(u, { signReceipt: this.opts.signReceipt, addTask: (tasks2) => mesh.addTasks(tasks2) })
|
|
1760
|
+
)
|
|
1761
|
+
);
|
|
1762
|
+
messages.push({ role: "user", content: reResults });
|
|
1763
|
+
}
|
|
1764
|
+
finalText = reResp.content;
|
|
1765
|
+
lastResponse = reResp;
|
|
1766
|
+
}
|
|
1708
1767
|
const durationMs = Date.now() - start;
|
|
1709
1768
|
if (productiveCallCount === 0) {
|
|
1710
1769
|
log({
|
|
@@ -1980,7 +2039,7 @@ function jitter(base) {
|
|
|
1980
2039
|
|
|
1981
2040
|
// src/commit-hook.ts
|
|
1982
2041
|
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1983
|
-
import { dirname as
|
|
2042
|
+
import { dirname as dirname4, join, resolve as resolve2 } from "path";
|
|
1984
2043
|
import { spawnSync } from "child_process";
|
|
1985
2044
|
var SAFE_HANDLE = /^[a-z0-9_-]{1,64}$/i;
|
|
1986
2045
|
function makeCommitHook(opts) {
|
|
@@ -2000,7 +2059,7 @@ function makeCommitHook(opts) {
|
|
|
2000
2059
|
const safeTaskId = task.id.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 80);
|
|
2001
2060
|
const fileName = `${date}_${safeTaskId}_${identity.handle}.md`;
|
|
2002
2061
|
const filePath = join(outputDir, fileName);
|
|
2003
|
-
mkdirSync2(
|
|
2062
|
+
mkdirSync2(dirname4(filePath), { recursive: true });
|
|
2004
2063
|
writeFileSync2(filePath, renderMemo(result, task, identity, date), "utf8");
|
|
2005
2064
|
const relPath = relativeTo(cwd, filePath);
|
|
2006
2065
|
const addRes = spawn2("git", ["add", relPath], { cwd, encoding: "utf8" });
|
|
@@ -2270,12 +2329,12 @@ import { join as join2 } from "path";
|
|
|
2270
2329
|
|
|
2271
2330
|
// src/audit-log.ts
|
|
2272
2331
|
import { mkdirSync as mkdirSync3, appendFileSync, readFileSync as readFileSync2, existsSync as existsSync2, statSync, renameSync } from "fs";
|
|
2273
|
-
import { dirname as
|
|
2332
|
+
import { dirname as dirname5 } from "path";
|
|
2274
2333
|
var AuditLog = class {
|
|
2275
2334
|
constructor(opts) {
|
|
2276
2335
|
this.logPath = opts.logPath;
|
|
2277
2336
|
this.maxBytes = opts.maxBytes ?? 50 * 1024 * 1024;
|
|
2278
|
-
mkdirSync3(
|
|
2337
|
+
mkdirSync3(dirname5(this.logPath), { recursive: true });
|
|
2279
2338
|
}
|
|
2280
2339
|
record(event) {
|
|
2281
2340
|
this.rotateIfFull();
|
|
@@ -2899,7 +2958,7 @@ function envVarLinesFor(handle, walletAddress, bearer) {
|
|
|
2899
2958
|
|
|
2900
2959
|
// src/index.ts
|
|
2901
2960
|
import { writeFileSync as writeFileSync4, readFileSync as readFileSync5, existsSync as existsSync4, mkdirSync as mkdirSync5 } from "fs";
|
|
2902
|
-
import { dirname as
|
|
2961
|
+
import { dirname as dirname6, resolve as resolve3 } from "path";
|
|
2903
2962
|
async function main() {
|
|
2904
2963
|
const args = process.argv.slice(2);
|
|
2905
2964
|
const cmd = args[0] ?? "help";
|
|
@@ -2989,7 +3048,8 @@ async function cmdRun(opts) {
|
|
|
2989
3048
|
apiBase: identity.meshApiBase,
|
|
2990
3049
|
bearer,
|
|
2991
3050
|
teamId: identity.teamId,
|
|
2992
|
-
signer: buildRequestSigner(seat)
|
|
3051
|
+
signer: buildRequestSigner(seat),
|
|
3052
|
+
localKnowledgePath: process.env.HOLOSCRIPT_AGENT_LOCAL_KNOWLEDGE_PATH
|
|
2993
3053
|
});
|
|
2994
3054
|
const commitHook = buildCommitHook(identity, mesh);
|
|
2995
3055
|
const auditLog = buildAuditLog();
|
|
@@ -3224,11 +3284,11 @@ async function cmdAblate(rest) {
|
|
|
3224
3284
|
timeoutPerCellMs: spec.timeoutPerCellMs
|
|
3225
3285
|
});
|
|
3226
3286
|
if (outJson) {
|
|
3227
|
-
mkdirSync5(
|
|
3287
|
+
mkdirSync5(dirname6(resolve3(outJson)), { recursive: true });
|
|
3228
3288
|
writeFileSync4(outJson, JSON.stringify(matrix, null, 2), "utf8");
|
|
3229
3289
|
}
|
|
3230
3290
|
if (outMd) {
|
|
3231
|
-
mkdirSync5(
|
|
3291
|
+
mkdirSync5(dirname6(resolve3(outMd)), { recursive: true });
|
|
3232
3292
|
writeFileSync4(outMd, renderAblationMarkdown(matrix), "utf8");
|
|
3233
3293
|
}
|
|
3234
3294
|
if (!outMd && !outJson) {
|
|
@@ -3262,7 +3322,8 @@ async function cmdWhoami() {
|
|
|
3262
3322
|
apiBase: identity.meshApiBase,
|
|
3263
3323
|
bearer,
|
|
3264
3324
|
teamId: identity.teamId,
|
|
3265
|
-
signer: buildRequestSigner(seat)
|
|
3325
|
+
signer: buildRequestSigner(seat),
|
|
3326
|
+
localKnowledgePath: process.env.HOLOSCRIPT_AGENT_LOCAL_KNOWLEDGE_PATH
|
|
3266
3327
|
});
|
|
3267
3328
|
const me = await mesh.whoAmI();
|
|
3268
3329
|
console.log(JSON.stringify({ identity: identityForLog(identity), me }, null, 2));
|
|
@@ -3437,6 +3498,7 @@ OPTIONAL ENV
|
|
|
3437
3498
|
HOLOSCRIPT_AGENT_LOCAL_LLM_BASE_URL local-llm provider base URL (default http://localhost:8080)
|
|
3438
3499
|
HOLOSCRIPT_AGENT_LOCAL_LLM_MODEL local-llm model id (e.g. "qwen3:4b-instruct"); overrides HOLOSCRIPT_AGENT_MODEL for the local provider
|
|
3439
3500
|
HOLOSCRIPT_AGENT_LOCAL_LLM_TIMEOUT_MS local-llm request timeout in ms (default 300000 \u2014 edge devices like Jetson need >120s)
|
|
3501
|
+
HOLOSCRIPT_AGENT_LOCAL_KNOWLEDGE_PATH local JSONL path for sovereign private knowledge store (bypasses mcp-orchestrator /knowledge/sync)
|
|
3440
3502
|
`);
|
|
3441
3503
|
}
|
|
3442
3504
|
main().catch((err) => {
|