@markusylisiurunen/tau 0.2.129 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/README.md +111 -59
  2. package/dist/core/async/cli.js +12 -2
  3. package/dist/core/async/cli.js.map +1 -1
  4. package/dist/core/async/http_protocol.js +7 -1
  5. package/dist/core/async/http_protocol.js.map +1 -1
  6. package/dist/core/async/http_server.js +9 -1
  7. package/dist/core/async/http_server.js.map +1 -1
  8. package/dist/core/async/index.js.map +1 -1
  9. package/dist/core/async/session_manager.js +94 -51
  10. package/dist/core/async/session_manager.js.map +1 -1
  11. package/dist/core/cli.js +12 -15
  12. package/dist/core/cli.js.map +1 -1
  13. package/dist/core/commands/registry.js +9 -55
  14. package/dist/core/commands/registry.js.map +1 -1
  15. package/dist/core/config/index.js.map +1 -1
  16. package/dist/core/config/runtime.js +0 -1
  17. package/dist/core/config/runtime.js.map +1 -1
  18. package/dist/core/config/schema.js +102 -17
  19. package/dist/core/config/schema.js.map +1 -1
  20. package/dist/core/debug.js +1 -20
  21. package/dist/core/debug.js.map +1 -1
  22. package/dist/core/diff_review/{session.js → bridge.js} +57 -114
  23. package/dist/core/diff_review/bridge.js.map +1 -0
  24. package/dist/core/diff_review/index.js +2 -3
  25. package/dist/core/diff_review/index.js.map +1 -1
  26. package/dist/core/diff_review/review_thread.js +48 -23
  27. package/dist/core/diff_review/review_thread.js.map +1 -1
  28. package/dist/core/diff_review/snapshot.js +40 -11
  29. package/dist/core/diff_review/snapshot.js.map +1 -1
  30. package/dist/core/events/parser.js +43 -12
  31. package/dist/core/events/parser.js.map +1 -1
  32. package/dist/core/index.js +0 -1
  33. package/dist/core/index.js.map +1 -1
  34. package/dist/core/modes/index.js +1 -2
  35. package/dist/core/modes/index.js.map +1 -1
  36. package/dist/core/modes/rpc_server.js +15 -321
  37. package/dist/core/modes/rpc_server.js.map +1 -1
  38. package/dist/core/modes/websocket_server.js +181 -0
  39. package/dist/core/modes/websocket_server.js.map +1 -0
  40. package/dist/core/personas.js +11 -1
  41. package/dist/core/personas.js.map +1 -1
  42. package/dist/core/runtime/chat_runtime.js +19 -12
  43. package/dist/core/runtime/chat_runtime.js.map +1 -1
  44. package/dist/core/runtime/conversation_turn_runtime.js +16 -1
  45. package/dist/core/runtime/conversation_turn_runtime.js.map +1 -1
  46. package/dist/core/runtime/runtime_bootstrap.js +326 -0
  47. package/dist/core/runtime/runtime_bootstrap.js.map +1 -1
  48. package/dist/core/session/compaction.js +13 -11
  49. package/dist/core/session/compaction.js.map +1 -1
  50. package/dist/core/session/core_session.js +9 -0
  51. package/dist/core/session/core_session.js.map +1 -1
  52. package/dist/core/session/direct_bash.js +34 -0
  53. package/dist/core/session/direct_bash.js.map +1 -0
  54. package/dist/core/session/pruning.js +595 -0
  55. package/dist/core/session/pruning.js.map +1 -0
  56. package/dist/core/session/runner.js +45 -5
  57. package/dist/core/session/runner.js.map +1 -1
  58. package/dist/core/session/session_engine.js +84 -10
  59. package/dist/core/session/session_engine.js.map +1 -1
  60. package/dist/core/static/prompts/diff-review-wrapper.md +1 -1
  61. package/dist/core/subagents/subagent_engine.js +1 -1
  62. package/dist/core/subagents/subagent_engine.js.map +1 -1
  63. package/dist/core/tools/catalog.js.map +1 -1
  64. package/dist/core/tools/diff_review.js +12 -12
  65. package/dist/core/tools/diff_review.js.map +1 -1
  66. package/dist/core/tools/execution_backend.js +9 -2
  67. package/dist/core/tools/execution_backend.js.map +1 -1
  68. package/dist/core/tools/registry.js.map +1 -1
  69. package/dist/core/usage/logs.js.map +1 -1
  70. package/dist/core/utils/context.js +1 -1
  71. package/dist/core/utils/context.js.map +1 -1
  72. package/dist/core/utils/context_builder.js +1 -18
  73. package/dist/core/utils/context_builder.js.map +1 -1
  74. package/dist/core/utils/gemini_speech.js +1 -1
  75. package/dist/core/utils/gemini_speech.js.map +1 -1
  76. package/dist/core/utils/project_files.js +11 -16
  77. package/dist/core/utils/project_files.js.map +1 -1
  78. package/dist/core/utils/spawn_capture.js +2 -2
  79. package/dist/core/utils/spawn_capture.js.map +1 -1
  80. package/dist/core/version.js +1 -1
  81. package/dist/core/version.js.map +1 -1
  82. package/dist/execution/cloudflare_sandbox_execution_environment.js +400 -0
  83. package/dist/execution/cloudflare_sandbox_execution_environment.js.map +1 -0
  84. package/dist/execution/execution_environment.js +24 -0
  85. package/dist/execution/execution_environment.js.map +1 -0
  86. package/dist/execution/fly_sprite_execution_environment.js +559 -0
  87. package/dist/execution/fly_sprite_execution_environment.js.map +1 -0
  88. package/dist/execution/local_execution_environment.js +93 -0
  89. package/dist/execution/local_execution_environment.js.map +1 -0
  90. package/dist/execution/runtime_config_snapshot.js +209 -0
  91. package/dist/execution/runtime_config_snapshot.js.map +1 -0
  92. package/dist/execution/sandbox_tool_helpers.js +55 -0
  93. package/dist/execution/sandbox_tool_helpers.js.map +1 -0
  94. package/dist/execution/tool_backend_execution_environment.js +55 -0
  95. package/dist/execution/tool_backend_execution_environment.js.map +1 -0
  96. package/dist/host/hosted_ephemeral_agent_session.js +286 -0
  97. package/dist/host/hosted_ephemeral_agent_session.js.map +1 -0
  98. package/dist/host/index.js +3 -0
  99. package/dist/host/index.js.map +1 -0
  100. package/dist/host/local_session_host.js +1464 -0
  101. package/dist/host/local_session_host.js.map +1 -0
  102. package/dist/host/session_host.js +2 -0
  103. package/dist/host/session_host.js.map +1 -0
  104. package/dist/host/session_protocol_handler.js +873 -0
  105. package/dist/host/session_protocol_handler.js.map +1 -0
  106. package/dist/main.js +537 -77
  107. package/dist/main.js.map +1 -1
  108. package/dist/protocol/index.d.ts +2 -0
  109. package/dist/protocol/index.js +2 -0
  110. package/dist/protocol/index.js.map +1 -0
  111. package/dist/protocol/session_protocol.d.ts +691 -0
  112. package/dist/protocol/session_protocol.js +2117 -0
  113. package/dist/protocol/session_protocol.js.map +1 -0
  114. package/dist/sdk/client.d.ts +2 -1
  115. package/dist/sdk/client.js +136 -392
  116. package/dist/sdk/client.js.map +1 -1
  117. package/dist/sdk/index.d.ts +7 -4
  118. package/dist/sdk/index.js +4 -2
  119. package/dist/sdk/index.js.map +1 -1
  120. package/dist/sdk/session.d.ts +5 -0
  121. package/dist/sdk/session.js +406 -0
  122. package/dist/sdk/session.js.map +1 -0
  123. package/dist/sdk/types.d.ts +97 -78
  124. package/dist/store/file_session_store.js +157 -0
  125. package/dist/store/file_session_store.js.map +1 -0
  126. package/dist/store/memory_session_store.js +24 -0
  127. package/dist/store/memory_session_store.js.map +1 -0
  128. package/dist/store/session_store.js +32 -0
  129. package/dist/store/session_store.js.map +1 -0
  130. package/dist/transport/errors.d.ts +29 -0
  131. package/dist/{sdk → transport}/errors.js +5 -5
  132. package/dist/transport/errors.js.map +1 -0
  133. package/dist/transport/in_process_session_transport.js +102 -0
  134. package/dist/transport/in_process_session_transport.js.map +1 -0
  135. package/dist/transport/index.d.ts +7 -0
  136. package/dist/transport/index.js +4 -0
  137. package/dist/transport/index.js.map +1 -0
  138. package/dist/transport/pending_session_protocol_requests.js +67 -0
  139. package/dist/transport/pending_session_protocol_requests.js.map +1 -0
  140. package/dist/transport/session_protocol_transport_helpers.js +98 -0
  141. package/dist/transport/session_protocol_transport_helpers.js.map +1 -0
  142. package/dist/transport/session_transport.d.ts +11 -0
  143. package/dist/transport/session_transport.js +2 -0
  144. package/dist/transport/session_transport.js.map +1 -0
  145. package/dist/transport/stdio_session_transport.d.ts +38 -0
  146. package/dist/transport/stdio_session_transport.js +243 -0
  147. package/dist/transport/stdio_session_transport.js.map +1 -0
  148. package/dist/transport/websocket_session_transport.d.ts +52 -0
  149. package/dist/transport/websocket_session_transport.js +235 -0
  150. package/dist/transport/websocket_session_transport.js.map +1 -0
  151. package/dist/tui/chat_controller/assistant_clipboard.js +39 -0
  152. package/dist/tui/chat_controller/assistant_clipboard.js.map +1 -0
  153. package/dist/tui/chat_controller/command_hints.js +43 -0
  154. package/dist/tui/chat_controller/command_hints.js.map +1 -0
  155. package/dist/tui/chat_controller/diff_review_service.js +9 -9
  156. package/dist/tui/chat_controller/diff_review_service.js.map +1 -1
  157. package/dist/tui/chat_controller/diff_review_user_message.js +17 -0
  158. package/dist/tui/chat_controller/diff_review_user_message.js.map +1 -0
  159. package/dist/tui/chat_controller/history_labels.js +8 -0
  160. package/dist/tui/chat_controller/history_labels.js.map +1 -0
  161. package/dist/tui/chat_controller/history_message_model.js +36 -0
  162. package/dist/tui/chat_controller/history_message_model.js.map +1 -0
  163. package/dist/tui/chat_controller/status_format.js +22 -0
  164. package/dist/tui/chat_controller/status_format.js.map +1 -0
  165. package/dist/tui/chat_view.js +0 -1
  166. package/dist/tui/chat_view.js.map +1 -1
  167. package/dist/tui/index.js +1 -1
  168. package/dist/tui/index.js.map +1 -1
  169. package/dist/tui/listen_capture.js +87 -0
  170. package/dist/tui/listen_capture.js.map +1 -0
  171. package/dist/tui/memory_mode.js +23 -0
  172. package/dist/tui/memory_mode.js.map +1 -0
  173. package/dist/tui/session_chat_app.js +227 -0
  174. package/dist/tui/session_chat_app.js.map +1 -0
  175. package/dist/tui/session_chat_controller.js +2176 -0
  176. package/dist/tui/session_chat_controller.js.map +1 -0
  177. package/dist/tui/session_tool_execution_backend.js +170 -0
  178. package/dist/tui/session_tool_execution_backend.js.map +1 -0
  179. package/dist/tui/speech_playback.js +107 -0
  180. package/dist/tui/speech_playback.js.map +1 -0
  181. package/dist/tui/ui/assistant_message.js +27 -21
  182. package/dist/tui/ui/assistant_message.js.map +1 -1
  183. package/dist/tui/ui/chat_container.js +4 -0
  184. package/dist/tui/ui/chat_container.js.map +1 -1
  185. package/dist/tui/ui/custom_editor.js +0 -5
  186. package/dist/tui/ui/custom_editor.js.map +1 -1
  187. package/dist/tui/ui/slash_autocomplete.js +14 -36
  188. package/dist/tui/ui/slash_autocomplete.js.map +1 -1
  189. package/dist/tui/ui/tool_ui_registry.js +21 -0
  190. package/dist/tui/ui/tool_ui_registry.js.map +1 -1
  191. package/package.json +6 -3
  192. package/dist/core/config/bash_commands.js +0 -44
  193. package/dist/core/config/bash_commands.js.map +0 -1
  194. package/dist/core/diff_review/session.js.map +0 -1
  195. package/dist/core/modes/rpc_adapter.js +0 -17
  196. package/dist/core/modes/rpc_adapter.js.map +0 -1
  197. package/dist/core/modes/rpc_protocol.js +0 -500
  198. package/dist/core/modes/rpc_protocol.js.map +0 -1
  199. package/dist/core/session/checkpoint.js +0 -34
  200. package/dist/core/session/checkpoint.js.map +0 -1
  201. package/dist/sdk/errors.d.ts +0 -33
  202. package/dist/sdk/errors.js.map +0 -1
  203. package/dist/tui/app.js +0 -64
  204. package/dist/tui/app.js.map +0 -1
  205. package/dist/tui/chat_controller/session_maintenance_service.js +0 -634
  206. package/dist/tui/chat_controller/session_maintenance_service.js.map +0 -1
  207. package/dist/tui/chat_controller.js +0 -2579
  208. package/dist/tui/chat_controller.js.map +0 -1
@@ -0,0 +1,873 @@
1
+ import { formatSteeringUserMessage } from "../core/runtime/steering.js";
2
+ import { createSessionProtocolErrorResponse, createSessionProtocolReadyMessage, createSessionProtocolSuccessResponse, SESSION_PROTOCOL_ERROR_CODES, SESSION_PROTOCOL_METHODS, SESSION_PROTOCOL_VERSION, } from "../protocol/session_protocol.js";
3
+ const sessionMutationQueues = new WeakMap();
4
+ function getSessionMutationQueueState(session) {
5
+ let state = sessionMutationQueues.get(session);
6
+ if (!state) {
7
+ state = { queue: Promise.resolve(), pending: 0 };
8
+ sessionMutationQueues.set(session, state);
9
+ }
10
+ return state;
11
+ }
12
+ export class SessionProtocolHandler {
13
+ host;
14
+ send;
15
+ sessionStates = new Map();
16
+ initialized = false;
17
+ closed = false;
18
+ constructor(options) {
19
+ this.host = options.host;
20
+ this.send = options.send;
21
+ this.emitReady();
22
+ }
23
+ async handleRequest(request) {
24
+ if (this.closed) {
25
+ return;
26
+ }
27
+ try {
28
+ switch (request.method) {
29
+ case "initialize":
30
+ this.handleInitialize(request);
31
+ return;
32
+ case "session.create":
33
+ await this.handleCreate(request);
34
+ return;
35
+ case "session.list":
36
+ await this.handleList(request);
37
+ return;
38
+ case "session.observe":
39
+ await this.handleAttach(request);
40
+ return;
41
+ case "session.unobserve":
42
+ await this.handleUnobserve(request);
43
+ return;
44
+ case "session.record":
45
+ await this.handleRecord(request);
46
+ return;
47
+ case "session.submit":
48
+ await this.handleSubmit(request);
49
+ return;
50
+ case "session.queue":
51
+ await this.handleQueue(request);
52
+ return;
53
+ case "session.steer":
54
+ await this.handleSteer(request);
55
+ return;
56
+ case "session.retry":
57
+ await this.handleRetry(request);
58
+ return;
59
+ case "session.exec":
60
+ await this.handleExec(request);
61
+ return;
62
+ case "session.interrupt":
63
+ await this.handleInterrupt(request);
64
+ return;
65
+ case "session.snapshot":
66
+ await this.handleSnapshot(request);
67
+ return;
68
+ case "session.setRisk":
69
+ await this.handleSetRisk(request);
70
+ return;
71
+ case "session.setReasoning":
72
+ await this.handleSetReasoning(request);
73
+ return;
74
+ case "session.setPersona":
75
+ await this.handleSetPersona(request);
76
+ return;
77
+ case "session.resolvePrompt":
78
+ await this.handleResolvePrompt(request);
79
+ return;
80
+ case "session.autocompletePaths":
81
+ await this.handleAutocompletePaths(request);
82
+ return;
83
+ case "session.reload":
84
+ await this.handleReload(request);
85
+ return;
86
+ case "session.compact":
87
+ await this.handleCompact(request);
88
+ return;
89
+ case "session.prune":
90
+ await this.handlePrune(request);
91
+ return;
92
+ case "session.rewind":
93
+ await this.handleRewind(request);
94
+ return;
95
+ case "session.terminateSubagent":
96
+ await this.handleTerminateSubagent(request);
97
+ return;
98
+ case "session.ephemeral.create":
99
+ await this.handleEphemeralCreate(request);
100
+ return;
101
+ case "session.ephemeral.submit":
102
+ await this.handleEphemeralSubmit(request);
103
+ return;
104
+ case "session.ephemeral.close":
105
+ await this.handleEphemeralClose(request);
106
+ return;
107
+ }
108
+ }
109
+ catch (error) {
110
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, SESSION_PROTOCOL_ERROR_CODES.internalError, "session protocol request failed", {
111
+ cause: error instanceof Error ? error.message : String(error),
112
+ }));
113
+ }
114
+ }
115
+ async close(mode) {
116
+ if (this.closed) {
117
+ return;
118
+ }
119
+ this.closed = true;
120
+ const shutdownHost = mode === "shutdown-host";
121
+ const interruptActiveTurns = mode === "interrupt" || shutdownHost;
122
+ const states = [...this.sessionStates.values()];
123
+ for (const state of states) {
124
+ this.unsubscribeDeltaListener(state);
125
+ if (interruptActiveTurns && (state.activeSubmit || state.session.isTurnRunning)) {
126
+ state.session.interruptTurn();
127
+ }
128
+ if (interruptActiveTurns && state.activeBash) {
129
+ state.activeBash.abortController.abort();
130
+ }
131
+ }
132
+ if (interruptActiveTurns) {
133
+ await Promise.allSettled(states.map((state) => state.activeSubmit?.promise).filter((promise) => promise));
134
+ await Promise.allSettled(states.map((state) => state.activeBash?.promise).filter((promise) => promise));
135
+ }
136
+ this.sessionStates.clear();
137
+ if (shutdownHost) {
138
+ await this.host.shutdown();
139
+ }
140
+ }
141
+ emitReady() {
142
+ this.sendMessage(createSessionProtocolReadyMessage());
143
+ }
144
+ handleInitialize(request) {
145
+ const result = {
146
+ protocolVersion: SESSION_PROTOCOL_VERSION,
147
+ methods: [...SESSION_PROTOCOL_METHODS],
148
+ alreadyInitialized: this.initialized,
149
+ };
150
+ this.initialized = true;
151
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "initialize", result));
152
+ }
153
+ async handleSubmit(request) {
154
+ const state = await this.getSessionState(request.params.sessionId);
155
+ if (!state) {
156
+ this.sendSessionNotFound(request.id, request.params.sessionId);
157
+ return;
158
+ }
159
+ if (this.getPendingMutationCount(state) > 0) {
160
+ this.sendSubmitBusy(state, request.id);
161
+ return;
162
+ }
163
+ const startedSubmit = await this.enqueueMutation(state, () => this.startUserMessageTurn(state, request));
164
+ if (!startedSubmit) {
165
+ return;
166
+ }
167
+ const { activeSubmit } = startedSubmit;
168
+ try {
169
+ await activeSubmit.promise;
170
+ }
171
+ finally {
172
+ await this.enqueueMutation(state, () => {
173
+ if (state.activeSubmit === activeSubmit) {
174
+ state.activeSubmit = undefined;
175
+ }
176
+ });
177
+ this.schedulePendingSubmitDrains(state);
178
+ }
179
+ }
180
+ async handleQueue(request) {
181
+ const state = await this.getSessionState(request.params.sessionId);
182
+ if (!state) {
183
+ this.sendSessionNotFound(request.id, request.params.sessionId);
184
+ return;
185
+ }
186
+ if (this.getPendingMutationCount(state) > 0) {
187
+ this.sendSubmitBusy(state, request.id);
188
+ return;
189
+ }
190
+ const startedSubmit = await this.enqueueMutation(state, () => {
191
+ if (state.activeSubmit || state.session.isTurnRunning || state.activeBash) {
192
+ state.pendingQueuedSubmits.push(request);
193
+ return undefined;
194
+ }
195
+ return this.startUserMessageTurn(state, request);
196
+ });
197
+ if (!startedSubmit) {
198
+ return;
199
+ }
200
+ const { activeSubmit } = startedSubmit;
201
+ try {
202
+ await activeSubmit.promise;
203
+ }
204
+ finally {
205
+ await this.enqueueMutation(state, () => {
206
+ if (state.activeSubmit === activeSubmit) {
207
+ state.activeSubmit = undefined;
208
+ }
209
+ });
210
+ this.schedulePendingSubmitDrains(state);
211
+ }
212
+ }
213
+ async handleSteer(request) {
214
+ const state = await this.getSessionState(request.params.sessionId);
215
+ if (!state) {
216
+ this.sendSessionNotFound(request.id, request.params.sessionId);
217
+ return;
218
+ }
219
+ if (this.getPendingMutationCount(state) > 0) {
220
+ this.sendSubmitBusy(state, request.id);
221
+ return;
222
+ }
223
+ const startedSubmit = await this.enqueueMutation(state, () => {
224
+ if (state.activeSubmit || state.session.isTurnRunning || state.activeBash) {
225
+ state.pendingSteeringSubmits.push(request);
226
+ state.session.requestTurnBoundaryStop();
227
+ return undefined;
228
+ }
229
+ return this.startUserMessageTurn(state, request);
230
+ });
231
+ if (!startedSubmit) {
232
+ return;
233
+ }
234
+ const { activeSubmit } = startedSubmit;
235
+ try {
236
+ await activeSubmit.promise;
237
+ }
238
+ finally {
239
+ await this.enqueueMutation(state, () => {
240
+ if (state.activeSubmit === activeSubmit) {
241
+ state.activeSubmit = undefined;
242
+ }
243
+ });
244
+ this.schedulePendingSubmitDrains(state);
245
+ }
246
+ }
247
+ async handleExec(request) {
248
+ const state = await this.getSessionState(request.params.sessionId);
249
+ if (!state) {
250
+ this.sendSessionNotFound(request.id, request.params.sessionId);
251
+ return;
252
+ }
253
+ if (this.getPendingMutationCount(state) > 0 ||
254
+ state.activeSubmit ||
255
+ state.session.isTurnRunning ||
256
+ state.activeBash) {
257
+ this.sendSubmitBusy(state, request.id);
258
+ return;
259
+ }
260
+ const activeBash = await this.enqueueMutation(state, () => this.startExec(state, request));
261
+ if (!activeBash) {
262
+ return;
263
+ }
264
+ try {
265
+ await activeBash.promise;
266
+ }
267
+ finally {
268
+ await this.enqueueMutation(state, () => {
269
+ if (state.activeBash === activeBash) {
270
+ state.activeBash = undefined;
271
+ }
272
+ });
273
+ this.schedulePendingSubmitDrains(state);
274
+ }
275
+ }
276
+ async handleRetry(request) {
277
+ const state = await this.getSessionState(request.params.sessionId);
278
+ if (!state) {
279
+ this.sendSessionNotFound(request.id, request.params.sessionId);
280
+ return;
281
+ }
282
+ if (this.getPendingMutationCount(state) > 0) {
283
+ this.sendSubmitBusy(state, request.id);
284
+ return;
285
+ }
286
+ const startedRetry = await this.enqueueMutation(state, () => this.startRetry(state, request));
287
+ if (!startedRetry) {
288
+ return;
289
+ }
290
+ const { activeSubmit } = startedRetry;
291
+ try {
292
+ await activeSubmit.promise;
293
+ }
294
+ finally {
295
+ await this.enqueueMutation(state, () => {
296
+ if (state.activeSubmit === activeSubmit) {
297
+ state.activeSubmit = undefined;
298
+ }
299
+ });
300
+ this.schedulePendingSubmitDrains(state);
301
+ }
302
+ }
303
+ async handleCreate(request) {
304
+ const session = await this.host.createSession(request.params);
305
+ if (this.closed) {
306
+ await session.dispose();
307
+ return;
308
+ }
309
+ this.registerSession(session);
310
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.create", await session.snapshot()));
311
+ }
312
+ async handleList(request) {
313
+ const result = {
314
+ sessions: await this.host.listSessions(),
315
+ };
316
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.list", result));
317
+ }
318
+ async handleAttach(request) {
319
+ const wasObserved = this.sessionStates.has(request.params.sessionId);
320
+ const state = await this.getSessionState(request.params.sessionId, {
321
+ bufferInitialDeltas: !wasObserved,
322
+ });
323
+ if (!state) {
324
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, SESSION_PROTOCOL_ERROR_CODES.notFound, "session not found", { sessionId: request.params.sessionId }));
325
+ return;
326
+ }
327
+ if (!wasObserved && !state.bufferedDeltas) {
328
+ state.bufferedDeltas = [];
329
+ }
330
+ let observed = false;
331
+ try {
332
+ const snapshot = await state.session.snapshot();
333
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.observe", snapshot));
334
+ observed = true;
335
+ this.flushBufferedDeltasAfterSnapshot(state, snapshot.revision);
336
+ }
337
+ finally {
338
+ if (!observed && !wasObserved) {
339
+ this.unsubscribeDeltaListener(state);
340
+ this.sessionStates.delete(request.params.sessionId);
341
+ }
342
+ else if (!observed) {
343
+ this.flushBufferedDeltasAfterSnapshot(state, 0);
344
+ }
345
+ }
346
+ }
347
+ async handleUnobserve(request) {
348
+ const state = this.sessionStates.get(request.params.sessionId);
349
+ if (!state) {
350
+ this.sendSessionNotFound(request.id, request.params.sessionId);
351
+ return;
352
+ }
353
+ this.unsubscribeDeltaListener(state);
354
+ this.sessionStates.delete(request.params.sessionId);
355
+ const result = {
356
+ unobserved: true,
357
+ };
358
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.unobserve", result));
359
+ }
360
+ async handleRecord(request) {
361
+ const state = await this.getSessionState(request.params.sessionId);
362
+ if (!state) {
363
+ this.sendSessionNotFound(request.id, request.params.sessionId);
364
+ return;
365
+ }
366
+ await this.withSessionMutation(request, "user message added", async (mutationState) => {
367
+ const result = await mutationState.session.record({
368
+ text: request.params.text,
369
+ ...(request.params.historyEntryId !== undefined
370
+ ? { historyEntryId: request.params.historyEntryId }
371
+ : {}),
372
+ });
373
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.record", result));
374
+ });
375
+ }
376
+ async startUserMessageTurn(state, request) {
377
+ if (state.activeSubmit || state.session.isTurnRunning || state.activeBash) {
378
+ this.sendSubmitBusy(state, request.id);
379
+ return undefined;
380
+ }
381
+ const addOptions = request.params.historyEntryId
382
+ ? { historyEntryId: request.params.historyEntryId }
383
+ : undefined;
384
+ const { userHistoryEntryId } = await state.session.record({
385
+ text: request.params.text,
386
+ ...(addOptions ? { historyEntryId: addOptions.historyEntryId } : {}),
387
+ });
388
+ const submitPromise = this.executeSubmit(state, request.id, request.method, userHistoryEntryId);
389
+ const activeSubmit = { requestId: request.id, promise: submitPromise };
390
+ state.activeSubmit = activeSubmit;
391
+ return {
392
+ activeSubmit,
393
+ };
394
+ }
395
+ startRetry(state, request) {
396
+ if (state.activeSubmit || state.session.isTurnRunning || state.activeBash) {
397
+ this.sendSubmitBusy(state, request.id);
398
+ return undefined;
399
+ }
400
+ const retryPromise = this.executeRetry(state, request.id);
401
+ const activeSubmit = { requestId: request.id, promise: retryPromise };
402
+ state.activeSubmit = activeSubmit;
403
+ return {
404
+ activeSubmit,
405
+ };
406
+ }
407
+ startExec(state, request) {
408
+ if (state.activeSubmit || state.session.isTurnRunning || state.activeBash) {
409
+ this.sendSubmitBusy(state, request.id);
410
+ return undefined;
411
+ }
412
+ const abortController = new AbortController();
413
+ const promise = this.executeExec(state, request, abortController.signal);
414
+ const activeBash = { requestId: request.id, abortController, promise };
415
+ state.activeBash = activeBash;
416
+ return activeBash;
417
+ }
418
+ schedulePendingSubmitDrains(state) {
419
+ void this.drainPendingSteeringSubmits(state).catch((error) => {
420
+ this.failPendingUserMessageRequests(state, "session.steer", error);
421
+ });
422
+ void this.drainPendingQueuedSubmits(state).catch((error) => {
423
+ this.failPendingUserMessageRequests(state, "session.queue", error);
424
+ });
425
+ }
426
+ async drainPendingSteeringSubmits(state) {
427
+ const batch = await this.enqueueMutation(state, async () => {
428
+ if (state.pendingSteeringSubmits.length === 0 ||
429
+ state.activeSubmit ||
430
+ state.session.isTurnRunning) {
431
+ return undefined;
432
+ }
433
+ const requests = state.pendingSteeringSubmits.splice(0);
434
+ const primaryRequest = requests[0];
435
+ const preferredHistoryEntryId = requests.length === 1 ? primaryRequest.params.historyEntryId : undefined;
436
+ let userHistoryEntryId;
437
+ try {
438
+ ({ userHistoryEntryId } = await state.session.record({
439
+ text: formatSteeringUserMessage(requests.map((item) => item.params.text)),
440
+ ...(preferredHistoryEntryId ? { historyEntryId: preferredHistoryEntryId } : {}),
441
+ }));
442
+ }
443
+ catch (error) {
444
+ this.sendUserMessageDrainFailure("session.steer", requests, error);
445
+ return undefined;
446
+ }
447
+ const submitPromise = this.executeSubmit(state, primaryRequest.id, "session.steer", userHistoryEntryId, requests);
448
+ const activeSubmit = {
449
+ requestId: primaryRequest.id,
450
+ promise: submitPromise,
451
+ };
452
+ state.activeSubmit = activeSubmit;
453
+ return { activeSubmit };
454
+ });
455
+ if (!batch) {
456
+ return;
457
+ }
458
+ try {
459
+ await batch.activeSubmit.promise;
460
+ }
461
+ finally {
462
+ await this.enqueueMutation(state, () => {
463
+ if (state.activeSubmit === batch.activeSubmit) {
464
+ state.activeSubmit = undefined;
465
+ }
466
+ });
467
+ this.schedulePendingSubmitDrains(state);
468
+ }
469
+ }
470
+ async drainPendingQueuedSubmits(state) {
471
+ const next = await this.enqueueMutation(state, async () => {
472
+ if (state.pendingQueuedSubmits.length === 0 ||
473
+ state.activeSubmit ||
474
+ state.session.isTurnRunning) {
475
+ return undefined;
476
+ }
477
+ const request = state.pendingQueuedSubmits.shift();
478
+ try {
479
+ return await this.startUserMessageTurn(state, request);
480
+ }
481
+ catch (error) {
482
+ this.sendUserMessageDrainFailure("session.queue", [request], error);
483
+ return undefined;
484
+ }
485
+ });
486
+ if (!next) {
487
+ return;
488
+ }
489
+ try {
490
+ await next.activeSubmit.promise;
491
+ }
492
+ finally {
493
+ await this.enqueueMutation(state, () => {
494
+ if (state.activeSubmit === next.activeSubmit) {
495
+ state.activeSubmit = undefined;
496
+ }
497
+ });
498
+ this.schedulePendingSubmitDrains(state);
499
+ }
500
+ }
501
+ failPendingUserMessageRequests(state, method, error) {
502
+ const pending = method === "session.steer"
503
+ ? state.pendingSteeringSubmits.splice(0)
504
+ : state.pendingQueuedSubmits.splice(0);
505
+ this.sendUserMessageDrainFailure(method, pending, error);
506
+ }
507
+ sendUserMessageDrainFailure(method, requests, error) {
508
+ for (const request of requests) {
509
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, SESSION_PROTOCOL_ERROR_CODES.internalError, "failed to drain pending user message", { cause: error instanceof Error ? error.message : String(error) }));
510
+ }
511
+ }
512
+ async executeSubmit(state, requestId, method, userHistoryEntryId, responseRequests) {
513
+ try {
514
+ const turnResult = await state.session.runTurn();
515
+ await state.session.snapshot();
516
+ const result = {
517
+ userHistoryEntryId,
518
+ turn: {
519
+ aborted: turnResult.aborted,
520
+ ...(turnResult.blocked ? { blocked: turnResult.blocked } : {}),
521
+ },
522
+ };
523
+ if (responseRequests) {
524
+ for (const request of responseRequests) {
525
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, method, result));
526
+ }
527
+ }
528
+ else {
529
+ this.sendMessage(createSessionProtocolSuccessResponse(requestId, method, result));
530
+ }
531
+ }
532
+ catch (error) {
533
+ await this.snapshotAfterFailedSubmit(state);
534
+ const requests = responseRequests ?? [{ id: requestId }];
535
+ for (const request of requests) {
536
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, SESSION_PROTOCOL_ERROR_CODES.internalError, "failed to run session turn", { cause: error instanceof Error ? error.message : String(error) }));
537
+ }
538
+ }
539
+ }
540
+ async snapshotAfterFailedSubmit(state) {
541
+ try {
542
+ await state.session.snapshot();
543
+ }
544
+ catch {
545
+ // Preserve the original turn failure response; the accepted user message was already
546
+ // committed before model work started.
547
+ }
548
+ }
549
+ async executeRetry(state, requestId) {
550
+ try {
551
+ const turnResult = await state.session.runTurn();
552
+ await state.session.snapshot();
553
+ const result = {
554
+ turn: {
555
+ aborted: turnResult.aborted,
556
+ ...(turnResult.blocked ? { blocked: turnResult.blocked } : {}),
557
+ },
558
+ };
559
+ this.sendMessage(createSessionProtocolSuccessResponse(requestId, "session.retry", result));
560
+ }
561
+ catch (error) {
562
+ this.sendMessage(createSessionProtocolErrorResponse(requestId, SESSION_PROTOCOL_ERROR_CODES.internalError, "failed to run session turn", { cause: error instanceof Error ? error.message : String(error) }));
563
+ }
564
+ }
565
+ async executeExec(state, request, signal) {
566
+ try {
567
+ const result = await state.session.exec({
568
+ command: request.params.command,
569
+ ...(request.params.cwd !== undefined ? { cwd: request.params.cwd } : {}),
570
+ ...(request.params.timeoutMs !== undefined ? { timeoutMs: request.params.timeoutMs } : {}),
571
+ signal,
572
+ });
573
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.exec", result));
574
+ }
575
+ catch (error) {
576
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, signal.aborted
577
+ ? SESSION_PROTOCOL_ERROR_CODES.invalidRequest
578
+ : SESSION_PROTOCOL_ERROR_CODES.internalError, signal.aborted ? "execution command was interrupted" : "failed to run execution command", { cause: error instanceof Error ? error.message : String(error) }));
579
+ }
580
+ }
581
+ async handleInterrupt(request) {
582
+ const state = await this.getSessionState(request.params.sessionId);
583
+ if (!state) {
584
+ this.sendSessionNotFound(request.id, request.params.sessionId);
585
+ return;
586
+ }
587
+ const interrupted = state.session.interruptTurn();
588
+ const interruptedBash = Boolean(state.activeBash);
589
+ state.activeBash?.abortController.abort();
590
+ this.rejectPendingSteeringSubmits(state, "session was interrupted");
591
+ const result = {
592
+ interrupted: interrupted || interruptedBash,
593
+ isTurnRunning: state.session.isTurnRunning || interruptedBash,
594
+ };
595
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.interrupt", result));
596
+ }
597
+ async handleSnapshot(request) {
598
+ const state = await this.getSessionState(request.params.sessionId);
599
+ if (!state) {
600
+ this.sendSessionNotFound(request.id, request.params.sessionId);
601
+ return;
602
+ }
603
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.snapshot", await state.session.snapshot()));
604
+ }
605
+ async handleSetRisk(request) {
606
+ await this.withSessionMutation(request, "session risk level changed", async (state) => {
607
+ const snapshot = await state.session.setRiskLevel(request.params.riskLevel);
608
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.setRisk", snapshot));
609
+ });
610
+ }
611
+ async handleSetReasoning(request) {
612
+ await this.withSessionMutation(request, "session reasoning changed", async (state) => {
613
+ const snapshot = await state.session.setReasoning(request.params.reasoning);
614
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.setReasoning", snapshot));
615
+ });
616
+ }
617
+ async handleSetPersona(request) {
618
+ await this.withSessionMutation(request, "session persona changed", async (state) => {
619
+ const snapshot = await state.session.setPersona(request.params.personaId);
620
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.setPersona", snapshot));
621
+ });
622
+ }
623
+ async handleResolvePrompt(request) {
624
+ const state = await this.getSessionState(request.params.sessionId);
625
+ if (!state) {
626
+ this.sendSessionNotFound(request.id, request.params.sessionId);
627
+ return;
628
+ }
629
+ const result = await state.session.resolvePrompt(request.params.promptId);
630
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.resolvePrompt", result));
631
+ }
632
+ async handleAutocompletePaths(request) {
633
+ const state = await this.getSessionState(request.params.sessionId);
634
+ if (!state) {
635
+ this.sendSessionNotFound(request.id, request.params.sessionId);
636
+ return;
637
+ }
638
+ const result = await state.session.autocompletePaths({
639
+ query: request.params.query,
640
+ limit: request.params.limit,
641
+ });
642
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.autocompletePaths", result));
643
+ }
644
+ async handleReload(request) {
645
+ await this.withSessionMutation(request, "session reloaded", async (state) => {
646
+ const result = await state.session.reload();
647
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.reload", result));
648
+ });
649
+ }
650
+ async handleCompact(request) {
651
+ await this.withSessionMutation(request, "session compacted", async (state) => {
652
+ const result = await state.session.compact({
653
+ mode: request.params.mode,
654
+ ...(request.params.guidance !== undefined ? { guidance: request.params.guidance } : {}),
655
+ });
656
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.compact", result));
657
+ });
658
+ }
659
+ async handlePrune(request) {
660
+ await this.withSessionMutation(request, "session pruned", async (state) => {
661
+ const result = await state.session.pruneToolResults({
662
+ strategy: request.params.strategy,
663
+ fraction: request.params.fraction,
664
+ ...(request.params.guidance !== undefined ? { guidance: request.params.guidance } : {}),
665
+ });
666
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.prune", result));
667
+ });
668
+ }
669
+ async handleRewind(request) {
670
+ await this.withSessionMutation(request, "session rewound", async (state) => {
671
+ const result = await state.session.rewindToHistoryEntryId(request.params.historyEntryId);
672
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.rewind", result));
673
+ });
674
+ }
675
+ async handleTerminateSubagent(request) {
676
+ await this.withSessionMutation(request, "subagent termination requested", async (state) => {
677
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.terminateSubagent", await state.session.terminateSubagent(request.params.subagentId)));
678
+ });
679
+ }
680
+ async handleEphemeralCreate(request) {
681
+ await this.withSessionMutation(request, "ephemeral context created", async (mutationState) => {
682
+ const result = await mutationState.session.createEphemeralContext({
683
+ instructions: request.params.instructions,
684
+ tools: request.params.tools,
685
+ riskLevel: request.params.riskLevel,
686
+ });
687
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.ephemeral.create", result));
688
+ });
689
+ }
690
+ async handleEphemeralSubmit(request) {
691
+ const state = await this.getSessionState(request.params.sessionId);
692
+ if (!state) {
693
+ this.sendSessionNotFound(request.id, request.params.sessionId);
694
+ return;
695
+ }
696
+ const result = await state.session.submitEphemeralThread({
697
+ contextId: request.params.contextId,
698
+ threadId: request.params.threadId,
699
+ ...(request.params.forkFromThreadId !== undefined
700
+ ? { forkFromThreadId: request.params.forkFromThreadId }
701
+ : {}),
702
+ message: request.params.message,
703
+ });
704
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.ephemeral.submit", result));
705
+ }
706
+ async handleEphemeralClose(request) {
707
+ await this.withSessionMutation(request, "ephemeral context closed", async (mutationState) => {
708
+ const result = await mutationState.session.closeEphemeralContext(request.params.contextId);
709
+ this.sendMessage(createSessionProtocolSuccessResponse(request.id, "session.ephemeral.close", result));
710
+ });
711
+ }
712
+ async withSessionMutation(request, queuedSteeringRejectionMessage, handler) {
713
+ const state = await this.getSessionState(request.params.sessionId);
714
+ if (!state) {
715
+ this.sendSessionNotFound(request.id, request.params.sessionId);
716
+ return;
717
+ }
718
+ await this.runSessionMutation(state, async () => {
719
+ if (state.session.sessionId !== request.params.sessionId) {
720
+ this.sendSessionNotFound(request.id, request.params.sessionId);
721
+ return;
722
+ }
723
+ await this.interruptAndWaitForActiveSubmit(state);
724
+ this.rejectPendingSteeringSubmits(state, queuedSteeringRejectionMessage);
725
+ this.rejectPendingQueuedSubmits(state, queuedSteeringRejectionMessage);
726
+ await handler(state);
727
+ });
728
+ }
729
+ rejectPendingSteeringSubmits(state, message) {
730
+ const requests = state.pendingSteeringSubmits.splice(0);
731
+ for (const request of requests) {
732
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, SESSION_PROTOCOL_ERROR_CODES.invalidRequest, message));
733
+ }
734
+ }
735
+ rejectPendingQueuedSubmits(state, message) {
736
+ const requests = state.pendingQueuedSubmits.splice(0);
737
+ for (const request of requests) {
738
+ this.sendMessage(createSessionProtocolErrorResponse(request.id, SESSION_PROTOCOL_ERROR_CODES.invalidRequest, message));
739
+ }
740
+ }
741
+ registerSession(session, options = {}) {
742
+ const existing = this.sessionStates.get(session.sessionId);
743
+ if (existing) {
744
+ return existing;
745
+ }
746
+ const state = {
747
+ session,
748
+ ...(options.bufferInitialDeltas ? { bufferedDeltas: [] } : {}),
749
+ pendingSteeringSubmits: [],
750
+ pendingQueuedSubmits: [],
751
+ };
752
+ state.unsubscribeDelta = session.onDelta((delta) => {
753
+ if (this.closed || !this.sessionStates.has(session.sessionId)) {
754
+ return;
755
+ }
756
+ if (state.bufferedDeltas) {
757
+ state.bufferedDeltas.push(delta);
758
+ return;
759
+ }
760
+ this.sendMessage(delta);
761
+ });
762
+ state.unsubscribeEphemeral = session.onEphemeral((message) => {
763
+ if (this.closed || !this.sessionStates.has(session.sessionId)) {
764
+ return;
765
+ }
766
+ this.sendMessage(message);
767
+ });
768
+ this.sessionStates.set(session.sessionId, state);
769
+ return state;
770
+ }
771
+ async getSessionState(sessionId, options = {}) {
772
+ const existing = this.sessionStates.get(sessionId);
773
+ if (existing) {
774
+ if (existing.session.isDisposed || existing.session.sessionId !== sessionId) {
775
+ this.unsubscribeSessionListeners(existing);
776
+ this.sessionStates.delete(sessionId);
777
+ return undefined;
778
+ }
779
+ return existing;
780
+ }
781
+ const session = await this.host.observeSession(sessionId);
782
+ return session ? this.registerSession(session, options) : undefined;
783
+ }
784
+ flushBufferedDeltasAfterSnapshot(state, snapshotRevision) {
785
+ const bufferedDeltas = state.bufferedDeltas;
786
+ if (!bufferedDeltas) {
787
+ return;
788
+ }
789
+ state.bufferedDeltas = undefined;
790
+ for (const delta of bufferedDeltas) {
791
+ if (delta.toRevision > snapshotRevision) {
792
+ this.sendMessage(delta);
793
+ }
794
+ }
795
+ }
796
+ sendSessionNotFound(id, sessionId) {
797
+ this.sendMessage(createSessionProtocolErrorResponse(id, SESSION_PROTOCOL_ERROR_CODES.notFound, "session not found", { sessionId }));
798
+ }
799
+ sendSubmitBusy(state, id) {
800
+ const message = this.getPendingMutationCount(state) > 0
801
+ ? "a mutating session request is in progress"
802
+ : state.activeBash
803
+ ? "a bash command is already running"
804
+ : "a session turn is already running";
805
+ this.sendMessage(createSessionProtocolErrorResponse(id, SESSION_PROTOCOL_ERROR_CODES.busy, message));
806
+ }
807
+ enqueueMutation(state, handler) {
808
+ const queue = getSessionMutationQueueState(state.session);
809
+ const run = queue.queue.then(handler);
810
+ queue.queue = run.then(() => undefined, () => undefined);
811
+ return run;
812
+ }
813
+ async runSessionMutation(state, handler) {
814
+ const queue = getSessionMutationQueueState(state.session);
815
+ queue.pending += 1;
816
+ try {
817
+ return await this.enqueueMutation(state, handler);
818
+ }
819
+ finally {
820
+ queue.pending -= 1;
821
+ }
822
+ }
823
+ getPendingMutationCount(state) {
824
+ return getSessionMutationQueueState(state.session).pending;
825
+ }
826
+ async interruptAndWaitForActiveSubmit(state) {
827
+ const activeSubmit = state.activeSubmit;
828
+ const activeBash = state.activeBash;
829
+ if (!activeSubmit && !state.session.isTurnRunning && !activeBash) {
830
+ return;
831
+ }
832
+ state.session.interruptTurn();
833
+ activeBash?.abortController.abort();
834
+ if (!activeSubmit && !activeBash) {
835
+ return;
836
+ }
837
+ try {
838
+ await activeSubmit?.promise;
839
+ }
840
+ catch {
841
+ // ignore submit failure while finishing active mutation
842
+ }
843
+ try {
844
+ await activeBash?.promise;
845
+ }
846
+ catch {
847
+ // ignore bash failure while finishing active mutation
848
+ }
849
+ }
850
+ unsubscribeDeltaListener(state) {
851
+ this.unsubscribeSessionListeners(state);
852
+ }
853
+ unsubscribeSessionListeners(state) {
854
+ if (!state.unsubscribeDelta) {
855
+ // continue to ephemeral cleanup below
856
+ }
857
+ else {
858
+ state.unsubscribeDelta();
859
+ state.unsubscribeDelta = undefined;
860
+ }
861
+ if (state.unsubscribeEphemeral) {
862
+ state.unsubscribeEphemeral();
863
+ state.unsubscribeEphemeral = undefined;
864
+ }
865
+ }
866
+ sendMessage(message) {
867
+ if (this.closed) {
868
+ return;
869
+ }
870
+ this.send(message);
871
+ }
872
+ }
873
+ //# sourceMappingURL=session_protocol_handler.js.map