@wjarka/cezarion 0.14.8 → 0.14.9-dev.1004
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.
- package/README.md +129 -2
- package/dist/application-update/bin-recovery.d.ts +5 -0
- package/dist/application-update/bin-recovery.js +96 -0
- package/dist/application-update/bin-recovery.js.map +1 -0
- package/dist/application-update/discovery.d.ts +22 -0
- package/dist/application-update/discovery.js +91 -0
- package/dist/application-update/discovery.js.map +1 -0
- package/dist/application-update/helper.d.ts +57 -0
- package/dist/application-update/helper.js +374 -0
- package/dist/application-update/helper.js.map +1 -0
- package/dist/application-update/launcher.d.ts +8 -0
- package/dist/application-update/launcher.js +53 -0
- package/dist/application-update/launcher.js.map +1 -0
- package/dist/application-update/lock.d.ts +4 -0
- package/dist/application-update/lock.js +70 -0
- package/dist/application-update/lock.js.map +1 -0
- package/dist/application-update/npm-process.d.ts +17 -0
- package/dist/application-update/npm-process.js +133 -0
- package/dist/application-update/npm-process.js.map +1 -0
- package/dist/application-update/service.d.ts +69 -0
- package/dist/application-update/service.js +456 -0
- package/dist/application-update/service.js.map +1 -0
- package/dist/artifacts/cli.d.ts +1 -0
- package/dist/artifacts/cli.js +32 -0
- package/dist/artifacts/cli.js.map +1 -0
- package/dist/artifacts/lifecycle.d.ts +4 -0
- package/dist/artifacts/lifecycle.js +42 -0
- package/dist/artifacts/lifecycle.js.map +1 -0
- package/dist/artifacts/resolve.d.ts +18 -0
- package/dist/artifacts/resolve.js +102 -0
- package/dist/artifacts/resolve.js.map +1 -0
- package/dist/artifacts/store.d.ts +8 -0
- package/dist/artifacts/store.js +323 -0
- package/dist/artifacts/store.js.map +1 -0
- package/dist/automations/scheduler.d.ts +6 -0
- package/dist/automations/scheduler.js +45 -17
- package/dist/automations/scheduler.js.map +1 -1
- package/dist/automations/store.d.ts +12 -4
- package/dist/automations/store.js +199 -22
- package/dist/automations/store.js.map +1 -1
- package/dist/automations/types.d.ts +2 -0
- package/dist/automations/types.js +1 -0
- package/dist/automations/types.js.map +1 -1
- package/dist/ci-wait/mcp.d.ts +3 -0
- package/dist/ci-wait/mcp.js +14 -3
- package/dist/ci-wait/mcp.js.map +1 -1
- package/dist/contract/application-update.d.ts +32 -0
- package/dist/contract/artifacts.d.ts +83 -0
- package/dist/contract/ask-schema.d.ts +32 -0
- package/dist/contract/ask.d.ts +2 -32
- package/dist/contract/automations.d.ts +6 -0
- package/dist/contract/conversations.d.ts +165 -0
- package/dist/contract/delegation.d.ts +68 -0
- package/dist/contract/discovery.d.ts +134 -0
- package/dist/contract/events.d.ts +49 -0
- package/dist/contract/health.d.ts +12 -0
- package/dist/contract/index.d.ts +3 -0
- package/dist/contract/index.js +1654 -1454
- package/dist/contract/projects.d.ts +40 -0
- package/dist/contract/runs.d.ts +186 -0
- package/dist/contract/skills.d.ts +33 -0
- package/dist/core/agent-runner.d.ts +33 -4
- package/dist/core/agent-runner.js +5 -0
- package/dist/core/agent-runner.js.map +1 -1
- package/dist/core/claude-cli-runner.d.ts +6 -12
- package/dist/core/claude-cli-runner.js +90 -20
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/core/codex-app-server-runner.d.ts +2 -1
- package/dist/core/codex-app-server-runner.js +327 -92
- package/dist/core/codex-app-server-runner.js.map +1 -1
- package/dist/core/codex-app-server-transport.d.ts +13 -1
- package/dist/core/codex-app-server-transport.js +58 -17
- package/dist/core/codex-app-server-transport.js.map +1 -1
- package/dist/core/cursor-acp-runner.d.ts +6 -1
- package/dist/core/cursor-acp-runner.js +162 -26
- package/dist/core/cursor-acp-runner.js.map +1 -1
- package/dist/core/cursor-provider-error.d.ts +3 -0
- package/dist/core/cursor-provider-error.js +16 -1
- package/dist/core/cursor-provider-error.js.map +1 -1
- package/dist/core/input-submissions.d.ts +14 -0
- package/dist/core/input-submissions.js +26 -0
- package/dist/core/input-submissions.js.map +1 -0
- package/dist/core/opencode-server-runner.d.ts +4 -1
- package/dist/core/opencode-server-runner.js +156 -26
- package/dist/core/opencode-server-runner.js.map +1 -1
- package/dist/core/pi-runner.d.ts +2 -1
- package/dist/core/pi-runner.js +88 -10
- package/dist/core/pi-runner.js.map +1 -1
- package/dist/core/provider-auth.d.ts +99 -1
- package/dist/core/provider-auth.js +203 -2
- package/dist/core/provider-auth.js.map +1 -1
- package/dist/delegation/cli.d.ts +1 -0
- package/dist/delegation/cli.js +103 -7
- package/dist/delegation/cli.js.map +1 -1
- package/dist/delegation/conversations.js +13 -5
- package/dist/delegation/conversations.js.map +1 -1
- package/dist/delegation/input.d.ts +2 -1
- package/dist/delegation/input.js +6 -1
- package/dist/delegation/input.js.map +1 -1
- package/dist/delegation/policy.d.ts +3 -2
- package/dist/delegation/policy.js +3 -1
- package/dist/delegation/policy.js.map +1 -1
- package/dist/delegation/process-liveness.d.ts +66 -0
- package/dist/delegation/process-liveness.js +215 -0
- package/dist/delegation/process-liveness.js.map +1 -0
- package/dist/delegation/provision.d.ts +1 -1
- package/dist/delegation/provision.js +10 -5
- package/dist/delegation/provision.js.map +1 -1
- package/dist/delegation/questions.d.ts +19 -0
- package/dist/delegation/questions.js +45 -0
- package/dist/delegation/questions.js.map +1 -0
- package/dist/delegation/results.d.ts +1 -1
- package/dist/delegation/results.js +33 -19
- package/dist/delegation/results.js.map +1 -1
- package/dist/delegation/routes.d.ts +185 -11
- package/dist/delegation/routes.js +6 -2
- package/dist/delegation/routes.js.map +1 -1
- package/dist/delegation/service.d.ts +16 -5
- package/dist/delegation/service.js +161 -13
- package/dist/delegation/service.js.map +1 -1
- package/dist/delegation/transport.d.ts +228 -65
- package/dist/discovery/catalog.d.ts +14 -0
- package/dist/discovery/catalog.js +31 -0
- package/dist/discovery/catalog.js.map +1 -0
- package/dist/discovery/cli.d.ts +8 -0
- package/dist/discovery/cli.js +104 -0
- package/dist/discovery/cli.js.map +1 -0
- package/dist/git-worktree.d.ts +3 -1
- package/dist/git-worktree.js +26 -4
- package/dist/git-worktree.js.map +1 -1
- package/dist/index.js +92 -26
- package/dist/index.js.map +1 -1
- package/dist/open-url.d.ts +2 -0
- package/dist/open-url.js +20 -0
- package/dist/open-url.js.map +1 -0
- package/dist/release/snapshot.d.ts +3 -3
- package/dist/release/snapshot.js +6 -6
- package/dist/release/snapshot.js.map +1 -1
- package/dist/runs/agent-tmpdir.d.ts +2 -0
- package/dist/runs/agent-tmpdir.js +5 -2
- package/dist/runs/agent-tmpdir.js.map +1 -1
- package/dist/runs/client-request.d.ts +11 -0
- package/dist/runs/client-request.js +26 -0
- package/dist/runs/client-request.js.map +1 -0
- package/dist/runs/delegation-state.d.ts +17 -0
- package/dist/runs/event-history.js +6 -0
- package/dist/runs/event-history.js.map +1 -1
- package/dist/runs/retention.d.ts +7 -2
- package/dist/runs/retention.js +39 -7
- package/dist/runs/retention.js.map +1 -1
- package/dist/runs/store.d.ts +100 -6
- package/dist/runs/store.js +245 -2
- package/dist/runs/store.js.map +1 -1
- package/dist/runs/webhook.d.ts +125 -0
- package/dist/runs/webhook.js +256 -0
- package/dist/runs/webhook.js.map +1 -0
- package/dist/server/provider-auth-runtime.d.ts +30 -4
- package/dist/server/provider-auth-runtime.js +71 -6
- package/dist/server/provider-auth-runtime.js.map +1 -1
- package/dist/server/server.d.ts +8226 -4860
- package/dist/server/server.js +309 -15
- package/dist/server/server.js.map +1 -1
- package/dist/server-install/platforms/ubuntu-vps.d.ts +5 -1
- package/dist/server-install/platforms/ubuntu-vps.js +160 -18
- package/dist/server-install/platforms/ubuntu-vps.js.map +1 -1
- package/dist/server-install/steps.d.ts +4 -2
- package/dist/server-install/steps.js +5 -4
- package/dist/server-install/steps.js.map +1 -1
- package/dist/skills-remote.d.ts +30 -5
- package/dist/skills-remote.js +122 -32
- package/dist/skills-remote.js.map +1 -1
- package/dist/skills.js +15 -6
- package/dist/skills.js.map +1 -1
- package/dist/task-cli/cli.d.ts +38 -0
- package/dist/task-cli/cli.js +580 -0
- package/dist/task-cli/cli.js.map +1 -0
- package/dist/task-cli/discovery.d.ts +22 -0
- package/dist/task-cli/discovery.js +96 -0
- package/dist/task-cli/discovery.js.map +1 -0
- package/dist/task-cli/http.d.ts +46 -0
- package/dist/task-cli/http.js +101 -0
- package/dist/task-cli/http.js.map +1 -0
- package/dist/task-cli/projections.d.ts +40 -0
- package/dist/task-cli/projections.js +45 -0
- package/dist/task-cli/projections.js.map +1 -0
- package/dist/task-cli/watch.d.ts +78 -0
- package/dist/task-cli/watch.js +336 -0
- package/dist/task-cli/watch.js.map +1 -0
- package/dist/workflows/run.d.ts +160 -2
- package/dist/workflows/run.js +1141 -108
- package/dist/workflows/run.js.map +1 -1
- package/dist/workspace/agent-profiles.d.ts +22 -0
- package/dist/workspace/agent-profiles.js +15 -0
- package/dist/workspace/agent-profiles.js.map +1 -1
- package/dist/workspace/config.d.ts +9 -0
- package/dist/workspace/config.js +13 -1
- package/dist/workspace/config.js.map +1 -1
- package/dist/workspace/projects-cli.js +1 -1
- package/dist/workspace/projects.d.ts +24 -1
- package/dist/workspace/projects.js +14 -1
- package/dist/workspace/projects.js.map +1 -1
- package/package.json +5 -5
- package/scripts/mock-claude.mjs +64 -2
- package/scripts/mock-codex-app-server.mjs +124 -1
- package/scripts/mock-cursor-acp.mjs +54 -3
- package/scripts/mock-cursor-hang-stdin-late.mjs +35 -0
- package/scripts/mock-cursor-hang-stdin.mjs +30 -0
- package/scripts/mock-opencode-serve.mjs +102 -2
- package/scripts/mock-pi-rpc.mjs +82 -3
- package/web/dist/assets/agent-accounts-CerclGiO.js +1 -0
- package/web/dist/assets/centered-state-DH8qtCz-.js +43 -0
- package/web/dist/assets/collapsible-De2G0xqQ.js +1 -0
- package/web/dist/assets/commit-list-CQubP1iS.js +1 -0
- package/web/dist/assets/compare-variants-C_NijJvg.js +1 -0
- package/web/dist/assets/{diff-tRE59UNb2.js → diff-8M5HlDxq2.js} +2 -2
- package/web/dist/assets/diff-controls-B6ok0TtQ.js +1 -0
- package/web/dist/assets/{diff-stat-Cgnl_hYF.js → diff-stat-D1AODN2a.js} +1 -1
- package/web/dist/assets/{diff-view-DN1ort7Q.js → diff-view-Bs5Gh7UF.js} +2 -2
- package/web/dist/assets/dropdown-menu-7KGQMoFj.js +1 -0
- package/web/dist/assets/git-toolbar-DqbapMXr.js +1 -0
- package/web/dist/assets/github-lMHiuYSx.js +1 -0
- package/web/dist/assets/{image-preview-C2JGcBlC.js → image-preview-jzHoEyLM.js} +1 -1
- package/web/dist/assets/index-D4OJTArJ.js +7 -0
- package/web/dist/assets/index-D5NkwqY7.css +1 -0
- package/web/dist/assets/markdown-DvbymqXz.js +2 -0
- package/web/dist/assets/pill-BL6ugwXo.js +1 -0
- package/web/dist/assets/project-router-ChC5Rb-Y.js +1 -0
- package/web/dist/assets/prompt-templates-j8qlK7c-.js +15 -0
- package/web/dist/assets/repo-git-BbPAgkqb.js +1 -0
- package/web/dist/assets/{run-diff-CyG0Nciv.js → run-diff-BuATgZpx.js} +2 -2
- package/web/dist/assets/run-header-DuAWswYM.js +1 -0
- package/web/dist/assets/skills-DiC2XrU1.js +1 -0
- package/web/dist/assets/{tab-link-BWNp3mLY.js → tab-link-BmGKj9Rt.js} +1 -1
- package/web/dist/assets/task-changes-CP9kPnsy.js +1 -0
- package/web/dist/assets/task-commits-D6NfTw5r.js +1 -0
- package/web/dist/assets/task-files-W-b_gtcb.js +2 -0
- package/web/dist/assets/task-thread-Bw_nQuTx.css +1 -0
- package/web/dist/assets/task-thread-Q7Nj33DH.js +9 -0
- package/web/dist/assets/textarea-ByZ-txS7.js +1 -0
- package/web/dist/assets/thread-loading-DHT9p5o5.js +1 -0
- package/web/dist/assets/token-metrics-k4XoovDS.js +1 -0
- package/web/dist/assets/utils-BTFBhZ55.js +64 -0
- package/web/dist/assets/workflows-DqQszPHJ.js +11 -0
- package/web/dist/assets/{zoomable-image-Z3JDnm0b.js → zoomable-image-Ba6y6Wyl.js} +1 -1
- package/web/dist/index.html +14 -14
- package/web/dist/assets/agent-accounts-BZm_2WAv.js +0 -1
- package/web/dist/assets/alert-dialog-B422RI-k.js +0 -43
- package/web/dist/assets/collapsible-Cy_Fmkrd.js +0 -1
- package/web/dist/assets/commit-list-Dcnk7TnI.js +0 -1
- package/web/dist/assets/compare-variants-Ciqwvd_f.js +0 -1
- package/web/dist/assets/diff-controls-BGrXGi7O.js +0 -1
- package/web/dist/assets/dropdown-menu-dwDGT8MM.js +0 -1
- package/web/dist/assets/git-toolbar-UiHnGvE0.js +0 -1
- package/web/dist/assets/github-D-MrkbJ7.js +0 -1
- package/web/dist/assets/ibm-plex-mono-regular-BF3vfe7_.ttf +0 -0
- package/web/dist/assets/index-BszfYvBq.js +0 -7
- package/web/dist/assets/index-CWh4pKP3.css +0 -1
- package/web/dist/assets/markdown-C_IcKVQd.js +0 -2
- package/web/dist/assets/pill-BsbO6G7y.js +0 -1
- package/web/dist/assets/project-router-DbLkll25.js +0 -1
- package/web/dist/assets/prompt-templates-BlTGJB5W.js +0 -15
- package/web/dist/assets/repo-git-Bm3s6Why.js +0 -1
- package/web/dist/assets/run-header-IFyIGCey.js +0 -1
- package/web/dist/assets/skills-C-C4DVFY.js +0 -1
- package/web/dist/assets/task-changes-D2PHtWUW.js +0 -1
- package/web/dist/assets/task-commits-N5u9U0_R.js +0 -1
- package/web/dist/assets/task-files-BS7c7CFm.js +0 -2
- package/web/dist/assets/task-thread-BQFdz1dJ.css +0 -1
- package/web/dist/assets/task-thread-DGM2dZV-.js +0 -9
- package/web/dist/assets/textarea-FjaU370i.js +0 -1
- package/web/dist/assets/thread-loading-77sJ8Yu_.js +0 -1
- package/web/dist/assets/token-metrics-Clxh03bE.js +0 -1
- package/web/dist/assets/utils-PYYcpCFZ.js +0 -64
- package/web/dist/assets/workflows-Czl0ZfXH.js +0 -11
|
@@ -1,9 +1,29 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Offline Cursor ACP wire. Shapes: cursor.com/docs/cli/acp; ACP v1 schema.
|
|
3
3
|
import { createInterface } from 'node:readline';
|
|
4
|
-
import { appendFileSync } from 'node:fs';
|
|
4
|
+
import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
5
|
+
import * as parityGateFs from 'node:fs';
|
|
6
|
+
|
|
7
|
+
// #401: let the test observe the actual monitoring park before releasing late wire frames.
|
|
8
|
+
async function afterParityPark(prompt) {
|
|
9
|
+
const gate = /parity-release=([^\s"\\]+)/.exec(prompt)?.[1];
|
|
10
|
+
if (!gate) throw new Error('post-park scenario requires a release path');
|
|
11
|
+
while (!parityGateFs.existsSync(gate)) await new Promise(resolve => setTimeout(resolve, 10));
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
const record = (file, value) => { if (file) appendFileSync(file, `${JSON.stringify(value, (key, value) => key === 'env' && Array.isArray(value) ? value.map(item => item.name?.startsWith('CEZ_TOOL_') ? { ...item, value: '[redacted]' } : item) : value)}\n`); };
|
|
6
15
|
record(process.env.CEZ_MOCK_ARGS_FILE, process.argv.slice(2));
|
|
16
|
+
/** #529: remaining-count file. Each session/new or session/load decrements; at >0 the process writes stderr and exits 1 before the reply. */
|
|
17
|
+
function consumeBootstrapCrash() {
|
|
18
|
+
const file = process.env.CEZ_MOCK_CURSOR_CRASH_ON_LOAD;
|
|
19
|
+
if (!file) return;
|
|
20
|
+
let remaining = 0;
|
|
21
|
+
try { remaining = Number(readFileSync(file, 'utf8')); } catch { return; }
|
|
22
|
+
if (!Number.isFinite(remaining) || remaining <= 0) return;
|
|
23
|
+
writeFileSync(file, String(remaining - 1));
|
|
24
|
+
process.stderr.write(`${process.env.CEZ_MOCK_CURSOR_CRASH_STDERR ?? 'Cursor ACP mock bootstrap crash'}\n`);
|
|
25
|
+
process.exit(1);
|
|
26
|
+
}
|
|
7
27
|
const emit = value => process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', ...value })}\n`);
|
|
8
28
|
const reply = (id, result) => emit({ id, result });
|
|
9
29
|
let sessionId = 'cursor-offline-session';
|
|
@@ -34,17 +54,48 @@ async function prompt(id, content) {
|
|
|
34
54
|
if (input.includes('mock:rpc-error')) { emit({ id, error: { code: -32603, message: 'Provider rejected request' } }); return; }
|
|
35
55
|
// #443 provider-error envelopes: the specific scenarios must be matched BEFORE the
|
|
36
56
|
// bare 'mock:provider-error' prefix, which every one of them contains.
|
|
57
|
+
// #508: observed Cursor 2026.09.18-9a7762b error envelope, followed by end_turn.
|
|
58
|
+
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; }
|
|
59
|
+
// #528: observed Cursor SSL record-layer envelope (run 4bcd5420 seq 2667). Matched
|
|
60
|
+
// before the bare mock:provider-error prefix, same pattern as protocol.
|
|
61
|
+
if (input.includes('mock:provider-error-ssl') && (input.includes('-exhaust') || prompts === 1)) { text('\n\nError: RetriableError: [internal] C0AC9346CC7B0000:error:0A000119:SSL routines:tls_get_more_records:decryption failed or bad record mac:../deps/openssl/openssl/ssl/record/methods/tls_common.c:869:'); complete(id); return; }
|
|
62
|
+
// #531: observed run 2f891027 seq 2005 capacity envelope. Match before the bare prefix.
|
|
63
|
+
// Do not reuse the protocol mock's `includes('-exhaust')` check: that token is a
|
|
64
|
+
// substring of `resource-exhausted` and would never recover.
|
|
65
|
+
if (input.includes('mock:provider-error-resource-exhausted-exhaust')) { text('\n\nError: RetriableError: [resource_exhausted] Error'); complete(id); return; }
|
|
66
|
+
if (input.includes('mock:provider-error-resource-exhausted') && prompts === 1) { text('\n\nError: RetriableError: [resource_exhausted] Error'); complete(id); return; }
|
|
67
|
+
if (input.includes('mock:provider-error-unknown-protocol')) { text('\n\nError: [invalid_argument] protocol error: unknown frame'); complete(id); return; }
|
|
37
68
|
if (input.includes('mock:provider-error-transient') && prompts === 1) { text('\n\nError: 502 bad gateway.'); complete(id); return; }
|
|
38
69
|
if (input.includes('mock:provider-error-bare')) { text('\n\nError: 502 bad gateway.'); complete(id); return; }
|
|
39
70
|
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; }
|
|
40
71
|
if (input.includes('mock:provider-error-instant-far')) { text(`\n\nError: usage limit reached, try again at ${new Date(Date.now() + 6 * 3600000).toISOString()}.`); complete(id); return; }
|
|
41
72
|
if (input.includes('mock:provider-error-verbose')) { text(`\n\nError: usage limit reached. The request ${'x'.repeat(600)} could not be completed, try again at ${new Date(Date.now() + 6 * 3600000).toISOString()}.`); complete(id); return; }
|
|
42
73
|
if (input.includes('mock:provider-error') && !input.includes('mock:provider-error-')) { text('\n\nError: [unauthenticated] Backend rejected authentication.'); complete(id); return; }
|
|
74
|
+
// #401: ACP has chunks only, so completion must flush the same text to v1/v2.
|
|
75
|
+
if (input.includes('mock:ask-snapshot')) {
|
|
76
|
+
text('Choose a test library.\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}'); complete(id); return;
|
|
77
|
+
}
|
|
43
78
|
if (input.includes('mock:ask-bad')) { pendingAsk = { id }; emit({ id: 'bad-question', method: 'cursor/ask_question', params: { questions: [] } }); return; }
|
|
44
79
|
if (input.includes('mock:plan')) { pendingAsk = { id, input }; emit({ id: 'plan-1', method: 'cursor/create_plan', params: { name: 'Test plan', overview: 'Approve the changes?', plan: 'Implement the change and run tests.' } }); return; }
|
|
45
80
|
if (input.includes('mock:multi-ask')) { pendingAsk = { id }; emit({ id: 'multi-question', method: 'cursor/ask_question', params: { title: 'Choices', questions: [question, { ...question, id: 'build', prompt: 'Which build tool?', options: [{ id: 'vite', label: 'Vite' }, { id: 'webpack', label: 'Webpack' }] }] } }); return; }
|
|
46
81
|
if (input.includes('mock:ask')) { pendingAsk = { id, input }; emit({ id: 'question-1', method: 'cursor/ask_question', params: { toolCallId: 'ask-tool', title: 'Tests', questions: [question] } }); return; }
|
|
47
82
|
if (input.includes('mock:permission')) { pendingAsk = { id }; emit({ id: 'permission-1', method: 'session/request_permission', params: { sessionId, toolCall: { toolCallId: 'shell-1', title: 'echo hello', kind: 'execute' }, options: [{ optionId: 'allow-once', name: 'Allow once', kind: 'allow_once' }, { optionId: 'reject-once', name: 'Reject', kind: 'reject_once' }] } }); return; }
|
|
83
|
+
// ACP subagent attribution from __fixtures__/cursor/acp-subagents.ndjson.
|
|
84
|
+
if (input.includes('mock:subagent-after-park')) {
|
|
85
|
+
update({ sessionUpdate: 'subagent_spawned', subagentSessionId: 'park-child', name: 'Reviewer', task: 'Review', capabilities: {}, _meta: { cursor: { toolCallId: 'park-task', agentId: 'park-agent' } } });
|
|
86
|
+
update({ sessionUpdate: 'tool_call', toolCallId: 'late-read', kind: 'other', title: 'Inspect README', status: 'in_progress' }, 'park-child');
|
|
87
|
+
update({ sessionUpdate: 'tool_call_update', toolCallId: 'late-read', status: 'completed' }, 'park-child');
|
|
88
|
+
text('Watching the child.\nCEZ:MONITORING');
|
|
89
|
+
complete(id);
|
|
90
|
+
await afterParityPark(input);
|
|
91
|
+
update({ sessionUpdate: 'tool_call_update', toolCallId: 'late-read', status: 'completed', rawOutput: 'Read the file.' }, 'park-child');
|
|
92
|
+
update({ sessionUpdate: 'agent_message_chunk', content: { type: 'text', text: 'Late child text.' } }, 'park-child');
|
|
93
|
+
// Cursor task metadata can follow the regular completion (acp-subagents.ndjson).
|
|
94
|
+
// Sessionless extensions are attributed by their known child toolCallId even
|
|
95
|
+
// after prompt completion; this is the ordered barrier and a nested item update.
|
|
96
|
+
emit({ method: 'cursor/task', params: { toolCallId: 'late-read', description: 'Post-park child update processed.' } });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
48
99
|
if (input.includes('mock:subagent')) {
|
|
49
100
|
update({ sessionUpdate: 'tool_call', toolCallId: 'task-1', title: 'Review', kind: 'other', rawInput: { description: 'Review' }, status: 'in_progress' });
|
|
50
101
|
update({ sessionUpdate: 'subagent_spawned', subagentSessionId: 'child', name: 'Reviewer', task: 'Review', capabilities: {}, _meta: { cursor: { toolCallId: 'task-1', agentId: 'child-agent' } } });
|
|
@@ -89,8 +140,8 @@ createInterface({ input: process.stdin }).on('line', async line => {
|
|
|
89
140
|
switch (msg.method) {
|
|
90
141
|
case 'initialize': reply(msg.id, { protocolVersion: 1, agentCapabilities: { loadSession: true, promptCapabilities: { image: true } } }); break;
|
|
91
142
|
case 'cursor/list_available_models': reply(msg.id, { models: [{ value: model, name: model, configOptions: configOptions().filter(option => option.id !== 'model') }] }); break;
|
|
92
|
-
case 'session/new': reply(msg.id, { sessionId, ...configuration() }); break;
|
|
93
|
-
case 'session/load': sessionId = msg.params.sessionId; reply(msg.id, configuration()); break;
|
|
143
|
+
case 'session/new': consumeBootstrapCrash(); reply(msg.id, { sessionId, ...configuration() }); break;
|
|
144
|
+
case 'session/load': consumeBootstrapCrash(); sessionId = msg.params.sessionId; reply(msg.id, configuration()); break;
|
|
94
145
|
case 'session/set_model': model = msg.params.modelId; reply(msg.id, {}); break;
|
|
95
146
|
case 'session/set_config_option':
|
|
96
147
|
if (msg.params.configId === 'model') model = msg.params.value;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// #587: the bootstrap write succeeds first, then stdin dies.
|
|
3
|
+
//
|
|
4
|
+
// The parent writes session/new while the pipe's read end is still open, so
|
|
5
|
+
// that write cannot fail. The child then drops the read end and never answers
|
|
6
|
+
// session/new. The runner's next write — the bootstrap stdin probe — is what
|
|
7
|
+
// must surface the death, not the 15s request timeout.
|
|
8
|
+
//
|
|
9
|
+
// The close is three steps, and the order matters. Closing fd 0 while
|
|
10
|
+
// readline's libuv handle still owns it aborts the child on some machines
|
|
11
|
+
// (SIGABRT — the fixture would become a process-death test); on other
|
|
12
|
+
// machines closeSync alone throws EBADF and never closes the read end, so the
|
|
13
|
+
// parent's write silently succeeds and only the 15s request timeout can catch
|
|
14
|
+
// the hang (#588). destroy() disarms the handle, closeSync(0) actually closes
|
|
15
|
+
// the pipe's read end (destroy alone leaves the fd open). The stdin 'error'
|
|
16
|
+
// handler swallows the EBADF from the next read attempt.
|
|
17
|
+
import { createInterface } from 'node:readline';
|
|
18
|
+
import { closeSync } from 'node:fs';
|
|
19
|
+
const emit = value => process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', ...value })}\n`);
|
|
20
|
+
const rl = createInterface({ input: process.stdin });
|
|
21
|
+
process.stdin.on('error', () => { /* the EBADF below is intentional */ });
|
|
22
|
+
rl.on('line', line => {
|
|
23
|
+
let msg; try { msg = JSON.parse(line); } catch { return; }
|
|
24
|
+
if (msg.method === 'initialize') {
|
|
25
|
+
emit({ id: msg.id, result: { protocolVersion: 1, agentCapabilities: { loadSession: true } } });
|
|
26
|
+
} else if (msg.method === 'session/new') {
|
|
27
|
+
// The parent's write already succeeded into the kernel buffer; only now
|
|
28
|
+
// does the read end die, with the child staying up.
|
|
29
|
+
rl.close();
|
|
30
|
+
process.stdin.destroy();
|
|
31
|
+
try { closeSync(0); } catch { /* already closed */ }
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
process.on('SIGTERM', () => { /* ignore — the child must outlive its stdin */ });
|
|
35
|
+
setInterval(() => {}, 1e6);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// #587: answers initialize, then dies on stdin while the child stays up.
|
|
3
|
+
// The parent's next write (session/new) must hit EPIPE and surface as a fast
|
|
4
|
+
// v1 error.
|
|
5
|
+
//
|
|
6
|
+
// Close before replying: a fast parent sends session/new as soon as it reads
|
|
7
|
+
// initialize, and that write must encounter the dead input pipe. The close is
|
|
8
|
+
// three steps, and the order matters. Closing fd 0 while readline's libuv
|
|
9
|
+
// handle still owns it aborts the child on some machines (SIGABRT — the
|
|
10
|
+
// fixture would become a process-death test); on other machines closeSync
|
|
11
|
+
// alone throws EBADF and never closes the read end, so the parent's write
|
|
12
|
+
// silently succeeds and only the 15s request timeout can catch the hang
|
|
13
|
+
// (#588). destroy() disarms the handle, closeSync(0) actually closes the
|
|
14
|
+
// pipe's read end (destroy alone leaves the fd open).
|
|
15
|
+
import { createInterface } from 'node:readline';
|
|
16
|
+
import { closeSync } from 'node:fs';
|
|
17
|
+
const emit = value => process.stdout.write(`${JSON.stringify({ jsonrpc: '2.0', ...value })}\n`);
|
|
18
|
+
const rl = createInterface({ input: process.stdin });
|
|
19
|
+
process.stdin.on('error', () => { /* the EBADF below is intentional */ });
|
|
20
|
+
rl.on('line', line => {
|
|
21
|
+
let msg; try { msg = JSON.parse(line); } catch { return; }
|
|
22
|
+
if (msg.method === 'initialize') {
|
|
23
|
+
rl.close();
|
|
24
|
+
process.stdin.destroy();
|
|
25
|
+
try { closeSync(0); } catch { /* already closed */ }
|
|
26
|
+
emit({ id: msg.id, result: { protocolVersion: 1, agentCapabilities: { loadSession: true } } });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
process.on('SIGTERM', () => { /* ignore — the child must outlive its stdin */ });
|
|
30
|
+
setInterval(() => {}, 1e6);
|
|
@@ -11,6 +11,15 @@ import { createServer } from 'node:http';
|
|
|
11
11
|
|
|
12
12
|
if (process.env.CEZ_MOCK_ARGS_FILE && process.env.OPENCODE_CONFIG_CONTENT) appendFileSync(process.env.CEZ_MOCK_ARGS_FILE, JSON.stringify({ type: 'runtime-config', config: JSON.parse(process.env.OPENCODE_CONFIG_CONTENT) }) + '\n');
|
|
13
13
|
if (process.env.CEZ_MOCK_CI_PR) { const { probeCiTool } = await import('./mock-ci-tool.mjs'); await probeCiTool('opencode', JSON.parse(process.env.OPENCODE_CONFIG_CONTENT ?? '{}')); }
|
|
14
|
+
import * as parityGateFs from 'node:fs';
|
|
15
|
+
|
|
16
|
+
// #401: let the test observe the actual monitoring park before releasing late wire frames.
|
|
17
|
+
async function afterParityPark(prompt) {
|
|
18
|
+
const gate = /parity-release=([^\s"\\]+)/.exec(prompt)?.[1];
|
|
19
|
+
if (!gate) throw new Error('post-park scenario requires a release path');
|
|
20
|
+
while (!parityGateFs.existsSync(gate)) await new Promise(resolve => setTimeout(resolve, 10));
|
|
21
|
+
}
|
|
22
|
+
|
|
14
23
|
const args = process.argv.slice(2);
|
|
15
24
|
const arg = (flag, fallback) => {
|
|
16
25
|
const i = args.indexOf(flag);
|
|
@@ -22,9 +31,30 @@ const SESSION_ID = 'ses_mock_1';
|
|
|
22
31
|
const MESSAGE_ID = 'msg_mock_1';
|
|
23
32
|
|
|
24
33
|
let sse = null;
|
|
25
|
-
const
|
|
34
|
+
const write = (event) => {
|
|
26
35
|
if (sse) sse.write(`data: ${JSON.stringify(event)}\n\n`);
|
|
27
36
|
};
|
|
37
|
+
// #505: like opencode 1.18.32, a prompt_async POSTed while the session is busy is
|
|
38
|
+
// steered into the running loop: its user message exists at once, and the next
|
|
39
|
+
// assistant message names it as parentID. Steers are read just before the turn's
|
|
40
|
+
// idle, unless the turn is `late` (the upstream lost wake: never read).
|
|
41
|
+
let turnActive = false;
|
|
42
|
+
let turnLate = false;
|
|
43
|
+
let steers = [];
|
|
44
|
+
let steerSerial = 0;
|
|
45
|
+
const send = (event) => {
|
|
46
|
+
const ownIdle = event?.type === 'session.idle' && (!event.properties?.sessionID || event.properties.sessionID === SESSION_ID);
|
|
47
|
+
if (ownIdle) {
|
|
48
|
+
if (!turnLate) for (const steer of steers) {
|
|
49
|
+
const id = `msg_steer_reply_${++steerSerial}`;
|
|
50
|
+
write({ type: 'message.updated', properties: { info: { ...info({}), id, parentID: steer.userId } } });
|
|
51
|
+
write({ type: 'message.part.updated', properties: { part: { id: `prt_${id}`, messageID: id, sessionID: SESSION_ID, type: 'text', text: steer.text } } });
|
|
52
|
+
}
|
|
53
|
+
steers = []; turnActive = false; turnLate = false;
|
|
54
|
+
}
|
|
55
|
+
write(event);
|
|
56
|
+
};
|
|
57
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
58
|
|
|
29
59
|
/** Questions the runner can look up an id for. `replyQuestion`/`rejectQuestion`
|
|
30
60
|
* resolve the id through `GET /question` before answering (see
|
|
@@ -51,10 +81,14 @@ const sendQuestionPart = (input) =>
|
|
|
51
81
|
},
|
|
52
82
|
},
|
|
53
83
|
});
|
|
84
|
+
// The user message of the prompt being answered: opencode 1.18.32 creates it at the
|
|
85
|
+
// POST and names it as the assistant's parentID (#505 probe).
|
|
86
|
+
let currentUserId;
|
|
54
87
|
const info = (extra) => ({
|
|
55
88
|
id: MESSAGE_ID,
|
|
56
89
|
sessionID: SESSION_ID,
|
|
57
90
|
role: 'assistant',
|
|
91
|
+
...(currentUserId ? { parentID: currentUserId } : {}),
|
|
58
92
|
time: { created: 1760000000000 },
|
|
59
93
|
modelID: 'mock-model',
|
|
60
94
|
providerID: 'mock',
|
|
@@ -91,7 +125,8 @@ const server = createServer((req, res) => {
|
|
|
91
125
|
send({ type: 'message.part.updated', properties: { part: {
|
|
92
126
|
id: 'prt_answer', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: `human answer: ${body}${answerDone ? '\n\nCEZ:DONE' : ''}`,
|
|
93
127
|
} } });
|
|
94
|
-
|
|
128
|
+
// #505 review: CEZ_MOCK_OPENCODE_REPLY_HOLD_MS keeps the answered turn running.
|
|
129
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), Number(process.env.CEZ_MOCK_OPENCODE_REPLY_HOLD_MS ?? 0));
|
|
95
130
|
}, 30);
|
|
96
131
|
const acknowledge = () => { res.writeHead(200, { 'content-type': 'application/json' }); res.end('{}'); };
|
|
97
132
|
if (lateQuestionReply) setTimeout(acknowledge, 250);
|
|
@@ -112,9 +147,44 @@ const server = createServer((req, res) => {
|
|
|
112
147
|
res.end(JSON.stringify({ error: 'agent prompt rejected' }));
|
|
113
148
|
return;
|
|
114
149
|
}
|
|
150
|
+
if (turnActive && url.endsWith('/prompt_async')) {
|
|
151
|
+
const text = JSON.parse(body).parts.map(part => part.text ?? '').join('\n');
|
|
152
|
+
const userId = `msg_steer_user_${++steerSerial}`;
|
|
153
|
+
// #505 review: acknowledge a steer later than the runner's lost-wake window.
|
|
154
|
+
const ackDelay = Number(process.env.CEZ_MOCK_OPENCODE_STEER_ACK_MS ?? 0);
|
|
155
|
+
if (ackDelay > 0) setTimeout(() => { res.writeHead(204); res.end(); }, ackDelay);
|
|
156
|
+
else { res.writeHead(204); res.end(); }
|
|
157
|
+
send({ type: 'message.updated', properties: { info: { id: userId, sessionID: SESSION_ID, role: 'user', time: { created: Date.now() } } } });
|
|
158
|
+
send({ type: 'message.part.updated', properties: { part: { id: `prt_${userId}`, messageID: userId, sessionID: SESSION_ID, type: 'text', text } } });
|
|
159
|
+
steers.push({ userId, text });
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
turnActive = true;
|
|
115
163
|
// `prompt_async` semantics: acknowledge now, stream the turn over SSE.
|
|
116
164
|
res.writeHead(200, { 'content-type': 'application/json' });
|
|
117
165
|
res.end(JSON.stringify({ info: info({}), parts: [] }));
|
|
166
|
+
if (url.endsWith('/prompt_async')) {
|
|
167
|
+
currentUserId = `msg_user_${++steerSerial}`;
|
|
168
|
+
const text = JSON.parse(body).parts.map(part => part.text ?? '').join('\n');
|
|
169
|
+
send({ type: 'message.updated', properties: { info: { id: currentUserId, sessionID: SESSION_ID, role: 'user', time: { created: Date.now() } } } });
|
|
170
|
+
send({ type: 'message.part.updated', properties: { part: { id: `prt_${currentUserId}`, messageID: currentUserId, sessionID: SESSION_ID, type: 'text', text } } });
|
|
171
|
+
}
|
|
172
|
+
if (body.includes('mock:steer-tool')) {
|
|
173
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
174
|
+
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' } } } } });
|
|
175
|
+
await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
|
|
176
|
+
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' } } } });
|
|
177
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
if (body.includes('mock:steer-late')) {
|
|
181
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
182
|
+
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 } } } });
|
|
183
|
+
turnLate = true;
|
|
184
|
+
await sleep(300);
|
|
185
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
118
188
|
// The raw body is enough to spot a `mock:` marker — the prompt text is
|
|
119
189
|
// inside it whatever part shape the runner used to wrap it.
|
|
120
190
|
if (body.includes('mock:ci-wait')) {
|
|
@@ -133,6 +203,23 @@ const server = createServer((req, res) => {
|
|
|
133
203
|
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
134
204
|
return;
|
|
135
205
|
}
|
|
206
|
+
// SSE child attribution from __fixtures__/opencode/subtask-nested.ndjson.
|
|
207
|
+
if (body.includes('mock:subagent-after-park')) {
|
|
208
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
209
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'park-task', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'subtask', prompt: 'Review', description: 'Review', agent: 'general' } } });
|
|
210
|
+
send({ type: 'message.updated', properties: { info: { ...info({}), id: 'park-child-msg', sessionID: 'park-child', parentID: SESSION_ID, mode: 'subagent' } } });
|
|
211
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'early-child', messageID: 'park-child-msg', sessionID: 'park-child', type: 'text', text: 'Child review started.', time: { start: 1, end: 2 } } } });
|
|
212
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'park-parent', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Watching the child.\nCEZ:MONITORING', time: { start: 1, end: 2 } } } });
|
|
213
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
214
|
+
await afterParityPark(body);
|
|
215
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'late-read', messageID: 'park-child-msg', sessionID: 'park-child', type: 'tool', tool: 'read', state: { status: 'running', input: { path: 'README.md' } } } } });
|
|
216
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'late-read', messageID: 'park-child-msg', sessionID: 'park-child', type: 'tool', tool: 'read', state: { status: 'completed', input: { path: 'README.md' }, output: 'Read the file.' } } } });
|
|
217
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'late-child-text', messageID: 'park-child-msg', sessionID: 'park-child', type: 'text', text: 'Late child text.', time: { start: 3, end: 4 } } } });
|
|
218
|
+
// Closed child scopes are ignored. This passive usage snapshot proves the
|
|
219
|
+
// preceding SSE frames have reached the runner before the test observes it.
|
|
220
|
+
send({ type: 'message.updated', properties: { info: info({ tokens: { input: 424240, output: 2, reasoning: 0, cache: { read: 0, write: 0 } } }) } });
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
136
223
|
if (body.includes('mock:subagent')) {
|
|
137
224
|
// Wire shape from `__fixtures__/opencode/subtask-nested.ndjson`: a
|
|
138
225
|
// `subtask` part on the parent message, then a child message whose info
|
|
@@ -183,6 +270,12 @@ const server = createServer((req, res) => {
|
|
|
183
270
|
}, 60);
|
|
184
271
|
return;
|
|
185
272
|
}
|
|
273
|
+
if (body.includes('mock:provider-error-early')) {
|
|
274
|
+
// #505 review: the provider rejects before any assistant message exists.
|
|
275
|
+
send({ type: 'session.error', properties: { sessionID: SESSION_ID, error: { name: 'ProviderAuthError', data: { message: 'API key expired' } } } });
|
|
276
|
+
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
186
279
|
if (body.includes('mock:provider-error')) {
|
|
187
280
|
// The session-level error frame, wire shape copied verbatim from
|
|
188
281
|
// `__fixtures__/opencode/session-error.ndjson`. #53: a bare upstream
|
|
@@ -220,6 +313,13 @@ const server = createServer((req, res) => {
|
|
|
220
313
|
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
221
314
|
return;
|
|
222
315
|
}
|
|
316
|
+
// #401: one completed text snapshot, without an earlier streaming part.
|
|
317
|
+
if (body.includes('mock:ask-snapshot')) {
|
|
318
|
+
send({ type: 'message.updated', properties: { info: info({}) } });
|
|
319
|
+
send({ type: 'message.part.updated', properties: { part: { id: 'ask-snapshot', messageID: MESSAGE_ID, sessionID: SESSION_ID, type: 'text', text: 'Choose a test library.\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}', time: { start: 1, end: 2 } } } });
|
|
320
|
+
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
223
323
|
if (body.includes('mock:ask-bad')) {
|
|
224
324
|
// Malformed: `questions` is not an array, so the runner must REJECT the
|
|
225
325
|
// native question and finish the turn rather than wait for an answer to
|
package/scripts/mock-pi-rpc.mjs
CHANGED
|
@@ -8,7 +8,45 @@ 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
|
|
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
|
+
// Pi 0.87 defaults steeringMode to one-at-a-time (#551). Cezar's runner must
|
|
17
|
+
// send set_steering_mode all for a burst to reach the same next step.
|
|
18
|
+
let steeringMode = 'one-at-a-time';
|
|
19
|
+
// The prompt a queued handler is running; pi records it as the turn's user message.
|
|
20
|
+
let currentPrompt;
|
|
21
|
+
const send = (value) => {
|
|
22
|
+
if (value?.type === 'turn_start' && !activeTurn) {
|
|
23
|
+
activeTurn = { steers: [], late: false };
|
|
24
|
+
write(value);
|
|
25
|
+
if (currentPrompt !== undefined && process.env.CEZ_MOCK_PI_NO_USER_START !== '1') {
|
|
26
|
+
const message = { role: 'user', content: [{ type: 'text', text: currentPrompt }] };
|
|
27
|
+
currentPrompt = undefined;
|
|
28
|
+
write({ type: 'message_start', message });
|
|
29
|
+
write({ type: 'message_end', message });
|
|
30
|
+
}
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (value?.type === 'agent_settled' && activeTurn) {
|
|
34
|
+
const turn = activeTurn; activeTurn = null;
|
|
35
|
+
const deliverNow = turn.late ? [] : (steeringMode === 'all' ? turn.steers : turn.steers.slice(0, 1));
|
|
36
|
+
const defer = turn.late ? turn.steers : (steeringMode === 'all' ? [] : turn.steers.slice(1));
|
|
37
|
+
for (const text of deliverNow) {
|
|
38
|
+
write({ type: 'message_start', message: { role: 'user', content: [{ type: 'text', text }] } });
|
|
39
|
+
write({ type: 'message_end', message: { role: 'user', content: [{ type: 'text', text }] } });
|
|
40
|
+
sendText([text]);
|
|
41
|
+
write({ type: 'message_end', message: { role: 'assistant', usage: { input: 1, output: 1, cacheRead: 0, cacheWrite: 0, cost: { total: 0 } } } });
|
|
42
|
+
}
|
|
43
|
+
write(value);
|
|
44
|
+
// one-at-a-time leftover, or a late steer, runs as the next prompt.
|
|
45
|
+
for (const text of defer) queue = queue.then(() => { currentPrompt = text; return handle({ type: 'prompt', message: text }); });
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
write(value);
|
|
49
|
+
};
|
|
12
50
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
13
51
|
|
|
14
52
|
/** One valid CEZ:ASK payload (spec #473), used by the `mock:ask` scenario. */
|
|
@@ -32,8 +70,21 @@ function sendTurnEnd(usage = { input: 10, output: 5, cacheRead: 0, cacheWrite: 0
|
|
|
32
70
|
send({ type: 'agent_settled' });
|
|
33
71
|
}
|
|
34
72
|
|
|
35
|
-
|
|
73
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
74
|
+
let queue = Promise.resolve();
|
|
75
|
+
rl.on('line', (line) => {
|
|
36
76
|
const command = JSON.parse(line);
|
|
77
|
+
if (command.type === 'prompt' && command.streamingBehavior === 'steer' && activeTurn) {
|
|
78
|
+
if (process.env.CEZ_MOCK_STDIN_FILE) appendFileSync(process.env.CEZ_MOCK_STDIN_FILE, `${JSON.stringify({ userText: command.message, imageCount: 0, streamingBehavior: 'steer' })}\n`);
|
|
79
|
+
activeTurn.steers.push(command.message);
|
|
80
|
+
write({ id: command.id, type: 'response', command: 'prompt', success: true });
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
queue = queue.then(() => { currentPrompt = command.type === 'prompt' ? command.message : undefined; return handle(command); });
|
|
84
|
+
});
|
|
85
|
+
rl.on('close', () => { queue.then(() => process.exit(0)); });
|
|
86
|
+
|
|
87
|
+
async function handle(command) {
|
|
37
88
|
// Testability hook, mirroring mock-claude: CEZ_MOCK_STDIN_FILE=<path> appends
|
|
38
89
|
// each inbound prompt's text and image count, so tests can assert what the
|
|
39
90
|
// runner actually wrote onto the RPC (harness parity's AgentRunSpec probes).
|
|
@@ -55,7 +106,7 @@ for await (const line of readline.createInterface({ input: process.stdin })) {
|
|
|
55
106
|
thinkingLevel: 'medium',
|
|
56
107
|
isStreaming: false,
|
|
57
108
|
isCompacting: false,
|
|
58
|
-
steeringMode
|
|
109
|
+
steeringMode,
|
|
59
110
|
followUpMode: 'one-at-a-time',
|
|
60
111
|
autoCompactionEnabled: true,
|
|
61
112
|
messageCount: 0,
|
|
@@ -68,6 +119,21 @@ for await (const line of readline.createInterface({ input: process.stdin })) {
|
|
|
68
119
|
const { ciPrompt } = await import('./mock-ci-tool.mjs');
|
|
69
120
|
sendText([await ciPrompt('pi', process.argv.slice(2), command.message)]);
|
|
70
121
|
sendTurnEnd();
|
|
122
|
+
} else if (command.type === 'prompt' && command.message.includes('mock:steer-tool')) {
|
|
123
|
+
send({ id: command.id, type: 'response', command: 'prompt', success: true });
|
|
124
|
+
send({ type: 'agent_start' }); send({ type: 'turn_start' });
|
|
125
|
+
send({ type: 'tool_execution_start', toolCallId: 'tool-steer', toolName: 'bash', args: { command: 'wait' } });
|
|
126
|
+
await sleep(Number(process.env.CEZ_MOCK_STEER_MS ?? 600));
|
|
127
|
+
send({ type: 'tool_execution_end', toolCallId: 'tool-steer', toolName: 'bash', result: { content: [{ type: 'text', text: 'waited' }] }, isError: false });
|
|
128
|
+
sendText(['steer tool done']);
|
|
129
|
+
sendTurnEnd();
|
|
130
|
+
} else if (command.type === 'prompt' && command.message.includes('mock:steer-late')) {
|
|
131
|
+
send({ id: command.id, type: 'response', command: 'prompt', success: true });
|
|
132
|
+
send({ type: 'agent_start' }); send({ type: 'turn_start' });
|
|
133
|
+
sendText(['late window: final message already sent']);
|
|
134
|
+
activeTurn.late = true;
|
|
135
|
+
await sleep(300);
|
|
136
|
+
sendTurnEnd();
|
|
71
137
|
} else if (command.type === 'prompt' && command.message.includes('mock:agent-echo')) {
|
|
72
138
|
// rpc-lifecycle.ndjson's normal prompt/assistant/settled sequence.
|
|
73
139
|
send({ id: command.id, type: 'response', command: 'prompt', success: true });
|
|
@@ -115,6 +181,13 @@ for await (const line of readline.createInterface({ input: process.stdin })) {
|
|
|
115
181
|
send({ type: 'turn_start' });
|
|
116
182
|
sendText(['parity done: the task is complete\n\nCEZ:DONE']);
|
|
117
183
|
sendTurnEnd();
|
|
184
|
+
} else if (command.type === 'prompt' && command.message.includes('mock:ask-snapshot')) {
|
|
185
|
+
// #401: authoritative text_end without deltas, using rpc-lifecycle frames.
|
|
186
|
+
send({ id: command.id, type: 'response', command: 'prompt', success: true });
|
|
187
|
+
send({ type: 'agent_start' });
|
|
188
|
+
send({ type: 'turn_start' });
|
|
189
|
+
send({ type: 'message_update', message: {}, assistantMessageEvent: { type: 'text_end', contentIndex: 0, content: 'Choose a test library.\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}', partial: {} } });
|
|
190
|
+
sendTurnEnd();
|
|
118
191
|
} else if (command.type === 'prompt' && command.message.includes('mock:ask-bad')) {
|
|
119
192
|
// A marker whose JSON body is invalid. `parseAskMarker` must render no card
|
|
120
193
|
// and the turn must still end (harness parity R4) — pi has no native ask
|
|
@@ -245,6 +318,12 @@ for await (const line of readline.createInterface({ input: process.stdin })) {
|
|
|
245
318
|
});
|
|
246
319
|
}, 250);
|
|
247
320
|
}
|
|
321
|
+
} else if (command.type === 'set_steering_mode') {
|
|
322
|
+
if (command.mode === 'all' || command.mode === 'one-at-a-time') steeringMode = command.mode;
|
|
323
|
+
if (process.env.CEZ_MOCK_STDIN_FILE) {
|
|
324
|
+
try { appendFileSync(process.env.CEZ_MOCK_STDIN_FILE, `${JSON.stringify({ type: 'set_steering_mode', mode: command.mode })}\n`); } catch { /* best effort */ }
|
|
325
|
+
}
|
|
326
|
+
send({ ...(command.id ? { id: command.id } : {}), type: 'response', command: 'set_steering_mode', success: true });
|
|
248
327
|
} else if (command.type === 'abort') {
|
|
249
328
|
send({ type: 'response', command: 'abort', success: true });
|
|
250
329
|
}
|
|
@@ -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,Qn as l,S as u,T 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-DH8qtCz-.js";import{t as y}from"./utils-BTFBhZ55.js";import{_ as b,g as x,h as ee,m as te,p as S}from"./dropdown-menu-7KGQMoFj.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)(x,{...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":J(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`,[re,I]=E(F,{forceMount:void 0}),L=e=>{let{__scopePopover:t,forceMount:n,children:i,container:a}=e,o=k(F,t);return(0,w.jsx)(re,{scope:t,forceMount:n,children:(0,w.jsx)(c,{present:n||o.open,children:(0,w.jsx)(r,{asChild:!0,container:a,children:i})})})};L.displayName=F;var R=`PopoverContent`,z=C.forwardRef((e,t)=>{let n=I(R,e.__scopePopover),{forceMount:r=n.forceMount,...i}=e,a=k(R,e.__scopePopover);return(0,w.jsx)(c,{present:r||a.open,children:a.modal?(0,w.jsx)(V,{...i,ref:t}):(0,w.jsx)(H,{...i,ref:t})})});z.displayName=R;var B=a(`PopoverContent.RemoveScroll`),V=C.forwardRef((e,t)=>{let n=k(R,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:B,allowPinchZoom:!0,children:(0,w.jsx)(U,{...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})})})}),H=C.forwardRef((e,t)=>{let n=k(R,e.__scopePopover),r=C.useRef(!1),i=C.useRef(!1);return(0,w.jsx)(U,{...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()}})}),U=C.forwardRef((e,t)=>{let{__scopePopover:n,trapFocus:r,onOpenAutoFocus:i,onCloseAutoFocus:a,disableOutsidePointerEvents:o,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onInteractOutside:f,...p}=e,m=k(R,n),h=D(n);return u(),(0,w.jsx)(g,{asChild:!0,loop:!0,trapped:r,onMountAutoFocus:i,onUnmountAutoFocus:a,children:(0,w.jsx)(d,{asChild:!0,disableOutsidePointerEvents:o,onInteractOutside:f,onEscapeKeyDown:s,onPointerDownOutside:c,onFocusOutside:l,onDismiss:()=>m.onOpenChange(!1),deferPointerDownOutside:!0,children:(0,w.jsx)(ee,{"data-state":J(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)`}})})})}),W=`PopoverClose`,G=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=k(W,n);return(0,w.jsx)(s.button,{type:`button`,...r,ref:t,onClick:p(e.onClick,()=>i.onOpenChange(!1))})});G.displayName=W;var K=`PopoverArrow`,q=C.forwardRef((e,t)=>{let{__scopePopover:n,...r}=e,i=D(n);return(0,w.jsx)(te,{...i,...r,ref:t})});q.displayName=K;function J(e){return e?`open`:`closed`}var Y=A,X=M,Z=P,ie=L,ae=z;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)(Y,{"data-slot":`popover`,...e})})}function pe({onPointerDownCapture:e,onKeyDownCapture:t,...n}){let r=C.useContext($);return(0,w.jsx)(Z,{"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)(X,{"data-slot":`popover-anchor`,...e})}function ge(){let e=h(),t=l(),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};
|