antechamber 0.2.12 → 0.2.13

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 CHANGED
@@ -7,6 +7,14 @@ and this project uses [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.13] - 2026-10-16
11
+
12
+ ### Fixed
13
+
14
+ - Show and resume turns that another OmO client, such as the terminal, appended to a session while the connector
15
+ was detached. Previously the connector stayed pinned to its last saved message. A committed revert's abandoned
16
+ branch stays hidden.
17
+
10
18
  ## [0.2.12] - 2026-10-16
11
19
 
12
20
  ### Fixed
@@ -305,6 +305,8 @@ function sessionTreeState(value) {
305
305
  return false;
306
306
  if (value.leafId !== undefined && value.leafId !== null && typeof value.leafId !== "string")
307
307
  return false;
308
+ if (value.fileSize !== undefined && (typeof value.fileSize !== "number" || !Number.isFinite(value.fileSize)))
309
+ return false;
308
310
  if (value.revert !== undefined) {
309
311
  if (!record(value.revert) ||
310
312
  typeof value.revert.messageID !== "string" ||
@@ -18,6 +18,8 @@ export interface SessionTreeState {
18
18
  messages: [OCMessageWithParts, OCMessageWithParts];
19
19
  }>;
20
20
  leafId?: string | null;
21
+ /** Session file size when this connector last saved a live runtime; growth past it came from another client. */
22
+ fileSize?: number;
21
23
  revert?: {
22
24
  messageID: string;
23
25
  previousLeafId: string | null;
@@ -54,6 +56,13 @@ export declare function getEntries(pi: PiRpcClient, signal?: AbortSignal): Promi
54
56
  export declare function readSessionTree(piPath: string): Entries;
55
57
  /** Checkpoint persisted progress after agent_end, successful compaction, or process exit. */
56
58
  export declare function recordTurn(s: ManagedSession): void;
59
+ /**
60
+ * Another OmO client (e.g. the terminal) resumes at the file tail and appends there.
61
+ * Follow that tail when it descends from the saved selection and was written after
62
+ * this connector last saved, so a committed revert's abandoned branch stays hidden.
63
+ * Registries saved before the size marker existed follow any descendant tail.
64
+ */
65
+ export declare function followExternalAppends(s: ManagedSession, piPath: string): void;
57
66
  export declare function branchEntries(tree: Entries, leafId?: string | null): TreeEntry[];
58
67
  /** Live ids belong to the UI; restored ids belong to JSONL offsets. Save both. */
59
68
  export declare function bindMessageEntries(s: ManagedSession, tree: Entries): void;
@@ -1,5 +1,5 @@
1
1
  import { RPC_COMMAND_TIMEOUT_MS } from "./pi-rpc.js";
2
- import { readFileSync } from "node:fs";
2
+ import { readFileSync, statSync } from "node:fs";
3
3
  import { contextMessageID, nativePromptInput } from "./prompt-parts.js";
4
4
  export class SessionTreeError extends Error {
5
5
  }
@@ -58,6 +58,33 @@ export function recordTurn(s) {
58
58
  return;
59
59
  bindMessageEntries(s, tree);
60
60
  }
61
+ /**
62
+ * Another OmO client (e.g. the terminal) resumes at the file tail and appends there.
63
+ * Follow that tail when it descends from the saved selection and was written after
64
+ * this connector last saved, so a committed revert's abandoned branch stays hidden.
65
+ * Registries saved before the size marker existed follow any descendant tail.
66
+ */
67
+ export function followExternalAppends(s, piPath) {
68
+ const saved = s.tree.leafId;
69
+ if (s.tree.revert || saved === undefined || saved === null)
70
+ return;
71
+ const size = statSync(piPath, { throwIfNoEntry: false })?.size;
72
+ if (size === undefined || (s.tree.fileSize !== undefined && size <= s.tree.fileSize))
73
+ return;
74
+ let tree;
75
+ try {
76
+ tree = readSessionTree(piPath);
77
+ }
78
+ catch (error) {
79
+ // History parsing skips malformed lines; keep the saved selection rather than guess a tail.
80
+ if (error instanceof SessionTreeError)
81
+ return;
82
+ throw error;
83
+ }
84
+ if (tree.leafId === saved || !branchEntries(tree).some((entry) => entry.id === saved))
85
+ return;
86
+ s.tree.leafId = tree.leafId;
87
+ }
61
88
  export function branchEntries(tree, leafId = tree.leafId) {
62
89
  const byId = new Map(tree.entries.map((entry) => [entry.id, entry]));
63
90
  const branch = [];
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, recordTurn, readSessionTree, revertConversation, SessionTreeError, unrevertConversation, } from "./session-tree.js";
17
+ import { bindMessageEntries, durableShellTree, followExternalAppends, 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";
@@ -1167,6 +1167,9 @@ export class SessionManager {
1167
1167
  if (this.monitors.owns(s.info.id))
1168
1168
  return this.monitors.messages(s.info.id);
1169
1169
  if (s.messages === undefined) {
1170
+ // A dormant session may have been continued by another OmO client since the last save.
1171
+ if (s.pi === undefined && s.restored !== undefined)
1172
+ followExternalAppends(s, s.restored.piPath);
1170
1173
  const admissions = s.tree.localShellTurns?.length;
1171
1174
  s.messages = parsePiSessionFile(s, getAgentName());
1172
1175
  this.subagents.discover(s, s.messages);
@@ -1334,6 +1337,12 @@ export class SessionManager {
1334
1337
  const piSessionId = s.piSessionId ?? s.restored?.piSessionId;
1335
1338
  if (piPath === undefined || piSessionId === undefined)
1336
1339
  return;
1340
+ // Only a live runtime vouches for the file contents; dormant saves keep the previous marker.
1341
+ if (s.pi !== undefined) {
1342
+ const size = statSync(piPath, { throwIfNoEntry: false })?.size;
1343
+ if (size !== undefined)
1344
+ s.tree.fileSize = size;
1345
+ }
1337
1346
  this.store.set(s.info.id, {
1338
1347
  ...(this.createdSessions.has(piSessionId) ? { connectorOwned: true } : {}),
1339
1348
  tree: durableShellTree(s.tree),
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antechamber-omo-graph",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "OpenChamber extension: live, interactive graph of the OmO sub-agents behind the current session",
5
5
  "private": true,
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antechamber",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "OpenCode-compatible HTTP/SSE server that lets OpenChamber drive the pi coding agent (and OmO) over RPC",
5
5
  "type": "module",
6
6
  "license": "MIT",