@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
@@ -12,6 +12,7 @@ import { formatDiagnosticStatus, formatProviderDiagnostic, formatProviderDiagnos
12
12
  import { appendOrReplaceGrowingAssistantMessage, runProviderTurn } from "../provider-runner.js";
13
13
  import { renderPromptAttachmentAsText } from "../../prompt-attachments.js";
14
14
  import { claudeQuery } from "./query.js";
15
+ import { realClaudeRewindSdk, revertClaudeConversation, revertClaudeFiles } from "./rewind.js";
15
16
  import { normalizeProviderReplayTimestamp } from "../../provider-history-timestamps.js";
16
17
  import { getAgentStreamEventTurnId, } from "../../agent-sdk-types.js";
17
18
  import { createProviderEnv, createProviderEnvSpec, } from "../../provider-launch-config.js";
@@ -89,6 +90,9 @@ const CLAUDE_CAPABILITIES = {
89
90
  supportsMcpServers: true,
90
91
  supportsReasoningStream: true,
91
92
  supportsToolInvocations: true,
93
+ supportsRewindConversation: true,
94
+ supportsRewindFiles: true,
95
+ supportsRewindBoth: true,
92
96
  };
93
97
  const DEFAULT_MODES = [
94
98
  {
@@ -742,7 +746,7 @@ class TimelineAssembler {
742
746
  nextAssistantText !== INTERRUPT_TOOL_USE_PLACEHOLDER &&
743
747
  !isClaudeTranscriptNoiseText(nextAssistantText)) {
744
748
  state.emittedAssistantLength = state.assistantText.length;
745
- items.push({ type: "assistant_message", text: nextAssistantText });
749
+ items.push({ type: "assistant_message", text: nextAssistantText, messageId: state.id });
746
750
  }
747
751
  const nextReasoningText = state.reasoningText.slice(state.emittedReasoningLength);
748
752
  if (nextReasoningText.length > 0) {
@@ -834,7 +838,19 @@ function isSyntheticUserEntry(entry) {
834
838
  if (!candidate) {
835
839
  return false;
836
840
  }
837
- return candidate.isSynthetic === true || candidate.isMeta === true;
841
+ return (candidate.isSynthetic === true || candidate.isMeta === true || Boolean(candidate.toolUseResult));
842
+ }
843
+ function isToolResultUserEntry(entry) {
844
+ const candidate = toObjectRecord(entry);
845
+ if (!candidate) {
846
+ return false;
847
+ }
848
+ const message = toObjectRecord(candidate.message);
849
+ const content = message?.content;
850
+ return (Array.isArray(content) && content.some((block) => toObjectRecord(block)?.type === "tool_result"));
851
+ }
852
+ function isSyntheticHistoryUserEntry(entry) {
853
+ return isSyntheticUserEntry(entry) && !isToolResultUserEntry(entry);
838
854
  }
839
855
  function firstTrimmedString(sources) {
840
856
  for (const source of sources) {
@@ -845,13 +861,23 @@ function firstTrimmedString(sources) {
845
861
  }
846
862
  return null;
847
863
  }
864
+ function readTranscriptUuid(message) {
865
+ const root = toObjectRecord(message) ?? {};
866
+ const messageType = readTrimmedString(root.type);
867
+ if (messageType !== "user" && messageType !== "assistant") {
868
+ return null;
869
+ }
870
+ return firstTrimmedString([root.uuid]);
871
+ }
848
872
  export function readEventIdentifiers(message) {
849
873
  const root = toObjectRecord(message) ?? {};
850
874
  const messageType = readTrimmedString(root.type);
851
875
  const streamEvent = toObjectRecord(root.event);
852
876
  const streamEventMessage = toObjectRecord(streamEvent?.message);
853
877
  const messageContainer = toObjectRecord(root.message);
854
- const messageIdFromUuid = messageType === "user" ? root.uuid : undefined;
878
+ const messageIdFromUuid = messageType === "user" || messageType === "assistant" || messageType === "system"
879
+ ? root.uuid
880
+ : undefined;
855
881
  return {
856
882
  taskId: firstTrimmedString([
857
883
  root.task_id,
@@ -1165,10 +1191,12 @@ class ClaudeAgentSession {
1165
1191
  this.queryPumpPromise = null;
1166
1192
  this.queryRestartNeeded = false;
1167
1193
  this.pendingInterruptAbort = false;
1168
- this.lastForegroundPromptText = null;
1169
1194
  this.foregroundHasVisibleActivity = false;
1170
1195
  this.activeTurnHasAssistantText = false;
1171
1196
  this.userMessageIds = [];
1197
+ this.emittedUserMessageIds = new Set();
1198
+ this.rewindTurnAnchors = [];
1199
+ this.pendingFreshSessionId = null;
1172
1200
  this.recentStderr = "";
1173
1201
  this.closed = false;
1174
1202
  this.handlePermissionRequest = async (toolName, input, options) => {
@@ -1335,7 +1363,8 @@ class ClaudeAgentSession {
1335
1363
  this.completeAutonomousTurn();
1336
1364
  }
1337
1365
  const sdkMessage = this.toSdkUserMessage(prompt);
1338
- this.lastForegroundPromptText = this.extractPromptText(prompt);
1366
+ const sdkUserMessageId = typeof sdkMessage.uuid === "string" && sdkMessage.uuid.length > 0 ? sdkMessage.uuid : null;
1367
+ this.rememberRewindUserAnchor(sdkUserMessageId);
1339
1368
  const turnId = this.createTurnId("foreground");
1340
1369
  this.activeForegroundTurnId = turnId;
1341
1370
  this.foregroundHasVisibleActivity = false;
@@ -1370,6 +1399,11 @@ class ClaudeAgentSession {
1370
1399
  }
1371
1400
  this.startQueryPump();
1372
1401
  this.input.push(sdkMessage);
1402
+ setTimeout(() => {
1403
+ if (this.activeForegroundTurnId === turnId) {
1404
+ this.emitSubmittedUserMessage(sdkMessage, turnId);
1405
+ }
1406
+ }, 0);
1373
1407
  }
1374
1408
  catch (error) {
1375
1409
  this.finishForegroundTurn(this.buildTurnFailedEvent(error instanceof Error ? error.message : "Claude stream failed"));
@@ -1607,6 +1641,33 @@ class ClaudeAgentSession {
1607
1641
  }
1608
1642
  return Array.from(commandMap.values()).sort((a, b) => a.name.localeCompare(b.name));
1609
1643
  }
1644
+ async revertConversation(input) {
1645
+ const target = this.resolveConversationRewindTarget(input.messageId);
1646
+ if (target.kind === "fresh-session") {
1647
+ this.startFreshConversationSession();
1648
+ return;
1649
+ }
1650
+ await revertClaudeConversation({
1651
+ sdk: realClaudeRewindSdk,
1652
+ sessionId: this.claudeSessionId,
1653
+ messageId: target.messageId,
1654
+ resolveMessageId: (messageId) => this.resolveClaudeMessageId(messageId),
1655
+ setSessionId: (sessionId) => {
1656
+ this.rebindConversationSession(sessionId);
1657
+ },
1658
+ });
1659
+ }
1660
+ async revertFiles(input) {
1661
+ const messageId = await this.resolveClaudeMessageId(input.messageId);
1662
+ await revertClaudeFiles({
1663
+ query: await this.ensureQuery(),
1664
+ messageId,
1665
+ });
1666
+ }
1667
+ async revertBoth(input) {
1668
+ await this.revertFiles(input);
1669
+ await this.revertConversation(input);
1670
+ }
1610
1671
  resolveSlashCommandInvocation(prompt) {
1611
1672
  if (typeof prompt !== "string") {
1612
1673
  return null;
@@ -1722,10 +1783,6 @@ class ClaudeAgentSession {
1722
1783
  candidates.push(value);
1723
1784
  }
1724
1785
  };
1725
- const historyIds = this.readUserMessageIdsFromHistoryFile();
1726
- for (let idx = historyIds.length - 1; idx >= 0; idx -= 1) {
1727
- pushUnique(historyIds[idx]);
1728
- }
1729
1786
  for (let idx = this.persistedHistory.length - 1; idx >= 0; idx -= 1) {
1730
1787
  const entry = this.persistedHistory[idx];
1731
1788
  if (entry?.item.type === "user_message") {
@@ -1737,37 +1794,47 @@ class ClaudeAgentSession {
1737
1794
  }
1738
1795
  return candidates;
1739
1796
  }
1740
- readUserMessageIdsFromHistoryFile() {
1741
- if (!this.claudeSessionId) {
1742
- return [];
1743
- }
1744
- const historyPath = this.resolveHistoryPath(this.claudeSessionId);
1745
- if (!historyPath || !fs.existsSync(historyPath)) {
1746
- return [];
1747
- }
1748
- try {
1749
- const ids = [];
1750
- const content = fs.readFileSync(historyPath, "utf8");
1751
- for (const line of content.split(/\n+/)) {
1752
- const trimmed = line.trim();
1753
- if (!trimmed)
1754
- continue;
1755
- try {
1756
- const entry = JSON.parse(trimmed);
1757
- if (entry?.type === "user" && typeof entry.uuid === "string") {
1758
- ids.push(entry.uuid);
1759
- }
1760
- }
1761
- catch {
1762
- // ignore malformed lines
1763
- }
1764
- }
1765
- return ids;
1766
- }
1767
- catch {
1768
- return [];
1797
+ rebindConversationSession(sessionId) {
1798
+ const oldSessionId = this.claudeSessionId;
1799
+ this.claudeSessionId = sessionId;
1800
+ this.pendingFreshSessionId = null;
1801
+ this.persistence = null;
1802
+ this.cachedRuntimeInfo = null;
1803
+ this.queryRestartNeeded = true;
1804
+ this.persistedHistory = [];
1805
+ this.historyPending = false;
1806
+ this.userMessageIds = [];
1807
+ this.emittedUserMessageIds.clear();
1808
+ this.rewindTurnAnchors.length = 0;
1809
+ this.loadPersistedHistory(sessionId);
1810
+ if (oldSessionId && oldSessionId !== sessionId) {
1811
+ this.dispatchEvents([
1812
+ {
1813
+ type: "timeline",
1814
+ provider: "claude",
1815
+ item: this.createClaudeSessionChangedNotice(oldSessionId, sessionId),
1816
+ },
1817
+ {
1818
+ type: "thread_started",
1819
+ provider: "claude",
1820
+ sessionId,
1821
+ },
1822
+ ]);
1769
1823
  }
1770
1824
  }
1825
+ startFreshConversationSession() {
1826
+ const sessionId = randomUUID();
1827
+ this.claudeSessionId = sessionId;
1828
+ this.pendingFreshSessionId = sessionId;
1829
+ this.persistence = null;
1830
+ this.cachedRuntimeInfo = null;
1831
+ this.queryRestartNeeded = true;
1832
+ this.persistedHistory = [];
1833
+ this.historyPending = false;
1834
+ this.userMessageIds = [];
1835
+ this.emittedUserMessageIds.clear();
1836
+ this.rewindTurnAnchors.length = 0;
1837
+ }
1771
1838
  rememberUserMessageId(messageId) {
1772
1839
  if (typeof messageId !== "string" || messageId.length === 0) {
1773
1840
  return;
@@ -1778,6 +1845,78 @@ class ClaudeAgentSession {
1778
1845
  }
1779
1846
  this.userMessageIds.push(messageId);
1780
1847
  }
1848
+ rememberEmittedUserMessageId(messageId) {
1849
+ if (typeof messageId !== "string" || messageId.length === 0) {
1850
+ return;
1851
+ }
1852
+ this.emittedUserMessageIds.add(messageId);
1853
+ }
1854
+ rememberRewindUserAnchor(userMessageId) {
1855
+ if (typeof userMessageId !== "string" || userMessageId.length === 0) {
1856
+ return;
1857
+ }
1858
+ if (this.rewindTurnAnchors.some((anchor) => anchor.userMessageId === userMessageId)) {
1859
+ return;
1860
+ }
1861
+ this.rewindTurnAnchors.push({
1862
+ userMessageId,
1863
+ assistantMessageId: null,
1864
+ });
1865
+ }
1866
+ rememberRewindAssistantAnchor(assistantMessageId) {
1867
+ if (typeof assistantMessageId !== "string" || assistantMessageId.length === 0) {
1868
+ return;
1869
+ }
1870
+ for (let index = this.rewindTurnAnchors.length - 1; index >= 0; index -= 1) {
1871
+ const anchor = this.rewindTurnAnchors[index];
1872
+ if (!anchor) {
1873
+ continue;
1874
+ }
1875
+ anchor.assistantMessageId = assistantMessageId;
1876
+ return;
1877
+ }
1878
+ }
1879
+ rememberTranscriptProgress(message, messageId) {
1880
+ if (!messageId) {
1881
+ return;
1882
+ }
1883
+ if (message.type === "user" &&
1884
+ !isSyntheticUserEntry(message) &&
1885
+ !isToolResultUserEntry(message)) {
1886
+ this.rememberRewindUserAnchor(messageId);
1887
+ return;
1888
+ }
1889
+ if (message.type === "assistant") {
1890
+ this.rememberRewindAssistantAnchor(messageId);
1891
+ return;
1892
+ }
1893
+ if (message.type === "stream_event") {
1894
+ const event = toObjectRecord(message.event) ?? {};
1895
+ const eventType = readTrimmedString(event.type);
1896
+ if (eventType === "message_start") {
1897
+ this.rememberRewindAssistantAnchor(messageId);
1898
+ }
1899
+ return;
1900
+ }
1901
+ }
1902
+ resolveClaudeMessageId(messageId) {
1903
+ return messageId;
1904
+ }
1905
+ resolveConversationRewindTarget(messageId) {
1906
+ const targetUserMessageId = this.resolveClaudeMessageId(messageId);
1907
+ const index = this.rewindTurnAnchors.findIndex((anchor) => anchor.userMessageId === targetUserMessageId);
1908
+ if (index < 0) {
1909
+ throw new Error(`Claude rewind target ${messageId} is not in the tracked conversation`);
1910
+ }
1911
+ if (index === 0) {
1912
+ return { kind: "fresh-session" };
1913
+ }
1914
+ const previousTurn = this.rewindTurnAnchors[index - 1];
1915
+ if (!previousTurn?.assistantMessageId) {
1916
+ throw new Error(`Claude rewind cannot preserve turn ${index} because its assistant response id was not observed`);
1917
+ }
1918
+ return { kind: "fork", messageId: previousTurn.assistantMessageId };
1919
+ }
1781
1920
  async ensureQuery() {
1782
1921
  if (this.query && !this.queryRestartNeeded) {
1783
1922
  return this.query;
@@ -1893,6 +2032,13 @@ class ClaudeAgentSession {
1893
2032
  .toLowerCase()
1894
2033
  .includes("\\.local\\bin"),
1895
2034
  }, "Resolved Claude executable");
2035
+ const sessionBinding = {};
2036
+ if (this.pendingFreshSessionId) {
2037
+ sessionBinding.sessionId = this.pendingFreshSessionId;
2038
+ }
2039
+ else if (this.claudeSessionId) {
2040
+ sessionBinding.resume = this.claudeSessionId;
2041
+ }
1896
2042
  const base = {
1897
2043
  cwd: this.config.cwd,
1898
2044
  includePartialMessages: true,
@@ -1920,7 +2066,7 @@ class ClaudeAgentSession {
1920
2066
  enableFileCheckpointing: true,
1921
2067
  // If we have a session ID from a previous query (e.g., after interrupt),
1922
2068
  // resume that session to continue the conversation history.
1923
- ...(this.claudeSessionId ? { resume: this.claudeSessionId } : {}),
2069
+ ...sessionBinding,
1924
2070
  ...(thinking ? { thinking } : {}),
1925
2071
  ...(effort ? { effort } : {}),
1926
2072
  ...extraClaudeOptions,
@@ -1934,7 +2080,7 @@ class ClaudeAgentSession {
1934
2080
  base.model = this.config.model;
1935
2081
  }
1936
2082
  this.lastOptionsModel = base.model ?? null;
1937
- if (this.claudeSessionId) {
2083
+ if (this.claudeSessionId && !this.pendingFreshSessionId) {
1938
2084
  base.resume = this.claudeSessionId;
1939
2085
  }
1940
2086
  if (this.runtimeSettings?.disallowedTools?.length) {
@@ -2066,15 +2212,6 @@ class ClaudeAgentSession {
2066
2212
  event.type === "turn_failed" ||
2067
2213
  event.type === "turn_canceled");
2068
2214
  }
2069
- extractPromptText(prompt) {
2070
- if (typeof prompt === "string") {
2071
- return prompt;
2072
- }
2073
- const textParts = prompt
2074
- .filter((block) => block.type === "text")
2075
- .map((block) => block.text);
2076
- return textParts.length > 0 ? textParts.join("\n") : null;
2077
- }
2078
2215
  async executeRewindTurn(_turnId, invocation) {
2079
2216
  this.notifySubscribers({ type: "turn_started", provider: "claude" });
2080
2217
  try {
@@ -2128,7 +2265,6 @@ class ClaudeAgentSession {
2128
2265
  }
2129
2266
  this.notifySubscribers(event);
2130
2267
  this.activeForegroundTurnId = null;
2131
- this.lastForegroundPromptText = null;
2132
2268
  this.cancelCurrentTurn = null;
2133
2269
  this.activeTurnHasAssistantText = false;
2134
2270
  this.syncTurnState("foreground turn terminal");
@@ -2142,7 +2278,6 @@ class ClaudeAgentSession {
2142
2278
  if (terminalSeen) {
2143
2279
  if (this.activeForegroundTurnId) {
2144
2280
  this.activeForegroundTurnId = null;
2145
- this.lastForegroundPromptText = null;
2146
2281
  this.cancelCurrentTurn = null;
2147
2282
  this.activeTurnHasAssistantText = false;
2148
2283
  this.syncTurnState("foreground turn terminal");
@@ -2285,15 +2420,6 @@ class ClaudeAgentSession {
2285
2420
  }
2286
2421
  }
2287
2422
  }
2288
- isEchoedForegroundUserMessage(event) {
2289
- if (event.type !== "timeline" ||
2290
- event.item.type !== "user_message" ||
2291
- !this.activeForegroundTurnId ||
2292
- !this.lastForegroundPromptText) {
2293
- return false;
2294
- }
2295
- return event.item.text.trim() === this.lastForegroundPromptText.trim();
2296
- }
2297
2423
  shouldSuppressStaleResult(message) {
2298
2424
  // Suppress stale results from interrupted requests. The cancel path already
2299
2425
  // emitted the terminal event; this result is leftover from the killed API
@@ -2330,6 +2456,7 @@ class ClaudeAgentSession {
2330
2456
  }
2331
2457
  const turnId = this.activeForegroundTurnId ?? this.autonomousTurn?.id ?? null;
2332
2458
  const identifiers = readEventIdentifiers(message);
2459
+ this.rememberTranscriptProgress(message, readTranscriptUuid(message));
2333
2460
  this.logger.trace({
2334
2461
  agentId: this.agentId,
2335
2462
  provider: "claude",
@@ -2354,9 +2481,7 @@ class ClaudeAgentSession {
2354
2481
  item,
2355
2482
  provider: "claude",
2356
2483
  }));
2357
- // User message dedup: suppress echoed user messages that match the foreground prompt
2358
- const filteredMessageEvents = messageEvents.filter((event) => !this.isEchoedForegroundUserMessage(event));
2359
- const events = [...filteredMessageEvents, ...assistantTimelineEvents];
2484
+ const events = [...messageEvents, ...assistantTimelineEvents];
2360
2485
  if (events.length === 0) {
2361
2486
  return;
2362
2487
  }
@@ -2431,20 +2556,22 @@ class ClaudeAgentSession {
2431
2556
  return this.sidechainTracker.handleMessage(message, parentToolUseId);
2432
2557
  }
2433
2558
  const events = [];
2434
- const sessionCapture = this.captureSessionIdFromMessage(message);
2435
- if (sessionCapture.notice) {
2436
- events.push({
2437
- type: "timeline",
2438
- provider: "claude",
2439
- item: sessionCapture.notice,
2440
- });
2441
- }
2442
- if (sessionCapture.threadStartedSessionId) {
2443
- events.push({
2444
- type: "thread_started",
2445
- provider: "claude",
2446
- sessionId: sessionCapture.threadStartedSessionId,
2447
- });
2559
+ if (message.type !== "system") {
2560
+ const sessionCapture = this.captureSessionIdFromMessage(message);
2561
+ if (sessionCapture.notice) {
2562
+ events.push({
2563
+ type: "timeline",
2564
+ provider: "claude",
2565
+ item: sessionCapture.notice,
2566
+ });
2567
+ }
2568
+ if (sessionCapture.threadStartedSessionId) {
2569
+ events.push({
2570
+ type: "thread_started",
2571
+ provider: "claude",
2572
+ sessionId: sessionCapture.threadStartedSessionId,
2573
+ });
2574
+ }
2448
2575
  }
2449
2576
  switch (message.type) {
2450
2577
  case "system":
@@ -2474,6 +2601,22 @@ class ClaudeAgentSession {
2474
2601
  }
2475
2602
  return events;
2476
2603
  }
2604
+ emitSubmittedUserMessage(message, turnId) {
2605
+ const events = [];
2606
+ this.appendUserMessageEvents(message, events);
2607
+ if (events.length === 0) {
2608
+ return;
2609
+ }
2610
+ this.foregroundHasVisibleActivity = true;
2611
+ for (const event of events) {
2612
+ if (event.type === "timeline") {
2613
+ this.notifySubscribers({ ...event, turnId });
2614
+ }
2615
+ else {
2616
+ this.notifySubscribers(event);
2617
+ }
2618
+ }
2619
+ }
2477
2620
  appendSystemMessageEvents(message, events) {
2478
2621
  if (message.subtype === "init") {
2479
2622
  const sessionUpdate = this.handleSystemMessage(message);
@@ -2565,7 +2708,11 @@ class ClaudeAgentSession {
2565
2708
  return;
2566
2709
  }
2567
2710
  const messageId = typeof message.uuid === "string" && message.uuid.length > 0 ? message.uuid : undefined;
2711
+ if (messageId && this.emittedUserMessageIds.has(messageId)) {
2712
+ return;
2713
+ }
2568
2714
  this.rememberUserMessageId(messageId);
2715
+ this.rememberEmittedUserMessageId(messageId);
2569
2716
  const content = message.message?.content;
2570
2717
  const taskNotificationItem = mapTaskNotificationUserContentToToolCall({
2571
2718
  content,
@@ -2673,10 +2820,12 @@ class ClaudeAgentSession {
2673
2820
  }
2674
2821
  if (this.claudeSessionId === null) {
2675
2822
  this.claudeSessionId = sessionId;
2823
+ this.pendingFreshSessionId = null;
2676
2824
  this.persistence = null;
2677
2825
  return { threadStartedSessionId: sessionId, notice: null };
2678
2826
  }
2679
2827
  if (this.claudeSessionId === sessionId) {
2828
+ this.pendingFreshSessionId = null;
2680
2829
  return { threadStartedSessionId: null, notice: null };
2681
2830
  }
2682
2831
  const oldSessionId = this.claudeSessionId;
@@ -2685,6 +2834,7 @@ class ClaudeAgentSession {
2685
2834
  // not be failed just because the underlying subprocess cycled.
2686
2835
  this.logger.warn({ existingSessionId: this.claudeSessionId, newSessionId: sessionId }, "Claude session ID changed in message; accepting new session");
2687
2836
  this.claudeSessionId = sessionId;
2837
+ this.pendingFreshSessionId = null;
2688
2838
  this.persistence = null;
2689
2839
  return {
2690
2840
  threadStartedSessionId: sessionId,
@@ -2709,10 +2859,12 @@ class ClaudeAgentSession {
2709
2859
  let notice = null;
2710
2860
  if (existingSessionId === null) {
2711
2861
  this.claudeSessionId = newSessionId;
2862
+ this.pendingFreshSessionId = null;
2712
2863
  threadStartedSessionId = newSessionId;
2713
2864
  this.logger.debug({ sessionId: newSessionId }, "Claude session ID set for the first time");
2714
2865
  }
2715
2866
  else if (existingSessionId === newSessionId) {
2867
+ this.pendingFreshSessionId = null;
2716
2868
  this.logger.debug({ sessionId: newSessionId }, "Claude session ID unchanged (same value)");
2717
2869
  }
2718
2870
  else {
@@ -2720,6 +2872,7 @@ class ClaudeAgentSession {
2720
2872
  // with a new session mid-turn). Accept the new ID and continue.
2721
2873
  this.logger.warn({ existingSessionId, newSessionId }, "Claude session ID changed in init message; accepting new session");
2722
2874
  this.claudeSessionId = newSessionId;
2875
+ this.pendingFreshSessionId = null;
2723
2876
  threadStartedSessionId = newSessionId;
2724
2877
  notice = this.createClaudeSessionChangedNotice(existingSessionId, newSessionId);
2725
2878
  }
@@ -2960,11 +3113,19 @@ class ClaudeAgentSession {
2960
3113
  if (entry.isSidechain) {
2961
3114
  return;
2962
3115
  }
2963
- if (entry.type === "user" && typeof entry.uuid === "string") {
2964
- this.rememberUserMessageId(entry.uuid);
2965
- }
2966
3116
  const historyTimestamp = normalizeProviderReplayTimestamp(entry.timestamp);
2967
3117
  const items = this.convertHistoryEntry(entry);
3118
+ const isVisibleUserEntry = entry.type === "user" &&
3119
+ typeof entry.uuid === "string" &&
3120
+ !isSyntheticHistoryUserEntry(entry) &&
3121
+ !isToolResultUserEntry(entry);
3122
+ if (isVisibleUserEntry && typeof entry.uuid === "string") {
3123
+ this.rememberUserMessageId(entry.uuid);
3124
+ this.rememberRewindUserAnchor(entry.uuid);
3125
+ }
3126
+ if (entry.type === "assistant" && typeof entry.uuid === "string") {
3127
+ this.rememberRewindAssistantAnchor(entry.uuid);
3128
+ }
2968
3129
  if (items.length > 0) {
2969
3130
  timeline.push(...items.map((item) => ({
2970
3131
  item,
@@ -2976,10 +3137,26 @@ class ClaudeAgentSession {
2976
3137
  const cwd = this.config.cwd;
2977
3138
  if (!cwd)
2978
3139
  return null;
2979
- const sanitized = sanitizeClaudeProjectPath(cwd);
2980
3140
  const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
2981
- const dir = path.join(configDir, "projects", sanitized);
2982
- return path.join(dir, `${sessionId}.jsonl`);
3141
+ const candidates = [cwd];
3142
+ try {
3143
+ const realCwd = fs.realpathSync(cwd);
3144
+ if (realCwd !== cwd) {
3145
+ candidates.push(realCwd);
3146
+ }
3147
+ }
3148
+ catch {
3149
+ // Fall back to the configured cwd when the path has already disappeared.
3150
+ }
3151
+ for (const candidate of candidates) {
3152
+ const sanitized = sanitizeClaudeProjectPath(candidate);
3153
+ const historyPath = path.join(configDir, "projects", sanitized, `${sessionId}.jsonl`);
3154
+ if (fs.existsSync(historyPath)) {
3155
+ return historyPath;
3156
+ }
3157
+ }
3158
+ const sanitized = sanitizeClaudeProjectPath(cwd);
3159
+ return path.join(configDir, "projects", sanitized, `${sessionId}.jsonl`);
2983
3160
  }
2984
3161
  convertHistoryEntry(entry) {
2985
3162
  return convertClaudeHistoryEntry(entry, (content) => this.mapBlocksToTimeline(content));
@@ -3528,6 +3705,19 @@ function normalizeHistoryBlocks(content) {
3528
3705
  }
3529
3706
  return null;
3530
3707
  }
3708
+ function mapAssistantHistoryBlocksWithMessageId(entry, content, mapBlocks) {
3709
+ const items = mapBlocks(content);
3710
+ const assistantMessageId = typeof entry.uuid === "string" && entry.uuid.length > 0 ? entry.uuid : null;
3711
+ if (!assistantMessageId) {
3712
+ return items;
3713
+ }
3714
+ for (const item of items) {
3715
+ if (item.type === "assistant_message" && !item.messageId) {
3716
+ item.messageId = assistantMessageId;
3717
+ }
3718
+ }
3719
+ return items;
3720
+ }
3531
3721
  function convertClaudeHistoryEntryPreamble(entry) {
3532
3722
  if (entry.type === "system" && entry.subtype === "compact_boundary") {
3533
3723
  const compactMetadata = readCompactionMetadata(entry);
@@ -3549,7 +3739,7 @@ function convertClaudeHistoryEntryPreamble(entry) {
3549
3739
  if (entry.isCompactSummary) {
3550
3740
  return { shortCircuit: [] };
3551
3741
  }
3552
- if (entry.type === "user" && isSyntheticUserEntry(entry)) {
3742
+ if (entry.type === "user" && isSyntheticHistoryUserEntry(entry)) {
3553
3743
  return { shortCircuit: [] };
3554
3744
  }
3555
3745
  const message = entry?.message;
@@ -3604,7 +3794,7 @@ export function convertClaudeHistoryEntry(entry, mapBlocks) {
3604
3794
  return mapped;
3605
3795
  }
3606
3796
  if (entry.type === "assistant" && contentValue) {
3607
- return mapBlocks(contentValue);
3797
+ return mapAssistantHistoryBlocksWithMessageId(entry, contentValue, mapBlocks);
3608
3798
  }
3609
3799
  return timeline;
3610
3800
  }