@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.
- package/LICENSE +201 -0
- package/README.md +20 -0
- package/dist/client-entry.d.mts +4 -0
- package/dist/client-entry.mjs +2 -0
- package/dist/index.d.mts +230 -0
- package/dist/index.mjs +3059 -0
- package/dist/json-codec-BFLXlg3K.mjs +70 -0
- package/dist/stdio-transport.d.mts +8 -0
- package/dist/stdio-transport.mjs +45 -0
- package/dist/transport-BkAbPvRc.d.mts +533 -0
- package/dist/websocket-transport-D6zJ4PLC.d.mts +88 -0
- package/dist/websocket-transport-o6r-jV5p.mjs +473 -0
- package/package.json +53 -0
|
@@ -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
|
+
}
|