antechamber 1.0.28 → 1.0.30
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 +17 -0
- package/dist/mcp.d.ts +8 -0
- package/dist/mcp.js +45 -0
- package/dist/pi-event-translation.d.ts +2 -0
- package/dist/pi-event-translation.js +18 -0
- package/dist/pi-rpc.d.ts +6 -0
- package/dist/pi-rpc.js +22 -1
- package/dist/sessions.d.ts +7 -0
- package/dist/sessions.js +37 -13
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- 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,23 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [1.0.30] - 2026-10-28
|
|
11
|
+
|
|
12
|
+
- update
|
|
13
|
+
- update
|
|
14
|
+
- update
|
|
15
|
+
|
|
16
|
+
## [1.0.29] - 2026-10-28
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
- Stop OmO from cancelling a `/compact` that revives a session. OmO cancels a running compaction whenever an MCP
|
|
21
|
+
server's tools register, which a freshly started agent does for about a second, so the chat showed
|
|
22
|
+
"Compaction failed" twice (OpenChamber 2.5 draws these events as transcript rows) above a compaction that then
|
|
23
|
+
succeeded. A compaction now waits, at most 10 s, until OmO reports no MCP server still connecting. Cancellations
|
|
24
|
+
nobody asked for are retried up to four times instead of two, and a retried attempt is no longer reported as a
|
|
25
|
+
failure; a compaction the user stops still is.
|
|
26
|
+
|
|
10
27
|
## [1.0.28] - 2026-10-28
|
|
11
28
|
|
|
12
29
|
- fix(mcp): sign in to MCP servers without a running OmO session (#79)
|
package/dist/mcp.d.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* An extension that registers its own `mcp` command makes OmO rename the builtin (`mcp:1`),
|
|
24
24
|
* so the builtin is found by its source, not by name.
|
|
25
25
|
*/
|
|
26
|
+
import { PiRpcClient } from "./pi-rpc.js";
|
|
26
27
|
import type { RouteServices } from "./route-services.js";
|
|
27
28
|
type OCMcpStatus = {
|
|
28
29
|
status: "connected";
|
|
@@ -64,6 +65,13 @@ export declare const MCP_AUTH_COMMAND_TIMEOUT_MS: number;
|
|
|
64
65
|
/** OmO's runtime states fold onto OpenCode's; lazy servers are usable, so they read as connected. */
|
|
65
66
|
export declare function openCodeMcpStatus(server: OmoMcpServer): OCMcpStatus;
|
|
66
67
|
export declare function v2McpStatus(server: OmoMcpServer): V2McpStatus;
|
|
68
|
+
/**
|
|
69
|
+
* Resolves once no MCP server is still connecting, after `budgetMs`, or on abort. OmO cancels a running
|
|
70
|
+
* compaction whenever a server's tools register (setActiveTools -> abortCompaction, senpi
|
|
71
|
+
* chunks/chunk-NNQ7DPUD.js). A probe on 2026-10-28 found three servers connecting when get_state answered,
|
|
72
|
+
* all connected within about a second, each change announced by `loaded_surfaces_changed`.
|
|
73
|
+
*/
|
|
74
|
+
export declare function waitForMcpServers(pi: PiRpcClient, budgetMs: number, signal?: AbortSignal): Promise<void>;
|
|
67
75
|
/** The URL OmO prints on its own line after naming the server (`auth` and `auth-start` both do). */
|
|
68
76
|
export declare function authorizationUrl(text: string, name: string): string | undefined;
|
|
69
77
|
/**
|
package/dist/mcp.js
CHANGED
|
@@ -95,6 +95,51 @@ async function loadedServers(pi) {
|
|
|
95
95
|
]
|
|
96
96
|
: []);
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Resolves once no MCP server is still connecting, after `budgetMs`, or on abort. OmO cancels a running
|
|
100
|
+
* compaction whenever a server's tools register (setActiveTools -> abortCompaction, senpi
|
|
101
|
+
* chunks/chunk-NNQ7DPUD.js). A probe on 2026-10-28 found three servers connecting when get_state answered,
|
|
102
|
+
* all connected within about a second, each change announced by `loaded_surfaces_changed`.
|
|
103
|
+
*/
|
|
104
|
+
export async function waitForMcpServers(pi, budgetMs, signal) {
|
|
105
|
+
const deadline = Date.now() + budgetMs;
|
|
106
|
+
let changed = false;
|
|
107
|
+
let wake = () => { };
|
|
108
|
+
const listener = (ev) => {
|
|
109
|
+
if (ev.type !== "loaded_surfaces_changed")
|
|
110
|
+
return;
|
|
111
|
+
changed = true;
|
|
112
|
+
wake();
|
|
113
|
+
};
|
|
114
|
+
pi.onEvent(listener);
|
|
115
|
+
try {
|
|
116
|
+
while (!signal?.aborted) {
|
|
117
|
+
changed = false;
|
|
118
|
+
const servers = await loadedServers(pi).catch(() => []);
|
|
119
|
+
if (!servers.some((server) => server.status === "connecting"))
|
|
120
|
+
return;
|
|
121
|
+
if (changed)
|
|
122
|
+
continue;
|
|
123
|
+
const left = deadline - Date.now();
|
|
124
|
+
if (left <= 0)
|
|
125
|
+
return;
|
|
126
|
+
await new Promise((resolve) => {
|
|
127
|
+
const done = () => {
|
|
128
|
+
clearTimeout(timer);
|
|
129
|
+
signal?.removeEventListener("abort", done);
|
|
130
|
+
wake = () => { };
|
|
131
|
+
resolve();
|
|
132
|
+
};
|
|
133
|
+
const timer = setTimeout(done, left);
|
|
134
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
135
|
+
wake = done;
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
finally {
|
|
140
|
+
pi.off("event", listener);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
98
143
|
async function directoryServers(sessions, directory) {
|
|
99
144
|
// A directory whose sessions are all dormant would list nothing. Its servers are mostly
|
|
100
145
|
// the user's global and built-in ones, so any running session gives a better answer.
|
|
@@ -76,6 +76,8 @@ export declare class PiEventTranslator {
|
|
|
76
76
|
private onToolEvent;
|
|
77
77
|
private onMessageEvent;
|
|
78
78
|
private onCompactionEvent;
|
|
79
|
+
/** Publishes a held compaction cancellation once sessionRpc will not retry it. */
|
|
80
|
+
releaseCompactionEnd(s: ManagedSession): void;
|
|
79
81
|
private finishStreaming;
|
|
80
82
|
private onMessageUpdate;
|
|
81
83
|
private onProviderTools;
|
|
@@ -634,6 +634,11 @@ export class PiEventTranslator {
|
|
|
634
634
|
onCompactionEvent(s, ev) {
|
|
635
635
|
switch (ev.type) {
|
|
636
636
|
case "compaction_start":
|
|
637
|
+
// sessionRpc retried a cancelled compaction: the UI still shows the first attempt running.
|
|
638
|
+
if (s.heldCompactionEnd !== undefined) {
|
|
639
|
+
delete s.heldCompactionEnd;
|
|
640
|
+
return;
|
|
641
|
+
}
|
|
637
642
|
// An auto-compaction can fall between steps; its summary is a new step.
|
|
638
643
|
this.completeAwaitingTools(s);
|
|
639
644
|
s.compactionID = id("compaction");
|
|
@@ -654,6 +659,11 @@ export class PiEventTranslator {
|
|
|
654
659
|
}
|
|
655
660
|
return;
|
|
656
661
|
case "compaction_end": {
|
|
662
|
+
const retry = s.compactRetry;
|
|
663
|
+
if (ev.aborted === true && retry !== undefined && retry.left > 0 && (s.abortRequests ?? 0) === retry.aborts) {
|
|
664
|
+
s.heldCompactionEnd = ev;
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
657
667
|
delete s.info.time.compacting;
|
|
658
668
|
const result = ev.result;
|
|
659
669
|
const inputID = s.compactionID ?? id("compaction");
|
|
@@ -715,6 +725,14 @@ export class PiEventTranslator {
|
|
|
715
725
|
return;
|
|
716
726
|
}
|
|
717
727
|
}
|
|
728
|
+
/** Publishes a held compaction cancellation once sessionRpc will not retry it. */
|
|
729
|
+
releaseCompactionEnd(s) {
|
|
730
|
+
const ev = s.heldCompactionEnd;
|
|
731
|
+
if (ev === undefined)
|
|
732
|
+
return;
|
|
733
|
+
delete s.heldCompactionEnd;
|
|
734
|
+
this.onCompactionEvent(s, ev);
|
|
735
|
+
}
|
|
718
736
|
finishStreaming(s) {
|
|
719
737
|
if (s.streaming)
|
|
720
738
|
s.streaming = undefined;
|
package/dist/pi-rpc.d.ts
CHANGED
|
@@ -24,6 +24,12 @@ export declare const COMPACT_RPC_TIMEOUT_MS: number;
|
|
|
24
24
|
export declare class UpstreamProtocolError extends Error {
|
|
25
25
|
constructor(message: string);
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* How far above antechamber's own `oom_score_adj` each agent is placed. The agent and everything it starts
|
|
29
|
+
* (test runners, compilers, OmO's task host) inherit it, so under memory pressure the kernel OOM killer and
|
|
30
|
+
* earlyoom take a workload before the adapter, whose death stops every session at once.
|
|
31
|
+
*/
|
|
32
|
+
export declare const AGENT_OOM_SCORE_ADJ_MARGIN = 300;
|
|
27
33
|
/** A process's group and kernel start time: with the PID, the identity that survives PID reuse checks. */
|
|
28
34
|
interface ProcessIdentity {
|
|
29
35
|
readonly pid: number;
|
package/dist/pi-rpc.js
CHANGED
|
@@ -11,7 +11,7 @@ import { spawn } from "node:child_process";
|
|
|
11
11
|
import { StringDecoder } from "node:string_decoder";
|
|
12
12
|
import { EventEmitter } from "node:events";
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
|
-
import { readdirSync, readFileSync } from "node:fs";
|
|
14
|
+
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
15
|
import { constants as osConstants } from "node:os";
|
|
16
16
|
import { systemClock } from "./lifecycle.js";
|
|
17
17
|
import { agentBinary, envSetting } from "./config.js";
|
|
@@ -31,6 +31,25 @@ export class UpstreamProtocolError extends Error {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
const PROCESS_KILL_GRACE_MS = 5_000;
|
|
34
|
+
/**
|
|
35
|
+
* How far above antechamber's own `oom_score_adj` each agent is placed. The agent and everything it starts
|
|
36
|
+
* (test runners, compilers, OmO's task host) inherit it, so under memory pressure the kernel OOM killer and
|
|
37
|
+
* earlyoom take a workload before the adapter, whose death stops every session at once.
|
|
38
|
+
*/
|
|
39
|
+
export const AGENT_OOM_SCORE_ADJ_MARGIN = 300;
|
|
40
|
+
/** Linux only: rank the agent above antechamber for the OOM killer. Raising a child's score needs no privilege. */
|
|
41
|
+
function rankAboveAdapterForOom(pid) {
|
|
42
|
+
if (process.platform !== "linux")
|
|
43
|
+
return;
|
|
44
|
+
try {
|
|
45
|
+
const target = Math.min(1000, Number(readFileSync("/proc/self/oom_score_adj", "utf8")) + AGENT_OOM_SCORE_ADJ_MARGIN);
|
|
46
|
+
if (Number(readFileSync(`/proc/${pid}/oom_score_adj`, "utf8")) < target)
|
|
47
|
+
writeFileSync(`/proc/${pid}/oom_score_adj`, String(target));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
console.error(`[pi-rpc] could not raise the agent's OOM score: ${error instanceof Error ? error.message : error}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
34
53
|
// Linux /proc. Any read failure (gone, no permission, no /proc) means the identity cannot be
|
|
35
54
|
// verified, and an unverifiable process is never signalled: fail closed.
|
|
36
55
|
const procTable = {
|
|
@@ -165,6 +184,8 @@ export class PiRpcClient extends EventEmitter {
|
|
|
165
184
|
},
|
|
166
185
|
detached: true,
|
|
167
186
|
});
|
|
187
|
+
if (this.proc.pid !== undefined)
|
|
188
|
+
rankAboveAdapterForOom(this.proc.pid);
|
|
168
189
|
this.proc.stderr.on("data", (chunk) => {
|
|
169
190
|
this.stderrTail = (this.stderrTail + chunk.toString()).slice(-8192);
|
|
170
191
|
});
|
package/dist/sessions.d.ts
CHANGED
|
@@ -51,6 +51,13 @@ export interface ManagedSession extends ThinkingState {
|
|
|
51
51
|
/** A manual compaction started while idle; its end returns the session to idle. */
|
|
52
52
|
compactingFromIdle?: boolean;
|
|
53
53
|
compactionID?: string;
|
|
54
|
+
/** Retries sessionRpc still owes a `compact` command that OmO cancels on its own. */
|
|
55
|
+
compactRetry?: {
|
|
56
|
+
left: number;
|
|
57
|
+
aborts: number;
|
|
58
|
+
};
|
|
59
|
+
/** A cancelled compaction_end held back while its retry may continue the same compaction. */
|
|
60
|
+
heldCompactionEnd?: Record<string, unknown>;
|
|
54
61
|
/** Cancels the adapter-owned user bash waiter and its OmO execution. */
|
|
55
62
|
cancelShell?: (interrupted?: boolean) => void;
|
|
56
63
|
/**
|
package/dist/sessions.js
CHANGED
|
@@ -20,6 +20,7 @@ import { MonitorSessions } from "./monitor-sessions.js";
|
|
|
20
20
|
import { WorkStatusBridge } from "./work-status.js";
|
|
21
21
|
import { OmoShells } from "./omo-shells.js";
|
|
22
22
|
import { sessionDiff } from "./session-diff.js";
|
|
23
|
+
import { waitForMcpServers } from "./mcp.js";
|
|
23
24
|
import { announceSessionNotice, noticeMessage, skillCommandText, slashTitlePrompt } from "./session-commands.js";
|
|
24
25
|
import { ocIdFor, piSessionDir, piSessionsRootMtime, scanAllPiSessions, scanPiSessions, SessionStore, readSessionFileMeta, } from "./session-store.js";
|
|
25
26
|
import { ADAPTER_VERSION } from "./version.js";
|
|
@@ -30,7 +31,11 @@ const PI_FILE_RESOLVE_RETRY_MS = 200;
|
|
|
30
31
|
const SESSION_TITLE_MAX_CHARS = 60;
|
|
31
32
|
/** OmO's reply when a compaction is aborted (`CompactionCancelledError`). */
|
|
32
33
|
const OMO_COMPACTION_CANCELLED = "Compaction cancelled";
|
|
33
|
-
|
|
34
|
+
// Real revivals took two cancels before the third attempt committed (OmO compaction.log, 2026-10-27),
|
|
35
|
+
// so two retries left no margin for one more MCP server connecting late.
|
|
36
|
+
const COMPACT_CANCEL_RETRIES = 4;
|
|
37
|
+
/** Longest a compaction waits for connecting MCP servers; a stuck server falls back to the retries. */
|
|
38
|
+
const MCP_SETTLE_WAIT_MS = 10_000;
|
|
34
39
|
/**
|
|
35
40
|
* A dormant session's record as OmO will resume it. OpenChamber 2.x restores a reopened session's effort
|
|
36
41
|
* from this record and resends it, so the model and level come together from the file; the registry only
|
|
@@ -829,20 +834,39 @@ export class SessionManager {
|
|
|
829
834
|
throw new Error(`session cannot be resumed: ${sessionId}`);
|
|
830
835
|
this.lifecycle.touch(s);
|
|
831
836
|
const timeout = command.type === "compact" ? COMPACT_RPC_TIMEOUT_MS : RPC_COMMAND_TIMEOUT_MS;
|
|
837
|
+
// OmO cancels a compaction whenever an MCP server's tools register, which a freshly revived agent
|
|
838
|
+
// does for about a second; start it once they have. The retries below remain the backstop.
|
|
839
|
+
if (command.type === "compact")
|
|
840
|
+
await waitForMcpServers(pi, MCP_SETTLE_WAIT_MS, signal);
|
|
832
841
|
signal?.throwIfAborted();
|
|
833
842
|
const abortsBefore = s.abortRequests ?? 0;
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
command
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
843
|
+
// The translator holds back a cancellation this loop may retry, so the UI shows one compaction.
|
|
844
|
+
if (command.type === "compact")
|
|
845
|
+
s.compactRetry = { left: COMPACT_CANCEL_RETRIES, aborts: abortsBefore };
|
|
846
|
+
let response;
|
|
847
|
+
try {
|
|
848
|
+
response = await pi.command(command, timeout, undefined, onDispatch);
|
|
849
|
+
// OmO cancels a running compaction whenever its active tool set changes. On a session
|
|
850
|
+
// just revived for this request, MCP servers finish connecting a moment after startup
|
|
851
|
+
// and cancel it although nobody asked. Retry unless the user sent an abort meanwhile.
|
|
852
|
+
for (let retry = 0; retry < COMPACT_CANCEL_RETRIES &&
|
|
853
|
+
command.type === "compact" &&
|
|
854
|
+
!response.success &&
|
|
855
|
+
response.error === OMO_COMPACTION_CANCELLED &&
|
|
856
|
+
(s.abortRequests ?? 0) === abortsBefore &&
|
|
857
|
+
!signal?.aborted &&
|
|
858
|
+
s.pi === pi; retry++) {
|
|
859
|
+
if (s.compactRetry)
|
|
860
|
+
s.compactRetry.left--;
|
|
861
|
+
response = await pi.command(command, timeout);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
finally {
|
|
865
|
+
if (command.type === "compact") {
|
|
866
|
+
delete s.compactRetry;
|
|
867
|
+
this.events.releaseCompactionEnd(s);
|
|
868
|
+
}
|
|
869
|
+
}
|
|
846
870
|
if (!signal?.aborted && response.success && (command.type === "compact" || command.type === "bash"))
|
|
847
871
|
await this.reloadMessagesLive(s, signal);
|
|
848
872
|
return response;
|
package/dist/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const ADAPTER_VERSION: string;
|
|
2
|
-
export declare const OPENCODE_API_VERSION = "2.
|
|
2
|
+
export declare const OPENCODE_API_VERSION = "2.999.0";
|
package/dist/version.js
CHANGED
|
@@ -11,4 +11,4 @@ export const ADAPTER_VERSION = manifest.version;
|
|
|
11
11
|
// OpenChamber parses --version as an OpenCode version and requires major 2.
|
|
12
12
|
// This API contract version must not follow the npm package version.
|
|
13
13
|
// OpenChamber 2.5.2 requires 2.1.0, which added top-level `archived` to PATCH /api/session/{id}.
|
|
14
|
-
export const OPENCODE_API_VERSION = "2.
|
|
14
|
+
export const OPENCODE_API_VERSION = "2.999.0";
|