antechamber 0.2.10 → 0.2.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 +15 -0
- package/dist/pi-event-translation.js +2 -0
- package/dist/pi-rpc.d.ts +2 -0
- package/dist/pi-rpc.js +44 -4
- package/dist/session-tree.d.ts +1 -1
- package/dist/session-tree.js +12 -3
- package/dist/sessions.js +8 -1
- package/extensions/omo-dag/package.json +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,21 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.12] - 2026-10-16
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Preserve recorded conversation progress when the agent process exits before
|
|
15
|
+
`agent_end`, and retain successful background compaction across revival.
|
|
16
|
+
Checkpoints respect reverted selections and do not switch to sibling branches.
|
|
17
|
+
|
|
18
|
+
## [0.2.11] - 2026-10-16
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- Keep pending prompts alive while the agent is running or compacting instead of terminating useful work at the
|
|
23
|
+
ordinary RPC deadline. Silent requests still time out, and prompt admission has a bounded overall wait.
|
|
24
|
+
|
|
10
25
|
## [0.2.10] - 2026-10-16
|
|
11
26
|
|
|
12
27
|
### Fixed
|
|
@@ -544,6 +544,7 @@ export class PiEventTranslator {
|
|
|
544
544
|
},
|
|
545
545
|
});
|
|
546
546
|
if (ev.aborted !== true && typeof result?.summary === "string" && typeof result.firstKeptEntryId === "string") {
|
|
547
|
+
recordTurn(s);
|
|
547
548
|
const messages = this.services.ensureMessages(s);
|
|
548
549
|
let key;
|
|
549
550
|
// compaction_end follows persistence, but its result omits the entry ID.
|
|
@@ -563,6 +564,7 @@ export class PiEventTranslator {
|
|
|
563
564
|
for (const part of message.parts)
|
|
564
565
|
this.emitPart(s, part);
|
|
565
566
|
}
|
|
567
|
+
this.services.persistMeta(s);
|
|
566
568
|
}
|
|
567
569
|
this.services.emit(s.info.directory, { type: "session.updated", properties: { info: s.info } });
|
|
568
570
|
if (s.compactingFromIdle) {
|
package/dist/pi-rpc.d.ts
CHANGED
|
@@ -69,6 +69,8 @@ export declare class PiRpcClient extends EventEmitter {
|
|
|
69
69
|
private closed;
|
|
70
70
|
private stderrTail;
|
|
71
71
|
private disposing;
|
|
72
|
+
private agentActive;
|
|
73
|
+
private compacting;
|
|
72
74
|
get pid(): number | undefined;
|
|
73
75
|
get isBusy(): boolean;
|
|
74
76
|
onEvent(listener: (payload: Record<string, unknown>) => void): this;
|
package/dist/pi-rpc.js
CHANGED
|
@@ -21,6 +21,7 @@ export const RPC_COMMAND_TIMEOUT_MS = 60_000;
|
|
|
21
21
|
* long before a large session finished, leaving the run orphaned.
|
|
22
22
|
*/
|
|
23
23
|
export const COMPACT_RPC_TIMEOUT_MS = 31 * 60_000;
|
|
24
|
+
const PROMPT_ADMISSION_TIMEOUT_MS = COMPACT_RPC_TIMEOUT_MS + RPC_COMMAND_TIMEOUT_MS;
|
|
24
25
|
const MAX_JSONL_FRAME_BYTES = 64 * 1024 * 1024;
|
|
25
26
|
export class UpstreamProtocolError extends Error {
|
|
26
27
|
constructor(message) {
|
|
@@ -67,6 +68,8 @@ export class PiRpcClient extends EventEmitter {
|
|
|
67
68
|
closed = false;
|
|
68
69
|
stderrTail = "";
|
|
69
70
|
disposing;
|
|
71
|
+
agentActive = false;
|
|
72
|
+
compacting = false;
|
|
70
73
|
get pid() {
|
|
71
74
|
return this.proc.pid;
|
|
72
75
|
}
|
|
@@ -235,6 +238,29 @@ export class PiRpcClient extends EventEmitter {
|
|
|
235
238
|
this.handleExtensionUI(msg);
|
|
236
239
|
return;
|
|
237
240
|
}
|
|
241
|
+
const wasWorking = this.agentActive || this.compacting;
|
|
242
|
+
switch (msg.type) {
|
|
243
|
+
case "agent_start":
|
|
244
|
+
case "auto_retry_start":
|
|
245
|
+
this.agentActive = true;
|
|
246
|
+
break;
|
|
247
|
+
case "agent_end":
|
|
248
|
+
if (msg.willRetry !== true)
|
|
249
|
+
this.agentActive = false;
|
|
250
|
+
break;
|
|
251
|
+
case "agent_idle":
|
|
252
|
+
this.agentActive = false;
|
|
253
|
+
break;
|
|
254
|
+
case "compaction_start":
|
|
255
|
+
this.compacting = true;
|
|
256
|
+
break;
|
|
257
|
+
case "compaction_end":
|
|
258
|
+
this.compacting = false;
|
|
259
|
+
break;
|
|
260
|
+
}
|
|
261
|
+
if (wasWorking !== (this.agentActive || this.compacting))
|
|
262
|
+
for (const request of this.pending.values())
|
|
263
|
+
request.onWorkState?.();
|
|
238
264
|
this.emit("event", msg);
|
|
239
265
|
}
|
|
240
266
|
handleExtensionUI(msg) {
|
|
@@ -261,16 +287,21 @@ export class PiRpcClient extends EventEmitter {
|
|
|
261
287
|
this.pending.clear();
|
|
262
288
|
}
|
|
263
289
|
/** Send a command and wait for its correlated response. */
|
|
264
|
-
command(cmd, timeoutMs
|
|
290
|
+
command(cmd, timeoutMs, signal, onDispatch) {
|
|
265
291
|
if (this.closed)
|
|
266
292
|
return Promise.reject(new Error("pi process is not running"));
|
|
267
293
|
if (signal?.aborted)
|
|
268
294
|
return Promise.reject(signal.reason);
|
|
269
295
|
const id = randomUUID();
|
|
270
296
|
const expectedCommand = String(cmd.type);
|
|
297
|
+
const deadlineMs = timeoutMs ?? RPC_COMMAND_TIMEOUT_MS;
|
|
298
|
+
const waitsForWork = expectedCommand === "prompt" && timeoutMs === undefined;
|
|
271
299
|
return new Promise((resolve, reject) => {
|
|
300
|
+
let timer;
|
|
301
|
+
let admissionTimer;
|
|
272
302
|
const cleanup = () => {
|
|
273
303
|
clearTimeout(timer);
|
|
304
|
+
clearTimeout(admissionTimer);
|
|
274
305
|
signal?.removeEventListener("abort", onAbort);
|
|
275
306
|
this.pending.delete(id);
|
|
276
307
|
};
|
|
@@ -281,9 +312,17 @@ export class PiRpcClient extends EventEmitter {
|
|
|
281
312
|
const onAbort = () => {
|
|
282
313
|
rejectCommand(signal?.reason);
|
|
283
314
|
};
|
|
284
|
-
const
|
|
285
|
-
this.closeForTransportFailure(new Error(`pi command "${expectedCommand}" timed out after ${
|
|
286
|
-
}
|
|
315
|
+
const expire = (duration) => {
|
|
316
|
+
this.closeForTransportFailure(new Error(`pi command "${expectedCommand}" timed out after ${duration}ms`));
|
|
317
|
+
};
|
|
318
|
+
const armDeadline = () => {
|
|
319
|
+
clearTimeout(timer);
|
|
320
|
+
if (!waitsForWork || !(this.agentActive || this.compacting))
|
|
321
|
+
timer = setTimeout(() => expire(deadlineMs), deadlineMs);
|
|
322
|
+
};
|
|
323
|
+
armDeadline();
|
|
324
|
+
if (waitsForWork)
|
|
325
|
+
admissionTimer = setTimeout(() => expire(PROMPT_ADMISSION_TIMEOUT_MS), PROMPT_ADMISSION_TIMEOUT_MS);
|
|
287
326
|
this.pending.set(id, {
|
|
288
327
|
command: expectedCommand,
|
|
289
328
|
resolve: (r) => {
|
|
@@ -292,6 +331,7 @@ export class PiRpcClient extends EventEmitter {
|
|
|
292
331
|
resolve(r.success ? { ...r, data: r.data } : r);
|
|
293
332
|
},
|
|
294
333
|
reject: rejectCommand,
|
|
334
|
+
...(waitsForWork ? { onWorkState: armDeadline } : {}),
|
|
295
335
|
});
|
|
296
336
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
297
337
|
this.write({ ...cmd, id });
|
package/dist/session-tree.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ export declare class SessionTreeError extends Error {
|
|
|
52
52
|
export declare function durableShellTree(tree: SessionTreeState): SessionTreeState;
|
|
53
53
|
export declare function getEntries(pi: PiRpcClient, signal?: AbortSignal): Promise<Entries>;
|
|
54
54
|
export declare function readSessionTree(piPath: string): Entries;
|
|
55
|
-
/**
|
|
55
|
+
/** Checkpoint persisted progress after agent_end, successful compaction, or process exit. */
|
|
56
56
|
export declare function recordTurn(s: ManagedSession): void;
|
|
57
57
|
export declare function branchEntries(tree: Entries, leafId?: string | null): TreeEntry[];
|
|
58
58
|
/** Live ids belong to the UI; restored ids belong to JSONL offsets. Save both. */
|
package/dist/session-tree.js
CHANGED
|
@@ -44,10 +44,19 @@ export function readSessionTree(piPath) {
|
|
|
44
44
|
}
|
|
45
45
|
return { entries, leafId: entries.at(-1)?.id ?? null };
|
|
46
46
|
}
|
|
47
|
-
/**
|
|
47
|
+
/** Checkpoint persisted progress after agent_end, successful compaction, or process exit. */
|
|
48
48
|
export function recordTurn(s) {
|
|
49
|
-
if (s.piPath)
|
|
50
|
-
|
|
49
|
+
if (!s.piPath)
|
|
50
|
+
return;
|
|
51
|
+
const tree = readSessionTree(s.piPath);
|
|
52
|
+
// Disk order is not navigation: only a descendant can advance a saved selection.
|
|
53
|
+
// An ancestor, missing selection, or newer sibling must leave its bindings intact.
|
|
54
|
+
if (!s.tree.revert &&
|
|
55
|
+
s.tree.leafId !== undefined &&
|
|
56
|
+
s.tree.leafId !== null &&
|
|
57
|
+
!branchEntries(tree).some((entry) => entry.id === s.tree.leafId))
|
|
58
|
+
return;
|
|
59
|
+
bindMessageEntries(s, tree);
|
|
51
60
|
}
|
|
52
61
|
export function branchEntries(tree, leafId = tree.leafId) {
|
|
53
62
|
const byId = new Map(tree.entries.map((entry) => [entry.id, entry]));
|
package/dist/sessions.js
CHANGED
|
@@ -14,7 +14,7 @@ import { SessionLifecycle } from "./lifecycle.js";
|
|
|
14
14
|
import { ApprovalBridge, extensionUiRequestFrom } from "./approvals.js";
|
|
15
15
|
import { getAgentName, getExposedAgentNames } from "./agent-name.js";
|
|
16
16
|
import { RequestBodyError } from "./http.js";
|
|
17
|
-
import { bindMessageEntries, durableShellTree, forkConversation, getEntries, navigate, readSessionTree, revertConversation, SessionTreeError, unrevertConversation, } from "./session-tree.js";
|
|
17
|
+
import { bindMessageEntries, durableShellTree, forkConversation, getEntries, navigate, recordTurn, readSessionTree, revertConversation, SessionTreeError, unrevertConversation, } from "./session-tree.js";
|
|
18
18
|
import { SubagentBridge } from "./subagents.js";
|
|
19
19
|
import { MonitorSessions } from "./monitor-sessions.js";
|
|
20
20
|
import { sessionDiff } from "./session-diff.js";
|
|
@@ -239,6 +239,13 @@ export class SessionManager {
|
|
|
239
239
|
name: "UnknownError",
|
|
240
240
|
data: { message: `pi process exited (code=${code})` },
|
|
241
241
|
});
|
|
242
|
+
try {
|
|
243
|
+
recordTurn(s);
|
|
244
|
+
this.persistMeta(s);
|
|
245
|
+
}
|
|
246
|
+
catch (error) {
|
|
247
|
+
this.reportPersistenceError(s, error);
|
|
248
|
+
}
|
|
242
249
|
delete s.interruptRequested;
|
|
243
250
|
this.subagents.stopParent(s.info.id);
|
|
244
251
|
this.monitors.stopParent(s.info.id);
|