@getpaseo/server 0.1.81 → 0.1.83

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.
Files changed (163) hide show
  1. package/dist/server/client/daemon-client.d.ts +2 -1
  2. package/dist/server/client/daemon-client.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client.js +29 -0
  4. package/dist/server/client/daemon-client.js.map +1 -1
  5. package/dist/server/server/agent/agent-manager.d.ts +9 -7
  6. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  7. package/dist/server/server/agent/agent-manager.js +78 -92
  8. package/dist/server/server/agent/agent-manager.js.map +1 -1
  9. package/dist/server/server/agent/agent-metadata-generator.js +1 -1
  10. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  11. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  12. package/dist/server/server/agent/agent-projections.js +4 -4
  13. package/dist/server/server/agent/agent-projections.js.map +1 -1
  14. package/dist/server/server/agent/agent-prompt.d.ts +13 -10
  15. package/dist/server/server/agent/agent-prompt.d.ts.map +1 -1
  16. package/dist/server/server/agent/agent-prompt.js +38 -18
  17. package/dist/server/server/agent/agent-prompt.js.map +1 -1
  18. package/dist/server/server/agent/agent-sdk-types.d.ts +13 -0
  19. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  20. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  21. package/dist/server/server/agent/agent-storage.d.ts +2 -7
  22. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  23. package/dist/server/server/agent/agent-storage.js +2 -16
  24. package/dist/server/server/agent/agent-storage.js.map +1 -1
  25. package/dist/server/server/agent/agent-timeline-store-types.d.ts +0 -4
  26. package/dist/server/server/agent/agent-timeline-store-types.d.ts.map +1 -1
  27. package/dist/server/server/agent/agent-timeline-store.d.ts +0 -5
  28. package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -1
  29. package/dist/server/server/agent/agent-timeline-store.js +0 -33
  30. package/dist/server/server/agent/agent-timeline-store.js.map +1 -1
  31. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  32. package/dist/server/server/agent/mcp-server.js +42 -28
  33. package/dist/server/server/agent/mcp-server.js.map +1 -1
  34. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  35. package/dist/server/server/agent/provider-launch-config.js +0 -1
  36. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  37. package/dist/server/server/agent/provider-manifest.d.ts +1 -1
  38. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  39. package/dist/server/server/agent/provider-registry.d.ts +2 -1
  40. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  41. package/dist/server/server/agent/provider-registry.js +5 -1
  42. package/dist/server/server/agent/provider-registry.js.map +1 -1
  43. package/dist/server/server/agent/provider-snapshot-manager.d.ts +2 -2
  44. package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
  45. package/dist/server/server/agent/provider-snapshot-manager.js +60 -26
  46. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  47. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  48. package/dist/server/server/agent/providers/acp-agent.js +3 -0
  49. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  50. package/dist/server/server/agent/providers/claude/agent.d.ts.map +1 -1
  51. package/dist/server/server/agent/providers/claude/agent.js +275 -85
  52. package/dist/server/server/agent/providers/claude/agent.js.map +1 -1
  53. package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
  54. package/dist/server/server/agent/providers/claude/rewind.d.ts.map +1 -0
  55. package/dist/server/server/agent/providers/claude/rewind.js +30 -0
  56. package/dist/server/server/agent/providers/claude/rewind.js.map +1 -0
  57. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
  58. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts.map +1 -0
  59. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
  60. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js.map +1 -0
  61. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +168 -0
  62. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts.map +1 -1
  63. package/dist/server/server/agent/providers/codex/app-server-transport.js +48 -0
  64. package/dist/server/server/agent/providers/codex/app-server-transport.js.map +1 -1
  65. package/dist/server/server/agent/providers/codex/rewind.d.ts +21 -0
  66. package/dist/server/server/agent/providers/codex/rewind.d.ts.map +1 -0
  67. package/dist/server/server/agent/providers/codex/rewind.js +46 -0
  68. package/dist/server/server/agent/providers/codex/rewind.js.map +1 -0
  69. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts +5 -0
  70. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts.map +1 -1
  71. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js +45 -0
  72. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js.map +1 -1
  73. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +1 -1
  74. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  75. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +11 -11
  76. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  77. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +32 -34
  78. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  79. package/dist/server/server/agent/providers/codex-app-server-agent.js +206 -60
  80. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  81. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
  82. package/dist/server/server/agent/providers/copilot-acp-agent.js +3 -0
  83. package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
  84. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +13 -0
  85. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -1
  86. package/dist/server/server/agent/providers/mock-load-test-agent.js +55 -1
  87. package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -1
  88. package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
  89. package/dist/server/server/agent/providers/opencode/rewind.d.ts.map +1 -0
  90. package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
  91. package/dist/server/server/agent/providers/opencode/rewind.js.map +1 -0
  92. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -1
  93. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +110 -2
  94. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  95. package/dist/server/server/agent/providers/opencode-agent.d.ts +18 -0
  96. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  97. package/dist/server/server/agent/providers/opencode-agent.js +265 -38
  98. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  99. package/dist/server/server/agent/providers/pi/agent.d.ts +21 -1
  100. package/dist/server/server/agent/providers/pi/agent.d.ts.map +1 -1
  101. package/dist/server/server/agent/providers/pi/agent.js +355 -13
  102. package/dist/server/server/agent/providers/pi/agent.js.map +1 -1
  103. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +5 -1
  104. package/dist/server/server/agent/providers/pi/history-mapper.d.ts.map +1 -1
  105. package/dist/server/server/agent/providers/pi/history-mapper.js +3 -2
  106. package/dist/server/server/agent/providers/pi/history-mapper.js.map +1 -1
  107. package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
  108. package/dist/server/server/agent/providers/pi/rewind.d.ts.map +1 -0
  109. package/dist/server/server/agent/providers/pi/rewind.js +8 -0
  110. package/dist/server/server/agent/providers/pi/rewind.js.map +1 -0
  111. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +10 -0
  112. package/dist/server/server/agent/providers/pi/rpc-types.d.ts.map +1 -1
  113. package/dist/server/server/agent/providers/pi/runtime.d.ts +2 -0
  114. package/dist/server/server/agent/providers/pi/runtime.d.ts.map +1 -1
  115. package/dist/server/server/agent/providers/pi/runtime.js +4 -0
  116. package/dist/server/server/agent/providers/pi/runtime.js.map +1 -1
  117. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +10 -0
  118. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts.map +1 -1
  119. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +49 -1
  120. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js.map +1 -1
  121. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +4 -1
  122. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  123. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +57 -1
  124. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  125. package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
  126. package/dist/server/server/agent/rewind/rewind.d.ts.map +1 -0
  127. package/dist/server/server/agent/rewind/rewind.js +29 -0
  128. package/dist/server/server/agent/rewind/rewind.js.map +1 -0
  129. package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
  130. package/dist/server/server/agent/rewind/test-rewind-session.d.ts.map +1 -0
  131. package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
  132. package/dist/server/server/agent/rewind/test-rewind-session.js.map +1 -0
  133. package/dist/server/server/persistence-hooks.d.ts.map +1 -1
  134. package/dist/server/server/persistence-hooks.js +0 -2
  135. package/dist/server/server/persistence-hooks.js.map +1 -1
  136. package/dist/server/server/session.d.ts +2 -0
  137. package/dist/server/server/session.d.ts.map +1 -1
  138. package/dist/server/server/session.js +56 -25
  139. package/dist/server/server/session.js.map +1 -1
  140. package/dist/server/server/websocket-server.d.ts.map +1 -1
  141. package/dist/server/server/websocket-server.js +2 -0
  142. package/dist/server/server/websocket-server.js.map +1 -1
  143. package/dist/server/shared/messages.d.ts +281 -0
  144. package/dist/server/shared/messages.d.ts.map +1 -1
  145. package/dist/server/shared/messages.js +27 -0
  146. package/dist/server/shared/messages.js.map +1 -1
  147. package/dist/server/terminal/terminal.d.ts.map +1 -1
  148. package/dist/server/terminal/terminal.js +14 -0
  149. package/dist/server/terminal/terminal.js.map +1 -1
  150. package/dist/src/server/agent/agent-sdk-types.js.map +1 -1
  151. package/dist/src/server/agent/provider-launch-config.js +0 -1
  152. package/dist/src/server/agent/provider-launch-config.js.map +1 -1
  153. package/dist/src/shared/messages.js +27 -0
  154. package/dist/src/shared/messages.js.map +1 -1
  155. package/package.json +3 -3
  156. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts +0 -9
  157. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +0 -1
  158. package/dist/server/server/agent/providers/codex-rollout-timeline.js +0 -555
  159. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +0 -1
  160. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +0 -10
  161. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts.map +0 -1
  162. package/dist/server/server/agent/providers/pi/session-descriptor.js +0 -300
  163. package/dist/server/server/agent/providers/pi/session-descriptor.js.map +0 -1
@@ -7,13 +7,18 @@ import { renderPromptAttachmentAsText } from "../../prompt-attachments.js";
7
7
  import { composeSystemPromptParts } from "../../system-prompt.js";
8
8
  import { findExecutable } from "../../../../utils/executable.js";
9
9
  import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnosticError, resolveBinaryVersion, toDiagnosticErrorMessage, } from "../diagnostic-utils.js";
10
- import { streamPiHistory } from "./history-mapper.js";
10
+ import { getUserMessageText, streamPiHistory, } from "./history-mapper.js";
11
11
  import { PiCliRuntime } from "./cli-runtime.js";
12
- import { listPiPersistedAgents } from "./session-descriptor.js";
12
+ import { revertPiConversation } from "./rewind.js";
13
13
  import { mapToolDetail, parseToolArgs, parseToolResult, resolveToolCallName, } from "./tool-call-mapper.js";
14
14
  const PI_PROVIDER = "pi";
15
15
  const DEFAULT_PI_THINKING_LEVEL = "medium";
16
16
  const PI_BINARY_COMMAND = process.env.PI_COMMAND ?? process.env.PI_ACP_PI_COMMAND ?? "pi";
17
+ const PASEO_PI_TREE_EXTENSION_COMMAND = "paseo_tree";
18
+ const PASEO_PI_CAPTURE_EXTENSION_COMMAND = "paseo_capture_entries";
19
+ const PASEO_PI_ENTRY_CAPTURE_MARKER = "PASEO_ENTRY_CAPTURE";
20
+ const PASEO_PI_COMMAND_RESULT_MARKER = "PASEO_COMMAND_RESULT";
21
+ const PASEO_PI_EXTENSION_RESULT_TIMEOUT_MS = 10000;
17
22
  const PI_CAPABILITIES = {
18
23
  supportsStreaming: true,
19
24
  supportsSessionPersistence: true,
@@ -21,6 +26,9 @@ const PI_CAPABILITIES = {
21
26
  supportsMcpServers: false,
22
27
  supportsReasoningStream: true,
23
28
  supportsToolInvocations: true,
29
+ supportsRewindConversation: true,
30
+ supportsRewindFiles: false,
31
+ supportsRewindBoth: false,
24
32
  };
25
33
  const PI_THINKING_OPTIONS = [
26
34
  { id: "off", label: "Off", description: "No extra reasoning" },
@@ -211,6 +219,103 @@ function createPiMcpConfigFile(servers) {
211
219
  cleanup: () => rmSync(dir, { recursive: true, force: true }),
212
220
  };
213
221
  }
222
+ function createPiPaseoExtensionFile() {
223
+ const dir = mkdtempSync(join(tmpdir(), "paseo-pi-extension-"));
224
+ const filePath = join(dir, "paseo-integration.mjs");
225
+ writeFileSync(filePath, `
226
+ function decodePayload(encoded) {
227
+ return JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
228
+ }
229
+
230
+ function readTextContent(content) {
231
+ if (typeof content === "string") {
232
+ return content;
233
+ }
234
+ if (!Array.isArray(content)) {
235
+ return "";
236
+ }
237
+ return content
238
+ .filter((part) => part && part.type === "text" && typeof part.text === "string")
239
+ .map((part) => part.text)
240
+ .join("\\n\\n");
241
+ }
242
+
243
+ function getCapturedUserEntries(ctx) {
244
+ return ctx.sessionManager
245
+ .getEntries()
246
+ .filter((entry) => entry.type === "message" && entry.message?.role === "user")
247
+ .map((entry) => ({
248
+ id: entry.id,
249
+ parentId: entry.parentId ?? null,
250
+ text: readTextContent(entry.message.content),
251
+ }));
252
+ }
253
+
254
+ function emitEntryCapture(ctx, reason, requestId) {
255
+ ctx.ui.notify(
256
+ "${PASEO_PI_ENTRY_CAPTURE_MARKER} " +
257
+ JSON.stringify({ reason, requestId, entries: getCapturedUserEntries(ctx) }),
258
+ "info",
259
+ );
260
+ }
261
+
262
+ function emitCommandResult(ctx, requestId, result) {
263
+ ctx.ui.notify(
264
+ "${PASEO_PI_COMMAND_RESULT_MARKER} " + JSON.stringify({ requestId, ...result }),
265
+ result.ok ? "info" : "error",
266
+ );
267
+ }
268
+
269
+ export default function paseoIntegration(pi) {
270
+ pi.on("session_start", async (_event, ctx) => {
271
+ emitEntryCapture(ctx, "session_start");
272
+ });
273
+
274
+ pi.on("turn_end", async (_event, ctx) => {
275
+ emitEntryCapture(ctx, "turn_end");
276
+ });
277
+
278
+ pi.registerCommand("${PASEO_PI_CAPTURE_EXTENSION_COMMAND}", {
279
+ description: "Internal Paseo entry capture bridge",
280
+ handler: async (args, ctx) => {
281
+ const payload = decodePayload(args.trim());
282
+ emitEntryCapture(ctx, "command", payload.requestId);
283
+ },
284
+ });
285
+
286
+ pi.registerCommand("${PASEO_PI_TREE_EXTENSION_COMMAND}", {
287
+ description: "Internal Paseo tree navigation bridge",
288
+ handler: async (args, ctx) => {
289
+ const payload = decodePayload(args.trim());
290
+ try {
291
+ const result = await ctx.navigateTree(payload.targetId, { summarize: false });
292
+ emitEntryCapture(ctx, "tree_navigation");
293
+ emitCommandResult(ctx, payload.requestId, { ok: true, result });
294
+ } catch (error) {
295
+ const message = error instanceof Error ? error.message : String(error);
296
+ emitCommandResult(ctx, payload.requestId, { ok: false, error: message });
297
+ throw error;
298
+ }
299
+ },
300
+ });
301
+ }
302
+ `.trimStart(), "utf8");
303
+ return {
304
+ path: filePath,
305
+ cleanup: () => rmSync(dir, { recursive: true, force: true }),
306
+ };
307
+ }
308
+ function combineCleanup(cleanups) {
309
+ const activeCleanups = cleanups.filter((cleanup) => Boolean(cleanup));
310
+ if (activeCleanups.length === 0) {
311
+ return undefined;
312
+ }
313
+ return () => {
314
+ for (const cleanup of activeCleanups) {
315
+ cleanup();
316
+ }
317
+ };
318
+ }
214
319
  function isPiMcpAdapterCommand(command) {
215
320
  if (command.source !== "extension" || !/^mcp(?::\d+)?$/.test(command.name)) {
216
321
  return false;
@@ -239,11 +344,41 @@ function resolveThinkingOptionId(cachedThinkingOptionId, sessionThinkingLevel) {
239
344
  function modelToId(model) {
240
345
  return model?.provider && model.id ? `${model.provider}/${model.id}` : null;
241
346
  }
347
+ function piAssistantText(message) {
348
+ const text = message.content
349
+ .flatMap((part) => {
350
+ if (part.type === "text") {
351
+ return [part.text];
352
+ }
353
+ if (part.type === "thinking") {
354
+ return [part.thinking];
355
+ }
356
+ return [];
357
+ })
358
+ .join("\n\n")
359
+ .trim();
360
+ return text.length > 0 ? text : null;
361
+ }
362
+ function formatPiErrorMessage(message) {
363
+ const headline = message.errorMessage?.trim() || "Pi turn failed";
364
+ const details = [
365
+ message.stopReason ? `stopReason=${message.stopReason}` : null,
366
+ message.provider && message.model ? `model=${message.provider}/${message.model}` : null,
367
+ message.responseModel ? `responseModel=${message.responseModel}` : null,
368
+ message.responseId ? `responseId=${message.responseId}` : null,
369
+ ].filter((detail) => detail !== null);
370
+ const partialText = piAssistantText(message);
371
+ if (partialText) {
372
+ details.push(`partial=${JSON.stringify(partialText.slice(0, 500))}`);
373
+ }
374
+ return details.length > 0 ? `${headline} (${details.join(", ")})` : headline;
375
+ }
242
376
  function latestPiErrorMessage(messages) {
243
377
  const latestAssistant = messages.findLast((message) => message.role === "assistant");
244
- return latestAssistant && "errorMessage" in latestAssistant
245
- ? (latestAssistant.errorMessage ?? null)
246
- : null;
378
+ if (!latestAssistant || !latestAssistant.errorMessage?.trim()) {
379
+ return null;
380
+ }
381
+ return formatPiErrorMessage(latestAssistant);
247
382
  }
248
383
  function isRecord(value) {
249
384
  return typeof value === "object" && value !== null && !Array.isArray(value);
@@ -251,6 +386,42 @@ function isRecord(value) {
251
386
  function optionalString(value) {
252
387
  return typeof value === "string" ? value : undefined;
253
388
  }
389
+ function parseExtensionMarkerPayload(message, marker) {
390
+ const prefix = `${marker} `;
391
+ if (!message.startsWith(prefix)) {
392
+ return null;
393
+ }
394
+ try {
395
+ const parsed = JSON.parse(message.slice(prefix.length));
396
+ return isRecord(parsed) ? parsed : null;
397
+ }
398
+ catch {
399
+ return null;
400
+ }
401
+ }
402
+ function parseCapturedEntries(value) {
403
+ if (!Array.isArray(value)) {
404
+ return [];
405
+ }
406
+ return value.flatMap((entry) => {
407
+ if (!isRecord(entry)) {
408
+ return [];
409
+ }
410
+ const id = optionalString(entry.id)?.trim();
411
+ const text = optionalString(entry.text);
412
+ if (!id || text === undefined) {
413
+ return [];
414
+ }
415
+ const parentId = entry.parentId === null ? null : optionalString(entry.parentId)?.trim();
416
+ return [
417
+ {
418
+ id,
419
+ parentId: parentId || null,
420
+ text,
421
+ },
422
+ ];
423
+ });
424
+ }
254
425
  function mapExtensionUiRequestToPermission(event) {
255
426
  switch (event.method) {
256
427
  case "select":
@@ -355,6 +526,11 @@ export class PiRpcAgentSession {
355
526
  this.activeToolCalls = new Map();
356
527
  this.pendingExtensionUiRequests = new Map();
357
528
  this.activeTurnId = null;
529
+ this.capturedUserEntries = [];
530
+ this.capturedUserEntriesById = new Map();
531
+ this.seenUserEntryIds = new Set();
532
+ this.pendingUserMessages = [];
533
+ this.pendingExtensionResults = new Map();
358
534
  this.closed = false;
359
535
  this.runtimeSession = options.runtimeSession;
360
536
  this.config = options.config;
@@ -417,7 +593,8 @@ export class PiRpcAgentSession {
417
593
  };
418
594
  }
419
595
  async *streamHistory() {
420
- yield* streamPiHistory(PI_PROVIDER, await this.runtimeSession.getMessages());
596
+ await this.requestEntryCapture("history");
597
+ yield* streamPiHistory(PI_PROVIDER, await this.runtimeSession.getMessages(), this.capturedUserEntries);
421
598
  }
422
599
  async getRuntimeInfo() {
423
600
  await this.refreshState();
@@ -472,6 +649,33 @@ export class PiRpcAgentSession {
472
649
  async interrupt() {
473
650
  await this.runtimeSession.abort();
474
651
  }
652
+ async revertConversation(input) {
653
+ if (this.activeTurnId) {
654
+ throw new Error("Cannot rewind the Pi conversation while a Pi turn is active");
655
+ }
656
+ await this.refreshState().catch(() => undefined);
657
+ await this.requestEntryCapture("rewind");
658
+ const targetEntry = this.capturedUserEntriesById.get(input.messageId);
659
+ if (!targetEntry) {
660
+ throw new Error(`Pi rewind target ${input.messageId} was not found in captured tree entries`);
661
+ }
662
+ await revertPiConversation({
663
+ messageId: input.messageId,
664
+ navigator: {
665
+ navigateTree: (treeEntryId) => this.runPiTreeExtensionCommand(treeEntryId),
666
+ },
667
+ });
668
+ // Pi keeps all tree nodes, so selecting the previous leaf later reverses this rewind.
669
+ this.currentLeafOverrideId = targetEntry.parentId;
670
+ this.activeToolCalls.clear();
671
+ }
672
+ async runPiTreeExtensionCommand(targetId) {
673
+ const requestId = randomUUID();
674
+ const resultPromise = this.waitForExtensionResult(requestId);
675
+ const payload = Buffer.from(JSON.stringify({ targetId, requestId })).toString("base64url");
676
+ await this.runtimeSession.prompt(`/${PASEO_PI_TREE_EXTENSION_COMMAND} ${payload}`);
677
+ return await resultPromise;
678
+ }
475
679
  async close() {
476
680
  if (this.closed) {
477
681
  return;
@@ -481,6 +685,7 @@ export class PiRpcAgentSession {
481
685
  await this.runtimeSession.close();
482
686
  }
483
687
  finally {
688
+ this.rejectAllExtensionResults(new Error("Pi session closed"));
484
689
  this.cleanup?.();
485
690
  }
486
691
  }
@@ -525,7 +730,114 @@ export class PiRpcAgentSession {
525
730
  currentTurnIdForEvent() {
526
731
  return this.activeTurnId ?? undefined;
527
732
  }
733
+ async requestEntryCapture(reason) {
734
+ const requestId = randomUUID();
735
+ const resultPromise = this.waitForExtensionResult(requestId);
736
+ const payload = Buffer.from(JSON.stringify({ requestId, reason })).toString("base64url");
737
+ await this.runtimeSession.prompt(`/${PASEO_PI_CAPTURE_EXTENSION_COMMAND} ${payload}`);
738
+ await resultPromise;
739
+ }
740
+ waitForExtensionResult(requestId) {
741
+ return new Promise((resolve, reject) => {
742
+ const timer = setTimeout(() => {
743
+ this.pendingExtensionResults.delete(requestId);
744
+ reject(new Error(`Pi extension result timed out for request ${requestId}`));
745
+ }, PASEO_PI_EXTENSION_RESULT_TIMEOUT_MS);
746
+ this.pendingExtensionResults.set(requestId, { resolve, reject, timer });
747
+ });
748
+ }
749
+ resolveExtensionResult(requestId, result) {
750
+ const pending = this.pendingExtensionResults.get(requestId);
751
+ if (!pending) {
752
+ return;
753
+ }
754
+ clearTimeout(pending.timer);
755
+ this.pendingExtensionResults.delete(requestId);
756
+ pending.resolve(result);
757
+ }
758
+ rejectExtensionResult(requestId, error) {
759
+ const pending = this.pendingExtensionResults.get(requestId);
760
+ if (!pending) {
761
+ return;
762
+ }
763
+ clearTimeout(pending.timer);
764
+ this.pendingExtensionResults.delete(requestId);
765
+ pending.reject(error);
766
+ }
767
+ rejectAllExtensionResults(error) {
768
+ for (const requestId of this.pendingExtensionResults.keys()) {
769
+ this.rejectExtensionResult(requestId, error);
770
+ }
771
+ }
772
+ recordCapturedUserEntries(entries) {
773
+ const previouslySeenEntryIds = new Set(this.seenUserEntryIds);
774
+ this.capturedUserEntries.splice(0, this.capturedUserEntries.length, ...entries);
775
+ this.capturedUserEntriesById.clear();
776
+ for (const entry of entries) {
777
+ this.capturedUserEntriesById.set(entry.id, entry);
778
+ }
779
+ this.flushPendingUserMessages(previouslySeenEntryIds);
780
+ for (const entry of entries) {
781
+ this.seenUserEntryIds.add(entry.id);
782
+ }
783
+ }
784
+ flushPendingUserMessages(previouslySeenEntryIds) {
785
+ for (let index = 0; index < this.pendingUserMessages.length; index += 1) {
786
+ const pending = this.pendingUserMessages[index];
787
+ const entry = this.capturedUserEntries.find((candidate) => !previouslySeenEntryIds.has(candidate.id));
788
+ if (!entry) {
789
+ continue;
790
+ }
791
+ previouslySeenEntryIds.add(entry.id);
792
+ this.pendingUserMessages.splice(index, 1);
793
+ index -= 1;
794
+ this.emit({
795
+ type: "timeline",
796
+ provider: PI_PROVIDER,
797
+ turnId: pending.turnId,
798
+ item: {
799
+ type: "user_message",
800
+ text: pending.text,
801
+ messageId: entry.id,
802
+ },
803
+ });
804
+ }
805
+ }
806
+ handleEntryCaptureMarker(message) {
807
+ const payload = parseExtensionMarkerPayload(message, PASEO_PI_ENTRY_CAPTURE_MARKER);
808
+ if (!payload) {
809
+ return false;
810
+ }
811
+ const entries = parseCapturedEntries(payload.entries);
812
+ this.recordCapturedUserEntries(entries);
813
+ if (typeof payload.requestId === "string") {
814
+ this.resolveExtensionResult(payload.requestId, entries);
815
+ }
816
+ return true;
817
+ }
818
+ handleCommandResultMarker(message) {
819
+ const payload = parseExtensionMarkerPayload(message, PASEO_PI_COMMAND_RESULT_MARKER);
820
+ if (!payload) {
821
+ return false;
822
+ }
823
+ if (typeof payload.requestId !== "string") {
824
+ return true;
825
+ }
826
+ if (payload.ok === true) {
827
+ this.resolveExtensionResult(payload.requestId, payload.result);
828
+ return true;
829
+ }
830
+ const error = typeof payload.error === "string" ? payload.error : "Pi extension command failed";
831
+ this.rejectExtensionResult(payload.requestId, new Error(error));
832
+ return true;
833
+ }
528
834
  handleExtensionUiRequest(event) {
835
+ const message = optionalString(event.message);
836
+ if (event.method === "notify" && message) {
837
+ if (this.handleEntryCaptureMarker(message) || this.handleCommandResultMarker(message)) {
838
+ return;
839
+ }
840
+ }
529
841
  const request = mapExtensionUiRequestToPermission(event);
530
842
  if (!request) {
531
843
  return;
@@ -550,6 +862,7 @@ export class PiRpcAgentSession {
550
862
  this.handleSessionEvent(event);
551
863
  }
552
864
  handleProcessExit(error) {
865
+ this.rejectAllExtensionResults(new Error(error));
553
866
  if (!this.activeTurnId) {
554
867
  return;
555
868
  }
@@ -579,6 +892,11 @@ export class PiRpcAgentSession {
579
892
  turnId,
580
893
  });
581
894
  return;
895
+ case "message_start":
896
+ return;
897
+ case "message_end":
898
+ this.handleMessageEnd(event, turnId);
899
+ return;
582
900
  case "message_update":
583
901
  this.handleMessageUpdate(event, turnId);
584
902
  return;
@@ -664,6 +982,25 @@ export class PiRpcAgentSession {
664
982
  });
665
983
  }
666
984
  }
985
+ handleMessageEnd(event, turnId) {
986
+ if (event.message.role !== "user") {
987
+ return;
988
+ }
989
+ const text = getUserMessageText(event.message.content);
990
+ if (!text) {
991
+ return;
992
+ }
993
+ this.pendingUserMessages.push({ text, turnId });
994
+ void this.requestEntryCapture("message_end").catch((error) => {
995
+ const message = error instanceof Error ? error.message : String(error);
996
+ this.emit({
997
+ type: "turn_failed",
998
+ provider: PI_PROVIDER,
999
+ turnId,
1000
+ error: message,
1001
+ });
1002
+ });
1003
+ }
667
1004
  emitToolCallEvent(toolCallId, toolCall, status, result, error) {
668
1005
  const turnId = this.currentTurnIdForEvent();
669
1006
  const detail = mapToolDetail(toolCall, result);
@@ -729,6 +1066,7 @@ export class PiRpcAgentClient {
729
1066
  }
730
1067
  async createSession(config, launchContext) {
731
1068
  const mcpConfig = await this.prepareMcpConfig(config.cwd, config.mcpServers);
1069
+ const paseoExtension = createPiPaseoExtensionFile();
732
1070
  let runtimeSession;
733
1071
  try {
734
1072
  runtimeSession = await this.runtime.startSession({
@@ -738,10 +1076,12 @@ export class PiRpcAgentClient {
738
1076
  systemPrompt: composeSystemPromptParts(config.systemPrompt, config.daemonAppendSystemPrompt),
739
1077
  env: launchContext?.env,
740
1078
  mcpConfigPath: mcpConfig?.path,
1079
+ extensionPaths: [paseoExtension.path],
741
1080
  });
742
1081
  }
743
1082
  catch (error) {
744
1083
  mcpConfig?.cleanup();
1084
+ paseoExtension.cleanup();
745
1085
  throw error;
746
1086
  }
747
1087
  try {
@@ -750,12 +1090,13 @@ export class PiRpcAgentClient {
750
1090
  config,
751
1091
  initialState: await runtimeSession.getState(),
752
1092
  capabilities: withPiMcpCapability(mcpConfig !== null),
753
- cleanup: mcpConfig?.cleanup,
1093
+ cleanup: combineCleanup([mcpConfig?.cleanup, paseoExtension.cleanup]),
754
1094
  });
755
1095
  }
756
1096
  catch (error) {
757
1097
  await runtimeSession.close().catch(() => undefined);
758
1098
  mcpConfig?.cleanup();
1099
+ paseoExtension.cleanup();
759
1100
  throw error;
760
1101
  }
761
1102
  }
@@ -767,6 +1108,7 @@ export class PiRpcAgentClient {
767
1108
  const persistenceMetadata = parsePersistenceMetadata(handle.metadata);
768
1109
  const resumeConfig = buildResumeConfig(persistenceMetadata, overrides);
769
1110
  const mcpConfig = await this.prepareMcpConfig(resumeConfig.cwd, resumeConfig.config.mcpServers);
1111
+ const paseoExtension = createPiPaseoExtensionFile();
770
1112
  let runtimeSession;
771
1113
  try {
772
1114
  runtimeSession = await this.runtime.startSession({
@@ -776,10 +1118,12 @@ export class PiRpcAgentClient {
776
1118
  thinkingOptionId: normalizePiThinkingOption(resumeConfig.thinkingOptionId) ?? undefined,
777
1119
  systemPrompt: composeSystemPromptParts(resumeConfig.config.systemPrompt, resumeConfig.config.daemonAppendSystemPrompt),
778
1120
  mcpConfigPath: mcpConfig?.path,
1121
+ extensionPaths: [paseoExtension.path],
779
1122
  });
780
1123
  }
781
1124
  catch (error) {
782
1125
  mcpConfig?.cleanup();
1126
+ paseoExtension.cleanup();
783
1127
  throw error;
784
1128
  }
785
1129
  try {
@@ -788,12 +1132,13 @@ export class PiRpcAgentClient {
788
1132
  config: resumeConfig.config,
789
1133
  initialState: await runtimeSession.getState(),
790
1134
  capabilities: withPiMcpCapability(mcpConfig !== null),
791
- cleanup: mcpConfig?.cleanup,
1135
+ cleanup: combineCleanup([mcpConfig?.cleanup, paseoExtension.cleanup]),
792
1136
  });
793
1137
  }
794
1138
  catch (error) {
795
1139
  await runtimeSession.close().catch(() => undefined);
796
1140
  mcpConfig?.cleanup();
1141
+ paseoExtension.cleanup();
797
1142
  throw error;
798
1143
  }
799
1144
  }
@@ -809,11 +1154,8 @@ export class PiRpcAgentClient {
809
1154
  async listModes(_options) {
810
1155
  return [];
811
1156
  }
812
- async listPersistedAgents(options) {
813
- return await listPiPersistedAgents({
814
- ...options,
815
- runtimeSettings: this.runtimeSettings,
816
- });
1157
+ async listPersistedAgents(_options) {
1158
+ return [];
817
1159
  }
818
1160
  async isAvailable() {
819
1161
  const binary = await this.resolvePiBinary();