spec-cat 0.1.12 → 0.1.13

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 (46) hide show
  1. package/.output/nitro.json +1 -1
  2. package/.output/public/_nuxt/{CG1pdqEM.js → ASOk6VwA.js} +1 -1
  3. package/.output/public/_nuxt/B24bRLVA.js +1 -0
  4. package/.output/public/_nuxt/{DZfSmUhR.js → B3SzeM5g.js} +1 -1
  5. package/.output/public/_nuxt/{wF8_ZCpc.js → CPqjerP3.js} +1 -1
  6. package/.output/public/_nuxt/D3Ay6Tay.js +126 -0
  7. package/.output/public/_nuxt/{BGX7vZ2E.js → DN_vgzh9.js} +1 -1
  8. package/.output/public/_nuxt/{CJ5aKHH_.js → Djrn6aq0.js} +2 -2
  9. package/.output/public/_nuxt/{BgAW4X6A.js → DqCsQSnP.js} +1 -1
  10. package/.output/public/_nuxt/{Bo0qwvIG.js → afuUTAOf.js} +1 -1
  11. package/.output/public/_nuxt/builds/latest.json +1 -1
  12. package/.output/public/_nuxt/builds/meta/a94a9679-4171-4043-b507-a48b9a35f087.json +1 -0
  13. package/.output/public/_nuxt/{BRNtaPwh.js → dYG8t45S.js} +1 -1
  14. package/.output/public/_nuxt/entry.qjEvbHpP.css +1 -0
  15. package/.output/server/chunks/_/chat.mjs +8 -2
  16. package/.output/server/chunks/_/chat.mjs.map +1 -1
  17. package/.output/server/chunks/_/codexProvider.mjs +62 -102
  18. package/.output/server/chunks/_/codexProvider.mjs.map +1 -1
  19. package/.output/server/chunks/_/providerProcessError.mjs.map +1 -1
  20. package/.output/server/chunks/_/validateWorktree.mjs +1 -1
  21. package/.output/server/chunks/build/client.precomputed.mjs +1 -1
  22. package/.output/server/chunks/build/client.precomputed.mjs.map +1 -1
  23. package/.output/server/chunks/nitro/nitro.mjs +813 -795
  24. package/.output/server/chunks/routes/_ws.mjs +346 -256
  25. package/.output/server/chunks/routes/_ws.mjs.map +1 -1
  26. package/.output/server/chunks/routes/api/chat/finalize.post.mjs +8 -6
  27. package/.output/server/chunks/routes/api/chat/finalize.post.mjs.map +1 -1
  28. package/.output/server/chunks/routes/api/chat/generate-commit-message.post.mjs +1 -1
  29. package/.output/server/chunks/routes/api/chat/preview.post.mjs +1 -1
  30. package/.output/server/chunks/routes/api/chat/preview.post.mjs.map +1 -1
  31. package/.output/server/chunks/routes/api/chat/rebase.post.mjs +1 -1
  32. package/.output/server/chunks/routes/api/chat/worktree.post.mjs +3 -3
  33. package/.output/server/chunks/routes/api/chat/worktree.post.mjs.map +1 -1
  34. package/.output/server/chunks/routes/api/conversations/archives/_archiveId/restore.post.mjs +3 -7
  35. package/.output/server/chunks/routes/api/conversations/archives/_archiveId/restore.post.mjs.map +1 -1
  36. package/.output/server/chunks/routes/api/conversations/update.post.mjs +59 -0
  37. package/.output/server/chunks/routes/api/conversations/update.post.mjs.map +1 -0
  38. package/.output/server/chunks/routes/api/rebase/continue-sync.post.mjs +1 -1
  39. package/.output/server/chunks/routes/api/rebase/continue.post.mjs +8 -6
  40. package/.output/server/chunks/routes/api/rebase/continue.post.mjs.map +1 -1
  41. package/.output/server/package.json +1 -1
  42. package/package.json +1 -1
  43. package/.output/public/_nuxt/CQT7IgVF.js +0 -1
  44. package/.output/public/_nuxt/CWTghksz.js +0 -128
  45. package/.output/public/_nuxt/builds/meta/83cad684-1ef2-41bf-8031-41fb1a7c2996.json +0 -1
  46. package/.output/public/_nuxt/entry.CA9G_QPQ.css +0 -1
@@ -26,7 +26,7 @@ import 'node:module';
26
26
  import 'node:url';
27
27
 
28
28
  const execAsync = promisify(exec);
29
- const WORKTREE_PREFIX = "/tmp/br-";
29
+ const WORKTREE_PREFIX = "/tmp/sc-";
30
30
  async function ensureChatWorktree(projectDir, worktreePath, knownBranch) {
31
31
  if (!worktreePath.startsWith(WORKTREE_PREFIX)) {
32
32
  return { recovered: false };
@@ -42,7 +42,7 @@ async function ensureChatWorktree(projectDir, worktreePath, knownBranch) {
42
42
  if (!convId) {
43
43
  return { recovered: false, error: `Cannot derive branch name from worktree path: ${worktreePath}` };
44
44
  }
45
- branchName = `br/${convId}`;
45
+ branchName = `sc/${convId}`;
46
46
  }
47
47
  try {
48
48
  await execAsync("git worktree prune", { cwd: projectDir });
@@ -99,9 +99,304 @@ async function loadSpecContext(projectDir, featureId) {
99
99
  return sections.join("\n");
100
100
  }
101
101
 
102
+ function extractSessionId(message) {
103
+ const eventType = typeof message.type === "string" ? message.type.toLowerCase() : "";
104
+ const subtype = typeof message.subtype === "string" ? message.subtype.toLowerCase() : "";
105
+ const isErrorLike = eventType.includes("error") || eventType.includes("failed") || subtype.startsWith("error");
106
+ if (isErrorLike) {
107
+ return null;
108
+ }
109
+ const sessionIdKeys = ["session_id", "sessionId", "conversation_id", "conversationId", "thread_id", "threadId"];
110
+ for (const key of sessionIdKeys) {
111
+ const value = message[key];
112
+ if (typeof value === "string" && value.length > 0) {
113
+ return value;
114
+ }
115
+ }
116
+ const response = message.response;
117
+ if (response && typeof response === "object" && !Array.isArray(response)) {
118
+ const responseObj = response;
119
+ for (const key of sessionIdKeys) {
120
+ const value = responseObj[key];
121
+ if (typeof value === "string" && value.length > 0) {
122
+ return value;
123
+ }
124
+ }
125
+ }
126
+ return null;
127
+ }
128
+ function normalizeTurnResultSubtype(value, isError) {
129
+ if (isError) return "error";
130
+ const raw = typeof value === "string" ? value.toLowerCase() : "";
131
+ if (raw === "success") return "success";
132
+ if (raw === "max_turns" || raw === "error_max_turns") return "max_turns";
133
+ if (raw.startsWith("error")) return "error";
134
+ return "success";
135
+ }
136
+ function transformClaudeEvent(event) {
137
+ var _a;
138
+ const sessionId = extractSessionId(event) || void 0;
139
+ const events = [];
140
+ if (event.type === "stream_event" && event.event && typeof event.event === "object") {
141
+ const streamEvent = event.event;
142
+ if (streamEvent.type === "content_block_start" && streamEvent.content_block && typeof streamEvent.content_block === "object") {
143
+ const block = streamEvent.content_block;
144
+ const blockType = block.type;
145
+ const normalizedBlockType = blockType === "server_tool_use" ? "tool_use" : blockType;
146
+ events.push({
147
+ type: "block_start",
148
+ sessionId,
149
+ blockId: block.id || `blk-${Date.now()}-${Math.random().toString(36).substring(2, 6)}`,
150
+ blockType: normalizedBlockType,
151
+ index: streamEvent.index,
152
+ name: block.name,
153
+ toolUseId: block.id,
154
+ text: block.text,
155
+ thinking: block.thinking
156
+ });
157
+ }
158
+ if (streamEvent.type === "content_block_delta" && streamEvent.delta && typeof streamEvent.delta === "object") {
159
+ const delta = streamEvent.delta;
160
+ events.push({
161
+ type: "block_delta",
162
+ sessionId,
163
+ blockId: "",
164
+ // Client matches by index/type if ID is missing in delta
165
+ index: streamEvent.index,
166
+ text: delta.text,
167
+ thinking: delta.thinking,
168
+ partialJson: delta.partial_json
169
+ });
170
+ }
171
+ if (streamEvent.type === "content_block_stop") {
172
+ events.push({
173
+ type: "block_end",
174
+ sessionId,
175
+ blockId: "",
176
+ index: streamEvent.index
177
+ });
178
+ }
179
+ }
180
+ if (event.type === "tool_result") {
181
+ events.push({
182
+ type: "tool_result",
183
+ sessionId,
184
+ toolUseId: event.tool_use_id,
185
+ content: event.content,
186
+ isError: !!event.is_error
187
+ });
188
+ }
189
+ if (event.type === "permission_request" && event.permission && typeof event.permission === "object") {
190
+ const perm = event.permission;
191
+ events.push({
192
+ type: "permission_request",
193
+ sessionId,
194
+ tool: perm.tool || "Permission",
195
+ description: perm.description,
196
+ input: perm
197
+ });
198
+ }
199
+ if (event.type === "result") {
200
+ const usage = event.usage;
201
+ const isError = Boolean(event.is_error);
202
+ events.push({
203
+ type: "turn_result",
204
+ sessionId,
205
+ subtype: normalizeTurnResultSubtype(event.subtype, isError),
206
+ totalCostUsd: event.total_cost_usd,
207
+ durationMs: event.duration_ms,
208
+ numTurns: event.num_turns,
209
+ usage: usage ? {
210
+ inputTokens: usage.input_tokens || 0,
211
+ outputTokens: usage.output_tokens || 0,
212
+ cacheCreationInputTokens: usage.cache_creation_input_tokens || 0,
213
+ cacheReadInputTokens: usage.cache_read_input_tokens || 0
214
+ } : void 0
215
+ });
216
+ }
217
+ if (event.type === "system" && event.subtype === "init") {
218
+ events.push({
219
+ type: "session_init",
220
+ sessionId,
221
+ model: event.model,
222
+ tools: event.tools || [],
223
+ permissionMode: event.permissionMode || "",
224
+ cwd: event.cwd || ""
225
+ });
226
+ }
227
+ if (event.type === "user" && (event.message || event.content)) {
228
+ const content = ((_a = event.message) == null ? void 0 : _a.content) || event.content;
229
+ if (Array.isArray(content)) {
230
+ for (const block of content) {
231
+ if ((block == null ? void 0 : block.type) === "tool_result" && (block == null ? void 0 : block.is_error) && typeof block.content === "string") {
232
+ const errorContent = block.content;
233
+ if (errorContent.includes("requested permissions") || errorContent.includes("haven't granted")) {
234
+ const tools = parseToolsFromError(errorContent);
235
+ events.push({
236
+ type: "permission_request",
237
+ sessionId,
238
+ tool: tools[0] || "Permission",
239
+ tools,
240
+ description: errorContent
241
+ });
242
+ }
243
+ }
244
+ }
245
+ }
246
+ }
247
+ return events;
248
+ }
249
+ function transformCodexEvent(event) {
250
+ const type = event.type;
251
+ const canonicalTypes = [
252
+ "session_init",
253
+ "block_start",
254
+ "block_delta",
255
+ "block_end",
256
+ "tool_result",
257
+ "permission_request",
258
+ "turn_result",
259
+ "error",
260
+ "done"
261
+ ];
262
+ if (canonicalTypes.includes(type)) {
263
+ return [event];
264
+ }
265
+ return transformClaudeEvent(event);
266
+ }
267
+ function isRenderableEvent(event) {
268
+ switch (event.type) {
269
+ case "block_start":
270
+ return ["text", "thinking", "tool_use"].includes(event.blockType);
271
+ case "block_delta":
272
+ return !!(event.text || event.thinking || event.partialJson);
273
+ case "tool_result":
274
+ case "permission_request":
275
+ return true;
276
+ case "turn_result":
277
+ return event.subtype !== "success";
278
+ // Errors in result are renderable
279
+ default:
280
+ return false;
281
+ }
282
+ }
283
+ function checkForPermissionRequest(event, approvedTools, providerId) {
284
+ if (event.type === "permission_request") {
285
+ return event;
286
+ }
287
+ if (event.type === "block_start" && event.blockType === "tool_use" && providerId === "codex") {
288
+ const normalizedTool = normalizeToolName(event.name || "");
289
+ if (normalizedTool && codexToolNeedsAskApproval(normalizedTool) && !approvedTools.has(normalizedTool)) {
290
+ return {
291
+ type: "permission_request",
292
+ sessionId: event.sessionId,
293
+ tool: normalizedTool,
294
+ tools: [normalizedTool],
295
+ description: `Permission required: ${normalizedTool}`
296
+ };
297
+ }
298
+ }
299
+ if (event.type === "tool_result" && event.isError) {
300
+ if (isPermissionRequestText(event.content)) {
301
+ const tools = parseToolsFromError(event.content);
302
+ return {
303
+ type: "permission_request",
304
+ sessionId: event.sessionId,
305
+ tool: tools[0] || "Permission",
306
+ tools,
307
+ description: event.content
308
+ };
309
+ }
310
+ }
311
+ return null;
312
+ }
313
+ function normalizeToolName(tool) {
314
+ if (!tool) return "";
315
+ const trimmed = tool.trim();
316
+ if (!trimmed) return "";
317
+ return trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
318
+ }
319
+ function isPermissionRequestText(text) {
320
+ if (!text) return false;
321
+ return /permission required|approval required|requires approval|requested permissions|haven't granted|hasn't granted|not approved|approval policy|permission denied|operation not permitted|read-only file system|cannot touch/i.test(text);
322
+ }
323
+ function parseToolsFromError(errorContent) {
324
+ const lowerContent = errorContent.toLowerCase();
325
+ const tools = [];
326
+ const toolNameMatch = errorContent.match(/(?:use the |Permission Required: )(\w+)(?: tool)?/i);
327
+ if (toolNameMatch) {
328
+ const toolName = toolNameMatch[1];
329
+ const normalized = toolName.charAt(0).toUpperCase() + toolName.slice(1).toLowerCase();
330
+ if (["Read", "Write", "Edit", "Bash", "Glob", "Grep", "Webfetch", "Websearch"].includes(normalized)) {
331
+ const tool = normalized === "Webfetch" ? "WebFetch" : normalized === "Websearch" ? "WebSearch" : normalized;
332
+ return [tool];
333
+ }
334
+ }
335
+ if (lowerContent.includes("write to") || lowerContent.includes("write ")) {
336
+ tools.push("Write", "Edit");
337
+ }
338
+ if (lowerContent.includes("edit ") && !tools.includes("Edit")) {
339
+ tools.push("Edit");
340
+ }
341
+ if (lowerContent.includes("read ")) {
342
+ tools.push("Read");
343
+ }
344
+ if (lowerContent.includes("run ") || lowerContent.includes("execute") || lowerContent.includes("bash")) {
345
+ tools.push("Bash");
346
+ }
347
+ if (lowerContent.includes("glob")) {
348
+ tools.push("Glob");
349
+ }
350
+ if (lowerContent.includes("grep")) {
351
+ tools.push("Grep");
352
+ }
353
+ if (lowerContent.includes("fetch") || lowerContent.includes("webfetch")) {
354
+ tools.push("WebFetch");
355
+ }
356
+ if (lowerContent.includes("websearch")) {
357
+ tools.push("WebSearch");
358
+ }
359
+ return tools.length > 0 ? tools : ["Write", "Edit"];
360
+ }
361
+ function normalizeTools(tools) {
362
+ const seen = /* @__PURE__ */ new Set();
363
+ for (const tool of tools) {
364
+ const normalized = normalizeToolName(tool);
365
+ if (normalized) {
366
+ seen.add(normalized);
367
+ }
368
+ }
369
+ return Array.from(seen);
370
+ }
371
+ function codexToolNeedsAskApproval(tool) {
372
+ const normalized = normalizeToolName(tool);
373
+ if (!normalized) return false;
374
+ if (normalized === "Read" || normalized === "Glob" || normalized === "Grep" || normalized === "WebSearch") {
375
+ return false;
376
+ }
377
+ return true;
378
+ }
379
+ function extractPermissionRequestFromProcessOutput(nonJsonOutput) {
380
+ if (!Array.isArray(nonJsonOutput) || nonJsonOutput.length === 0) return null;
381
+ const description = nonJsonOutput.join("\n");
382
+ if (!isPermissionRequestText(description)) {
383
+ return null;
384
+ }
385
+ const tools = parseToolsFromError(description);
386
+ return { tools, description };
387
+ }
388
+
102
389
  const peerStates = /* @__PURE__ */ new Map();
103
390
  const MAX_ATTACHMENT_COUNT = 4;
104
391
  const MAX_ATTACHMENT_SIZE_BYTES = 5 * 1024 * 1024;
392
+ const SPECKIT_AUTONOMY_DIRECTIVE = [
393
+ "Speckit Execution Mode (MANDATORY):",
394
+ "- Do not ask the user for confirmation, follow-up, or permission to proceed.",
395
+ '- Do not end with questions like "Would you like me to..." or "Shall I...".',
396
+ "- For remediation and traceability gaps, directly edit the relevant spec files now (spec.md, plan.md, tasks.md) when writable.",
397
+ "- Prefer concrete file edits over recommendations; provide a brief change summary after edits are complete.",
398
+ "- Only stop without edits if blocked by a hard constraint (missing files, permission failure), and report the blocker explicitly."
399
+ ].join("\n");
105
400
  function getPeerState(peerId) {
106
401
  let state = peerStates.get(peerId);
107
402
  if (!state) {
@@ -117,48 +412,15 @@ function getPeerState(peerId) {
117
412
  }
118
413
  return state;
119
414
  }
415
+ function isSpeckitCommand(message) {
416
+ return message.trim().startsWith("/speckit.");
417
+ }
120
418
  function killProc(proc) {
121
419
  try {
122
420
  proc.kill();
123
421
  } catch {
124
422
  }
125
423
  }
126
- function hasRenderableProviderContent(message) {
127
- const type = typeof message.type === "string" ? message.type : "";
128
- if (type === "stream_event") {
129
- const event = message.event;
130
- if (event && typeof event === "object") {
131
- const streamEvent = event;
132
- if (streamEvent.type === "content_block_start") {
133
- const contentBlock = streamEvent.content_block;
134
- if (contentBlock && typeof contentBlock === "object") {
135
- const block = contentBlock;
136
- const blockType = typeof block.type === "string" ? block.type : "";
137
- if (blockType === "text" || blockType === "thinking" || blockType === "tool_use" || blockType === "server_tool_use") {
138
- return true;
139
- }
140
- }
141
- }
142
- if (streamEvent.type === "content_block_delta") {
143
- const delta = streamEvent.delta;
144
- if (delta && typeof delta === "object") {
145
- const d = delta;
146
- if (typeof d.text === "string" || typeof d.thinking === "string" || typeof d.partial_json === "string") {
147
- return true;
148
- }
149
- }
150
- }
151
- }
152
- }
153
- if (type === "tool_result" || type === "permission_request") {
154
- return true;
155
- }
156
- if (type === "result") {
157
- const subtype = typeof message.subtype === "string" ? message.subtype : "";
158
- return subtype.startsWith("error");
159
- }
160
- return false;
161
- }
162
424
  function normalizeImageAttachments(attachments) {
163
425
  if (!Array.isArray(attachments)) return [];
164
426
  return attachments.slice(0, MAX_ATTACHMENT_COUNT).map((item) => {
@@ -196,22 +458,21 @@ function buildProviderMessage(baseMessage, attachments) {
196
458
  }
197
459
  function sendAssistantText(peer, text, sessionId) {
198
460
  peer.send(JSON.stringify({
199
- type: "provider_json",
200
- data: {
201
- type: "stream_event",
202
- ...sessionId ? { session_id: sessionId } : {},
203
- event: {
204
- type: "content_block_start",
205
- content_block: { type: "text", text }
206
- }
461
+ type: "ui_event",
462
+ event: {
463
+ type: "block_start",
464
+ sessionId: sessionId || void 0,
465
+ blockId: `blk-${Date.now()}`,
466
+ blockType: "text",
467
+ text
207
468
  }
208
469
  }));
209
470
  peer.send(JSON.stringify({
210
- type: "provider_json",
211
- data: {
212
- type: "stream_event",
213
- ...sessionId ? { session_id: sessionId } : {},
214
- event: { type: "content_block_stop" }
471
+ type: "ui_event",
472
+ event: {
473
+ type: "block_end",
474
+ sessionId: sessionId || void 0,
475
+ blockId: ""
215
476
  }
216
477
  }));
217
478
  }
@@ -340,8 +601,8 @@ async function handleChatMessage(peer, msg) {
340
601
  }));
341
602
  return;
342
603
  }
343
- const isSpeckitCommand = msg.message.trim().startsWith("/speckit.");
344
- if (isSpeckitCommand) {
604
+ const speckitCommand = isSpeckitCommand(msg.message);
605
+ if (speckitCommand) {
345
606
  console.log("[WS] Speckit command detected - auto-resetting context for peer:", peer.id);
346
607
  clearProviderSession(state);
347
608
  } else {
@@ -353,9 +614,9 @@ async function handleChatMessage(peer, msg) {
353
614
  }
354
615
  state.pendingMessage = msg;
355
616
  state.pendingTools = [];
356
- if (!isSpeckitCommand && msg.sessionId) {
617
+ if (!speckitCommand && msg.sessionId) {
357
618
  state.providerSessionId = msg.sessionId;
358
- } else if (!isSpeckitCommand) {
619
+ } else if (!speckitCommand) {
359
620
  state.approvedTools.clear();
360
621
  state.providerSessionId = null;
361
622
  }
@@ -366,7 +627,7 @@ async function handleChatMessage(peer, msg) {
366
627
  attachmentCount: attachments.length,
367
628
  providerId: msg.providerId,
368
629
  providerModelKey: msg.providerModelKey,
369
- isSpeckitCommand
630
+ isSpeckitCommand: speckitCommand
370
631
  });
371
632
  runProvider(peer, state, msg);
372
633
  }
@@ -404,7 +665,7 @@ async function runProvider(peer, state, msg, isRetry = false, forceEphemeral = f
404
665
  return;
405
666
  }
406
667
  }
407
- if (workingDirectory.startsWith("/tmp/br-") && !existsSync(workingDirectory)) {
668
+ if (workingDirectory.startsWith("/tmp/sc-") && !existsSync(workingDirectory)) {
408
669
  const result = await ensureChatWorktree(projectDir, workingDirectory, msg.worktreeBranch);
409
670
  if (result.recovered) {
410
671
  peer.send(JSON.stringify({ type: "worktree_recovered" }));
@@ -420,6 +681,7 @@ async function runProvider(peer, state, msg, isRetry = false, forceEphemeral = f
420
681
  const usedResumeFlag = !isRetry && !!state.providerSessionId;
421
682
  const resumeSessionId = usedResumeFlag ? state.providerSessionId : void 0;
422
683
  let systemPrompt;
684
+ const speckitCommand = isSpeckitCommand(msg.message);
423
685
  if (msg.featureId && !usedResumeFlag) {
424
686
  try {
425
687
  const specContext = await loadSpecContext(projectDir, msg.featureId);
@@ -430,6 +692,11 @@ async function runProvider(peer, state, msg, isRetry = false, forceEphemeral = f
430
692
  console.error("[WS] Failed to load spec context:", error);
431
693
  }
432
694
  }
695
+ if (speckitCommand && !usedResumeFlag) {
696
+ systemPrompt = systemPrompt ? `${systemPrompt}
697
+
698
+ ${SPECKIT_AUTONOMY_DIRECTIVE}` : SPECKIT_AUTONOMY_DIRECTIVE;
699
+ }
433
700
  console.log("[WS] Running provider stream:", selection.providerId, selection.modelKey, isRetry ? "(retry)" : "");
434
701
  const generation = state.procGeneration;
435
702
  let permissionRequested = false;
@@ -450,85 +717,33 @@ async function runProvider(peer, state, msg, isRetry = false, forceEphemeral = f
450
717
  },
451
718
  {
452
719
  onProviderJson(parsed) {
453
- var _a, _b, _c, _d, _e;
454
- const sessionId = extractSessionId(parsed);
455
- if (sessionId) {
456
- state.providerSessionId = sessionId;
457
- }
458
- if (hasRenderableProviderContent(parsed)) {
459
- emittedRenderableContent = true;
460
- }
461
- if (selection.providerId === "codex" && (mode === "ask" || mode === "plan") && !permissionRequested) {
462
- const requestedTool = extractToolUseNameFromProviderJson(parsed);
463
- const normalizedTool = normalizeToolName(requestedTool || "");
464
- if (normalizedTool && codexToolNeedsAskApproval(normalizedTool) && !state.approvedTools.has(normalizedTool)) {
465
- permissionRequested = true;
466
- state.pendingTools = [normalizedTool];
467
- peer.send(JSON.stringify({
468
- type: "permission_request",
469
- tool: normalizedTool,
470
- tools: state.pendingTools,
471
- description: `Permission required: ${normalizedTool}`
472
- }));
473
- (_a = state.proc) == null ? void 0 : _a.kill();
474
- return;
720
+ var _a;
721
+ const adapter = selection.providerId === "claude" ? transformClaudeEvent : transformCodexEvent;
722
+ const events = adapter(parsed);
723
+ for (const event of events) {
724
+ if (event.sessionId) {
725
+ state.providerSessionId = event.sessionId;
475
726
  }
476
- }
477
- if (parsed.type === "permission_request" && !permissionRequested) {
478
- permissionRequested = true;
479
- const permission = parsed.permission && typeof parsed.permission === "object" ? parsed.permission : null;
480
- const tool = permission && typeof permission.tool === "string" ? permission.tool : "Permission";
481
- const normalizedTool = normalizeToolName(tool);
482
- const description = permission && typeof permission.description === "string" ? permission.description : "Permission required to continue execution.";
483
- state.pendingTools = normalizedTool ? [normalizedTool] : [];
484
- peer.send(JSON.stringify({
485
- type: "permission_request",
486
- tool: normalizedTool || "Permission",
487
- tools: state.pendingTools,
488
- description
489
- }));
490
- (_b = state.proc) == null ? void 0 : _b.kill();
491
- return;
492
- }
493
- if ((mode === "ask" || mode === "plan") && !permissionRequested) {
494
- const inferred = extractPermissionRequestFromToolResult(parsed);
495
- if (inferred) {
496
- permissionRequested = true;
497
- state.pendingTools = normalizeTools(inferred.tools);
498
- peer.send(JSON.stringify({
499
- type: "permission_request",
500
- tool: state.pendingTools[0] || "Permission",
501
- tools: state.pendingTools,
502
- description: inferred.description
503
- }));
504
- (_c = state.proc) == null ? void 0 : _c.kill();
505
- return;
727
+ if (isRenderableEvent(event)) {
728
+ emittedRenderableContent = true;
506
729
  }
507
- }
508
- if (selection.providerId === "claude" && parsed.type === "user" && !permissionRequested) {
509
- const content = ((_d = parsed.message) == null ? void 0 : _d.content) || parsed.content;
510
- if (Array.isArray(content)) {
511
- for (const block of content) {
512
- if ((block == null ? void 0 : block.type) === "tool_result" && (block == null ? void 0 : block.is_error) && typeof block.content === "string") {
513
- const errorContent = block.content;
514
- if (errorContent.includes("requested permissions") || errorContent.includes("haven't granted")) {
515
- permissionRequested = true;
516
- const tools = parseToolsFromError(errorContent);
517
- state.pendingTools = normalizeTools(tools);
518
- peer.send(JSON.stringify({
519
- type: "permission_request",
520
- tool: state.pendingTools[0],
521
- tools: state.pendingTools,
522
- description: errorContent
523
- }));
524
- (_e = state.proc) == null ? void 0 : _e.kill();
525
- return;
526
- }
527
- }
730
+ if ((mode === "ask" || mode === "plan") && !permissionRequested) {
731
+ const permRequest = checkForPermissionRequest(event, state.approvedTools, selection.providerId);
732
+ if (permRequest) {
733
+ permissionRequested = true;
734
+ state.pendingTools = permRequest.tools || [permRequest.tool];
735
+ peer.send(JSON.stringify({
736
+ type: "permission_request",
737
+ tool: permRequest.tool,
738
+ tools: state.pendingTools,
739
+ description: permRequest.description || `Permission required: ${permRequest.tool}`
740
+ }));
741
+ (_a = state.proc) == null ? void 0 : _a.kill();
742
+ return;
528
743
  }
529
744
  }
745
+ peer.send(JSON.stringify({ type: "ui_event", event }));
530
746
  }
531
- peer.send(JSON.stringify({ type: "provider_json", data: parsed }));
532
747
  },
533
748
  onClose({ exitCode, signal, nonJsonOutput }) {
534
749
  if (state.procGeneration !== generation) {
@@ -651,131 +866,6 @@ ${summary}` : "Provider completed without returning visible response content.";
651
866
  state.proc = null;
652
867
  }
653
868
  }
654
- function extractSessionId(message) {
655
- const eventType = typeof message.type === "string" ? message.type.toLowerCase() : "";
656
- const subtype = typeof message.subtype === "string" ? message.subtype.toLowerCase() : "";
657
- const isErrorLike = eventType.includes("error") || eventType.includes("failed") || subtype.startsWith("error");
658
- if (isErrorLike) {
659
- return null;
660
- }
661
- const sessionIdKeys = ["session_id", "sessionId", "conversation_id", "conversationId", "thread_id", "threadId"];
662
- for (const key of sessionIdKeys) {
663
- const value = message[key];
664
- if (typeof value === "string" && value.length > 0) {
665
- return value;
666
- }
667
- }
668
- const response = message.response;
669
- if (response && typeof response === "object" && !Array.isArray(response)) {
670
- const responseObj = response;
671
- for (const key of sessionIdKeys) {
672
- const value = responseObj[key];
673
- if (typeof value === "string" && value.length > 0) {
674
- return value;
675
- }
676
- }
677
- }
678
- return null;
679
- }
680
- function parseToolsFromError(errorContent) {
681
- const lowerContent = errorContent.toLowerCase();
682
- const tools = [];
683
- const toolNameMatch = errorContent.match(/(?:use the |Permission Required: )(\w+)(?: tool)?/i);
684
- if (toolNameMatch) {
685
- const toolName = toolNameMatch[1];
686
- const normalized = toolName.charAt(0).toUpperCase() + toolName.slice(1).toLowerCase();
687
- if (["Read", "Write", "Edit", "Bash", "Glob", "Grep", "Webfetch", "Websearch"].includes(normalized)) {
688
- const tool = normalized === "Webfetch" ? "WebFetch" : normalized === "Websearch" ? "WebSearch" : normalized;
689
- return [tool];
690
- }
691
- }
692
- if (lowerContent.includes("write to") || lowerContent.includes("write ")) {
693
- tools.push("Write", "Edit");
694
- }
695
- if (lowerContent.includes("edit ") && !tools.includes("Edit")) {
696
- tools.push("Edit");
697
- }
698
- if (lowerContent.includes("read ")) {
699
- tools.push("Read");
700
- }
701
- if (lowerContent.includes("run ") || lowerContent.includes("execute") || lowerContent.includes("bash")) {
702
- tools.push("Bash");
703
- }
704
- if (lowerContent.includes("glob")) {
705
- tools.push("Glob");
706
- }
707
- if (lowerContent.includes("grep")) {
708
- tools.push("Grep");
709
- }
710
- if (lowerContent.includes("fetch") || lowerContent.includes("webfetch")) {
711
- tools.push("WebFetch");
712
- }
713
- if (lowerContent.includes("websearch")) {
714
- tools.push("WebSearch");
715
- }
716
- return tools.length > 0 ? tools : ["Write", "Edit"];
717
- }
718
- function extractToolUseNameFromProviderJson(parsed) {
719
- if (parsed.type !== "stream_event") return null;
720
- const event = parsed.event;
721
- if (!event || typeof event !== "object" || Array.isArray(event)) return null;
722
- const eventObj = event;
723
- if (eventObj.type !== "content_block_start") return null;
724
- const contentBlock = eventObj.content_block;
725
- if (!contentBlock || typeof contentBlock !== "object" || Array.isArray(contentBlock)) return null;
726
- const blockObj = contentBlock;
727
- const blockType = typeof blockObj.type === "string" ? blockObj.type : "";
728
- if (blockType !== "tool_use" && blockType !== "server_tool_use") return null;
729
- return typeof blockObj.name === "string" && blockObj.name.length > 0 ? blockObj.name : null;
730
- }
731
- function normalizeToolName(tool) {
732
- if (!tool) return "";
733
- const trimmed = tool.trim();
734
- if (!trimmed) return "";
735
- return trimmed.charAt(0).toUpperCase() + trimmed.slice(1);
736
- }
737
- function normalizeTools(tools) {
738
- const seen = /* @__PURE__ */ new Set();
739
- for (const tool of tools) {
740
- const normalized = normalizeToolName(tool);
741
- if (normalized) {
742
- seen.add(normalized);
743
- }
744
- }
745
- return Array.from(seen);
746
- }
747
- function codexToolNeedsAskApproval(tool) {
748
- const normalized = normalizeToolName(tool);
749
- if (!normalized) return false;
750
- if (normalized === "Read" || normalized === "Glob" || normalized === "Grep" || normalized === "WebSearch") {
751
- return false;
752
- }
753
- return true;
754
- }
755
- function isPermissionRequestText(text) {
756
- if (!text) return false;
757
- return /permission required|approval required|requires approval|requested permissions|haven't granted|hasn't granted|not approved|approval policy|permission denied|operation not permitted|read-only file system|cannot touch/i.test(text);
758
- }
759
- function extractPermissionRequestFromToolResult(parsed) {
760
- if (parsed.type !== "tool_result") return null;
761
- if (parsed.is_error !== true) return null;
762
- if (typeof parsed.content !== "string") return null;
763
- const description = parsed.content;
764
- if (!isPermissionRequestText(description)) {
765
- return null;
766
- }
767
- const tools = parseToolsFromError(description);
768
- return { tools, description };
769
- }
770
- function extractPermissionRequestFromProcessOutput(nonJsonOutput) {
771
- if (!Array.isArray(nonJsonOutput) || nonJsonOutput.length === 0) return null;
772
- const description = nonJsonOutput.join("\n");
773
- if (!isPermissionRequestText(description)) {
774
- return null;
775
- }
776
- const tools = parseToolsFromError(description);
777
- return { tools, description };
778
- }
779
869
 
780
870
  export { _ws as default };
781
871
  //# sourceMappingURL=_ws.mjs.map