@stratusagent/gateway 0.11.4 → 0.11.5

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,592 @@
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, assertPathSafeAgentId, fleetDbIn, isSymlinkedStatePathSync, symlinkedStateDirectoryMessage, symlinkedStateFileMessage, } 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.perAgent) {
112
+ // Never a symlink — see `isSymlinkedStatePath`, which owns that rule.
113
+ // Refused rather than quarantined, unlike the migration: at runtime
114
+ // there is no report to name it in, and a turn that cannot be stored
115
+ // must not read as stored.
116
+ //
117
+ // Under `perAgent` and not `ownedDirectory`, which is the distinction
118
+ // this comment used to describe while the code ignored it: `~/.stratus`
119
+ // is a symlink on plenty of real installs (a home on another disk), and
120
+ // the fleet index and the schedule store live directly in it.
121
+ if (isSymlinkedStatePathSync(dir)) {
122
+ throw new Error(symlinkedStateDirectoryMessage(dir));
123
+ }
124
+ // And the database itself. A link here is followed just as readily:
125
+ // the table is created, the rows indexed and the mode tightened in
126
+ // whatever it points at.
127
+ if (isSymlinkedStatePathSync(filePath)) {
128
+ throw new Error(symlinkedStateFileMessage(filePath));
129
+ }
130
+ }
131
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
132
+ if (options.ownedDirectory === true || options.perAgent === true) {
133
+ chmodSync(dir, 0o700);
134
+ }
135
+ const db = new DatabaseSync(filePath);
136
+ try {
137
+ chmodSync(filePath, 0o600);
138
+ }
139
+ catch (error) {
140
+ // A database that cannot be tightened must not be used: conversation
141
+ // bodies would stay readable by other local users for the daemon's
142
+ // whole lifetime, silently.
143
+ if (error.code !== 'ENOENT') {
144
+ throw error;
145
+ }
146
+ }
147
+ // Forced into existence here (the user_version pragma is a real page-one
148
+ // write) so the tightening below covers the WAL sidecars for the
149
+ // connection's whole lifetime.
150
+ db.exec('PRAGMA journal_mode = WAL');
151
+ db.exec('PRAGMA busy_timeout = 5000');
152
+ return db;
153
+ };
154
+ export class SqliteSessionStore {
155
+ db;
156
+ constructor(filePath, options = {}) {
157
+ this.db = openStratusDatabase(filePath, options);
158
+ this.db.exec(SESSIONS_TABLE);
159
+ this.db.exec('PRAGMA user_version = 0');
160
+ tightenSqliteFile(filePath);
161
+ }
162
+ write(session) {
163
+ this.db
164
+ .prepare('INSERT OR REPLACE INTO sessions (id, agent_id, status, body, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)')
165
+ .run(session.id, session.agent.id, session.status, JSON.stringify(session), session.createdAt, session.updatedAt);
166
+ return session;
167
+ }
168
+ async create(input) {
169
+ const now = new Date().toISOString();
170
+ return this.write({ ...input, createdAt: now, updatedAt: now });
171
+ }
172
+ async get(id) {
173
+ const row = this.db.prepare('SELECT body FROM sessions WHERE id = ?').get(id);
174
+ return row ? JSON.parse(row.body) : undefined;
175
+ }
176
+ async save(session) {
177
+ await this.saveReturning(session);
178
+ }
179
+ /**
180
+ * The same save, handing back the row as it was written.
181
+ *
182
+ * For a caller that has to mirror it: the sharded store keeps the fleet
183
+ * index in step, and an index holding a timestamp the store does not is
184
+ * drift the startup reconcile would have to clean up every boot.
185
+ */
186
+ async saveReturning(session) {
187
+ return this.write({ ...session, updatedAt: new Date().toISOString() });
188
+ }
189
+ /**
190
+ * Session ids in a state, oldest first — the index a restarting daemon
191
+ * sweeps for turns parked on a human. The `status` column carries it, so
192
+ * no conversation body is deserialized to answer the question.
193
+ */
194
+ async listIdsByStatus(status) {
195
+ const rows = this.db
196
+ .prepare('SELECT id FROM sessions WHERE status = ? ORDER BY updated_at ASC')
197
+ .all(status);
198
+ return rows.map((row) => row.id);
199
+ }
200
+ /**
201
+ * Every row's routing and lifecycle columns, no bodies — what the
202
+ * startup reconcile compares this shard against the fleet index with.
203
+ */
204
+ rows() {
205
+ const rows = this.db
206
+ .prepare('SELECT id, agent_id, status, created_at, updated_at FROM sessions')
207
+ .all();
208
+ return rows.map((row) => ({
209
+ id: row.id,
210
+ agentId: row.agent_id,
211
+ status: row.status,
212
+ createdAt: row.created_at,
213
+ updatedAt: row.updated_at,
214
+ }));
215
+ }
216
+ close() {
217
+ this.db.close();
218
+ }
219
+ }
220
+ /**
221
+ * Thrown when a session id another agent already holds is claimed.
222
+ *
223
+ * Sessions never cross agent identities — the dispatcher says so when it
224
+ * finds the conversation, and this is the same rule at the durable seam,
225
+ * where a shared database's primary key used to enforce it for free.
226
+ */
227
+ export class SessionIdTakenError extends Error {
228
+ constructor(sessionId, heldBy, claimedBy) {
229
+ super(`Session ${sessionId} already belongs to agent ${heldBy}, so ${claimedBy} cannot open a conversation under that id — `
230
+ + 'sessions never cross agent identities. Give the new conversation an id of its own.');
231
+ this.name = 'SessionIdTakenError';
232
+ }
233
+ }
234
+ /**
235
+ * The fleet's session index, in `fleet.db` beside the schedules.
236
+ *
237
+ * Its own connection rather than a table on the schedule store, for the
238
+ * reason the schedule store gives for its own: `stratus schedules` opens
239
+ * that file from another process, and WAL is what makes two connections on
240
+ * one file routine.
241
+ */
242
+ export class FleetSessionIndex {
243
+ db;
244
+ constructor(filePath, options = {}) {
245
+ this.db = openStratusDatabase(filePath, options);
246
+ this.db.exec(SESSION_INDEX_TABLE);
247
+ tightenSqliteFile(filePath);
248
+ }
249
+ /**
250
+ * Claim an id for an agent, or refuse it.
251
+ *
252
+ * The claim is the create's single transactional authority: it lands
253
+ * before the shard write, so a crash between the two leaves a claim with
254
+ * no conversation — which the reconcile releases — rather than a
255
+ * conversation no lookup can reach. Re-claiming an id this agent already
256
+ * holds is genuinely a no-op: `create` on an existing id is how a caller
257
+ * re-opens its own session, and `save` claims every id it writes.
258
+ *
259
+ * Taking the id is all this does. It deliberately does NOT write the row
260
+ * it was handed, because the id it is claiming may already have a
261
+ * conversation: overwriting that row's status and timestamps here would
262
+ * announce a transition the shard write has not made yet, and a write
263
+ * that then fails leaves the live daemon answering from a lifecycle row
264
+ * no store agrees with — a pending session missing from recovery until
265
+ * the next restart reconciles it. The row is written by the caller,
266
+ * after the shard write, from what the store actually stored.
267
+ */
268
+ claim(row) {
269
+ // The insert is what decides, not a read before it. `DO NOTHING` makes
270
+ // the row's creation the atomic step: two callers racing for an id no
271
+ // one holds cannot both find it free, because one of their inserts is
272
+ // the conflict. The read after it is then a question with an answer —
273
+ // who holds this id now — rather than a guess that a write could have
274
+ // overtaken. Nothing is overwritten on the way, so a claim that loses
275
+ // has not touched the winner's row.
276
+ this.db
277
+ .prepare('INSERT INTO session_index (id, agent_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?) ON CONFLICT(id) DO NOTHING')
278
+ .run(row.id, row.agentId, row.status, row.createdAt, row.updatedAt);
279
+ const held = this.agentFor(row.id);
280
+ if (held !== undefined && held !== row.agentId) {
281
+ throw new SessionIdTakenError(row.id, held, row.agentId);
282
+ }
283
+ }
284
+ /** Write the row as it now stands. Called after every shard write. */
285
+ record(row) {
286
+ this.db
287
+ .prepare('INSERT OR REPLACE INTO session_index (id, agent_id, status, created_at, updated_at) VALUES (?, ?, ?, ?, ?)')
288
+ .run(row.id, row.agentId, row.status, row.createdAt, row.updatedAt);
289
+ }
290
+ /** Drop a claim — a reconcile releasing an id whose conversation never landed. */
291
+ release(id) {
292
+ this.db.prepare('DELETE FROM session_index WHERE id = ?').run(id);
293
+ }
294
+ agentFor(id) {
295
+ const row = this.db.prepare('SELECT agent_id FROM session_index WHERE id = ?').get(id);
296
+ return row?.agent_id;
297
+ }
298
+ rows() {
299
+ const rows = this.db
300
+ .prepare('SELECT id, agent_id, status, created_at, updated_at FROM session_index')
301
+ .all();
302
+ return rows.map((row) => ({
303
+ id: row.id,
304
+ agentId: row.agent_id,
305
+ status: row.status,
306
+ createdAt: row.created_at,
307
+ updatedAt: row.updated_at,
308
+ }));
309
+ }
310
+ idsByStatus(status) {
311
+ const rows = this.db
312
+ .prepare('SELECT id FROM session_index WHERE status = ? ORDER BY updated_at ASC')
313
+ .all(status);
314
+ return rows.map((row) => row.id);
315
+ }
316
+ countByStatus() {
317
+ const rows = this.db
318
+ .prepare('SELECT status, COUNT(*) AS total FROM session_index GROUP BY status')
319
+ .all();
320
+ const counts = {};
321
+ for (const row of rows) {
322
+ counts[row.status] = Number(row.total);
323
+ }
324
+ return counts;
325
+ }
326
+ lastActivityByAgent() {
327
+ const rows = this.db
328
+ .prepare(`
329
+ SELECT agent_id,
330
+ MAX(updated_at) AS last_active_at,
331
+ SUM(CASE WHEN status IN ('running', 'pending_approval') THEN 1 ELSE 0 END) AS active_sessions
332
+ FROM session_index
333
+ GROUP BY agent_id
334
+ `)
335
+ .all();
336
+ const activity = {};
337
+ for (const row of rows) {
338
+ activity[row.agent_id] = {
339
+ lastActiveAt: row.last_active_at,
340
+ activeSessions: Number(row.active_sessions),
341
+ };
342
+ }
343
+ return activity;
344
+ }
345
+ list(agentId, limit) {
346
+ // -1 is SQLite's "no limit", so one prepared statement serves both cases
347
+ // rather than four.
348
+ const bound = limit !== undefined && Number.isInteger(limit) && limit >= 0 ? limit : -1;
349
+ const rows = (agentId
350
+ ? 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)
351
+ : this.db.prepare('SELECT id, agent_id, status, created_at, updated_at FROM session_index ORDER BY updated_at DESC LIMIT ?').all(bound));
352
+ return rows.map((row) => ({
353
+ id: row.id,
354
+ agentId: row.agent_id,
355
+ status: row.status,
356
+ createdAt: row.created_at,
357
+ updatedAt: row.updated_at,
358
+ }));
359
+ }
360
+ close() {
361
+ this.db.close();
362
+ }
363
+ }
364
+ /**
365
+ * The fleet's session store: one SQLite database per agent, behind the
366
+ * `SessionStore` every runner already takes, with the fleet-wide reads the
367
+ * control API and the restart sweeps need answered across all of them.
368
+ *
369
+ * A runner is handed *this* — and every write goes to the store opened on
370
+ * its own agent's path. What the facade adds is the set of questions that
371
+ * were never about one agent: which agent holds an id, how many sessions
372
+ * are in each state, when each agent last did anything, and which ids are
373
+ * parked or abandoned fleet-wide.
374
+ */
375
+ export class ShardedSessionStore {
376
+ stateDir;
377
+ ownedDirectory;
378
+ index;
379
+ shards = new Map();
380
+ constructor(options) {
381
+ this.stateDir = options.stateDir;
382
+ this.ownedDirectory = options.ownedDirectory ?? false;
383
+ this.index = new FleetSessionIndex(fleetDbIn(options.stateDir), this.ownedDirectory ? { ownedDirectory: true } : {});
384
+ }
385
+ /**
386
+ * One agent's store, opened on first use and kept open.
387
+ *
388
+ * Opened lazily because a roster's agents are not all busy: a daemon
389
+ * with forty souls should hold descriptors for the ones that have
390
+ * conversations, not for every id on disk.
391
+ */
392
+ shardFor(agentId) {
393
+ const existing = this.shards.get(agentId);
394
+ if (existing !== undefined) {
395
+ return existing;
396
+ }
397
+ // Always owner-only: a per-agent directory is state this repository
398
+ // creates, never a path an embedder pointed at something shared.
399
+ const shard = new SqliteSessionStore(agentSessionDbIn(this.stateDir, agentId), { perAgent: true });
400
+ this.shards.set(agentId, shard);
401
+ return shard;
402
+ }
403
+ async create(input) {
404
+ const agentId = input.agent.id;
405
+ assertPathSafeAgentId(agentId);
406
+ const now = new Date().toISOString();
407
+ // The claim first, and it is what refuses a sibling's id: the store
408
+ // write below is only reachable for an id this agent may have.
409
+ this.index.claim({ id: input.id, agentId, status: input.status, createdAt: now, updatedAt: now });
410
+ const session = await this.shardFor(agentId).create(input);
411
+ this.index.record({
412
+ id: session.id,
413
+ agentId,
414
+ status: session.status,
415
+ createdAt: session.createdAt,
416
+ updatedAt: session.updatedAt,
417
+ });
418
+ return session;
419
+ }
420
+ async get(id) {
421
+ const agentId = this.index.agentFor(id);
422
+ // No claim, no conversation. The index is the authority for "which
423
+ // agent's store holds this id" precisely so a lookup never has to
424
+ // guess by opening stores until one answers.
425
+ return agentId === undefined ? undefined : this.shardFor(agentId).get(id);
426
+ }
427
+ async save(session) {
428
+ const agentId = session.agent.id;
429
+ assertPathSafeAgentId(agentId);
430
+ // One id belongs to one agent, and this is the path that could change
431
+ // that quietly. `save` takes whatever agent the session now names: a
432
+ // caller handing back a session whose `agent.id` has changed would
433
+ // write a second copy into the new agent's shard and then repoint the
434
+ // index at it — the original transcript still on disk but in a store
435
+ // nothing resolves to, and the next start refusing to serve at all,
436
+ // because the reconcile finds one id in two shards and says so.
437
+ //
438
+ // The same `claim` as `create`, not a read of the index. Asking who
439
+ // holds the id and *then* writing the shard leaves the whole file write
440
+ // between the question and the answer, so two callers saving an id
441
+ // nobody has indexed yet — different agents, the same id — both see it
442
+ // free and both write. Claiming takes the id in one statement before
443
+ // either shard write can start, so the loser is refused here, where the
444
+ // error can still name the agent it belongs to, instead of at the next
445
+ // start where it is a daemon that will not come up.
446
+ this.index.claim({
447
+ id: session.id,
448
+ agentId,
449
+ status: session.status,
450
+ createdAt: session.createdAt,
451
+ updatedAt: session.updatedAt,
452
+ });
453
+ // The conversation is the truth and the index is derived from it, so
454
+ // the shard write goes first: a crash in between leaves an index row
455
+ // one status behind, which the next start reconciles, rather than an
456
+ // index pointing at a turn the store never took.
457
+ const written = await this.shardFor(agentId).saveReturning(session);
458
+ this.index.record({
459
+ id: written.id,
460
+ agentId,
461
+ status: written.status,
462
+ createdAt: written.createdAt,
463
+ updatedAt: written.updatedAt,
464
+ });
465
+ }
466
+ async listIdsByStatus(status) {
467
+ return this.index.idsByStatus(status);
468
+ }
469
+ countByStatus() {
470
+ return this.index.countByStatus();
471
+ }
472
+ lastActivityByAgent() {
473
+ return this.index.lastActivityByAgent();
474
+ }
475
+ list(agentId, limit) {
476
+ return this.index.list(agentId, limit);
477
+ }
478
+ /**
479
+ * Put the index and the shards back in step, before anything reads
480
+ * either. Two durable writes stand where one atomic primary key used to,
481
+ * and this is what makes a crash between them resolve one way rather
482
+ * than leaving them to disagree:
483
+ *
484
+ * - a claim whose conversation never landed is released, freeing the id;
485
+ * - a conversation the index has lost — or disagrees with — is
486
+ * re-indexed from the shard, which is the record;
487
+ * - the same id in two agents' shards is refused loudly, naming both,
488
+ * because picking one would silently hand somebody else's conversation
489
+ * to whoever asks for that id next.
490
+ */
491
+ async reconcile() {
492
+ const report = { released: [], reindexed: [] };
493
+ const owners = new Map();
494
+ for (const agentId of await this.shardedAgentIds()) {
495
+ // Read through a connection this reconcile owns, and let it go again
496
+ // unless the store was already open: a roster's every agent has a
497
+ // store on disk, and holding one descriptor per agent from start-up
498
+ // would make a big roster pay at rest for a sweep that runs once.
499
+ const cached = this.shards.get(agentId);
500
+ const shard = cached ?? new SqliteSessionStore(agentSessionDbIn(this.stateDir, agentId), { perAgent: true });
501
+ try {
502
+ for (const row of shard.rows()) {
503
+ const seen = owners.get(row.id);
504
+ if (seen !== undefined) {
505
+ throw new Error(`Session ${row.id} exists in both ${seen.agentId}'s and ${agentId}'s store. `
506
+ + 'Two agents cannot share a session id; move or remove one of the two `agents/<id>/sessions.db` rows before starting.');
507
+ }
508
+ owners.set(row.id, row);
509
+ }
510
+ }
511
+ finally {
512
+ if (cached === undefined) {
513
+ shard.close();
514
+ }
515
+ }
516
+ }
517
+ const indexed = new Map(this.index.rows().map((row) => [row.id, row]));
518
+ for (const [id] of indexed) {
519
+ if (!owners.has(id)) {
520
+ this.index.release(id);
521
+ report.released.push(id);
522
+ }
523
+ }
524
+ for (const [id, row] of owners) {
525
+ const claim = indexed.get(id);
526
+ if (claim === undefined
527
+ || claim.agentId !== row.agentId
528
+ || claim.status !== row.status
529
+ || claim.updatedAt !== row.updatedAt) {
530
+ this.index.record(row);
531
+ report.reindexed.push(id);
532
+ }
533
+ }
534
+ return report;
535
+ }
536
+ /**
537
+ * Which agents have a store on disk — the roster is not the answer, on
538
+ * purpose: the gateway keeps a missing soul's sessions when it drops the
539
+ * soul, and a recovery sweep that skipped those would strand exactly the
540
+ * conversations nobody is watching.
541
+ */
542
+ async shardedAgentIds() {
543
+ let entries;
544
+ try {
545
+ entries = await readdir(agentsDirIn(this.stateDir), { withFileTypes: true });
546
+ }
547
+ catch (error) {
548
+ if (error.code === 'ENOENT') {
549
+ return [];
550
+ }
551
+ throw error;
552
+ }
553
+ const named = entries
554
+ .filter((entry) => entry.isDirectory())
555
+ .map((entry) => entry.name)
556
+ .filter((name) => {
557
+ try {
558
+ // A directory whose name cannot key a path is not an agent's —
559
+ // `agentStateDirIn` is what says so, and the reason it throws
560
+ // rather than sanitizing.
561
+ agentStateDirIn(this.stateDir, name);
562
+ return true;
563
+ }
564
+ catch {
565
+ return false;
566
+ }
567
+ });
568
+ // And only the ones that already hold a shard. This sweep reads what is
569
+ // there; opening a store *creates* the database and tightens the
570
+ // directory around it, so treating every well-named subdirectory as an
571
+ // agent means a start-up that writes `sessions.db` into whatever an
572
+ // operator keeps under `agents/` — a `backups/` folder, an export — and
573
+ // chmods it to 0700 on the way past. The old layout reserved no such
574
+ // names, so nothing warned them. An agent with no conversations yet has
575
+ // nothing to reconcile either, which is the same answer.
576
+ const held = [];
577
+ for (const name of named) {
578
+ if (await shardFileExists(agentSessionDbIn(this.stateDir, name))) {
579
+ held.push(name);
580
+ }
581
+ }
582
+ return held;
583
+ }
584
+ close() {
585
+ for (const shard of this.shards.values()) {
586
+ shard.close();
587
+ }
588
+ this.shards.clear();
589
+ this.index.close();
590
+ }
591
+ }
592
+ //# 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.5",
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/core": "0.11.5",
22
+ "@stratusagent/executor-local": "0.11.5",
23
+ "@stratusagent/agents": "0.11.5",
24
+ "@stratusagent/plugins": "0.11.5",
25
+ "@stratusagent/state": "0.11.5"
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.5"
31
31
  },
32
32
  "license": "MIT",
33
33
  "repository": {