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
package/dist/latency.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { performance } from "node:perf_hooks";
|
|
2
|
+
import { envSetting } from "./config.js";
|
|
3
|
+
const SAMPLE_MS = 100;
|
|
4
|
+
// The router uses explicit path comparisons rather than a routing framework. Unknown
|
|
5
|
+
// paths get a constant label so neither identifiers nor query credentials enter logs.
|
|
6
|
+
const ROUTE_PATTERNS = `
|
|
7
|
+
/ /health /global/health /global/upgrade /event /global/event
|
|
8
|
+
/session /session/status /project /project/current /config /global/config /provider
|
|
9
|
+
/config/providers /experimental/session /question /permission /lsp /formatter
|
|
10
|
+
/command /path /vcs /agent /file /file/content /file/status /find /find/file /find/symbol
|
|
11
|
+
/provider/auth /auth/:providerID /provider/:providerID/oauth/authorize /provider/:providerID/oauth/callback
|
|
12
|
+
/question/:requestID/reply /question/:requestID/reject /permission/:requestID/reply
|
|
13
|
+
/api/info /api/location /api/config /api/provider /api/model /api/model/default
|
|
14
|
+
/api/agent /api/agent/:agentID /api/command /api/skill /api/debug/location
|
|
15
|
+
/api/credential /api/credential/:credentialID /api/credential/:credentialID/activate
|
|
16
|
+
/api/integration/:integrationID /api/integration/:integrationID/connect/key
|
|
17
|
+
/api/integration/:integrationID/connect/external /api/integration/:integrationID/connect/oauth
|
|
18
|
+
/api/integration/:integrationID/connect/command /api/integration/:integrationID/connect/oauth/:flowID
|
|
19
|
+
/api/integration/:integrationID/connect/command/:flowID
|
|
20
|
+
/api/integration/:integrationID/connect/oauth/:flowID/complete
|
|
21
|
+
/api/plugin /api/plugin/check /api/plugin/update /api/experimental/generate /api/experimental/generate/text
|
|
22
|
+
/api/shell /api/shell/:shellID /api/shell/:shellID/output /api/websearch/provider
|
|
23
|
+
/api/experimental/session/stats /api/experimental/usage/limits /api/experimental/fs/watch
|
|
24
|
+
/api/fs/find /api/project /api/project/:projectID /api/permission/request /api/question/request
|
|
25
|
+
/api/permission/saved /api/permission/saved/:permissionID /api/form /api/vcs/init /api/vcs
|
|
26
|
+
/api/session /api/session/active /api/event /api/global/event/ws
|
|
27
|
+
/api/experimental/session/:sessionID/instructions/entries/:entryID
|
|
28
|
+
/antechamber/work-status/:sessionID
|
|
29
|
+
`
|
|
30
|
+
.trim()
|
|
31
|
+
.split(/\s+/);
|
|
32
|
+
for (const prefix of ["", "/api"]) {
|
|
33
|
+
const base = `${prefix}/session/:sessionID`;
|
|
34
|
+
ROUTE_PATTERNS.push(base, `${base}/message/:messageID`);
|
|
35
|
+
for (const action of [
|
|
36
|
+
"diff",
|
|
37
|
+
"children",
|
|
38
|
+
"todo",
|
|
39
|
+
"message",
|
|
40
|
+
"prompt_async",
|
|
41
|
+
"abort",
|
|
42
|
+
"fork",
|
|
43
|
+
"revert",
|
|
44
|
+
"unrevert",
|
|
45
|
+
"summarize",
|
|
46
|
+
"shell",
|
|
47
|
+
"command",
|
|
48
|
+
"share",
|
|
49
|
+
"generate",
|
|
50
|
+
"background",
|
|
51
|
+
"move",
|
|
52
|
+
"view",
|
|
53
|
+
"compact",
|
|
54
|
+
"prompt",
|
|
55
|
+
"synthetic",
|
|
56
|
+
"context",
|
|
57
|
+
"interrupt",
|
|
58
|
+
"revert/stage",
|
|
59
|
+
"revert/commit",
|
|
60
|
+
"agent",
|
|
61
|
+
"model",
|
|
62
|
+
"form",
|
|
63
|
+
"question",
|
|
64
|
+
"permission",
|
|
65
|
+
"inbox",
|
|
66
|
+
])
|
|
67
|
+
ROUTE_PATTERNS.push(`${base}/${action}`);
|
|
68
|
+
for (const resource of ["form", "question", "permission"])
|
|
69
|
+
for (const suffix of ["", "/reply", "/reject"])
|
|
70
|
+
ROUTE_PATTERNS.push(`${base}/${resource}/:requestID${suffix}`);
|
|
71
|
+
}
|
|
72
|
+
const routes = ROUTE_PATTERNS.map((pattern) => ({
|
|
73
|
+
pattern,
|
|
74
|
+
match: new RegExp(`^${pattern.replace(/:[A-Za-z]+/g, "[^/]+")}$`),
|
|
75
|
+
}));
|
|
76
|
+
function routePattern(req) {
|
|
77
|
+
const path = new URL(req.url ?? "/", "http://localhost").pathname.replace(/\/+$/, "") || "/";
|
|
78
|
+
return routes.find((route) => route.match.test(path))?.pattern ?? "unmatched";
|
|
79
|
+
}
|
|
80
|
+
function threshold(name, fallback) {
|
|
81
|
+
const value = Number(envSetting(name));
|
|
82
|
+
return Number.isFinite(value) && value > 0 ? value : fallback;
|
|
83
|
+
}
|
|
84
|
+
export class LatencyMonitor {
|
|
85
|
+
lagThreshold = threshold("EVENT_LOOP_LAG_MS", 250);
|
|
86
|
+
requestThreshold = threshold("SLOW_REQUEST_MS", 1000);
|
|
87
|
+
timer;
|
|
88
|
+
start() {
|
|
89
|
+
let expected = performance.now() + SAMPLE_MS;
|
|
90
|
+
this.timer = setInterval(() => {
|
|
91
|
+
const now = performance.now();
|
|
92
|
+
const lag = now - expected;
|
|
93
|
+
expected = now + SAMPLE_MS;
|
|
94
|
+
if (lag > this.lagThreshold)
|
|
95
|
+
console.warn(`[latency] event-loop-lag duration_ms=${Math.round(lag)} threshold_ms=${this.lagThreshold}`);
|
|
96
|
+
}, SAMPLE_MS);
|
|
97
|
+
this.timer.unref();
|
|
98
|
+
}
|
|
99
|
+
stop() {
|
|
100
|
+
clearInterval(this.timer);
|
|
101
|
+
this.timer = undefined;
|
|
102
|
+
}
|
|
103
|
+
async request(req, res, handle) {
|
|
104
|
+
const start = performance.now();
|
|
105
|
+
try {
|
|
106
|
+
await handle();
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
// SSE handlers return after setup; connection lifetime is not request latency.
|
|
110
|
+
const duration = performance.now() - start;
|
|
111
|
+
if (duration > this.requestThreshold)
|
|
112
|
+
console.warn(`[latency] slow-request method=${req.method ?? "GET"} route=${routePattern(req)} status=${res.statusCode} duration_ms=${Math.round(duration)}`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
package/dist/misc-routes.js
CHANGED
|
@@ -42,18 +42,10 @@ async function confinedDirectory(root, path) {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
export async function miscRoute(method, path, url, directory) {
|
|
45
|
-
if (method === "GET" && ["/skill", "/
|
|
45
|
+
if (method === "GET" && ["/skill", "/formatter"].includes(path)) {
|
|
46
46
|
return { status: 200, body: [] };
|
|
47
47
|
}
|
|
48
|
-
if (
|
|
49
|
-
return { status: 200, body: {} };
|
|
50
|
-
if ((method === "PUT" && /^\/auth\/[^/]+$/.test(path)) ||
|
|
51
|
-
(method === "POST" && /^\/provider\/[^/]+\/oauth\/(authorize|callback)$/.test(path)) ||
|
|
52
|
-
(method === "PATCH" && path === "/config")) {
|
|
53
|
-
return unsupported("Not supported with OmO; configure providers in OmO");
|
|
54
|
-
}
|
|
55
|
-
if ((["POST", "DELETE"].includes(method) && /^\/mcp\/[^/]+\/(connect|disconnect|auth)$/.test(path)) ||
|
|
56
|
-
(method === "POST" && path === "/experimental/control-plane/move-session")) {
|
|
48
|
+
if (["POST", "DELETE"].includes(method) && /^\/mcp\/[^/]+\/(connect|disconnect|auth)$/.test(path)) {
|
|
57
49
|
return unsupported("Not supported with OmO; configure MCP and session directories in OmO");
|
|
58
50
|
}
|
|
59
51
|
if (method !== "GET" || !["/file", "/find/file"].includes(path))
|
|
@@ -93,6 +85,7 @@ export async function miscRoute(method, path, url, directory) {
|
|
|
93
85
|
const dirs = url.searchParams.get("dirs") !== "false" && url.searchParams.get("type") !== "file";
|
|
94
86
|
const onlyDirs = url.searchParams.get("type") === "directory";
|
|
95
87
|
const files = [];
|
|
88
|
+
const matchedDirs = new Set();
|
|
96
89
|
let visited = 0;
|
|
97
90
|
const lowerQuery = query.toLowerCase();
|
|
98
91
|
async function visit(directory, depth) {
|
|
@@ -107,12 +100,21 @@ export async function miscRoute(method, path, url, directory) {
|
|
|
107
100
|
continue;
|
|
108
101
|
const absolute = resolve(directory, entry.name);
|
|
109
102
|
const path = relative(root, absolute).split(sep).join("/");
|
|
103
|
+
if (onlyDirs && entry.isFile() && path.toLowerCase().includes(lowerQuery)) {
|
|
104
|
+
matchedDirs.add("/");
|
|
105
|
+
const segments = path.split("/");
|
|
106
|
+
for (let i = 1; i < segments.length; i++)
|
|
107
|
+
matchedDirs.add(`${segments.slice(0, i).join("/")}/`);
|
|
108
|
+
}
|
|
110
109
|
if (((entry.isFile() && !onlyDirs) || (entry.isDirectory() && dirs)) && path.toLowerCase().includes(lowerQuery))
|
|
111
|
-
|
|
110
|
+
if (onlyDirs)
|
|
111
|
+
matchedDirs.add(`${path}/`);
|
|
112
|
+
else
|
|
113
|
+
files.push(path);
|
|
112
114
|
if (entry.isDirectory() && entry.name !== "node_modules")
|
|
113
115
|
await visit(absolute, depth + 1);
|
|
114
116
|
}
|
|
115
117
|
}
|
|
116
118
|
await visit(root, 0);
|
|
117
|
-
return { status: 200, body: files.sort() };
|
|
119
|
+
return { status: 200, body: onlyDirs ? [...matchedDirs].sort().slice(0, limit) : files.sort() };
|
|
118
120
|
}
|
|
@@ -202,6 +202,24 @@ export class PiEventTranslator {
|
|
|
202
202
|
}
|
|
203
203
|
delete s.compactingFromIdle;
|
|
204
204
|
delete s.info.time.compacting;
|
|
205
|
+
// A compaction the run never finished must not stay "running": a later Compact would join it and report
|
|
206
|
+
// success for work that will never happen.
|
|
207
|
+
if (s.compactionID !== undefined) {
|
|
208
|
+
const inputID = s.compactionID;
|
|
209
|
+
const reason = s.compactionReason ?? "manual";
|
|
210
|
+
delete s.compactionID;
|
|
211
|
+
delete s.compactionReason;
|
|
212
|
+
delete s.heldCompactionEnd;
|
|
213
|
+
this.services.emit(s.info.directory, {
|
|
214
|
+
type: "session.compaction.failed",
|
|
215
|
+
properties: {
|
|
216
|
+
sessionID: s.info.id,
|
|
217
|
+
inputID,
|
|
218
|
+
reason,
|
|
219
|
+
error: { name: "CompactionError", data: { message: error.data.message } },
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
}
|
|
205
223
|
}
|
|
206
224
|
setStatus(s, status) {
|
|
207
225
|
s.status = status;
|
|
@@ -211,6 +229,14 @@ export class PiEventTranslator {
|
|
|
211
229
|
});
|
|
212
230
|
if (status.type === "idle") {
|
|
213
231
|
s.info.time.idle = Date.now();
|
|
232
|
+
// Restored as time.idle after a restart, so /view can still accept the watermark a client saw.
|
|
233
|
+
s.tree.lastIdle = s.info.time.idle;
|
|
234
|
+
try {
|
|
235
|
+
this.services.persistMeta(s);
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
console.error(`[session] could not save the idle time of ${s.info.id}:`, error);
|
|
239
|
+
}
|
|
214
240
|
const last = s.messages?.findLast((message) => message.info.role === "assistant" && !message.info.id.startsWith("msg_background_"));
|
|
215
241
|
s.info.outcome =
|
|
216
242
|
s.interruptRequested || (last?.info.role === "assistant" && last.info.error?.name === "MessageAbortedError")
|
|
@@ -323,6 +349,7 @@ export class PiEventTranslator {
|
|
|
323
349
|
this.onMessageEvent(s, ev);
|
|
324
350
|
break;
|
|
325
351
|
case "compaction_start":
|
|
352
|
+
case "compaction_progress":
|
|
326
353
|
case "compaction_end":
|
|
327
354
|
this.onCompactionEvent(s, ev);
|
|
328
355
|
break;
|
|
@@ -641,7 +668,8 @@ export class PiEventTranslator {
|
|
|
641
668
|
}
|
|
642
669
|
// An auto-compaction can fall between steps; its summary is a new step.
|
|
643
670
|
this.completeAwaitingTools(s);
|
|
644
|
-
s.compactionID
|
|
671
|
+
s.compactionID ??= id("msg");
|
|
672
|
+
s.compactionReason = typeof ev.reason === "string" ? ev.reason : "manual";
|
|
645
673
|
this.services.emit(s.info.directory, {
|
|
646
674
|
type: "session.compaction.started",
|
|
647
675
|
properties: {
|
|
@@ -658,6 +686,15 @@ export class PiEventTranslator {
|
|
|
658
686
|
this.setStatus(s, { type: "busy" });
|
|
659
687
|
}
|
|
660
688
|
return;
|
|
689
|
+
case "compaction_progress":
|
|
690
|
+
// Extension compaction feedback carries actual partial summary fragments
|
|
691
|
+
// (senpi/dist/core/agent-session.js _updateExtensionCompactionFeedback).
|
|
692
|
+
if (s.compactionID !== undefined && typeof ev.delta === "string" && ev.delta !== "")
|
|
693
|
+
this.services.emit(s.info.directory, {
|
|
694
|
+
type: "session.compaction.delta",
|
|
695
|
+
properties: { sessionID: s.info.id, text: ev.delta },
|
|
696
|
+
});
|
|
697
|
+
return;
|
|
661
698
|
case "compaction_end": {
|
|
662
699
|
const retry = s.compactRetry;
|
|
663
700
|
if (ev.aborted === true && retry !== undefined && retry.left > 0 && (s.abortRequests ?? 0) === retry.aborts) {
|
|
@@ -666,8 +703,9 @@ export class PiEventTranslator {
|
|
|
666
703
|
}
|
|
667
704
|
delete s.info.time.compacting;
|
|
668
705
|
const result = ev.result;
|
|
669
|
-
const inputID = s.compactionID ?? id("
|
|
706
|
+
const inputID = s.compactionID ?? id("msg");
|
|
670
707
|
delete s.compactionID;
|
|
708
|
+
delete s.compactionReason;
|
|
671
709
|
this.services.emit(s.info.directory, ev.aborted === true || typeof result?.summary !== "string"
|
|
672
710
|
? {
|
|
673
711
|
type: "session.compaction.failed",
|
|
@@ -702,6 +740,10 @@ export class PiEventTranslator {
|
|
|
702
740
|
key = entry.id;
|
|
703
741
|
}
|
|
704
742
|
}
|
|
743
|
+
if (key !== undefined) {
|
|
744
|
+
s.tree.messageEntries[inputID] ??= key;
|
|
745
|
+
(s.tree.compactionReasons ??= {})[key] = ev.reason === "manual" ? "manual" : "auto";
|
|
746
|
+
}
|
|
705
747
|
for (const message of compactionMessages(s, key ?? id("compaction"), result.summary, Date.now(), s.model, s.thinkingLevel)) {
|
|
706
748
|
if (messages.some((item) => item.info.id === message.info.id))
|
|
707
749
|
continue;
|
package/dist/route-services.d.ts
CHANGED
|
@@ -5,15 +5,20 @@ import type { Catalog } from "./catalog.js";
|
|
|
5
5
|
import type { ModelSelection } from "./thinking-levels.js";
|
|
6
6
|
import type { OCEvent, OCMessageWithParts, OCPromptPart } from "./types.js";
|
|
7
7
|
import type { TodoUpdated } from "./todo-bridge.js";
|
|
8
|
-
import type {
|
|
8
|
+
import type { FileWatchRegistry } from "./file-watch.js";
|
|
9
|
+
import type { getCommands } from "./session-commands.js";
|
|
10
|
+
import type { TextGeneration } from "./text-generation.js";
|
|
9
11
|
export interface RouteServices {
|
|
10
12
|
readonly sessions: SessionManager;
|
|
11
13
|
readonly defaultDirectory: string;
|
|
12
14
|
readonly url: string;
|
|
13
15
|
readonly body: (req: IncomingMessage) => Promise<unknown>;
|
|
14
|
-
readonly catalog: () => Promise<Catalog>;
|
|
16
|
+
readonly catalog: (directory?: string, refresh?: boolean) => Promise<Catalog>;
|
|
15
17
|
readonly broadcast: (directory: string, event: OCEvent | TodoUpdated) => void;
|
|
16
|
-
readonly
|
|
18
|
+
readonly fileWatches: FileWatchRegistry;
|
|
19
|
+
readonly commands: (directory: string) => ReturnType<typeof getCommands>;
|
|
20
|
+
readonly vcsBranch: (directory: string) => Promise<string | undefined>;
|
|
21
|
+
readonly textGeneration: TextGeneration;
|
|
17
22
|
}
|
|
18
23
|
export declare const OPENCODE_COMPAT_VERSION: string | null;
|
|
19
24
|
export declare function numberParam(value: string | null): number | undefined;
|
package/dist/routes-v1.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/**
|
|
1
|
+
/** Unprefixed compatibility routes retained for evidenced or uncertain callers. */
|
|
2
2
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
3
3
|
import { type RouteServices } from "./route-services.js";
|
|
4
4
|
export declare function handleV1(services: RouteServices, req: IncomingMessage, res: ServerResponse, url: URL, path: string, method: string, directory: string): Promise<void>;
|