antechamber 1.0.40 → 2.0.1
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 +126 -0
- package/README.md +88 -141
- package/bin/opencode-antechamber +14 -4
- package/dist/approvals.d.ts +9 -1
- package/dist/approvals.js +10 -2
- package/dist/catalog.d.ts +13 -2
- package/dist/catalog.js +88 -52
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +1 -1
- package/dist/commands/context.d.ts +5 -2
- package/dist/commands/context.js +8 -4
- package/dist/commands/doctor.js +4 -3
- package/dist/commands/setup.js +9 -28
- package/dist/config.d.ts +6 -2
- package/dist/config.js +18 -4
- package/dist/file-watch.d.ts +35 -0
- package/dist/file-watch.js +163 -0
- package/dist/latency.d.ts +10 -0
- package/dist/latency.js +115 -0
- package/dist/misc-routes.js +14 -12
- package/dist/pi-event-translation.js +44 -2
- package/dist/route-services.d.ts +8 -3
- package/dist/routes-v1.d.ts +1 -1
- package/dist/routes-v1.js +29 -418
- package/dist/routes-v2.js +252 -34
- package/dist/self-update.d.ts +2 -1
- package/dist/self-update.js +5 -1
- package/dist/server.d.ts +17 -3
- package/dist/server.js +191 -18
- package/dist/session-commands.d.ts +3 -1
- package/dist/session-commands.js +13 -7
- package/dist/session-history.js +3 -0
- package/dist/session-store.d.ts +19 -10
- package/dist/session-store.js +266 -128
- package/dist/session-tree.d.ts +15 -1
- package/dist/session-tree.js +2 -2
- package/dist/sessions.d.ts +85 -1
- package/dist/sessions.js +238 -10
- package/dist/sse.d.ts +0 -2
- package/dist/sse.js +13 -46
- package/dist/text-generation.d.ts +20 -0
- package/dist/text-generation.js +93 -0
- package/dist/tool-map.js +1 -3
- package/dist/types.d.ts +60 -0
- package/dist/v2.d.ts +8 -1
- package/dist/v2.js +24 -4
- package/dist/work-status.js +1 -1
- package/extensions/antechamber/README.md +100 -0
- package/extensions/antechamber/bin/opencode-antechamber +42 -0
- package/extensions/{omo-dag/docs/DESIGN.md → antechamber/docs/GRAPH.md} +5 -5
- package/extensions/{omo-work/docs/DESIGN.md → antechamber/docs/WORK.md} +3 -3
- package/extensions/antechamber/package.json +60 -0
- package/extensions/{omo-dag → antechamber}/panel/index.html +8 -1
- package/extensions/antechamber/panel/main.js +261 -0
- package/extensions/antechamber/runtime/launcher.js +201 -0
- package/extensions/antechamber/service/main.js +884 -0
- package/package.json +75 -26
- package/extensions/omo-compat/README.md +0 -48
- package/extensions/omo-compat/icon.svg +0 -1
- package/extensions/omo-compat/package.json +0 -43
- package/extensions/omo-dag/README.md +0 -167
- package/extensions/omo-dag/package.json +0 -33
- package/extensions/omo-dag/panel/main.js +0 -176
- package/extensions/omo-dag/service/main.js +0 -457
- package/extensions/omo-work/README.md +0 -88
- package/extensions/omo-work/package.json +0 -30
- package/extensions/omo-work/service/main.js +0 -149
- /package/extensions/{omo-dag → antechamber}/icon.svg +0 -0
- /package/extensions/{omo-work → antechamber}/status/index.html +0 -0
- /package/extensions/{omo-work → antechamber}/status/main.js +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { PiRpcClient } from "./pi-rpc.js";
|
|
2
|
+
/**
|
|
3
|
+
* A hang guard, not a budget: OpenChamber sizes its own small-model deadline by prompt length up to 900 s
|
|
4
|
+
* (server/lib/small-model/index.js generationBudgetMs) and its disconnect cancels the run here, and OpenCode's
|
|
5
|
+
* generate.text has no server deadline. This only has to outlast the longest budget a caller may give.
|
|
6
|
+
*/
|
|
7
|
+
export const GENERATION_DEADLINE_MS = 15 * 60 * 1000;
|
|
8
|
+
/** Own the auxiliary agents separately from user sessions, with no admission queue. */
|
|
9
|
+
export class TextGeneration {
|
|
10
|
+
active = new Map();
|
|
11
|
+
stopped = false;
|
|
12
|
+
async generate(directory, prompt, model, signal) {
|
|
13
|
+
if (this.stopped)
|
|
14
|
+
throw new Error("Text generation is stopping");
|
|
15
|
+
if (this.active.size >= 2)
|
|
16
|
+
throw new Error("Text generation is at capacity");
|
|
17
|
+
const cancellation = new AbortController();
|
|
18
|
+
const result = generateText(directory, prompt, model, signal ? AbortSignal.any([signal, cancellation.signal]) : cancellation.signal);
|
|
19
|
+
this.active.set(cancellation, result);
|
|
20
|
+
try {
|
|
21
|
+
return await result;
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
this.active.delete(cancellation);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
async dispose() {
|
|
28
|
+
this.stopped = true;
|
|
29
|
+
for (const cancellation of this.active.keys())
|
|
30
|
+
cancellation.abort(new Error("Text generation is stopping"));
|
|
31
|
+
await Promise.allSettled(this.active.values());
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/** Run a model turn in an agent with no persistent session, never in a user's chat. */
|
|
35
|
+
export async function generateText(directory, prompt, model, caller) {
|
|
36
|
+
caller?.throwIfAborted();
|
|
37
|
+
// Startup overrides initialize agent state without persisting the default, unlike set_model.
|
|
38
|
+
// senpi dist/cli/args.js:93-104 -> main.js:561-576,843 -> core/sdk.js:125,292-298.
|
|
39
|
+
const pi = new PiRpcClient({
|
|
40
|
+
cwd: directory,
|
|
41
|
+
args: ["--no-session", "--no-tools", ...(model ? ["--provider", model.providerID, "--model", model.id] : [])],
|
|
42
|
+
});
|
|
43
|
+
const cancellation = new AbortController();
|
|
44
|
+
const signal = caller ? AbortSignal.any([caller, cancellation.signal]) : cancellation.signal;
|
|
45
|
+
// This budget covers startup/model resolution, admission, the turn, and the result read.
|
|
46
|
+
const timer = setTimeout(() => cancellation.abort(new Error("Text generation timed out")), GENERATION_DEADLINE_MS);
|
|
47
|
+
try {
|
|
48
|
+
const finished = new Promise((resolve, reject) => {
|
|
49
|
+
const cleanup = () => {
|
|
50
|
+
pi.off("event", onEvent);
|
|
51
|
+
signal.removeEventListener("abort", onAbort);
|
|
52
|
+
};
|
|
53
|
+
const onAbort = () => {
|
|
54
|
+
cleanup();
|
|
55
|
+
reject(signal.reason);
|
|
56
|
+
};
|
|
57
|
+
const onEvent = (event) => {
|
|
58
|
+
if (event.type !== "agent_end")
|
|
59
|
+
return;
|
|
60
|
+
cleanup();
|
|
61
|
+
resolve();
|
|
62
|
+
};
|
|
63
|
+
pi.onEvent(onEvent);
|
|
64
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
65
|
+
});
|
|
66
|
+
// A prompt RPC may acknowledge admission before the agent finishes; agent_end
|
|
67
|
+
// is the completion boundary before reading the resulting entries.
|
|
68
|
+
const admitted = pi.command({ type: "prompt", message: prompt }, undefined, signal).then((response) => {
|
|
69
|
+
if (!response.success)
|
|
70
|
+
throw new Error(response.error ?? "Text generation failed");
|
|
71
|
+
});
|
|
72
|
+
await Promise.all([admitted, finished]);
|
|
73
|
+
const history = await pi.command({ type: "get_entries" }, undefined, signal);
|
|
74
|
+
if (!history.success || !Array.isArray(history.data?.entries))
|
|
75
|
+
throw new Error("Text generation produced no readable history");
|
|
76
|
+
const assistant = history.data.entries.findLast((entry) => entry.type === "message" && entry.message?.role === "assistant");
|
|
77
|
+
const content = assistant?.message?.content;
|
|
78
|
+
const text = typeof content === "string"
|
|
79
|
+
? content
|
|
80
|
+
: content
|
|
81
|
+
?.filter((part) => part.type === "text")
|
|
82
|
+
.map((part) => part.text ?? "")
|
|
83
|
+
.join("");
|
|
84
|
+
if (!text)
|
|
85
|
+
throw new Error("Text generation produced no text");
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
cancellation.abort();
|
|
91
|
+
await pi.dispose();
|
|
92
|
+
}
|
|
93
|
+
}
|
package/dist/tool-map.js
CHANGED
|
@@ -257,9 +257,7 @@ export function nestedToolParts(parent, details, sessions = {}) {
|
|
|
257
257
|
messageID: parent.messageID,
|
|
258
258
|
type: "tool",
|
|
259
259
|
callID: `nested:${callID}`,
|
|
260
|
-
|
|
261
|
-
// and a failed spawn has no session to open, so it goes out as a plain card that does both.
|
|
262
|
-
tool: failed && mapped.tool === "task" ? "agent" : mapped.tool,
|
|
260
|
+
tool: mapped.tool,
|
|
263
261
|
state: !failed
|
|
264
262
|
? { ...state, status: "completed" }
|
|
265
263
|
: {
|
package/dist/types.d.ts
CHANGED
|
@@ -48,6 +48,7 @@ export interface OCSession {
|
|
|
48
48
|
created: number;
|
|
49
49
|
updated: number;
|
|
50
50
|
idle?: number;
|
|
51
|
+
viewed?: number;
|
|
51
52
|
compacting?: number;
|
|
52
53
|
/** Epoch ms — presence marks the session archived (SDK v2 session.update). */
|
|
53
54
|
archived?: number;
|
|
@@ -67,6 +68,8 @@ export interface OCUserMessage {
|
|
|
67
68
|
};
|
|
68
69
|
/** No-reply context admitted without a turn (v2 lists it as `type: "synthetic"`). */
|
|
69
70
|
synthetic?: boolean;
|
|
71
|
+
/** Human-readable label for a v2 synthetic notice. */
|
|
72
|
+
description?: string;
|
|
70
73
|
/** Client metadata sent with the prompt (OpenChamber 2.x). */
|
|
71
74
|
metadata?: Record<string, unknown>;
|
|
72
75
|
}
|
|
@@ -291,6 +294,33 @@ export type OCEvent = {
|
|
|
291
294
|
properties: {
|
|
292
295
|
server: string;
|
|
293
296
|
};
|
|
297
|
+
} | {
|
|
298
|
+
type: "filesystem.changed";
|
|
299
|
+
properties: {
|
|
300
|
+
file: string;
|
|
301
|
+
event: "change" | "unlink";
|
|
302
|
+
};
|
|
303
|
+
} | {
|
|
304
|
+
type: "project.updated";
|
|
305
|
+
properties: {
|
|
306
|
+
id: string;
|
|
307
|
+
canonical: string;
|
|
308
|
+
time: {
|
|
309
|
+
created: number;
|
|
310
|
+
updated: number;
|
|
311
|
+
active: number;
|
|
312
|
+
};
|
|
313
|
+
sandboxes: string[];
|
|
314
|
+
vcs: string;
|
|
315
|
+
};
|
|
316
|
+
} | {
|
|
317
|
+
type: "provider.updated" | "model.updated" | "command.updated";
|
|
318
|
+
properties: Record<string, never>;
|
|
319
|
+
} | {
|
|
320
|
+
type: "vcs.branch.updated";
|
|
321
|
+
properties: {
|
|
322
|
+
branch?: string;
|
|
323
|
+
};
|
|
294
324
|
} | {
|
|
295
325
|
type: "session.created";
|
|
296
326
|
properties: {
|
|
@@ -301,6 +331,30 @@ export type OCEvent = {
|
|
|
301
331
|
properties: {
|
|
302
332
|
info: OCSession;
|
|
303
333
|
};
|
|
334
|
+
} | {
|
|
335
|
+
type: "session.renamed";
|
|
336
|
+
properties: {
|
|
337
|
+
sessionID: string;
|
|
338
|
+
title: string;
|
|
339
|
+
};
|
|
340
|
+
} | {
|
|
341
|
+
type: "session.agent.selected";
|
|
342
|
+
properties: {
|
|
343
|
+
sessionID: string;
|
|
344
|
+
agent: string;
|
|
345
|
+
previous?: string;
|
|
346
|
+
};
|
|
347
|
+
} | {
|
|
348
|
+
type: "session.archived" | "session.unarchived";
|
|
349
|
+
properties: {
|
|
350
|
+
sessionID: string;
|
|
351
|
+
};
|
|
352
|
+
} | {
|
|
353
|
+
type: "session.viewed";
|
|
354
|
+
properties: {
|
|
355
|
+
sessionID: string;
|
|
356
|
+
idle: number;
|
|
357
|
+
};
|
|
304
358
|
} | {
|
|
305
359
|
type: "session.revert.staged";
|
|
306
360
|
properties: {
|
|
@@ -327,6 +381,12 @@ export type OCEvent = {
|
|
|
327
381
|
reason: string;
|
|
328
382
|
inputID: string;
|
|
329
383
|
};
|
|
384
|
+
} | {
|
|
385
|
+
type: "session.compaction.delta";
|
|
386
|
+
properties: {
|
|
387
|
+
sessionID: string;
|
|
388
|
+
text: string;
|
|
389
|
+
};
|
|
330
390
|
} | {
|
|
331
391
|
type: "session.compaction.ended";
|
|
332
392
|
properties: {
|
package/dist/v2.d.ts
CHANGED
|
@@ -123,6 +123,8 @@ export interface V2Session {
|
|
|
123
123
|
time: {
|
|
124
124
|
created: number;
|
|
125
125
|
updated: number;
|
|
126
|
+
idle?: number;
|
|
127
|
+
viewed?: number;
|
|
126
128
|
archived?: number;
|
|
127
129
|
};
|
|
128
130
|
title: string;
|
|
@@ -148,7 +150,12 @@ export interface V2Message {
|
|
|
148
150
|
completed?: number;
|
|
149
151
|
streamed?: number;
|
|
150
152
|
};
|
|
151
|
-
type: "user" | "assistant" | "synthetic";
|
|
153
|
+
type: "user" | "assistant" | "synthetic" | "idle" | "compaction";
|
|
154
|
+
status?: "running" | "completed" | "failed";
|
|
155
|
+
reason?: string;
|
|
156
|
+
summary?: string;
|
|
157
|
+
outcome?: "succeeded" | "failed" | "interrupted";
|
|
158
|
+
description?: string;
|
|
152
159
|
text?: string;
|
|
153
160
|
files?: Array<Record<string, unknown>>;
|
|
154
161
|
agents?: Array<Record<string, unknown>>;
|
package/dist/v2.js
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
*/
|
|
24
24
|
import { randomUUID } from "node:crypto";
|
|
25
25
|
import { getAgentName } from "./agent-name.js";
|
|
26
|
+
import { toV2Permission } from "./approvals.js";
|
|
26
27
|
import { questionFormID, toQuestionForm } from "./question-forms.js";
|
|
27
28
|
import { v2PromptAttachments } from "./prompt-parts.js";
|
|
28
29
|
function genId(prefix) {
|
|
@@ -97,6 +98,7 @@ export function toV2Session(info, opts = {}) {
|
|
|
97
98
|
created: info.time.created,
|
|
98
99
|
updated: info.time.updated,
|
|
99
100
|
...(info.time.idle !== undefined ? { idle: info.time.idle } : {}),
|
|
101
|
+
...(info.time.viewed !== undefined ? { viewed: info.time.viewed } : {}),
|
|
100
102
|
...(info.time.archived !== undefined ? { archived: info.time.archived } : {}),
|
|
101
103
|
},
|
|
102
104
|
title: info.title,
|
|
@@ -127,7 +129,14 @@ function toV2UserMessage(info) {
|
|
|
127
129
|
const metadata = user.metadata !== undefined ? { metadata: user.metadata } : {};
|
|
128
130
|
// Admitted no-reply context lists as OpenCode's synthetic item, not as a user turn.
|
|
129
131
|
if (user.synthetic)
|
|
130
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
id: user.id,
|
|
134
|
+
time: { created: user.time.created },
|
|
135
|
+
type: "synthetic",
|
|
136
|
+
text,
|
|
137
|
+
...(user.description !== undefined ? { description: user.description } : {}),
|
|
138
|
+
...metadata,
|
|
139
|
+
};
|
|
131
140
|
const inputs = info.parts.flatMap((part) => (part.type === "file" || part.type === "agent" ? [part] : []));
|
|
132
141
|
return {
|
|
133
142
|
id: user.id,
|
|
@@ -290,16 +299,28 @@ export class V2EventTranslator {
|
|
|
290
299
|
switch (ev.type) {
|
|
291
300
|
case "session.inbox.enqueued":
|
|
292
301
|
case "session.inbox.delivered":
|
|
302
|
+
case "session.renamed":
|
|
303
|
+
case "session.agent.selected":
|
|
304
|
+
case "session.archived":
|
|
305
|
+
case "session.unarchived":
|
|
306
|
+
case "session.viewed":
|
|
293
307
|
case "session.revert.staged":
|
|
294
308
|
case "session.revert.cleared":
|
|
295
309
|
case "session.revert.committed":
|
|
296
310
|
case "session.compaction.started":
|
|
311
|
+
case "session.compaction.delta":
|
|
297
312
|
case "session.compaction.ended":
|
|
298
313
|
case "session.compaction.failed":
|
|
299
314
|
case "session.shell.started":
|
|
300
315
|
case "session.shell.ended":
|
|
301
316
|
case "shell.created":
|
|
302
317
|
case "shell.exited":
|
|
318
|
+
case "filesystem.changed":
|
|
319
|
+
case "project.updated":
|
|
320
|
+
case "provider.updated":
|
|
321
|
+
case "model.updated":
|
|
322
|
+
case "command.updated":
|
|
323
|
+
case "vcs.branch.updated":
|
|
303
324
|
return [this.frame(directory, ev.type, ev.properties)];
|
|
304
325
|
case "message.removed":
|
|
305
326
|
return [
|
|
@@ -318,6 +339,7 @@ export class V2EventTranslator {
|
|
|
318
339
|
subpath: subpathOf(info.directory),
|
|
319
340
|
title: info.title,
|
|
320
341
|
agent: info.agent ?? getAgentName(),
|
|
342
|
+
...(info.model !== undefined ? { model: info.model } : {}),
|
|
321
343
|
...(info.parentID !== undefined ? { parentID: info.parentID } : {}),
|
|
322
344
|
...(info.parentMessageID !== undefined ? { parentMessageID: info.parentMessageID } : {}),
|
|
323
345
|
...(info.metadata !== undefined ? { metadata: info.metadata } : {}),
|
|
@@ -327,11 +349,9 @@ export class V2EventTranslator {
|
|
|
327
349
|
case "session.updated": {
|
|
328
350
|
const info = ev.properties.info;
|
|
329
351
|
return [
|
|
330
|
-
this.frame(directory, "session.renamed", { sessionID: info.id, title: info.title }),
|
|
331
352
|
...(info.metadata !== undefined
|
|
332
353
|
? [this.frame(directory, "session.metadata.updated", { sessionID: info.id, metadata: info.metadata })]
|
|
333
354
|
: []),
|
|
334
|
-
this.frame(directory, "session.agent.selected", { sessionID: info.id, agent: info.agent ?? getAgentName() }),
|
|
335
355
|
...(info.cost !== undefined
|
|
336
356
|
? [
|
|
337
357
|
this.frame(directory, "session.usage.updated", {
|
|
@@ -427,7 +447,7 @@ export class V2EventTranslator {
|
|
|
427
447
|
}
|
|
428
448
|
case "permission.asked": {
|
|
429
449
|
const p = ev.properties;
|
|
430
|
-
return [this.frame(directory, "permission.asked", p)];
|
|
450
|
+
return [this.frame(directory, "permission.asked", toV2Permission(p))];
|
|
431
451
|
}
|
|
432
452
|
case "permission.replied":
|
|
433
453
|
return [this.frame(directory, "permission.replied", ev.properties)];
|
package/dist/work-status.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Background work of one session for the OmO Work status section (extensions/
|
|
2
|
+
* Background work of one session for the OmO Work status section (extensions/antechamber):
|
|
3
3
|
* todo phases, terminal monitors, detached eval cells, background terminal sessions and the
|
|
4
4
|
* goal continuation timer. OpenChamber 2.x has no native surface for any of these.
|
|
5
5
|
*
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# antechamber: OpenChamber extension
|
|
2
|
+
|
|
3
|
+
One OpenChamber 2.x extension that installs [antechamber](../../README.md) and OmO, keeps them at the versions this
|
|
4
|
+
release pins, and adds the OmO views to OpenChamber:
|
|
5
|
+
|
|
6
|
+
- **OmO Graph**, a rail panel (and a full-screen page) drawing the OmO sub-agents behind the open session as a live
|
|
7
|
+
graph. See [docs/GRAPH.md](docs/GRAPH.md).
|
|
8
|
+
- **OmO Work**, a section of the Work Status panel with the session's todo phases, goal timer and other wake
|
|
9
|
+
sources. See [docs/WORK.md](docs/WORK.md).
|
|
10
|
+
- **OmO tool headers**: eval cells read `JS Eval` (or `Python Eval`) and their summary instead of "Shell Command",
|
|
11
|
+
and a sub-agent card reads its agent type and description instead of "Agent Task".
|
|
12
|
+
- **The runtime**: `bin/opencode-antechamber`, the program OpenChamber runs as its OpenCode server. Its first start
|
|
13
|
+
installs, with Bun, the antechamber release whose version is this extension's version, and the `omo-ai` that
|
|
14
|
+
release pins, into `~/.antechamber/runtime/<version>`.
|
|
15
|
+
|
|
16
|
+
It replaces the separate OmO Graph, OmO Work and OmO Compatibility extensions of antechamber 1.x.
|
|
17
|
+
|
|
18
|
+
## Requirements
|
|
19
|
+
|
|
20
|
+
- OpenChamber **2.3.2 or newer**, web or desktop. VS Code and mobile load no extensions.
|
|
21
|
+
- [Bun](https://bun.sh) 1.4 or newer on the machine OpenChamber runs on, on `PATH` or in `~/.bun/bin`.
|
|
22
|
+
- Network access to the npm registry the first time each version starts.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
1. In OpenChamber, open **Settings → Extensions → Add → URL** and enter `https://github.com/eliottness/antechamber`.
|
|
27
|
+
The repository root carries this extension's manifest (paths rebased into `extensions/antechamber/`) and the
|
|
28
|
+
built files are committed, so the clone is the extension. `https://github.com/eliottness/antechamber#extension/antechamber`
|
|
29
|
+
installs the same extension from a branch holding only its files.
|
|
30
|
+
2. Approve what it asks for: **sessions**, to follow the open session and open sub-agent sessions, and **a local
|
|
31
|
+
service that runs `bun`**, which installs the runtime and reads the OmO task store and antechamber's run records.
|
|
32
|
+
3. Open the **OmO** rail panel and its settings (the sliders icon). The **Runtime** block shows the install and the
|
|
33
|
+
launcher's path. The service starts installing as soon as a panel or the Work section opens.
|
|
34
|
+
4. In **Settings → OpenCode CLI**, set the path to that launcher: the path the Runtime block shows,
|
|
35
|
+
`<OpenChamber data dir>/extensions/antechamber/extensions/antechamber/bin/opencode-antechamber` for the repository URL. OpenChamber restarts its server on
|
|
36
|
+
antechamber.
|
|
37
|
+
|
|
38
|
+
Other ways to add it: a **Folder** (`extensions/antechamber` of an npm install or a built checkout; `antechamber
|
|
39
|
+
setup` prints it), or the release **ZIP**
|
|
40
|
+
(`https://github.com/eliottness/antechamber/releases/latest/download/antechamber-openchamber.zip`). OpenChamber
|
|
41
|
+
checks only Git installs for updates.
|
|
42
|
+
|
|
43
|
+
For a server you reach over HTTP:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
curl -sX POST "$OC/api/guests" -H 'content-type: application/json' \
|
|
47
|
+
-d '{"url":"https://github.com/eliottness/antechamber"}'
|
|
48
|
+
curl -sX PUT "$OC/api/guests/antechamber/capabilities" -H 'content-type: application/json' \
|
|
49
|
+
-d '{"granted":["sessions","service"]}'
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Updating
|
|
53
|
+
|
|
54
|
+
Every antechamber release publishes the extension at its own version to the `extension/antechamber` branch, and
|
|
55
|
+
OpenChamber offers it under **Update** in Settings → Extensions. Updating the extension is updating antechamber and
|
|
56
|
+
OmO: the next time OpenChamber starts its server, the launcher installs the new pair. OpenChamber's own OpenCode
|
|
57
|
+
Update action runs the launcher's `upgrade`, which only points here.
|
|
58
|
+
|
|
59
|
+
The previous runtime stays installed for an adapter that is still running from it; older ones are removed.
|
|
60
|
+
|
|
61
|
+
## Settings
|
|
62
|
+
|
|
63
|
+
The launcher reads these from OpenChamber's environment (the service never sees them, because OpenChamber starts
|
|
64
|
+
extension services with a minimal environment):
|
|
65
|
+
|
|
66
|
+
| Variable | Default | Effect |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| `ANTECHAMBER_RUNTIME_DIR` | `~/.antechamber/runtime` | Where runtimes are installed |
|
|
69
|
+
| `ANTECHAMBER_RUNTIME_SPEC` | unset | A local antechamber tarball or folder to run instead of the pinned release (development) |
|
|
70
|
+
|
|
71
|
+
Every other antechamber setting (`ANTECHAMBER_PI_BINARY`, `ANTECHAMBER_STATE_DIR`, ...) works as documented in
|
|
72
|
+
[docs/configuration.md](../../docs/configuration.md). If your deployment moves the state or agent directories, enter the
|
|
73
|
+
same paths in the Graph panel's settings and the Work section's **Data path**.
|
|
74
|
+
|
|
75
|
+
## How it works
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
OpenChamber --spawn--> bin/opencode-antechamber --exec--> bun <runtime>/node_modules/antechamber/dist/cli.js serve
|
|
79
|
+
| `--spawn--> <runtime>/node_modules/omo-ai/bin/omo.js --mode rpc
|
|
80
|
+
`--bun runtime/launcher.js resolve (install when missing)
|
|
81
|
+
|
|
82
|
+
panel / section (sandboxed iframes) --serviceRequest--> OpenChamber host --loopback + token--> service/main.js
|
|
83
|
+
/graph reads the OmO task store and the session registry
|
|
84
|
+
/status relays antechamber's /antechamber/work-status routes
|
|
85
|
+
/runtime reports and starts the runtime install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The launcher and the service share one install: a lock directory beside the runtime lets whichever starts first
|
|
89
|
+
install, and the other waits. `bun install` runs in a staging directory with `omo-ai` trusted (its postinstall
|
|
90
|
+
prepares OmO's engine), and the runtime is moved into place only once antechamber's CLI is there.
|
|
91
|
+
|
|
92
|
+
## Development
|
|
93
|
+
|
|
94
|
+
From the repository root: `npm run build:extension` syncs the root `package.json` manifest and bundles the panel and the section (browser IIFEs), the service
|
|
95
|
+
(ESM for OpenChamber's runtime) and the launcher (ESM for Bun); `npm test`, `npm run typecheck`, `npm run lint` and
|
|
96
|
+
`npm run format:check` cover it; `npm run pack:extension -- out.zip` writes the release archive. To run a local
|
|
97
|
+
build through the launcher, `npm pack` antechamber and point `ANTECHAMBER_RUNTIME_SPEC` at the tarball.
|
|
98
|
+
|
|
99
|
+
Sources: `src/graph/` (panel, layout, task store, `/graph`), `src/work/` (section, `/status` relay),
|
|
100
|
+
`src/runtime/` (install manager and launcher), `src/service.ts` (the one service).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# OpenChamber launcher: point Settings -> OpenCode CLI (settings.opencodeBinary) at this file.
|
|
3
|
+
# OpenChamber runs `<binary> serve --hostname H --port P` and `<binary> --version`; its Update action
|
|
4
|
+
# runs `<binary> upgrade`. The first `serve` installs the antechamber release this extension pins (and
|
|
5
|
+
# the OmO it pins) with Bun; updating the extension in Settings -> Extensions updates both.
|
|
6
|
+
set -eu
|
|
7
|
+
SCRIPT_PATH=$0
|
|
8
|
+
while [ -h "$SCRIPT_PATH" ]; do
|
|
9
|
+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" && pwd)"
|
|
10
|
+
LINK="$(readlink "$SCRIPT_PATH")"
|
|
11
|
+
case "$LINK" in
|
|
12
|
+
/*) SCRIPT_PATH=$LINK ;;
|
|
13
|
+
*) SCRIPT_PATH="$SCRIPT_DIR/$LINK" ;;
|
|
14
|
+
esac
|
|
15
|
+
done
|
|
16
|
+
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" && pwd)"
|
|
17
|
+
LAUNCHER="$SCRIPT_DIR/../runtime/launcher.js"
|
|
18
|
+
# GUI apps and services often have a PATH without Bun's install directory.
|
|
19
|
+
BUN=$(command -v bun || true)
|
|
20
|
+
for CANDIDATE in "${BUN_INSTALL:-${HOME:-}/.bun}/bin/bun" "${HOME:-}/.bun/bin/bun"; do
|
|
21
|
+
if [ -z "$BUN" ] && [ -x "$CANDIDATE" ]; then BUN=$CANDIDATE; fi
|
|
22
|
+
done
|
|
23
|
+
if [ -z "$BUN" ]; then
|
|
24
|
+
echo "opencode-antechamber: bun not found on PATH or in ~/.bun/bin; install it from https://bun.sh" >&2
|
|
25
|
+
exit 1
|
|
26
|
+
fi
|
|
27
|
+
case "${1:-}" in
|
|
28
|
+
serve)
|
|
29
|
+
shift
|
|
30
|
+
CLI=$("$BUN" "$LAUNCHER" resolve)
|
|
31
|
+
ANTECHAMBER_MANAGED=openchamber-extension
|
|
32
|
+
export ANTECHAMBER_MANAGED
|
|
33
|
+
exec "$BUN" "$CLI" serve "$@"
|
|
34
|
+
;;
|
|
35
|
+
--version|-v) exec "$BUN" "$LAUNCHER" version ;;
|
|
36
|
+
upgrade)
|
|
37
|
+
echo "antechamber is managed by its OpenChamber extension: update it in Settings -> Extensions."
|
|
38
|
+
exit 0
|
|
39
|
+
;;
|
|
40
|
+
esac
|
|
41
|
+
echo "opencode-antechamber: unsupported command '${1:-}' (only 'serve', '--version' and 'upgrade' are implemented)" >&2
|
|
42
|
+
exit 1
|
|
@@ -25,7 +25,7 @@ does that in ~2 ms with an mtime cache and returns one JSON document.
|
|
|
25
25
|
`description` / `name`, `agent_type` / `category`, `model`, the
|
|
26
26
|
`created_at` / `started_at` / `terminal_at` timestamps,
|
|
27
27
|
`final_response`, `error_message`, `run_stats`, `spawn_spec.prompt`.
|
|
28
|
-
2. **Connector registry**: `<stateDir>/sessions.json
|
|
28
|
+
2. **Connector registry**: `<stateDir>/sessions.sqlite` (`sessions.json` before antechamber 2.0), mapping OpenChamber
|
|
29
29
|
session ids to OmO session ids and titles.
|
|
30
30
|
|
|
31
31
|
Task discovery reads regular `.json` files and skips directories and symlinks.
|
|
@@ -45,7 +45,7 @@ uses the same identity rules so `openSession` opens the correct child.
|
|
|
45
45
|
Legacy deletion tombstones retain their owning parent's bare identity, even
|
|
46
46
|
when they predate task metadata in the registry.
|
|
47
47
|
|
|
48
|
-
## Graph model (`src/graph.ts`)
|
|
48
|
+
## Graph model (`src/graph/graph.ts`)
|
|
49
49
|
|
|
50
50
|
- **Run**: every task whose `root_session_id` equals the resolved root. The
|
|
51
51
|
root is resolved from the open session: it is either a root itself, a
|
|
@@ -69,7 +69,7 @@ when they predate task metadata in the registry.
|
|
|
69
69
|
failed, `cancelled`/`canceled`/`killed` → cancelled. The raw status is kept
|
|
70
70
|
for the label, so `lost` reads as `lost`.
|
|
71
71
|
|
|
72
|
-
## Layout (`src/layout.ts`)
|
|
72
|
+
## Layout (`src/graph/layout.ts`)
|
|
73
73
|
|
|
74
74
|
Columns are fixed-width, and panel CSS dimensions come from the same card-size
|
|
75
75
|
constants used by the layout. Within a column, cards stack in the order of their
|
|
@@ -80,7 +80,7 @@ Béziers. Several edges sharing one side are spread over the card's middle
|
|
|
80
80
|
band (Archify's "spread shared endpoints deterministically" rule) instead of
|
|
81
81
|
piling onto one point.
|
|
82
82
|
|
|
83
|
-
## Interaction (`src/panel.ts`, `panel/index.html`)
|
|
83
|
+
## Interaction (`src/graph/panel.ts`, `panel/index.html`)
|
|
84
84
|
|
|
85
85
|
- Hover or focus a card: direct inputs (amber) and outputs (accent) are fully
|
|
86
86
|
lit, the transitive upstream/downstream reach stays half-lit, and
|
|
@@ -102,7 +102,7 @@ piling onto one point.
|
|
|
102
102
|
and binds to `127.0.0.1` on the port the host assigns.
|
|
103
103
|
- The optional `agentDir` / `stateDir` query parameters let the panel point
|
|
104
104
|
it at other directories. It reads only `*.json` task records and
|
|
105
|
-
`sessions.json` under them. The service runs with the user's permissions
|
|
105
|
+
`sessions.sqlite` (or `sessions.json`) under them. The service runs with the user's permissions
|
|
106
106
|
(OpenChamber's approval dialog says so), so this is not a sandbox. It just
|
|
107
107
|
keeps the surface small.
|
|
108
108
|
- Prompts and final responses stay local. They reach only the panel the user
|
|
@@ -30,11 +30,11 @@ OpenChamber host ── loopback + OPENCHAMBER_SERVICE_TOKEN ──▶ service/m
|
|
|
30
30
|
`service`, and a `panel` block with no entry. OpenChamber 2.x's manifest
|
|
31
31
|
parser requires a `panel` object, but without `panel.entry` there is no rail
|
|
32
32
|
icon or panel; the section is the only surface.
|
|
33
|
-
- **Frame** (`src/status.ts`, `src/view.ts`): renders the snapshot. It follows
|
|
33
|
+
- **Frame** (`src/work/status.ts`, `src/work/view.ts`): renders the snapshot. It follows
|
|
34
34
|
the open session through `host.onSession`, sizes itself through
|
|
35
35
|
`host.setHeight`, and publishes one host header control, **Data path**, when
|
|
36
36
|
the host supports status controls.
|
|
37
|
-
- **Service** (`src/
|
|
37
|
+
- **Service** (`src/work/routes.ts`, `src/work/discovery.ts`): stateless relay. It finds
|
|
38
38
|
running antechamber servers through their run records and forwards the
|
|
39
39
|
frame's requests.
|
|
40
40
|
- **Adapter** (`src/work-status.ts` in antechamber): builds the snapshot and
|
|
@@ -58,7 +58,7 @@ The frame shows `todo`, `goal` and `wakeSources`. `monitors`, `evalCells` and
|
|
|
58
58
|
`background` stay in the snapshot, and the adapter keeps its
|
|
59
59
|
`/antechamber/work-status/<session>/eval/<cell>[/stop]` routes, for section
|
|
60
60
|
versions installed before these moved to OpenChamber's background commands
|
|
61
|
-
(ZIP installs never update). The frame checks the envelope (`src/model.ts`) and
|
|
61
|
+
(ZIP installs never update). The frame checks the envelope (`src/work/model.ts`) and
|
|
62
62
|
imports these types from the adapter, so a shape change on either side fails
|
|
63
63
|
the typecheck.
|
|
64
64
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "antechamber-openchamber",
|
|
3
|
+
"version": "2.0.1",
|
|
4
|
+
"description": "OpenChamber extension for antechamber: installs and pins the antechamber server and OmO, draws the OmO Graph and the OmO Work section, and titles OmO tool cards",
|
|
5
|
+
"private": true,
|
|
6
|
+
"type": "module",
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"openchamber": {
|
|
9
|
+
"apiVersion": 1,
|
|
10
|
+
"engines": {
|
|
11
|
+
"openchamber": ">=2.3.2"
|
|
12
|
+
},
|
|
13
|
+
"contributes": {
|
|
14
|
+
"panel": {
|
|
15
|
+
"id": "antechamber",
|
|
16
|
+
"name": "OmO",
|
|
17
|
+
"icon": "icon.svg",
|
|
18
|
+
"entry": "panel/index.html"
|
|
19
|
+
},
|
|
20
|
+
"page": {
|
|
21
|
+
"entry": "panel/index.html",
|
|
22
|
+
"title": "OmO Graph"
|
|
23
|
+
},
|
|
24
|
+
"statusSection": {
|
|
25
|
+
"entry": "status/index.html",
|
|
26
|
+
"title": "OmO Work",
|
|
27
|
+
"height": 120
|
|
28
|
+
},
|
|
29
|
+
"capabilities": [
|
|
30
|
+
"sessions"
|
|
31
|
+
],
|
|
32
|
+
"service": {
|
|
33
|
+
"entry": "service/main.js",
|
|
34
|
+
"runtime": "host",
|
|
35
|
+
"permissions": {
|
|
36
|
+
"exec": [
|
|
37
|
+
"bun"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"tools": [
|
|
42
|
+
{
|
|
43
|
+
"match": "shell",
|
|
44
|
+
"title": "{metadata.evalTitle}",
|
|
45
|
+
"subtitle": "{metadata.evalSubtitle}"
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"match": "task",
|
|
49
|
+
"title": "{metadata.agentTitle}",
|
|
50
|
+
"subtitle": "{metadata.agentSubtitle}"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"match": "agent",
|
|
54
|
+
"title": "{metadata.agentTitle}",
|
|
55
|
+
"subtitle": "{metadata.agentSubtitle}"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -51,6 +51,12 @@
|
|
|
51
51
|
#settings .row { display: flex; justify-content: flex-end; gap: 6px; }
|
|
52
52
|
#settings button { padding: 5px 12px; border-radius: 6px; border: 1px solid var(--border); background: var(--surface-2); cursor: pointer; }
|
|
53
53
|
#settings #btn-save { background: var(--accent); color: var(--oc-primary-fg, #001); border-color: transparent; }
|
|
54
|
+
#runtime { display: grid; gap: 4px; padding-bottom: 8px; border-bottom: 1px solid var(--border); font-size: 11px; color: var(--muted); }
|
|
55
|
+
#runtime strong { color: var(--fg); font-weight: 600; }
|
|
56
|
+
#runtime code { font: 11px var(--mono); color: var(--fg); word-break: break-all; user-select: all; -webkit-user-select: all; }
|
|
57
|
+
#runtime[data-state="failed"] { color: var(--bad); }
|
|
58
|
+
#runtime[data-state="installing"] { color: var(--run); }
|
|
59
|
+
#runtime .row { justify-content: flex-start; }
|
|
54
60
|
|
|
55
61
|
#viewport { position: relative; flex: 1; overflow: hidden; cursor: grab; touch-action: none; -webkit-user-select: none; user-select: none;
|
|
56
62
|
background-image: radial-gradient(color-mix(in srgb, var(--muted) 22%, transparent) 1px, transparent 1px);
|
|
@@ -151,12 +157,13 @@
|
|
|
151
157
|
<button class="tool" id="btn-in" type="button" title="Zoom in (+)" aria-label="Zoom in">
|
|
152
158
|
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M12 5v14"/></svg>
|
|
153
159
|
</button>
|
|
154
|
-
<button class="tool" id="btn-settings" type="button" title="
|
|
160
|
+
<button class="tool" id="btn-settings" type="button" title="Settings" aria-label="Settings" aria-expanded="false" aria-controls="settings">
|
|
155
161
|
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 7h10M18 7h2M4 17h4M12 17h8"/><circle cx="16" cy="7" r="2"/><circle cx="10" cy="17" r="2"/></svg>
|
|
156
162
|
</button>
|
|
157
163
|
</div>
|
|
158
164
|
</header>
|
|
159
165
|
<form id="settings" hidden>
|
|
166
|
+
<section id="runtime" aria-live="polite"><strong>Runtime</strong><span id="runtime-state">Checking…</span></section>
|
|
160
167
|
<label>OmO agent directory <input name="agentDir" placeholder="~/.omo/agent (default, then ~/.pi/agent)" autocomplete="off" spellcheck="false" /></label>
|
|
161
168
|
<label>Connector state directory <input name="stateDir" placeholder="~/.config/antechamber (default)" autocomplete="off" spellcheck="false" /></label>
|
|
162
169
|
<div class="row"><button id="btn-save" type="button">Save</button></div>
|