antechamber 0.1.1 → 0.2.0
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 +28 -0
- package/README.md +17 -14
- package/bin/opencode-antechamber +10 -1
- package/bin/opencode-pi +11 -1
- package/dist/approvals.d.ts +4 -1
- package/dist/approvals.js +51 -33
- package/dist/catalog.d.ts +1 -1
- package/dist/catalog.js +1 -1
- package/dist/commands/context.d.ts +0 -1
- package/dist/commands/context.js +1 -1
- package/dist/commands/doctor.d.ts +3 -2
- package/dist/commands/serve.d.ts +2 -1
- package/dist/commands/setup.d.ts +2 -3
- package/dist/commands/setup.js +16 -4
- package/dist/http.d.ts +8 -1
- package/dist/http.js +42 -19
- package/dist/pi-event-translation.d.ts +9 -5
- package/dist/pi-event-translation.js +233 -79
- package/dist/pi-rpc.d.ts +20 -2
- package/dist/pi-rpc.js +96 -29
- package/dist/prompt-parts.d.ts +40 -0
- package/dist/prompt-parts.js +157 -0
- package/dist/route-services.d.ts +45 -2
- package/dist/route-services.js +70 -3
- package/dist/routes-v1.js +110 -65
- package/dist/routes-v2.js +376 -73
- package/dist/server.d.ts +9 -1
- package/dist/server.js +109 -9
- package/dist/session-commands.d.ts +12 -15
- package/dist/session-commands.js +52 -11
- package/dist/session-diff.d.ts +1 -1
- package/dist/session-diff.js +10 -2
- package/dist/session-history.js +108 -42
- package/dist/session-hosts.js +2 -2
- package/dist/session-store.d.ts +8 -4
- package/dist/session-store.js +183 -33
- package/dist/session-tree.d.ts +9 -1
- package/dist/session-tree.js +51 -26
- package/dist/sessions.d.ts +32 -5
- package/dist/sessions.js +330 -119
- package/dist/sse.d.ts +16 -6
- package/dist/sse.js +77 -21
- package/dist/subagents.d.ts +5 -0
- package/dist/subagents.js +84 -71
- package/dist/tool-map.d.ts +3 -2
- package/dist/types.d.ts +152 -1
- package/dist/v2.d.ts +18 -4
- package/dist/v2.js +128 -44
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/extensions/omo-dag/README.md +2 -0
- package/extensions/omo-dag/docs/DESIGN.md +10 -1
- package/extensions/omo-dag/panel/index.html +1 -1
- package/extensions/omo-dag/panel/main.js +60 -60
- package/extensions/omo-dag/service/main.js +72 -8
- package/package.json +1 -1
package/dist/sse.d.ts
CHANGED
|
@@ -2,18 +2,28 @@
|
|
|
2
2
|
import type { ServerResponse } from "node:http";
|
|
3
3
|
import type { OCEvent } from "./types.js";
|
|
4
4
|
import type { TodoUpdated } from "./todo-bridge.js";
|
|
5
|
+
export type SSESubscriber = {
|
|
6
|
+
flavor: "v1" | "v2";
|
|
7
|
+
directory?: string;
|
|
8
|
+
global: boolean;
|
|
9
|
+
};
|
|
5
10
|
export declare class SSE {
|
|
6
11
|
private options;
|
|
7
|
-
/** SSE subscribers tagged by protocol flavor
|
|
8
|
-
readonly clients: Map<ServerResponse,
|
|
12
|
+
/** SSE subscribers tagged by protocol flavor and explicit directory scope. */
|
|
13
|
+
readonly clients: Map<ServerResponse, SSESubscriber>;
|
|
9
14
|
private v2events;
|
|
15
|
+
private nextEventID;
|
|
16
|
+
private replay;
|
|
10
17
|
constructor(options: {
|
|
11
18
|
defaultDirectory: string;
|
|
12
19
|
},
|
|
13
|
-
/** SSE subscribers tagged by protocol flavor
|
|
14
|
-
clients: Map<ServerResponse,
|
|
20
|
+
/** SSE subscribers tagged by protocol flavor and explicit directory scope. */
|
|
21
|
+
clients: Map<ServerResponse, SSESubscriber>);
|
|
15
22
|
close(): void;
|
|
16
|
-
broadcast(
|
|
23
|
+
broadcast(directory: string, event: OCEvent | TodoUpdated): void;
|
|
17
24
|
writeSSE(res: ServerResponse, frame: string): boolean;
|
|
18
|
-
handleSSE(res: ServerResponse,
|
|
25
|
+
handleSSE(res: ServerResponse, subscriber: SSESubscriber, lastEventID: string | undefined, write: (res: ServerResponse, frame: string) => boolean): void;
|
|
26
|
+
private record;
|
|
27
|
+
private retain;
|
|
28
|
+
private writeRecord;
|
|
19
29
|
}
|
package/dist/sse.js
CHANGED
|
@@ -4,12 +4,15 @@ import { errorBody, json } from "./http.js";
|
|
|
4
4
|
import { V2EventTranslator } from "./v2.js";
|
|
5
5
|
const MAX_SSE_SUBSCRIBERS = 64;
|
|
6
6
|
const MAX_SSE_PENDING_BYTES = 8 * 1024 * 1024;
|
|
7
|
+
const MAX_SSE_REPLAY_RECORDS = 1024;
|
|
7
8
|
export class SSE {
|
|
8
9
|
options;
|
|
9
10
|
clients;
|
|
10
11
|
v2events = new V2EventTranslator();
|
|
12
|
+
nextEventID = 0;
|
|
13
|
+
replay = [];
|
|
11
14
|
constructor(options,
|
|
12
|
-
/** SSE subscribers tagged by protocol flavor
|
|
15
|
+
/** SSE subscribers tagged by protocol flavor and explicit directory scope. */
|
|
13
16
|
clients) {
|
|
14
17
|
this.options = options;
|
|
15
18
|
this.clients = clients;
|
|
@@ -20,25 +23,25 @@ export class SSE {
|
|
|
20
23
|
res.end();
|
|
21
24
|
}
|
|
22
25
|
}
|
|
23
|
-
broadcast(
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
broadcast(directory, event) {
|
|
27
|
+
const v2Frames = event.type === "todo.updated"
|
|
28
|
+
? [{ id: `evt_${randomUUID()}`, type: event.type, location: { directory }, data: event.properties }]
|
|
29
|
+
: this.v2events.translate(directory, event);
|
|
30
|
+
const records = v2Frames.length === 0
|
|
31
|
+
? [this.record(directory, { v1: event })]
|
|
32
|
+
: v2Frames.map((v2, index) => this.record(directory, index === 0 ? { v1: event, v2 } : { v2 }));
|
|
33
|
+
for (const record of records)
|
|
34
|
+
this.retain(record);
|
|
35
|
+
for (const [res, subscriber] of this.clients) {
|
|
30
36
|
if (res.writableEnded || res.destroyed) {
|
|
31
37
|
this.clients.delete(res);
|
|
32
38
|
continue;
|
|
33
39
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
this.writeSSE(res, v1Frame);
|
|
40
|
+
for (const record of records) {
|
|
41
|
+
if (!subscriber.global && subscriber.directory !== directory)
|
|
42
|
+
continue;
|
|
43
|
+
if (!this.writeRecord(res, subscriber, record))
|
|
44
|
+
break;
|
|
42
45
|
}
|
|
43
46
|
}
|
|
44
47
|
}
|
|
@@ -56,7 +59,7 @@ export class SSE {
|
|
|
56
59
|
res.destroy();
|
|
57
60
|
return false;
|
|
58
61
|
}
|
|
59
|
-
handleSSE(res,
|
|
62
|
+
handleSSE(res, subscriber, lastEventID, write) {
|
|
60
63
|
if (this.clients.size >= MAX_SSE_SUBSCRIBERS) {
|
|
61
64
|
json(res, 503, errorBody("ServiceUnavailableError", "Too many SSE subscribers"));
|
|
62
65
|
return;
|
|
@@ -66,7 +69,7 @@ export class SSE {
|
|
|
66
69
|
"Cache-Control": "no-cache",
|
|
67
70
|
Connection: "keep-alive",
|
|
68
71
|
});
|
|
69
|
-
this.clients.set(res,
|
|
72
|
+
this.clients.set(res, subscriber);
|
|
70
73
|
// OpenChamber's SSE proxy aborts an upstream that is silent for 20s, so
|
|
71
74
|
// mirror real opencode's `: heartbeat` comment keepalive.
|
|
72
75
|
const heartbeat = setInterval(() => {
|
|
@@ -78,15 +81,68 @@ export class SSE {
|
|
|
78
81
|
this.clients.delete(res);
|
|
79
82
|
});
|
|
80
83
|
// OpenCode emits server.connected on subscribe; OpenChamber waits for it.
|
|
81
|
-
const connected = flavor === "v2"
|
|
84
|
+
const connected = subscriber.flavor === "v2"
|
|
82
85
|
? {
|
|
83
86
|
id: `evt_${Date.now()}`,
|
|
84
87
|
created: Date.now(),
|
|
85
88
|
type: "server.connected",
|
|
86
|
-
location: { directory: this.options.defaultDirectory },
|
|
89
|
+
location: { directory: subscriber.directory ?? this.options.defaultDirectory },
|
|
87
90
|
data: {},
|
|
88
91
|
}
|
|
89
92
|
: { type: "server.connected", properties: {} };
|
|
90
|
-
|
|
93
|
+
const initial = subscriber.flavor === "v1" && subscriber.global
|
|
94
|
+
? {
|
|
95
|
+
directory: subscriber.directory ?? this.options.defaultDirectory,
|
|
96
|
+
payload: { id: `evt_${randomUUID()}`, ...connected },
|
|
97
|
+
}
|
|
98
|
+
: connected;
|
|
99
|
+
write(res, `data: ${JSON.stringify(initial)}\n\n`);
|
|
100
|
+
const cursor = Number(lastEventID);
|
|
101
|
+
if (!Number.isSafeInteger(cursor) || cursor < 0)
|
|
102
|
+
return;
|
|
103
|
+
for (const record of this.replay) {
|
|
104
|
+
if (record.id > cursor && !this.writeRecord(res, subscriber, record))
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
record(directory, frames) {
|
|
109
|
+
return { id: ++this.nextEventID, directory, ...frames };
|
|
110
|
+
}
|
|
111
|
+
retain(record) {
|
|
112
|
+
this.replay.push(record);
|
|
113
|
+
if (this.replay.length > MAX_SSE_REPLAY_RECORDS)
|
|
114
|
+
this.replay.shift();
|
|
115
|
+
}
|
|
116
|
+
writeRecord(res, subscriber, record) {
|
|
117
|
+
if (!subscriber.global && subscriber.directory !== record.directory)
|
|
118
|
+
return true;
|
|
119
|
+
const data = subscriber.flavor === "v2"
|
|
120
|
+
? record.v2
|
|
121
|
+
: record.v1 === undefined
|
|
122
|
+
? undefined
|
|
123
|
+
: subscriber.global
|
|
124
|
+
? {
|
|
125
|
+
directory: record.directory,
|
|
126
|
+
project: record.directory,
|
|
127
|
+
payload: {
|
|
128
|
+
id: `evt_${record.id}`,
|
|
129
|
+
...record.v1,
|
|
130
|
+
properties: {
|
|
131
|
+
...record.v1.properties,
|
|
132
|
+
...(record.v1.type === "session.created" ||
|
|
133
|
+
record.v1.type === "session.updated" ||
|
|
134
|
+
record.v1.type === "session.deleted"
|
|
135
|
+
? { sessionID: record.v1.properties.info.id }
|
|
136
|
+
: {}),
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
: record.v1;
|
|
141
|
+
if (data === undefined)
|
|
142
|
+
return true;
|
|
143
|
+
const frame = subscriber.flavor === "v1" && !subscriber.global
|
|
144
|
+
? `data: ${JSON.stringify(data)}\n\n`
|
|
145
|
+
: `id: ${record.id}\ndata: ${JSON.stringify(data)}\n\n`;
|
|
146
|
+
return this.writeSSE(res, frame);
|
|
91
147
|
}
|
|
92
148
|
}
|
package/dist/subagents.d.ts
CHANGED
|
@@ -46,11 +46,16 @@ export declare class SubagentBridge {
|
|
|
46
46
|
private discoverParts;
|
|
47
47
|
private create;
|
|
48
48
|
restore(id: string): ManagedSession | undefined;
|
|
49
|
+
private managedSession;
|
|
49
50
|
restoreAll(): void;
|
|
50
51
|
owns(id: string): boolean;
|
|
51
52
|
messages(id: string): OCMessageWithParts[];
|
|
52
53
|
private watch;
|
|
53
54
|
private refresh;
|
|
55
|
+
private reconcileTaskRecord;
|
|
56
|
+
private hydrateTranscript;
|
|
57
|
+
private reconcileRuntime;
|
|
58
|
+
private publishRefresh;
|
|
54
59
|
private saveArchive;
|
|
55
60
|
private fallback;
|
|
56
61
|
private persist;
|
package/dist/subagents.js
CHANGED
|
@@ -295,26 +295,15 @@ export class SubagentBridge {
|
|
|
295
295
|
}
|
|
296
296
|
}
|
|
297
297
|
create(parent, task) {
|
|
298
|
-
const session = {
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
},
|
|
308
|
-
pi: undefined,
|
|
309
|
-
messages: undefined,
|
|
310
|
-
model: undefined,
|
|
311
|
-
streaming: undefined,
|
|
312
|
-
status: { type: "idle" },
|
|
313
|
-
toolByCallId: new Map(),
|
|
314
|
-
tree: { messageEntries: {} },
|
|
315
|
-
hasPendingInteraction: () => false,
|
|
316
|
-
hasRunningChildren: () => false,
|
|
317
|
-
};
|
|
298
|
+
const session = this.managedSession({
|
|
299
|
+
id: ocIdFor(task.id),
|
|
300
|
+
parentID: parent.info.id,
|
|
301
|
+
projectID: parent.info.projectID,
|
|
302
|
+
directory: parent.info.directory,
|
|
303
|
+
title: typeof task.input.description === "string" ? task.input.description : task.id,
|
|
304
|
+
version: parent.info.version,
|
|
305
|
+
time: { ...parent.info.time },
|
|
306
|
+
});
|
|
318
307
|
const child = {
|
|
319
308
|
session,
|
|
320
309
|
task,
|
|
@@ -339,15 +328,21 @@ export class SubagentBridge {
|
|
|
339
328
|
const entry = this.store.get(id);
|
|
340
329
|
if (!entry?.task || !entry.parentID)
|
|
341
330
|
return undefined;
|
|
342
|
-
const parent = {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
331
|
+
const parent = this.managedSession({
|
|
332
|
+
id: entry.parentID,
|
|
333
|
+
projectID: entry.directory,
|
|
334
|
+
directory: entry.directory,
|
|
335
|
+
title: "",
|
|
336
|
+
version: ADAPTER_VERSION,
|
|
337
|
+
time: { created: entry.created, updated: entry.updated },
|
|
338
|
+
});
|
|
339
|
+
const child = this.create(parent, entry.task);
|
|
340
|
+
child.session.info.title = entry.title ?? child.session.info.title;
|
|
341
|
+
return child.session;
|
|
342
|
+
}
|
|
343
|
+
managedSession(info) {
|
|
344
|
+
return {
|
|
345
|
+
info,
|
|
351
346
|
pi: undefined,
|
|
352
347
|
messages: undefined,
|
|
353
348
|
model: undefined,
|
|
@@ -358,9 +353,6 @@ export class SubagentBridge {
|
|
|
358
353
|
hasPendingInteraction: () => false,
|
|
359
354
|
hasRunningChildren: () => false,
|
|
360
355
|
};
|
|
361
|
-
const child = this.create(parent, entry.task);
|
|
362
|
-
child.session.info.title = entry.title ?? child.session.info.title;
|
|
363
|
-
return child.session;
|
|
364
356
|
}
|
|
365
357
|
restoreAll() {
|
|
366
358
|
for (const id of this.store.childIds())
|
|
@@ -396,44 +388,63 @@ export class SubagentBridge {
|
|
|
396
388
|
}
|
|
397
389
|
refresh(child, publish, useRecord = true) {
|
|
398
390
|
const archive = this.archive(child.task.id);
|
|
391
|
+
const found = this.reconcileTaskRecord(child, archive, useRecord);
|
|
392
|
+
const running = active(child.task.details.status) && (found !== undefined || child.live);
|
|
393
|
+
const state = {
|
|
394
|
+
previousMessages: child.session.messages ?? [],
|
|
395
|
+
previousStatus: child.session.status.type,
|
|
396
|
+
running,
|
|
397
|
+
costChanged: this.hydrateTranscript(child, archive, running, child.session.status.type),
|
|
398
|
+
updatedParts: this.reconcileRuntime(child, running),
|
|
399
|
+
};
|
|
400
|
+
if (state.costChanged)
|
|
401
|
+
this.emit(child.session.info.directory, { type: "session.updated", properties: { info: child.session.info } });
|
|
402
|
+
if (publish)
|
|
403
|
+
for (const part of state.updatedParts)
|
|
404
|
+
this.emitPart(child.session, part);
|
|
405
|
+
this.persist(child);
|
|
406
|
+
this.publishRefresh(child, state, publish);
|
|
407
|
+
}
|
|
408
|
+
reconcileTaskRecord(child, archive, useRecord) {
|
|
399
409
|
const found = findTask(child.task.id, child.task.parentPiId);
|
|
400
|
-
if (found)
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
// Match the recorded session, not another retained run in the same directory.
|
|
413
|
-
const sessions = join(dirname(dirname(found.path)), "children", child.task.id, "sessions", child.task.id);
|
|
414
|
-
try {
|
|
415
|
-
const file = readdirSync(sessions, { withFileTypes: true }).find((entry) => entry.isFile() && entry.name.endsWith(`_${found.data.child_session_id}.jsonl`));
|
|
416
|
-
child.livePath = file ? join(sessions, file.name) : undefined;
|
|
417
|
-
}
|
|
418
|
-
catch (error) {
|
|
419
|
-
if (!(error instanceof Error && "code" in error && ["ENOENT", "ENOTDIR", "EACCES"].includes(String(error.code))))
|
|
420
|
-
throw error;
|
|
421
|
-
child.livePath = undefined;
|
|
422
|
-
}
|
|
423
|
-
}
|
|
410
|
+
if (!found)
|
|
411
|
+
return undefined;
|
|
412
|
+
child.recordPath = found.path;
|
|
413
|
+
const updated = typeof found.data.updated_at === "string" ? Date.parse(found.data.updated_at) : NaN;
|
|
414
|
+
const retainedTerminal = Math.max(child.task.terminalAt ?? 0, existsSync(archive) ? statSync(archive).mtimeMs : 0);
|
|
415
|
+
const staleRunning = active(found.data.status) && terminal(child.task.details.status) && updated <= retainedTerminal;
|
|
416
|
+
if (useRecord && !staleRunning)
|
|
417
|
+
child.task.details = { ...child.task.details, ...found.data };
|
|
418
|
+
const host = found.data.host_session;
|
|
419
|
+
if (record(host) && typeof host.session_path === "string") {
|
|
420
|
+
child.livePath = host.session_path;
|
|
421
|
+
return found;
|
|
424
422
|
}
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
if (typeof found.data.child_session_id !== "string" || found.data.child_session_id === "")
|
|
424
|
+
return found;
|
|
425
|
+
// In-process agents persist under the task store but have no RPC host_session.
|
|
426
|
+
// Match the recorded session, not another retained run in the same directory.
|
|
427
|
+
const sessions = join(dirname(dirname(found.path)), "children", child.task.id, "sessions", child.task.id);
|
|
428
|
+
try {
|
|
429
|
+
const file = readdirSync(sessions, { withFileTypes: true }).find((entry) => entry.isFile() && entry.name.endsWith(`_${found.data.child_session_id}.jsonl`));
|
|
430
|
+
child.livePath = file ? join(sessions, file.name) : undefined;
|
|
431
|
+
}
|
|
432
|
+
catch (error) {
|
|
433
|
+
if (!(error instanceof Error && "code" in error && ["ENOENT", "ENOTDIR", "EACCES"].includes(String(error.code))))
|
|
434
|
+
throw error;
|
|
435
|
+
child.livePath = undefined;
|
|
436
|
+
}
|
|
437
|
+
return found;
|
|
438
|
+
}
|
|
439
|
+
hydrateTranscript(child, archive, running, previousStatus) {
|
|
428
440
|
// A resumed live task can be newer than its archive; completed history prefers the archive.
|
|
429
441
|
const liveRun = running || previousStatus === "busy";
|
|
430
442
|
let raw = liveRun ? undefined : optionalFile(archive);
|
|
431
443
|
if (running && child.recordPath)
|
|
432
444
|
this.watch(child, child.recordPath);
|
|
433
445
|
if (raw === undefined && child.livePath !== undefined) {
|
|
434
|
-
if (running)
|
|
446
|
+
if (running)
|
|
435
447
|
this.watch(child, child.livePath);
|
|
436
|
-
}
|
|
437
448
|
child.tail.read(child.livePath);
|
|
438
449
|
if (child.tail.text !== "")
|
|
439
450
|
raw = child.tail.text;
|
|
@@ -459,36 +470,38 @@ export class SubagentBridge {
|
|
|
459
470
|
}
|
|
460
471
|
child.loaded = true;
|
|
461
472
|
// Each child reports only its own spend; OpenChamber sums the subtree by parentID.
|
|
462
|
-
|
|
463
|
-
|
|
473
|
+
return syncSessionCost(child.session);
|
|
474
|
+
}
|
|
475
|
+
reconcileRuntime(child, running) {
|
|
464
476
|
child.session.status = { type: running ? "busy" : "idle" };
|
|
465
477
|
if (!running)
|
|
466
478
|
this.closeWatchers(child);
|
|
467
|
-
|
|
479
|
+
const parts = [...child.parts.values()];
|
|
480
|
+
for (const part of parts) {
|
|
468
481
|
updateTaskParts([part], {
|
|
469
482
|
details: child.task.details,
|
|
470
483
|
content: [{ type: "text", text: child.task.details.final_response ?? "" }],
|
|
471
484
|
}, !running, Date.now());
|
|
472
|
-
if (publish)
|
|
473
|
-
this.emitPart(child.session, part);
|
|
474
485
|
}
|
|
475
|
-
|
|
486
|
+
return parts;
|
|
487
|
+
}
|
|
488
|
+
publishRefresh(child, state, publish) {
|
|
476
489
|
if (!publish)
|
|
477
490
|
return;
|
|
478
|
-
const before = new Map(previousMessages.map((message) => [message.info.id, JSON.stringify(message)]));
|
|
479
|
-
for (const message of child.session.messages) {
|
|
491
|
+
const before = new Map(state.previousMessages.map((message) => [message.info.id, JSON.stringify(message)]));
|
|
492
|
+
for (const message of child.session.messages ?? []) {
|
|
480
493
|
if (before.get(message.info.id) === JSON.stringify(message))
|
|
481
494
|
continue;
|
|
482
495
|
this.emit(child.session.info.directory, { type: "message.updated", properties: { info: message.info } });
|
|
483
496
|
for (const part of message.parts)
|
|
484
497
|
this.emitPart(child.session, part);
|
|
485
498
|
}
|
|
486
|
-
if (previousStatus !== child.session.status.type) {
|
|
499
|
+
if (state.previousStatus !== child.session.status.type) {
|
|
487
500
|
this.emit(child.session.info.directory, {
|
|
488
501
|
type: "session.status",
|
|
489
502
|
properties: { sessionID: child.session.info.id, status: child.session.status },
|
|
490
503
|
});
|
|
491
|
-
if (!running)
|
|
504
|
+
if (!state.running)
|
|
492
505
|
this.emit(child.session.info.directory, {
|
|
493
506
|
type: "session.idle",
|
|
494
507
|
properties: { sessionID: child.session.info.id },
|
package/dist/tool-map.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { OCToolPart } from "./types.js";
|
|
2
2
|
/** OmO arguments translated to OpenChamber's renderer contracts. */
|
|
3
|
-
|
|
3
|
+
interface MappedTool {
|
|
4
4
|
tool: string;
|
|
5
5
|
input: Record<string, unknown>;
|
|
6
6
|
title: string;
|
|
@@ -12,7 +12,7 @@ export interface ToolResult {
|
|
|
12
12
|
details?: unknown;
|
|
13
13
|
isError?: boolean;
|
|
14
14
|
}
|
|
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
18
|
/** Text blocks only; images and structured details are not tool output text. */
|
|
@@ -30,3 +30,4 @@ export declare function providerToolParts(content: unknown, sessionID: string, m
|
|
|
30
30
|
export declare function applyProviderToolResults(content: unknown, parts: Iterable<OCToolPart>, timestamp: number): OCToolPart[];
|
|
31
31
|
/** Only an actual result (or a self-contained provider status) means success. */
|
|
32
32
|
export declare function finishProviderToolParts(parts: Iterable<OCToolPart>, timestamp: number, error?: string): OCToolPart[];
|
|
33
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -12,6 +12,31 @@ export interface OCSession {
|
|
|
12
12
|
projectID: string;
|
|
13
13
|
directory: string;
|
|
14
14
|
parentID?: string;
|
|
15
|
+
parentMessageID?: string;
|
|
16
|
+
agent?: string;
|
|
17
|
+
model?: {
|
|
18
|
+
id: string;
|
|
19
|
+
providerID: string;
|
|
20
|
+
variant?: string;
|
|
21
|
+
};
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
outcome?: "succeeded" | "failed" | "interrupted";
|
|
24
|
+
slug?: string;
|
|
25
|
+
path?: string;
|
|
26
|
+
summary?: {
|
|
27
|
+
additions: number;
|
|
28
|
+
deletions: number;
|
|
29
|
+
files: number;
|
|
30
|
+
};
|
|
31
|
+
tokens?: {
|
|
32
|
+
input: number;
|
|
33
|
+
output: number;
|
|
34
|
+
reasoning: number;
|
|
35
|
+
cache: {
|
|
36
|
+
read: number;
|
|
37
|
+
write: number;
|
|
38
|
+
};
|
|
39
|
+
};
|
|
15
40
|
title: string;
|
|
16
41
|
version: string;
|
|
17
42
|
revert?: {
|
|
@@ -22,6 +47,7 @@ export interface OCSession {
|
|
|
22
47
|
time: {
|
|
23
48
|
created: number;
|
|
24
49
|
updated: number;
|
|
50
|
+
idle?: number;
|
|
25
51
|
compacting?: number;
|
|
26
52
|
/** Epoch ms — presence marks the session archived (SDK v2 session.update). */
|
|
27
53
|
archived?: number;
|
|
@@ -36,6 +62,10 @@ export interface OCUserMessage {
|
|
|
36
62
|
};
|
|
37
63
|
agent: string;
|
|
38
64
|
model: OCModelRef;
|
|
65
|
+
/** No-reply context admitted without a turn (v2 lists it as `type: "synthetic"`). */
|
|
66
|
+
synthetic?: boolean;
|
|
67
|
+
/** Client metadata sent with the prompt (OpenChamber 2.x). */
|
|
68
|
+
metadata?: Record<string, unknown>;
|
|
39
69
|
}
|
|
40
70
|
export interface OCAssistantMessage {
|
|
41
71
|
id: string;
|
|
@@ -156,7 +186,34 @@ export interface OCCompactionPart {
|
|
|
156
186
|
type: "compaction";
|
|
157
187
|
auto: boolean;
|
|
158
188
|
}
|
|
159
|
-
export type
|
|
189
|
+
export type OCPromptPart = ({
|
|
190
|
+
type: "text";
|
|
191
|
+
text: string;
|
|
192
|
+
synthetic?: boolean;
|
|
193
|
+
} | {
|
|
194
|
+
type: "file";
|
|
195
|
+
mime: string;
|
|
196
|
+
url: string;
|
|
197
|
+
filename?: string;
|
|
198
|
+
} | {
|
|
199
|
+
type: "agent";
|
|
200
|
+
name: string;
|
|
201
|
+
source?: {
|
|
202
|
+
value: string;
|
|
203
|
+
start: number;
|
|
204
|
+
end: number;
|
|
205
|
+
};
|
|
206
|
+
}) & {
|
|
207
|
+
id?: string;
|
|
208
|
+
};
|
|
209
|
+
export type OCInputPart = OCPromptPart & {
|
|
210
|
+
id: string;
|
|
211
|
+
sessionID: string;
|
|
212
|
+
messageID: string;
|
|
213
|
+
};
|
|
214
|
+
export type OCPart = OCTextPart | OCReasoningPart | OCToolPart | OCCompactionPart | Exclude<OCInputPart, {
|
|
215
|
+
type: "text";
|
|
216
|
+
}>;
|
|
160
217
|
export interface OCMessageWithParts {
|
|
161
218
|
info: OCMessage;
|
|
162
219
|
parts: OCPart[];
|
|
@@ -181,6 +238,15 @@ export type OCEvent = {
|
|
|
181
238
|
delivery: "steer" | "queue";
|
|
182
239
|
payload: {
|
|
183
240
|
text: string;
|
|
241
|
+
files?: Array<Record<string, unknown>>;
|
|
242
|
+
agents?: Array<Record<string, unknown>>;
|
|
243
|
+
metadata?: Record<string, unknown>;
|
|
244
|
+
};
|
|
245
|
+
} | {
|
|
246
|
+
type: "synthetic";
|
|
247
|
+
payload: {
|
|
248
|
+
text: string;
|
|
249
|
+
metadata?: Record<string, unknown>;
|
|
184
250
|
};
|
|
185
251
|
};
|
|
186
252
|
};
|
|
@@ -209,6 +275,79 @@ export type OCEvent = {
|
|
|
209
275
|
properties: {
|
|
210
276
|
info: OCSession;
|
|
211
277
|
};
|
|
278
|
+
} | {
|
|
279
|
+
type: "session.revert.staged";
|
|
280
|
+
properties: {
|
|
281
|
+
sessionID: string;
|
|
282
|
+
revert: {
|
|
283
|
+
messageID: string;
|
|
284
|
+
};
|
|
285
|
+
};
|
|
286
|
+
} | {
|
|
287
|
+
type: "session.revert.cleared";
|
|
288
|
+
properties: {
|
|
289
|
+
sessionID: string;
|
|
290
|
+
};
|
|
291
|
+
} | {
|
|
292
|
+
type: "session.revert.committed";
|
|
293
|
+
properties: {
|
|
294
|
+
sessionID: string;
|
|
295
|
+
to: string;
|
|
296
|
+
};
|
|
297
|
+
} | {
|
|
298
|
+
type: "session.compaction.started";
|
|
299
|
+
properties: {
|
|
300
|
+
sessionID: string;
|
|
301
|
+
reason: string;
|
|
302
|
+
inputID: string;
|
|
303
|
+
};
|
|
304
|
+
} | {
|
|
305
|
+
type: "session.compaction.ended";
|
|
306
|
+
properties: {
|
|
307
|
+
sessionID: string;
|
|
308
|
+
reason: string;
|
|
309
|
+
inputID: string;
|
|
310
|
+
text: string;
|
|
311
|
+
recent: string;
|
|
312
|
+
};
|
|
313
|
+
} | {
|
|
314
|
+
type: "session.compaction.failed";
|
|
315
|
+
properties: {
|
|
316
|
+
sessionID: string;
|
|
317
|
+
reason: string;
|
|
318
|
+
inputID: string;
|
|
319
|
+
error: {
|
|
320
|
+
name: string;
|
|
321
|
+
data: {
|
|
322
|
+
message: string;
|
|
323
|
+
};
|
|
324
|
+
};
|
|
325
|
+
};
|
|
326
|
+
} | {
|
|
327
|
+
type: "session.shell.started" | "session.shell.ended";
|
|
328
|
+
properties: {
|
|
329
|
+
sessionID: string;
|
|
330
|
+
shell: {
|
|
331
|
+
id: string;
|
|
332
|
+
command: string;
|
|
333
|
+
cwd: string;
|
|
334
|
+
shell: string;
|
|
335
|
+
file: string;
|
|
336
|
+
status: "running" | "exited" | "timeout" | "killed";
|
|
337
|
+
exit?: number;
|
|
338
|
+
metadata: Record<string, unknown>;
|
|
339
|
+
time: {
|
|
340
|
+
started: number;
|
|
341
|
+
completed?: number;
|
|
342
|
+
};
|
|
343
|
+
};
|
|
344
|
+
output?: {
|
|
345
|
+
output: string;
|
|
346
|
+
cursor: number;
|
|
347
|
+
size: number;
|
|
348
|
+
truncated: boolean;
|
|
349
|
+
};
|
|
350
|
+
};
|
|
212
351
|
} | {
|
|
213
352
|
type: "session.deleted";
|
|
214
353
|
properties: {
|
|
@@ -253,6 +392,15 @@ export type OCEvent = {
|
|
|
253
392
|
part: OCPart;
|
|
254
393
|
delta?: string;
|
|
255
394
|
};
|
|
395
|
+
} | {
|
|
396
|
+
type: "message.part.delta";
|
|
397
|
+
properties: {
|
|
398
|
+
sessionID: string;
|
|
399
|
+
messageID: string;
|
|
400
|
+
partID: string;
|
|
401
|
+
field: "text";
|
|
402
|
+
delta: string;
|
|
403
|
+
};
|
|
256
404
|
} | {
|
|
257
405
|
type: "permission.asked";
|
|
258
406
|
properties: {
|
|
@@ -332,12 +480,15 @@ export interface OCProject {
|
|
|
332
480
|
vcs?: "git";
|
|
333
481
|
time: {
|
|
334
482
|
created: number;
|
|
483
|
+
updated?: number;
|
|
335
484
|
initialized?: number;
|
|
336
485
|
};
|
|
486
|
+
sandboxes?: string[];
|
|
337
487
|
}
|
|
338
488
|
/** Incoming prompt body for POST /session/{id}/message and /prompt_async */
|
|
339
489
|
export interface OCPromptBody {
|
|
340
490
|
messageID?: string;
|
|
491
|
+
delivery?: "steer" | "queue";
|
|
341
492
|
model?: OCModelRef;
|
|
342
493
|
agent?: string;
|
|
343
494
|
noReply?: boolean;
|