@demicodes/agent 0.1.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.
@@ -0,0 +1,88 @@
1
+ import { c as ConversationSummary, f as AbortResult, n as AgentServerTransport, s as ClientSessionEvent, t as AgentClientTransport } from "./transport-BkAbPvRc.mjs";
2
+ import { ProviderSelection } from "@demicodes/provider";
3
+ import { Block, UserContentBlock } from "@demicodes/core";
4
+
5
+ //#region src/client.d.ts
6
+ type AgentClientListener = (event: ClientSessionEvent) => void;
7
+ declare class AgentClient {
8
+ private readonly transport;
9
+ private readonly listeners;
10
+ private readonly pendingActionWaiters;
11
+ private readonly pendingSteerWaiters;
12
+ private readonly pendingAbortWaiters;
13
+ private readonly pendingConversationWaiters;
14
+ private readonly queuedMessageIds;
15
+ private blocks;
16
+ private revision;
17
+ private awaitingResync;
18
+ private phase;
19
+ private unsubscribeTransport;
20
+ constructor(transport: AgentClientTransport);
21
+ open(provider: ProviderSelection, cwd: string, sessionId: string): Promise<void>;
22
+ listConversations(cwd: string): Promise<ConversationSummary[]>;
23
+ sendMessage(content: UserContentBlock[]): Promise<void>;
24
+ send(content: UserContentBlock[]): Promise<void>;
25
+ dequeueMessage(messageId: string): void;
26
+ sendQueuedMessage(messageId: string): void;
27
+ steerQueuedMessage(messageId: string, options?: {
28
+ steerId?: string;
29
+ }): Promise<void>;
30
+ clearMessageQueue(messageIds?: string[]): void;
31
+ steer(content: UserContentBlock[], options?: {
32
+ steerId?: string;
33
+ }): Promise<void>;
34
+ cancelPendingSteer(steerId: string): void;
35
+ /**
36
+ * Switches the provider/model for an open session. The change takes effect on the next
37
+ * turn (the server applies it at a turn boundary), so this is fire-and-forget.
38
+ */
39
+ setProvider(provider: ProviderSelection): void;
40
+ retry(): Promise<void>;
41
+ resume(): Promise<void>;
42
+ compact(): Promise<void>;
43
+ abort(): Promise<AbortResult>;
44
+ shellWrite(commandId: string, stdin: string): Promise<void>;
45
+ close(): Promise<void>;
46
+ subscribe(listener: AgentClientListener): () => void;
47
+ transcript(): {
48
+ blocks: Block[];
49
+ };
50
+ private sendFrame;
51
+ private handleServerFrame;
52
+ private waitForFrame;
53
+ private waitForAction;
54
+ private waitForSteer;
55
+ private handleSteerResult;
56
+ private handleActionPhase;
57
+ private markNextActionActive;
58
+ private resolveActiveAction;
59
+ private resolveQueuedSendWaiter;
60
+ private moveQueuedSendWaiterToFront;
61
+ private rejectPendingAction;
62
+ private rejectErroredAction;
63
+ private resolveAllActionWaiters;
64
+ private rejectAllActionWaiters;
65
+ private rejectAllSteerWaiters;
66
+ private settleActionWaiter;
67
+ private waitForAbort;
68
+ private waitForShellWrite;
69
+ private emit;
70
+ private resolveAbortWaiter;
71
+ private rejectAllAbortWaiters;
72
+ }
73
+ //#endregion
74
+ //#region src/websocket-transport.d.ts
75
+ interface JsonWebSocket {
76
+ send(data: string): void;
77
+ close(): void;
78
+ addEventListener(type: 'message', listener: (event: {
79
+ data: unknown;
80
+ }) => void): void;
81
+ removeEventListener(type: 'message', listener: (event: {
82
+ data: unknown;
83
+ }) => void): void;
84
+ }
85
+ declare function createWebSocketClientTransport(socket: JsonWebSocket): AgentClientTransport;
86
+ declare function createWebSocketServerTransport(socket: JsonWebSocket): AgentServerTransport;
87
+ //#endregion
88
+ export { AgentClientListener as a, AgentClient as i, createWebSocketClientTransport as n, createWebSocketServerTransport as r, JsonWebSocket as t };
@@ -0,0 +1,473 @@
1
+ import { n as stringifyAgentJson, t as parseAgentJson } from "./json-codec-BFLXlg3K.mjs";
2
+ //#region src/patch.ts
3
+ /**
4
+ * Applies journal-produced transcript patches to a block list, returning a new
5
+ * array. Touched blocks are replaced (never mutated in place), so callers can
6
+ * safely share block objects between snapshots.
7
+ */
8
+ function applyTranscriptPatches(blocks, patches) {
9
+ let next = [...blocks];
10
+ for (const patch of patches) switch (patch.op) {
11
+ case "replace":
12
+ next = [...patch.value];
13
+ break;
14
+ case "replace_block":
15
+ next[patch.path[1]] = patch.value;
16
+ break;
17
+ case "add":
18
+ next.splice(patch.path[1], 0, patch.value);
19
+ break;
20
+ case "remove":
21
+ next.splice(patch.path[1], 1);
22
+ break;
23
+ case "append_text": {
24
+ const index = patch.path[1];
25
+ const block = next[index];
26
+ if (block && (block.type === "text" || block.type === "thinking")) next[index] = {
27
+ ...block,
28
+ text: block.text + patch.delta
29
+ };
30
+ break;
31
+ }
32
+ }
33
+ return next;
34
+ }
35
+ function cloneBlocks(blocks) {
36
+ return structuredClone(blocks);
37
+ }
38
+ //#endregion
39
+ //#region src/client.ts
40
+ var AgentClient = class {
41
+ transport;
42
+ listeners = /* @__PURE__ */ new Set();
43
+ pendingActionWaiters = [];
44
+ pendingSteerWaiters = /* @__PURE__ */ new Map();
45
+ pendingAbortWaiters = [];
46
+ pendingConversationWaiters = [];
47
+ queuedMessageIds = /* @__PURE__ */ new Set();
48
+ blocks = [];
49
+ revision = null;
50
+ awaitingResync = false;
51
+ phase = null;
52
+ unsubscribeTransport;
53
+ constructor(transport) {
54
+ this.transport = transport;
55
+ this.unsubscribeTransport = transport.onFrame((frame) => this.handleServerFrame(frame));
56
+ }
57
+ open(provider, cwd, sessionId) {
58
+ const wait = this.waitForFrame("opened");
59
+ this.sendFrame({
60
+ type: "open",
61
+ provider,
62
+ cwd,
63
+ sessionId
64
+ });
65
+ return wait;
66
+ }
67
+ listConversations(cwd) {
68
+ return new Promise((resolve) => {
69
+ this.pendingConversationWaiters.push(resolve);
70
+ this.sendFrame({
71
+ type: "list_conversations",
72
+ cwd
73
+ });
74
+ });
75
+ }
76
+ sendMessage(content) {
77
+ const messageId = globalThis.crypto.randomUUID();
78
+ const wait = this.waitForAction("send", messageId);
79
+ this.sendFrame({
80
+ type: "send",
81
+ messageId,
82
+ content
83
+ });
84
+ return wait;
85
+ }
86
+ send(content) {
87
+ return this.sendMessage(content);
88
+ }
89
+ dequeueMessage(messageId) {
90
+ this.sendFrame({
91
+ type: "dequeue_message",
92
+ messageId
93
+ });
94
+ this.resolveQueuedSendWaiter(messageId);
95
+ this.queuedMessageIds.delete(messageId);
96
+ }
97
+ sendQueuedMessage(messageId) {
98
+ this.moveQueuedSendWaiterToFront(messageId);
99
+ this.sendFrame({
100
+ type: "send_queued_message",
101
+ messageId
102
+ });
103
+ }
104
+ steerQueuedMessage(messageId, options = {}) {
105
+ const steerId = options.steerId ?? globalThis.crypto.randomUUID();
106
+ const wait = this.waitForSteer(steerId);
107
+ this.sendFrame({
108
+ type: "steer_queued_message",
109
+ messageId,
110
+ steerId
111
+ });
112
+ return wait.then(() => {
113
+ this.resolveQueuedSendWaiter(messageId);
114
+ this.queuedMessageIds.delete(messageId);
115
+ });
116
+ }
117
+ clearMessageQueue(messageIds = [...this.queuedMessageIds]) {
118
+ this.sendFrame({ type: "clear_message_queue" });
119
+ for (const messageId of messageIds) {
120
+ this.resolveQueuedSendWaiter(messageId);
121
+ this.queuedMessageIds.delete(messageId);
122
+ }
123
+ }
124
+ steer(content, options = {}) {
125
+ const steerId = options.steerId ?? globalThis.crypto.randomUUID();
126
+ const wait = this.waitForSteer(steerId);
127
+ this.sendFrame({
128
+ type: "steer",
129
+ steerId,
130
+ content
131
+ });
132
+ return wait;
133
+ }
134
+ cancelPendingSteer(steerId) {
135
+ this.sendFrame({
136
+ type: "cancel_pending_steer",
137
+ steerId
138
+ });
139
+ }
140
+ /**
141
+ * Switches the provider/model for an open session. The change takes effect on the next
142
+ * turn (the server applies it at a turn boundary), so this is fire-and-forget.
143
+ */
144
+ setProvider(provider) {
145
+ this.sendFrame({
146
+ type: "set_provider",
147
+ provider
148
+ });
149
+ }
150
+ retry() {
151
+ const wait = this.waitForAction("retry");
152
+ this.sendFrame({ type: "retry" });
153
+ return wait;
154
+ }
155
+ resume() {
156
+ const wait = this.waitForAction("resume");
157
+ this.sendFrame({ type: "resume" });
158
+ return wait;
159
+ }
160
+ compact() {
161
+ const wait = this.waitForAction("compact");
162
+ this.sendFrame({ type: "compact" });
163
+ return wait;
164
+ }
165
+ abort() {
166
+ const wait = this.waitForAbort();
167
+ this.sendFrame({ type: "abort" });
168
+ return wait;
169
+ }
170
+ shellWrite(commandId, stdin) {
171
+ const wait = this.waitForShellWrite(commandId);
172
+ this.sendFrame({
173
+ type: "shell_write",
174
+ commandId,
175
+ stdin
176
+ });
177
+ return wait;
178
+ }
179
+ close() {
180
+ const wait = this.waitForFrame("closed");
181
+ this.sendFrame({ type: "close" });
182
+ return wait.finally(() => {
183
+ this.unsubscribeTransport();
184
+ this.transport.close();
185
+ });
186
+ }
187
+ subscribe(listener) {
188
+ this.listeners.add(listener);
189
+ return () => {
190
+ this.listeners.delete(listener);
191
+ };
192
+ }
193
+ transcript() {
194
+ return { blocks: [...this.blocks] };
195
+ }
196
+ sendFrame(frame) {
197
+ this.transport.send(frame);
198
+ }
199
+ handleServerFrame(frame) {
200
+ switch (frame.type) {
201
+ case "transcript_snapshot":
202
+ this.blocks = [...frame.blocks];
203
+ this.revision = frame.revision;
204
+ this.awaitingResync = false;
205
+ this.emit({
206
+ type: "transcript_snapshot",
207
+ blocks: this.blocks
208
+ });
209
+ return;
210
+ case "transcript_patch":
211
+ if (this.awaitingResync) return;
212
+ if (this.revision !== null && frame.revision !== this.revision + 1) {
213
+ this.awaitingResync = true;
214
+ this.sendFrame({ type: "sync_transcript" });
215
+ return;
216
+ }
217
+ this.revision = frame.revision;
218
+ this.blocks = applyTranscriptPatches(this.blocks, frame.patches);
219
+ this.emit({
220
+ type: "transcript_patch",
221
+ patches: frame.patches,
222
+ blocks: this.blocks
223
+ });
224
+ return;
225
+ case "closed":
226
+ this.blocks = [];
227
+ this.revision = null;
228
+ this.awaitingResync = false;
229
+ this.phase = null;
230
+ this.queuedMessageIds.clear();
231
+ this.emit(frame);
232
+ this.resolveAllActionWaiters();
233
+ this.rejectAllSteerWaiters(/* @__PURE__ */ new Error("Session closed"));
234
+ this.rejectAllAbortWaiters(/* @__PURE__ */ new Error("Session closed"));
235
+ return;
236
+ case "opened":
237
+ this.emit(frame);
238
+ return;
239
+ case "phase": {
240
+ const previousPhase = this.phase;
241
+ this.phase = frame.phase;
242
+ this.emit(frame);
243
+ this.handleActionPhase(previousPhase, frame.phase);
244
+ return;
245
+ }
246
+ case "queue":
247
+ this.queuedMessageIds.clear();
248
+ for (const message of frame.queue) this.queuedMessageIds.add(message.id);
249
+ this.emit(frame);
250
+ return;
251
+ case "steer_result":
252
+ this.emit(frame);
253
+ this.handleSteerResult(frame);
254
+ return;
255
+ case "abort_result":
256
+ this.emit(frame);
257
+ this.resolveAbortWaiter(frame.result);
258
+ return;
259
+ case "tool_progress":
260
+ case "shell_output":
261
+ case "shell_write_result":
262
+ case "audit":
263
+ case "retry_scheduled":
264
+ this.emit(frame);
265
+ return;
266
+ case "conversations":
267
+ this.pendingConversationWaiters.shift()?.(frame.conversations);
268
+ return;
269
+ case "rejected":
270
+ this.emit(frame);
271
+ this.rejectPendingAction(frame.command, new Error(frame.reason));
272
+ if (frame.command === "abort") this.rejectAllAbortWaiters(new Error(frame.reason));
273
+ return;
274
+ case "error":
275
+ this.emit(frame);
276
+ this.rejectErroredAction(new Error(frame.message));
277
+ this.rejectAllSteerWaiters(new Error(frame.message));
278
+ this.rejectAllAbortWaiters(new Error(frame.message));
279
+ return;
280
+ }
281
+ }
282
+ waitForFrame(type) {
283
+ return new Promise((resolve, reject) => {
284
+ const unsubscribe = this.subscribe((event) => {
285
+ if (event.type === type) {
286
+ unsubscribe();
287
+ resolve();
288
+ } else if (event.type === "error") {
289
+ unsubscribe();
290
+ reject(new Error(event.message));
291
+ } else if (event.type === "rejected") {
292
+ unsubscribe();
293
+ reject(new Error(event.reason));
294
+ }
295
+ });
296
+ });
297
+ }
298
+ waitForAction(command, messageId) {
299
+ return new Promise((resolve, reject) => {
300
+ this.pendingActionWaiters.push({
301
+ command,
302
+ messageId,
303
+ sawActivePhase: false,
304
+ resolve,
305
+ reject
306
+ });
307
+ });
308
+ }
309
+ waitForSteer(steerId) {
310
+ return new Promise((resolve, reject) => {
311
+ this.pendingSteerWaiters.set(steerId, {
312
+ resolve,
313
+ reject
314
+ });
315
+ });
316
+ }
317
+ handleSteerResult(frame) {
318
+ const waiter = this.pendingSteerWaiters.get(frame.steerId);
319
+ if (!waiter) return;
320
+ this.pendingSteerWaiters.delete(frame.steerId);
321
+ if (frame.status === "accepted") waiter.resolve();
322
+ else waiter.reject(new Error(frame.reason));
323
+ }
324
+ handleActionPhase(previousPhase, phase) {
325
+ if (phase !== "idle") {
326
+ if (previousPhase === "idle" || previousPhase === null) this.markNextActionActive();
327
+ return;
328
+ }
329
+ this.resolveActiveAction();
330
+ }
331
+ markNextActionActive() {
332
+ const waiter = this.pendingActionWaiters.find((candidate) => !candidate.sawActivePhase);
333
+ if (waiter) waiter.sawActivePhase = true;
334
+ }
335
+ resolveActiveAction() {
336
+ const waiter = this.pendingActionWaiters.find((candidate) => candidate.sawActivePhase);
337
+ if (!waiter) return;
338
+ this.settleActionWaiter(waiter, () => waiter.resolve());
339
+ }
340
+ resolveQueuedSendWaiter(messageId) {
341
+ const waiter = this.pendingActionWaiters.find((candidate) => candidate.command === "send" && candidate.messageId === messageId && !candidate.sawActivePhase);
342
+ if (!waiter) return;
343
+ this.settleActionWaiter(waiter, () => waiter.resolve());
344
+ }
345
+ moveQueuedSendWaiterToFront(messageId) {
346
+ const waiter = this.pendingActionWaiters.find((candidate) => candidate.command === "send" && candidate.messageId === messageId && !candidate.sawActivePhase);
347
+ if (!waiter) return;
348
+ const currentIndex = this.pendingActionWaiters.indexOf(waiter);
349
+ if (currentIndex === -1) return;
350
+ this.pendingActionWaiters.splice(currentIndex, 1);
351
+ const insertionIndex = this.pendingActionWaiters.findIndex((candidate) => candidate.command === "send" && !candidate.sawActivePhase);
352
+ if (insertionIndex === -1) this.pendingActionWaiters.push(waiter);
353
+ else this.pendingActionWaiters.splice(insertionIndex, 0, waiter);
354
+ }
355
+ rejectPendingAction(command, error) {
356
+ const waiter = this.pendingActionWaiters.find((candidate) => candidate.command === command && !candidate.sawActivePhase) ?? this.pendingActionWaiters.find((candidate) => candidate.command === command);
357
+ if (!waiter) return;
358
+ this.settleActionWaiter(waiter, () => waiter.reject(error));
359
+ }
360
+ rejectErroredAction(error) {
361
+ const waiter = this.pendingActionWaiters.find((candidate) => candidate.sawActivePhase);
362
+ if (waiter) {
363
+ this.settleActionWaiter(waiter, () => waiter.reject(error));
364
+ return;
365
+ }
366
+ this.rejectAllActionWaiters(error);
367
+ }
368
+ resolveAllActionWaiters() {
369
+ const waiters = this.pendingActionWaiters.splice(0);
370
+ for (const waiter of waiters) waiter.resolve();
371
+ }
372
+ rejectAllActionWaiters(error) {
373
+ const waiters = this.pendingActionWaiters.splice(0);
374
+ for (const waiter of waiters) waiter.reject(error);
375
+ }
376
+ rejectAllSteerWaiters(error) {
377
+ const waiters = [...this.pendingSteerWaiters.values()];
378
+ this.pendingSteerWaiters.clear();
379
+ for (const waiter of waiters) waiter.reject(error);
380
+ }
381
+ settleActionWaiter(waiter, settle) {
382
+ const index = this.pendingActionWaiters.indexOf(waiter);
383
+ if (index === -1) return;
384
+ this.pendingActionWaiters.splice(index, 1);
385
+ settle();
386
+ }
387
+ waitForAbort() {
388
+ return new Promise((resolve, reject) => {
389
+ this.pendingAbortWaiters.push({
390
+ resolve,
391
+ reject
392
+ });
393
+ });
394
+ }
395
+ waitForShellWrite(commandId) {
396
+ return new Promise((resolve, reject) => {
397
+ const unsubscribe = this.subscribe((event) => {
398
+ if (event.type === "shell_output" && event.commandId === commandId) {
399
+ unsubscribe();
400
+ resolve();
401
+ return;
402
+ }
403
+ if (event.type === "shell_write_result" && event.commandId === commandId) {
404
+ unsubscribe();
405
+ resolve();
406
+ return;
407
+ }
408
+ if (event.type === "error") {
409
+ unsubscribe();
410
+ reject(new Error(event.message));
411
+ return;
412
+ }
413
+ if (event.type === "rejected" && event.command === "shell_write") {
414
+ unsubscribe();
415
+ reject(new Error(event.reason));
416
+ return;
417
+ }
418
+ if (event.type === "closed") {
419
+ unsubscribe();
420
+ resolve();
421
+ }
422
+ });
423
+ });
424
+ }
425
+ emit(event) {
426
+ for (const listener of this.listeners) listener(event);
427
+ }
428
+ resolveAbortWaiter(result) {
429
+ const waiter = this.pendingAbortWaiters.shift();
430
+ if (!waiter) return;
431
+ waiter.resolve(result);
432
+ }
433
+ rejectAllAbortWaiters(error) {
434
+ const waiters = this.pendingAbortWaiters.splice(0);
435
+ for (const waiter of waiters) waiter.reject(error);
436
+ }
437
+ };
438
+ //#endregion
439
+ //#region src/websocket-transport.ts
440
+ function createWebSocketClientTransport(socket) {
441
+ return new WebSocketJsonTransport(socket);
442
+ }
443
+ function createWebSocketServerTransport(socket) {
444
+ return new WebSocketJsonTransport(socket);
445
+ }
446
+ var WebSocketJsonTransport = class {
447
+ socket;
448
+ handlers = /* @__PURE__ */ new Set();
449
+ onMessage = (event) => {
450
+ const frame = parseAgentJson(typeof event.data === "string" ? event.data : String(event.data));
451
+ for (const handler of this.handlers) handler(frame);
452
+ };
453
+ constructor(socket) {
454
+ this.socket = socket;
455
+ this.socket.addEventListener("message", this.onMessage);
456
+ }
457
+ send(frame) {
458
+ this.socket.send(stringifyAgentJson(frame));
459
+ }
460
+ onFrame(handler) {
461
+ this.handlers.add(handler);
462
+ return () => {
463
+ this.handlers.delete(handler);
464
+ };
465
+ }
466
+ close() {
467
+ this.handlers.clear();
468
+ this.socket.removeEventListener("message", this.onMessage);
469
+ this.socket.close();
470
+ }
471
+ };
472
+ //#endregion
473
+ export { cloneBlocks as a, applyTranscriptPatches as i, createWebSocketServerTransport as n, AgentClient as r, createWebSocketClientTransport as t };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@demicodes/agent",
3
+ "description": "Session runtime and transport-neutral client and server protocol for Demi.",
4
+ "version": "0.1.0",
5
+ "private": false,
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "development": "./src/index.ts",
10
+ "types": "./dist/index.d.mts",
11
+ "import": "./dist/index.mjs"
12
+ },
13
+ "./client": {
14
+ "development": "./src/client-entry.ts",
15
+ "types": "./dist/client-entry.d.mts",
16
+ "import": "./dist/client-entry.mjs"
17
+ },
18
+ "./stdio": {
19
+ "development": "./src/stdio-transport.ts",
20
+ "types": "./dist/stdio-transport.d.mts",
21
+ "import": "./dist/stdio-transport.mjs"
22
+ }
23
+ },
24
+ "dependencies": {
25
+ "@demicodes/core": "workspace:*",
26
+ "@demicodes/provider": "workspace:*",
27
+ "@demicodes/shell": "workspace:*",
28
+ "@demicodes/utils": "workspace:*"
29
+ },
30
+ "license": "Apache-2.0",
31
+ "main": "./dist/index.mjs",
32
+ "module": "./dist/index.mjs",
33
+ "types": "./dist/index.d.mts",
34
+ "files": [
35
+ "dist"
36
+ ],
37
+ "scripts": {
38
+ "build": "tsdown"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "git+https://github.com/wspl/demi.git",
46
+ "directory": "packages/agent"
47
+ },
48
+ "homepage": "https://github.com/wspl/demi/tree/main/packages/agent#readme",
49
+ "bugs": "https://github.com/wspl/demi/issues",
50
+ "devDependencies": {
51
+ "zod": "^4.0.0"
52
+ }
53
+ }