@theokit/agents 7.3.1 → 7.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{agent-handle-tgyu8J7q.d.ts → agent-handle-BX4oFqfb.d.ts} +10 -10
- package/dist/auth.d.ts +68 -66
- package/dist/auth.js +106 -98
- package/dist/auth.js.map +1 -1
- package/dist/{bridge-entry-ofFpOe-j.d.ts → bridge-entry-Z8GjrwH4.d.ts} +85 -84
- package/dist/bridge.d.ts +1 -1
- package/dist/bridge.js +1 -2
- package/dist/{chunk-CVDPGEJF.js → chunk-FJA4QQLZ.js} +191 -246
- package/dist/chunk-FJA4QQLZ.js.map +1 -0
- package/dist/{chunk-7ECCHM4N.js → chunk-PHUNONZT.js} +523 -1053
- package/dist/chunk-PHUNONZT.js.map +1 -0
- package/dist/client-react.d.ts +1 -1
- package/dist/client-react.js +3 -9
- package/dist/client-react.js.map +1 -1
- package/dist/client.d.ts +3 -3
- package/dist/client.js +1 -2
- package/dist/index.d.ts +82 -50
- package/dist/index.js +161 -394
- package/dist/index.js.map +1 -1
- package/dist/interactive.js +6 -3
- package/dist/interactive.js.map +1 -1
- package/dist/persistence.js +0 -2
- package/dist/persistence.js.map +1 -1
- package/dist/pty.js +7 -3
- package/dist/pty.js.map +1 -1
- package/dist/sandbox.js +26 -3
- package/dist/sandbox.js.map +1 -1
- package/dist/testing.js +3 -21
- package/dist/testing.js.map +1 -1
- package/dist/tools.js +55 -3
- package/dist/tools.js.map +1 -1
- package/package.json +12 -12
- package/LICENSE +0 -201
- package/dist/chunk-7ECCHM4N.js.map +0 -1
- package/dist/chunk-CVDPGEJF.js.map +0 -1
- package/dist/chunk-Z4QWC7IK.js +0 -7
- package/dist/chunk-Z4QWC7IK.js.map +0 -1
|
@@ -1,31 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
__name
|
|
3
|
-
} from "./chunk-Z4QWC7IK.js";
|
|
4
|
-
|
|
5
1
|
// src/client/consume-ui-message-stream.ts
|
|
6
2
|
import { parseWireStream, readMessageStream } from "@theokit/presenter/wire";
|
|
7
3
|
async function consumeUIMessageStream(response, onMessage) {
|
|
8
4
|
const chunkStream = await responseToChunkStream(response);
|
|
9
5
|
await consumeChunkStream(chunkStream, onMessage);
|
|
10
6
|
}
|
|
11
|
-
__name(consumeUIMessageStream, "consumeUIMessageStream");
|
|
12
7
|
function responseToChunkStream(response) {
|
|
13
8
|
if (response.body === null) {
|
|
14
|
-
return Promise.resolve(
|
|
15
|
-
|
|
16
|
-
controller
|
|
17
|
-
|
|
18
|
-
|
|
9
|
+
return Promise.resolve(
|
|
10
|
+
new ReadableStream({
|
|
11
|
+
start(controller) {
|
|
12
|
+
controller.close();
|
|
13
|
+
}
|
|
14
|
+
})
|
|
15
|
+
);
|
|
19
16
|
}
|
|
20
17
|
return Promise.resolve(parseWireStream(response.body));
|
|
21
18
|
}
|
|
22
|
-
__name(responseToChunkStream, "responseToChunkStream");
|
|
23
19
|
async function consumeChunkStream(stream, onMessage) {
|
|
24
20
|
for await (const message of readMessageStream(stream)) {
|
|
25
21
|
onMessage(message);
|
|
26
22
|
}
|
|
27
23
|
}
|
|
28
|
-
__name(consumeChunkStream, "consumeChunkStream");
|
|
29
24
|
|
|
30
25
|
// src/client/http-transport.ts
|
|
31
26
|
function toRecord(headers) {
|
|
@@ -33,11 +28,7 @@ function toRecord(headers) {
|
|
|
33
28
|
if (headers instanceof Headers) return Object.fromEntries(headers.entries());
|
|
34
29
|
return headers;
|
|
35
30
|
}
|
|
36
|
-
__name(toRecord, "toRecord");
|
|
37
31
|
var HttpTransport = class {
|
|
38
|
-
static {
|
|
39
|
-
__name(this, "HttpTransport");
|
|
40
|
-
}
|
|
41
32
|
#api;
|
|
42
33
|
#headers;
|
|
43
34
|
#fetch;
|
|
@@ -68,15 +59,13 @@ var HttpTransport = class {
|
|
|
68
59
|
// conversation (SDK history + session-scoped tools like `todolist`) persists across turns instead of
|
|
69
60
|
// resetting on a fresh random session each request. Placed last so a session is never shadowed by an
|
|
70
61
|
// `id` field inside the typed input. Undefined chatId ⇒ key omitted ⇒ server falls back (unchanged).
|
|
71
|
-
body: JSON.stringify({
|
|
72
|
-
...extra,
|
|
73
|
-
messages,
|
|
74
|
-
id: chatId
|
|
75
|
-
}),
|
|
62
|
+
body: JSON.stringify({ ...extra, messages, id: chatId }),
|
|
76
63
|
signal: abortSignal
|
|
77
64
|
});
|
|
78
65
|
if (!response.ok) {
|
|
79
|
-
throw new Error(
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Agent request to ${this.#api} failed: ${response.status} ${response.statusText}`
|
|
68
|
+
);
|
|
80
69
|
}
|
|
81
70
|
this.#lastRunId = response.headers.get("x-theokit-run-id") ?? void 0;
|
|
82
71
|
return responseToChunkStream(response);
|
|
@@ -85,14 +74,13 @@ var HttpTransport = class {
|
|
|
85
74
|
if (this.#lastRunId === void 0) return null;
|
|
86
75
|
const response = await this.#fetch(`${this.#api}/runs/${this.#lastRunId}/stream`, {
|
|
87
76
|
method: "GET",
|
|
88
|
-
headers: {
|
|
89
|
-
...this.#resolveHeaders(),
|
|
90
|
-
...toRecord(options.headers)
|
|
91
|
-
}
|
|
77
|
+
headers: { ...this.#resolveHeaders(), ...toRecord(options.headers) }
|
|
92
78
|
});
|
|
93
79
|
if (response.status === 404) return null;
|
|
94
80
|
if (!response.ok) {
|
|
95
|
-
throw new Error(
|
|
81
|
+
throw new Error(
|
|
82
|
+
`Agent reconnect to run ${this.#lastRunId} failed: ${response.status} ${response.statusText}`
|
|
83
|
+
);
|
|
96
84
|
}
|
|
97
85
|
return responseToChunkStream(response);
|
|
98
86
|
}
|
|
@@ -122,7 +110,6 @@ function extractLastUserText(messages) {
|
|
|
122
110
|
}
|
|
123
111
|
return "";
|
|
124
112
|
}
|
|
125
|
-
__name(extractLastUserText, "extractLastUserText");
|
|
126
113
|
|
|
127
114
|
// src/client/in-process-transport.ts
|
|
128
115
|
function generatorToStream(gen) {
|
|
@@ -144,101 +131,93 @@ function generatorToStream(gen) {
|
|
|
144
131
|
}
|
|
145
132
|
});
|
|
146
133
|
}
|
|
147
|
-
__name(generatorToStream, "generatorToStream");
|
|
148
134
|
var ApprovalAbortedError = class extends Error {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
approvalId;
|
|
153
|
-
constructor(approvalId, motivo) {
|
|
154
|
-
super(`Aprova\xE7\xE3o '${approvalId}' descartada: ${motivo}.`), this.approvalId = approvalId;
|
|
135
|
+
constructor(approvalId, reason) {
|
|
136
|
+
super(`Approval '${approvalId}' discarded: ${reason}.`);
|
|
137
|
+
this.approvalId = approvalId;
|
|
155
138
|
this.name = "ApprovalAbortedError";
|
|
156
139
|
}
|
|
140
|
+
approvalId;
|
|
157
141
|
};
|
|
158
142
|
var InProcessTransport = class {
|
|
159
|
-
static {
|
|
160
|
-
__name(this, "InProcessTransport");
|
|
161
|
-
}
|
|
162
143
|
#run;
|
|
163
144
|
/**
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
145
|
+
* Parked inline approvals: `approvalId` → how to resolve **or reject** the stalled promise.
|
|
146
|
+
*
|
|
147
|
+
* M92 — `reject` arrived together with the eviction. Before there was only `resolve`, and nothing
|
|
148
|
+
* erased the entry when the turn aborted: the promise stayed pending **forever**, and the SDK tool
|
|
149
|
+
* call hung with it. A promise that neither resolves **nor** rejects is the quietest way to swallow
|
|
150
|
+
* an error — not even a stack trace exists (`error-handling.md § 2`).
|
|
151
|
+
*/
|
|
171
152
|
#pending = /* @__PURE__ */ new Map();
|
|
172
|
-
/**
|
|
173
|
-
#
|
|
153
|
+
/** The current turn. A new `send()` increments it and sweeps the previous one. */
|
|
154
|
+
#turn = 0;
|
|
174
155
|
constructor(options) {
|
|
175
156
|
this.#run = options.run;
|
|
176
157
|
}
|
|
177
158
|
/**
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
#
|
|
159
|
+
* Creates the `awaitApproval` for THIS turn, with the number and the signal closed over.
|
|
160
|
+
*
|
|
161
|
+
* A shared field will not do, and the M92 review measured why: a turn-1 runner that parks **after**
|
|
162
|
+
* turn 2's `send` reads the already-overwritten field and is born labelled turn 2 — turn 1's abort
|
|
163
|
+
* does not sweep it, and the promise hangs. M92's first fix swapped "read at approval time" for
|
|
164
|
+
* "read at `send`", and stayed wrong for the same reason: a single field.
|
|
165
|
+
*
|
|
166
|
+
* The closure is the only place a runner's turn can live without another one overwriting it.
|
|
167
|
+
*/
|
|
168
|
+
#createAwaitApproval(turn, signal) {
|
|
188
169
|
return (req) => new Promise((resolve, reject) => {
|
|
189
|
-
if (
|
|
190
|
-
reject(new ApprovalAbortedError(req.approvalId, "
|
|
170
|
+
if (signal?.aborted === true) {
|
|
171
|
+
reject(new ApprovalAbortedError(req.approvalId, "the turn was already aborted"));
|
|
191
172
|
return;
|
|
192
173
|
}
|
|
193
174
|
if (this.#pending.has(req.approvalId)) {
|
|
194
|
-
reject(
|
|
175
|
+
reject(
|
|
176
|
+
new Error(`Duplicate pending approval id '${req.approvalId}' \u2014 ids must be unique.`)
|
|
177
|
+
);
|
|
195
178
|
return;
|
|
196
179
|
}
|
|
197
|
-
this.#pending.set(req.approvalId, {
|
|
198
|
-
resolve,
|
|
199
|
-
reject,
|
|
200
|
-
turno
|
|
201
|
-
});
|
|
180
|
+
this.#pending.set(req.approvalId, { resolve, reject, turn });
|
|
202
181
|
});
|
|
203
182
|
}
|
|
204
183
|
/**
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
#
|
|
212
|
-
for (const [id,
|
|
213
|
-
|
|
214
|
-
]) {
|
|
215
|
-
if (entrada.turno !== turno) continue;
|
|
184
|
+
* Sweeps a turn's approvals, rejecting each one with a TYPED error.
|
|
185
|
+
*
|
|
186
|
+
* Reject rather than `resolve(false)`: a `false` is indistinguishable from *"the user denied"*, and
|
|
187
|
+
* the difference matters — denying is a decision, aborting is an interruption. The SDK needs both to
|
|
188
|
+
* unwind the tool call correctly.
|
|
189
|
+
*/
|
|
190
|
+
#sweepTurn(turn, reason) {
|
|
191
|
+
for (const [id, entry] of [...this.#pending]) {
|
|
192
|
+
if (entry.turn !== turn) continue;
|
|
216
193
|
this.#pending.delete(id);
|
|
217
|
-
|
|
194
|
+
entry.reject(new ApprovalAbortedError(id, reason));
|
|
218
195
|
}
|
|
219
196
|
}
|
|
220
|
-
/**
|
|
221
|
-
get
|
|
197
|
+
/** How many approvals are parked. Exists so the test can prove the eviction. */
|
|
198
|
+
get pending() {
|
|
222
199
|
return this.#pending.size;
|
|
223
200
|
}
|
|
224
201
|
sendMessages(options) {
|
|
225
202
|
const { messages, abortSignal, metadata } = options;
|
|
226
|
-
this.#
|
|
227
|
-
this.#
|
|
228
|
-
const
|
|
203
|
+
this.#sweepTurn(this.#turn, "a new turn started");
|
|
204
|
+
this.#turn += 1;
|
|
205
|
+
const currentTurn = this.#turn;
|
|
229
206
|
if (abortSignal?.aborted === true) {
|
|
230
|
-
this.#
|
|
207
|
+
this.#sweepTurn(currentTurn, "the turn was already aborted");
|
|
231
208
|
} else {
|
|
232
|
-
abortSignal?.addEventListener(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
209
|
+
abortSignal?.addEventListener(
|
|
210
|
+
"abort",
|
|
211
|
+
() => {
|
|
212
|
+
this.#sweepTurn(currentTurn, "the turn was aborted");
|
|
213
|
+
},
|
|
214
|
+
{ once: true }
|
|
215
|
+
);
|
|
237
216
|
}
|
|
238
217
|
const generator = this.#run({
|
|
239
218
|
message: extractLastUserText(messages),
|
|
240
219
|
signal: abortSignal ?? void 0,
|
|
241
|
-
awaitApproval: this.#
|
|
220
|
+
awaitApproval: this.#createAwaitApproval(currentTurn, abortSignal ?? void 0),
|
|
242
221
|
// M43 — forward per-request context (the seam's `metadata`) to the runner.
|
|
243
222
|
context: metadata
|
|
244
223
|
});
|
|
@@ -248,21 +227,20 @@ var InProcessTransport = class {
|
|
|
248
227
|
return Promise.resolve(null);
|
|
249
228
|
}
|
|
250
229
|
approve(approvalId, decision) {
|
|
251
|
-
const
|
|
252
|
-
if (
|
|
253
|
-
return Promise.reject(
|
|
230
|
+
const entry = this.#pending.get(approvalId);
|
|
231
|
+
if (entry === void 0) {
|
|
232
|
+
return Promise.reject(
|
|
233
|
+
new Error(`No pending approval '${approvalId}' (unknown or already settled).`)
|
|
234
|
+
);
|
|
254
235
|
}
|
|
255
236
|
this.#pending.delete(approvalId);
|
|
256
|
-
|
|
237
|
+
entry.resolve(decision);
|
|
257
238
|
return Promise.resolve();
|
|
258
239
|
}
|
|
259
240
|
};
|
|
260
241
|
|
|
261
242
|
// src/client/channel-transport.ts
|
|
262
243
|
var ChannelTransport = class {
|
|
263
|
-
static {
|
|
264
|
-
__name(this, "ChannelTransport");
|
|
265
|
-
}
|
|
266
244
|
#source;
|
|
267
245
|
constructor(options) {
|
|
268
246
|
this.#source = options.source;
|
|
@@ -272,57 +250,57 @@ var ChannelTransport = class {
|
|
|
272
250
|
const message = extractLastUserText(messages);
|
|
273
251
|
const source = this.#source;
|
|
274
252
|
let closed = false;
|
|
275
|
-
let teardown =
|
|
276
|
-
let detachAbort =
|
|
253
|
+
let teardown = () => void 0;
|
|
254
|
+
let detachAbort = () => void 0;
|
|
277
255
|
const stream = new ReadableStream({
|
|
278
256
|
start(controller) {
|
|
279
|
-
const finish =
|
|
257
|
+
const finish = (settle) => {
|
|
280
258
|
if (closed) return;
|
|
281
259
|
closed = true;
|
|
282
260
|
detachAbort();
|
|
283
261
|
settle();
|
|
284
|
-
}
|
|
285
|
-
teardown = source.start(
|
|
286
|
-
message,
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
262
|
+
};
|
|
263
|
+
teardown = source.start(
|
|
264
|
+
{ message, context: metadata },
|
|
265
|
+
{
|
|
266
|
+
onLine: (line) => {
|
|
267
|
+
if (closed) return;
|
|
268
|
+
let parsed;
|
|
269
|
+
try {
|
|
270
|
+
parsed = JSON.parse(line);
|
|
271
|
+
} catch {
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (typeof parsed !== "object" || parsed === null || typeof parsed.type !== "string") {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
controller.enqueue(parsed);
|
|
278
|
+
},
|
|
279
|
+
onClose: () => {
|
|
280
|
+
finish(() => {
|
|
281
|
+
controller.close();
|
|
282
|
+
});
|
|
283
|
+
},
|
|
284
|
+
onError: (err) => {
|
|
285
|
+
finish(() => {
|
|
286
|
+
controller.error(err);
|
|
287
|
+
});
|
|
299
288
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
onClose: /* @__PURE__ */ __name(() => {
|
|
303
|
-
finish(() => {
|
|
304
|
-
controller.close();
|
|
305
|
-
});
|
|
306
|
-
}, "onClose"),
|
|
307
|
-
onError: /* @__PURE__ */ __name((err) => {
|
|
308
|
-
finish(() => {
|
|
309
|
-
controller.error(err);
|
|
310
|
-
});
|
|
311
|
-
}, "onError")
|
|
312
|
-
});
|
|
289
|
+
}
|
|
290
|
+
);
|
|
313
291
|
if (abortSignal !== void 0) {
|
|
314
|
-
const onAbort =
|
|
292
|
+
const onAbort = () => {
|
|
315
293
|
finish(() => {
|
|
316
294
|
teardown();
|
|
317
295
|
controller.close();
|
|
318
296
|
});
|
|
319
|
-
}
|
|
297
|
+
};
|
|
320
298
|
if (abortSignal.aborted) onAbort();
|
|
321
299
|
else {
|
|
322
300
|
abortSignal.addEventListener("abort", onAbort);
|
|
323
|
-
detachAbort =
|
|
301
|
+
detachAbort = () => {
|
|
324
302
|
abortSignal.removeEventListener("abort", onAbort);
|
|
325
|
-
}
|
|
303
|
+
};
|
|
326
304
|
}
|
|
327
305
|
}
|
|
328
306
|
},
|
|
@@ -354,24 +332,14 @@ function inputToText(input) {
|
|
|
354
332
|
if (typeof input === "string") return input;
|
|
355
333
|
return JSON.stringify(input);
|
|
356
334
|
}
|
|
357
|
-
__name(inputToText, "inputToText");
|
|
358
335
|
function buildUserMessage(input) {
|
|
359
336
|
return {
|
|
360
337
|
id: crypto.randomUUID(),
|
|
361
338
|
role: "user",
|
|
362
|
-
parts: [
|
|
363
|
-
{
|
|
364
|
-
type: "text",
|
|
365
|
-
text: inputToText(input)
|
|
366
|
-
}
|
|
367
|
-
]
|
|
339
|
+
parts: [{ type: "text", text: inputToText(input) }]
|
|
368
340
|
};
|
|
369
341
|
}
|
|
370
|
-
__name(buildUserMessage, "buildUserMessage");
|
|
371
342
|
var AgentClient = class {
|
|
372
|
-
static {
|
|
373
|
-
__name(this, "AgentClient");
|
|
374
|
-
}
|
|
375
343
|
#transport;
|
|
376
344
|
#chatId = crypto.randomUUID();
|
|
377
345
|
#listeners = /* @__PURE__ */ new Set();
|
|
@@ -388,29 +356,25 @@ var AgentClient = class {
|
|
|
388
356
|
#currentUser;
|
|
389
357
|
/** A stable id for the current turn's assistant (the SDK leaves it empty — we fabricate one). */
|
|
390
358
|
#currentAssistantId = "";
|
|
391
|
-
#snapshot = {
|
|
392
|
-
messages: [],
|
|
393
|
-
thread: [],
|
|
394
|
-
status: "idle",
|
|
395
|
-
error: void 0
|
|
396
|
-
};
|
|
359
|
+
#snapshot = { messages: [], thread: [], status: "idle", error: void 0 };
|
|
397
360
|
/**
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
361
|
+
* M92 — the committed prefix, materialized ONCE per write instead of once per token delta.
|
|
362
|
+
*
|
|
363
|
+
* `#committed` changes in only two places (measured): in `send()`'s `done` and in `reset()`. Between
|
|
364
|
+
* deltas it is constant, so rebuilding it on every `#emit` is work the structure already guarantees
|
|
365
|
+
* is useless.
|
|
366
|
+
*
|
|
367
|
+
* Invalidation happens **on write**, not by comparison: comparing would cost the same O(C) this
|
|
368
|
+
* avoids, and memoizing by length would be wrong in `reset()` — equal length with different content
|
|
369
|
+
* is possible, and the bug would be invisible.
|
|
370
|
+
*
|
|
371
|
+
* Honesty about the size of the win: measured, the spread costs **0.0062 ms per delta @400 messages**
|
|
372
|
+
* — 3.1 ms across a 500-delta turn. It is real and it is micro. The order of magnitude of this
|
|
373
|
+
* milestone is in the coalescing below, because what hangs off each emit (deriving the timeline)
|
|
374
|
+
* costs **3.274 ms per call** at the same thread size (M86).
|
|
375
|
+
*/
|
|
376
|
+
#committedPrefix = [];
|
|
377
|
+
/** Opt-in coalescing: `0` (the default) emits per delta, as always. */
|
|
414
378
|
#emitIntervalMs;
|
|
415
379
|
#timerDeEmit;
|
|
416
380
|
constructor(transport, contextResolver, options) {
|
|
@@ -419,47 +383,39 @@ var AgentClient = class {
|
|
|
419
383
|
this.#emitIntervalMs = options?.emitIntervalMs ?? 0;
|
|
420
384
|
}
|
|
421
385
|
/** Subscribe to state changes; returns an unsubscribe fn. */
|
|
422
|
-
subscribe =
|
|
386
|
+
subscribe = (listener) => {
|
|
423
387
|
this.#listeners.add(listener);
|
|
424
388
|
return () => {
|
|
425
389
|
this.#listeners.delete(listener);
|
|
426
390
|
};
|
|
427
|
-
}
|
|
391
|
+
};
|
|
428
392
|
/** The current immutable snapshot (stable reference until the next emit). */
|
|
429
|
-
getSnapshot =
|
|
393
|
+
getSnapshot = () => this.#snapshot;
|
|
430
394
|
/**
|
|
431
|
-
|
|
432
|
-
|
|
395
|
+
* Emits NOW. Used directly on status transitions — see `#scheduleEmit`.
|
|
396
|
+
*/
|
|
433
397
|
#emit() {
|
|
434
398
|
if (this.#timerDeEmit !== void 0) {
|
|
435
399
|
clearTimeout(this.#timerDeEmit);
|
|
436
400
|
this.#timerDeEmit = void 0;
|
|
437
401
|
}
|
|
438
|
-
const
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
] : this.#messages;
|
|
442
|
-
const thread = this.#prefixo.concat(cauda);
|
|
443
|
-
this.#snapshot = {
|
|
444
|
-
messages: this.#messages,
|
|
445
|
-
thread,
|
|
446
|
-
status: this.#status,
|
|
447
|
-
error: this.#error
|
|
448
|
-
};
|
|
402
|
+
const tail = this.#currentUser ? [this.#currentUser, ...this.#messages] : this.#messages;
|
|
403
|
+
const thread = this.#committedPrefix.concat(tail);
|
|
404
|
+
this.#snapshot = { messages: this.#messages, thread, status: this.#status, error: this.#error };
|
|
449
405
|
for (const listener of this.#listeners) listener();
|
|
450
406
|
}
|
|
451
407
|
/**
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
#
|
|
408
|
+
* Emits per WINDOW when coalescing is on; immediately when it is off.
|
|
409
|
+
*
|
|
410
|
+
* Trailing edge: the timer emits at the **end** of the window, with the most recent state. What this
|
|
411
|
+
* buys is not a cheaper emit — it is **fewer emits**, and what hangs off each one is the 3.274 ms
|
|
412
|
+
* derivation measured in M86.
|
|
413
|
+
*
|
|
414
|
+
* Status transitions (`done`/`error`/`abort`) do NOT go through here: they call `#emit` directly,
|
|
415
|
+
* because a final state trapped in a 16 ms timer is a final state lost if the process exits first —
|
|
416
|
+
* and `exec` exits right after the turn.
|
|
417
|
+
*/
|
|
418
|
+
#scheduleEmit() {
|
|
463
419
|
if (this.#emitIntervalMs <= 0) {
|
|
464
420
|
this.#emit();
|
|
465
421
|
return;
|
|
@@ -471,16 +427,14 @@ var AgentClient = class {
|
|
|
471
427
|
}, this.#emitIntervalMs);
|
|
472
428
|
}
|
|
473
429
|
#upsert(message) {
|
|
474
|
-
const next = [
|
|
475
|
-
...this.#messages
|
|
476
|
-
];
|
|
430
|
+
const next = [...this.#messages];
|
|
477
431
|
const idx = next.findIndex((existing) => existing.id === message.id);
|
|
478
432
|
if (idx >= 0) next[idx] = message;
|
|
479
433
|
else next.push(message);
|
|
480
434
|
this.#messages = next;
|
|
481
435
|
}
|
|
482
436
|
async #drive(open, controller) {
|
|
483
|
-
const aborted =
|
|
437
|
+
const aborted = () => controller.signal.aborted;
|
|
484
438
|
try {
|
|
485
439
|
const stream = await open();
|
|
486
440
|
if (aborted()) return;
|
|
@@ -491,12 +445,9 @@ var AgentClient = class {
|
|
|
491
445
|
}
|
|
492
446
|
await consumeChunkStream(stream, (message) => {
|
|
493
447
|
if (aborted()) return;
|
|
494
|
-
const stamped = message.id ? message : {
|
|
495
|
-
...message,
|
|
496
|
-
id: this.#currentAssistantId
|
|
497
|
-
};
|
|
448
|
+
const stamped = message.id ? message : { ...message, id: this.#currentAssistantId };
|
|
498
449
|
this.#upsert(stamped);
|
|
499
|
-
this.#
|
|
450
|
+
this.#scheduleEmit();
|
|
500
451
|
});
|
|
501
452
|
if (aborted()) return;
|
|
502
453
|
this.#status = "done";
|
|
@@ -509,14 +460,10 @@ var AgentClient = class {
|
|
|
509
460
|
}
|
|
510
461
|
}
|
|
511
462
|
/** Send a typed input; opens a fresh stream (replaces prior messages). */
|
|
512
|
-
send =
|
|
463
|
+
send = (input) => {
|
|
513
464
|
if (this.#status === "done" && this.#currentUser) {
|
|
514
|
-
this.#committed = [
|
|
515
|
-
|
|
516
|
-
this.#currentUser,
|
|
517
|
-
...this.#messages
|
|
518
|
-
];
|
|
519
|
-
this.#prefixo = this.#committed;
|
|
465
|
+
this.#committed = [...this.#committed, this.#currentUser, ...this.#messages];
|
|
466
|
+
this.#committedPrefix = this.#committed;
|
|
520
467
|
}
|
|
521
468
|
this.abort();
|
|
522
469
|
const controller = new AbortController();
|
|
@@ -529,24 +476,25 @@ var AgentClient = class {
|
|
|
529
476
|
this.#status = "streaming";
|
|
530
477
|
this.#emit();
|
|
531
478
|
const context = this.#contextResolver?.();
|
|
532
|
-
void this.#drive(
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
userMsg
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
479
|
+
void this.#drive(
|
|
480
|
+
() => this.#transport.sendMessages({
|
|
481
|
+
trigger: "submit-message",
|
|
482
|
+
chatId: this.#chatId,
|
|
483
|
+
messageId: void 0,
|
|
484
|
+
messages: [userMsg],
|
|
485
|
+
abortSignal: controller.signal,
|
|
486
|
+
// Only object inputs flow as the request `body` (the turn text is always in `messages`);
|
|
487
|
+
// a primitive input is carried by the user message, never spread into the body.
|
|
488
|
+
body: typeof input === "object" && input !== null ? input : void 0,
|
|
489
|
+
// M43 — per-request context reaches every transport (headers → HTTP, metadata → in-process/channel).
|
|
490
|
+
headers: context?.headers,
|
|
491
|
+
metadata: context?.metadata
|
|
492
|
+
}),
|
|
493
|
+
controller
|
|
494
|
+
);
|
|
495
|
+
};
|
|
548
496
|
/** Resume an interrupted stream via the transport's `reconnectToStream` (no-op when unavailable). */
|
|
549
|
-
reconnect =
|
|
497
|
+
reconnect = () => {
|
|
550
498
|
const controller = new AbortController();
|
|
551
499
|
this.#controller = controller;
|
|
552
500
|
if (!this.#currentAssistantId) this.#currentAssistantId = crypto.randomUUID();
|
|
@@ -554,55 +502,52 @@ var AgentClient = class {
|
|
|
554
502
|
this.#status = "streaming";
|
|
555
503
|
this.#emit();
|
|
556
504
|
const context = this.#contextResolver?.();
|
|
557
|
-
void this.#drive(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
505
|
+
void this.#drive(
|
|
506
|
+
() => this.#transport.reconnectToStream({
|
|
507
|
+
chatId: this.#chatId,
|
|
508
|
+
headers: context?.headers,
|
|
509
|
+
metadata: context?.metadata
|
|
510
|
+
}),
|
|
511
|
+
controller
|
|
512
|
+
);
|
|
513
|
+
};
|
|
563
514
|
/** Abort an in-flight stream (not an error — leaves messages as-is). */
|
|
564
|
-
abort =
|
|
515
|
+
abort = () => {
|
|
565
516
|
this.#controller?.abort();
|
|
566
517
|
this.#controller = null;
|
|
567
518
|
if (this.#status === "streaming") {
|
|
568
519
|
this.#status = this.#committed.length > 0 || this.#messages.length > 0 ? "done" : "idle";
|
|
569
520
|
this.#emit();
|
|
570
521
|
}
|
|
571
|
-
}
|
|
522
|
+
};
|
|
572
523
|
/** Clear messages + error, back to idle. */
|
|
573
|
-
reset =
|
|
524
|
+
reset = () => {
|
|
574
525
|
this.abort();
|
|
575
526
|
this.#messages = [];
|
|
576
527
|
this.#committed = [];
|
|
577
|
-
this.#
|
|
528
|
+
this.#committedPrefix = this.#committed;
|
|
578
529
|
this.#currentUser = void 0;
|
|
579
530
|
this.#error = void 0;
|
|
580
531
|
this.#status = "idle";
|
|
581
532
|
this.#emit();
|
|
582
|
-
}
|
|
533
|
+
};
|
|
583
534
|
/** Settle a paused HITL approval via the transport's HITL path (HTTP POST or inline callback). */
|
|
584
|
-
approve =
|
|
535
|
+
approve = async (approvalId, decision) => {
|
|
585
536
|
await this.#transport.approve?.(approvalId, decision);
|
|
586
|
-
}
|
|
537
|
+
};
|
|
587
538
|
};
|
|
588
539
|
|
|
589
540
|
// src/client/agent-handle.ts
|
|
590
541
|
function agentHandle(path) {
|
|
591
542
|
return {
|
|
592
543
|
path,
|
|
593
|
-
inProcess:
|
|
594
|
-
|
|
595
|
-
}), "inProcess"),
|
|
596
|
-
channel: /* @__PURE__ */ __name((source) => new ChannelTransport({
|
|
597
|
-
source
|
|
598
|
-
}), "channel")
|
|
544
|
+
inProcess: (run) => new InProcessTransport({ run }),
|
|
545
|
+
channel: (source) => new ChannelTransport({ source })
|
|
599
546
|
};
|
|
600
547
|
}
|
|
601
|
-
__name(agentHandle, "agentHandle");
|
|
602
548
|
function isAgentHandle(value) {
|
|
603
549
|
return typeof value === "object" && value !== null && typeof value.path === "string" && typeof value.sendMessages !== "function";
|
|
604
550
|
}
|
|
605
|
-
__name(isAgentHandle, "isAgentHandle");
|
|
606
551
|
|
|
607
552
|
export {
|
|
608
553
|
consumeUIMessageStream,
|
|
@@ -617,4 +562,4 @@ export {
|
|
|
617
562
|
agentHandle,
|
|
618
563
|
isAgentHandle
|
|
619
564
|
};
|
|
620
|
-
//# sourceMappingURL=chunk-
|
|
565
|
+
//# sourceMappingURL=chunk-FJA4QQLZ.js.map
|