@wjarka/cezarion 0.16.0-dev.1503 → 0.16.0-pr923.1504

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 (79) hide show
  1. package/README.md +15 -4
  2. package/dist/contract/index.js +3 -0
  3. package/dist/contract/runs.d.ts +30 -0
  4. package/dist/contract/skills.d.ts +4 -0
  5. package/dist/core/agent-runner.d.ts +9 -6
  6. package/dist/core/agent-runner.js +4 -2
  7. package/dist/core/agent-runner.js.map +1 -1
  8. package/dist/core/cursor-print-capabilities.d.ts +12 -0
  9. package/dist/core/cursor-print-capabilities.js +77 -0
  10. package/dist/core/cursor-print-capabilities.js.map +1 -0
  11. package/dist/core/cursor-print-process.d.ts +31 -0
  12. package/dist/core/cursor-print-process.js +129 -0
  13. package/dist/core/cursor-print-process.js.map +1 -0
  14. package/dist/core/cursor-print-runner.d.ts +26 -0
  15. package/dist/core/cursor-print-runner.js +512 -0
  16. package/dist/core/cursor-print-runner.js.map +1 -0
  17. package/dist/core/cursor-print-tool-catalog.d.ts +6 -0
  18. package/dist/core/cursor-print-tool-catalog.js +78 -0
  19. package/dist/core/cursor-print-tool-catalog.js.map +1 -0
  20. package/dist/core/cursor-print-ui-mapper.d.ts +47 -0
  21. package/dist/core/cursor-print-ui-mapper.js +573 -0
  22. package/dist/core/cursor-print-ui-mapper.js.map +1 -0
  23. package/dist/core/cursor-runner.d.ts +24 -0
  24. package/dist/core/cursor-runner.js +146 -0
  25. package/dist/core/cursor-runner.js.map +1 -0
  26. package/dist/core/process-usage.d.ts +5 -0
  27. package/dist/core/process-usage.js +24 -4
  28. package/dist/core/process-usage.js.map +1 -1
  29. package/dist/core/runner-factory.d.ts +4 -1
  30. package/dist/core/runner-factory.js +3 -3
  31. package/dist/core/runner-factory.js.map +1 -1
  32. package/dist/delegation/transport.d.ts +13 -13
  33. package/dist/runs/store.d.ts +9 -0
  34. package/dist/runs/store.js +1 -0
  35. package/dist/runs/store.js.map +1 -1
  36. package/dist/server/server.d.ts +118 -92
  37. package/dist/workflows/run.js +39 -8
  38. package/dist/workflows/run.js.map +1 -1
  39. package/package.json +3 -3
  40. package/scripts/mock-ci-tool.mjs +12 -4
  41. package/scripts/mock-cursor-acp.mjs +1 -0
  42. package/scripts/mock-cursor-print.mjs +196 -0
  43. package/scripts/probe-cursor-print.mjs +616 -0
  44. package/web/dist/assets/{agent-accounts-CFOBMVHc.js → agent-accounts-CDhVzwnd.js} +1 -1
  45. package/web/dist/assets/{alert-dialog-BrpNUwMt.js → alert-dialog-A0-kEfJK.js} +1 -1
  46. package/web/dist/assets/{button-C_rd7CPp.js → button-Jj1Itx83.js} +1 -1
  47. package/web/dist/assets/{compare-variants-D6WXhvXA.js → compare-variants-aor2j4bO.js} +1 -1
  48. package/web/dist/assets/{diff-DrEARXTV.js → diff-CDL5SDNO.js} +2 -2
  49. package/web/dist/assets/{diff-controls-B1-j7nFg.js → diff-controls-BWAkxipD.js} +1 -1
  50. package/web/dist/assets/{diff-view-DvGLjdTk.js → diff-view-DWh9FnNY.js} +1 -1
  51. package/web/dist/assets/{dropdown-menu-BegOZFrj.js → dropdown-menu-Ddqp8eUw.js} +1 -1
  52. package/web/dist/assets/{format-DpNe3S0A.js → format-DTNjugyE.js} +1 -1
  53. package/web/dist/assets/{git-toolbar-Cw4ljmhB.js → git-toolbar-B4rwm9Lr.js} +1 -1
  54. package/web/dist/assets/{github-CfV5sqL0.js → github-DgMD1v4U.js} +1 -1
  55. package/web/dist/assets/{image-preview-FOEfqpZP.js → image-preview-Dy5i96RC.js} +1 -1
  56. package/web/dist/assets/{index-DgWWlOLy.js → index-BfAearY6.js} +4 -4
  57. package/web/dist/assets/{nav-row-styles-vp2JbhuJ.js → nav-row-styles-Ci-Ke7wG.js} +1 -1
  58. package/web/dist/assets/{popover-BohP6WTx.js → popover-BjjDOSGv.js} +1 -1
  59. package/web/dist/assets/{preview-pane-connected-qTA5KNoJ.js → preview-pane-connected-Nv_nhV63.js} +1 -1
  60. package/web/dist/assets/{preview-state-vuMviq6q.js → preview-state-D8-YBJ6m.js} +1 -1
  61. package/web/dist/assets/{prompt-templates-Cubdut_C.js → prompt-templates-DFeXUVOZ.js} +1 -1
  62. package/web/dist/assets/{repo-git-Cf4t5PD0.js → repo-git-DcWh5s_b.js} +1 -1
  63. package/web/dist/assets/{run-diff-BpkUW37Z.js → run-diff-1ZzFrOx1.js} +1 -1
  64. package/web/dist/assets/{run-header-68WHf9wV.js → run-header-CPoYzbAy.js} +1 -1
  65. package/web/dist/assets/{skills-DiVIgKeU.js → skills-CnLazLgF.js} +1 -1
  66. package/web/dist/assets/{status-dot-CqTJHSP9.js → status-dot-DwFmt_Jg.js} +1 -1
  67. package/web/dist/assets/{tab-link-B07Hu4QX.js → tab-link-COpvArgR.js} +1 -1
  68. package/web/dist/assets/{task-changes--lQF6EYf.js → task-changes-CY-mi-mC.js} +1 -1
  69. package/web/dist/assets/{task-commits-Cx0qy4DN.js → task-commits-EvLi8T4a.js} +1 -1
  70. package/web/dist/assets/{task-files-D50s-gKW.js → task-files-BR3d8X2U.js} +1 -1
  71. package/web/dist/assets/{task-github-item-DaqmSwYT.js → task-github-item-BVsPg9RI.js} +1 -1
  72. package/web/dist/assets/{task-thread-BPQ64lui.js → task-thread-DQCsu_v8.js} +1 -1
  73. package/web/dist/assets/{textarea-D20O4Pw0.js → textarea-BbNV5Uo2.js} +1 -1
  74. package/web/dist/assets/{thread-state-DWcFMrvb.js → thread-state-DTkIvHsU.js} +1 -1
  75. package/web/dist/assets/{use-hand-to-agent-state-Cy9eyyfL.js → use-hand-to-agent-state-DT-QBsPC.js} +1 -1
  76. package/web/dist/assets/{utils-JsL3ZsJf.js → utils-POVs3UKu.js} +1 -1
  77. package/web/dist/assets/{workflows-jwG7uxqi.js → workflows-BTE-5RwX.js} +1 -1
  78. package/web/dist/assets/{zoomable-image-BJJ64KGc.js → zoomable-image-cL1VgYK4.js} +1 -1
  79. package/web/dist/index.html +13 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wjarka/cezarion",
3
- "version": "0.16.0-dev.1503",
3
+ "version": "0.16.0-pr923.1504",
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",
@@ -53,8 +53,8 @@
53
53
  "zod": "^4.4.3"
54
54
  },
55
55
  "devDependencies": {
56
- "@open-mercato/cezar-api-client": "0.16.0-dev.1503",
57
- "@open-mercato/cezar-contract": "0.16.0-dev.1503",
56
+ "@open-mercato/cezar-api-client": "0.16.0-pr923.1504",
57
+ "@open-mercato/cezar-contract": "0.16.0-pr923.1504",
58
58
  "@types/node": "^24.19.1",
59
59
  "@types/ws": "^8.18.1",
60
60
  "esbuild": "^0.28.1",
@@ -1,6 +1,6 @@
1
1
  // Offline harness fixtures share the real bundled MCP protocol, never a fake receipt.
2
- import { writeFileSync, existsSync } from 'node:fs';
3
- import { basename } from 'node:path';
2
+ import { writeFileSync, readFileSync, existsSync } from 'node:fs';
3
+ import { basename, join } from 'node:path';
4
4
  import { fileURLToPath, pathToFileURL } from 'node:url';
5
5
  import { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
6
  import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
@@ -34,8 +34,16 @@ export async function probeCezarTool(backend, wire, name, args) {
34
34
  server = Object.entries(wire.config ?? {}).find(([key]) => key.startsWith('mcp_servers.cezar_ci_'))?.[1];
35
35
  env = Object.fromEntries((server?.env_vars ?? []).map(name => [name, process.env[name]]));
36
36
  } else if (backend === 'cursor') {
37
- server = wire.mcpServers.find(server => server.name.startsWith('cezar_ci_'));
38
- env = Object.fromEntries(server?.env.map(({name,value}) => [name,value]) ?? []);
37
+ if (Array.isArray(wire)) {
38
+ const dir = wire[wire.indexOf('--plugin-dir') + 1];
39
+ const servers = JSON.parse(readFileSync(join(dir, 'mcp.json'), 'utf8')).mcpServers;
40
+ server = Object.entries(servers).find(([name]) => name.startsWith('cezar_ci_'))?.[1];
41
+ env = Object.fromEntries(Object.entries(server?.env ?? {}).map(([key, value]) =>
42
+ [key, value.replace(/\$\{([^}]+)\}/g, (_, name) => process.env[name] ?? '')]));
43
+ } else {
44
+ server = wire.mcpServers.find(server => server.name.startsWith('cezar_ci_'));
45
+ env = Object.fromEntries(server?.env.map(({name,value}) => [name,value]) ?? []);
46
+ }
39
47
  } else {
40
48
  const local = Object.entries(wire.mcp ?? {}).find(([key]) => key.startsWith('cezar_ci_'))?.[1];
41
49
  server = local && { command: local.command[0], args: local.command.slice(1) };
@@ -28,6 +28,7 @@ function watchdogStall(prompt) {
28
28
  import { createInterface } from 'node:readline';
29
29
  import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
30
30
  import * as parityGateFs from 'node:fs';
31
+ if (process.argv.includes('--version')) { process.stdout.write('2026.09.15-mock-acp\n'); process.exit(0); }
31
32
 
32
33
  // #401: let the test observe the actual monitoring park before releasing late wire frames.
33
34
  async function afterParityPark(prompt) {
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env node
2
+ import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
+ import { spawn } from 'node:child_process';
4
+ import { join } from 'node:path';
5
+ import { randomUUID } from 'node:crypto';
6
+
7
+ const args = process.argv.slice(2);
8
+ if (args.includes('--version')) { process.stdout.write('2026.10.01-e373342\n'); process.exit(0); }
9
+ if (args.includes('__cezar_probe_invalid__')) {
10
+ process.stderr.write('Invalid --allowed-tools value(s): __cezar_probe_invalid__. Expected one of: '+
11
+ 'shell_tool_call, delete_tool_call, glob_tool_call, grep_tool_call, read_tool_call, update_todos_tool_call, read_todos_tool_call, edit_tool_call, ls_tool_call, read_lints_tool_call, mcp_tool_call, sem_search_tool_call, create_plan_tool_call, web_search_tool_call, list_mcp_resources_tool_call, read_mcp_resource_tool_call, apply_agent_diff_tool_call, ask_question_tool_call, fetch_tool_call, switch_mode_tool_call, generate_image_tool_call, record_screen_tool_call, computer_use_tool_call, write_shell_stdin_tool_call, reflect_tool_call, setup_vm_environment_tool_call, truncated_tool_call, web_fetch_tool_call, report_bugfix_results_tool_call, ai_attribution_tool_call, pr_management_tool_call, mcp_auth_tool_call, await_tool_call, blame_by_file_path_tool_call, get_mcp_tools_tool_call, report_bug_tool_call, set_active_branch_tool_call, communicate_update_tool_call, send_final_summary_tool_call, update_pr_code_tour_tool_call, replace_env_tool_call, edit_pr_labels_tool_call, record_ci_investigation_findings_tool_call, fetch_cloud_agent_data_tool_call, send_to_user_tool_call, pi_read_tool_call, pi_bash_tool_call, pi_edit_tool_call, pi_write_tool_call, pi_grep_tool_call, pi_find_tool_call, pi_ls_tool_call, connect_scm_tool_call, search_conversations_tool_call, create_goal_tool_call, update_goal_tool_call, get_pr_code_tour_tool_call, write_canvas_tool_call, read_canvas_tool_call, task_tool_call, create_agent_tool_call, adopt_tool_call, send_to_agent_tool_call, send_message_tool_call, get_agent_status_tool_call, stop_agent_tool_call, read_agent_transcript_tool_call, start_grind_execution_tool_call, start_grind_planning_tool_call\n');
12
+ process.exit(1);
13
+ }
14
+ const flag = name => { const at = args.indexOf(name); return at < 0 ? undefined : args[at + 1]; };
15
+ const resumeId = flag('--resume');
16
+ const mode = process.env.CEZ_MOCK_CURSOR_PRINT_MODE ?? 'normal';
17
+ const turn = resumeId ? 2 : 1;
18
+ // Cursor accepts a print prompt on stdin. Preserve the positional form so a
19
+ // regression back to argv delivery is visible to the runner tests.
20
+ const valueFlags = new Set(['--output-format', '--model', '--resume', '--allowed-tools',
21
+ '--add-dir', '--plugin-dir', '--image']);
22
+ let positionalPrompt = '';
23
+ for (let i = 0; i < args.length; i++) {
24
+ if (valueFlags.has(args[i])) { i++; continue; }
25
+ if (!args[i].startsWith('-')) positionalPrompt = args[i];
26
+ }
27
+ const stdinChunks = [];
28
+ if (!positionalPrompt) for await (const chunk of process.stdin) stdinChunks.push(chunk);
29
+ const stdinPrompt = Buffer.concat(stdinChunks).toString('utf8');
30
+ const promptArg = stdinPrompt || positionalPrompt;
31
+ const promptBoundary = promptArg.lastIndexOf('\n\n---\n\n');
32
+ const prompt = promptBoundary < 0 ? promptArg : promptArg.slice(promptBoundary + '\n\n---\n\n'.length);
33
+ const taggedScenarios = [
34
+ ['resume-done', 'a0000001'], ['autonomous-ask-cap', 'a0000002'],
35
+ ['autonomous-cap', 'a0000003'], ['silent-tail-late-turn-start', 'a0000004'],
36
+ ['silent-tail-no-reply', 'a0000005'], ['silent-tail-late-reply', 'a0000006'],
37
+ ['silent-tail-slow-done', 'a0000007'], ['silent-tail-again', 'a0000008'],
38
+ ['silent-tail', 'a0000009'], ['tool-tail', 'a000000a'],
39
+ ];
40
+ const freshTag = taggedScenarios.find(([name]) => prompt.includes(`mock:${name}`))?.[1];
41
+ const id = resumeId && mode !== 'mismatch-on-resume' ? resumeId
42
+ : (freshTag ? freshTag + randomUUID().slice(8) : randomUUID());
43
+ const stateFile = join(process.cwd(), '.ai', 'cezar', 'mock-cursor-print', `${id}.json`);
44
+ let origin = taggedScenarios.find(([, tag]) => resumeId?.startsWith(tag))?.[0];
45
+ if (resumeId && existsSync(stateFile)) {
46
+ try { origin = JSON.parse(readFileSync(stateFile, 'utf8')).origin ?? origin; } catch { /* isolated mock state */ }
47
+ }
48
+ if (freshTag) {
49
+ origin = taggedScenarios.find(([, tag]) => tag === freshTag)?.[0];
50
+ mkdirSync(join(process.cwd(), '.ai', 'cezar', 'mock-cursor-print'), { recursive: true });
51
+ writeFileSync(stateFile, JSON.stringify({ origin }));
52
+ }
53
+ const frame = value => process.stdout.write(`${JSON.stringify(value)}\n`);
54
+ const assistant = text => frame({ type: 'assistant', session_id: id,
55
+ message: { role: 'assistant', content: [{ type: 'text', text }] } });
56
+ const result = (text, error = false) => frame({ type: 'result', subtype: error ? 'error_during_execution' : 'success',
57
+ session_id: id, is_error: error, result: text });
58
+ const readTool = (content = 'A project') => {
59
+ const call = { readToolCall: { args: { path: 'README.md' } } };
60
+ frame({ type: 'tool_call', subtype: 'started', call_id: 'read-1', session_id: id, tool_call: call });
61
+ frame({ type: 'tool_call', subtype: 'completed', call_id: 'read-1', session_id: id,
62
+ tool_call: { readToolCall: { ...call.readToolCall, result: { success: { content } } } } });
63
+ };
64
+ const waitForRelease = async (fallbackMs) => {
65
+ const file = process.env.CEZ_MOCK_RELEASE_FILE;
66
+ if (!file) { await new Promise(resolve => setTimeout(resolve, fallbackMs)); return; }
67
+ const deadline = Date.now() + 15_000;
68
+ while (!existsSync(file)) {
69
+ if (Date.now() > deadline) throw new Error('CEZ_MOCK_RELEASE_FILE was not created');
70
+ await new Promise(resolve => setTimeout(resolve, 10));
71
+ }
72
+ };
73
+ const finish = (text) => { assistant(text); result(text); };
74
+ const log = process.env.CEZ_MOCK_CURSOR_PRINT_LOG;
75
+ if (log) appendFileSync(log, `${JSON.stringify({ pid: process.pid, resumeId, id, args,
76
+ promptSource: stdinPrompt ? 'stdin' : 'argv', prompt: promptArg })}\n`);
77
+ if (process.env.CEZ_MOCK_ARGS_FILE) {
78
+ appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, `${JSON.stringify(args)}\n`);
79
+ const pluginDir = flag('--plugin-dir');
80
+ if (pluginDir) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE,
81
+ `${JSON.stringify({ type: 'cursor-print-plugin', ...JSON.parse(readFileSync(`${pluginDir}/mcp.json`, 'utf8')) })}\n`);
82
+ }
83
+ if (process.env.CEZ_MOCK_STDIN_FILE && stdinPrompt) {
84
+ appendFileSync(process.env.CEZ_MOCK_STDIN_FILE,
85
+ `${JSON.stringify({ method: 'cursor-print/stdin', prompt: stdinPrompt })}\n`);
86
+ }
87
+ if (mode === 'delay-init') await new Promise(resolve => setTimeout(resolve, 10_000));
88
+ frame({ type: 'system', subtype: 'init', session_id: id, model: flag('--model') ?? 'auto', cwd: process.cwd() });
89
+ if (mode === 'no-result') process.exit(0);
90
+ if (mode === 'portable-ask' && !resumeId) {
91
+ const ask = 'CEZ:ASK {"questions":[{"header":"Library","question":"Which library?","multiSelect":false,"options":[{"label":"Vitest","description":"Use Vitest"},{"label":"Jest","description":"Use Jest"}]}]}';
92
+ frame({ type: 'assistant', session_id: id, message: { role: 'assistant', content: [{ type: 'text', text: `Choose one.\n${ask}` }] } });
93
+ frame({ type: 'result', subtype: 'success', session_id: id, is_error: false, result: `Choose one.\n${ask}` });
94
+ process.exit(0);
95
+ }
96
+ if (mode === 'native-question') {
97
+ const args = { title: 'Choice', questions: [{ prompt: 'Choose', options: [{ label: 'Alpha' }, { label: 'Beta' }], allowMultiple: false }], runAsync: false };
98
+ const call = { askQuestionToolCall: { args } };
99
+ frame({ type: 'tool_call', subtype: 'started', call_id: 'question-1', session_id: id, tool_call: call });
100
+ frame({ type: 'tool_call', subtype: 'completed', call_id: 'question-1', session_id: id,
101
+ tool_call: { askQuestionToolCall: { args, result: { rejected: { reason: 'Questions skipped by the user, continue with the information you already have' } } } } });
102
+ finish('I could not ask the question.'); process.exit(0);
103
+ }
104
+ if (mode === 'provider-error') {
105
+ result('RetriableError: provider unavailable', true);
106
+ process.exit(0);
107
+ }
108
+ if (prompt.includes('mock:crash-stderr')) {
109
+ const { crashWithStderr } = await import('./mock-runner-crash.mjs');
110
+ crashWithStderr(prompt, '{"type":"assistant",');
111
+ }
112
+ if (prompt.includes('mock:autonomous') || prompt.startsWith('Continue working autonomously until the task is fully complete.')) {
113
+ const { autonomousReply } = await import('./mock-autonomous.mjs');
114
+ if (origin === 'autonomous-ask-cap' && resumeId && prompt.startsWith('Continue working autonomously until the task is fully complete.')) {
115
+ finish('Still working.'); process.exit(0);
116
+ }
117
+ const tagged = origin?.startsWith('autonomous') ? `${prompt} mock:${origin}` : prompt;
118
+ finish(autonomousReply(tagged)); process.exit(0);
119
+ }
120
+ if (prompt.includes('mock:silent-tail') || prompt.includes('mock:tool-tail') ||
121
+ prompt.includes('Your last turn ended without a message to the user.')) {
122
+ const silent = await import('./mock-silent-tail.mjs');
123
+ if (prompt.includes('Your last turn ended without a message to the user.')) {
124
+ if (origin === 'silent-tail-no-reply') { setInterval(() => {}, 1000); await new Promise(() => {}); }
125
+ if (origin === 'silent-tail-late-reply') await new Promise(resolve => setTimeout(resolve, silent.LATE_REPLY_MS));
126
+ if (origin === 'silent-tail-slow-done') {
127
+ assistant(silent.SLOW_DONE_PREFIX);
128
+ await new Promise(resolve => setTimeout(resolve, silent.SLOW_DONE_TAIL_MS));
129
+ }
130
+ if (origin === 'silent-tail-again') { result(''); process.exit(0); }
131
+ finish(origin === 'tool-tail' ? silent.FINAL_MESSAGE_STANDING : silent.SILENT_TAIL_DONE);
132
+ process.exit(0);
133
+ }
134
+ assistant(prompt.includes('mock:tool-tail') ? silent.TOOL_TAIL_OPENING : silent.SILENT_TAIL_OPENING);
135
+ readTool(origin === 'silent-tail-again' ? silent.SILENT_TAIL_REASONING : 'A project'); result(''); process.exit(0);
136
+ }
137
+ if (origin === 'resume-done' && resumeId) { finish('Resumed work finished.\nCEZ:DONE'); process.exit(0); }
138
+ if (prompt.includes('mock:ci-wait') || process.env.CEZ_MOCK_CI_PR) {
139
+ const { ciPrompt, probeCiTool } = await import('./mock-ci-tool.mjs');
140
+ const response = prompt.includes('mock:ci-wait') ? await ciPrompt('cursor', args, prompt)
141
+ : JSON.stringify((await probeCiTool('cursor', args)).response);
142
+ finish(response); process.exit(0);
143
+ }
144
+ if (prompt.includes('mock:no-progress')) {
145
+ writeFileSync('watchdog.pid', String(process.pid));
146
+ if (prompt.includes('ignore-term')) process.on('SIGTERM', () => {});
147
+ if (prompt.includes('held-pipe')) spawn(process.execPath, ['-e', 'setTimeout(() => {}, 5000)'],
148
+ { stdio: ['ignore', process.stdout, process.stderr] });
149
+ if (prompt.includes('leftover')) {
150
+ const leftover = (file) => `process.on('SIGTERM',()=>{}); require('fs').writeFileSync(${JSON.stringify(file)}, String(process.pid)); setTimeout(()=>{},20000)`;
151
+ spawn(process.execPath, ['-e', leftover('leftover-group.pid')], { stdio: 'ignore' });
152
+ spawn(process.execPath, ['-e', leftover('leftover-session.pid')], { stdio: 'ignore', detached: true }).unref();
153
+ }
154
+ setInterval(() => {}, 1000);
155
+ await new Promise(() => {});
156
+ }
157
+ if (prompt.includes('mock:hold-done')) { await waitForRelease(400); finish('parity hold-done: content after the pause\nCEZ:DONE'); process.exit(0); }
158
+ if (prompt.includes('mock:hold-ask')) { await waitForRelease(400); finish('Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}'); process.exit(0); }
159
+ if (prompt.includes('mock:hold-gated')) { await waitForRelease(500); finish('parity hold-gated: content after the pause'); process.exit(0); }
160
+ if (prompt.includes('mock:turn-messages:')) {
161
+ const { turnMessages } = await import('./mock-turn-messages.mjs');
162
+ for (const message of turnMessages(prompt)) assistant(message + '\n');
163
+ result(''); process.exit(0);
164
+ }
165
+ if (prompt.includes('mock:ask-snapshot')) {
166
+ finish(prompt.includes('mock:ask-snapshot-bad') ? 'CEZ:ASK {not valid json'
167
+ : 'Using the CEZ:ASK structured question format instead:\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}');
168
+ process.exit(0);
169
+ }
170
+ if (prompt.includes('mock:ask-prose')) { finish('Use `CEZ:ASK {"questions":[]}` in your reply.\n> CEZ:ASK {not valid json'); process.exit(0); }
171
+ if (prompt.includes('mock:ask-bad')) { finish('Malformed question skipped.'); process.exit(0); }
172
+ if (prompt.includes('mock:provider-error')) { result('RetriableError: provider unavailable', true); process.exit(0); }
173
+ if (prompt.includes('mock:split-text')) {
174
+ const text = 'parity split text\nCEZ:MONITORING'; assistant(text); result(text); process.exit(0);
175
+ }
176
+ if (prompt.includes('mock:done') || (resumeId && /Library:|Tests:|Plan:/.test(prompt))) {
177
+ const text = 'Done.\nCEZ:DONE'; assistant(text); result(text); process.exit(0);
178
+ }
179
+ if (prompt.includes('mock:ask') && (!resumeId || prompt.includes('mock:ask-reply-late'))) {
180
+ const text = 'Pick one.\n\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}';
181
+ assistant(text); result(text); process.exit(0);
182
+ }
183
+ if (prompt.includes('mock:hold')) await new Promise(resolve => setTimeout(resolve, 500));
184
+ if (prompt === 'inspect the working tree' || prompt.includes('mock:baseline')) {
185
+ readTool(); const text = 'Cursor inspected the workspace.'; assistant(text); result(text); process.exit(0);
186
+ }
187
+ if (prompt.includes('mock:agent-echo')) { finish(prompt); process.exit(0); }
188
+ const text = `turn ${turn}: ${prompt}`;
189
+ if (mode === 'delay-work' && resumeId) await new Promise(resolve => setTimeout(resolve, 200));
190
+ assistant(text);
191
+ if (mode === 'crash-after-work') process.exit(1);
192
+ result(text);
193
+ if (mode === 'duplicate-result') frame({ type: 'result', subtype: 'success', session_id: id, is_error: false, result: text });
194
+ if (mode === 'late-frame') frame({ type: 'assistant', session_id: id,
195
+ message: { role: 'assistant', content: [{ type: 'text', text: 'LATE_FRAME' }] } });
196
+ if (mode === 'result-before-exit') await new Promise(resolve => setTimeout(resolve, 250));