@stratusagent/gateway 0.11.4 → 0.11.6

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.
@@ -0,0 +1,598 @@
1
+ import { chmodSync, mkdirSync } from 'node:fs';
2
+ import { lstat, readdir } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { DatabaseSync } from 'node:sqlite';
5
+ import { agentSessionDbIn, agentStateDirIn, agentsDirIn, assertDerivedStatePathSync, assertPathSafeAgentId, fleetDbIn, } from '@stratusagent/state';
6
+ /**
7
+ * Sessions live one database per agent — `agents/<id>/sessions.db` — with
8
+ * one fleet-wide index saying which agent's store holds a given session id.
9
+ *
10
+ * The sharding is the point (step 15's layer A): a store is opened on one
11
+ * agent's path, so there is no query that could return another agent's
12
+ * conversations. The index is what the sharding costs. Session ids are
13
+ * caller-chosen and the control API resolves them *without* an agent —
14
+ * `GET /sessions/:id`, a message to an existing session — which was
15
+ * unambiguous only because one shared database's primary key made a
16
+ * duplicate id unrepresentable. The index restores that: it is where an id
17
+ * is claimed, and it is what a lookup with no agent in hand consults.
18
+ *
19
+ * It also carries `status`, `created_at`, and `updated_at`, because the
20
+ * session surface is fleet-wide in more places than the point lookup: the
21
+ * health counts, the roster's per-agent activity, the unfiltered listing,
22
+ * and the restart sweep that resumes parked approvals and fails abandoned
23
+ * turns. Answering those from the index rather than by fanning out over
24
+ * every shard is what keeps a recovery sweep from walking only whichever
25
+ * store happened to be open — an agent whose parked approval never
26
+ * resumes.
27
+ */
28
+ const SESSIONS_TABLE = `
29
+ CREATE TABLE IF NOT EXISTS sessions (
30
+ id TEXT PRIMARY KEY,
31
+ agent_id TEXT NOT NULL,
32
+ status TEXT NOT NULL,
33
+ body TEXT NOT NULL,
34
+ created_at TEXT NOT NULL,
35
+ updated_at TEXT NOT NULL
36
+ )
37
+ `;
38
+ /**
39
+ * The same columns minus the conversation body: the index is a routing
40
+ * table and a set of claimed ids, never a second copy of the transcript.
41
+ * Nothing about a conversation can be read out of the fleet database.
42
+ */
43
+ const SESSION_INDEX_TABLE = `
44
+ CREATE TABLE IF NOT EXISTS session_index (
45
+ id TEXT PRIMARY KEY,
46
+ agent_id TEXT NOT NULL,
47
+ status TEXT NOT NULL,
48
+ created_at TEXT NOT NULL,
49
+ updated_at TEXT NOT NULL
50
+ )
51
+ `;
52
+ /**
53
+ * Sessions hold complete conversations (prompts, replies, tool output,
54
+ * provider replay state) — owner-only, like the credentials file. The file
55
+ * chmods cover databases created earlier under a looser umask too;
56
+ * directories the stores create are born 0700.
57
+ *
58
+ * Exported because the layout migration writes the same files from
59
+ * `@stratusagent/state` before any store is open, and a second hand-rolled
60
+ * chmod loop is how one of the two ends up leaving a sidecar readable.
61
+ */
62
+ export const tightenSqliteFile = (filePath) => {
63
+ // The database file is tightened FIRST: SQLite derives sidecar
64
+ // permissions from the main file's mode, so everything created later
65
+ // inherits owner-only.
66
+ for (const sensitive of [filePath, `${filePath}-wal`, `${filePath}-shm`, `${filePath}-journal`]) {
67
+ try {
68
+ chmodSync(sensitive, 0o600);
69
+ }
70
+ catch (error) {
71
+ // A sidecar that does not exist has nothing to tighten; anything
72
+ // else means session data stays readable — refuse to run over it.
73
+ if (error.code !== 'ENOENT') {
74
+ throw error;
75
+ }
76
+ }
77
+ }
78
+ };
79
+ /**
80
+ * Whether a real file is there — the sweep's test for "this directory holds
81
+ * a shard".
82
+ *
83
+ * `lstat`, so a symlink is not one. `stat` follows it, which would classify
84
+ * a directory holding a linked `sessions.db` as an agent's and hand the
85
+ * sweep a store it then creates, indexes and tightens somewhere outside the
86
+ * home. The old layout reserved none of these names, so an operator's
87
+ * `agents/<something>/sessions.db` pointing elsewhere is theirs, not ours.
88
+ */
89
+ const shardFileExists = async (filePath) => {
90
+ try {
91
+ return (await lstat(filePath)).isFile();
92
+ }
93
+ catch (error) {
94
+ if (error.code === 'ENOENT') {
95
+ return false;
96
+ }
97
+ throw error;
98
+ }
99
+ };
100
+ /**
101
+ * Opens the file, tightens it and its sidecars, and puts the connection in
102
+ * the mode every Stratus database runs in. Shared by the per-agent store,
103
+ * the fleet index, and the schedule store, because the posture is one rule:
104
+ * WAL (a DELETE journal recreates a rollback file on every write, which a
105
+ * one-time chmod could never cover), a busy timeout (these files have a
106
+ * second writer — `stratus schedules cancel` opens its own connection —
107
+ * and WAL serializes writers file-wide), and owner-only throughout.
108
+ */
109
+ const openStratusDatabase = (filePath, options = {}) => {
110
+ const dir = path.dirname(filePath);
111
+ if (options.stateHome !== undefined) {
112
+ // Never through a symlink, at any component below the home — see
113
+ // `assertDerivedStatePath`, which owns that rule. Refused rather than
114
+ // quarantined, unlike the migration: at runtime there is no report to
115
+ // name it in, and a turn that cannot be stored must not read as stored.
116
+ //
117
+ // The file as well as its directory, and `fleet.db` as well as a shard:
118
+ // a link at either is followed just as readily — the table is created,
119
+ // the rows indexed and the mode tightened in whatever it points at, and
120
+ // for the index that is the session ids and the schedule rows leaving
121
+ // the home entirely.
122
+ assertDerivedStatePathSync(options.stateHome, filePath, 'file');
123
+ }
124
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
125
+ // `mkdir`'s mode applies only to what it creates, so a directory an
126
+ // earlier build left at `0755` keeps it without this. The home itself only
127
+ // when it is ours; anything below it is a name we chose.
128
+ // Resolved on both sides before comparing. An embedder can hand us
129
+ // `/srv/shared/state/` with the separator still on it, or a `.` component
130
+ // in the middle; `path.join` normalizes what it builds and `path.dirname`
131
+ // answers in that spelling, so a raw string comparison reads the home as
132
+ // its own child and tightens a directory that is not ours — following the
133
+ // link, if the home is one.
134
+ if (options.stateHome !== undefined
135
+ && (path.resolve(dir) !== path.resolve(options.stateHome) || options.ownedHome === true)) {
136
+ chmodSync(dir, 0o700);
137
+ }
138
+ const db = new DatabaseSync(filePath);
139
+ try {
140
+ chmodSync(filePath, 0o600);
141
+ }
142
+ catch (error) {
143
+ // A database that cannot be tightened must not be used: conversation
144
+ // bodies would stay readable by other local users for the daemon's
145
+ // whole lifetime, silently.
146
+ if (error.code !== 'ENOENT') {
147
+ throw error;
148
+ }
149
+ }
150
+ // Forced into existence here (the user_version pragma is a real page-one
151
+ // write) so the tightening below covers the WAL sidecars for the
152
+ // connection's whole lifetime.
153
+ db.exec('PRAGMA journal_mode = WAL');
154
+ db.exec('PRAGMA busy_timeout = 5000');
155
+ return db;
156
+ };
157
+ export class SqliteSessionStore {
158
+ db;
159
+ constructor(filePath, options = {}) {
160
+ this.db = openStratusDatabase(filePath, options);
161
+ this.db.exec(SESSIONS_TABLE);
162
+ this.db.exec('PRAGMA user_version = 0');
163
+ tightenSqliteFile(filePath);
164
+ }
165
+ write(session) {
166
+ this.db
167
+ .prepare('INSERT OR REPLACE INTO sessions (id, agent_id, status, body, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
168
+ .run(session.id, session.agent.id, session.status, JSON.stringify(session), session.createdAt, session.updatedAt);
169
+ return session;
170
+ }
171
+ async create(input) {
172
+ const now = new Date().toISOString();
173
+ return this.write({ ...input, createdAt: now, updatedAt: now });
174
+ }
175
+ async get(id) {
176
+ const row = this.db.prepare('SELECT body FROM sessions WHERE id = ?').get(id);
177
+ return row ? JSON.parse(row.body) : undefined;
178
+ }
179
+ async save(session) {
180
+ await this.saveReturning(session);
181
+ }
182
+ /**
183
+ * The same save, handing back the row as it was written.
184
+ *
185
+ * For a caller that has to mirror it: the sharded store keeps the fleet
186
+ * index in step, and an index holding a timestamp the store does not is
187
+ * drift the startup reconcile would have to clean up every boot.
188
+ */
189
+ async saveReturning(session) {
190
+ return this.write({ ...session, updatedAt: new Date().toISOString() });
191
+ }
192
+ /**
193
+ * Session ids in a state, oldest first — the index a restarting daemon
194
+ * sweeps for turns parked on a human. The `status` column carries it, so
195
+ * no conversation body is deserialized to answer the question.
196
+ */
197
+ async listIdsByStatus(status) {
198
+ const rows = this.db
199
+ .prepare('SELECT id FROM sessions WHERE status = ? ORDER BY updated_at ASC')
200
+ .all(status);
201
+ return rows.map((row) => row.id);
202
+ }
203
+ /**
204
+ * Every row's routing and lifecycle columns, no bodies — what the
205
+ * startup reconcile compares this shard against the fleet index with.
206
+ */
207
+ rows() {
208
+ const rows = this.db
209
+ .prepare('SELECT id, agent_id, status, created_at, updated_at FROM sessions')
210
+ .all();
211
+ return rows.map((row) => ({
212
+ id: row.id,
213
+ agentId: row.agent_id,
214
+ status: row.status,
215
+ createdAt: row.created_at,
216
+ updatedAt: row.updated_at,
217
+ }));
218
+ }
219
+ close() {
220
+ this.db.close();
221
+ }
222
+ }
223
+ /**
224
+ * Thrown when a session id another agent already holds is claimed.
225
+ *
226
+ * Sessions never cross agent identities — the dispatcher says so when it
227
+ * finds the conversation, and this is the same rule at the durable seam,
228
+ * where a shared database's primary key used to enforce it for free.
229
+ */
230
+ export class SessionIdTakenError extends Error {
231
+ constructor(sessionId, heldBy, claimedBy) {
232
+ super(`Session ${sessionId} already belongs to agent ${heldBy}, so ${claimedBy} cannot open a conversation under that id — `
233
+ + 'sessions never cross agent identities. Give the new conversation an id of its own.');
234
+ this.name = 'SessionIdTakenError';
235
+ }
236
+ }
237
+ /**
238
+ * The fleet's session index, in `fleet.db` beside the schedules.
239
+ *
240
+ * Its own connection rather than a table on the schedule store, for the
241
+ * reason the schedule store gives for its own: `stratus schedules` opens
242
+ * that file from another process, and WAL is what makes two connections on
243
+ * one file routine.
244
+ */
245
+ export class FleetSessionIndex {
246
+ db;
247
+ constructor(filePath, options = {}) {
248
+ this.db = openStratusDatabase(filePath, options);
249
+ this.db.exec(SESSION_INDEX_TABLE);
250
+ tightenSqliteFile(filePath);
251
+ }
252
+ /**
253
+ * Claim an id for an agent, or refuse it.
254
+ *
255
+ * The claim is the create's single transactional authority: it lands
256
+ * before the shard write, so a crash between the two leaves a claim with
257
+ * no conversation — which the reconcile releases — rather than a
258
+ * conversation no lookup can reach. Re-claiming an id this agent already
259
+ * holds is genuinely a no-op: `create` on an existing id is how a caller
260
+ * re-opens its own session, and `save` claims every id it writes.
261
+ *
262
+ * Taking the id is all this does. It deliberately does NOT write the row
263
+ * it was handed, because the id it is claiming may already have a
264
+ * conversation: overwriting that row's status and timestamps here would
265
+ * announce a transition the shard write has not made yet, and a write
266
+ * that then fails leaves the live daemon answering from a lifecycle row
267
+ * no store agrees with — a pending session missing from recovery until
268
+ * the next restart reconciles it. The row is written by the caller,
269
+ * after the shard write, from what the store actually stored.
270
+ */
271
+ claim(row) {
272
+ // The insert is what decides, not a read before it. `DO NOTHING` makes
273
+ // the row's creation the atomic step: two callers racing for an id no
274
+ // one holds cannot both find it free, because one of their inserts is
275
+ // the conflict. The read after it is then a question with an answer —
276
+ // who holds this id now — rather than a guess that a write could have
277
+ // overtaken. Nothing is overwritten on the way, so a claim that loses
278
+ // has not touched the winner's row.
279
+ this.db
280
+ .prepare('INSERT INTO session_index (id, agent_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING')
281
+ .run(row.id, row.agentId, row.status, row.createdAt, row.updatedAt);
282
+ const held = this.agentFor(row.id);
283
+ if (held !== undefined && held !== row.agentId) {
284
+ throw new SessionIdTakenError(row.id, held, row.agentId);
285
+ }
286
+ }
287
+ /** Write the row as it now stands. Called after every shard write. */
288
+ record(row) {
289
+ this.db
290
+ .prepare('INSERT OR REPLACE INTO session_index (id, agent_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?)')
291
+ .run(row.id, row.agentId, row.status, row.createdAt, row.updatedAt);
292
+ }
293
+ /** Drop a claim — a reconcile releasing an id whose conversation never landed. */
294
+ release(id) {
295
+ this.db.prepare('DELETE FROM session_index WHERE id = ?').run(id);
296
+ }
297
+ agentFor(id) {
298
+ const row = this.db.prepare('SELECT agent_id FROM session_index WHERE id = ?').get(id);
299
+ return row?.agent_id;
300
+ }
301
+ rows() {
302
+ const rows = this.db
303
+ .prepare('SELECT id, agent_id, status, created_at, updated_at FROM session_index')
304
+ .all();
305
+ return rows.map((row) => ({
306
+ id: row.id,
307
+ agentId: row.agent_id,
308
+ status: row.status,
309
+ createdAt: row.created_at,
310
+ updatedAt: row.updated_at,
311
+ }));
312
+ }
313
+ idsByStatus(status) {
314
+ const rows = this.db
315
+ .prepare('SELECT id FROM session_index WHERE status = ? ORDER BY updated_at ASC')
316
+ .all(status);
317
+ return rows.map((row) => row.id);
318
+ }
319
+ countByStatus() {
320
+ const rows = this.db
321
+ .prepare('SELECT status, COUNT(*) AS total FROM session_index GROUP BY status')
322
+ .all();
323
+ const counts = {};
324
+ for (const row of rows) {
325
+ counts[row.status] = Number(row.total);
326
+ }
327
+ return counts;
328
+ }
329
+ lastActivityByAgent() {
330
+ const rows = this.db
331
+ .prepare(`
332
+ SELECT agent_id,
333
+ MAX(updated_at) AS last_active_at,
334
+ SUM(CASE WHEN status IN ('running', 'pending_approval') THEN 1 ELSE 0 END) AS active_sessions
335
+ FROM session_index
336
+ GROUP BY agent_id
337
+ `)
338
+ .all();
339
+ const activity = {};
340
+ for (const row of rows) {
341
+ activity[row.agent_id] = {
342
+ lastActiveAt: row.last_active_at,
343
+ activeSessions: Number(row.active_sessions),
344
+ };
345
+ }
346
+ return activity;
347
+ }
348
+ list(agentId, limit) {
349
+ // -1 is SQLite's "no limit", so one prepared statement serves both cases
350
+ // rather than four.
351
+ const bound = limit !== undefined && Number.isInteger(limit) && limit >= 0 ? limit : -1;
352
+ const rows = (agentId
353
+ ? this.db.prepare('SELECT id, agent_id, status, created_at, updated_at FROM session_index WHERE agent_id = ? ORDER BY updated_at DESC LIMIT ?').all(agentId, bound)
354
+ : this.db.prepare('SELECT id, agent_id, status, created_at, updated_at FROM session_index ORDER BY updated_at DESC LIMIT ?').all(bound));
355
+ return rows.map((row) => ({
356
+ id: row.id,
357
+ agentId: row.agent_id,
358
+ status: row.status,
359
+ createdAt: row.created_at,
360
+ updatedAt: row.updated_at,
361
+ }));
362
+ }
363
+ close() {
364
+ this.db.close();
365
+ }
366
+ }
367
+ /**
368
+ * The fleet's session store: one SQLite database per agent, behind the
369
+ * `SessionStore` every runner already takes, with the fleet-wide reads the
370
+ * control API and the restart sweeps need answered across all of them.
371
+ *
372
+ * A runner is handed *this* — and every write goes to the store opened on
373
+ * its own agent's path. What the facade adds is the set of questions that
374
+ * were never about one agent: which agent holds an id, how many sessions
375
+ * are in each state, when each agent last did anything, and which ids are
376
+ * parked or abandoned fleet-wide.
377
+ */
378
+ export class ShardedSessionStore {
379
+ stateDir;
380
+ ownedDirectory;
381
+ index;
382
+ shards = new Map();
383
+ constructor(options) {
384
+ this.stateDir = options.stateDir;
385
+ this.ownedDirectory = options.ownedDirectory ?? false;
386
+ this.index = new FleetSessionIndex(fleetDbIn(options.stateDir), {
387
+ stateHome: options.stateDir,
388
+ ...(this.ownedDirectory ? { ownedHome: true } : {}),
389
+ });
390
+ }
391
+ /**
392
+ * One agent's store, opened on first use and kept open.
393
+ *
394
+ * Opened lazily because a roster's agents are not all busy: a daemon
395
+ * with forty souls should hold descriptors for the ones that have
396
+ * conversations, not for every id on disk.
397
+ */
398
+ shardFor(agentId) {
399
+ const existing = this.shards.get(agentId);
400
+ if (existing !== undefined) {
401
+ return existing;
402
+ }
403
+ // Always owner-only: a per-agent directory is state this repository
404
+ // creates, never a path an embedder pointed at something shared.
405
+ const shard = new SqliteSessionStore(agentSessionDbIn(this.stateDir, agentId), { stateHome: this.stateDir });
406
+ this.shards.set(agentId, shard);
407
+ return shard;
408
+ }
409
+ async create(input) {
410
+ const agentId = input.agent.id;
411
+ assertPathSafeAgentId(agentId);
412
+ const now = new Date().toISOString();
413
+ // The claim first, and it is what refuses a sibling's id: the store
414
+ // write below is only reachable for an id this agent may have.
415
+ this.index.claim({ id: input.id, agentId, status: input.status, createdAt: now, updatedAt: now });
416
+ const session = await this.shardFor(agentId).create(input);
417
+ this.index.record({
418
+ id: session.id,
419
+ agentId,
420
+ status: session.status,
421
+ createdAt: session.createdAt,
422
+ updatedAt: session.updatedAt,
423
+ });
424
+ return session;
425
+ }
426
+ async get(id) {
427
+ const agentId = this.index.agentFor(id);
428
+ // No claim, no conversation. The index is the authority for "which
429
+ // agent's store holds this id" precisely so a lookup never has to
430
+ // guess by opening stores until one answers.
431
+ return agentId === undefined ? undefined : this.shardFor(agentId).get(id);
432
+ }
433
+ async save(session) {
434
+ const agentId = session.agent.id;
435
+ assertPathSafeAgentId(agentId);
436
+ // One id belongs to one agent, and this is the path that could change
437
+ // that quietly. `save` takes whatever agent the session now names: a
438
+ // caller handing back a session whose `agent.id` has changed would
439
+ // write a second copy into the new agent's shard and then repoint the
440
+ // index at it — the original transcript still on disk but in a store
441
+ // nothing resolves to, and the next start refusing to serve at all,
442
+ // because the reconcile finds one id in two shards and says so.
443
+ //
444
+ // The same `claim` as `create`, not a read of the index. Asking who
445
+ // holds the id and *then* writing the shard leaves the whole file write
446
+ // between the question and the answer, so two callers saving an id
447
+ // nobody has indexed yet — different agents, the same id — both see it
448
+ // free and both write. Claiming takes the id in one statement before
449
+ // either shard write can start, so the loser is refused here, where the
450
+ // error can still name the agent it belongs to, instead of at the next
451
+ // start where it is a daemon that will not come up.
452
+ this.index.claim({
453
+ id: session.id,
454
+ agentId,
455
+ status: session.status,
456
+ createdAt: session.createdAt,
457
+ updatedAt: session.updatedAt,
458
+ });
459
+ // The conversation is the truth and the index is derived from it, so
460
+ // the shard write goes first: a crash in between leaves an index row
461
+ // one status behind, which the next start reconciles, rather than an
462
+ // index pointing at a turn the store never took.
463
+ const written = await this.shardFor(agentId).saveReturning(session);
464
+ this.index.record({
465
+ id: written.id,
466
+ agentId,
467
+ status: written.status,
468
+ createdAt: written.createdAt,
469
+ updatedAt: written.updatedAt,
470
+ });
471
+ }
472
+ async listIdsByStatus(status) {
473
+ return this.index.idsByStatus(status);
474
+ }
475
+ countByStatus() {
476
+ return this.index.countByStatus();
477
+ }
478
+ lastActivityByAgent() {
479
+ return this.index.lastActivityByAgent();
480
+ }
481
+ list(agentId, limit) {
482
+ return this.index.list(agentId, limit);
483
+ }
484
+ /**
485
+ * Put the index and the shards back in step, before anything reads
486
+ * either. Two durable writes stand where one atomic primary key used to,
487
+ * and this is what makes a crash between them resolve one way rather
488
+ * than leaving them to disagree:
489
+ *
490
+ * - a claim whose conversation never landed is released, freeing the id;
491
+ * - a conversation the index has lost — or disagrees with — is
492
+ * re-indexed from the shard, which is the record;
493
+ * - the same id in two agents' shards is refused loudly, naming both,
494
+ * because picking one would silently hand somebody else's conversation
495
+ * to whoever asks for that id next.
496
+ */
497
+ async reconcile() {
498
+ const report = { released: [], reindexed: [] };
499
+ const owners = new Map();
500
+ for (const agentId of await this.shardedAgentIds()) {
501
+ // Read through a connection this reconcile owns, and let it go again
502
+ // unless the store was already open: a roster's every agent has a
503
+ // store on disk, and holding one descriptor per agent from start-up
504
+ // would make a big roster pay at rest for a sweep that runs once.
505
+ const cached = this.shards.get(agentId);
506
+ const shard = cached ?? new SqliteSessionStore(agentSessionDbIn(this.stateDir, agentId), { stateHome: this.stateDir });
507
+ try {
508
+ for (const row of shard.rows()) {
509
+ const seen = owners.get(row.id);
510
+ if (seen !== undefined) {
511
+ throw new Error(`Session ${row.id} exists in both ${seen.agentId}'s and ${agentId}'s store. `
512
+ + 'Two agents cannot share a session id; move or remove one of the two `agents/<id>/sessions.db` rows before starting.');
513
+ }
514
+ owners.set(row.id, row);
515
+ }
516
+ }
517
+ finally {
518
+ if (cached === undefined) {
519
+ shard.close();
520
+ }
521
+ }
522
+ }
523
+ const indexed = new Map(this.index.rows().map((row) => [row.id, row]));
524
+ for (const [id] of indexed) {
525
+ if (!owners.has(id)) {
526
+ this.index.release(id);
527
+ report.released.push(id);
528
+ }
529
+ }
530
+ for (const [id, row] of owners) {
531
+ const claim = indexed.get(id);
532
+ if (claim === undefined
533
+ || claim.agentId !== row.agentId
534
+ || claim.status !== row.status
535
+ || claim.updatedAt !== row.updatedAt) {
536
+ this.index.record(row);
537
+ report.reindexed.push(id);
538
+ }
539
+ }
540
+ return report;
541
+ }
542
+ /**
543
+ * Which agents have a store on disk — the roster is not the answer, on
544
+ * purpose: the gateway keeps a missing soul's sessions when it drops the
545
+ * soul, and a recovery sweep that skipped those would strand exactly the
546
+ * conversations nobody is watching.
547
+ */
548
+ async shardedAgentIds() {
549
+ let entries;
550
+ try {
551
+ entries = await readdir(agentsDirIn(this.stateDir), { withFileTypes: true });
552
+ }
553
+ catch (error) {
554
+ if (error.code === 'ENOENT') {
555
+ return [];
556
+ }
557
+ throw error;
558
+ }
559
+ const named = entries
560
+ .filter((entry) => entry.isDirectory())
561
+ .map((entry) => entry.name)
562
+ .filter((name) => {
563
+ try {
564
+ // A directory whose name cannot key a path is not an agent's —
565
+ // `agentStateDirIn` is what says so, and the reason it throws
566
+ // rather than sanitizing.
567
+ agentStateDirIn(this.stateDir, name);
568
+ return true;
569
+ }
570
+ catch {
571
+ return false;
572
+ }
573
+ });
574
+ // And only the ones that already hold a shard. This sweep reads what is
575
+ // there; opening a store *creates* the database and tightens the
576
+ // directory around it, so treating every well-named subdirectory as an
577
+ // agent means a start-up that writes `sessions.db` into whatever an
578
+ // operator keeps under `agents/` — a `backups/` folder, an export — and
579
+ // chmods it to 0700 on the way past. The old layout reserved no such
580
+ // names, so nothing warned them. An agent with no conversations yet has
581
+ // nothing to reconcile either, which is the same answer.
582
+ const held = [];
583
+ for (const name of named) {
584
+ if (await shardFileExists(agentSessionDbIn(this.stateDir, name))) {
585
+ held.push(name);
586
+ }
587
+ }
588
+ return held;
589
+ }
590
+ close() {
591
+ for (const shard of this.shards.values()) {
592
+ shard.close();
593
+ }
594
+ this.shards.clear();
595
+ this.index.close();
596
+ }
597
+ }
598
+ //# sourceMappingURL=sessions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stratusagent/gateway",
3
- "version": "0.11.4",
3
+ "version": "0.11.6",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -18,16 +18,16 @@
18
18
  "node": ">=22.13 <23 || >=23.4"
19
19
  },
20
20
  "dependencies": {
21
- "@stratusagent/core": "0.11.4",
22
- "@stratusagent/agents": "0.11.4",
23
- "@stratusagent/plugins": "0.11.4",
24
- "@stratusagent/executor-local": "0.11.4",
25
- "@stratusagent/state": "0.11.4"
21
+ "@stratusagent/agents": "0.11.6",
22
+ "@stratusagent/core": "0.11.6",
23
+ "@stratusagent/plugins": "0.11.6",
24
+ "@stratusagent/executor-local": "0.11.6",
25
+ "@stratusagent/state": "0.11.6"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^24.6.0",
29
29
  "typescript": "^5.9.3",
30
- "@stratusagent/permissions": "0.11.4"
30
+ "@stratusagent/permissions": "0.11.6"
31
31
  },
32
32
  "license": "MIT",
33
33
  "repository": {