@getpaseo/server 0.1.80 → 0.1.82

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 (216) hide show
  1. package/dist/server/client/daemon-client.d.ts +6 -2
  2. package/dist/server/client/daemon-client.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client.js +36 -1
  4. package/dist/server/client/daemon-client.js.map +1 -1
  5. package/dist/server/client/terminal-stream-router.d.ts +4 -0
  6. package/dist/server/client/terminal-stream-router.d.ts.map +1 -1
  7. package/dist/server/client/terminal-stream-router.js +8 -0
  8. package/dist/server/client/terminal-stream-router.js.map +1 -1
  9. package/dist/server/server/agent/agent-manager.d.ts +9 -7
  10. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  11. package/dist/server/server/agent/agent-manager.js +78 -92
  12. package/dist/server/server/agent/agent-manager.js.map +1 -1
  13. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
  14. package/dist/server/server/agent/agent-metadata-generator.js +6 -7
  15. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  16. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  17. package/dist/server/server/agent/agent-projections.js +4 -4
  18. package/dist/server/server/agent/agent-projections.js.map +1 -1
  19. package/dist/server/server/agent/agent-prompt.d.ts +2 -9
  20. package/dist/server/server/agent/agent-prompt.d.ts.map +1 -1
  21. package/dist/server/server/agent/agent-prompt.js +7 -18
  22. package/dist/server/server/agent/agent-prompt.js.map +1 -1
  23. package/dist/server/server/agent/agent-response-loop.d.ts +5 -0
  24. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -1
  25. package/dist/server/server/agent/agent-response-loop.js +12 -1
  26. package/dist/server/server/agent/agent-response-loop.js.map +1 -1
  27. package/dist/server/server/agent/agent-sdk-types.d.ts +13 -0
  28. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  29. package/dist/server/server/agent/agent-sdk-types.js.map +1 -1
  30. package/dist/server/server/agent/agent-storage.d.ts +2 -7
  31. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  32. package/dist/server/server/agent/agent-storage.js +2 -16
  33. package/dist/server/server/agent/agent-storage.js.map +1 -1
  34. package/dist/server/server/agent/agent-timeline-store-types.d.ts +0 -4
  35. package/dist/server/server/agent/agent-timeline-store-types.d.ts.map +1 -1
  36. package/dist/server/server/agent/agent-timeline-store.d.ts +0 -5
  37. package/dist/server/server/agent/agent-timeline-store.d.ts.map +1 -1
  38. package/dist/server/server/agent/agent-timeline-store.js +0 -33
  39. package/dist/server/server/agent/agent-timeline-store.js.map +1 -1
  40. package/dist/server/server/agent/import-sessions.d.ts.map +1 -1
  41. package/dist/server/server/agent/import-sessions.js +6 -0
  42. package/dist/server/server/agent/import-sessions.js.map +1 -1
  43. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  44. package/dist/server/server/agent/mcp-server.js +0 -9
  45. package/dist/server/server/agent/mcp-server.js.map +1 -1
  46. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  47. package/dist/server/server/agent/provider-launch-config.js +0 -1
  48. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  49. package/dist/server/server/agent/provider-manifest.d.ts +1 -1
  50. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  51. package/dist/server/server/agent/provider-registry.d.ts +2 -1
  52. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  53. package/dist/server/server/agent/provider-registry.js +5 -1
  54. package/dist/server/server/agent/provider-registry.js.map +1 -1
  55. package/dist/server/server/agent/provider-snapshot-manager.d.ts +2 -2
  56. package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
  57. package/dist/server/server/agent/provider-snapshot-manager.js +60 -26
  58. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  59. package/dist/server/server/agent/providers/acp-agent.d.ts.map +1 -1
  60. package/dist/server/server/agent/providers/acp-agent.js +3 -0
  61. package/dist/server/server/agent/providers/acp-agent.js.map +1 -1
  62. package/dist/server/server/agent/providers/claude/agent.d.ts.map +1 -1
  63. package/dist/server/server/agent/providers/claude/agent.js +275 -85
  64. package/dist/server/server/agent/providers/claude/agent.js.map +1 -1
  65. package/dist/server/server/agent/providers/claude/rewind.d.ts +30 -0
  66. package/dist/server/server/agent/providers/claude/rewind.d.ts.map +1 -0
  67. package/dist/server/server/agent/providers/claude/rewind.js +30 -0
  68. package/dist/server/server/agent/providers/claude/rewind.js.map +1 -0
  69. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts +24 -0
  70. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.d.ts.map +1 -0
  71. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js +23 -0
  72. package/dist/server/server/agent/providers/claude/test-rewind-claude-sdk.js.map +1 -0
  73. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts +168 -0
  74. package/dist/server/server/agent/providers/codex/app-server-transport.d.ts.map +1 -1
  75. package/dist/server/server/agent/providers/codex/app-server-transport.js +48 -0
  76. package/dist/server/server/agent/providers/codex/app-server-transport.js.map +1 -1
  77. package/dist/server/server/agent/providers/codex/rewind.d.ts +21 -0
  78. package/dist/server/server/agent/providers/codex/rewind.d.ts.map +1 -0
  79. package/dist/server/server/agent/providers/codex/rewind.js +46 -0
  80. package/dist/server/server/agent/providers/codex/rewind.js.map +1 -0
  81. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts +5 -0
  82. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.d.ts.map +1 -1
  83. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js +45 -0
  84. package/dist/server/server/agent/providers/codex/test-utils/fake-app-server.js.map +1 -1
  85. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts +1 -1
  86. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  87. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +11 -11
  88. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  89. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +32 -34
  90. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  91. package/dist/server/server/agent/providers/codex-app-server-agent.js +206 -60
  92. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  93. package/dist/server/server/agent/providers/copilot-acp-agent.d.ts.map +1 -1
  94. package/dist/server/server/agent/providers/copilot-acp-agent.js +3 -0
  95. package/dist/server/server/agent/providers/copilot-acp-agent.js.map +1 -1
  96. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts +13 -0
  97. package/dist/server/server/agent/providers/mock-load-test-agent.d.ts.map +1 -1
  98. package/dist/server/server/agent/providers/mock-load-test-agent.js +55 -1
  99. package/dist/server/server/agent/providers/mock-load-test-agent.js.map +1 -1
  100. package/dist/server/server/agent/providers/opencode/rewind.d.ts +18 -0
  101. package/dist/server/server/agent/providers/opencode/rewind.d.ts.map +1 -0
  102. package/dist/server/server/agent/providers/opencode/rewind.js +14 -0
  103. package/dist/server/server/agent/providers/opencode/rewind.js.map +1 -0
  104. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts +4 -0
  105. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.d.ts.map +1 -1
  106. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js +12 -2
  107. package/dist/server/server/agent/providers/opencode/test-utils/test-opencode-runtime.js.map +1 -1
  108. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.d.ts.map +1 -1
  109. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js +110 -2
  110. package/dist/server/server/agent/providers/opencode/tool-call-detail-parser.js.map +1 -1
  111. package/dist/server/server/agent/providers/opencode-agent.d.ts +18 -0
  112. package/dist/server/server/agent/providers/opencode-agent.d.ts.map +1 -1
  113. package/dist/server/server/agent/providers/opencode-agent.js +265 -42
  114. package/dist/server/server/agent/providers/opencode-agent.js.map +1 -1
  115. package/dist/server/server/agent/providers/pi/agent.d.ts +23 -1
  116. package/dist/server/server/agent/providers/pi/agent.d.ts.map +1 -1
  117. package/dist/server/server/agent/providers/pi/agent.js +470 -17
  118. package/dist/server/server/agent/providers/pi/agent.js.map +1 -1
  119. package/dist/server/server/agent/providers/pi/cli-runtime.js +4 -1
  120. package/dist/server/server/agent/providers/pi/cli-runtime.js.map +1 -1
  121. package/dist/server/server/agent/providers/pi/history-mapper.d.ts +5 -1
  122. package/dist/server/server/agent/providers/pi/history-mapper.d.ts.map +1 -1
  123. package/dist/server/server/agent/providers/pi/history-mapper.js +3 -2
  124. package/dist/server/server/agent/providers/pi/history-mapper.js.map +1 -1
  125. package/dist/server/server/agent/providers/pi/rewind.d.ts +8 -0
  126. package/dist/server/server/agent/providers/pi/rewind.d.ts.map +1 -0
  127. package/dist/server/server/agent/providers/pi/rewind.js +8 -0
  128. package/dist/server/server/agent/providers/pi/rewind.js.map +1 -0
  129. package/dist/server/server/agent/providers/pi/rpc-types.d.ts +10 -0
  130. package/dist/server/server/agent/providers/pi/rpc-types.d.ts.map +1 -1
  131. package/dist/server/server/agent/providers/pi/runtime.d.ts +7 -0
  132. package/dist/server/server/agent/providers/pi/runtime.d.ts.map +1 -1
  133. package/dist/server/server/agent/providers/pi/runtime.js +4 -0
  134. package/dist/server/server/agent/providers/pi/runtime.js.map +1 -1
  135. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts +23 -0
  136. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.d.ts.map +1 -1
  137. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js +54 -1
  138. package/dist/server/server/agent/providers/pi/test-utils/fake-pi.js.map +1 -1
  139. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +4 -1
  140. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  141. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +57 -1
  142. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  143. package/dist/server/server/agent/rewind/rewind.d.ts +10 -0
  144. package/dist/server/server/agent/rewind/rewind.d.ts.map +1 -0
  145. package/dist/server/server/agent/rewind/rewind.js +29 -0
  146. package/dist/server/server/agent/rewind/rewind.js.map +1 -0
  147. package/dist/server/server/agent/rewind/test-rewind-session.d.ts +48 -0
  148. package/dist/server/server/agent/rewind/test-rewind-session.d.ts.map +1 -0
  149. package/dist/server/server/agent/rewind/test-rewind-session.js +88 -0
  150. package/dist/server/server/agent/rewind/test-rewind-session.js.map +1 -0
  151. package/dist/server/server/persistence-hooks.d.ts.map +1 -1
  152. package/dist/server/server/persistence-hooks.js +0 -2
  153. package/dist/server/server/persistence-hooks.js.map +1 -1
  154. package/dist/server/server/session.d.ts +2 -0
  155. package/dist/server/server/session.d.ts.map +1 -1
  156. package/dist/server/server/session.js +39 -9
  157. package/dist/server/server/session.js.map +1 -1
  158. package/dist/server/server/websocket-server.d.ts.map +1 -1
  159. package/dist/server/server/websocket-server.js +4 -0
  160. package/dist/server/server/websocket-server.js.map +1 -1
  161. package/dist/server/server/worktree-branch-name-generator.d.ts +1 -0
  162. package/dist/server/server/worktree-branch-name-generator.d.ts.map +1 -1
  163. package/dist/server/server/worktree-branch-name-generator.js +5 -6
  164. package/dist/server/server/worktree-branch-name-generator.js.map +1 -1
  165. package/dist/server/shared/binary-frames/terminal.d.ts +1 -0
  166. package/dist/server/shared/binary-frames/terminal.d.ts.map +1 -1
  167. package/dist/server/shared/binary-frames/terminal.js +3 -1
  168. package/dist/server/shared/binary-frames/terminal.js.map +1 -1
  169. package/dist/server/shared/messages.d.ts +502 -0
  170. package/dist/server/shared/messages.d.ts.map +1 -1
  171. package/dist/server/shared/messages.js +43 -0
  172. package/dist/server/shared/messages.js.map +1 -1
  173. package/dist/server/shared/terminal-snapshot.d.ts +3 -0
  174. package/dist/server/shared/terminal-snapshot.d.ts.map +1 -0
  175. package/dist/server/shared/terminal-snapshot.js +165 -0
  176. package/dist/server/shared/terminal-snapshot.js.map +1 -0
  177. package/dist/server/terminal/terminal-manager.d.ts +2 -2
  178. package/dist/server/terminal/terminal-manager.d.ts.map +1 -1
  179. package/dist/server/terminal/terminal-manager.js +3 -3
  180. package/dist/server/terminal/terminal-manager.js.map +1 -1
  181. package/dist/server/terminal/terminal-restore.d.ts +17 -0
  182. package/dist/server/terminal/terminal-restore.d.ts.map +1 -0
  183. package/dist/server/terminal/terminal-restore.js +46 -0
  184. package/dist/server/terminal/terminal-restore.js.map +1 -0
  185. package/dist/server/terminal/terminal-session-controller.d.ts +3 -0
  186. package/dist/server/terminal/terminal-session-controller.d.ts.map +1 -1
  187. package/dist/server/terminal/terminal-session-controller.js +88 -27
  188. package/dist/server/terminal/terminal-session-controller.js.map +1 -1
  189. package/dist/server/terminal/terminal-worker-process.js +1 -1
  190. package/dist/server/terminal/terminal-worker-process.js.map +1 -1
  191. package/dist/server/terminal/terminal-worker-protocol.d.ts +2 -1
  192. package/dist/server/terminal/terminal-worker-protocol.d.ts.map +1 -1
  193. package/dist/server/terminal/terminal.d.ts +11 -2
  194. package/dist/server/terminal/terminal.d.ts.map +1 -1
  195. package/dist/server/terminal/terminal.js +33 -8
  196. package/dist/server/terminal/terminal.js.map +1 -1
  197. package/dist/server/terminal/worker-terminal-manager.d.ts.map +1 -1
  198. package/dist/server/terminal/worker-terminal-manager.js +16 -4
  199. package/dist/server/terminal/worker-terminal-manager.js.map +1 -1
  200. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  201. package/dist/server/utils/checkout-git.js +7 -7
  202. package/dist/server/utils/checkout-git.js.map +1 -1
  203. package/dist/src/server/agent/agent-sdk-types.js.map +1 -1
  204. package/dist/src/server/agent/provider-launch-config.js +0 -1
  205. package/dist/src/server/agent/provider-launch-config.js.map +1 -1
  206. package/dist/src/shared/messages.js +43 -0
  207. package/dist/src/shared/messages.js.map +1 -1
  208. package/package.json +3 -3
  209. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts +0 -9
  210. package/dist/server/server/agent/providers/codex-rollout-timeline.d.ts.map +0 -1
  211. package/dist/server/server/agent/providers/codex-rollout-timeline.js +0 -555
  212. package/dist/server/server/agent/providers/codex-rollout-timeline.js.map +0 -1
  213. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts +0 -10
  214. package/dist/server/server/agent/providers/pi/session-descriptor.d.ts.map +0 -1
  215. package/dist/server/server/agent/providers/pi/session-descriptor.js +0 -300
  216. 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,163 @@ 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);
382
+ }
383
+ function isRecord(value) {
384
+ return typeof value === "object" && value !== null && !Array.isArray(value);
385
+ }
386
+ function optionalString(value) {
387
+ return typeof value === "string" ? value : undefined;
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
+ }
425
+ function mapExtensionUiRequestToPermission(event) {
426
+ switch (event.method) {
427
+ case "select":
428
+ return buildExtensionUiQuestionPermission(event, {
429
+ question: optionalString(event.title) ?? "Select an option",
430
+ options: Array.isArray(event.options)
431
+ ? event.options.filter((option) => typeof option === "string")
432
+ : [],
433
+ multiSelect: false,
434
+ });
435
+ case "input":
436
+ return buildExtensionUiQuestionPermission(event, {
437
+ question: optionalString(event.title) ?? "Enter a value",
438
+ options: [],
439
+ multiSelect: false,
440
+ });
441
+ case "editor":
442
+ return buildExtensionUiQuestionPermission(event, {
443
+ question: optionalString(event.title) ?? "Edit text",
444
+ options: [],
445
+ multiSelect: false,
446
+ });
447
+ case "confirm":
448
+ return buildExtensionUiQuestionPermission(event, {
449
+ question: [optionalString(event.title), optionalString(event.message)]
450
+ .filter(Boolean)
451
+ .join("\n\n"),
452
+ options: ["Yes", "No"],
453
+ multiSelect: false,
454
+ });
455
+ default:
456
+ return null;
457
+ }
458
+ }
459
+ function buildExtensionUiQuestionPermission(event, input) {
460
+ const header = "Response";
461
+ return {
462
+ id: event.id,
463
+ provider: PI_PROVIDER,
464
+ name: `Pi ${event.method}`,
465
+ kind: "question",
466
+ title: input.question,
467
+ input: {
468
+ questions: [
469
+ {
470
+ question: input.question,
471
+ header,
472
+ options: input.options.map((label) => ({ label })),
473
+ multiSelect: input.multiSelect,
474
+ },
475
+ ],
476
+ },
477
+ metadata: {
478
+ extensionUiMethod: event.method,
479
+ answerHeader: header,
480
+ },
481
+ };
482
+ }
483
+ function firstPermissionAnswer(input) {
484
+ const answers = isRecord(input?.answers) ? input.answers : null;
485
+ if (!answers) {
486
+ return null;
487
+ }
488
+ const first = Object.values(answers).find((value) => typeof value === "string");
489
+ return typeof first === "string" ? first : null;
490
+ }
491
+ function buildExtensionUiResponse(request, response) {
492
+ if (response.behavior === "deny") {
493
+ return { cancelled: true };
494
+ }
495
+ const method = optionalString(request.metadata?.extensionUiMethod);
496
+ const answer = firstPermissionAnswer(response.updatedInput);
497
+ if (answer === null) {
498
+ return { cancelled: true };
499
+ }
500
+ if (method === "confirm") {
501
+ return { confirmed: /^yes$/i.test(answer.trim()) };
502
+ }
503
+ return { value: answer };
247
504
  }
248
505
  function mapPiModel(model) {
249
506
  return {
@@ -267,7 +524,13 @@ export class PiRpcAgentSession {
267
524
  this.provider = PI_PROVIDER;
268
525
  this.subscribers = new Set();
269
526
  this.activeToolCalls = new Map();
527
+ this.pendingExtensionUiRequests = new Map();
270
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();
271
534
  this.closed = false;
272
535
  this.runtimeSession = options.runtimeSession;
273
536
  this.config = options.config;
@@ -330,7 +593,8 @@ export class PiRpcAgentSession {
330
593
  };
331
594
  }
332
595
  async *streamHistory() {
333
- yield* streamPiHistory(PI_PROVIDER, await this.runtimeSession.getMessages());
596
+ await this.requestEntryCapture("history");
597
+ yield* streamPiHistory(PI_PROVIDER, await this.runtimeSession.getMessages(), this.capturedUserEntries);
334
598
  }
335
599
  async getRuntimeInfo() {
336
600
  await this.refreshState();
@@ -353,11 +617,22 @@ export class PiRpcAgentSession {
353
617
  throw new Error("Pi does not expose selectable modes");
354
618
  }
355
619
  getPendingPermissions() {
356
- return [];
620
+ return [...this.pendingExtensionUiRequests.values()];
357
621
  }
358
622
  async respondToPermission(requestId, response) {
359
- void requestId;
360
- void response;
623
+ const request = this.pendingExtensionUiRequests.get(requestId);
624
+ if (!request) {
625
+ throw new Error(`No pending permission request with id '${requestId}'`);
626
+ }
627
+ this.pendingExtensionUiRequests.delete(requestId);
628
+ this.runtimeSession.respondToExtensionUiRequest(requestId, buildExtensionUiResponse(request, response));
629
+ this.emit({
630
+ type: "permission_resolved",
631
+ provider: PI_PROVIDER,
632
+ requestId,
633
+ resolution: response,
634
+ turnId: this.currentTurnIdForEvent(),
635
+ });
361
636
  }
362
637
  describePersistence() {
363
638
  return {
@@ -374,6 +649,33 @@ export class PiRpcAgentSession {
374
649
  async interrupt() {
375
650
  await this.runtimeSession.abort();
376
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
+ }
377
679
  async close() {
378
680
  if (this.closed) {
379
681
  return;
@@ -383,6 +685,7 @@ export class PiRpcAgentSession {
383
685
  await this.runtimeSession.close();
384
686
  }
385
687
  finally {
688
+ this.rejectAllExtensionResults(new Error("Pi session closed"));
386
689
  this.cleanup?.();
387
690
  }
388
691
  }
@@ -427,9 +730,129 @@ export class PiRpcAgentSession {
427
730
  currentTurnIdForEvent() {
428
731
  return this.activeTurnId ?? undefined;
429
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
+ }
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
+ }
841
+ const request = mapExtensionUiRequestToPermission(event);
842
+ if (!request) {
843
+ return;
844
+ }
845
+ this.pendingExtensionUiRequests.set(request.id, request);
846
+ this.emit({
847
+ type: "permission_requested",
848
+ provider: PI_PROVIDER,
849
+ request,
850
+ turnId: this.currentTurnIdForEvent(),
851
+ });
852
+ }
430
853
  handleRuntimeEvent(event) {
431
854
  if (event.type === "extension_ui_request") {
432
- this.runtimeSession.cancelExtensionUiRequest(event.id);
855
+ this.handleExtensionUiRequest(event);
433
856
  return;
434
857
  }
435
858
  if (event.type === "process_exit") {
@@ -439,6 +862,7 @@ export class PiRpcAgentSession {
439
862
  this.handleSessionEvent(event);
440
863
  }
441
864
  handleProcessExit(error) {
865
+ this.rejectAllExtensionResults(new Error(error));
442
866
  if (!this.activeTurnId) {
443
867
  return;
444
868
  }
@@ -468,6 +892,11 @@ export class PiRpcAgentSession {
468
892
  turnId,
469
893
  });
470
894
  return;
895
+ case "message_start":
896
+ return;
897
+ case "message_end":
898
+ this.handleMessageEnd(event, turnId);
899
+ return;
471
900
  case "message_update":
472
901
  this.handleMessageUpdate(event, turnId);
473
902
  return;
@@ -553,6 +982,25 @@ export class PiRpcAgentSession {
553
982
  });
554
983
  }
555
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
+ }
556
1004
  emitToolCallEvent(toolCallId, toolCall, status, result, error) {
557
1005
  const turnId = this.currentTurnIdForEvent();
558
1006
  const detail = mapToolDetail(toolCall, result);
@@ -618,6 +1066,7 @@ export class PiRpcAgentClient {
618
1066
  }
619
1067
  async createSession(config, launchContext) {
620
1068
  const mcpConfig = await this.prepareMcpConfig(config.cwd, config.mcpServers);
1069
+ const paseoExtension = createPiPaseoExtensionFile();
621
1070
  let runtimeSession;
622
1071
  try {
623
1072
  runtimeSession = await this.runtime.startSession({
@@ -627,10 +1076,12 @@ export class PiRpcAgentClient {
627
1076
  systemPrompt: composeSystemPromptParts(config.systemPrompt, config.daemonAppendSystemPrompt),
628
1077
  env: launchContext?.env,
629
1078
  mcpConfigPath: mcpConfig?.path,
1079
+ extensionPaths: [paseoExtension.path],
630
1080
  });
631
1081
  }
632
1082
  catch (error) {
633
1083
  mcpConfig?.cleanup();
1084
+ paseoExtension.cleanup();
634
1085
  throw error;
635
1086
  }
636
1087
  try {
@@ -639,12 +1090,13 @@ export class PiRpcAgentClient {
639
1090
  config,
640
1091
  initialState: await runtimeSession.getState(),
641
1092
  capabilities: withPiMcpCapability(mcpConfig !== null),
642
- cleanup: mcpConfig?.cleanup,
1093
+ cleanup: combineCleanup([mcpConfig?.cleanup, paseoExtension.cleanup]),
643
1094
  });
644
1095
  }
645
1096
  catch (error) {
646
1097
  await runtimeSession.close().catch(() => undefined);
647
1098
  mcpConfig?.cleanup();
1099
+ paseoExtension.cleanup();
648
1100
  throw error;
649
1101
  }
650
1102
  }
@@ -656,6 +1108,7 @@ export class PiRpcAgentClient {
656
1108
  const persistenceMetadata = parsePersistenceMetadata(handle.metadata);
657
1109
  const resumeConfig = buildResumeConfig(persistenceMetadata, overrides);
658
1110
  const mcpConfig = await this.prepareMcpConfig(resumeConfig.cwd, resumeConfig.config.mcpServers);
1111
+ const paseoExtension = createPiPaseoExtensionFile();
659
1112
  let runtimeSession;
660
1113
  try {
661
1114
  runtimeSession = await this.runtime.startSession({
@@ -665,10 +1118,12 @@ export class PiRpcAgentClient {
665
1118
  thinkingOptionId: normalizePiThinkingOption(resumeConfig.thinkingOptionId) ?? undefined,
666
1119
  systemPrompt: composeSystemPromptParts(resumeConfig.config.systemPrompt, resumeConfig.config.daemonAppendSystemPrompt),
667
1120
  mcpConfigPath: mcpConfig?.path,
1121
+ extensionPaths: [paseoExtension.path],
668
1122
  });
669
1123
  }
670
1124
  catch (error) {
671
1125
  mcpConfig?.cleanup();
1126
+ paseoExtension.cleanup();
672
1127
  throw error;
673
1128
  }
674
1129
  try {
@@ -677,12 +1132,13 @@ export class PiRpcAgentClient {
677
1132
  config: resumeConfig.config,
678
1133
  initialState: await runtimeSession.getState(),
679
1134
  capabilities: withPiMcpCapability(mcpConfig !== null),
680
- cleanup: mcpConfig?.cleanup,
1135
+ cleanup: combineCleanup([mcpConfig?.cleanup, paseoExtension.cleanup]),
681
1136
  });
682
1137
  }
683
1138
  catch (error) {
684
1139
  await runtimeSession.close().catch(() => undefined);
685
1140
  mcpConfig?.cleanup();
1141
+ paseoExtension.cleanup();
686
1142
  throw error;
687
1143
  }
688
1144
  }
@@ -698,11 +1154,8 @@ export class PiRpcAgentClient {
698
1154
  async listModes(_options) {
699
1155
  return [];
700
1156
  }
701
- async listPersistedAgents(options) {
702
- return await listPiPersistedAgents({
703
- ...options,
704
- runtimeSettings: this.runtimeSettings,
705
- });
1157
+ async listPersistedAgents(_options) {
1158
+ return [];
706
1159
  }
707
1160
  async isAvailable() {
708
1161
  const binary = await this.resolvePiBinary();