antechamber 0.2.19 → 0.2.21

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,21 @@ and this project uses [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.21] - 2026-10-17
11
+
12
+ ### Fixed
13
+
14
+ - Keep a session's thinking level when it is reopened. User messages now carry the level as `model.variant`, both
15
+ live and in history rebuilt from OmO's session file. Without it OpenChamber fell back to the agent's default effort
16
+ and sent that with the next prompt, which reset the level and broke the prompt cache.
17
+
18
+ ## [0.2.20] - 2026-10-17
19
+
20
+ ### Fixed
21
+
22
+ - Show a session as running in OpenChamber while a subagent it started from `eval` (such as an OmO Graph run) is
23
+ still working. The adapter marked such a child busy without announcing it, so the parent's running dot stayed off.
24
+
10
25
  ## [0.2.19] - 2026-10-17
11
26
 
12
27
  ### Fixed
@@ -35,7 +35,7 @@ export declare function syncSessionCost(s: ManagedSession): boolean;
35
35
  * IDs derive from the native compaction entry, not its retained boundary:
36
36
  * multiple compactions can retain the same entry.
37
37
  */
38
- export declare function compactionMessages(s: ManagedSession, key: string, summary: string, created: number, model: OCModelRef | undefined): [OCMessageWithParts, OCMessageWithParts];
38
+ export declare function compactionMessages(s: ManagedSession, key: string, summary: string, created: number, model: OCModelRef | undefined, thinkingLevel: string | undefined): [OCMessageWithParts, OCMessageWithParts];
39
39
  interface PiEventServices {
40
40
  readonly emit: (directory: string, event: OCEvent | TodoUpdated) => void;
41
41
  readonly subagents: SubagentBridge;
@@ -5,6 +5,7 @@ import { TodoBridge } from "./todo-bridge.js";
5
5
  import { readSessionTree, recordTurn } from "./session-tree.js";
6
6
  import { touchedFiles } from "./session-diff.js";
7
7
  import { getAgentName } from "./agent-name.js";
8
+ import { userMessageModel } from "./thinking-levels.js";
8
9
  import { debugLog } from "./misc-routes.js";
9
10
  import { envSetting } from "./config.js";
10
11
  export function id(prefix) {
@@ -100,14 +101,21 @@ export function syncSessionCost(s) {
100
101
  * IDs derive from the native compaction entry, not its retained boundary:
101
102
  * multiple compactions can retain the same entry.
102
103
  */
103
- export function compactionMessages(s, key, summary, created, model) {
104
+ export function compactionMessages(s, key, summary, created, model, thinkingLevel) {
104
105
  const sessionID = s.info.id;
105
106
  const userID = `msg_compaction_${key}`;
106
107
  const summaryID = `${userID}_summary`;
107
108
  const served = model ?? { providerID: "unknown", modelID: "unknown" };
108
109
  return [
109
110
  {
110
- info: { id: userID, sessionID, role: "user", time: { created }, agent: getAgentName(), model: served },
111
+ info: {
112
+ id: userID,
113
+ sessionID,
114
+ role: "user",
115
+ time: { created },
116
+ agent: getAgentName(),
117
+ model: userMessageModel(served, thinkingLevel),
118
+ },
111
119
  parts: [{ id: `prt_compaction_${key}`, sessionID, messageID: userID, type: "compaction", auto: false }],
112
120
  },
113
121
  {
@@ -222,7 +230,7 @@ export class PiEventTranslator {
222
230
  role: "user",
223
231
  time: { created: message.timestamp ?? Date.now() },
224
232
  agent: getAgentName(),
225
- model: s.model ?? { providerID: "unknown", modelID: "unknown" },
233
+ model: userMessageModel(s.model ?? { providerID: "unknown", modelID: "unknown" }, s.thinkingLevel),
226
234
  },
227
235
  parts: [{ id: id("prt"), sessionID: s.info.id, messageID, type: "text", text }],
228
236
  };
@@ -554,7 +562,7 @@ export class PiEventTranslator {
554
562
  key = entry.id;
555
563
  }
556
564
  }
557
- for (const message of compactionMessages(s, key ?? id("compaction"), result.summary, Date.now(), s.model)) {
565
+ for (const message of compactionMessages(s, key ?? id("compaction"), result.summary, Date.now(), s.model, s.thinkingLevel)) {
558
566
  if (messages.some((item) => item.info.id === message.info.id))
559
567
  continue;
560
568
  // Keep a hidden redo branch after the new turn, as appendMessage does.
@@ -2,6 +2,7 @@
2
2
  import { readFileSync } from "node:fs";
3
3
  import { applyProviderToolResults, finishProviderToolParts, mapTool, providerToolParts, resultMetadata, resultText, } from "./tool-map.js";
4
4
  import { TodoBridge } from "./todo-bridge.js";
5
+ import { userMessageModel } from "./thinking-levels.js";
5
6
  import { contextMessageID } from "./prompt-parts.js";
6
7
  import { bindMessageEntries } from "./session-tree.js";
7
8
  import { taskParts, updateTaskParts, taskCompletions } from "./subagents.js";
@@ -127,6 +128,7 @@ export function parsePiSessionText(s, agentName, raw) {
127
128
  const out = [];
128
129
  const toolParts = new Map();
129
130
  let currentModel = { providerID: "unknown", modelID: "unknown" };
131
+ let currentThinkingLevel;
130
132
  let lastUserId = "";
131
133
  // Like live agent_end, a new user turn closes the previous run: its own
132
134
  // unresolved provider cards end with that run's stop reason.
@@ -142,9 +144,13 @@ export function parsePiSessionText(s, agentName, raw) {
142
144
  currentModel = { providerID: e.provider, modelID: e.modelId };
143
145
  continue;
144
146
  }
147
+ if (e.type === "thinking_level_change" && typeof e.thinkingLevel === "string") {
148
+ currentThinkingLevel = e.thinkingLevel;
149
+ continue;
150
+ }
145
151
  if (e.type === "compaction" && typeof e.summary === "string" && typeof e.firstKeptEntryId === "string") {
146
152
  // Not a run boundary: an automatic compaction can sit between steps of one turn.
147
- out.push(...compactionMessages(s, e.id ?? `r${indexOf.get(e) ?? 0}`, e.summary, entryTime(e), currentModel));
153
+ out.push(...compactionMessages(s, e.id ?? `r${indexOf.get(e) ?? 0}`, e.summary, entryTime(e), currentModel, currentThinkingLevel));
148
154
  continue;
149
155
  }
150
156
  const contextID = contextMessageID(e);
@@ -165,7 +171,7 @@ export function parsePiSessionText(s, agentName, raw) {
165
171
  role: "user",
166
172
  time: { created: time },
167
173
  agent: agentName,
168
- model: currentModel,
174
+ model: userMessageModel(currentModel, currentThinkingLevel),
169
175
  synthetic: true,
170
176
  },
171
177
  parts: [
@@ -205,7 +211,7 @@ export function parsePiSessionText(s, agentName, raw) {
205
211
  role: "user",
206
212
  time: { created },
207
213
  agent: agentName,
208
- model: currentModel,
214
+ model: userMessageModel(currentModel, currentThinkingLevel),
209
215
  };
210
216
  const parts = [
211
217
  {
@@ -457,7 +463,7 @@ export function parsePiSessionText(s, agentName, raw) {
457
463
  role: "user",
458
464
  time: { created: entryTime(entry) },
459
465
  agent: agentName,
460
- model: currentModel,
466
+ model: userMessageModel(currentModel, currentThinkingLevel),
461
467
  },
462
468
  parts: [{ id: `prt_${messageID}_pending`, sessionID, messageID, type: "text", text }],
463
469
  });
package/dist/sessions.js CHANGED
@@ -9,7 +9,7 @@ import { existsSync, readdirSync, statSync, unlinkSync } from "node:fs";
9
9
  import { join } from "node:path";
10
10
  import { COMPACT_RPC_TIMEOUT_MS, PiRpcClient, RPC_COMMAND_TIMEOUT_MS, } from "./pi-rpc.js";
11
11
  import { stopSessionHosts } from "./session-hosts.js";
12
- import { setThinkingLevel } from "./thinking-levels.js";
12
+ import { setThinkingLevel, userMessageModel } from "./thinking-levels.js";
13
13
  import { SessionLifecycle } from "./lifecycle.js";
14
14
  import { ApprovalBridge, extensionUiRequestFrom } from "./approvals.js";
15
15
  import { getAgentName, getExposedAgentNames } from "./agent-name.js";
@@ -908,7 +908,7 @@ export class SessionManager {
908
908
  role: "user",
909
909
  time: { created: Date.now() },
910
910
  agent: getAgentName(),
911
- model: s.model ?? model ?? { providerID: "unknown", modelID: "unknown" },
911
+ model: userMessageModel(s.model ?? model ?? { providerID: "unknown", modelID: "unknown" }, s.thinkingLevel),
912
912
  ...(context ? { synthetic: true } : {}),
913
913
  ...(metadata !== undefined ? { metadata } : {}),
914
914
  };
package/dist/subagents.js CHANGED
@@ -616,15 +616,15 @@ export class SubagentBridge {
616
616
  return parts;
617
617
  }
618
618
  publishRefresh(child, state, publish) {
619
- if (!publish)
620
- return;
621
- const before = new Map(state.previousMessages.map((message) => [message.info.id, JSON.stringify(message)]));
622
- for (const message of child.session.messages ?? []) {
623
- if (before.get(message.info.id) === JSON.stringify(message))
624
- continue;
625
- this.emit(child.session.info.directory, { type: "message.updated", properties: { info: message.info } });
626
- for (const part of message.parts)
627
- this.emitPart(child.session, part);
619
+ if (publish) {
620
+ const before = new Map(state.previousMessages.map((message) => [message.info.id, JSON.stringify(message)]));
621
+ for (const message of child.session.messages ?? []) {
622
+ if (before.get(message.info.id) === JSON.stringify(message))
623
+ continue;
624
+ this.emit(child.session.info.directory, { type: "message.updated", properties: { info: message.info } });
625
+ for (const part of message.parts)
626
+ this.emitPart(child.session, part);
627
+ }
628
628
  }
629
629
  if (state.previousStatus !== child.session.status.type) {
630
630
  this.emit(child.session.info.directory, {
@@ -1,5 +1,5 @@
1
1
  import type { PiRpcClient } from "./pi-rpc.js";
2
- import type { OCModelRef } from "./types.js";
2
+ import type { OCModelRef, OCUserMessage } from "./types.js";
3
3
  export interface ModelSelection extends OCModelRef {
4
4
  readonly variant?: string;
5
5
  }
@@ -10,6 +10,12 @@ export interface ThinkingModel {
10
10
  }
11
11
  /** OmO's RPC list is authoritative; null map entries explicitly disable a level. */
12
12
  export declare function thinkingVariants(model: ThinkingModel): Record<string, Record<string, unknown>> | undefined;
13
+ /**
14
+ * A user message's model as OpenCode >= 1.4 stores it: the thinking level travels as
15
+ * `model.variant`. Without it OpenChamber restores the agent's default effort on every
16
+ * reopen and sends that with the next prompt, which resets the level and breaks the cache.
17
+ */
18
+ export declare function userMessageModel(model: OCModelRef, variant: string | undefined): OCUserMessage["model"];
13
19
  export interface ThinkingState {
14
20
  thinkingLevel?: string;
15
21
  }
@@ -8,6 +8,14 @@ export function thinkingVariants(model) {
8
8
  .map(([level]) => level);
9
9
  return levels.length === 0 ? undefined : Object.fromEntries(levels.map((level) => [level, {}]));
10
10
  }
11
+ /**
12
+ * A user message's model as OpenCode >= 1.4 stores it: the thinking level travels as
13
+ * `model.variant`. Without it OpenChamber restores the agent's default effort on every
14
+ * reopen and sends that with the next prompt, which resets the level and breaks the cache.
15
+ */
16
+ export function userMessageModel(model, variant) {
17
+ return { providerID: model.providerID, modelID: model.modelID, ...(variant !== undefined ? { variant } : {}) };
18
+ }
11
19
  /** Cache only the level OmO actually applied, including when it clamps a request. */
12
20
  export async function setThinkingLevel(state, pi, level) {
13
21
  if (level === undefined || state.thinkingLevel === level)
package/dist/types.d.ts CHANGED
@@ -61,7 +61,10 @@ export interface OCUserMessage {
61
61
  created: number;
62
62
  };
63
63
  agent: string;
64
- model: OCModelRef;
64
+ /** `variant` is the thinking level; OpenChamber restores the effort picker from the latest user message. */
65
+ model: OCModelRef & {
66
+ variant?: string;
67
+ };
65
68
  /** No-reply context admitted without a turn (v2 lists it as `type: "synthetic"`). */
66
69
  synthetic?: boolean;
67
70
  /** Client metadata sent with the prompt (OpenChamber 2.x). */
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "antechamber-omo-graph",
3
- "version": "0.2.19",
3
+ "version": "0.2.21",
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.19",
3
+ "version": "0.2.21",
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",