@pstdio/pocketcoder-remote 0.3.1 → 0.3.3
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/bin.js +3 -2
- package/dist/extension.js +191 -168
- package/package.json +2 -2
package/dist/bin.js
CHANGED
|
@@ -3,7 +3,7 @@ import { spawn } from "node:child_process";
|
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { dirname, resolve, sep } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
|
-
//#region src/launch.ts
|
|
6
|
+
//#region src/session/launch.ts
|
|
7
7
|
const PI_FLAGS = [
|
|
8
8
|
"--provider",
|
|
9
9
|
"pocketcoder-agentapi",
|
|
@@ -30,7 +30,8 @@ function piBinPath(resolvePath) {
|
|
|
30
30
|
return resolve(packageRoot, bin);
|
|
31
31
|
}
|
|
32
32
|
function extensionPath(moduleDir) {
|
|
33
|
-
|
|
33
|
+
if (moduleDir.endsWith(`${sep}src${sep}session`)) return resolve(moduleDir, "../extension.ts");
|
|
34
|
+
return resolve(moduleDir, "extension.js");
|
|
34
35
|
}
|
|
35
36
|
function resolvePiInvocation(options = {}) {
|
|
36
37
|
const env = options.env ?? process.env;
|
package/dist/extension.js
CHANGED
|
@@ -2,9 +2,9 @@ import { readFileSync, statSync } from "node:fs";
|
|
|
2
2
|
import { basename, extname, isAbsolute, resolve } from "node:path";
|
|
3
3
|
import { createAssistantMessageEventStream } from "@earendil-works/pi-ai";
|
|
4
4
|
import { ConversationGoneError, PocketCoderClient, PocketCoderError, TERMINAL_WORKSPACE_STATES, WorkspaceTerminalError, WorkspaceTurnResolutionError, isPocketCoderErrorCode, splitAttachmentManifest } from "@pstdio/pocketcoder-sdk";
|
|
5
|
-
import { randomUUID } from "node:crypto";
|
|
6
5
|
import { Text } from "@earendil-works/pi-tui";
|
|
7
|
-
|
|
6
|
+
import { randomUUID } from "node:crypto";
|
|
7
|
+
//#region src/client/control-plane.ts
|
|
8
8
|
/** @internal The remote UI keeps its historical config vocabulary at its boundary. */
|
|
9
9
|
var ControlPlaneClient = class extends PocketCoderClient {
|
|
10
10
|
constructor(config, fetchImpl = fetch) {
|
|
@@ -15,7 +15,7 @@ var ControlPlaneClient = class extends PocketCoderClient {
|
|
|
15
15
|
}
|
|
16
16
|
};
|
|
17
17
|
//#endregion
|
|
18
|
-
//#region src/remote-request-error.ts
|
|
18
|
+
//#region src/client/remote-request-error.ts
|
|
19
19
|
var RemoteRequestError = class extends Error {
|
|
20
20
|
phase;
|
|
21
21
|
promptAccepted;
|
|
@@ -73,7 +73,7 @@ async function remoteResponseError(response, phase, promptAccepted) {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
//#endregion
|
|
76
|
-
//#region src/attachments.ts
|
|
76
|
+
//#region src/attachments/attachments.ts
|
|
77
77
|
const DIRECT_MODE_ATTACHMENT_ERROR = "file attachments need the PocketCoder workspace API; a direct AgentAPI URL (POCKETCODER_AGENTAPI_URL) cannot accept managed uploads";
|
|
78
78
|
const IMAGE_EXTENSIONS = {
|
|
79
79
|
"image/gif": "gif",
|
|
@@ -221,7 +221,119 @@ function registerAttachCommand(pi, deps) {
|
|
|
221
221
|
});
|
|
222
222
|
}
|
|
223
223
|
//#endregion
|
|
224
|
-
//#region src/
|
|
224
|
+
//#region src/ui/renderers.ts
|
|
225
|
+
const HISTORY_ENTRY_TYPE = "pocketcoder-conversation";
|
|
226
|
+
const NOTICE_ENTRY_TYPE = "pocketcoder-history-notice";
|
|
227
|
+
function roleHeader(data, theme) {
|
|
228
|
+
const time = data.occurred_at.replace("T", " ").replace(/\.\d+Z?$|Z$/, "");
|
|
229
|
+
return theme.fg("muted", `${data.role} · ${time}`);
|
|
230
|
+
}
|
|
231
|
+
function userBody(content, theme) {
|
|
232
|
+
const { text, attachments } = splitAttachmentManifest(content);
|
|
233
|
+
const body = theme.fg("userMessageText", text);
|
|
234
|
+
if (!attachments) return body;
|
|
235
|
+
return `${body}\n${attachments.map((attachment) => theme.fg("muted", `⌁ ${attachment.name} (${attachment.size_bytes} bytes)`)).join("\n")}`;
|
|
236
|
+
}
|
|
237
|
+
function formatConversationMessage(data, theme) {
|
|
238
|
+
switch (data.kind ?? "text") {
|
|
239
|
+
default: {
|
|
240
|
+
const header = roleHeader(data, theme);
|
|
241
|
+
let body = data.content;
|
|
242
|
+
if (data.role === "user") body = userBody(data.content, theme);
|
|
243
|
+
else if (data.role !== "assistant") body = theme.fg("dim", data.content);
|
|
244
|
+
return `${header}\n${body}`;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function formatHistoryNotice(data, theme) {
|
|
249
|
+
return theme.fg(data.level === "warning" ? "warning" : "muted", data.text);
|
|
250
|
+
}
|
|
251
|
+
function registerConversationRenderers(pi) {
|
|
252
|
+
pi.registerEntryRenderer(HISTORY_ENTRY_TYPE, (entry, _options, theme) => {
|
|
253
|
+
if (!entry.data) return void 0;
|
|
254
|
+
return new Text(formatConversationMessage(entry.data, theme), 1, 0);
|
|
255
|
+
});
|
|
256
|
+
pi.registerEntryRenderer(NOTICE_ENTRY_TYPE, (entry, _options, theme) => {
|
|
257
|
+
if (!entry.data) return void 0;
|
|
258
|
+
return new Text(formatHistoryNotice(entry.data, theme), 1, 0);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
//#endregion
|
|
262
|
+
//#region src/history/history.ts
|
|
263
|
+
async function collectTranscriptTail(controlPlane, workspaceId, options) {
|
|
264
|
+
const tail = [];
|
|
265
|
+
let total = 0;
|
|
266
|
+
let pages = 0;
|
|
267
|
+
let cursor;
|
|
268
|
+
while (pages < options.maxPages) {
|
|
269
|
+
const page = await controlPlane.conversations.list(workspaceId, {
|
|
270
|
+
cursor,
|
|
271
|
+
limit: options.pageLimit
|
|
272
|
+
});
|
|
273
|
+
pages += 1;
|
|
274
|
+
for (const message of page.items) {
|
|
275
|
+
total += 1;
|
|
276
|
+
tail.push({
|
|
277
|
+
role: message.role,
|
|
278
|
+
content: message.content,
|
|
279
|
+
seq: message.seq,
|
|
280
|
+
occurred_at: message.occurred_at,
|
|
281
|
+
kind: message.metadata?.kind,
|
|
282
|
+
metadata: message.metadata
|
|
283
|
+
});
|
|
284
|
+
if (tail.length > options.maxMessages) tail.shift();
|
|
285
|
+
}
|
|
286
|
+
if (page.nextCursor === null) return {
|
|
287
|
+
tail,
|
|
288
|
+
total,
|
|
289
|
+
pages,
|
|
290
|
+
exhaustedPages: false
|
|
291
|
+
};
|
|
292
|
+
cursor = page.nextCursor;
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
tail,
|
|
296
|
+
total,
|
|
297
|
+
pages,
|
|
298
|
+
exhaustedPages: true
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
async function replayHistory(pi, controlPlane, workspaceId, options = {}) {
|
|
302
|
+
let transcript;
|
|
303
|
+
try {
|
|
304
|
+
transcript = await collectTranscriptTail(controlPlane, workspaceId, {
|
|
305
|
+
pageLimit: options.pageLimit ?? 200,
|
|
306
|
+
maxMessages: options.maxMessages ?? 1e3,
|
|
307
|
+
maxPages: options.maxPages ?? 50
|
|
308
|
+
});
|
|
309
|
+
} catch (error) {
|
|
310
|
+
if (error instanceof ConversationGoneError) {
|
|
311
|
+
pi.appendEntry(NOTICE_ENTRY_TYPE, {
|
|
312
|
+
text: error.code === "conversation.deleted" ? "conversation history was deleted" : "conversation history has expired",
|
|
313
|
+
level: "warning"
|
|
314
|
+
});
|
|
315
|
+
return {
|
|
316
|
+
replayed: 0,
|
|
317
|
+
total: 0,
|
|
318
|
+
truncated: false,
|
|
319
|
+
gone: true
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
const { tail, total, pages, exhaustedPages } = transcript;
|
|
325
|
+
const truncated = total > tail.length || exhaustedPages;
|
|
326
|
+
if (truncated) pi.appendEntry(NOTICE_ENTRY_TYPE, { text: exhaustedPages ? `history partially replayed (stopped after ${pages} pages)` : `showing last ${tail.length} of ${total} messages` });
|
|
327
|
+
for (const message of tail) pi.appendEntry(HISTORY_ENTRY_TYPE, message);
|
|
328
|
+
return {
|
|
329
|
+
replayed: tail.length,
|
|
330
|
+
total,
|
|
331
|
+
truncated,
|
|
332
|
+
gone: false
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/session/session-target.ts
|
|
225
337
|
function relayTarget(baseUrl, key, workspaceId) {
|
|
226
338
|
const base = baseUrl.replace(/\/$/, "");
|
|
227
339
|
return {
|
|
@@ -277,7 +389,7 @@ var TargetRef = class {
|
|
|
277
389
|
}
|
|
278
390
|
};
|
|
279
391
|
//#endregion
|
|
280
|
-
//#region src/commands.ts
|
|
392
|
+
//#region src/session/commands.ts
|
|
281
393
|
function workspaceLabel(workspace) {
|
|
282
394
|
return `${workspace.external_id} · ${workspace.template.name} · ${workspace.id.slice(0, 8)}`;
|
|
283
395
|
}
|
|
@@ -364,113 +476,7 @@ function registerWorkspaceCommands(pi, deps) {
|
|
|
364
476
|
});
|
|
365
477
|
}
|
|
366
478
|
//#endregion
|
|
367
|
-
//#region src/
|
|
368
|
-
const HISTORY_ENTRY_TYPE = "pocketcoder-conversation";
|
|
369
|
-
const NOTICE_ENTRY_TYPE = "pocketcoder-history-notice";
|
|
370
|
-
function roleHeader(data, theme) {
|
|
371
|
-
const time = data.occurred_at.replace("T", " ").replace(/\.\d+Z?$|Z$/, "");
|
|
372
|
-
return theme.fg("muted", `${data.role} · ${time}`);
|
|
373
|
-
}
|
|
374
|
-
function userBody(content, theme) {
|
|
375
|
-
const { text, attachments } = splitAttachmentManifest(content);
|
|
376
|
-
const body = theme.fg("userMessageText", text);
|
|
377
|
-
if (!attachments) return body;
|
|
378
|
-
return `${body}\n${attachments.map((attachment) => theme.fg("muted", `⌁ ${attachment.name} (${attachment.size_bytes} bytes)`)).join("\n")}`;
|
|
379
|
-
}
|
|
380
|
-
function formatConversationMessage(data, theme) {
|
|
381
|
-
switch (data.kind ?? "text") {
|
|
382
|
-
default: return `${roleHeader(data, theme)}\n${data.role === "user" ? userBody(data.content, theme) : data.role === "assistant" ? data.content : theme.fg("dim", data.content)}`;
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
function formatHistoryNotice(data, theme) {
|
|
386
|
-
return theme.fg(data.level === "warning" ? "warning" : "muted", data.text);
|
|
387
|
-
}
|
|
388
|
-
function registerConversationRenderers(pi) {
|
|
389
|
-
pi.registerEntryRenderer(HISTORY_ENTRY_TYPE, (entry, _options, theme) => {
|
|
390
|
-
if (!entry.data) return void 0;
|
|
391
|
-
return new Text(formatConversationMessage(entry.data, theme), 1, 0);
|
|
392
|
-
});
|
|
393
|
-
pi.registerEntryRenderer(NOTICE_ENTRY_TYPE, (entry, _options, theme) => {
|
|
394
|
-
if (!entry.data) return void 0;
|
|
395
|
-
return new Text(formatHistoryNotice(entry.data, theme), 1, 0);
|
|
396
|
-
});
|
|
397
|
-
}
|
|
398
|
-
//#endregion
|
|
399
|
-
//#region src/history.ts
|
|
400
|
-
async function collectTranscriptTail(controlPlane, workspaceId, options) {
|
|
401
|
-
const tail = [];
|
|
402
|
-
let total = 0;
|
|
403
|
-
let pages = 0;
|
|
404
|
-
let cursor;
|
|
405
|
-
while (pages < options.maxPages) {
|
|
406
|
-
const page = await controlPlane.conversations.list(workspaceId, {
|
|
407
|
-
cursor,
|
|
408
|
-
limit: options.pageLimit
|
|
409
|
-
});
|
|
410
|
-
pages += 1;
|
|
411
|
-
for (const message of page.items) {
|
|
412
|
-
total += 1;
|
|
413
|
-
tail.push({
|
|
414
|
-
role: message.role,
|
|
415
|
-
content: message.content,
|
|
416
|
-
seq: message.seq,
|
|
417
|
-
occurred_at: message.occurred_at,
|
|
418
|
-
kind: message.metadata?.kind,
|
|
419
|
-
metadata: message.metadata
|
|
420
|
-
});
|
|
421
|
-
if (tail.length > options.maxMessages) tail.shift();
|
|
422
|
-
}
|
|
423
|
-
if (page.nextCursor === null) return {
|
|
424
|
-
tail,
|
|
425
|
-
total,
|
|
426
|
-
pages,
|
|
427
|
-
exhaustedPages: false
|
|
428
|
-
};
|
|
429
|
-
cursor = page.nextCursor;
|
|
430
|
-
}
|
|
431
|
-
return {
|
|
432
|
-
tail,
|
|
433
|
-
total,
|
|
434
|
-
pages,
|
|
435
|
-
exhaustedPages: true
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
async function replayHistory(pi, controlPlane, workspaceId, options = {}) {
|
|
439
|
-
let transcript;
|
|
440
|
-
try {
|
|
441
|
-
transcript = await collectTranscriptTail(controlPlane, workspaceId, {
|
|
442
|
-
pageLimit: options.pageLimit ?? 200,
|
|
443
|
-
maxMessages: options.maxMessages ?? 1e3,
|
|
444
|
-
maxPages: options.maxPages ?? 50
|
|
445
|
-
});
|
|
446
|
-
} catch (error) {
|
|
447
|
-
if (error instanceof ConversationGoneError) {
|
|
448
|
-
pi.appendEntry(NOTICE_ENTRY_TYPE, {
|
|
449
|
-
text: error.code === "conversation.deleted" ? "conversation history was deleted" : "conversation history has expired",
|
|
450
|
-
level: "warning"
|
|
451
|
-
});
|
|
452
|
-
return {
|
|
453
|
-
replayed: 0,
|
|
454
|
-
total: 0,
|
|
455
|
-
truncated: false,
|
|
456
|
-
gone: true
|
|
457
|
-
};
|
|
458
|
-
}
|
|
459
|
-
throw error;
|
|
460
|
-
}
|
|
461
|
-
const { tail, total, pages, exhaustedPages } = transcript;
|
|
462
|
-
const truncated = total > tail.length || exhaustedPages;
|
|
463
|
-
if (truncated) pi.appendEntry(NOTICE_ENTRY_TYPE, { text: exhaustedPages ? `history partially replayed (stopped after ${pages} pages)` : `showing last ${tail.length} of ${total} messages` });
|
|
464
|
-
for (const message of tail) pi.appendEntry(HISTORY_ENTRY_TYPE, message);
|
|
465
|
-
return {
|
|
466
|
-
replayed: tail.length,
|
|
467
|
-
total,
|
|
468
|
-
truncated,
|
|
469
|
-
gone: false
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
//#endregion
|
|
473
|
-
//#region src/status.ts
|
|
479
|
+
//#region src/ui/status.ts
|
|
474
480
|
const STATUS_KEY = "pocketcoder";
|
|
475
481
|
function statusText(workspace) {
|
|
476
482
|
return `ws ${workspace.id.slice(0, 8)} · ${workspace.state}/${workspace.agent_state}`;
|
|
@@ -553,7 +559,7 @@ var StatusPoller = class {
|
|
|
553
559
|
}
|
|
554
560
|
};
|
|
555
561
|
//#endregion
|
|
556
|
-
//#region src/live-session.ts
|
|
562
|
+
//#region src/session/live-session.ts
|
|
557
563
|
var LiveSession = class {
|
|
558
564
|
targets;
|
|
559
565
|
createPoller;
|
|
@@ -609,57 +615,7 @@ var LiveSession = class {
|
|
|
609
615
|
}
|
|
610
616
|
};
|
|
611
617
|
//#endregion
|
|
612
|
-
//#region src/
|
|
613
|
-
function textOf(message) {
|
|
614
|
-
const content = message.content[0];
|
|
615
|
-
return content?.type === "text" ? content.text : "";
|
|
616
|
-
}
|
|
617
|
-
async function emitRemoteResponse(stream, output, send, onOutputStart = () => {}) {
|
|
618
|
-
let previous = "";
|
|
619
|
-
let started = false;
|
|
620
|
-
const update = (snapshot) => {
|
|
621
|
-
if (snapshot === previous) return;
|
|
622
|
-
const delta = snapshot.startsWith(previous) ? snapshot.slice(previous.length) : "";
|
|
623
|
-
if (!started) {
|
|
624
|
-
output.content.push({
|
|
625
|
-
type: "text",
|
|
626
|
-
text: ""
|
|
627
|
-
});
|
|
628
|
-
started = true;
|
|
629
|
-
onOutputStart();
|
|
630
|
-
stream.push({
|
|
631
|
-
type: "text_start",
|
|
632
|
-
contentIndex: 0,
|
|
633
|
-
partial: output
|
|
634
|
-
});
|
|
635
|
-
}
|
|
636
|
-
const content = output.content[0];
|
|
637
|
-
if (content?.type === "text") content.text = snapshot;
|
|
638
|
-
previous = snapshot;
|
|
639
|
-
stream.push({
|
|
640
|
-
type: "text_delta",
|
|
641
|
-
contentIndex: 0,
|
|
642
|
-
delta,
|
|
643
|
-
partial: output
|
|
644
|
-
});
|
|
645
|
-
};
|
|
646
|
-
update(await send(update));
|
|
647
|
-
if (!started) update("");
|
|
648
|
-
stream.push({
|
|
649
|
-
type: "text_end",
|
|
650
|
-
contentIndex: 0,
|
|
651
|
-
content: textOf(output),
|
|
652
|
-
partial: output
|
|
653
|
-
});
|
|
654
|
-
output.stopReason = "stop";
|
|
655
|
-
stream.push({
|
|
656
|
-
type: "done",
|
|
657
|
-
reason: "stop",
|
|
658
|
-
message: output
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
//#endregion
|
|
662
|
-
//#region src/agentapi-events.ts
|
|
618
|
+
//#region src/stream/agentapi-events.ts
|
|
663
619
|
function isRecord$2(value) {
|
|
664
620
|
return typeof value === "object" && value !== null;
|
|
665
621
|
}
|
|
@@ -731,7 +687,7 @@ async function* readAgentApiEvents(body, signal) {
|
|
|
731
687
|
}
|
|
732
688
|
}
|
|
733
689
|
//#endregion
|
|
734
|
-
//#region src/client-helpers.ts
|
|
690
|
+
//#region src/client/client-helpers.ts
|
|
735
691
|
function delay(ms, signal) {
|
|
736
692
|
return new Promise((resolve, reject) => {
|
|
737
693
|
const onAbort = () => {
|
|
@@ -775,7 +731,7 @@ function changesUrlFor(serviceUrl) {
|
|
|
775
731
|
return url.toString();
|
|
776
732
|
}
|
|
777
733
|
//#endregion
|
|
778
|
-
//#region src/client.ts
|
|
734
|
+
//#region src/client/client.ts
|
|
779
735
|
function isRecord(value) {
|
|
780
736
|
return typeof value === "object" && value !== null;
|
|
781
737
|
}
|
|
@@ -784,6 +740,7 @@ var RemoteAgentClient = class {
|
|
|
784
740
|
key;
|
|
785
741
|
pollIntervalMs;
|
|
786
742
|
timeoutMs;
|
|
743
|
+
readyTimeoutMs;
|
|
787
744
|
fetchImpl;
|
|
788
745
|
changesUrl;
|
|
789
746
|
constructor(config, fetchImpl = fetch) {
|
|
@@ -791,6 +748,7 @@ var RemoteAgentClient = class {
|
|
|
791
748
|
this.key = config.key;
|
|
792
749
|
this.pollIntervalMs = config.pollIntervalMs ?? 250;
|
|
793
750
|
this.timeoutMs = config.timeoutMs ?? 6e5;
|
|
751
|
+
this.readyTimeoutMs = Math.min(config.readyTimeoutMs ?? 12e4, this.timeoutMs);
|
|
794
752
|
this.fetchImpl = fetchImpl;
|
|
795
753
|
this.changesUrl = changesUrlFor(this.serviceUrl);
|
|
796
754
|
}
|
|
@@ -851,7 +809,22 @@ var RemoteAgentClient = class {
|
|
|
851
809
|
agentState: body.workspace.agent_state
|
|
852
810
|
};
|
|
853
811
|
}
|
|
812
|
+
async waitForInput(signal) {
|
|
813
|
+
const deadline = Date.now() + this.readyTimeoutMs;
|
|
814
|
+
let cursor = 0;
|
|
815
|
+
for (;;) {
|
|
816
|
+
const remainingMs = deadline - Date.now();
|
|
817
|
+
const change = await this.workspaceChange(cursor, Math.max(1, Math.min(30, Math.ceil(remainingMs / 1e3))), "submit_readiness", false, signal);
|
|
818
|
+
const state = change ? change.agentState : await this.status("submit_readiness", false, signal);
|
|
819
|
+
if (state === "stable") return;
|
|
820
|
+
const advanced = change !== void 0 && change.cursor !== cursor;
|
|
821
|
+
if (change) cursor = change.cursor;
|
|
822
|
+
if (Date.now() >= deadline) throw new Error(`remote agent was still ${state} after ${this.readyTimeoutMs}ms and cannot accept a message`);
|
|
823
|
+
if (!advanced) await delay(this.pollIntervalMs, signal);
|
|
824
|
+
}
|
|
825
|
+
}
|
|
854
826
|
async submit(prompt, attachmentIds, signal) {
|
|
827
|
+
await this.waitForInput(signal);
|
|
855
828
|
const response = await this.request("/message", {
|
|
856
829
|
method: "POST",
|
|
857
830
|
body: JSON.stringify({
|
|
@@ -961,7 +934,7 @@ var RemoteAgentClient = class {
|
|
|
961
934
|
}
|
|
962
935
|
};
|
|
963
936
|
//#endregion
|
|
964
|
-
//#region src/turn.ts
|
|
937
|
+
//#region src/session/turn.ts
|
|
965
938
|
function isRetryableTerminal(error) {
|
|
966
939
|
return error instanceof RemoteRequestError && error.code === "workspace.terminal" && !error.promptAccepted;
|
|
967
940
|
}
|
|
@@ -1006,6 +979,56 @@ async function executeRemoteTurn(options) {
|
|
|
1006
979
|
throw new Error("remote turn retry invariant failed");
|
|
1007
980
|
}
|
|
1008
981
|
//#endregion
|
|
982
|
+
//#region src/stream/response-stream.ts
|
|
983
|
+
function textOf(message) {
|
|
984
|
+
const content = message.content[0];
|
|
985
|
+
return content?.type === "text" ? content.text : "";
|
|
986
|
+
}
|
|
987
|
+
async function emitRemoteResponse(stream, output, send, onOutputStart = () => {}) {
|
|
988
|
+
let previous = "";
|
|
989
|
+
let started = false;
|
|
990
|
+
const update = (snapshot) => {
|
|
991
|
+
if (snapshot === previous) return;
|
|
992
|
+
const delta = snapshot.startsWith(previous) ? snapshot.slice(previous.length) : "";
|
|
993
|
+
if (!started) {
|
|
994
|
+
output.content.push({
|
|
995
|
+
type: "text",
|
|
996
|
+
text: ""
|
|
997
|
+
});
|
|
998
|
+
started = true;
|
|
999
|
+
onOutputStart();
|
|
1000
|
+
stream.push({
|
|
1001
|
+
type: "text_start",
|
|
1002
|
+
contentIndex: 0,
|
|
1003
|
+
partial: output
|
|
1004
|
+
});
|
|
1005
|
+
}
|
|
1006
|
+
const content = output.content[0];
|
|
1007
|
+
if (content?.type === "text") content.text = snapshot;
|
|
1008
|
+
previous = snapshot;
|
|
1009
|
+
stream.push({
|
|
1010
|
+
type: "text_delta",
|
|
1011
|
+
contentIndex: 0,
|
|
1012
|
+
delta,
|
|
1013
|
+
partial: output
|
|
1014
|
+
});
|
|
1015
|
+
};
|
|
1016
|
+
update(await send(update));
|
|
1017
|
+
if (!started) update("");
|
|
1018
|
+
stream.push({
|
|
1019
|
+
type: "text_end",
|
|
1020
|
+
contentIndex: 0,
|
|
1021
|
+
content: textOf(output),
|
|
1022
|
+
partial: output
|
|
1023
|
+
});
|
|
1024
|
+
output.stopReason = "stop";
|
|
1025
|
+
stream.push({
|
|
1026
|
+
type: "done",
|
|
1027
|
+
reason: "stop",
|
|
1028
|
+
message: output
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
//#endregion
|
|
1009
1032
|
//#region src/extension.ts
|
|
1010
1033
|
const PROVIDER = "pocketcoder-agentapi";
|
|
1011
1034
|
const MODEL = "remote-agent";
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"type:app"
|
|
7
7
|
]
|
|
8
8
|
},
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.3",
|
|
10
10
|
"private": false,
|
|
11
11
|
"description": "Pi-based terminal UI for PocketCoder workspaces.",
|
|
12
12
|
"type": "module",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"@earendil-works/pi-ai": "0.83.0",
|
|
47
47
|
"@earendil-works/pi-coding-agent": "0.83.0",
|
|
48
48
|
"@earendil-works/pi-tui": "0.83.0",
|
|
49
|
-
"@pstdio/pocketcoder-sdk": "^0.
|
|
49
|
+
"@pstdio/pocketcoder-sdk": "^0.6.0"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"@types/bun": "1.3.14",
|