antechamber 1.0.9 → 1.0.12
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/CHANGELOG.md +21 -0
- package/dist/session-store.d.ts +5 -1
- package/dist/session-store.js +55 -14
- package/dist/subagents.js +26 -22
- package/extensions/omo-compat/package.json +1 -1
- package/extensions/omo-dag/package.json +1 -1
- package/extensions/omo-work/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.12] - 2026-10-21
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Stop saving child-task spawn specs in the session registry; OmO's task records already hold them.
|
|
15
|
+
This shrinks the registry by about a third in sessions with hundreds of child tasks.
|
|
16
|
+
|
|
17
|
+
## [1.0.11] - 2026-10-21
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Cut the event-loop stall of each session registry write by reusing the in-memory registry while
|
|
22
|
+
the file is unchanged, cloning only the entries a batch reads, and writing compact JSON.
|
|
23
|
+
|
|
24
|
+
## [1.0.10] - 2026-10-21
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- Keep sessions with many child tasks responsive by ignoring unchanged task snapshots and saving
|
|
29
|
+
each snapshot's changes in one registry write.
|
|
30
|
+
|
|
10
31
|
## [1.0.9] - 2026-10-21
|
|
11
32
|
|
|
12
33
|
### Fixed
|
package/dist/session-store.d.ts
CHANGED
|
@@ -91,10 +91,14 @@ export declare class SessionStore {
|
|
|
91
91
|
readonly stateDir: string;
|
|
92
92
|
private readonly file;
|
|
93
93
|
private entries;
|
|
94
|
+
/** The registry bytes `entries` was parsed from or last written as; equal bytes on disk mean the cache is current. */
|
|
95
|
+
private raw;
|
|
94
96
|
private batchState;
|
|
95
97
|
constructor(stateDir?: string);
|
|
96
98
|
private load;
|
|
97
|
-
private
|
|
99
|
+
private committed;
|
|
100
|
+
private readRaw;
|
|
101
|
+
private parseEntries;
|
|
98
102
|
private withRegistryLock;
|
|
99
103
|
private persist;
|
|
100
104
|
private mutate;
|
package/dist/session-store.js
CHANGED
|
@@ -371,6 +371,17 @@ function registryEntry(value) {
|
|
|
371
371
|
return false;
|
|
372
372
|
return true;
|
|
373
373
|
}
|
|
374
|
+
/**
|
|
375
|
+
* OmO keeps a task's spawn spec in its own task record, which is where the adapter reads it
|
|
376
|
+
* (adoption, eval-agent matching). The registry copy was never read back and made up about a third
|
|
377
|
+
* of the file in sessions with hundreds of children.
|
|
378
|
+
*/
|
|
379
|
+
function persistedEntry(entry) {
|
|
380
|
+
if (entry.task === undefined || !("spawn_spec" in entry.task.details))
|
|
381
|
+
return entry;
|
|
382
|
+
const { spawn_spec: _spec, ...details } = entry.task.details;
|
|
383
|
+
return { ...entry, task: { ...entry.task, details } };
|
|
384
|
+
}
|
|
374
385
|
function errorCode(error) {
|
|
375
386
|
return error instanceof Error && "code" in error ? String(error.code) : undefined;
|
|
376
387
|
}
|
|
@@ -378,6 +389,8 @@ export class SessionStore {
|
|
|
378
389
|
stateDir;
|
|
379
390
|
file;
|
|
380
391
|
entries;
|
|
392
|
+
/** The registry bytes `entries` was parsed from or last written as; equal bytes on disk mean the cache is current. */
|
|
393
|
+
raw;
|
|
381
394
|
batchState;
|
|
382
395
|
constructor(stateDir) {
|
|
383
396
|
const dir = stateDir ?? envSetting("STATE_DIR") ?? defaultStateDir();
|
|
@@ -386,26 +399,41 @@ export class SessionStore {
|
|
|
386
399
|
}
|
|
387
400
|
load() {
|
|
388
401
|
if (this.batchState !== undefined) {
|
|
389
|
-
|
|
402
|
+
// Shallow: changes replace whole entries, and get() clones an entry before handing it out.
|
|
403
|
+
this.batchState.entries ??= new Map(this.committed());
|
|
390
404
|
return this.batchState.entries;
|
|
391
405
|
}
|
|
406
|
+
return this.committed();
|
|
407
|
+
}
|
|
408
|
+
committed() {
|
|
392
409
|
if (this.entries !== undefined)
|
|
393
410
|
return this.entries;
|
|
394
|
-
this.
|
|
411
|
+
this.raw = this.readRaw();
|
|
412
|
+
this.entries = this.parseEntries(this.raw);
|
|
395
413
|
return this.entries;
|
|
396
414
|
}
|
|
397
|
-
|
|
415
|
+
readRaw() {
|
|
416
|
+
try {
|
|
417
|
+
return readFileSync(this.file);
|
|
418
|
+
}
|
|
419
|
+
catch {
|
|
420
|
+
return undefined;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
parseEntries(raw) {
|
|
398
424
|
const entries = new Map();
|
|
425
|
+
if (raw === undefined)
|
|
426
|
+
return entries;
|
|
399
427
|
try {
|
|
400
|
-
const
|
|
401
|
-
if (!record(
|
|
428
|
+
const parsed = JSON.parse(raw.toString("utf8"));
|
|
429
|
+
if (!record(parsed))
|
|
402
430
|
return entries;
|
|
403
|
-
for (const [id, value] of Object.entries(
|
|
431
|
+
for (const [id, value] of Object.entries(parsed))
|
|
404
432
|
if (registryEntry(value))
|
|
405
433
|
entries.set(id, value);
|
|
406
434
|
}
|
|
407
435
|
catch {
|
|
408
|
-
/*
|
|
436
|
+
/* corrupt — start empty */
|
|
409
437
|
}
|
|
410
438
|
return entries;
|
|
411
439
|
}
|
|
@@ -450,11 +478,13 @@ export class SessionStore {
|
|
|
450
478
|
}
|
|
451
479
|
}
|
|
452
480
|
persist(entries) {
|
|
453
|
-
const obj = Object.fromEntries(entries);
|
|
481
|
+
const obj = Object.fromEntries([...entries].map(([id, entry]) => [id, persistedEntry(entry)]));
|
|
454
482
|
const tmp = `${this.file}.${process.pid}.tmp`;
|
|
455
|
-
|
|
483
|
+
const data = Buffer.from(JSON.stringify(obj));
|
|
484
|
+
writeFileSync(tmp, data, { mode: REGISTRY_FILE_MODE });
|
|
456
485
|
chmodSync(tmp, REGISTRY_FILE_MODE);
|
|
457
486
|
renameSync(tmp, this.file);
|
|
487
|
+
return data;
|
|
458
488
|
}
|
|
459
489
|
mutate(change) {
|
|
460
490
|
if (this.batchState !== undefined) {
|
|
@@ -463,10 +493,14 @@ export class SessionStore {
|
|
|
463
493
|
return;
|
|
464
494
|
}
|
|
465
495
|
this.withRegistryLock(() => {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
496
|
+
// Another process may have written since; only parse again when the bytes differ.
|
|
497
|
+
const raw = this.readRaw();
|
|
498
|
+
const entries = this.entries !== undefined && raw !== undefined && this.raw !== undefined && raw.equals(this.raw)
|
|
499
|
+
? new Map(this.entries)
|
|
500
|
+
: this.parseEntries(raw);
|
|
501
|
+
const written = change(entries) ? this.persist(entries) : raw;
|
|
469
502
|
this.entries = entries;
|
|
503
|
+
this.raw = written;
|
|
470
504
|
});
|
|
471
505
|
}
|
|
472
506
|
/** Synchronous restoration reads its own changes, then merges them under one short-lived write lock. */
|
|
@@ -474,7 +508,7 @@ export class SessionStore {
|
|
|
474
508
|
if (this.batchState !== undefined)
|
|
475
509
|
return action();
|
|
476
510
|
const changes = [];
|
|
477
|
-
const batch = { entries: undefined, changes };
|
|
511
|
+
const batch = { entries: undefined, isolated: new WeakSet(), changes };
|
|
478
512
|
this.batchState = batch;
|
|
479
513
|
try {
|
|
480
514
|
const result = action();
|
|
@@ -495,7 +529,14 @@ export class SessionStore {
|
|
|
495
529
|
}
|
|
496
530
|
}
|
|
497
531
|
get(ocSessionId) {
|
|
498
|
-
|
|
532
|
+
const entries = this.load();
|
|
533
|
+
const entry = entries.get(ocSessionId);
|
|
534
|
+
if (this.batchState === undefined || entry === undefined || this.batchState.isolated.has(entry))
|
|
535
|
+
return entry;
|
|
536
|
+
const staged = structuredClone(entry);
|
|
537
|
+
entries.set(ocSessionId, staged);
|
|
538
|
+
this.batchState.isolated.add(staged);
|
|
539
|
+
return staged;
|
|
499
540
|
}
|
|
500
541
|
idFor(piSessionId) {
|
|
501
542
|
return [...this.load()].find(([, entry]) => entry.piSessionId === piSessionId)?.[0];
|
package/dist/subagents.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** OmO task cards and read-only views of their independently persisted sessions. */
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, statSync, watch, writeFileSync, } from "node:fs";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
5
|
import { piAgentDir } from "./agent-dir.js";
|
|
5
6
|
import { syncSessionCost } from "./pi-event-translation.js";
|
|
6
7
|
import { ocIdFor, SessionPersistenceError, SessionStore } from "./session-store.js";
|
|
@@ -541,28 +542,31 @@ export class SubagentBridge {
|
|
|
541
542
|
observe(parent, ev) {
|
|
542
543
|
if (ev.name !== "omo.task.updated" || !record(ev.data) || !Array.isArray(ev.data.tasks))
|
|
543
544
|
return;
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
this.
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
545
|
+
const rows = ev.data.tasks;
|
|
546
|
+
this.store.batch(() => withTaskLookups(this.store, () => {
|
|
547
|
+
for (const row of rows) {
|
|
548
|
+
if (!record(row) || !taskId(row.task_id))
|
|
549
|
+
continue;
|
|
550
|
+
if (typeof row.parent_session_id === "string" &&
|
|
551
|
+
row.parent_session_id !== (parent.piSessionId ?? parent.restored?.piSessionId))
|
|
552
|
+
continue;
|
|
553
|
+
const id = this.sessionId(parent, row.task_id);
|
|
554
|
+
if (!this.children.has(id))
|
|
555
|
+
this.restore(id);
|
|
556
|
+
const child = this.children.get(id);
|
|
557
|
+
if (!child || child.session.info.parentID !== parent.info.id)
|
|
558
|
+
continue;
|
|
559
|
+
const data = snapshotState(row);
|
|
560
|
+
// OmO re-sends every row ~150 ms after each progress event of any task; only changes matter.
|
|
561
|
+
if (Object.entries(data).every(([key, value]) => isDeepStrictEqual(child.task.details[key], value)))
|
|
562
|
+
continue;
|
|
563
|
+
this.wire(parent);
|
|
564
|
+
if (!this.applyNative(child, data))
|
|
565
|
+
continue;
|
|
566
|
+
child.live = true;
|
|
567
|
+
this.refresh(child, true, false);
|
|
568
|
+
}
|
|
569
|
+
}));
|
|
566
570
|
}
|
|
567
571
|
/**
|
|
568
572
|
* Merge task state reported by OmO (record, snapshot) unless it is older than what is known: an
|