blun-king-cli 9.1.564 → 9.1.566
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/LIESMICH.txt +2 -2
- package/README.md +1 -1
- package/agent-spine-plugin/.claude-plugin/marketplace.json +20 -20
- package/agent-spine-plugin/.claude-plugin/plugin.json +14 -14
- package/agent-spine-plugin/.codex-plugin/plugin.json +34 -34
- package/agent-spine-plugin/.mcp.json +8 -8
- package/agent-spine-plugin/CONTRIBUTING.md +52 -0
- package/agent-spine-plugin/LICENSE +186 -186
- package/agent-spine-plugin/README.md +406 -0
- package/agent-spine-plugin/SECURITY.md +47 -0
- package/agent-spine-plugin/assets/agentspine-banner.svg +32 -32
- package/agent-spine-plugin/bin/agentspine-mcp.js +4 -4
- package/agent-spine-plugin/bin/agentspine.js +7 -7
- package/agent-spine-plugin/blun.plugin.json +33 -33
- package/agent-spine-plugin/hooks/codex.json +47 -47
- package/agent-spine-plugin/hooks/hooks.json +106 -106
- package/agent-spine-plugin/hooks/version.json +5 -5
- package/agent-spine-plugin/package.json +69 -69
- package/agent-spine-plugin/scripts/check-hosts.js +199 -199
- package/agent-spine-plugin/skill/SKILL.md +76 -76
- package/agent-spine-plugin/skills/agent-spine/SKILL.md +85 -85
- package/agent-spine-plugin/src/cli.js +1488 -1442
- package/agent-spine-plugin/src/hook.js +882 -861
- package/agent-spine-plugin/src/index.js +94 -93
- package/agent-spine-plugin/src/lib/acceptance.js +333 -333
- package/agent-spine-plugin/src/lib/attention.js +755 -755
- package/agent-spine-plugin/src/lib/audit.js +351 -342
- package/agent-spine-plugin/src/lib/authentication.js +515 -515
- package/agent-spine-plugin/src/lib/briefing.js +317 -317
- package/agent-spine-plugin/src/lib/catalog.js +167 -167
- package/agent-spine-plugin/src/lib/channel-runtime.js +665 -665
- package/agent-spine-plugin/src/lib/context.js +154 -154
- package/agent-spine-plugin/src/lib/continuity.js +338 -338
- package/agent-spine-plugin/src/lib/coordination.js +577 -577
- package/agent-spine-plugin/src/lib/documents.js +217 -217
- package/agent-spine-plugin/src/lib/feed-transport.js +499 -499
- package/agent-spine-plugin/src/lib/filesystem-retry.js +32 -32
- package/agent-spine-plugin/src/lib/gateway-runtime.js +1623 -760
- package/agent-spine-plugin/src/lib/graph.js +337 -337
- package/agent-spine-plugin/src/lib/hook-audit.js +29 -29
- package/agent-spine-plugin/src/lib/https-transport.js +392 -392
- package/agent-spine-plugin/src/lib/indexed-memory-offline.js +40 -40
- package/agent-spine-plugin/src/lib/indexed-memory.js +281 -281
- package/agent-spine-plugin/src/lib/learning.js +6923 -6466
- package/agent-spine-plugin/src/lib/object-transport.js +206 -206
- package/agent-spine-plugin/src/lib/owned-file-lock.js +143 -143
- package/agent-spine-plugin/src/lib/paths.js +109 -109
- package/agent-spine-plugin/src/lib/peer-transport.js +283 -283
- package/agent-spine-plugin/src/lib/persona-runtime.js +581 -581
- package/agent-spine-plugin/src/lib/preflight.js +702 -702
- package/agent-spine-plugin/src/lib/runtime.js +13 -13
- package/agent-spine-plugin/src/lib/selfstarter.js +911 -866
- package/agent-spine-plugin/src/lib/sharing.js +969 -969
- package/agent-spine-plugin/src/lib/source-roots.js +530 -498
- package/agent-spine-plugin/src/lib/sqlite-transport.js +501 -501
- package/agent-spine-plugin/src/lib/telegram-adapter.js +119 -119
- package/agent-spine-plugin/src/lib/voice-runtime.js +39 -39
- package/agent-spine-plugin/src/mcp.js +597 -572
- package/agent-spine-plugin/src/version.js +1 -1
- package/agent-spine-plugin/src/worker.js +210 -195
- package/bin/active-steer-priority-policy.cjs +24 -0
- package/bin/launcher-runtime.js +8 -1
- package/bin/mnemo-tool-agent-policy.cjs +22 -0
- package/bin/native-runtime-cache.cjs +76 -0
- package/bin/thinking-activity-status-policy.cjs +132 -0
- package/bin/thinking-only-guard.cjs +75 -0
- package/bin/tool-call-loop-policy.cjs +53 -0
- package/bin/turn-thinking-policy.cjs +25 -1
- package/blun.mjs +556 -63
- package/package.json +4 -1
- package/standard-skills/translate-native/LICENSE +21 -21
- package/standard-skills/translate-native/references/evaluation-protocol.md +95 -95
- package/standard-skills/translate-native/references/native-orthography.md +79 -79
- package/standard-skills/translate-native/references/native-translation-standard.md +94 -94
- package/standard-skills/translate-native/references/structured-content.md +72 -72
- package/standard-skills/translate-native/references/translationese-review.md +77 -77
- package/standard-skills/translate-native/scripts/blun_language_guard.py +697 -697
- package/standard-skills/translate-native/scripts/check_diacritics.py +353 -353
- package/standard-skills/translate-native/scripts/guard_service_client.py +264 -264
- package/standard-skills/translate-native/scripts/language_gateway.py +62 -62
- package/standard-skills/translate-native/scripts/language_quality.py +377 -377
- package/standard-skills/translate-native/scripts/pre_output_guard.py +64 -64
- package/standard-skills/translate-native/scripts/translation_guard.py +916 -916
- package/standard-tools/language-guard/blun_language_guard.py +697 -697
- package/standard-tools/language-guard/check_diacritics.py +353 -353
- package/standard-tools/language-guard/guard_service_client.py +264 -264
- package/standard-tools/language-guard/language_quality.py +377 -377
- package/standard-tools/language-guard/translation_guard.py +916 -916
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = "0.
|
|
1
|
+
export const VERSION = "0.59.1";
|
|
@@ -1,195 +1,210 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { watch } from "node:fs";
|
|
4
|
-
import { lstat, realpath } from "node:fs/promises";
|
|
5
|
-
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
child.on("
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} : {})
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
await
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
const
|
|
155
|
-
if (!
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { watch } from "node:fs";
|
|
4
|
+
import { lstat, realpath } from "node:fs/promises";
|
|
5
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
6
|
+
import {
|
|
7
|
+
claimGatewayWork, completeGatewayRun, deliverPrepared, executionAttemptForStep,
|
|
8
|
+
failGatewayRun, loadGatewayRuntime, reconcileGateway, updateGatewayHealth
|
|
9
|
+
} from "./lib/gateway-runtime.js";
|
|
10
|
+
import { createTelegramAdapter } from "./lib/telegram-adapter.js";
|
|
11
|
+
import { acknowledgeChannelDelivery, loadChannelRuntime } from "./lib/channel-runtime.js";
|
|
12
|
+
import { loadPersonaRuntime, syncPersonaRosterFromEnvironment } from "./lib/persona-runtime.js";
|
|
13
|
+
import { isMainModule } from "./lib/runtime.js";
|
|
14
|
+
|
|
15
|
+
const MAX_FRAME = 64 * 1024;
|
|
16
|
+
const WAKE_FILES = new Set(["attention.json", "channel-runtime.json", "gateway-policy.json", "persona-policy.json", "persona-runtime.json"]);
|
|
17
|
+
|
|
18
|
+
async function wakeSnapshot(directory) {
|
|
19
|
+
return Promise.all([...WAKE_FILES].sort().map(async (name) => {
|
|
20
|
+
try {
|
|
21
|
+
const metadata = await lstat(join(directory, name), { bigint: true });
|
|
22
|
+
return `${name}:${metadata.dev}:${metadata.ino}:${metadata.size}:${metadata.mtimeNs}`;
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error.code === "ENOENT") return `${name}:missing`;
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
})).then((entries) => entries.join("\n"));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function waitForGatewayWake(root, delayMs, { watchFactory = watch, realpathFactory = realpath, onReady = null } = {}) {
|
|
31
|
+
const delay = Math.max(250, Math.min(60000, Number(delayMs) || 60000));
|
|
32
|
+
const { directory } = await loadGatewayRuntime(root);
|
|
33
|
+
let watchPath;
|
|
34
|
+
try { watchPath = await realpathFactory(directory); } catch { return "watch-unavailable"; }
|
|
35
|
+
let before;
|
|
36
|
+
try { before = await wakeSnapshot(watchPath); } catch { return "watch-unavailable"; }
|
|
37
|
+
return new Promise((resolvePromise) => {
|
|
38
|
+
let settled = false; let watcher;
|
|
39
|
+
const finish = (reason) => {
|
|
40
|
+
if (settled) return;
|
|
41
|
+
settled = true; clearTimeout(timer);
|
|
42
|
+
watcher?.close();
|
|
43
|
+
resolvePromise(reason);
|
|
44
|
+
};
|
|
45
|
+
const timer = setTimeout(() => finish("timer"), delay);
|
|
46
|
+
try {
|
|
47
|
+
watcher = watchFactory(watchPath, { persistent: true }, (_eventType, filename) => {
|
|
48
|
+
const name = filename === null ? null : String(filename);
|
|
49
|
+
if (name === null || WAKE_FILES.has(name)) finish("event");
|
|
50
|
+
});
|
|
51
|
+
watcher.on?.("error", () => finish("watch-error"));
|
|
52
|
+
Promise.resolve().then(() => onReady?.(watchPath)).then(() => wakeSnapshot(watchPath)).then((after) => {
|
|
53
|
+
if (after !== before) finish("event");
|
|
54
|
+
}).catch(() => finish("watch-error"));
|
|
55
|
+
} catch { finish("watch-unavailable"); }
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function exactRunner(env) {
|
|
60
|
+
const runner = env.AGENTSPINE_HOST_RUNNER;
|
|
61
|
+
if (!runner || !isAbsolute(runner)) throw new Error("AGENTSPINE_HOST_RUNNER must be an absolute locally approved executable path");
|
|
62
|
+
return resolve(runner);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function invokeHostRunner(item, { env = process.env, timeoutMs = 300000, spawnProcess = spawn } = {}) {
|
|
66
|
+
const executable = exactRunner(env);
|
|
67
|
+
return new Promise((resolvePromise, reject) => {
|
|
68
|
+
const child = spawnProcess(executable, [], {
|
|
69
|
+
shell: false, windowsHide: true, stdio: ["pipe", "pipe", "pipe"],
|
|
70
|
+
env: Object.fromEntries(["PATH", "Path", "HOME", "USERPROFILE", "SystemRoot", "SYSTEMROOT", "WINDIR",
|
|
71
|
+
"AGENTSPINE_STATE_DIR", "CLAUDE_CONFIG_DIR", "CODEX_HOME"].filter((key) => env[key] !== undefined).map((key) => [key, env[key]]))
|
|
72
|
+
});
|
|
73
|
+
let stdout = Buffer.alloc(0); let stderr = Buffer.alloc(0); let settled = false;
|
|
74
|
+
const finish = (error, value) => { if (settled) return; settled = true; clearTimeout(timer); error ? reject(error) : resolvePromise(value); };
|
|
75
|
+
child.stdout.on("data", (chunk) => { stdout = Buffer.concat([stdout, chunk]); if (stdout.length > MAX_FRAME) child.kill(); });
|
|
76
|
+
child.stderr.on("data", (chunk) => { stderr = Buffer.concat([stderr, chunk]).subarray(0, 2048); });
|
|
77
|
+
child.on("error", (error) => finish(error));
|
|
78
|
+
child.on("close", (code) => {
|
|
79
|
+
if (stdout.length > MAX_FRAME) return finish(new Error("host runner response exceeds 64 KiB"));
|
|
80
|
+
if (code !== 0) return finish(new Error("host runner failed: " + stderr.toString("utf8").slice(0, 2048)));
|
|
81
|
+
try { finish(null, JSON.parse(stdout.toString("utf8"))); } catch { finish(new Error("host runner returned invalid JSON")); }
|
|
82
|
+
});
|
|
83
|
+
const timer = setTimeout(() => { child.kill(); finish(new Error("host runner exceeded its bounded timeout")); }, timeoutMs);
|
|
84
|
+
child.stdin.end(JSON.stringify({ schema: "agentspine.run-request/v1", item, authority: "execution-state-only" }) + "\n");
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function hostWorkItem(root, item) {
|
|
89
|
+
const [{ policy }, personas, channel] = await Promise.all([
|
|
90
|
+
loadGatewayRuntime(root), loadPersonaRuntime(root), loadChannelRuntime(root)
|
|
91
|
+
]);
|
|
92
|
+
const persona = personas.runtime.personas.find((entry) => entry.personaId === item.agentId && entry.status === "active");
|
|
93
|
+
const identity = personas.policy.bindings.find((entry) => entry.id === persona?.bindingId && entry.active);
|
|
94
|
+
if (!persona || !identity) throw new Error("claimed work lost its authenticated agent identity");
|
|
95
|
+
const goal = item.goalId === null ? null : policy.goals.find((entry) => entry.goalId === item.goalId) || null;
|
|
96
|
+
const goalStep = item.goalStepId === null || item.goalStepId === undefined ? null
|
|
97
|
+
: goal?.plan?.steps.find((entry) => entry.stepId === item.goalStepId) || null;
|
|
98
|
+
if (item.goalStepId && !goalStep) throw new Error("claimed work lost its exact goal-plan step");
|
|
99
|
+
const executionAttempt = executionAttemptForStep(goalStep);
|
|
100
|
+
const event = item.channelEventId === null ? null
|
|
101
|
+
: channel.runtime.events.find((entry) => entry.eventId === item.channelEventId) || null;
|
|
102
|
+
if (item.channelEventId && !event) throw new Error("claimed channel work lost its exact event");
|
|
103
|
+
return {
|
|
104
|
+
...structuredClone(item), host: identity.host, profileId: identity.profileId, projectRoot: root,
|
|
105
|
+
goal: goal ? structuredClone(goal) : null,
|
|
106
|
+
goalStep: goalStep ? { ...structuredClone(goalStep),
|
|
107
|
+
...(executionAttempt === null ? {} : { executionAttempt }) } : null,
|
|
108
|
+
hostEnvironment: {
|
|
109
|
+
AGENTSPINE_GATEWAY_CONTEXT: "agentspine.gateway-start/v1",
|
|
110
|
+
AGENTSPINE_ENTITY_ID: item.agentId,
|
|
111
|
+
AGENTSPINE_PROJECT_ID: item.projectId,
|
|
112
|
+
...(item.groupId === null ? {} : { AGENTSPINE_GROUP_ID: item.groupId }),
|
|
113
|
+
...(event ? {
|
|
114
|
+
AGENTSPINE_CHANNEL_EVENT_ID: event.eventId,
|
|
115
|
+
AGENTSPINE_CHANNEL_PROVIDER: event.provider
|
|
116
|
+
} : {})
|
|
117
|
+
},
|
|
118
|
+
channelStart: event ? {
|
|
119
|
+
agent_spine_scope: { entity_id: event.agentId, project_id: event.projectId, group_id: event.groupId },
|
|
120
|
+
agent_spine_channel_event: { event_id: event.eventId, provider: event.provider },
|
|
121
|
+
session_key: event.sessionKey,
|
|
122
|
+
authority: "explicit-local-channel-policy"
|
|
123
|
+
} : null
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export async function runWorkerTick({ root = process.cwd(), workerId = "gateway-worker:local", now = new Date(),
|
|
128
|
+
hostRunner = invokeHostRunner, adapter = null, env = process.env } = {}) {
|
|
129
|
+
const deliveryAdapter = adapter || createTelegramAdapter({ root, env });
|
|
130
|
+
await syncPersonaRosterFromEnvironment({ root, env, now });
|
|
131
|
+
const initial = await loadGatewayRuntime(root);
|
|
132
|
+
if (!initial.policy.enabled || initial.policy.killSwitch) return { status: "stopped", processed: false };
|
|
133
|
+
if (typeof deliveryAdapter.poll === "function") {
|
|
134
|
+
try { await deliveryAdapter.poll(); }
|
|
135
|
+
catch (error) {
|
|
136
|
+
await updateGatewayHealth({ root, worker: "degraded", adapter: "failed", now });
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await reconcileGateway({ root, now });
|
|
141
|
+
await updateGatewayHealth({ root, worker: "healthy", adapter: "healthy", now });
|
|
142
|
+
const afterReconcile = await loadGatewayRuntime(root);
|
|
143
|
+
const dueOutbox = afterReconcile.runtime.outbox.filter((item) => ["prepared", "failed"].includes(item.status)
|
|
144
|
+
&& new Date(item.nextAttemptAt) <= new Date(now)).sort((a, b) => a.nextAttemptAt.localeCompare(b.nextAttemptAt)
|
|
145
|
+
|| a.createdAt.localeCompare(b.createdAt) || a.outboxId.localeCompare(b.outboxId))[0] || null;
|
|
146
|
+
if (dueOutbox) {
|
|
147
|
+
const delivery = await deliverPrepared({ root, outboxId: dueOutbox.outboxId, adapter: deliveryAdapter, now });
|
|
148
|
+
if (delivery.outbox.status === "delivered") await acknowledgeChannelDelivery({ root,
|
|
149
|
+
eventId: delivery.outbox.eventId, bindingId: delivery.outbox.bindingId,
|
|
150
|
+
deliveryReceiptId: delivery.receipt.id, now });
|
|
151
|
+
return { status: delivery.outbox.status, processed: true, outboxId: delivery.outbox.outboxId,
|
|
152
|
+
recoveredDelivery: true };
|
|
153
|
+
}
|
|
154
|
+
const claim = await claimGatewayWork({ root, workerId, now });
|
|
155
|
+
if (!claim.item) return { status: claim.reason, processed: false };
|
|
156
|
+
let result;
|
|
157
|
+
try { result = await hostRunner(await hostWorkItem(root, claim.item), { env }); }
|
|
158
|
+
catch (error) {
|
|
159
|
+
const failed = await failGatewayRun({ root, queueId: claim.item.queueId, workerId,
|
|
160
|
+
error: "Host runtime unavailable: " + String(error.message).slice(0, 400), now });
|
|
161
|
+
return { status: failed.item.status, processed: true, queueId: failed.item.queueId, retryAt: failed.item.availableAt };
|
|
162
|
+
}
|
|
163
|
+
const completed = await completeGatewayRun({ root, queueId: claim.item.queueId, workerId, result, now });
|
|
164
|
+
if (!completed.outbox) return {
|
|
165
|
+
status: completed.clarification ? "needs-clarification"
|
|
166
|
+
: completed.exploration ? "exploring" : completed.item.status,
|
|
167
|
+
processed: true, queueId: completed.item.queueId,
|
|
168
|
+
...(completed.clarification ? { clarification: completed.clarification } : {}),
|
|
169
|
+
...(completed.exploration ? { exploration: completed.exploration } : {})
|
|
170
|
+
};
|
|
171
|
+
const delivery = await deliverPrepared({ root, outboxId: completed.outbox.outboxId,
|
|
172
|
+
adapter: deliveryAdapter, now });
|
|
173
|
+
if (delivery.outbox.status === "delivered") await acknowledgeChannelDelivery({ root,
|
|
174
|
+
eventId: delivery.outbox.eventId, bindingId: delivery.outbox.bindingId,
|
|
175
|
+
deliveryReceiptId: delivery.receipt.id, now });
|
|
176
|
+
return { status: delivery.outbox.status, processed: true, queueId: completed.item.queueId, outboxId: delivery.outbox.outboxId };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export async function runWorker({ root = process.cwd(), once = false, env = process.env,
|
|
180
|
+
waitForWake = waitForGatewayWake } = {}) {
|
|
181
|
+
const workerId = "gateway-worker:" + process.pid;
|
|
182
|
+
do {
|
|
183
|
+
const result = await runWorkerTick({ root, workerId, env });
|
|
184
|
+
if (once) return result;
|
|
185
|
+
const { policy, runtime } = await loadGatewayRuntime(root);
|
|
186
|
+
if (!policy.enabled || policy.killSwitch) return { status: "stopped", processed: false };
|
|
187
|
+
const pending = [
|
|
188
|
+
...runtime.queue.filter((item) => item.status === "pending").map((item) => new Date(item.availableAt).getTime()),
|
|
189
|
+
...runtime.outbox.filter((item) => ["prepared", "failed"].includes(item.status))
|
|
190
|
+
.map((item) => new Date(item.nextAttemptAt).getTime())
|
|
191
|
+
];
|
|
192
|
+
const delay = pending.length ? Math.max(250, Math.min(60000, Math.min(...pending) - Date.now())) : 60000;
|
|
193
|
+
await waitForWake(root, delay);
|
|
194
|
+
} while (true);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (isMainModule(import.meta.url)) {
|
|
198
|
+
const args = process.argv.slice(2); const once = args.includes("--once");
|
|
199
|
+
const rootIndex = args.indexOf("--root");
|
|
200
|
+
if (rootIndex >= 0 && (!args[rootIndex + 1] || args[rootIndex + 1].startsWith("--"))) throw new Error("--root requires a path");
|
|
201
|
+
const root = rootIndex >= 0 ? args[rootIndex + 1] : process.cwd();
|
|
202
|
+
const rosterIndex = args.indexOf("--persona-roster");
|
|
203
|
+
if (rosterIndex >= 0 && (!args[rosterIndex + 1] || args[rosterIndex + 1].startsWith("--"))) {
|
|
204
|
+
throw new Error("--persona-roster requires a path");
|
|
205
|
+
}
|
|
206
|
+
const env = { ...process.env };
|
|
207
|
+
if (rosterIndex >= 0) env.AGENTSPINE_PERSONA_ROSTER_FILE = args[rosterIndex + 1];
|
|
208
|
+
runWorker({ root, once, env }).then((result) => process.stdout.write(JSON.stringify(result) + "\n"))
|
|
209
|
+
.catch((error) => { process.stderr.write("AgentSpine worker: " + String(error.message).slice(0, 2048) + "\n"); process.exitCode = 1; });
|
|
210
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ACTIVE_STEER_INTERRUPT_CODE = 'BLUN_ACTIVE_STEER_INTERRUPT';
|
|
4
|
+
const ACTIVE_STEER_PRIORITY_REMINDER = [
|
|
5
|
+
'A newer user steer is the highest-priority next action.',
|
|
6
|
+
'Act on it now with the available tools. Do not continue an idle prompt or ask for another task.',
|
|
7
|
+
].join(' ');
|
|
8
|
+
|
|
9
|
+
function createActiveSteerInterrupt() {
|
|
10
|
+
const error = new Error('Active model request superseded by a newer user steer');
|
|
11
|
+
error.code = ACTIVE_STEER_INTERRUPT_CODE;
|
|
12
|
+
return error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isActiveSteerInterrupt(error) {
|
|
16
|
+
return error?.code === ACTIVE_STEER_INTERRUPT_CODE;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
module.exports = {
|
|
20
|
+
ACTIVE_STEER_INTERRUPT_CODE,
|
|
21
|
+
ACTIVE_STEER_PRIORITY_REMINDER,
|
|
22
|
+
createActiveSteerInterrupt,
|
|
23
|
+
isActiveSteerInterrupt,
|
|
24
|
+
};
|
package/bin/launcher-runtime.js
CHANGED
|
@@ -31,7 +31,10 @@ const {
|
|
|
31
31
|
const { CORE_LOADED_MESSAGE } = require('./core-bootstrap');
|
|
32
32
|
const { prepareManagedNodeRuntime } = require('./node-runtime');
|
|
33
33
|
const { repairConfiguredNativeModules } = require('./native-module-repair');
|
|
34
|
-
const {
|
|
34
|
+
const {
|
|
35
|
+
resolveMnemoProfileConnectConfig,
|
|
36
|
+
startMnemoConnectHeartbeat,
|
|
37
|
+
} = require('./mnemo-connect-heartbeat.cjs');
|
|
35
38
|
const { recordRuntimeExit } = require('./runtime-exit-ledger.cjs');
|
|
36
39
|
const { acquireSharedRuntimeLease } = require('./update-lease');
|
|
37
40
|
const { runExplicitUpdate, runUpdateNotice } = require('./update-notice');
|
|
@@ -539,10 +542,14 @@ async function runLauncher(options = {}) {
|
|
|
539
542
|
env.BLUN_SHARED_HOME = privatePaths.sharedHome;
|
|
540
543
|
env.BLUN_LOG_HOME = privatePaths.sharedHome;
|
|
541
544
|
env.BLUN_PROFILE = PROFILE.profileName;
|
|
545
|
+
const mnemoProfileConfig = resolveMnemoProfileConnectConfig(path.join(profilePaths.home, 'mcp.json'));
|
|
546
|
+
if (mnemoProfileConfig?.agentName) env.MNEMO_AGENT = mnemoProfileConfig.agentName;
|
|
547
|
+
if (mnemoProfileConfig?.baseUrl) env.BLUN_MNEMO_HUB_URL = mnemoProfileConfig.baseUrl;
|
|
542
548
|
const mnemoConnect = startMnemoConnectHeartbeat({
|
|
543
549
|
env,
|
|
544
550
|
profileName: PROFILE.profileName,
|
|
545
551
|
mcpConfigPath: path.join(profilePaths.home, 'mcp.json'),
|
|
552
|
+
profileConfig: mnemoProfileConfig,
|
|
546
553
|
version: readPackageVersion(),
|
|
547
554
|
});
|
|
548
555
|
try {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const MNEMO_TOOL_PREFIX = 'mcp__mnemo__';
|
|
4
|
+
|
|
5
|
+
function configuredMnemoAgent(env = process.env) {
|
|
6
|
+
const value = typeof env.MNEMO_AGENT === 'string' ? env.MNEMO_AGENT.trim() : '';
|
|
7
|
+
return value && !/[\u0000-\u001f\u007f]/u.test(value) && value.length <= 128 ? value : null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function applyMnemoAgentToToolArgs(toolName, args, env = process.env) {
|
|
11
|
+
if (typeof toolName !== 'string' || !toolName.toLowerCase().startsWith(MNEMO_TOOL_PREFIX)) return args;
|
|
12
|
+
if (!args || typeof args !== 'object' || Array.isArray(args) || !Object.hasOwn(args, 'agent_name')) return args;
|
|
13
|
+
const agentName = configuredMnemoAgent(env);
|
|
14
|
+
if (!agentName || args.agent_name === agentName) return args;
|
|
15
|
+
return { ...args, agent_name: agentName };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
module.exports = {
|
|
19
|
+
MNEMO_TOOL_PREFIX,
|
|
20
|
+
applyMnemoAgentToToolArgs,
|
|
21
|
+
configuredMnemoAgent,
|
|
22
|
+
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
const fs = require('node:fs');
|
|
5
|
+
const os = require('node:os');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
|
|
8
|
+
function isPathInside(root, candidate) {
|
|
9
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
10
|
+
return relative !== '' && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function cacheBase(options = {}) {
|
|
14
|
+
if (options.cacheBase) return path.resolve(options.cacheBase);
|
|
15
|
+
const env = options.env || process.env;
|
|
16
|
+
const platform = options.platform || process.platform;
|
|
17
|
+
if (env.BLUN_CACHE_DIR) return path.resolve(env.BLUN_CACHE_DIR);
|
|
18
|
+
if (platform === 'win32') {
|
|
19
|
+
return path.resolve(env.LOCALAPPDATA || path.join(options.homeDir || os.homedir(), 'AppData', 'Local'), 'blun');
|
|
20
|
+
}
|
|
21
|
+
if (platform === 'darwin') return path.join(options.homeDir || os.homedir(), 'Library', 'Caches', 'blun');
|
|
22
|
+
return path.join(env.XDG_CACHE_HOME || path.join(options.homeDir || os.homedir(), '.cache'), 'blun');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function sha256(bytes) {
|
|
26
|
+
return crypto.createHash('sha256').update(bytes).digest('hex');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function ensureCachedFile(sourcePath, targetPath, expectedHash) {
|
|
30
|
+
try {
|
|
31
|
+
if (sha256(fs.readFileSync(targetPath)) === expectedHash) return targetPath;
|
|
32
|
+
} catch {}
|
|
33
|
+
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
|
|
34
|
+
const temporary = `${targetPath}.${process.pid}.${Date.now()}.tmp`;
|
|
35
|
+
fs.copyFileSync(sourcePath, temporary);
|
|
36
|
+
try {
|
|
37
|
+
fs.renameSync(temporary, targetPath);
|
|
38
|
+
} catch (error) {
|
|
39
|
+
try {
|
|
40
|
+
if (sha256(fs.readFileSync(targetPath)) === expectedHash) {
|
|
41
|
+
fs.rmSync(temporary, { force: true });
|
|
42
|
+
return targetPath;
|
|
43
|
+
}
|
|
44
|
+
} catch {}
|
|
45
|
+
fs.rmSync(temporary, { force: true });
|
|
46
|
+
throw error;
|
|
47
|
+
}
|
|
48
|
+
return targetPath;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resolveNativeRuntimePath(requestPath, options = {}) {
|
|
52
|
+
if (typeof requestPath !== 'string' || path.extname(requestPath).toLowerCase() !== '.node') return requestPath;
|
|
53
|
+
const packageRoot = options.packageRoot && path.resolve(options.packageRoot);
|
|
54
|
+
const sourcePath = path.resolve(requestPath);
|
|
55
|
+
if (!packageRoot || !isPathInside(packageRoot, sourcePath)) return requestPath;
|
|
56
|
+
let bytes;
|
|
57
|
+
try {
|
|
58
|
+
bytes = fs.readFileSync(sourcePath);
|
|
59
|
+
} catch {
|
|
60
|
+
return requestPath;
|
|
61
|
+
}
|
|
62
|
+
const hash = sha256(bytes);
|
|
63
|
+
const relativePath = path.relative(packageRoot, sourcePath);
|
|
64
|
+
const relativeHash = sha256(Buffer.from(relativePath.replaceAll('\\', '/'))).slice(0, 16);
|
|
65
|
+
const targetPath = path.join(cacheBase(options), 'native-runtime', hash, `${relativeHash}-${path.basename(sourcePath)}`);
|
|
66
|
+
try {
|
|
67
|
+
return ensureCachedFile(sourcePath, targetPath, hash);
|
|
68
|
+
} catch {
|
|
69
|
+
return requestPath;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
module.exports = {
|
|
74
|
+
isPathInside,
|
|
75
|
+
resolveNativeRuntimePath,
|
|
76
|
+
};
|