antechamber 1.0.17 → 1.0.19
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 +16 -0
- package/dist/session-tree.d.ts +8 -0
- package/dist/session-tree.js +56 -6
- package/dist/sessions.js +3 -3
- 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,19 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.19] - 2026-10-25
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Keep the parent's last reply in a `/btw` side thread. OpenChamber forks `/btw` at the last completed assistant
|
|
15
|
+
reply, and the adapter cut strictly before it, so the fork ended mid-turn on the reply's tool calls and the side
|
|
16
|
+
agent never saw the answer the main thread had just given. An assistant boundary now keeps that reply and the
|
|
17
|
+
results of its own tool calls.
|
|
18
|
+
|
|
19
|
+
## [1.0.18] - 2026-10-25
|
|
20
|
+
|
|
21
|
+
- fix(session): accept late OmO extension entries after a fork refresh (#68)
|
|
22
|
+
|
|
10
23
|
## [1.0.17] - 2026-10-24
|
|
11
24
|
|
|
12
25
|
### Fixed
|
|
@@ -14,6 +27,9 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
14
27
|
- Keep a session's agent out of idle eviction while a background terminal session (`bash` run in the background)
|
|
15
28
|
is still running. The reaper killed it with the agent 15 minutes after the turn ended, so a CI poller or long
|
|
16
29
|
build died silently and the session never woke up with its result.
|
|
30
|
+
- Fork (and `/btw`) on large sessions no longer fails with "Native tree refresh did not restore the saved
|
|
31
|
+
navigation". OmO's session-start hooks append extension entries (rules scan, session bindings) on top of the
|
|
32
|
+
restored branch right after the adapter reloads the session, and the check treated those as a lost navigation.
|
|
17
33
|
|
|
18
34
|
## [1.0.16] - 2026-10-23
|
|
19
35
|
|
package/dist/session-tree.d.ts
CHANGED
|
@@ -36,9 +36,11 @@ export interface TreeEntry {
|
|
|
36
36
|
message?: {
|
|
37
37
|
role: string;
|
|
38
38
|
clientMessageId?: string;
|
|
39
|
+
toolCallId?: string;
|
|
39
40
|
content?: string | Array<{
|
|
40
41
|
type: string;
|
|
41
42
|
text?: string;
|
|
43
|
+
id?: string;
|
|
42
44
|
}>;
|
|
43
45
|
command?: string;
|
|
44
46
|
output?: string;
|
|
@@ -56,6 +58,12 @@ export declare class SessionTreeError extends Error {
|
|
|
56
58
|
}
|
|
57
59
|
export declare function durableShellTree(tree: SessionTreeState): SessionTreeState;
|
|
58
60
|
export declare function getEntries(pi: PiRpcClient, signal?: AbortSignal): Promise<Entries>;
|
|
61
|
+
/**
|
|
62
|
+
* Whether the native leaf is the navigated leaf. OmO session_start hooks (pi-rules.scan,
|
|
63
|
+
* session bindings) can append extension `custom` entries on top of it after a switch;
|
|
64
|
+
* those carry no conversation, so they do not undo the navigation.
|
|
65
|
+
*/
|
|
66
|
+
export declare function restoredLeaf(tree: Entries, leaf: string | null): boolean;
|
|
59
67
|
export declare function readSessionTree(piPath: string): Entries;
|
|
60
68
|
/** Checkpoint persisted progress after agent_end, successful compaction, or process exit. */
|
|
61
69
|
export declare function recordTurn(s: ManagedSession): void;
|
package/dist/session-tree.js
CHANGED
|
@@ -34,6 +34,24 @@ export async function getEntries(pi, signal) {
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Whether the native leaf is the navigated leaf. OmO session_start hooks (pi-rules.scan,
|
|
39
|
+
* session bindings) can append extension `custom` entries on top of it after a switch;
|
|
40
|
+
* those carry no conversation, so they do not undo the navigation.
|
|
41
|
+
*/
|
|
42
|
+
export function restoredLeaf(tree, leaf) {
|
|
43
|
+
let id = tree.leafId;
|
|
44
|
+
// Bounded so a corrupt parent loop cannot hang the operation.
|
|
45
|
+
for (let steps = 0; id !== leaf; steps++) {
|
|
46
|
+
if (steps >= tree.entries.length)
|
|
47
|
+
return false;
|
|
48
|
+
const entry = tree.entries.find((candidate) => candidate.id === id);
|
|
49
|
+
if (entry?.type !== "custom")
|
|
50
|
+
return false;
|
|
51
|
+
id = entry.parentId;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
37
55
|
export function readSessionTree(piPath) {
|
|
38
56
|
const lines = readFileSync(piPath, "utf8").trimEnd().split("\n");
|
|
39
57
|
const entries = [];
|
|
@@ -300,24 +318,56 @@ function entryForMessage(s, tree, messageID) {
|
|
|
300
318
|
}
|
|
301
319
|
return entry;
|
|
302
320
|
}
|
|
321
|
+
/**
|
|
322
|
+
* The last entry of an assistant reply: the reply, or the latest result of its own tool calls.
|
|
323
|
+
* OpenChamber 2.x /btw names a final reply (no tool calls); 1.24's /btw can name any completed
|
|
324
|
+
* step, whose tool results belong to the same OpenChamber message.
|
|
325
|
+
*/
|
|
326
|
+
function replyEnd(tree, reply, leafId) {
|
|
327
|
+
const content = reply.message?.content;
|
|
328
|
+
const calls = new Set(Array.isArray(content) ? content.flatMap((part) => (part.type === "toolCall" && part.id ? [part.id] : [])) : []);
|
|
329
|
+
const branch = branchEntries(tree, leafId);
|
|
330
|
+
const start = branch.indexOf(reply);
|
|
331
|
+
if (calls.size === 0 || start < 0)
|
|
332
|
+
return reply.id;
|
|
333
|
+
let end = reply.id;
|
|
334
|
+
for (const entry of branch.slice(start + 1)) {
|
|
335
|
+
const role = entry.message?.role;
|
|
336
|
+
if (role === "toolResult" && entry.message?.toolCallId !== undefined && calls.has(entry.message.toolCallId))
|
|
337
|
+
end = entry.id;
|
|
338
|
+
else if (role === "assistant" || role === "user")
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
return end;
|
|
342
|
+
}
|
|
303
343
|
export async function forkConversation(s, pi, tree, messageID) {
|
|
304
344
|
const entry = messageID === undefined ? branchEntries(tree).at(-1) : entryForMessage(s, tree, messageID);
|
|
305
345
|
if (!entry)
|
|
306
346
|
throw new SessionTreeError("Cannot fork an empty conversation");
|
|
307
347
|
const shellUser = entry.message?.role === "bashExecution" &&
|
|
308
348
|
s.messages?.find((message) => message.info.id === messageID)?.info.role === "user";
|
|
309
|
-
// OpenCode forks strictly before any selected message
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
349
|
+
// OpenCode forks strictly before any selected message, but OpenChamber only names an
|
|
350
|
+
// assistant reply as the boundary from /btw, which forks at the last completed reply
|
|
351
|
+
// so the side thread starts from a settled conversation: keep that reply. OmO's
|
|
352
|
+
// default "before" position only accepts user entries, so other non-user boundaries
|
|
353
|
+
// fork "at" the entry preceding them.
|
|
354
|
+
const reply = messageID !== undefined && entry.message?.role === "assistant";
|
|
355
|
+
const beforeOther = messageID !== undefined && !reply && entry.message?.role !== "user" && entry.message?.role !== "bashExecution";
|
|
356
|
+
const forkEntryId = reply
|
|
357
|
+
? // A staged revert hides the reply's results from the live leaf.
|
|
358
|
+
replyEnd(tree, entry, s.tree.revert?.previousLeafId ?? tree.leafId)
|
|
359
|
+
: shellUser || beforeOther
|
|
360
|
+
? entry.parentId
|
|
361
|
+
: entry.id;
|
|
314
362
|
if (forkEntryId === null) {
|
|
315
363
|
throw new SessionTreeError(shellUser ? "Cannot fork before a root shell entry" : "Cannot fork before the first entry");
|
|
316
364
|
}
|
|
317
365
|
const response = await pi.command({
|
|
318
366
|
type: "fork",
|
|
319
367
|
entryId: forkEntryId,
|
|
320
|
-
...(messageID === undefined || beforeOther || entry.message?.role === "bashExecution"
|
|
368
|
+
...(messageID === undefined || reply || beforeOther || entry.message?.role === "bashExecution"
|
|
369
|
+
? { position: "at" }
|
|
370
|
+
: {}),
|
|
321
371
|
});
|
|
322
372
|
if (!response.success)
|
|
323
373
|
throw new SessionTreeError(response.error ?? "OmO fork failed");
|
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, branchEntries, durableShellTree, followExternalAppends, forkConversation, getEntries, navigate, recordTurn, readSessionTree, revertConversation, SessionTreeError, unrevertConversation, } from "./session-tree.js";
|
|
17
|
+
import { bindMessageEntries, branchEntries, durableShellTree, followExternalAppends, forkConversation, getEntries, navigate, recordTurn, readSessionTree, restoredLeaf, 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 { WorkStatusBridge } from "./work-status.js";
|
|
@@ -532,7 +532,7 @@ export class SessionManager {
|
|
|
532
532
|
try {
|
|
533
533
|
const leaf = saved === undefined ? undefined : await navigate(pi, saved);
|
|
534
534
|
tree = await getEntries(pi);
|
|
535
|
-
if (leaf !== undefined && tree
|
|
535
|
+
if (leaf !== undefined && !restoredLeaf(tree, leaf))
|
|
536
536
|
throw new SessionTreeError("Native tree refresh did not restore the saved navigation");
|
|
537
537
|
if (leaf !== undefined)
|
|
538
538
|
s.tree.leafId = leaf;
|
|
@@ -1239,7 +1239,7 @@ export class SessionManager {
|
|
|
1239
1239
|
(s.tree.revert || resumedLeaf !== saved || !branchEntries(tree).some((entry) => entry.id === saved))) {
|
|
1240
1240
|
const leaf = await navigate(pi, saved);
|
|
1241
1241
|
tree = await getEntries(pi);
|
|
1242
|
-
if (tree
|
|
1242
|
+
if (!restoredLeaf(tree, leaf))
|
|
1243
1243
|
throw new SessionTreeError("Revival did not restore the saved navigation");
|
|
1244
1244
|
s.tree.leafId = leaf;
|
|
1245
1245
|
}
|