@wjarka/cezarion 0.14.8 → 0.14.9-dev.842

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 (207) hide show
  1. package/README.md +76 -2
  2. package/dist/application-update/bin-recovery.d.ts +5 -0
  3. package/dist/application-update/bin-recovery.js +96 -0
  4. package/dist/application-update/bin-recovery.js.map +1 -0
  5. package/dist/application-update/discovery.d.ts +22 -0
  6. package/dist/application-update/discovery.js +91 -0
  7. package/dist/application-update/discovery.js.map +1 -0
  8. package/dist/application-update/helper.d.ts +57 -0
  9. package/dist/application-update/helper.js +374 -0
  10. package/dist/application-update/helper.js.map +1 -0
  11. package/dist/application-update/launcher.d.ts +8 -0
  12. package/dist/application-update/launcher.js +53 -0
  13. package/dist/application-update/launcher.js.map +1 -0
  14. package/dist/application-update/lock.d.ts +4 -0
  15. package/dist/application-update/lock.js +70 -0
  16. package/dist/application-update/lock.js.map +1 -0
  17. package/dist/application-update/npm-process.d.ts +17 -0
  18. package/dist/application-update/npm-process.js +133 -0
  19. package/dist/application-update/npm-process.js.map +1 -0
  20. package/dist/application-update/service.d.ts +69 -0
  21. package/dist/application-update/service.js +456 -0
  22. package/dist/application-update/service.js.map +1 -0
  23. package/dist/artifacts/cli.d.ts +1 -0
  24. package/dist/artifacts/cli.js +32 -0
  25. package/dist/artifacts/cli.js.map +1 -0
  26. package/dist/artifacts/lifecycle.d.ts +4 -0
  27. package/dist/artifacts/lifecycle.js +42 -0
  28. package/dist/artifacts/lifecycle.js.map +1 -0
  29. package/dist/artifacts/resolve.d.ts +18 -0
  30. package/dist/artifacts/resolve.js +102 -0
  31. package/dist/artifacts/resolve.js.map +1 -0
  32. package/dist/artifacts/store.d.ts +8 -0
  33. package/dist/artifacts/store.js +323 -0
  34. package/dist/artifacts/store.js.map +1 -0
  35. package/dist/ci-wait/mcp.d.ts +3 -0
  36. package/dist/ci-wait/mcp.js +14 -3
  37. package/dist/ci-wait/mcp.js.map +1 -1
  38. package/dist/contract/application-update.d.ts +32 -0
  39. package/dist/contract/artifacts.d.ts +83 -0
  40. package/dist/contract/ask-schema.d.ts +32 -0
  41. package/dist/contract/ask.d.ts +2 -32
  42. package/dist/contract/conversations.d.ts +165 -0
  43. package/dist/contract/delegation.d.ts +39 -0
  44. package/dist/contract/events.d.ts +49 -0
  45. package/dist/contract/health.d.ts +12 -0
  46. package/dist/contract/index.d.ts +2 -0
  47. package/dist/contract/index.js +1374 -1247
  48. package/dist/contract/runs.d.ts +143 -0
  49. package/dist/contract/skills.d.ts +28 -0
  50. package/dist/core/agent-runner.d.ts +33 -4
  51. package/dist/core/agent-runner.js +5 -0
  52. package/dist/core/agent-runner.js.map +1 -1
  53. package/dist/core/claude-cli-runner.d.ts +6 -12
  54. package/dist/core/claude-cli-runner.js +89 -19
  55. package/dist/core/claude-cli-runner.js.map +1 -1
  56. package/dist/core/codex-app-server-runner.d.ts +2 -1
  57. package/dist/core/codex-app-server-runner.js +327 -92
  58. package/dist/core/codex-app-server-runner.js.map +1 -1
  59. package/dist/core/codex-app-server-transport.d.ts +13 -1
  60. package/dist/core/codex-app-server-transport.js +58 -17
  61. package/dist/core/codex-app-server-transport.js.map +1 -1
  62. package/dist/core/cursor-acp-runner.d.ts +4 -1
  63. package/dist/core/cursor-acp-runner.js +3 -0
  64. package/dist/core/cursor-acp-runner.js.map +1 -1
  65. package/dist/core/cursor-provider-error.js +6 -1
  66. package/dist/core/cursor-provider-error.js.map +1 -1
  67. package/dist/core/input-submissions.d.ts +14 -0
  68. package/dist/core/input-submissions.js +26 -0
  69. package/dist/core/input-submissions.js.map +1 -0
  70. package/dist/core/opencode-server-runner.d.ts +4 -1
  71. package/dist/core/opencode-server-runner.js +144 -21
  72. package/dist/core/opencode-server-runner.js.map +1 -1
  73. package/dist/core/pi-runner.d.ts +2 -1
  74. package/dist/core/pi-runner.js +72 -7
  75. package/dist/core/pi-runner.js.map +1 -1
  76. package/dist/delegation/cli.js +102 -6
  77. package/dist/delegation/cli.js.map +1 -1
  78. package/dist/delegation/conversations.js +13 -5
  79. package/dist/delegation/conversations.js.map +1 -1
  80. package/dist/delegation/input.d.ts +2 -1
  81. package/dist/delegation/input.js +6 -1
  82. package/dist/delegation/input.js.map +1 -1
  83. package/dist/delegation/provision.js +5 -3
  84. package/dist/delegation/provision.js.map +1 -1
  85. package/dist/delegation/questions.d.ts +19 -0
  86. package/dist/delegation/questions.js +45 -0
  87. package/dist/delegation/questions.js.map +1 -0
  88. package/dist/delegation/results.d.ts +1 -1
  89. package/dist/delegation/results.js +23 -13
  90. package/dist/delegation/results.js.map +1 -1
  91. package/dist/delegation/routes.d.ts +142 -15
  92. package/dist/delegation/routes.js +4 -1
  93. package/dist/delegation/routes.js.map +1 -1
  94. package/dist/delegation/service.d.ts +9 -5
  95. package/dist/delegation/service.js +106 -7
  96. package/dist/delegation/service.js.map +1 -1
  97. package/dist/delegation/transport.d.ts +188 -61
  98. package/dist/index.js +72 -26
  99. package/dist/index.js.map +1 -1
  100. package/dist/open-url.d.ts +2 -0
  101. package/dist/open-url.js +20 -0
  102. package/dist/open-url.js.map +1 -0
  103. package/dist/release/snapshot.d.ts +3 -3
  104. package/dist/release/snapshot.js +6 -6
  105. package/dist/release/snapshot.js.map +1 -1
  106. package/dist/runs/client-request.d.ts +11 -0
  107. package/dist/runs/client-request.js +26 -0
  108. package/dist/runs/client-request.js.map +1 -0
  109. package/dist/runs/delegation-state.d.ts +17 -0
  110. package/dist/runs/event-history.js +6 -0
  111. package/dist/runs/event-history.js.map +1 -1
  112. package/dist/runs/store.d.ts +78 -6
  113. package/dist/runs/store.js +163 -1
  114. package/dist/runs/store.js.map +1 -1
  115. package/dist/server/server.d.ts +3761 -1648
  116. package/dist/server/server.js +170 -4
  117. package/dist/server/server.js.map +1 -1
  118. package/dist/task-cli/cli.d.ts +38 -0
  119. package/dist/task-cli/cli.js +463 -0
  120. package/dist/task-cli/cli.js.map +1 -0
  121. package/dist/task-cli/discovery.d.ts +22 -0
  122. package/dist/task-cli/discovery.js +95 -0
  123. package/dist/task-cli/discovery.js.map +1 -0
  124. package/dist/task-cli/http.d.ts +43 -0
  125. package/dist/task-cli/http.js +101 -0
  126. package/dist/task-cli/http.js.map +1 -0
  127. package/dist/task-cli/projections.d.ts +40 -0
  128. package/dist/task-cli/projections.js +45 -0
  129. package/dist/task-cli/projections.js.map +1 -0
  130. package/dist/task-cli/watch.d.ts +78 -0
  131. package/dist/task-cli/watch.js +336 -0
  132. package/dist/task-cli/watch.js.map +1 -0
  133. package/dist/workflows/run.d.ts +93 -0
  134. package/dist/workflows/run.js +816 -81
  135. package/dist/workflows/run.js.map +1 -1
  136. package/dist/workspace/projects-cli.js +1 -1
  137. package/package.json +3 -3
  138. package/scripts/mock-claude.mjs +38 -2
  139. package/scripts/mock-codex-app-server.mjs +98 -1
  140. package/scripts/mock-cursor-acp.mjs +8 -0
  141. package/scripts/mock-opencode-serve.mjs +69 -2
  142. package/scripts/mock-pi-rpc.mjs +63 -2
  143. package/web/dist/assets/agent-accounts-JPfZPgKn.js +1 -0
  144. package/web/dist/assets/centered-state-HwCqQkH3.js +43 -0
  145. package/web/dist/assets/collapsible-CTymFdL7.js +1 -0
  146. package/web/dist/assets/commit-list-BZpYGQl2.js +1 -0
  147. package/web/dist/assets/compare-variants-9VkybaQ7.js +1 -0
  148. package/web/dist/assets/{diff-tRE59UNb2.js → diff-CZcD4D1r2.js} +2 -2
  149. package/web/dist/assets/diff-controls-CpcHG3Mm.js +1 -0
  150. package/web/dist/assets/{diff-stat-Cgnl_hYF.js → diff-stat-Cn7rzy1H.js} +1 -1
  151. package/web/dist/assets/{diff-view-DN1ort7Q.js → diff-view-C46nHiP2.js} +2 -2
  152. package/web/dist/assets/dropdown-menu-_YAzCaoQ.js +1 -0
  153. package/web/dist/assets/git-toolbar-BvKDz6uS.js +1 -0
  154. package/web/dist/assets/github-CgjJCGrD.js +1 -0
  155. package/web/dist/assets/{image-preview-C2JGcBlC.js → image-preview-B618kDBH.js} +1 -1
  156. package/web/dist/assets/index-BJtve0Pm.js +7 -0
  157. package/web/dist/assets/index-D0WAgmTQ.css +1 -0
  158. package/web/dist/assets/markdown-CFWZnvMh.js +2 -0
  159. package/web/dist/assets/pill-QfwkuR50.js +1 -0
  160. package/web/dist/assets/project-router-CkAA39N5.js +1 -0
  161. package/web/dist/assets/prompt-templates-Cfna_S0T.js +15 -0
  162. package/web/dist/assets/repo-git-DgzgJ0cP.js +1 -0
  163. package/web/dist/assets/{run-diff-CyG0Nciv.js → run-diff-YbCEQn5I.js} +2 -2
  164. package/web/dist/assets/run-header-DMgIbr1d.js +1 -0
  165. package/web/dist/assets/skills-BW79kUTd.js +1 -0
  166. package/web/dist/assets/{tab-link-BWNp3mLY.js → tab-link-MIHvczEm.js} +1 -1
  167. package/web/dist/assets/task-changes-C96ekt94.js +1 -0
  168. package/web/dist/assets/task-commits-DBfcMZyG.js +1 -0
  169. package/web/dist/assets/task-files-BKpH8dKQ.js +2 -0
  170. package/web/dist/assets/task-thread-Bw_nQuTx.css +1 -0
  171. package/web/dist/assets/task-thread-Dnq7Ek6W.js +9 -0
  172. package/web/dist/assets/textarea-D2vAVgYW.js +1 -0
  173. package/web/dist/assets/thread-loading-NW8Mthco.js +1 -0
  174. package/web/dist/assets/token-metrics-D188e6_0.js +1 -0
  175. package/web/dist/assets/utils-DKheTxGD.js +64 -0
  176. package/web/dist/assets/workflows-BFY1hgp3.js +11 -0
  177. package/web/dist/assets/{zoomable-image-Z3JDnm0b.js → zoomable-image-YW3Qdrr_.js} +1 -1
  178. package/web/dist/index.html +14 -14
  179. package/web/dist/assets/agent-accounts-BZm_2WAv.js +0 -1
  180. package/web/dist/assets/alert-dialog-B422RI-k.js +0 -43
  181. package/web/dist/assets/collapsible-Cy_Fmkrd.js +0 -1
  182. package/web/dist/assets/commit-list-Dcnk7TnI.js +0 -1
  183. package/web/dist/assets/compare-variants-Ciqwvd_f.js +0 -1
  184. package/web/dist/assets/diff-controls-BGrXGi7O.js +0 -1
  185. package/web/dist/assets/dropdown-menu-dwDGT8MM.js +0 -1
  186. package/web/dist/assets/git-toolbar-UiHnGvE0.js +0 -1
  187. package/web/dist/assets/github-D-MrkbJ7.js +0 -1
  188. package/web/dist/assets/ibm-plex-mono-regular-BF3vfe7_.ttf +0 -0
  189. package/web/dist/assets/index-BszfYvBq.js +0 -7
  190. package/web/dist/assets/index-CWh4pKP3.css +0 -1
  191. package/web/dist/assets/markdown-C_IcKVQd.js +0 -2
  192. package/web/dist/assets/pill-BsbO6G7y.js +0 -1
  193. package/web/dist/assets/project-router-DbLkll25.js +0 -1
  194. package/web/dist/assets/prompt-templates-BlTGJB5W.js +0 -15
  195. package/web/dist/assets/repo-git-Bm3s6Why.js +0 -1
  196. package/web/dist/assets/run-header-IFyIGCey.js +0 -1
  197. package/web/dist/assets/skills-C-C4DVFY.js +0 -1
  198. package/web/dist/assets/task-changes-D2PHtWUW.js +0 -1
  199. package/web/dist/assets/task-commits-N5u9U0_R.js +0 -1
  200. package/web/dist/assets/task-files-BS7c7CFm.js +0 -2
  201. package/web/dist/assets/task-thread-BQFdz1dJ.css +0 -1
  202. package/web/dist/assets/task-thread-DGM2dZV-.js +0 -9
  203. package/web/dist/assets/textarea-FjaU370i.js +0 -1
  204. package/web/dist/assets/thread-loading-77sJ8Yu_.js +0 -1
  205. package/web/dist/assets/token-metrics-Clxh03bE.js +0 -1
  206. package/web/dist/assets/utils-PYYcpCFZ.js +0 -64
  207. package/web/dist/assets/workflows-Czl0ZfXH.js +0 -11
@@ -10,7 +10,7 @@ const defaultIo = {
10
10
  const USAGE = `usage:
11
11
  cez projects [list] list the registered projects
12
12
  cez projects add [<dir>] register a folder (default: --repo, else cwd)
13
- cez projects remove <id> drop a registry entry (the repo is untouched)
13
+ cez projects remove|rm <id> drop a registry entry (the repo is untouched)
14
14
  cez projects tag <id> [<tag>…]
15
15
  set the grouping tags of a project (none clears them)
16
16
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wjarka/cezarion",
3
- "version": "0.14.8",
3
+ "version": "0.14.9-dev.842",
4
4
  "description": "Local cockpit for running and tracking AI agent tasks in your repo. Uses your logged-in `claude` CLI and `gh` — zero config, zero database.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -51,8 +51,8 @@
51
51
  "zod": "^4.4.3"
52
52
  },
53
53
  "devDependencies": {
54
- "@open-mercato/cezar-api-client": "^0.14.8",
55
- "@open-mercato/cezar-contract": "^0.14.8",
54
+ "@open-mercato/cezar-api-client": "0.14.9-dev.842",
55
+ "@open-mercato/cezar-contract": "0.14.9-dev.842",
56
56
  "@types/node": "^20.14.0",
57
57
  "@types/ws": "^8.18.1",
58
58
  "esbuild": "^0.28.1",
@@ -11,6 +11,12 @@ import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
11
11
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
12
12
  const emit = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
13
13
 
14
+ // `--help` lists the options cezar feature-detects (#505).
15
+ if (process.argv.includes('--help')) {
16
+ process.stdout.write('Usage: claude [options]\n --input-format <format>\n --replay-user-messages Re-emit user messages from stdin back on stdout\n');
17
+ process.exit(0);
18
+ }
19
+
14
20
  // Testability hook: CEZ_MOCK_ARGS_FILE=<path> appends the argv this mock was
15
21
  // spawned with (one JSON array per line), so tests and dry-run proofs can
16
22
  // assert exactly what reached the CLI (e.g. `--append-system-prompt …`).
@@ -26,6 +32,14 @@ if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-c
26
32
  emit({ type: 'system', subtype: 'init' });
27
33
 
28
34
  let turn = 0;
35
+ // #505: `--replay-user-messages` echoes each stdin line when the model consumes it.
36
+ const replay = process.argv.includes('--replay-user-messages') && process.env.CEZ_MOCK_CLAUDE_NO_REPLAY !== '1';
37
+ const emitReplay = (uuid, text) => {
38
+ if (replay && uuid) emit({ type: 'user', isReplay: true, uuid, message: { role: 'user', content: [{ type: 'text', text }] } });
39
+ };
40
+ // Non-null while a `mock:steer-tool` turn is inside its tool: later lines join that turn.
41
+ let steering = null;
42
+ let droppedOnce = false;
29
43
 
30
44
  // A tiny generated PNG (320x200) standing in for a browser screenshot.
31
45
  const MOCK_SCREENSHOT_B64 =
@@ -75,7 +89,22 @@ function writeHandoffAndTodo() {
75
89
  }
76
90
  }
77
91
 
78
- async function respond(userText, imageCount) {
92
+ async function respond(userText, imageCount, uuid) {
93
+ emitReplay(uuid, userText);
94
+ // `mock:steer-tool` → one tool call; lines written while it runs are consumed after
95
+ // it (replayed with their uuid) and settled by the SAME result, like Claude 2.1.280 (#505).
96
+ if (userText.includes('mock:steer-tool')) {
97
+ steering = [];
98
+ emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: 'toolu_steer', name: 'Bash', input: { command: 'wait' } }] } });
99
+ await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
100
+ const steered = steering; steering = null;
101
+ emit({ type: 'user', message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'toolu_steer', content: 'waited' }] } });
102
+ for (const s of steered) emitReplay(s.uuid, s.userText);
103
+ const text = ['steer tool done', ...steered.map(s => `saw: ${s.userText}`)].join('\n');
104
+ emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text }] } });
105
+ emit({ type: 'result', subtype: 'success', result: text, user_message_uuids: [uuid, ...steered.map(s => s.uuid)].filter(Boolean), usage: { input_tokens: 20, output_tokens: 10 } });
106
+ return;
107
+ }
79
108
  if (userText.includes('mock:ci-wait')) {
80
109
  const { ciPrompt } = await import('./mock-ci-tool.mjs');
81
110
  const text = await ciPrompt('claude', process.argv.slice(2), userText);
@@ -583,6 +612,7 @@ rl.on('line', (line) => {
583
612
  if (!trimmed) return;
584
613
  let userText = '(unparseable message)';
585
614
  let imageCount = 0;
615
+ let uuid;
586
616
  try {
587
617
  const msg = JSON.parse(trimmed);
588
618
  if (msg.type === 'control_request' && msg.request?.subtype === 'list_models') {
@@ -595,6 +625,7 @@ rl.on('line', (line) => {
595
625
  })}\n`);
596
626
  return;
597
627
  }
628
+ uuid = typeof msg.uuid === 'string' ? msg.uuid : undefined;
598
629
  const blocks = msg?.message?.content ?? [];
599
630
  userText = blocks.filter((b) => b.type === 'text').map((b) => b.text).join('\n') || '(no text)';
600
631
  imageCount = blocks.filter((b) => b.type === 'image').length;
@@ -613,7 +644,12 @@ rl.on('line', (line) => {
613
644
  // best effort — never break the mock over the hook
614
645
  }
615
646
  }
616
- queue = queue.then(() => respond(userText, imageCount));
647
+ // #505 liveness tests: silently drop a matching line (a harness that accepted input
648
+ // and never runs it). CEZ_MOCK_DROP_ONCE=1 drops only the first match.
649
+ const drop = process.env.CEZ_MOCK_DROP_LINES;
650
+ if (drop && userText.includes(drop) && !(process.env.CEZ_MOCK_DROP_ONCE === '1' && droppedOnce)) { droppedOnce = true; return; }
651
+ if (steering) { steering.push({ userText, uuid }); return; }
652
+ queue = queue.then(() => respond(userText, imageCount, uuid));
617
653
  });
618
654
  rl.on('close', () => {
619
655
  // EOF = session over; finish any in-flight turn then exit cleanly.
@@ -11,10 +11,42 @@
11
11
  import { createInterface } from 'node:readline';
12
12
  import { appendFileSync } from 'node:fs';
13
13
 
14
- const emit = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
14
+ const write = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
15
+ // #505: like the real app-server, any active main-thread turn accepts `turn/steer`.
16
+ // Steers accepted by an ordinary scripted turn are read just before it completes:
17
+ // a userMessage item (echoing clientUserMessageId as clientId) and an echo reply.
18
+ let activeTurnId = null;
19
+ let pendingSteers = [];
20
+ let steerEchoSerial = 0;
21
+ const emit = (obj) => {
22
+ const ending = (obj.method === 'turn/completed' || obj.method === 'turn/failed') && (!obj.params?.threadId || obj.params.threadId === 'th_mock_1');
23
+ if (ending && obj.method === 'turn/completed' && pendingSteers.length) {
24
+ const turnId = activeTurnId ?? 'turn_mock_1';
25
+ for (const entry of pendingSteers.splice(0)) {
26
+ const item = { type: 'userMessage', id: `item_user_generic_${++steerEchoSerial}`, clientId: entry.clientId, content: [{ type: 'text', text: entry.text }] };
27
+ write({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item } });
28
+ write({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item } });
29
+ write({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { type: 'agentMessage', id: `item_steer_echo_${steerEchoSerial}`, text: entry.text } } });
30
+ }
31
+ }
32
+ if (ending) { activeTurnId = null; pendingSteers = []; }
33
+ write(obj);
34
+ // #505 review: CEZ_MOCK_CODEX_LATE_START_ACK=1 answers turn/start after the turn completed.
35
+ if (ending && lateStartAck !== undefined) { const id = lateStartAck; lateStartAck = undefined; write({ id, result: { turn: { id: 'turn_mock_1' } } }); }
36
+ };
37
+ let lateStartAck;
38
+ let startSerial = 0;
15
39
  const rl = createInterface({ input: process.stdin });
16
40
  let echoSerial = 0;
17
41
  let ciWire;
42
+ // #505 steering scenarios: the one turn that accepts `turn/steer`, while it is open.
43
+ let steerTurn = null;
44
+ let steerSerial = 0;
45
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
46
+ const completeSteerTurn = (turnId) => {
47
+ steerTurn = null;
48
+ emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: turnId, status: 'completed' } } });
49
+ };
18
50
 
19
51
  const ignoreEof = process.env.MOCK_CODEX_IGNORE_EOF === '1';
20
52
  if (ignoreEof) {
@@ -43,6 +75,34 @@ rl.on('line', async (line) => {
43
75
  emit((Array.isArray(answer) && answer[0] === 'Vitest') || (Array.isArray(freeText) && freeText[0] === 'Use sensible defaults')
44
76
  ? { method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } }
45
77
  : { method: 'turn/failed', params: { turn: { id: 'turn_mock_1', status: 'failed' }, error: { message: 'bad answer' } } });
78
+ } else if (msg.method === 'turn/steer') {
79
+ // #505 ambiguity: the transport dies with the steer unanswered.
80
+ if (process.env.CEZ_MOCK_CODEX_EXIT_ON_STEER === '1') process.exit(1);
81
+ const turn = steerTurn;
82
+ if (!turn && activeTurnId && msg.params?.expectedTurnId === activeTurnId) {
83
+ pendingSteers.push({ clientId: msg.params?.clientUserMessageId ?? null, text: msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '' });
84
+ emit({ id: msg.id, result: {} });
85
+ return;
86
+ }
87
+ if (!turn || msg.params?.expectedTurnId !== turn.id) {
88
+ emit({ id: msg.id, error: { code: -32600, message: 'no active turn matching expectedTurnId' } });
89
+ return;
90
+ }
91
+ const text = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
92
+ if (turn.strand) {
93
+ // The turn completes first, then the server acknowledges the steer: nothing reads it.
94
+ completeSteerTurn(turn.id);
95
+ emit({ id: msg.id, result: {} });
96
+ return;
97
+ }
98
+ if (turn.race) {
99
+ // The turn ends before the server processes the steer: a definitive mismatch.
100
+ completeSteerTurn(turn.id);
101
+ emit({ id: msg.id, error: { code: -32600, message: 'no active turn matching expectedTurnId' } });
102
+ return;
103
+ }
104
+ turn.steered.push({ clientId: msg.params?.clientUserMessageId ?? null, text });
105
+ emit({ id: msg.id, result: {} });
46
106
  } else if (msg.method === 'initialize') {
47
107
  emit({ id: msg.id, result: { userAgent: 'mock-codex/0.0.0' } });
48
108
  } else if (msg.method === 'thread/start' || msg.method === 'thread/resume') {
@@ -67,9 +127,20 @@ rl.on('line', async (line) => {
67
127
  emit({ id: msg.id, result: { thread: { id: msg.params?.threadId } } });
68
128
  }
69
129
  } else if (msg.method === 'turn/start') {
130
+ activeTurnId = 'turn_mock_1';
131
+ // owned-input-delivery.testkit.ts patches the exact `emit(...)` line below; keep it verbatim.
132
+ if (process.env.CEZ_MOCK_CODEX_LATE_START_ACK === '1' && startSerial++ > 0) lateStartAck = msg.id; else {
70
133
  emit({ id: msg.id, result: { turn: { id: 'turn_mock_1' } } });
134
+ }
71
135
  emit({ method: 'turn/started', params: { turn: { id: 'turn_mock_1', status: 'inProgress', items: [] } } });
72
136
  const turnText = msg.params?.input?.map?.((part) => part.text ?? '').join('\n') ?? '';
137
+ // The real app-server records the turn's own input as a userMessage item,
138
+ // echoing clientUserMessageId as clientId (probe 0.155.1, #505).
139
+ const opening = { type: 'userMessage', id: `item_user_open_${++steerEchoSerial}`, clientId: msg.params?.clientUserMessageId ?? null, content: [{ type: 'text', text: turnText }] };
140
+ if (process.env.CEZ_MOCK_CODEX_NO_USER_ITEM !== '1') {
141
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
142
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
143
+ }
73
144
  if (turnText.includes('mock:ci-wait')) {
74
145
  const { ciPrompt } = await import('./mock-ci-tool.mjs');
75
146
  const text = await ciPrompt('codex', ciWire, turnText);
@@ -77,6 +148,32 @@ rl.on('line', async (line) => {
77
148
  emit({ method: 'turn/completed', params: { turn: { id: 'turn_mock_1', status: 'completed' } } });
78
149
  return;
79
150
  }
151
+ const steerScenario = ['mock:steer-tool', 'mock:steer-late', 'mock:steer-race', 'mock:steer-strand'].find((marker) => turnText.includes(marker));
152
+ if (steerScenario) {
153
+ const turnId = 'turn_mock_1';
154
+ steerTurn = { id: turnId, steered: [], race: steerScenario === 'mock:steer-race', strand: steerScenario === 'mock:steer-strand' };
155
+ const agent = (text) => emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { type: 'agentMessage', id: `item_steer_${++steerSerial}`, text } } });
156
+ if (steerScenario === 'mock:steer-late') {
157
+ agent(`late window: final message already sent${turnText.includes('mock:done-late') ? '\n\nCEZ:DONE' : ''}`);
158
+ await sleep(300);
159
+ completeSteerTurn(turnId); // steers accepted in this window are never read
160
+ return;
161
+ }
162
+ const exec = { type: 'commandExecution', id: 'exec_steer', command: 'wait', status: 'inProgress' };
163
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item: exec } });
164
+ await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
165
+ if (!steerTurn) return; // a race already ended it
166
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item: { ...exec, status: 'completed' } } });
167
+ const steered = steerTurn.steered;
168
+ for (const entry of steered) {
169
+ const item = { type: 'userMessage', id: `item_user_${++steerSerial}`, clientId: entry.clientId, content: [{ type: 'text', text: entry.text }] };
170
+ emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId, item } });
171
+ emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId, item } });
172
+ }
173
+ agent(['steer tool done', ...steered.map((entry) => `saw: ${entry.text}`)].join('\n'));
174
+ completeSteerTurn(turnId);
175
+ return;
176
+ }
80
177
  if (turnText.includes('mock:agent-echo')) {
81
178
  // Same documented agentMessage/turn completion frames as the baseline below.
82
179
  emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: `item_echo_${++echoSerial}`, text: turnText } } });
@@ -34,6 +34,14 @@ async function prompt(id, content) {
34
34
  if (input.includes('mock:rpc-error')) { emit({ id, error: { code: -32603, message: 'Provider rejected request' } }); return; }
35
35
  // #443 provider-error envelopes: the specific scenarios must be matched BEFORE the
36
36
  // bare 'mock:provider-error' prefix, which every one of them contains.
37
+ // #508: observed Cursor 2026.09.18-9a7762b error envelope, followed by end_turn.
38
+ if (input.includes('mock:provider-error-protocol') && (input.includes('-exhaust') || prompts === 1)) { text('\n\nError: RetriableError: [invalid_argument] protocol error: missing EndStreamResponse'); complete(id); return; }
39
+ // #531: observed run 2f891027 seq 2005 capacity envelope. Match before the bare prefix.
40
+ // Do not reuse the protocol mock's `includes('-exhaust')` check: that token is a
41
+ // substring of `resource-exhausted` and would never recover.
42
+ if (input.includes('mock:provider-error-resource-exhausted-exhaust')) { text('\n\nError: RetriableError: [resource_exhausted] Error'); complete(id); return; }
43
+ if (input.includes('mock:provider-error-resource-exhausted') && prompts === 1) { text('\n\nError: RetriableError: [resource_exhausted] Error'); complete(id); return; }
44
+ if (input.includes('mock:provider-error-unknown-protocol')) { text('\n\nError: [invalid_argument] protocol error: unknown frame'); complete(id); return; }
37
45
  if (input.includes('mock:provider-error-transient') && prompts === 1) { text('\n\nError: 502 bad gateway.'); complete(id); return; }
38
46
  if (input.includes('mock:provider-error-bare')) { text('\n\nError: 502 bad gateway.'); complete(id); return; }
39
47
  if (input.includes('mock:provider-error-instant-near') && prompts === 1) { text(`\n\nError: 429 rate limited, try again at ${new Date(Date.now() + 300).toISOString()}.`); complete(id); return; }
@@ -22,9 +22,30 @@ const SESSION_ID = 'ses_mock_1';
22
22
  const MESSAGE_ID = 'msg_mock_1';
23
23
 
24
24
  let sse = null;
25
- const send = (event) => {
25
+ const write = (event) => {
26
26
  if (sse) sse.write(`data: ${JSON.stringify(event)}\n\n`);
27
27
  };
28
+ // #505: like opencode 1.18.32, a prompt_async POSTed while the session is busy is
29
+ // steered into the running loop: its user message exists at once, and the next
30
+ // assistant message names it as parentID. Steers are read just before the turn's
31
+ // idle, unless the turn is `late` (the upstream lost wake: never read).
32
+ let turnActive = false;
33
+ let turnLate = false;
34
+ let steers = [];
35
+ let steerSerial = 0;
36
+ const send = (event) => {
37
+ const ownIdle = event?.type === 'session.idle' && (!event.properties?.sessionID || event.properties.sessionID === SESSION_ID);
38
+ if (ownIdle) {
39
+ if (!turnLate) for (const steer of steers) {
40
+ const id = `msg_steer_reply_${++steerSerial}`;
41
+ write({ type: 'message.updated', properties: { info: { ...info({}), id, parentID: steer.userId } } });
42
+ write({ type: 'message.part.updated', properties: { part: { id: `prt_${id}`, messageID: id, sessionID: SESSION_ID, type: 'text', text: steer.text } } });
43
+ }
44
+ steers = []; turnActive = false; turnLate = false;
45
+ }
46
+ write(event);
47
+ };
48
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
28
49
 
29
50
  /** Questions the runner can look up an id for. `replyQuestion`/`rejectQuestion`
30
51
  * resolve the id through `GET /question` before answering (see
@@ -51,10 +72,14 @@ const sendQuestionPart = (input) =>
51
72
  },
52
73
  },
53
74
  });
75
+ // The user message of the prompt being answered: opencode 1.18.32 creates it at the
76
+ // POST and names it as the assistant's parentID (#505 probe).
77
+ let currentUserId;
54
78
  const info = (extra) => ({
55
79
  id: MESSAGE_ID,
56
80
  sessionID: SESSION_ID,
57
81
  role: 'assistant',
82
+ ...(currentUserId ? { parentID: currentUserId } : {}),
58
83
  time: { created: 1760000000000 },
59
84
  modelID: 'mock-model',
60
85
  providerID: 'mock',
@@ -91,7 +116,8 @@ const server = createServer((req, res) => {
91
116
  send({ type: 'message.part.updated', properties: { part: {
92
117
  id: 'prt_answer', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: `human answer: ${body}${answerDone ? '\n\nCEZ:DONE' : ''}`,
93
118
  } } });
94
- send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
119
+ // #505 review: CEZ_MOCK_OPENCODE_REPLY_HOLD_MS keeps the answered turn running.
120
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), Number(process.env.CEZ_MOCK_OPENCODE_REPLY_HOLD_MS ?? 0));
95
121
  }, 30);
96
122
  const acknowledge = () => { res.writeHead(200, { 'content-type': 'application/json' }); res.end('{}'); };
97
123
  if (lateQuestionReply) setTimeout(acknowledge, 250);
@@ -112,9 +138,44 @@ const server = createServer((req, res) => {
112
138
  res.end(JSON.stringify({ error: 'agent prompt rejected' }));
113
139
  return;
114
140
  }
141
+ if (turnActive && url.endsWith('/prompt_async')) {
142
+ const text = JSON.parse(body).parts.map(part => part.text ?? '').join('\n');
143
+ const userId = `msg_steer_user_${++steerSerial}`;
144
+ // #505 review: acknowledge a steer later than the runner's lost-wake window.
145
+ const ackDelay = Number(process.env.CEZ_MOCK_OPENCODE_STEER_ACK_MS ?? 0);
146
+ if (ackDelay > 0) setTimeout(() => { res.writeHead(204); res.end(); }, ackDelay);
147
+ else { res.writeHead(204); res.end(); }
148
+ send({ type: 'message.updated', properties: { info: { id: userId, sessionID: SESSION_ID, role: 'user', time: { created: Date.now() } } } });
149
+ send({ type: 'message.part.updated', properties: { part: { id: `prt_${userId}`, messageID: userId, sessionID: SESSION_ID, type: 'text', text } } });
150
+ steers.push({ userId, text });
151
+ return;
152
+ }
153
+ turnActive = true;
115
154
  // `prompt_async` semantics: acknowledge now, stream the turn over SSE.
116
155
  res.writeHead(200, { 'content-type': 'application/json' });
117
156
  res.end(JSON.stringify({ info: info({}), parts: [] }));
157
+ if (url.endsWith('/prompt_async')) {
158
+ currentUserId = `msg_user_${++steerSerial}`;
159
+ const text = JSON.parse(body).parts.map(part => part.text ?? '').join('\n');
160
+ send({ type: 'message.updated', properties: { info: { id: currentUserId, sessionID: SESSION_ID, role: 'user', time: { created: Date.now() } } } });
161
+ send({ type: 'message.part.updated', properties: { part: { id: `prt_${currentUserId}`, messageID: currentUserId, sessionID: SESSION_ID, type: 'text', text } } });
162
+ }
163
+ if (body.includes('mock:steer-tool')) {
164
+ send({ type: 'message.updated', properties: { info: info({}) } });
165
+ send({ type: 'message.part.updated', properties: { part: { id: 'prt_steer_tool', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_steer', tool: 'bash', state: { status: 'running', input: { command: 'wait' } } } } });
166
+ await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
167
+ send({ type: 'message.part.updated', properties: { part: { id: 'prt_steer_tool', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'tool', callID: 'call_steer', tool: 'bash', state: { status: 'completed', input: { command: 'wait' }, output: 'waited' } } } });
168
+ send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
169
+ return;
170
+ }
171
+ if (body.includes('mock:steer-late')) {
172
+ send({ type: 'message.updated', properties: { info: info({}) } });
173
+ send({ type: 'message.part.updated', properties: { part: { id: 'prt_steer_late', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'late window: final message already sent', time: { start: 1760000000000, end: 1760000000001 } } } });
174
+ turnLate = true;
175
+ await sleep(300);
176
+ send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
177
+ return;
178
+ }
118
179
  // The raw body is enough to spot a `mock:` marker — the prompt text is
119
180
  // inside it whatever part shape the runner used to wrap it.
120
181
  if (body.includes('mock:ci-wait')) {
@@ -183,6 +244,12 @@ const server = createServer((req, res) => {
183
244
  }, 60);
184
245
  return;
185
246
  }
247
+ if (body.includes('mock:provider-error-early')) {
248
+ // #505 review: the provider rejects before any assistant message exists.
249
+ send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: { name: 'ProviderAuthError', data: { message: 'API key expired' } } } });
250
+ setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
251
+ return;
252
+ }
186
253
  if (body.includes('mock:provider-error')) {
187
254
  // The session-level error frame, wire shape copied verbatim from
188
255
  // `__fixtures__/opencode/session-error.ndjson`. #53: a bare upstream
@@ -8,7 +8,40 @@ process.on('SIGTERM', () => process.exit(143));
8
8
 
9
9
  if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('pi', process.argv.slice(2)); }
10
10
  const sessionId = '00000000-0000-4000-8000-0000000000pi';
11
- const send = (value) => process.stdout.write(`${JSON.stringify(value)}\n`);
11
+ const write = (value) => process.stdout.write(`${JSON.stringify(value)}\n`);
12
+ // #505: like pi 0.87, a prompt with streamingBehavior 'steer' joins the running
13
+ // turn. Steers accepted by a scripted turn are read just before it settles: a user
14
+ // message_start/end, then an echo, unless the turn is `late` (never read).
15
+ let activeTurn = null;
16
+ // The prompt a queued handler is running; pi records it as the turn's user message.
17
+ let currentPrompt;
18
+ const send = (value) => {
19
+ if (value?.type === 'turn_start' && !activeTurn) {
20
+ activeTurn = { steers: [], late: false };
21
+ write(value);
22
+ if (currentPrompt !== undefined && process.env.CEZ_MOCK_PI_NO_USER_START !== '1') {
23
+ const message = { role: 'user', content: [{ type: 'text', text: currentPrompt }] };
24
+ currentPrompt = undefined;
25
+ write({ type: 'message_start', message });
26
+ write({ type: 'message_end', message });
27
+ }
28
+ return;
29
+ }
30
+ if (value?.type === 'agent_settled' && activeTurn) {
31
+ const turn = activeTurn; activeTurn = null;
32
+ if (!turn.late) for (const text of turn.steers) {
33
+ write({ type: 'message_start', message: { role: 'user', content: [{ type: 'text', text }] } });
34
+ write({ type: 'message_end', message: { role: 'user', content: [{ type: 'text', text }] } });
35
+ sendText([text]);
36
+ write({ type: 'message_end', message: { role: 'assistant', usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, cost: { total: 0 } } } });
37
+ }
38
+ write(value);
39
+ // A steer that arrived after the final model call runs as the next prompt.
40
+ if (turn.late) for (const text of turn.steers) queue = queue.then(() => { currentPrompt = text; return handle({ type: 'prompt', message: text }); });
41
+ return;
42
+ }
43
+ write(value);
44
+ };
12
45
  const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
13
46
 
14
47
  /** One valid CEZ:ASK payload (spec #473), used by the `mock:ask` scenario. */
@@ -32,8 +65,21 @@ function sendTurnEnd(usage = { input: 10, output: 5, cacheRead: 0, cacheWrite: 0
32
65
  send({ type: 'agent_settled' });
33
66
  }
34
67
 
35
- for await (const line of readline.createInterface({ input: process.stdin })) {
68
+ const rl = readline.createInterface({ input: process.stdin });
69
+ let queue = Promise.resolve();
70
+ rl.on('line', (line) => {
36
71
  const command = JSON.parse(line);
72
+ if (command.type === 'prompt' && command.streamingBehavior === 'steer' && activeTurn) {
73
+ if (process.env.CEZ_MOCK_STDIN_FILE) appendFileSync(process.env.CEZ_MOCK_STDIN_FILE, `${JSON.stringify({ userText: command.message, imageCount: 0, streamingBehavior: 'steer' })}\n`);
74
+ activeTurn.steers.push(command.message);
75
+ write({ id: command.id, type: 'response', command: 'prompt', success: true });
76
+ return;
77
+ }
78
+ queue = queue.then(() => { currentPrompt = command.type === 'prompt' ? command.message : undefined; return handle(command); });
79
+ });
80
+ rl.on('close', () => { queue.then(() => process.exit(0)); });
81
+
82
+ async function handle(command) {
37
83
  // Testability hook, mirroring mock-claude: CEZ_MOCK_STDIN_FILE=<path> appends
38
84
  // each inbound prompt's text and image count, so tests can assert what the
39
85
  // runner actually wrote onto the RPC (harness parity's AgentRunSpec probes).
@@ -68,6 +114,21 @@ for await (const line of readline.createInterface({ input: process.stdin })) {
68
114
  const { ciPrompt } = await import('./mock-ci-tool.mjs');
69
115
  sendText([await ciPrompt('pi', process.argv.slice(2), command.message)]);
70
116
  sendTurnEnd();
117
+ } else if (command.type === 'prompt' && command.message.includes('mock:steer-tool')) {
118
+ send({ id: command.id, type: 'response', command: 'prompt', success: true });
119
+ send({ type: 'agent_start' }); send({ type: 'turn_start' });
120
+ send({ type: 'tool_execution_start', toolCallId: 'tool-steer', toolName: 'bash', args: { command: 'wait' } });
121
+ await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
122
+ send({ type: 'tool_execution_end', toolCallId: 'tool-steer', toolName: 'bash', result: { content: [{ type: 'text', text: 'waited' }] }, isError: false });
123
+ sendText(['steer tool done']);
124
+ sendTurnEnd();
125
+ } else if (command.type === 'prompt' && command.message.includes('mock:steer-late')) {
126
+ send({ id: command.id, type: 'response', command: 'prompt', success: true });
127
+ send({ type: 'agent_start' }); send({ type: 'turn_start' });
128
+ sendText(['late window: final message already sent']);
129
+ activeTurn.late = true;
130
+ await sleep(300);
131
+ sendTurnEnd();
71
132
  } else if (command.type === 'prompt' && command.message.includes('mock:agent-echo')) {
72
133
  // rpc-lifecycle.ndjson's normal prompt/assistant/settled sequence.
73
134
  send({ id: command.id, type: 'response', command: 'prompt', success: true });
@@ -0,0 +1 @@
1
+ import{r as e}from"./rolldown-runtime-QTnfLwEv.js";import{i as t,t as n}from"./react-runtime-CCIEwYL0.js";import{C as r,D as i,I as a,M as o,N as s,O as c,S as l,T as u,Zn as d,b as f,j as p,k as m,un as h,w as g,x as _,z as v}from"./centered-state-HwCqQkH3.js";import{t as y}from"./utils-DKheTxGD.js";import{_ as b,g as ee,h as x,m as te,p as S}from"./dropdown-menu-_YAzCaoQ.js";var C=e(t(),1),w=n(),T=`Popover`,[E,ne]=o(T,[b]),D=b(),[O,k]=E(T),A=e=>{let{__scopePopover:t,children:n,open:r,defaultOpen:a,onOpenChange:o,modal:s=!1}=e,c=D(t),l=C.useRef(null),[u,d]=C.useState(!1),[f,p]=m({prop:r,defaultProp:a??!1,onChange:o,caller:T});return(0,w.jsx)(ee,{...c,children:(0,w.jsx)(O,{scope:t,contentId:i(),triggerRef:l,open:f,onOpenChange:p,onOpenToggle:C.useCallback(()=>p(e=>!e),[p]),hasCustomAnchor:u,onCustomAnchorAdd:C.useCallback(()=>d(!0),[]),onCustomAnchorRemove:C.useCallback(()=>d(!1),[]),modal:s,children:n})})};A.displayName=T;var j=`PopoverAnchor`,M=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(j,n),a=D(n),{onCustomAnchorAdd:o,onCustomAnchorRemove:s}=i;return C.useEffect(()=>(o(),()=>s()),[o,s]),(0,w.jsx)(S,{...a,...r,ref:t})});M.displayName=j;var N=`PopoverTrigger`,P=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(N,n),a=D(n),o=v(t,i.triggerRef),c=(0,w.jsx)(s.button,{type:`button`,"aria-haspopup":`dialog`,"aria-expanded":i.open,"aria-controls":i.open?i.contentId:void 0,"data-state":Y(i.open),...r,ref:o,onClick:p(e.onClick,i.onOpenToggle)});return i.hasCustomAnchor?c:(0,w.jsx)(S,{asChild:!0,...a,children:c})});P.displayName=N;var F=`PopoverPortal`,[I,L]=E(F,{forceMount:void 0}),R=e=>{let{__scopePopover:t,forceMount:n,children:i,container:a}=e,o=k(F,t);return(0,w.jsx)(I,{scope:t,forceMount:n,children:(0,w.jsx)(c,{present:n||o.open,children:(0,w.jsx)(r,{asChild:!0,container:a,children:i})})})};R.displayName=F;var z=`PopoverContent`,B=C.forwardRef((e,t)=>{let n=L(z,e.__scopePopover),{forceMount:r=n.forceMount,...i}=e,a=k(z,e.__scopePopover);return(0,w.jsx)(c,{present:r||a.open,children:a.modal?(0,w.jsx)(H,{...i,ref:t}):(0,w.jsx)(U,{...i,ref:t})})});B.displayName=z;var V=a(`PopoverContent.RemoveScroll`),H=C.forwardRef((e,t)=>{let n=k(z,e.__scopePopover),r=C.useRef(null),i=v(t,r),a=C.useRef(!1);return C.useEffect(()=>{let e=r.current;if(e)return f(e)},[]),(0,w.jsx)(_,{as:V,allowPinchZoom:!0,children:(0,w.jsx)(W,{...e,ref:i,trapFocus:n.open,disableOutsidePointerEvents:!0,onCloseAutoFocus:p(e.onCloseAutoFocus,e=>{e.preventDefault(),a.current||n.triggerRef.current?.focus()}),onPointerDownOutside:p(e.onPointerDownOutside,e=>{let t=e.detail.originalEvent,n=t.button===0&&t.ctrlKey===!0,r=t.button===2||n;a.current=r},{checkForDefaultPrevented:!1}),onFocusOutside:p(e.onFocusOutside,e=>e.preventDefault(),{checkForDefaultPrevented:!1})})})}),U=C.forwardRef((e,t)=>{let n=k(z,e.__scopePopover),r=C.useRef(!1),i=C.useRef(!1);return(0,w.jsx)(W,{...e,ref:t,trapFocus:!1,disableOutsidePointerEvents:!1,onCloseAutoFocus:t=>{e.onCloseAutoFocus?.(t),t.defaultPrevented||(r.current||n.triggerRef.current?.focus(),t.preventDefault()),r.current=!1,i.current=!1},onInteractOutside:t=>{e.onInteractOutside?.(t),t.defaultPrevented||(r.current=!0,t.detail.originalEvent.type===`pointerdown`&&(i.current=!0));let a=t.target;n.triggerRef.current?.contains(a)&&t.preventDefault(),t.detail.originalEvent.type===`focusin`&&i.current&&t.preventDefault()}})}),W=C.forwardRef((e,t)=>{let{__scopePopover:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,disableOutsidePointerEvents:o,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:d,onInteractOutside:f,...p}=e,m=k(z,n),h=D(n);return l(),(0,w.jsx)(g,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,w.jsx)(u,{asChild:!0,disableOutsidePointerEvents:o,onInteractOutside:f,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:d,onDismiss:()=>m.onOpenChange(!1),deferPointerDownOutside:!0,children:(0,w.jsx)(x,{"data-state":Y(m.open),role:`dialog`,id:m.contentId,...h,...p,ref:t,style:{...p.style,"--radix-popover-content-transform-origin":`var(--radix-popper-transform-origin)`,"--radix-popover-content-available-width":`var(--radix-popper-available-width)`,"--radix-popover-content-available-height":`var(--radix-popper-available-height)`,"--radix-popover-trigger-width":`var(--radix-popper-anchor-width)`,"--radix-popover-trigger-height":`var(--radix-popper-anchor-height)`}})})})}),G=`PopoverClose`,K=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(G,n);return(0,w.jsx)(s.button,{type:`button`,...r,ref:t,onClick:p(e.onClick,()=>i.onOpenChange(!1))})});K.displayName=G;var q=`PopoverArrow`,J=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=D(n);return(0,w.jsx)(te,{...i,...r,ref:t})});J.displayName=q;function Y(e){return e?`open`:`closed`}var X=A,Z=M,re=P,ie=R,ae=B;function Q(e){let t=e.visualViewport;return t?Math.max(0,Math.round(e.innerHeight-t.height-t.offsetTop)):0}function oe(e,t,n,r=250){let i=e.visualViewport;if(!i)return t(0),()=>{};let a,o=()=>{let i=Q(e);t(i),n&&(clearTimeout(a),a=setTimeout(()=>n(i),r))};return t(Q(e)),i.addEventListener(`resize`,o),i.addEventListener(`scroll`,o),()=>{clearTimeout(a),i.removeEventListener(`resize`,o),i.removeEventListener(`scroll`,o),t(0)}}function se(e){let t=e.visualViewport;return t?{top:Math.max(0,Math.round(t.offsetTop)),bottom:Q(e)}:{top:0,bottom:0}}function ce(e,t=0){let n=typeof t==`number`?{top:t,right:t,bottom:t,left:t}:t;return{top:(n.top??0)+e.top,right:n.right??0,bottom:(n.bottom??0)+e.bottom,left:n.left??0}}function le(e,t){let n=e.visualViewport;if(!n)return t({top:0,bottom:0}),()=>{};let r=()=>t(se(e));return r(),n.addEventListener(`resize`,r),n.addEventListener(`scroll`,r),()=>{n.removeEventListener(`resize`,r),n.removeEventListener(`scroll`,r)}}function ue(){let[e,t]=(0,C.useState)({top:0,bottom:0});return(0,C.useEffect)(()=>le(window,e=>t(t=>t.top===e.top&&t.bottom===e.bottom?t:e)),[]),e}function de(e){let t=(0,C.useRef)(e);t.current=e,(0,C.useEffect)(()=>{let e=document.documentElement;return oe(window,t=>e.style.setProperty(`--kb`,`${t}px`),e=>t.current?.(e))},[])}var $=C.createContext(null);function fe({...e}){let t=C.useRef(!1);return(0,w.jsx)($,{value:t,children:(0,w.jsx)(X,{"data-slot":`popover`,...e})})}function pe({onPointerDownCapture:e,onKeyDownCapture:t,...n}){let r=C.useContext($);return(0,w.jsx)(re,{"data-slot":`popover-trigger`,...n,onPointerDownCapture:t=>{r&&(r.current=t.pointerType===`touch`),e?.(t)},onKeyDownCapture:e=>{r&&(r.current=!1),t?.(e)}})}function me({className:e,align:t=`center`,sideOffset:n=4,collisionPadding:r,onOpenAutoFocus:i,...a}){let o=ue(),s=C.useContext($);return(0,w.jsx)(ie,{children:(0,w.jsx)(ae,{"data-slot":`popover-content`,align:t,sideOffset:n,onOpenAutoFocus:e=>{let t=s?.current;if(s&&(s.current=!1),i?.(e),e.defaultPrevented||!t)return;let n=e.target;n instanceof HTMLElement&&n.querySelector(`[cmdk-input]`)&&(e.preventDefault(),n.focus({preventScroll:!0}))},collisionPadding:ce(o,r),className:y(`z-50 w-72 max-w-(--radix-popover-content-available-width) [overflow-wrap:anywhere] origin-(--radix-popover-content-transform-origin) rounded-md border bg-popover p-4 text-popover-foreground shadow-md outline-hidden data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95`,e),...a})})}function he({...e}){return(0,w.jsx)(Z,{"data-slot":`popover-anchor`,...e})}function ge(){let e=h(),t=d(),n=(e.data?.profiles??[]).map(e=>({provider:e.provider,id:e.id,label:e.label,configDir:e.configDir})),r=t.data?.info?.root;return{accounts:n,repoAccount:r?e.data?.selections?.[r]:void 0}}function _e(e,t){return e.filter(e=>e.provider===t).length>1}export{me as a,he as i,ge as n,pe as o,fe as r,de as s,_e as t};