agent-working-memory 0.17.0 → 0.18.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/README.md +321 -319
- package/dist/adapters/common.d.ts +34 -0
- package/dist/adapters/common.d.ts.map +1 -1
- package/dist/adapters/common.js +528 -446
- package/dist/adapters/common.js.map +1 -1
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +19 -0
- package/dist/api/routes.js.map +1 -1
- package/dist/cli/migrate.d.ts.map +1 -1
- package/dist/cli/migrate.js +31 -29
- package/dist/cli/migrate.js.map +1 -1
- package/dist/core/asked-value.d.ts +37 -0
- package/dist/core/asked-value.d.ts.map +1 -0
- package/dist/core/asked-value.js +77 -0
- package/dist/core/asked-value.js.map +1 -0
- package/dist/core/conflicts.d.ts +89 -0
- package/dist/core/conflicts.d.ts.map +1 -0
- package/dist/core/conflicts.js +267 -0
- package/dist/core/conflicts.js.map +1 -0
- package/dist/core/embeddings.d.ts +14 -2
- package/dist/core/embeddings.d.ts.map +1 -1
- package/dist/core/embeddings.js +32 -10
- package/dist/core/embeddings.js.map +1 -1
- package/dist/core/format-recall.d.ts +21 -1
- package/dist/core/format-recall.d.ts.map +1 -1
- package/dist/core/format-recall.js +60 -5
- package/dist/core/format-recall.js.map +1 -1
- package/dist/core/ml-worker.d.ts +24 -1
- package/dist/core/ml-worker.d.ts.map +1 -1
- package/dist/core/ml-worker.js +165 -4
- package/dist/core/ml-worker.js.map +1 -1
- package/dist/core/model-daemon-client.d.ts +77 -0
- package/dist/core/model-daemon-client.d.ts.map +1 -0
- package/dist/core/model-daemon-client.js +383 -0
- package/dist/core/model-daemon-client.js.map +1 -0
- package/dist/core/model-daemon-server.d.ts +71 -0
- package/dist/core/model-daemon-server.d.ts.map +1 -0
- package/dist/core/model-daemon-server.js +363 -0
- package/dist/core/model-daemon-server.js.map +1 -0
- package/dist/core/model-daemon.d.ts +137 -0
- package/dist/core/model-daemon.d.ts.map +1 -0
- package/dist/core/model-daemon.js +231 -0
- package/dist/core/model-daemon.js.map +1 -0
- package/dist/core/query-expander.d.ts +5 -0
- package/dist/core/query-expander.d.ts.map +1 -1
- package/dist/core/query-expander.js +3 -2
- package/dist/core/query-expander.js.map +1 -1
- package/dist/core/recall-config.d.ts +1 -1
- package/dist/core/recall-config.d.ts.map +1 -1
- package/dist/core/recall-config.js +5 -0
- package/dist/core/recall-config.js.map +1 -1
- package/dist/core/rerank-window.d.ts +7 -2
- package/dist/core/rerank-window.d.ts.map +1 -1
- package/dist/core/rerank-window.js +9 -4
- package/dist/core/rerank-window.js.map +1 -1
- package/dist/core/rerank2.d.ts +52 -2
- package/dist/core/rerank2.d.ts.map +1 -1
- package/dist/core/rerank2.js +60 -3
- package/dist/core/rerank2.js.map +1 -1
- package/dist/core/reranker.d.ts +19 -0
- package/dist/core/reranker.d.ts.map +1 -1
- package/dist/core/reranker.js +76 -12
- package/dist/core/reranker.js.map +1 -1
- package/dist/core/trust.d.ts +32 -0
- package/dist/core/trust.d.ts.map +1 -0
- package/dist/core/trust.js +66 -0
- package/dist/core/trust.js.map +1 -0
- package/dist/core/validity.d.ts +21 -0
- package/dist/core/validity.d.ts.map +1 -0
- package/dist/core/validity.js +41 -0
- package/dist/core/validity.js.map +1 -0
- package/dist/core/whoami.d.ts +3 -0
- package/dist/core/whoami.d.ts.map +1 -1
- package/dist/core/whoami.js +14 -0
- package/dist/core/whoami.js.map +1 -1
- package/dist/core/write-pipeline.d.ts +48 -0
- package/dist/core/write-pipeline.d.ts.map +1 -1
- package/dist/core/write-pipeline.js +80 -3
- package/dist/core/write-pipeline.js.map +1 -1
- package/dist/engine/activation.d.ts.map +1 -1
- package/dist/engine/activation.js +27 -4
- package/dist/engine/activation.js.map +1 -1
- package/dist/engine/consolidation.d.ts.map +1 -1
- package/dist/engine/consolidation.js +53 -4
- package/dist/engine/consolidation.js.map +1 -1
- package/dist/hooks/prime-query.d.ts +57 -0
- package/dist/hooks/prime-query.d.ts.map +1 -0
- package/dist/hooks/prime-query.js +388 -0
- package/dist/hooks/prime-query.js.map +1 -0
- package/dist/hooks/prime.d.ts +8 -0
- package/dist/hooks/prime.d.ts.map +1 -1
- package/dist/hooks/prime.js +21 -2
- package/dist/hooks/prime.js.map +1 -1
- package/dist/hooks/sidecar.d.ts +7 -0
- package/dist/hooks/sidecar.d.ts.map +1 -1
- package/dist/hooks/sidecar.js +10 -2
- package/dist/hooks/sidecar.js.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp.js +148 -103
- package/dist/mcp.js.map +1 -1
- package/dist/model-daemon.d.ts +8 -0
- package/dist/model-daemon.d.ts.map +1 -0
- package/dist/model-daemon.js +45 -0
- package/dist/model-daemon.js.map +1 -0
- package/dist/storage/factory.d.ts.map +1 -1
- package/dist/storage/factory.js +5 -0
- package/dist/storage/factory.js.map +1 -1
- package/dist/storage/pglite-schema.d.ts.map +1 -1
- package/dist/storage/pglite-schema.js +176 -170
- package/dist/storage/pglite-schema.js.map +1 -1
- package/dist/storage/pglite.d.ts +5 -0
- package/dist/storage/pglite.d.ts.map +1 -1
- package/dist/storage/pglite.js +165 -153
- package/dist/storage/pglite.js.map +1 -1
- package/dist/storage/postgres.d.ts +5 -0
- package/dist/storage/postgres.d.ts.map +1 -1
- package/dist/storage/postgres.js +165 -153
- package/dist/storage/postgres.js.map +1 -1
- package/dist/storage/sqlite.d.ts +22 -0
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +400 -363
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/types/engram.d.ts +33 -0
- package/dist/types/engram.d.ts.map +1 -1
- package/dist/types/engram.js.map +1 -1
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/README.md +1 -1
- package/plugin/bin/awm-mcp-launcher.cjs +2 -2
- package/plugin/skills/awm-memory/SKILL.md +11 -2
- package/src/adapters/common.ts +881 -802
- package/src/api/routes.ts +20 -0
- package/src/cli/migrate.ts +309 -307
- package/src/core/asked-value.ts +74 -0
- package/src/core/conflicts.ts +280 -0
- package/src/core/embeddings.ts +35 -10
- package/src/core/format-recall.ts +83 -25
- package/src/core/ml-worker.ts +458 -281
- package/src/core/model-daemon-client.ts +374 -0
- package/src/core/model-daemon-server.ts +344 -0
- package/src/core/model-daemon.ts +243 -0
- package/src/core/query-expander.ts +127 -126
- package/src/core/recall-config.ts +5 -0
- package/src/core/rerank-window.ts +164 -159
- package/src/core/rerank2.ts +143 -82
- package/src/core/reranker.ts +78 -11
- package/src/core/trust.ts +65 -0
- package/src/core/validity.ts +47 -0
- package/src/core/whoami.ts +136 -120
- package/src/core/write-pipeline.ts +119 -3
- package/src/engine/activation.ts +27 -4
- package/src/engine/consolidation.ts +950 -899
- package/src/hooks/prime-query.ts +375 -0
- package/src/hooks/prime.ts +25 -2
- package/src/hooks/sidecar.ts +13 -3
- package/src/index.ts +255 -248
- package/src/mcp.ts +1521 -1475
- package/src/model-daemon.ts +48 -0
- package/src/storage/factory.ts +25 -19
- package/src/storage/pglite-schema.ts +199 -193
- package/src/storage/pglite.ts +1513 -1498
- package/src/storage/postgres.ts +1613 -1598
- package/src/storage/sqlite.ts +2056 -2019
- package/src/types/engram.ts +30 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
// Copyright 2026 Robert Winter / Complete Ideas
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
/**
|
|
4
|
+
* Client side of the shared model daemon (see model-daemon.ts).
|
|
5
|
+
*
|
|
6
|
+
* Connecting: try the daemon's address. If nothing answers, take the lockfile (O_EXCL,
|
|
7
|
+
* holding this pid) and spawn the daemon detached, through a launcher so that it is not
|
|
8
|
+
* this process's child (see LAUNCHER); every other process starting at the
|
|
9
|
+
* same moment finds the lock taken and just keeps trying to connect. A lock whose pid is
|
|
10
|
+
* dead, or that is older than the start timeout, is stale and is taken over, so a
|
|
11
|
+
* spawner that crashed mid-start blocks nobody. A daemon this client spawned that exits
|
|
12
|
+
* with an error ends the wait at once instead of at the start timeout.
|
|
13
|
+
*
|
|
14
|
+
* Timeouts: a request waits in the daemon's queue for up to AWM_MODEL_DAEMON_QUEUE_MS
|
|
15
|
+
* (10 min) without failing, because a busy daemon is not a broken one; when it gives up
|
|
16
|
+
* it tells the daemon to drop the job. While the daemon reports a step `started` (and
|
|
17
|
+
* until it reports the job `requeued` between chunks), the step must finish within
|
|
18
|
+
* AWM_MODEL_DAEMON_TIMEOUT_MS (2 min); a daemon that does not is stuck, and the
|
|
19
|
+
* connection is dropped.
|
|
20
|
+
*
|
|
21
|
+
* The client never falls back to in-process inference itself; ml-worker.ts decides that.
|
|
22
|
+
*/
|
|
23
|
+
import { connect } from 'node:net';
|
|
24
|
+
import { spawn } from 'node:child_process';
|
|
25
|
+
import { randomBytes } from 'node:crypto';
|
|
26
|
+
import { closeSync, existsSync, openSync, readFileSync, renameSync, statSync, unlinkSync, writeSync, mkdirSync } from 'node:fs';
|
|
27
|
+
import { dirname, join } from 'node:path';
|
|
28
|
+
import { fileURLToPath } from 'node:url';
|
|
29
|
+
import { PROTOCOL_VERSION, HELLO_MAX_BYTES, MAX_FRAME_BYTES, addressProblem, encodeFrame, envInt, FrameReader, modelDaemonPaths, proof, proofMatches, runDirProblem, EXIT_FINGERPRINT_DRIFT, REQUEST_TIMEOUT_MS, WARM_TIMEOUT_MS, } from './model-daemon.js';
|
|
30
|
+
/** The daemon ran the request and it failed there. In-process would fail the same way. */
|
|
31
|
+
export class DaemonRemoteError extends Error {
|
|
32
|
+
}
|
|
33
|
+
/** The daemon could not be reached or stopped answering. */
|
|
34
|
+
export class DaemonUnavailableError extends Error {
|
|
35
|
+
reason;
|
|
36
|
+
constructor(message, reason) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.reason = reason;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const LOG_MAX_BYTES = 5 * 1024 * 1024;
|
|
42
|
+
const POLL_MS = 150;
|
|
43
|
+
const HELLO_TIMEOUT_MS = 5_000;
|
|
44
|
+
const sleep = (ms) => new Promise(r => setTimeout(r, ms));
|
|
45
|
+
function pidAlive(pid) {
|
|
46
|
+
if (!Number.isInteger(pid) || pid <= 0)
|
|
47
|
+
return false;
|
|
48
|
+
try {
|
|
49
|
+
process.kill(pid, 0);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return err.code === 'EPERM';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Starts the daemon and exits once it listens, passing on its exit code if it stops
|
|
58
|
+
* before that. The daemon's parent is then a process that no longer exists, so a tree
|
|
59
|
+
* kill of the AWM process that started it does not reach it: when Claude Code closes a
|
|
60
|
+
* session whose MCP server does not exit on stdin close, it kills the server's whole
|
|
61
|
+
* process tree, and a daemon spawned directly died with it (measured with Claude Code
|
|
62
|
+
* 2.1.302 on Windows). Inline, so it needs no loader in dev.
|
|
63
|
+
*/
|
|
64
|
+
const LAUNCHER = [
|
|
65
|
+
"const c = require('node:child_process').spawn(process.argv[1], process.argv.slice(2),",
|
|
66
|
+
" { detached: true, windowsHide: true, stdio: ['ignore', 'inherit', 'inherit', 'ipc'] });",
|
|
67
|
+
"c.on('message', () => process.exit(0));",
|
|
68
|
+
"c.on('exit', code => process.exit(code ?? 1));",
|
|
69
|
+
"c.on('error', () => process.exit(1));",
|
|
70
|
+
].join('\n');
|
|
71
|
+
export function defaultDaemonEntry() {
|
|
72
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
73
|
+
const js = join(here, '..', 'model-daemon.js');
|
|
74
|
+
if (existsSync(js))
|
|
75
|
+
return { command: process.execPath, args: [js] };
|
|
76
|
+
// Running from src/ under tsx (dev, tests): the daemon needs the same loader.
|
|
77
|
+
const ts = join(here, '..', 'model-daemon.ts');
|
|
78
|
+
return { command: process.execPath, args: ['--import', import.meta.resolve('tsx'), ts] };
|
|
79
|
+
}
|
|
80
|
+
export class ModelDaemonClient {
|
|
81
|
+
fingerprint;
|
|
82
|
+
paths;
|
|
83
|
+
entry;
|
|
84
|
+
spawnEnv;
|
|
85
|
+
startTimeoutMs;
|
|
86
|
+
requestTimeoutMs;
|
|
87
|
+
queueTimeoutMs;
|
|
88
|
+
socket = null;
|
|
89
|
+
connecting = null;
|
|
90
|
+
pending = new Map();
|
|
91
|
+
nextId = 1;
|
|
92
|
+
daemonPid = null;
|
|
93
|
+
spawned = 0;
|
|
94
|
+
constructor(opts) {
|
|
95
|
+
this.fingerprint = opts.fingerprint;
|
|
96
|
+
this.paths = modelDaemonPaths(this.fingerprint, opts.runDir);
|
|
97
|
+
this.entry = opts.entry ?? defaultDaemonEntry();
|
|
98
|
+
this.spawnEnv = opts.spawnEnv ?? {};
|
|
99
|
+
this.startTimeoutMs = opts.startTimeoutMs ?? envInt('AWM_MODEL_DAEMON_START_MS', 20_000);
|
|
100
|
+
this.requestTimeoutMs = opts.requestTimeoutMs ?? envInt('AWM_MODEL_DAEMON_TIMEOUT_MS', REQUEST_TIMEOUT_MS);
|
|
101
|
+
this.queueTimeoutMs = opts.queueTimeoutMs ?? envInt('AWM_MODEL_DAEMON_QUEUE_MS', 10 * 60_000);
|
|
102
|
+
}
|
|
103
|
+
get connected() { return !!this.socket && !this.socket.destroyed; }
|
|
104
|
+
async request(op, args, lane = 'interactive') {
|
|
105
|
+
const socket = await this.ensureConnected();
|
|
106
|
+
const id = this.nextId++;
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
const p = { op, resolve, reject, timer: this.queueTimer(id, op) };
|
|
109
|
+
this.pending.set(id, p);
|
|
110
|
+
socket.write(encodeFrame({ id, op, lane, args }));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
close() {
|
|
114
|
+
this.socket?.destroy();
|
|
115
|
+
this.socket = null;
|
|
116
|
+
}
|
|
117
|
+
/** Waiting in the queue: fail this request only, the daemon is busy, not gone. */
|
|
118
|
+
queueTimer(id, op) {
|
|
119
|
+
return setTimeout(() => {
|
|
120
|
+
const p = this.pending.get(id);
|
|
121
|
+
if (!p)
|
|
122
|
+
return;
|
|
123
|
+
this.pending.delete(id);
|
|
124
|
+
p.reject(new DaemonUnavailableError(`model daemon: ${op} still queued after ${this.queueTimeoutMs} ms`, 'queue-timeout'));
|
|
125
|
+
if (this.socket && !this.socket.destroyed)
|
|
126
|
+
this.socket.write(encodeFrame({ id: this.nextId++, op: 'cancel', args: { id } }));
|
|
127
|
+
}, this.queueTimeoutMs);
|
|
128
|
+
}
|
|
129
|
+
/** Running: no answer and no progress in time means the daemon is stuck. */
|
|
130
|
+
runTimer(id, op) {
|
|
131
|
+
const ms = op === 'warm' ? Math.max(this.requestTimeoutMs, WARM_TIMEOUT_MS) : this.requestTimeoutMs;
|
|
132
|
+
return setTimeout(() => {
|
|
133
|
+
const p = this.pending.get(id);
|
|
134
|
+
if (!p)
|
|
135
|
+
return;
|
|
136
|
+
this.pending.delete(id);
|
|
137
|
+
p.reject(new DaemonUnavailableError(`model daemon: ${op} step ran over ${ms} ms`, 'timeout'));
|
|
138
|
+
this.socket?.destroy();
|
|
139
|
+
}, ms);
|
|
140
|
+
}
|
|
141
|
+
ensureConnected() {
|
|
142
|
+
if (this.socket && !this.socket.destroyed)
|
|
143
|
+
return Promise.resolve(this.socket);
|
|
144
|
+
this.connecting ??= this.connectOrSpawn().finally(() => { this.connecting = null; });
|
|
145
|
+
return this.connecting;
|
|
146
|
+
}
|
|
147
|
+
async connectOrSpawn() {
|
|
148
|
+
const problem = addressProblem(this.paths);
|
|
149
|
+
if (problem)
|
|
150
|
+
throw new DaemonUnavailableError(problem, 'socket-path-too-long');
|
|
151
|
+
if (existsSync(this.paths.runDir)) {
|
|
152
|
+
const unsafe = runDirProblem(this.paths.runDir);
|
|
153
|
+
if (unsafe)
|
|
154
|
+
throw new DaemonUnavailableError(unsafe, 'run-dir-unsafe');
|
|
155
|
+
}
|
|
156
|
+
const deadline = Date.now() + this.startTimeoutMs;
|
|
157
|
+
let holdsLock = false;
|
|
158
|
+
let exited = null;
|
|
159
|
+
let last = 'unreachable';
|
|
160
|
+
try {
|
|
161
|
+
for (;;) {
|
|
162
|
+
const r = await this.tryConnect();
|
|
163
|
+
if (r.socket)
|
|
164
|
+
return r.socket;
|
|
165
|
+
last = r.reason;
|
|
166
|
+
// A daemon of a different protocol or config, or one that cannot prove it holds
|
|
167
|
+
// the token, will not change by waiting.
|
|
168
|
+
if (r.fatal)
|
|
169
|
+
throw new DaemonUnavailableError(`model daemon refused: ${r.reason}`, r.reason);
|
|
170
|
+
if (exited === EXIT_FINGERPRINT_DRIFT) {
|
|
171
|
+
// This process computed its fingerprint from code that has since changed on disk
|
|
172
|
+
// (a rebuild, an update): a daemon started now is a different daemon.
|
|
173
|
+
throw new DaemonUnavailableError('model daemon code changed on disk since this process started; restart the session to use the daemon', 'code-changed');
|
|
174
|
+
}
|
|
175
|
+
if (exited !== null && exited !== 0) {
|
|
176
|
+
throw new DaemonUnavailableError(`model daemon exited with code ${exited} during start; see ${this.paths.log}`, 'daemon-exited');
|
|
177
|
+
}
|
|
178
|
+
if (!holdsLock && !['bad token', 'starting'].includes(r.reason) && this.takeLock()) {
|
|
179
|
+
holdsLock = true;
|
|
180
|
+
// Exit code 0 is a daemon that lost the start race to another: keep polling.
|
|
181
|
+
this.spawnDaemon(code => { exited = code; });
|
|
182
|
+
}
|
|
183
|
+
if (Date.now() >= deadline)
|
|
184
|
+
break;
|
|
185
|
+
await sleep(POLL_MS);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
if (holdsLock)
|
|
190
|
+
this.releaseLock();
|
|
191
|
+
}
|
|
192
|
+
throw new DaemonUnavailableError(`model daemon did not start within ${this.startTimeoutMs} ms (${last}); see ${this.paths.log}`, 'start-timeout');
|
|
193
|
+
}
|
|
194
|
+
tryConnect() {
|
|
195
|
+
return new Promise(resolve => {
|
|
196
|
+
let settled = false;
|
|
197
|
+
const done = (v) => {
|
|
198
|
+
if (settled)
|
|
199
|
+
return;
|
|
200
|
+
settled = true;
|
|
201
|
+
clearTimeout(timer);
|
|
202
|
+
if (!v.socket)
|
|
203
|
+
socket.destroy();
|
|
204
|
+
resolve(v);
|
|
205
|
+
};
|
|
206
|
+
const socket = connect(this.paths.address);
|
|
207
|
+
socket.setEncoding('utf8');
|
|
208
|
+
const timer = setTimeout(() => done({ reason: 'hello timeout' }), HELLO_TIMEOUT_MS);
|
|
209
|
+
socket.once('error', err => done({ reason: err.code ?? err.message }));
|
|
210
|
+
socket.once('close', () => done({ reason: 'closed during hello' }));
|
|
211
|
+
socket.once('connect', () => {
|
|
212
|
+
// The token is read when the challenge arrives, not now: a daemon that has
|
|
213
|
+
// answered has written its token, while one that only just started listening may
|
|
214
|
+
// not have replaced a dead daemon's file yet.
|
|
215
|
+
const readToken = () => { try {
|
|
216
|
+
return readFileSync(this.paths.token, 'utf8').trim();
|
|
217
|
+
}
|
|
218
|
+
catch {
|
|
219
|
+
return '';
|
|
220
|
+
} };
|
|
221
|
+
let token = '';
|
|
222
|
+
const nonce = randomBytes(16).toString('hex');
|
|
223
|
+
let stage = 'challenge';
|
|
224
|
+
let daemonPid = null;
|
|
225
|
+
const reader = new FrameReader(frame => {
|
|
226
|
+
if (stage === 'ready')
|
|
227
|
+
return this.onResponse(frame);
|
|
228
|
+
const f = frame;
|
|
229
|
+
if (f?.t === 'hello' && !f.ok) {
|
|
230
|
+
const reason = f.error ?? 'refused';
|
|
231
|
+
// 'starting': the daemon has not written its token yet. 'closing': it is
|
|
232
|
+
// exiting on its lease. 'bad token': our proof used a stale file. All three
|
|
233
|
+
// clear up by trying again.
|
|
234
|
+
return done({ reason, fatal: !['bad token', 'closing', 'starting'].includes(reason) });
|
|
235
|
+
}
|
|
236
|
+
if (stage === 'challenge') {
|
|
237
|
+
if (f?.t !== 'challenge')
|
|
238
|
+
return done({ reason: 'unexpected handshake frame', fatal: true });
|
|
239
|
+
const verifies = (t) => !!t && proofMatches(proof(t, 'daemon', nonce, String(f.nonce), this.fingerprint), f.proof);
|
|
240
|
+
token = readToken();
|
|
241
|
+
if (!verifies(token)) {
|
|
242
|
+
token = readToken(); // a rename may have landed between the two reads
|
|
243
|
+
if (!verifies(token)) {
|
|
244
|
+
// Whatever answered on our address does not hold our token: send it nothing.
|
|
245
|
+
return done({ reason: 'daemon failed authentication', fatal: true });
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
daemonPid = f.pid ?? null;
|
|
249
|
+
stage = 'result';
|
|
250
|
+
socket.write(encodeFrame({ t: 'auth', proof: proof(token, 'client', nonce, String(f.nonce), this.fingerprint) }));
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
if (f?.t === 'hello' && f.ok) {
|
|
254
|
+
stage = 'ready';
|
|
255
|
+
reader.limit = MAX_FRAME_BYTES;
|
|
256
|
+
this.daemonPid = daemonPid;
|
|
257
|
+
this.attach(socket);
|
|
258
|
+
return done({ socket, reason: 'ok' });
|
|
259
|
+
}
|
|
260
|
+
return done({ reason: 'unexpected handshake frame', fatal: true });
|
|
261
|
+
}, HELLO_MAX_BYTES);
|
|
262
|
+
socket.on('data', (chunk) => { if (!reader.push(chunk))
|
|
263
|
+
socket.destroy(); });
|
|
264
|
+
socket.write(encodeFrame({ t: 'hello', protocol: PROTOCOL_VERSION, fingerprint: this.fingerprint, nonce }));
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
attach(socket) {
|
|
269
|
+
this.socket = socket;
|
|
270
|
+
socket.removeAllListeners('close');
|
|
271
|
+
socket.removeAllListeners('error');
|
|
272
|
+
socket.on('error', () => { });
|
|
273
|
+
socket.on('close', () => {
|
|
274
|
+
if (this.socket === socket)
|
|
275
|
+
this.socket = null;
|
|
276
|
+
for (const [, p] of this.pending) {
|
|
277
|
+
clearTimeout(p.timer);
|
|
278
|
+
p.reject(new DaemonUnavailableError('model daemon disconnected', 'disconnected'));
|
|
279
|
+
}
|
|
280
|
+
this.pending.clear();
|
|
281
|
+
});
|
|
282
|
+
// The connection must not keep an otherwise finished process alive.
|
|
283
|
+
socket.unref();
|
|
284
|
+
}
|
|
285
|
+
onResponse(frame) {
|
|
286
|
+
const p = this.pending.get(frame?.id);
|
|
287
|
+
if (!p)
|
|
288
|
+
return;
|
|
289
|
+
if (frame.started) {
|
|
290
|
+
clearTimeout(p.timer);
|
|
291
|
+
p.timer = this.runTimer(frame.id, p.op);
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
if (frame.requeued) {
|
|
295
|
+
clearTimeout(p.timer);
|
|
296
|
+
p.timer = this.queueTimer(frame.id, p.op);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
this.pending.delete(frame.id);
|
|
300
|
+
clearTimeout(p.timer);
|
|
301
|
+
if (frame.ok)
|
|
302
|
+
p.resolve(frame.result);
|
|
303
|
+
else
|
|
304
|
+
p.reject(new DaemonRemoteError(frame.error ?? 'model daemon error'));
|
|
305
|
+
}
|
|
306
|
+
takeLock() {
|
|
307
|
+
mkdirSync(this.paths.runDir, { recursive: true, mode: 0o700 });
|
|
308
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
309
|
+
try {
|
|
310
|
+
const fd = openSync(this.paths.lock, 'wx', 0o600);
|
|
311
|
+
writeSync(fd, JSON.stringify({ pid: process.pid, at: Date.now() }));
|
|
312
|
+
closeSync(fd);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
catch (err) {
|
|
316
|
+
if (err.code !== 'EEXIST')
|
|
317
|
+
throw err;
|
|
318
|
+
let holder = {};
|
|
319
|
+
try {
|
|
320
|
+
holder = JSON.parse(readFileSync(this.paths.lock, 'utf8'));
|
|
321
|
+
}
|
|
322
|
+
catch { /* half-written: judge by age */ }
|
|
323
|
+
const age = Date.now() - (holder.at ?? statMtime(this.paths.lock));
|
|
324
|
+
if (pidAlive(holder.pid ?? -1) && age < this.startTimeoutMs)
|
|
325
|
+
return false;
|
|
326
|
+
// Two processes can both judge it stale and both start a daemon; the second to
|
|
327
|
+
// listen gets EADDRINUSE and exits 0, so the race costs one process start.
|
|
328
|
+
try {
|
|
329
|
+
unlinkSync(this.paths.lock);
|
|
330
|
+
}
|
|
331
|
+
catch { /* another process took it over first */ }
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
releaseLock() {
|
|
337
|
+
try {
|
|
338
|
+
const holder = JSON.parse(readFileSync(this.paths.lock, 'utf8'));
|
|
339
|
+
if (holder.pid === process.pid)
|
|
340
|
+
unlinkSync(this.paths.lock);
|
|
341
|
+
}
|
|
342
|
+
catch { /* gone already */ }
|
|
343
|
+
}
|
|
344
|
+
spawnDaemon(onExit) {
|
|
345
|
+
let fd = 'ignore';
|
|
346
|
+
try {
|
|
347
|
+
if (existsSync(this.paths.log) && statSync(this.paths.log).size > LOG_MAX_BYTES) {
|
|
348
|
+
renameSync(this.paths.log, `${this.paths.log}.1`);
|
|
349
|
+
}
|
|
350
|
+
fd = openSync(this.paths.log, 'a', 0o600);
|
|
351
|
+
}
|
|
352
|
+
catch { /* no log, still start */ }
|
|
353
|
+
try {
|
|
354
|
+
// The exit code is the launcher's: 0 once the daemon listens (or lost the race),
|
|
355
|
+
// otherwise the daemon's own.
|
|
356
|
+
const child = spawn(process.execPath, ['-e', LAUNCHER, '--', this.entry.command, ...this.entry.args], {
|
|
357
|
+
// Not the session's project directory: on Windows a process's cwd cannot be
|
|
358
|
+
// deleted or renamed, and the daemon outlives the session that started it.
|
|
359
|
+
cwd: this.paths.runDir,
|
|
360
|
+
detached: true, windowsHide: true,
|
|
361
|
+
env: { ...process.env, ...this.spawnEnv, AWM_MODEL_DAEMON_FINGERPRINT: this.fingerprint },
|
|
362
|
+
stdio: ['ignore', fd, fd],
|
|
363
|
+
});
|
|
364
|
+
child.on('error', err => { console.error(`[awm] model daemon spawn failed: ${err.message}`); onExit(-1); });
|
|
365
|
+
child.on('exit', code => onExit(code ?? -1));
|
|
366
|
+
child.unref();
|
|
367
|
+
this.spawned++;
|
|
368
|
+
}
|
|
369
|
+
finally {
|
|
370
|
+
if (typeof fd === 'number')
|
|
371
|
+
closeSync(fd);
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function statMtime(path) {
|
|
376
|
+
try {
|
|
377
|
+
return statSync(path).mtimeMs;
|
|
378
|
+
}
|
|
379
|
+
catch {
|
|
380
|
+
return 0;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
//# sourceMappingURL=model-daemon-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-daemon-client.js","sourceRoot":"","sources":["../../src/core/model-daemon-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared model daemon process (see model-daemon.ts for the design).
|
|
3
|
+
*
|
|
4
|
+
* It runs ONE inference at a time. onnxruntime-node's CPU arena grows to the largest
|
|
5
|
+
* tensor a session has run, so concurrent runs on the same session would multiply the
|
|
6
|
+
* high-water mark this daemon exists to bound. One at a time costs no speed: four
|
|
7
|
+
* processes recalling at once on one machine finished in 12.3 min with their own models
|
|
8
|
+
* and 11.6 min through one daemon, because they compete for the same cores anyway.
|
|
9
|
+
*
|
|
10
|
+
* Scheduling: interactive requests run before background ones. An embed is run one
|
|
11
|
+
* chunk at a time; an unfinished interactive embed goes to the back of its lane, so
|
|
12
|
+
* sessions take turns and one session's 100-memory write-batch cannot hold up another
|
|
13
|
+
* session's recall; an unfinished background embed goes to the front of its lane, so
|
|
14
|
+
* its chunks stay in order. A request whose connection has closed is dropped unrun.
|
|
15
|
+
* Each step reports `started` when it begins, and a job sent back to the queue between
|
|
16
|
+
* chunks reports `requeued`, so a client's run timer covers only time the daemon spends
|
|
17
|
+
* on its job: its timeout measures a stuck daemon, never a busy one. A step that runs
|
|
18
|
+
* longer than AWM_MODEL_DAEMON_STUCK_MS (default: the client's step timeout, 2 min; for
|
|
19
|
+
* a warm, which may download the models, the client's 10 min) means the daemon is wedged
|
|
20
|
+
* on a promise that will never settle: it exits, and the next client starts a fresh one.
|
|
21
|
+
* The two limits are the same so that a client that gave up on a step is never left
|
|
22
|
+
* reconnecting to the daemon still stuck on it.
|
|
23
|
+
*
|
|
24
|
+
* The daemon exits when no authenticated client has been connected for
|
|
25
|
+
* AWM_MODEL_DAEMON_IDLE_MS (default 5 min). Clients hold one connection each for the
|
|
26
|
+
* life of their process.
|
|
27
|
+
*/
|
|
28
|
+
import { type Server } from 'node:net';
|
|
29
|
+
export interface DaemonHandlers {
|
|
30
|
+
embed(args: {
|
|
31
|
+
texts: string[];
|
|
32
|
+
pooling: 'cls' | 'mean';
|
|
33
|
+
dimensions: number;
|
|
34
|
+
}): Promise<number[][]>;
|
|
35
|
+
rerank(args: {
|
|
36
|
+
query: string;
|
|
37
|
+
passages: string[];
|
|
38
|
+
}): Promise<Array<{
|
|
39
|
+
index: number;
|
|
40
|
+
score: number;
|
|
41
|
+
}>>;
|
|
42
|
+
expand(args: {
|
|
43
|
+
prompt: string;
|
|
44
|
+
maxNewTokens: number;
|
|
45
|
+
noRepeatNgramSize: number;
|
|
46
|
+
}): Promise<string>;
|
|
47
|
+
warm(): Promise<void>;
|
|
48
|
+
/** Texts per scheduling step for an embed; 0 = the whole request at once. */
|
|
49
|
+
embedChunk: number;
|
|
50
|
+
}
|
|
51
|
+
export interface DaemonOptions {
|
|
52
|
+
fingerprint: string;
|
|
53
|
+
runDir?: string;
|
|
54
|
+
idleMs?: number;
|
|
55
|
+
handlers: DaemonHandlers;
|
|
56
|
+
log?: (msg: string) => void;
|
|
57
|
+
/** Called when the idle lease runs out, after the server has closed. The entry exits. */
|
|
58
|
+
onIdle?: () => void;
|
|
59
|
+
/** A step running longer than this is wedged (a warm gets WARM_TIMEOUT_MS at least). */
|
|
60
|
+
stuckMs?: number;
|
|
61
|
+
/** Called when a step is wedged. The entry exits with an error. */
|
|
62
|
+
onStuck?: () => void;
|
|
63
|
+
}
|
|
64
|
+
export interface RunningDaemon {
|
|
65
|
+
server: Server;
|
|
66
|
+
address: string;
|
|
67
|
+
fingerprint: string;
|
|
68
|
+
close(): Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
export declare function startModelDaemon(opts: DaemonOptions): Promise<RunningDaemon>;
|
|
71
|
+
//# sourceMappingURL=model-daemon-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-daemon-server.d.ts","sourceRoot":"","sources":["../../src/core/model-daemon-server.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EAAyB,KAAK,MAAM,EAAe,MAAM,UAAU,CAAC;AAW3E,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,EAAE,CAAC;QAAC,OAAO,EAAE,KAAK,GAAG,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACnG,MAAM,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;KAAE,GAAG,OAAO,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACtG,MAAM,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,iBAAiB,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,6EAA6E;IAC7E,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5B,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,wFAAwF;IACxF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAaD,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AA4BD,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,aAAa,CAAC,CA2OlF"}
|