antechamber 0.2.20 → 0.2.22
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 +19 -0
- package/README.md +12 -0
- package/dist/commands/setup.js +9 -0
- package/dist/pi-event-translation.d.ts +1 -1
- package/dist/pi-event-translation.js +31 -7
- package/dist/session-history.js +27 -22
- package/dist/sessions.js +2 -2
- package/dist/thinking-levels.d.ts +7 -1
- package/dist/thinking-levels.js +8 -0
- package/dist/tool-map.d.ts +5 -0
- package/dist/tool-map.js +41 -7
- package/dist/types.d.ts +4 -1
- package/extensions/omo-compat/README.md +41 -0
- package/extensions/omo-compat/icon.svg +1 -0
- package/extensions/omo-compat/package.json +28 -0
- package/extensions/omo-dag/package.json +1 -1
- package/package.json +4 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,18 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.22] - 2026-10-17
|
|
11
|
+
|
|
12
|
+
- fix(tools): stream live bash and eval output into OpenChamber cards (#29)
|
|
13
|
+
|
|
14
|
+
## [0.2.21] - 2026-10-17
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Keep a session's thinking level when it is reopened. User messages now carry the level as `model.variant`, both
|
|
19
|
+
live and in history rebuilt from OmO's session file. Without it OpenChamber fell back to the agent's default effort
|
|
20
|
+
and sent that with the next prompt, which reset the level and broke the prompt cache.
|
|
21
|
+
|
|
10
22
|
## [0.2.20] - 2026-10-17
|
|
11
23
|
|
|
12
24
|
### Fixed
|
|
@@ -21,6 +33,13 @@ and this project uses [Semantic Versioning](https://semver.org/).
|
|
|
21
33
|
- Report the real agent startup failure on macOS instead of `kill EPERM`. When OmO exited while a session was being
|
|
22
34
|
created, cleanup signalled its not yet reaped process group, macOS answered EPERM, and OpenChamber showed "Failed
|
|
23
35
|
to create session" with the original error hidden.
|
|
36
|
+
- Show live bash and eval output in OpenChamber 1.24, and display eval and terminal controls as shell cards with
|
|
37
|
+
their final results retained after history reload. A failed cell shows as a failed card.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- OmO Compatibility, a second bundled OpenChamber extension (1.24 or newer) with no code or permissions. It heads
|
|
42
|
+
eval cards with `JS Eval` or `Python Eval` and the cell's summary.
|
|
24
43
|
|
|
25
44
|
## [0.2.18] - 2026-10-17
|
|
26
45
|
|
package/README.md
CHANGED
|
@@ -234,6 +234,18 @@ OpenChamber clones the repository root, which is antechamber itself, not the
|
|
|
234
234
|
extension, and refuses it as an invalid manifest. Details:
|
|
235
235
|
[extensions/omo-dag/README.md](extensions/omo-dag/README.md).
|
|
236
236
|
|
|
237
|
+
## OpenChamber extension: OmO Compatibility
|
|
238
|
+
|
|
239
|
+
`extensions/omo-compat` is a second, code-free extension for OpenChamber 1.24
|
|
240
|
+
or newer. It tells OpenChamber how to display OmO tool calls that antechamber
|
|
241
|
+
maps onto OpenChamber's cards; today it heads eval cells with `JS Eval` or
|
|
242
|
+
`Python Eval` and the cell's summary. Add it the same way as OmO Graph: the
|
|
243
|
+
folder `antechamber setup` prints (`.../extensions/omo-compat`, no build
|
|
244
|
+
needed), or the release archive
|
|
245
|
+
`https://github.com/eliottness/antechamber/releases/latest/download/antechamber-omo-compat.zip`.
|
|
246
|
+
It asks for no permissions. Details:
|
|
247
|
+
[extensions/omo-compat/README.md](extensions/omo-compat/README.md).
|
|
248
|
+
|
|
237
249
|
## Configuration
|
|
238
250
|
|
|
239
251
|
Connector settings use the `ANTECHAMBER_` prefix; the deprecated `OCPI_`
|
package/dist/commands/setup.js
CHANGED
|
@@ -18,6 +18,11 @@ function omoGraphExtensionDir(ctx) {
|
|
|
18
18
|
const built = ["package.json", "panel/main.js", "service/main.js"].every((file) => ctx.fs.existsSync(join(dir, file)));
|
|
19
19
|
return built ? dir : undefined;
|
|
20
20
|
}
|
|
21
|
+
/** The bundled OmO Compatibility extension folder; it is a manifest only, so it needs no build. */
|
|
22
|
+
function omoCompatExtensionDir(ctx) {
|
|
23
|
+
const dir = resolve(dirname(ctx.cliPath), "..", "extensions", "omo-compat");
|
|
24
|
+
return ctx.fs.existsSync(join(dir, "package.json")) ? dir : undefined;
|
|
25
|
+
}
|
|
21
26
|
/** Plan all inputs before applying changes; dependencies are injected for hermetic tests. */
|
|
22
27
|
export function setup(options, ctx = createContext()) {
|
|
23
28
|
const lines = [];
|
|
@@ -174,5 +179,9 @@ export function setup(options, ctx = createContext()) {
|
|
|
174
179
|
if (extension !== undefined) {
|
|
175
180
|
report(`Optional OmO Graph extension: in OpenChamber, Settings \u2192 Extensions \u2192 Add \u2192 Folder: ${extension}`);
|
|
176
181
|
}
|
|
182
|
+
const compat = omoCompatExtensionDir(ctx);
|
|
183
|
+
if (compat !== undefined) {
|
|
184
|
+
report(`Optional OmO Compatibility extension (eval card headers): Settings \u2192 Extensions \u2192 Add \u2192 Folder: ${compat}`);
|
|
185
|
+
}
|
|
177
186
|
return lines;
|
|
178
187
|
}
|
|
@@ -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;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/** Pi message/part translation and the live event bridge, with explicit manager services. */
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { applyProviderToolResults, finishProviderToolParts, mapTool, providerToolParts, resultMetadata, resultText, } from "./tool-map.js";
|
|
3
|
+
import { applyProviderToolResults, finishProviderToolParts, mapTool, providerToolParts, resultMetadata, resultText, toolFailed, } from "./tool-map.js";
|
|
4
4
|
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: {
|
|
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
|
};
|
|
@@ -400,6 +408,22 @@ export class PiEventTranslator {
|
|
|
400
408
|
const part = typeof ev.toolCallId === "string" ? s.toolByCallId.get(ev.toolCallId) : undefined;
|
|
401
409
|
if (part === undefined || part.state.status !== "running")
|
|
402
410
|
return;
|
|
411
|
+
const partial = ev.partialResult;
|
|
412
|
+
let output = resultText(partial !== null && typeof partial === "object" && "content" in partial ? partial.content : undefined);
|
|
413
|
+
if (ev.toolName === "eval") {
|
|
414
|
+
// raw RPC capture of installed omo-ai 5.1.33 / senpi runtime, 2026-10-17:
|
|
415
|
+
// eval prefixes cumulative stdout with status lines, unlike its final result.
|
|
416
|
+
const header = /^\d+\/(\d+) cells? (?:running|complete)\b[^\n]*(?:\n|$)/.exec(output);
|
|
417
|
+
if (header !== null) {
|
|
418
|
+
output = output.slice(header[0].length);
|
|
419
|
+
if (header[1] === "1")
|
|
420
|
+
output = output.replace(/^\[1\] \S+ [^\n]* (?:running|complete)(?:\r?\n|$)/, "");
|
|
421
|
+
}
|
|
422
|
+
if (/^waiting for the \w+ kernel to be ready$/.test(output.trim()))
|
|
423
|
+
output = "";
|
|
424
|
+
}
|
|
425
|
+
if (output.trim() !== "")
|
|
426
|
+
part.state.metadata = { ...part.state.metadata, output };
|
|
403
427
|
this.emitPart(s, part);
|
|
404
428
|
return;
|
|
405
429
|
}
|
|
@@ -554,7 +578,7 @@ export class PiEventTranslator {
|
|
|
554
578
|
key = entry.id;
|
|
555
579
|
}
|
|
556
580
|
}
|
|
557
|
-
for (const message of compactionMessages(s, key ?? id("compaction"), result.summary, Date.now(), s.model)) {
|
|
581
|
+
for (const message of compactionMessages(s, key ?? id("compaction"), result.summary, Date.now(), s.model, s.thinkingLevel)) {
|
|
558
582
|
if (messages.some((item) => item.info.id === message.info.id))
|
|
559
583
|
continue;
|
|
560
584
|
// Keep a hidden redo branch after the new turn, as appendMessage does.
|
|
@@ -766,7 +790,7 @@ export class PiEventTranslator {
|
|
|
766
790
|
if (!part || callID === undefined)
|
|
767
791
|
continue;
|
|
768
792
|
s.toolByCallId.delete(callID);
|
|
769
|
-
s.todoBridge?.finish(part, tr
|
|
793
|
+
s.todoBridge?.finish(part, toolFailed(tr));
|
|
770
794
|
const text = resultText(tr.content);
|
|
771
795
|
const startedAt = part.state.status !== "pending" ? part.state.time.start : Date.now();
|
|
772
796
|
const input = part.state.input ?? {};
|
|
@@ -777,7 +801,7 @@ export class PiEventTranslator {
|
|
|
777
801
|
const title = part.state.status === "running" || part.state.status === "completed"
|
|
778
802
|
? (part.state.title ?? part.tool)
|
|
779
803
|
: part.tool;
|
|
780
|
-
if (tr
|
|
804
|
+
if (toolFailed(tr)) {
|
|
781
805
|
part.state = {
|
|
782
806
|
status: "error",
|
|
783
807
|
input,
|
package/dist/session-history.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/** Reconstruct OpenCode history from pi JSONL session files. */
|
|
2
2
|
import { readFileSync } from "node:fs";
|
|
3
|
-
import { applyProviderToolResults, finishProviderToolParts, mapTool, providerToolParts, resultMetadata, resultText, } from "./tool-map.js";
|
|
3
|
+
import { applyProviderToolResults, finishProviderToolParts, mapTool, providerToolParts, resultMetadata, resultText, toolFailed, } 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
|
{
|
|
@@ -390,23 +396,22 @@ export function parsePiSessionText(s, agentName, raw) {
|
|
|
390
396
|
const title = part.state.status === "running" || part.state.status === "completed"
|
|
391
397
|
? (part.state.title ?? part.tool)
|
|
392
398
|
: part.tool;
|
|
393
|
-
part.state =
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
:
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
};
|
|
399
|
+
part.state = toolFailed(m)
|
|
400
|
+
? {
|
|
401
|
+
status: "error",
|
|
402
|
+
input,
|
|
403
|
+
error: text !== "" ? text : `tool ${part.tool} failed`,
|
|
404
|
+
metadata,
|
|
405
|
+
time: { start: startedAt, end: created },
|
|
406
|
+
}
|
|
407
|
+
: {
|
|
408
|
+
status: "completed",
|
|
409
|
+
input,
|
|
410
|
+
output: text,
|
|
411
|
+
title,
|
|
412
|
+
metadata,
|
|
413
|
+
time: { start: startedAt, end: created },
|
|
414
|
+
};
|
|
410
415
|
}
|
|
411
416
|
// system / custom entries are not rendered
|
|
412
417
|
}
|
|
@@ -457,7 +462,7 @@ export function parsePiSessionText(s, agentName, raw) {
|
|
|
457
462
|
role: "user",
|
|
458
463
|
time: { created: entryTime(entry) },
|
|
459
464
|
agent: agentName,
|
|
460
|
-
model: currentModel,
|
|
465
|
+
model: userMessageModel(currentModel, currentThinkingLevel),
|
|
461
466
|
},
|
|
462
467
|
parts: [{ id: `prt_${messageID}_pending`, sessionID, messageID, type: "text", text }],
|
|
463
468
|
});
|
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
|
};
|
|
@@ -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
|
}
|
package/dist/thinking-levels.js
CHANGED
|
@@ -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/tool-map.d.ts
CHANGED
|
@@ -15,6 +15,11 @@ export interface ToolResult {
|
|
|
15
15
|
type ToolMapper = (args: Record<string, unknown>, result?: ToolResult) => MappedTool;
|
|
16
16
|
/** Mutable registry: todos 6/7 can assign handlers for `todo` and `task`. */
|
|
17
17
|
export declare const toolMappers: Record<string, ToolMapper>;
|
|
18
|
+
/**
|
|
19
|
+
* A failed result. Codemode leaves a failed eval cell's top-level isError false and sets
|
|
20
|
+
* details.isError instead (senpi-codemode src/tool/render-result.ts:42).
|
|
21
|
+
*/
|
|
22
|
+
export declare function toolFailed(result: ToolResult): boolean;
|
|
18
23
|
/** Text blocks only; images and structured details are not tool output text. */
|
|
19
24
|
export declare function resultText(content: unknown): string;
|
|
20
25
|
/** Only edit cards need patch metadata; write cards already preview input.content. */
|
package/dist/tool-map.js
CHANGED
|
@@ -5,6 +5,25 @@ function generic(tool, input) {
|
|
|
5
5
|
const hint = [input.summary, input.description, input.query, input.url, input.path, input.command].find((value) => typeof value === "string" && value !== "");
|
|
6
6
|
return { tool, input, title: (hint ?? tool).slice(0, 60) };
|
|
7
7
|
}
|
|
8
|
+
function terminalTool(name, args) {
|
|
9
|
+
const target = typeof args.bash_id === "string" ? ` ${args.bash_id}` : args.all === true ? " all" : "";
|
|
10
|
+
let command = `${name}${target}`;
|
|
11
|
+
if (name === "bash_input") {
|
|
12
|
+
if (typeof args.input === "string")
|
|
13
|
+
command += ` ${JSON.stringify(args.input)}`;
|
|
14
|
+
if (Array.isArray(args.keys)) {
|
|
15
|
+
const keys = args.keys.filter((key) => typeof key === "string").join(" ");
|
|
16
|
+
if (keys !== "")
|
|
17
|
+
command += ` ${keys}`;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (name === "bash_resize" && typeof args.cols === "number" && typeof args.rows === "number") {
|
|
21
|
+
command += ` ${args.cols}x${args.rows}`;
|
|
22
|
+
}
|
|
23
|
+
return { tool: "bash", input: { command, description: command }, title: command.slice(0, 60) };
|
|
24
|
+
}
|
|
25
|
+
/** Display names for codemode's kernel ids. */
|
|
26
|
+
const EVAL_LANGUAGES = { js: "JS", py: "Python" };
|
|
8
27
|
/** Mutable registry: todos 6/7 can assign handlers for `todo` and `task`. */
|
|
9
28
|
export const toolMappers = {
|
|
10
29
|
read: (args) => typeof args.path === "string"
|
|
@@ -40,13 +59,21 @@ export const toolMappers = {
|
|
|
40
59
|
title: args.command.slice(0, 60),
|
|
41
60
|
}
|
|
42
61
|
: generic("bash", args),
|
|
43
|
-
eval: (args) =>
|
|
44
|
-
?
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
eval: (args) => {
|
|
63
|
+
const language = typeof args.language === "string" && args.language !== "" ? args.language : "js";
|
|
64
|
+
const summary = typeof args.summary === "string" && args.summary !== "" ? args.summary : "eval";
|
|
65
|
+
const name = `${EVAL_LANGUAGES[language] ?? language.toUpperCase()} Eval`;
|
|
66
|
+
const command = typeof args.code === "string" && args.code !== ""
|
|
67
|
+
? args.code
|
|
68
|
+
: ["eval", args.action, args.cell_id].filter((value) => typeof value === "string").join(" ");
|
|
69
|
+
// Read by the OmO Compatibility extension's `bash` header rule; plain shell cards lack them and keep the default header.
|
|
70
|
+
const metadata = { evalTitle: name, evalSubtitle: summary };
|
|
71
|
+
return { tool: "bash", input: { command, description: `${name}: ${summary}` }, title: summary, metadata };
|
|
72
|
+
},
|
|
73
|
+
bash_output: (args) => terminalTool("bash_output", args),
|
|
74
|
+
bash_input: (args) => terminalTool("bash_input", args),
|
|
75
|
+
kill_bash: (args) => terminalTool("kill_bash", args),
|
|
76
|
+
bash_resize: (args) => terminalTool("bash_resize", args),
|
|
50
77
|
ask_user_question: (args) => {
|
|
51
78
|
if (!Array.isArray(args.questions) || !args.questions.every(record))
|
|
52
79
|
return generic("ask_user_question", args);
|
|
@@ -67,6 +94,13 @@ export const toolMappers = {
|
|
|
67
94
|
return { tool: "task", input, title: typeof input.description === "string" ? input.description : "task" };
|
|
68
95
|
},
|
|
69
96
|
};
|
|
97
|
+
/**
|
|
98
|
+
* A failed result. Codemode leaves a failed eval cell's top-level isError false and sets
|
|
99
|
+
* details.isError instead (senpi-codemode src/tool/render-result.ts:42).
|
|
100
|
+
*/
|
|
101
|
+
export function toolFailed(result) {
|
|
102
|
+
return result.isError === true || (record(result.details) && result.details.isError === true);
|
|
103
|
+
}
|
|
70
104
|
/** Text blocks only; images and structured details are not tool output text. */
|
|
71
105
|
export function resultText(content) {
|
|
72
106
|
if (!Array.isArray(content))
|
package/dist/types.d.ts
CHANGED
|
@@ -61,7 +61,10 @@ export interface OCUserMessage {
|
|
|
61
61
|
created: number;
|
|
62
62
|
};
|
|
63
63
|
agent: string;
|
|
64
|
-
|
|
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). */
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# OmO Compatibility
|
|
2
|
+
|
|
3
|
+
An OpenChamber extension that ships with antechamber. It changes how OpenChamber
|
|
4
|
+
displays some OmO tool calls, using only the display rules OpenChamber lets an
|
|
5
|
+
extension declare. It has no panel, page, service or permissions.
|
|
6
|
+
|
|
7
|
+
What it does today:
|
|
8
|
+
|
|
9
|
+
- **Eval cell headers.** OpenChamber 1.24 streams live output only for shell
|
|
10
|
+
cards, so antechamber shows OmO eval cells as shell cards. With this extension
|
|
11
|
+
their header reads `JS Eval` (or `Python Eval`) and the cell's summary instead
|
|
12
|
+
of "Shell Command" and the first line of code. Other shell cards keep their
|
|
13
|
+
normal header.
|
|
14
|
+
|
|
15
|
+
## Requirements
|
|
16
|
+
|
|
17
|
+
- OpenChamber **1.24 or newer** (the first release with `contributes.tools`).
|
|
18
|
+
- antechamber as OpenChamber's backend. Without it the rules match nothing and
|
|
19
|
+
change nothing.
|
|
20
|
+
|
|
21
|
+
## Install
|
|
22
|
+
|
|
23
|
+
In OpenChamber, open **Settings → Extensions → Add**:
|
|
24
|
+
|
|
25
|
+
- **Folder**, when OpenChamber runs on the machine where antechamber is
|
|
26
|
+
installed. `antechamber setup` prints the path:
|
|
27
|
+
`$(npm root -g)/antechamber/extensions/omo-compat` for an npm install, or
|
|
28
|
+
`<checkout>/extensions/omo-compat` from a source checkout. It needs no build.
|
|
29
|
+
- **URL**, from anywhere:
|
|
30
|
+
`https://github.com/eliottness/antechamber/releases/latest/download/antechamber-omo-compat.zip`.
|
|
31
|
+
Every GitHub release attaches this archive.
|
|
32
|
+
|
|
33
|
+
There is nothing to approve and no rail icon. Reload open chats after
|
|
34
|
+
installing so their cards pick up the new header.
|
|
35
|
+
|
|
36
|
+
For a server you reach over HTTP:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
curl -sX POST "$OC/api/guests" -H 'content-type: application/json' \
|
|
40
|
+
-d '{"path":"/abs/path/to/extensions/omo-compat"}'
|
|
41
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><title>OmO Compatibility</title><path d="M8 7 3 12l5 5"/><path d="m16 7 5 5-5 5"/><path d="M14 4l-4 16"/></svg>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "antechamber-omo-compat",
|
|
3
|
+
"version": "0.2.22",
|
|
4
|
+
"description": "OpenChamber extension: displays OmO tool calls the way OmO means them when OpenChamber runs on antechamber",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"openchamber": {
|
|
9
|
+
"apiVersion": 1,
|
|
10
|
+
"engines": {
|
|
11
|
+
"openchamber": ">=1.24.0"
|
|
12
|
+
},
|
|
13
|
+
"contributes": {
|
|
14
|
+
"panel": {
|
|
15
|
+
"id": "omo-compat",
|
|
16
|
+
"name": "OmO Compatibility",
|
|
17
|
+
"icon": "icon.svg"
|
|
18
|
+
},
|
|
19
|
+
"tools": [
|
|
20
|
+
{
|
|
21
|
+
"match": "bash",
|
|
22
|
+
"title": "{metadata.evalTitle}",
|
|
23
|
+
"subtitle": "{metadata.evalSubtitle}"
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "antechamber",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.22",
|
|
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",
|
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
"extensions/omo-dag/service/main.js",
|
|
36
36
|
"extensions/omo-dag/README.md",
|
|
37
37
|
"extensions/omo-dag/docs/DESIGN.md",
|
|
38
|
+
"extensions/omo-compat/package.json",
|
|
39
|
+
"extensions/omo-compat/icon.svg",
|
|
40
|
+
"extensions/omo-compat/README.md",
|
|
38
41
|
"README.md",
|
|
39
42
|
"LICENSE",
|
|
40
43
|
"CHANGELOG.md"
|