antechamber 0.1.0 → 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 +51 -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 +21 -3
- package/dist/pi-event-translation.js +317 -65
- package/dist/pi-rpc.d.ts +26 -2
- package/dist/pi-rpc.js +102 -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 +20 -15
- package/dist/session-commands.js +61 -11
- package/dist/session-diff.d.ts +1 -1
- package/dist/session-diff.js +10 -2
- package/dist/session-history.js +113 -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 -2
- package/dist/session-tree.js +51 -63
- package/dist/sessions.d.ts +34 -6
- package/dist/sessions.js +340 -146
- package/dist/sse.d.ts +16 -6
- package/dist/sse.js +77 -21
- package/dist/subagents.d.ts +5 -0
- package/dist/subagents.js +86 -69
- package/dist/tool-map.d.ts +3 -2
- package/dist/types.d.ts +164 -1
- package/dist/v2.d.ts +18 -4
- package/dist/v2.js +141 -50
- 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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, readSync, readdirSync, renameSync, statSync, watch, writeFileSync, } from "node:fs";
|
|
3
3
|
import { basename, dirname, join } from "node:path";
|
|
4
4
|
import { piAgentDir } from "./agent-dir.js";
|
|
5
|
+
import { syncSessionCost } from "./pi-event-translation.js";
|
|
5
6
|
import { ocIdFor, SessionPersistenceError } from "./session-store.js";
|
|
6
7
|
import { mapTool, resultText, toolMappers } from "./tool-map.js";
|
|
7
8
|
import { ADAPTER_VERSION } from "./version.js";
|
|
@@ -294,26 +295,15 @@ export class SubagentBridge {
|
|
|
294
295
|
}
|
|
295
296
|
}
|
|
296
297
|
create(parent, task) {
|
|
297
|
-
const session = {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
},
|
|
307
|
-
pi: undefined,
|
|
308
|
-
messages: undefined,
|
|
309
|
-
model: undefined,
|
|
310
|
-
streaming: undefined,
|
|
311
|
-
status: { type: "idle" },
|
|
312
|
-
toolByCallId: new Map(),
|
|
313
|
-
tree: { messageEntries: {} },
|
|
314
|
-
hasPendingInteraction: () => false,
|
|
315
|
-
hasRunningChildren: () => false,
|
|
316
|
-
};
|
|
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
|
+
});
|
|
317
307
|
const child = {
|
|
318
308
|
session,
|
|
319
309
|
task,
|
|
@@ -338,15 +328,21 @@ export class SubagentBridge {
|
|
|
338
328
|
const entry = this.store.get(id);
|
|
339
329
|
if (!entry?.task || !entry.parentID)
|
|
340
330
|
return undefined;
|
|
341
|
-
const parent = {
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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,
|
|
350
346
|
pi: undefined,
|
|
351
347
|
messages: undefined,
|
|
352
348
|
model: undefined,
|
|
@@ -357,9 +353,6 @@ export class SubagentBridge {
|
|
|
357
353
|
hasPendingInteraction: () => false,
|
|
358
354
|
hasRunningChildren: () => false,
|
|
359
355
|
};
|
|
360
|
-
const child = this.create(parent, entry.task);
|
|
361
|
-
child.session.info.title = entry.title ?? child.session.info.title;
|
|
362
|
-
return child.session;
|
|
363
356
|
}
|
|
364
357
|
restoreAll() {
|
|
365
358
|
for (const id of this.store.childIds())
|
|
@@ -395,44 +388,63 @@ export class SubagentBridge {
|
|
|
395
388
|
}
|
|
396
389
|
refresh(child, publish, useRecord = true) {
|
|
397
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) {
|
|
398
409
|
const found = findTask(child.task.id, child.task.parentPiId);
|
|
399
|
-
if (found)
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
// Match the recorded session, not another retained run in the same directory.
|
|
412
|
-
const sessions = join(dirname(dirname(found.path)), "children", child.task.id, "sessions", child.task.id);
|
|
413
|
-
try {
|
|
414
|
-
const file = readdirSync(sessions, { withFileTypes: true }).find((entry) => entry.isFile() && entry.name.endsWith(`_${found.data.child_session_id}.jsonl`));
|
|
415
|
-
child.livePath = file ? join(sessions, file.name) : undefined;
|
|
416
|
-
}
|
|
417
|
-
catch (error) {
|
|
418
|
-
if (!(error instanceof Error && "code" in error && ["ENOENT", "ENOTDIR", "EACCES"].includes(String(error.code))))
|
|
419
|
-
throw error;
|
|
420
|
-
child.livePath = undefined;
|
|
421
|
-
}
|
|
422
|
-
}
|
|
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;
|
|
423
422
|
}
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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) {
|
|
427
440
|
// A resumed live task can be newer than its archive; completed history prefers the archive.
|
|
428
441
|
const liveRun = running || previousStatus === "busy";
|
|
429
442
|
let raw = liveRun ? undefined : optionalFile(archive);
|
|
430
443
|
if (running && child.recordPath)
|
|
431
444
|
this.watch(child, child.recordPath);
|
|
432
445
|
if (raw === undefined && child.livePath !== undefined) {
|
|
433
|
-
if (running)
|
|
446
|
+
if (running)
|
|
434
447
|
this.watch(child, child.livePath);
|
|
435
|
-
}
|
|
436
448
|
child.tail.read(child.livePath);
|
|
437
449
|
if (child.tail.text !== "")
|
|
438
450
|
raw = child.tail.text;
|
|
@@ -457,34 +469,39 @@ export class SubagentBridge {
|
|
|
457
469
|
child.session.messages = this.fallback(child);
|
|
458
470
|
}
|
|
459
471
|
child.loaded = true;
|
|
472
|
+
// Each child reports only its own spend; OpenChamber sums the subtree by parentID.
|
|
473
|
+
return syncSessionCost(child.session);
|
|
474
|
+
}
|
|
475
|
+
reconcileRuntime(child, running) {
|
|
460
476
|
child.session.status = { type: running ? "busy" : "idle" };
|
|
461
477
|
if (!running)
|
|
462
478
|
this.closeWatchers(child);
|
|
463
|
-
|
|
479
|
+
const parts = [...child.parts.values()];
|
|
480
|
+
for (const part of parts) {
|
|
464
481
|
updateTaskParts([part], {
|
|
465
482
|
details: child.task.details,
|
|
466
483
|
content: [{ type: "text", text: child.task.details.final_response ?? "" }],
|
|
467
484
|
}, !running, Date.now());
|
|
468
|
-
if (publish)
|
|
469
|
-
this.emitPart(child.session, part);
|
|
470
485
|
}
|
|
471
|
-
|
|
486
|
+
return parts;
|
|
487
|
+
}
|
|
488
|
+
publishRefresh(child, state, publish) {
|
|
472
489
|
if (!publish)
|
|
473
490
|
return;
|
|
474
|
-
const before = new Map(previousMessages.map((message) => [message.info.id, JSON.stringify(message)]));
|
|
475
|
-
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 ?? []) {
|
|
476
493
|
if (before.get(message.info.id) === JSON.stringify(message))
|
|
477
494
|
continue;
|
|
478
495
|
this.emit(child.session.info.directory, { type: "message.updated", properties: { info: message.info } });
|
|
479
496
|
for (const part of message.parts)
|
|
480
497
|
this.emitPart(child.session, part);
|
|
481
498
|
}
|
|
482
|
-
if (previousStatus !== child.session.status.type) {
|
|
499
|
+
if (state.previousStatus !== child.session.status.type) {
|
|
483
500
|
this.emit(child.session.info.directory, {
|
|
484
501
|
type: "session.status",
|
|
485
502
|
properties: { sessionID: child.session.info.id, status: child.session.status },
|
|
486
503
|
});
|
|
487
|
-
if (!running)
|
|
504
|
+
if (!state.running)
|
|
488
505
|
this.emit(child.session.info.directory, {
|
|
489
506
|
type: "session.idle",
|
|
490
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 {};
|