antechamber 0.2.13 → 0.2.15
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 +23 -0
- package/README.md +1 -1
- package/dist/cli.js +4 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.js +20 -1
- package/dist/commands/serve.js +14 -1
- package/dist/mcp.d.ts +3 -1
- package/dist/mcp.js +7 -2
- package/dist/routes-v1.js +9 -1
- package/dist/running-servers.d.ts +18 -0
- package/dist/running-servers.js +35 -0
- package/dist/sessions.d.ts +4 -2
- package/dist/sessions.js +19 -3
- package/dist/subagents.js +10 -3
- package/extensions/omo-dag/package.json +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.15] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Show saved child-task errors when no transcript is available, and label missing runtime, token and cost statistics
|
|
15
|
+
as unknown instead of zero.
|
|
16
|
+
|
|
17
|
+
## [0.2.14] - 2026-10-16
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Retry a manual compaction that OmO cancelled without a user abort. Compacting a dormant session failed with
|
|
22
|
+
"Compaction cancelled" when its MCP servers finished connecting right after the agent started.
|
|
23
|
+
- List MCP servers for a directory whose sessions are all dormant, from the most recently used live session
|
|
24
|
+
elsewhere, instead of showing none.
|
|
25
|
+
- Keep OpenChamber 1.24's mobile transcript from opening blank after a long agent turn: a v1 message page with no
|
|
26
|
+
user message now reaches back to the prompt that opened its turn.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- `antechamber doctor` reports the installed, latest released and running adapter versions, and warns when the running
|
|
31
|
+
server is older, since a released fix only goes live after OpenChamber relaunches the adapter.
|
|
32
|
+
|
|
10
33
|
## [0.2.13] - 2026-10-16
|
|
11
34
|
|
|
12
35
|
### Fixed
|
package/README.md
CHANGED
|
@@ -142,7 +142,7 @@ antechamber serve --hostname 127.0.0.1 --port 4096
|
|
|
142
142
|
|---|---|
|
|
143
143
|
| `antechamber serve` | OpenCode-compatible server; `--hostname`/`--host`, `--port`, `--directory`. Bare `antechamber` also starts the server. |
|
|
144
144
|
| `antechamber setup` | Configure OpenChamber; `--settings <file>`, `--profile <dir>`, `--binary <path>`, `--dry-run`, `--systemd [unit]`, `--pi-binary <path>`, `--restart`, `--auto-update`. |
|
|
145
|
-
| `antechamber doctor` | Read-only Node, backend, agent/state directories, teardown, OpenChamber and auth checks; `--json`, `--settings <file>`, `--profile <dir>`. Exits 1 on any FAIL; warnings do not fail. |
|
|
145
|
+
| `antechamber doctor` | Read-only Node, backend, agent/state directories, teardown, OpenChamber and auth checks, plus the installed, latest released and running adapter versions (a running server older than the install needs an OpenChamber restart); `--json`, `--settings <file>`, `--profile <dir>`. Exits 1 on any FAIL; warnings do not fail. |
|
|
146
146
|
| `antechamber upgrade` | Install the latest release over a global npm or bun install, with the package manager that owns it. Refused for source checkouts and `npx`. |
|
|
147
147
|
| `antechamber version` | Package version plus OpenCode API contract version. `--version`/`-v` deliberately print exactly `opencode v2.0.20` for OpenChamber. |
|
|
148
148
|
| `antechamber help` | Usage and options; also `--help`/`-h`. |
|
package/dist/cli.js
CHANGED
|
@@ -61,10 +61,13 @@ try {
|
|
|
61
61
|
}
|
|
62
62
|
else if (command === "doctor") {
|
|
63
63
|
const { doctor, formatDoctor } = await import("./commands/doctor.js");
|
|
64
|
+
const { createContext } = await import("./commands/context.js");
|
|
65
|
+
const { fetchLatestVersion } = await import("./self-update.js");
|
|
66
|
+
const latest = await fetchLatestVersion().catch((error) => error instanceof Error ? error : new Error(String(error)));
|
|
64
67
|
const report = doctor({
|
|
65
68
|
...(values.settings !== undefined ? { settings: values.settings } : {}),
|
|
66
69
|
...(values.profile !== undefined ? { profile: values.profile } : {}),
|
|
67
|
-
});
|
|
70
|
+
}, createContext(), latest);
|
|
68
71
|
console.log(formatDoctor(report, values.json ?? false));
|
|
69
72
|
process.exitCode = report.exitCode;
|
|
70
73
|
}
|
|
@@ -9,6 +9,6 @@ interface DoctorReport {
|
|
|
9
9
|
readonly exitCode: 0 | 1;
|
|
10
10
|
}
|
|
11
11
|
/** Checks capabilities with access/stat only, never creating state or config files. */
|
|
12
|
-
export declare function doctor(options?: SettingsOptions, ctx?: CommandContext): DoctorReport;
|
|
12
|
+
export declare function doctor(options?: SettingsOptions, ctx?: CommandContext, latestVersion?: string | Error): DoctorReport;
|
|
13
13
|
export declare function formatDoctor(report: DoctorReport, json: boolean): string;
|
|
14
14
|
export {};
|
package/dist/commands/doctor.js
CHANGED
|
@@ -3,9 +3,12 @@ import { piAgentDir } from "../agent-dir.js";
|
|
|
3
3
|
import { detectAgentName } from "../agent-name.js";
|
|
4
4
|
import { agentBinary, defaultStateDir, envSetting, isPlainPi } from "../config.js";
|
|
5
5
|
import { findPidfdPython } from "../pidfd.js";
|
|
6
|
+
import { readRunningServers } from "../running-servers.js";
|
|
7
|
+
import { isNewerVersion } from "../self-update.js";
|
|
8
|
+
import { ADAPTER_VERSION } from "../version.js";
|
|
6
9
|
import { autoUpdateLauncherPath, autoUpdateLauncherTarget, createContext, isPackageBinary, readSettings, resolveBinary, settingsPath, } from "./context.js";
|
|
7
10
|
/** Checks capabilities with access/stat only, never creating state or config files. */
|
|
8
|
-
export function doctor(options = {}, ctx = createContext()) {
|
|
11
|
+
export function doctor(options = {}, ctx = createContext(), latestVersion) {
|
|
9
12
|
const checks = [];
|
|
10
13
|
const add = (name, status, message) => {
|
|
11
14
|
checks.push({ name, status, message });
|
|
@@ -42,6 +45,22 @@ export function doctor(options = {}, ctx = createContext()) {
|
|
|
42
45
|
catch (error) {
|
|
43
46
|
add("state-dir", "FAIL", `${stateDir}: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
47
|
}
|
|
48
|
+
const latest = typeof latestVersion === "string" ? latestVersion : undefined;
|
|
49
|
+
if (latestVersion instanceof Error)
|
|
50
|
+
add("version", "INFO", `${ADAPTER_VERSION}; latest unknown (${latestVersion.message})`);
|
|
51
|
+
else if (latest !== undefined && isNewerVersion(latest, ADAPTER_VERSION))
|
|
52
|
+
add("version", "WARN", `${ADAPTER_VERSION} installed; ${latest} is released: run antechamber upgrade`);
|
|
53
|
+
else
|
|
54
|
+
add("version", "PASS", ADAPTER_VERSION);
|
|
55
|
+
// A released fix is live only once OpenChamber relaunches the adapter.
|
|
56
|
+
const running = readRunningServers(stateDir, ctx.fs, (pid) => ctx.platform === "linux" ? ctx.fs.existsSync(`/proc/${pid}`) : true);
|
|
57
|
+
if (running.length === 0)
|
|
58
|
+
add("running", "INFO", `no running antechamber server recorded in ${stateDir}`);
|
|
59
|
+
for (const server of running) {
|
|
60
|
+
const target = latest !== undefined && isNewerVersion(latest, ADAPTER_VERSION) ? latest : ADAPTER_VERSION;
|
|
61
|
+
const stale = isNewerVersion(target, server.version);
|
|
62
|
+
add("running", stale ? "WARN" : "PASS", `pid ${server.pid} at ${server.url} runs ${server.version}${stale ? `; restart OpenChamber to run ${target}` : ""}`);
|
|
63
|
+
}
|
|
45
64
|
const python = ctx.platform === "linux"
|
|
46
65
|
? findPidfdPython(ctx.env, (candidate, args) => {
|
|
47
66
|
try {
|
package/dist/commands/serve.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { defaultStateDir, envSetting } from "../config.js";
|
|
4
|
+
import { recordRunningServer } from "../running-servers.js";
|
|
2
5
|
import { selfUpdate } from "../self-update.js";
|
|
3
6
|
import { AdapterServer, isLoopbackHost, serverPassword } from "../server.js";
|
|
7
|
+
import { ADAPTER_VERSION } from "../version.js";
|
|
4
8
|
export async function serve(options) {
|
|
5
9
|
const { port, host, directory, insecureNoAuth, compatible } = options;
|
|
6
10
|
if (!isLoopbackHost(host) &&
|
|
@@ -10,7 +14,9 @@ export async function serve(options) {
|
|
|
10
14
|
throw new Error(`refusing to bind ${host} without authentication. Set OPENCODE_SERVER_PASSWORD or ANTECHAMBER_SERVER_PASSWORD, or explicitly use --insecure-no-auth (ANTECHAMBER_ALLOW_INSECURE=1).`);
|
|
11
15
|
}
|
|
12
16
|
const server = new AdapterServer({ port, host, defaultDirectory: directory });
|
|
17
|
+
let forget = () => { };
|
|
13
18
|
const shutdown = () => {
|
|
19
|
+
forget();
|
|
14
20
|
void server.stop().then(() => process.exit(0), (error) => {
|
|
15
21
|
console.error("antechamber: shutdown failed:", error);
|
|
16
22
|
process.exit(1);
|
|
@@ -21,6 +27,13 @@ export async function serve(options) {
|
|
|
21
27
|
process.on("SIGINT", shutdown);
|
|
22
28
|
process.on("SIGTERM", shutdown);
|
|
23
29
|
await server.start();
|
|
30
|
+
try {
|
|
31
|
+
forget = recordRunningServer(envSetting("STATE_DIR") ?? defaultStateDir(homedir(), fs.existsSync), { pid: process.pid, version: ADAPTER_VERSION, url: server.url, started: new Date().toISOString() }, fs);
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
// Only doctor reads the record; serving must not depend on it.
|
|
35
|
+
console.error(`antechamber: cannot record the running version: ${error instanceof Error ? error.message : String(error)}`);
|
|
36
|
+
}
|
|
24
37
|
if (compatible)
|
|
25
38
|
console.log(`opencode server listening on ${server.url}`);
|
|
26
39
|
else {
|
package/dist/mcp.d.ts
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* OpenChamber scopes MCP calls by directory only, while OmO owns MCP per session process.
|
|
5
5
|
* Everything therefore targets the most recently used LIVE session in the directory; a
|
|
6
|
-
* dormant session is never revived just to answer an MCP request.
|
|
6
|
+
* dormant session is never revived just to answer an MCP request. Status reads for a
|
|
7
|
+
* directory without one fall back to the most recently used live session elsewhere.
|
|
8
|
+
* Authorization stays scoped to the directory.
|
|
7
9
|
*
|
|
8
10
|
* Status comes from the read-only `get_loaded_surfaces` RPC. Authorization drives OmO's
|
|
9
11
|
* documented `/mcp` subcommands (docs/mcp.md "Auth"):
|
package/dist/mcp.js
CHANGED
|
@@ -3,7 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* OpenChamber scopes MCP calls by directory only, while OmO owns MCP per session process.
|
|
5
5
|
* Everything therefore targets the most recently used LIVE session in the directory; a
|
|
6
|
-
* dormant session is never revived just to answer an MCP request.
|
|
6
|
+
* dormant session is never revived just to answer an MCP request. Status reads for a
|
|
7
|
+
* directory without one fall back to the most recently used live session elsewhere.
|
|
8
|
+
* Authorization stays scoped to the directory.
|
|
7
9
|
*
|
|
8
10
|
* Status comes from the read-only `get_loaded_surfaces` RPC. Authorization drives OmO's
|
|
9
11
|
* documented `/mcp` subcommands (docs/mcp.md "Auth"):
|
|
@@ -87,7 +89,10 @@ async function loadedServers(pi) {
|
|
|
87
89
|
: []);
|
|
88
90
|
}
|
|
89
91
|
async function directoryServers(sessions, directory) {
|
|
90
|
-
|
|
92
|
+
// A directory whose sessions are all dormant would list nothing. Its servers are mostly
|
|
93
|
+
// the user's global and built-in ones, so any running session gives a better answer.
|
|
94
|
+
const fallback = sessions.liveSessions()[0]?.pi;
|
|
95
|
+
const live = liveSession(sessions, directory) ?? (fallback === undefined ? undefined : { pi: fallback });
|
|
91
96
|
return live === undefined ? [] : loadedServers(live.pi).catch(() => []);
|
|
92
97
|
}
|
|
93
98
|
async function hasMcpCommand(pi) {
|
package/dist/routes-v1.js
CHANGED
|
@@ -147,7 +147,15 @@ export async function handleV1(services, req, res, url, path, method, directory)
|
|
|
147
147
|
json(res, 200, []);
|
|
148
148
|
return;
|
|
149
149
|
}
|
|
150
|
-
|
|
150
|
+
let start = limit === undefined ? 0 : Math.max(0, end - limit);
|
|
151
|
+
// OpenChamber 1.24 widens a page with no user message only up to 120 messages
|
|
152
|
+
// (150 on desktop), then renders it with no turn to anchor on: blank. A long agent
|
|
153
|
+
// turn outgrows that, so a page reaches back to the prompt that opened its turn.
|
|
154
|
+
if (!messages.slice(start, end).some((message) => message.info.role === "user")) {
|
|
155
|
+
const prompt = messages.findLastIndex((message, index) => index < start && message.info.role === "user");
|
|
156
|
+
if (prompt >= 0)
|
|
157
|
+
start = prompt;
|
|
158
|
+
}
|
|
151
159
|
const page = messages.slice(start, end);
|
|
152
160
|
if (start > 0 && page[0] !== undefined)
|
|
153
161
|
res.setHeader("x-next-cursor", page[0].info.id);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Records each `serve` process under `<stateDir>/run/<pid>.json` so `antechamber doctor`
|
|
3
|
+
* can tell which version is actually running. A merged and released fix does nothing until
|
|
4
|
+
* OpenChamber relaunches the adapter, and auto-update only installs it for the next launch.
|
|
5
|
+
*/
|
|
6
|
+
import type * as fs from "node:fs";
|
|
7
|
+
export interface RunningServer {
|
|
8
|
+
readonly pid: number;
|
|
9
|
+
readonly version: string;
|
|
10
|
+
readonly url: string;
|
|
11
|
+
readonly started: string;
|
|
12
|
+
}
|
|
13
|
+
type Fs = Pick<typeof fs, "existsSync" | "mkdirSync" | "readdirSync" | "readFileSync" | "rmSync" | "writeFileSync">;
|
|
14
|
+
/** Writes the record and returns its remover; a crash leaves a stale record that readers skip by PID. */
|
|
15
|
+
export declare function recordRunningServer(stateDir: string, server: RunningServer, files: Fs): () => void;
|
|
16
|
+
/** Records whose process is still alive, newest first. */
|
|
17
|
+
export declare function readRunningServers(stateDir: string, files: Fs, alive: (pid: number) => boolean): RunningServer[];
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
const runDir = (stateDir) => join(stateDir, "run");
|
|
3
|
+
/** Writes the record and returns its remover; a crash leaves a stale record that readers skip by PID. */
|
|
4
|
+
export function recordRunningServer(stateDir, server, files) {
|
|
5
|
+
const path = join(runDir(stateDir), `${server.pid}.json`);
|
|
6
|
+
files.mkdirSync(runDir(stateDir), { recursive: true, mode: 0o700 });
|
|
7
|
+
files.writeFileSync(path, `${JSON.stringify(server)}\n`, { mode: 0o600 });
|
|
8
|
+
return () => files.rmSync(path, { force: true });
|
|
9
|
+
}
|
|
10
|
+
/** Records whose process is still alive, newest first. */
|
|
11
|
+
export function readRunningServers(stateDir, files, alive) {
|
|
12
|
+
const dir = runDir(stateDir);
|
|
13
|
+
if (!files.existsSync(dir))
|
|
14
|
+
return [];
|
|
15
|
+
return files
|
|
16
|
+
.readdirSync(dir)
|
|
17
|
+
.filter((name) => /^\d+\.json$/.test(name))
|
|
18
|
+
.flatMap((name) => {
|
|
19
|
+
try {
|
|
20
|
+
const record = JSON.parse(files.readFileSync(join(dir, name), "utf8"));
|
|
21
|
+
if (typeof record !== "object" || record === null)
|
|
22
|
+
return [];
|
|
23
|
+
const { pid, version, url, started } = record;
|
|
24
|
+
if (typeof pid !== "number" || typeof version !== "string" || typeof url !== "string")
|
|
25
|
+
return [];
|
|
26
|
+
if (typeof started !== "string" || !alive(pid))
|
|
27
|
+
return [];
|
|
28
|
+
return [{ pid, version, url, started }];
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return []; // A record being written or removed concurrently.
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
.sort((a, b) => b.started.localeCompare(a.started));
|
|
35
|
+
}
|
package/dist/sessions.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ interface StreamingState {
|
|
|
20
20
|
}
|
|
21
21
|
export interface ManagedSession extends ThinkingState {
|
|
22
22
|
interruptRequested?: boolean;
|
|
23
|
+
/** Counts abort requests, so a compaction cancelled by the user is not retried. */
|
|
24
|
+
abortRequests?: number;
|
|
23
25
|
titleSource?: TitleSource;
|
|
24
26
|
activeUserMessageID?: string;
|
|
25
27
|
inputs?: Map<string, {
|
|
@@ -104,8 +106,8 @@ export declare class SessionManager {
|
|
|
104
106
|
private wirePi;
|
|
105
107
|
/** pi writes `<ts>_<sessionId>.jsonl` at startup — poll briefly for it. */
|
|
106
108
|
private resolvePiFile;
|
|
107
|
-
/** Sessions with a running agent in `directory`, most recently used first; never revives a dormant one. */
|
|
108
|
-
liveSessions(directory
|
|
109
|
+
/** Sessions with a running agent (in `directory`, if given), most recently used first; never revives a dormant one. */
|
|
110
|
+
liveSessions(directory?: string): ManagedSession[];
|
|
109
111
|
list(directory?: string): OCSession[];
|
|
110
112
|
/**
|
|
111
113
|
* Every restorable session on disk (all directories) plus live ones. Backs the
|
package/dist/sessions.js
CHANGED
|
@@ -26,6 +26,9 @@ const SESSION_SCAN_CACHE_TTL_MS = 5_000;
|
|
|
26
26
|
const PI_FILE_RESOLVE_ATTEMPTS = 10;
|
|
27
27
|
const PI_FILE_RESOLVE_RETRY_MS = 200;
|
|
28
28
|
const SESSION_TITLE_MAX_CHARS = 60;
|
|
29
|
+
/** OmO's reply when a compaction is aborted (`CompactionCancelledError`). */
|
|
30
|
+
const OMO_COMPACTION_CANCELLED = "Compaction cancelled";
|
|
31
|
+
const COMPACT_CANCEL_RETRIES = 2;
|
|
29
32
|
export class SessionManager {
|
|
30
33
|
emit;
|
|
31
34
|
defaultDirectory;
|
|
@@ -289,12 +292,12 @@ export class SessionManager {
|
|
|
289
292
|
}
|
|
290
293
|
return undefined;
|
|
291
294
|
}
|
|
292
|
-
/** Sessions with a running agent in `directory`, most recently used first; never revives a dormant one. */
|
|
295
|
+
/** Sessions with a running agent (in `directory`, if given), most recently used first; never revives a dormant one. */
|
|
293
296
|
liveSessions(directory) {
|
|
294
297
|
if (this.disposed)
|
|
295
298
|
return [];
|
|
296
299
|
return [...this.sessions.values()]
|
|
297
|
-
.filter((s) => s.pi !== undefined && s.info.directory === directory)
|
|
300
|
+
.filter((s) => s.pi !== undefined && (directory === undefined || s.info.directory === directory))
|
|
298
301
|
.sort((a, b) => this.lifecycle.lastUsed(b) - this.lifecycle.lastUsed(a));
|
|
299
302
|
}
|
|
300
303
|
list(directory) {
|
|
@@ -688,6 +691,7 @@ export class SessionManager {
|
|
|
688
691
|
const s = this.get(sessionId);
|
|
689
692
|
if (!s)
|
|
690
693
|
return false;
|
|
694
|
+
s.abortRequests = (s.abortRequests ?? 0) + 1;
|
|
691
695
|
if (s.pi !== undefined) {
|
|
692
696
|
if (s.status.type !== "idle")
|
|
693
697
|
s.interruptRequested = true;
|
|
@@ -755,7 +759,19 @@ export class SessionManager {
|
|
|
755
759
|
this.lifecycle.touch(s);
|
|
756
760
|
const timeout = command.type === "compact" ? COMPACT_RPC_TIMEOUT_MS : RPC_COMMAND_TIMEOUT_MS;
|
|
757
761
|
signal?.throwIfAborted();
|
|
758
|
-
const
|
|
762
|
+
const abortsBefore = s.abortRequests ?? 0;
|
|
763
|
+
let response = await pi.command(command, timeout, undefined, onDispatch);
|
|
764
|
+
// OmO cancels a running compaction whenever its active tool set changes. On a session
|
|
765
|
+
// just revived for this request, MCP servers finish connecting a moment after startup
|
|
766
|
+
// and cancel it although nobody asked. Retry unless the user sent an abort meanwhile.
|
|
767
|
+
for (let retry = 0; retry < COMPACT_CANCEL_RETRIES &&
|
|
768
|
+
command.type === "compact" &&
|
|
769
|
+
!response.success &&
|
|
770
|
+
response.error === OMO_COMPACTION_CANCELLED &&
|
|
771
|
+
(s.abortRequests ?? 0) === abortsBefore &&
|
|
772
|
+
!signal?.aborted &&
|
|
773
|
+
s.pi === pi; retry++)
|
|
774
|
+
response = await pi.command(command, timeout);
|
|
759
775
|
if (!signal?.aborted && response.success && (command.type === "compact" || command.type === "bash"))
|
|
760
776
|
await this.reloadMessagesLive(s, signal);
|
|
761
777
|
return response;
|
package/dist/subagents.js
CHANGED
|
@@ -639,10 +639,16 @@ export class SubagentBridge {
|
|
|
639
639
|
const model = typeof data.model === "string"
|
|
640
640
|
? data.model
|
|
641
641
|
: `${resolved.provider ?? "unknown"}/${resolved.model_id ?? "unknown"}`;
|
|
642
|
+
const finalResponse = stripTaskSuffix(typeof data.final_response === "string" ? data.final_response : "");
|
|
643
|
+
const error = terminal(data.status) && data.status !== "completed" && typeof data.error_message === "string"
|
|
644
|
+
? data.error_message
|
|
645
|
+
: undefined;
|
|
646
|
+
const runtime = stats.runtime_ms ?? data.duration_ms;
|
|
642
647
|
const text = [
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
"
|
|
648
|
+
finalResponse,
|
|
649
|
+
error && !finalResponse.includes(error) ? error : "",
|
|
650
|
+
`Model: ${model}\nRuntime: ${runtime === undefined ? "unknown" : `${runtime} ms`}\nTokens: ${stats.total_tokens ?? "unknown"}\nCost: ${stats.cost_usd === undefined ? "unknown" : `$${stats.cost_usd}`}`,
|
|
651
|
+
"Full transcript is unavailable.",
|
|
646
652
|
]
|
|
647
653
|
.filter(Boolean)
|
|
648
654
|
.join("\n\n");
|
|
@@ -666,6 +672,7 @@ export class SubagentBridge {
|
|
|
666
672
|
role: "assistant",
|
|
667
673
|
provider: resolved.provider ?? model.split("/")[0],
|
|
668
674
|
model: resolved.model_id ?? model.split("/").slice(1).join("/"),
|
|
675
|
+
...(error ? { stopReason: "error", errorMessage: error } : {}),
|
|
669
676
|
// run_stats tokens are totals across the whole run (every model call),
|
|
670
677
|
// not one request's context, so only the cost is carried as usage; the
|
|
671
678
|
// totals stay in the text above. Token counts here read as context fill.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antechamber",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.15",
|
|
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",
|
|
@@ -58,7 +58,8 @@
|
|
|
58
58
|
"smoke": "tsx contrib/smoke.ts",
|
|
59
59
|
"omo-smoke": "tsx contrib/omo/omo-smoke.ts",
|
|
60
60
|
"model-smoke": "tsx contrib/model-consistency.ts",
|
|
61
|
-
"v2-smoke": "tsx contrib/v2-smoke.ts"
|
|
61
|
+
"v2-smoke": "tsx contrib/v2-smoke.ts",
|
|
62
|
+
"replay": "tsx contrib/replay/transcripts.ts"
|
|
62
63
|
},
|
|
63
64
|
"allowScripts": {
|
|
64
65
|
"esbuild@0.28.2": true
|