antechamber 0.2.15 → 0.2.17
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 +19 -0
- package/dist/commands/doctor.js +11 -1
- package/dist/session-diff.js +4 -1
- package/dist/session-store.d.ts +2 -0
- package/dist/session-store.js +2 -0
- package/dist/sessions.js +9 -3
- package/dist/subagents.d.ts +5 -1
- package/dist/subagents.js +39 -3
- package/extensions/omo-dag/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,25 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.17] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Keep files in the session diff when the agent reports their absolute path through a symlinked workspace, such as
|
|
15
|
+
a macOS temporary directory under `/var`.
|
|
16
|
+
- Make `antechamber doctor` skip running-server records whose process has exited on macOS, not only on Linux.
|
|
17
|
+
- Stop a fast subagent task from staying busy on macOS when its task record changes before the file watcher is
|
|
18
|
+
live.
|
|
19
|
+
|
|
20
|
+
## [0.2.16] - 2026-10-17
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- Keep deleted subagent sessions deleted. They came back when their parent's history was read again or when the
|
|
25
|
+
adapter restarted, because OmO still holds their task record.
|
|
26
|
+
- Keep subagent sessions archived after an adapter restart. Archiving a parent with its subagents left the
|
|
27
|
+
subagents active as orphans once the adapter restarted.
|
|
28
|
+
|
|
10
29
|
## [0.2.15] - 2026-10-17
|
|
11
30
|
|
|
12
31
|
### Fixed
|
package/dist/commands/doctor.js
CHANGED
|
@@ -7,6 +7,16 @@ import { readRunningServers } from "../running-servers.js";
|
|
|
7
7
|
import { isNewerVersion } from "../self-update.js";
|
|
8
8
|
import { ADAPTER_VERSION } from "../version.js";
|
|
9
9
|
import { autoUpdateLauncherPath, autoUpdateLauncherTarget, createContext, isPackageBinary, readSettings, resolveBinary, settingsPath, } from "./context.js";
|
|
10
|
+
/** EPERM means the process exists under another user. */
|
|
11
|
+
function processAlive(pid) {
|
|
12
|
+
try {
|
|
13
|
+
process.kill(pid, 0);
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
return error instanceof Error && "code" in error && error.code === "EPERM";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
10
20
|
/** Checks capabilities with access/stat only, never creating state or config files. */
|
|
11
21
|
export function doctor(options = {}, ctx = createContext(), latestVersion) {
|
|
12
22
|
const checks = [];
|
|
@@ -53,7 +63,7 @@ export function doctor(options = {}, ctx = createContext(), latestVersion) {
|
|
|
53
63
|
else
|
|
54
64
|
add("version", "PASS", ADAPTER_VERSION);
|
|
55
65
|
// A released fix is live only once OpenChamber relaunches the adapter.
|
|
56
|
-
const running = readRunningServers(stateDir, ctx.fs,
|
|
66
|
+
const running = readRunningServers(stateDir, ctx.fs, processAlive);
|
|
57
67
|
if (running.length === 0)
|
|
58
68
|
add("running", "INFO", `no running antechamber server recorded in ${stateDir}`);
|
|
59
69
|
for (const server of running) {
|
package/dist/session-diff.js
CHANGED
|
@@ -26,7 +26,10 @@ async function git(directory, args) {
|
|
|
26
26
|
/** A removed file may be absent, but neither traversal nor symlinks may escape. */
|
|
27
27
|
async function confinedFile(directory, path) {
|
|
28
28
|
const root = await realpath(directory);
|
|
29
|
-
|
|
29
|
+
// Map paths spelled through a symlinked workspace (macOS /var -> /private/var) onto the canonical root.
|
|
30
|
+
const lexical = relative(resolve(directory), resolve(directory, path));
|
|
31
|
+
const insideLexical = lexical !== ".." && !lexical.startsWith(`..${sep}`) && !isAbsolute(lexical);
|
|
32
|
+
const absolute = insideLexical ? resolve(root, lexical) : resolve(root, path);
|
|
30
33
|
const rel = relative(root, absolute);
|
|
31
34
|
if (!rel || rel === ".." || rel.startsWith(`..${sep}`) || isAbsolute(rel))
|
|
32
35
|
return undefined;
|
package/dist/session-store.d.ts
CHANGED
|
@@ -35,6 +35,8 @@ export type TitleSource = "user" | "native" | "fallback" | "generated";
|
|
|
35
35
|
export interface SessionRegistryEntry {
|
|
36
36
|
/** Created by this connector, rather than discovered from another OmO client. */
|
|
37
37
|
connectorOwned?: true;
|
|
38
|
+
/** A child the user deleted; kept so its task record or parent history cannot list it again. */
|
|
39
|
+
deleted?: true;
|
|
38
40
|
tree?: SessionTreeState;
|
|
39
41
|
parentID?: string | undefined;
|
|
40
42
|
parentMessageID?: string;
|
package/dist/session-store.js
CHANGED
|
@@ -346,6 +346,8 @@ function registryEntry(value) {
|
|
|
346
346
|
return false;
|
|
347
347
|
if (value.connectorOwned !== undefined && value.connectorOwned !== true)
|
|
348
348
|
return false;
|
|
349
|
+
if (value.deleted !== undefined && value.deleted !== true)
|
|
350
|
+
return false;
|
|
349
351
|
if (value.tree !== undefined && !sessionTreeState(value.tree))
|
|
350
352
|
return false;
|
|
351
353
|
if (value.parentID !== undefined && typeof value.parentID !== "string")
|
package/dist/sessions.js
CHANGED
|
@@ -344,7 +344,7 @@ export class SessionManager {
|
|
|
344
344
|
this.emit(s.info.directory, { type: "session.deleted", properties: { info: s.info } });
|
|
345
345
|
await this.lifecycle.dormant(s);
|
|
346
346
|
const piPath = this.subagents.owns(sessionId) || this.monitors.owns(sessionId) ? undefined : (s.piPath ?? s.restored?.piPath);
|
|
347
|
-
this.subagents.remove(sessionId);
|
|
347
|
+
const child = this.subagents.remove(sessionId);
|
|
348
348
|
this.monitors.remove(sessionId);
|
|
349
349
|
if (piPath !== undefined) {
|
|
350
350
|
try {
|
|
@@ -355,7 +355,9 @@ export class SessionManager {
|
|
|
355
355
|
throw error;
|
|
356
356
|
}
|
|
357
357
|
}
|
|
358
|
-
|
|
358
|
+
// A deleted child keeps its tombstone; OmO still holds the task record that would list it again.
|
|
359
|
+
if (!child)
|
|
360
|
+
this.store.remove(sessionId);
|
|
359
361
|
return true;
|
|
360
362
|
}
|
|
361
363
|
/**
|
|
@@ -391,7 +393,9 @@ export class SessionManager {
|
|
|
391
393
|
delete s.info.time.archived; // 0 = unarchive
|
|
392
394
|
}
|
|
393
395
|
s.info.time.updated = Date.now();
|
|
394
|
-
|
|
396
|
+
// Child sessions have no pi path of their own, so persistMeta would skip them.
|
|
397
|
+
if (!this.subagents.save(s.info.id))
|
|
398
|
+
this.persistMeta(s);
|
|
395
399
|
this.emit(s.info.directory, { type: "session.updated", properties: { info: s.info } });
|
|
396
400
|
return s.info;
|
|
397
401
|
}
|
|
@@ -1318,6 +1322,8 @@ export class SessionManager {
|
|
|
1318
1322
|
/** Resolve a bare session id (e.g. deep link after restart) via known directories. */
|
|
1319
1323
|
resolveRestored(sessionId) {
|
|
1320
1324
|
const reg = this.store.get(sessionId);
|
|
1325
|
+
if (reg?.deleted)
|
|
1326
|
+
return undefined;
|
|
1321
1327
|
if (reg && existsSync(reg.piPath)) {
|
|
1322
1328
|
return this.adopt({
|
|
1323
1329
|
piSessionId: reg.piSessionId,
|
package/dist/subagents.d.ts
CHANGED
|
@@ -78,12 +78,16 @@ export declare class SubagentBridge {
|
|
|
78
78
|
private publishRefresh;
|
|
79
79
|
private saveArchive;
|
|
80
80
|
private fallback;
|
|
81
|
+
/** Save a rename or archive made through the session routes; false when `id` is not a child. */
|
|
82
|
+
save(id: string): boolean;
|
|
81
83
|
private persist;
|
|
82
84
|
private emitPart;
|
|
83
85
|
private closeWatchers;
|
|
84
86
|
finishCalls(parentID: string, cancelled?: boolean): void;
|
|
85
87
|
stopParent(parentID: string): void;
|
|
86
|
-
|
|
88
|
+
/** Forget a deleted child and leave a registry tombstone; false when `id` is not a child. */
|
|
89
|
+
remove(id: string): boolean;
|
|
90
|
+
private deleted;
|
|
87
91
|
dispose(): void;
|
|
88
92
|
}
|
|
89
93
|
export {};
|
package/dist/subagents.js
CHANGED
|
@@ -6,6 +6,9 @@ import { syncSessionCost } from "./pi-event-translation.js";
|
|
|
6
6
|
import { ocIdFor, SessionPersistenceError } from "./session-store.js";
|
|
7
7
|
import { mapTool, resultText, toolMappers } from "./tool-map.js";
|
|
8
8
|
import { ADAPTER_VERSION } from "./version.js";
|
|
9
|
+
// macOS watches directories through FSEvents, which goes live asynchronously after
|
|
10
|
+
// watch() returns; a task record replaced inside that window raises no event.
|
|
11
|
+
const FSEVENTS_STARTUP_RECHECK_MS = 1_000;
|
|
9
12
|
function record(value) {
|
|
10
13
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
11
14
|
}
|
|
@@ -319,6 +322,8 @@ export class SubagentBridge {
|
|
|
319
322
|
const id = metadata.taskID;
|
|
320
323
|
const details = record(metadata.task) ? metadata.task : {};
|
|
321
324
|
let child = this.children.get(ocIdFor(id));
|
|
325
|
+
if (!child && this.deleted(ocIdFor(id)))
|
|
326
|
+
continue;
|
|
322
327
|
if (!child) {
|
|
323
328
|
child = this.create(parent, {
|
|
324
329
|
id,
|
|
@@ -340,6 +345,7 @@ export class SubagentBridge {
|
|
|
340
345
|
}
|
|
341
346
|
}
|
|
342
347
|
create(parent, task) {
|
|
348
|
+
const archived = this.store.get(ocIdFor(task.id))?.archived;
|
|
343
349
|
const session = this.managedSession({
|
|
344
350
|
id: ocIdFor(task.id),
|
|
345
351
|
parentID: parent.info.id,
|
|
@@ -347,7 +353,7 @@ export class SubagentBridge {
|
|
|
347
353
|
directory: parent.info.directory,
|
|
348
354
|
title: typeof task.input.description === "string" ? task.input.description : task.id,
|
|
349
355
|
version: parent.info.version,
|
|
350
|
-
time: { ...parent.info.time },
|
|
356
|
+
time: { ...parent.info.time, ...(archived !== undefined ? { archived } : {}) },
|
|
351
357
|
});
|
|
352
358
|
const child = {
|
|
353
359
|
session,
|
|
@@ -373,7 +379,7 @@ export class SubagentBridge {
|
|
|
373
379
|
const entry = this.store.get(id);
|
|
374
380
|
if (!entry)
|
|
375
381
|
return this.removed.has(id) ? undefined : this.adopt(id);
|
|
376
|
-
if (!entry.task || !entry.parentID)
|
|
382
|
+
if (entry.deleted || !entry.task || !entry.parentID)
|
|
377
383
|
return undefined;
|
|
378
384
|
const child = this.create(this.detachedParent(entry.parentID, entry.directory, entry), entry.task);
|
|
379
385
|
child.session.info.title = entry.title ?? child.session.info.title;
|
|
@@ -503,6 +509,12 @@ export class SubagentBridge {
|
|
|
503
509
|
child.watchers.delete(dir);
|
|
504
510
|
});
|
|
505
511
|
child.watchers.set(dir, watcher);
|
|
512
|
+
if (process.platform === "darwin") {
|
|
513
|
+
setTimeout(() => {
|
|
514
|
+
if (child.watchers.get(dir) === watcher)
|
|
515
|
+
this.refresh(child, true);
|
|
516
|
+
}, FSEVENTS_STARTUP_RECHECK_MS).unref();
|
|
517
|
+
}
|
|
506
518
|
}
|
|
507
519
|
refresh(child, publish, useRecord = true) {
|
|
508
520
|
const archive = this.archive(child.task.id);
|
|
@@ -682,6 +694,14 @@ export class SubagentBridge {
|
|
|
682
694
|
});
|
|
683
695
|
return this.parse(child.session, `${request}\n${response}\n`);
|
|
684
696
|
}
|
|
697
|
+
/** Save a rename or archive made through the session routes; false when `id` is not a child. */
|
|
698
|
+
save(id) {
|
|
699
|
+
const child = this.children.get(id);
|
|
700
|
+
if (!child)
|
|
701
|
+
return false;
|
|
702
|
+
this.persist(child);
|
|
703
|
+
return true;
|
|
704
|
+
}
|
|
685
705
|
persist(child) {
|
|
686
706
|
const { info } = child.session;
|
|
687
707
|
try {
|
|
@@ -691,6 +711,7 @@ export class SubagentBridge {
|
|
|
691
711
|
directory: info.directory,
|
|
692
712
|
title: info.title,
|
|
693
713
|
parentID: info.parentID,
|
|
714
|
+
...(info.time.archived !== undefined ? { archived: info.time.archived } : {}),
|
|
694
715
|
task: child.task,
|
|
695
716
|
created: info.time.created,
|
|
696
717
|
updated: info.time.updated,
|
|
@@ -746,14 +767,29 @@ export class SubagentBridge {
|
|
|
746
767
|
this.closeWatchers(child);
|
|
747
768
|
}
|
|
748
769
|
}
|
|
770
|
+
/** Forget a deleted child and leave a registry tombstone; false when `id` is not a child. */
|
|
749
771
|
remove(id) {
|
|
750
772
|
const child = this.children.get(id);
|
|
751
773
|
if (!child)
|
|
752
|
-
return;
|
|
774
|
+
return false;
|
|
753
775
|
this.closeWatchers(child);
|
|
754
776
|
child.parts.clear();
|
|
755
777
|
this.children.delete(id);
|
|
756
778
|
this.removed.add(id);
|
|
779
|
+
const { info } = child.session;
|
|
780
|
+
this.store.set(id, {
|
|
781
|
+
deleted: true,
|
|
782
|
+
piSessionId: child.session.piSessionId ?? child.task.id,
|
|
783
|
+
piPath: this.archive(child.task.id),
|
|
784
|
+
directory: info.directory,
|
|
785
|
+
...(info.parentID !== undefined ? { parentID: info.parentID } : {}),
|
|
786
|
+
created: info.time.created,
|
|
787
|
+
updated: info.time.updated,
|
|
788
|
+
});
|
|
789
|
+
return true;
|
|
790
|
+
}
|
|
791
|
+
deleted(id) {
|
|
792
|
+
return this.removed.has(id) || this.store.get(id)?.deleted === true;
|
|
757
793
|
}
|
|
758
794
|
dispose() {
|
|
759
795
|
for (const child of this.children.values()) {
|