@wjarka/cezarion 0.14.9-dev.944 → 0.14.9-dev.958
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/dist/server/server.d.ts
CHANGED
|
@@ -4318,19 +4318,6 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
4318
4318
|
} & {
|
|
4319
4319
|
"/repo/commit/:sha": {
|
|
4320
4320
|
$get: {
|
|
4321
|
-
output: string;
|
|
4322
|
-
outputFormat: "text";
|
|
4323
|
-
status: import("hono/utils/http-status").ContentfulStatusCode;
|
|
4324
|
-
input: {
|
|
4325
|
-
query: {
|
|
4326
|
-
structured?: string | undefined;
|
|
4327
|
-
};
|
|
4328
|
-
} & {
|
|
4329
|
-
param: {
|
|
4330
|
-
sha: string;
|
|
4331
|
-
};
|
|
4332
|
-
};
|
|
4333
|
-
} | {
|
|
4334
4321
|
output: {
|
|
4335
4322
|
error: string;
|
|
4336
4323
|
code?: "invalid_input" | undefined;
|
|
@@ -4394,6 +4381,19 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
4394
4381
|
sha: string;
|
|
4395
4382
|
};
|
|
4396
4383
|
};
|
|
4384
|
+
} | {
|
|
4385
|
+
output: string;
|
|
4386
|
+
outputFormat: "text";
|
|
4387
|
+
status: import("hono/utils/http-status").ContentfulStatusCode;
|
|
4388
|
+
input: {
|
|
4389
|
+
query: {
|
|
4390
|
+
structured?: string | undefined;
|
|
4391
|
+
};
|
|
4392
|
+
} & {
|
|
4393
|
+
param: {
|
|
4394
|
+
sha: string;
|
|
4395
|
+
};
|
|
4396
|
+
};
|
|
4397
4397
|
};
|
|
4398
4398
|
};
|
|
4399
4399
|
} & {
|
|
@@ -12306,20 +12306,20 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
12306
12306
|
} & {
|
|
12307
12307
|
"/runs/:id/handoff": {
|
|
12308
12308
|
$get: {
|
|
12309
|
-
output:
|
|
12310
|
-
|
|
12311
|
-
|
|
12309
|
+
output: {
|
|
12310
|
+
error: string;
|
|
12311
|
+
};
|
|
12312
|
+
outputFormat: "json";
|
|
12313
|
+
status: 404;
|
|
12312
12314
|
input: {
|
|
12313
12315
|
param: {
|
|
12314
12316
|
id: string;
|
|
12315
12317
|
};
|
|
12316
12318
|
};
|
|
12317
12319
|
} | {
|
|
12318
|
-
output:
|
|
12319
|
-
|
|
12320
|
-
|
|
12321
|
-
outputFormat: "json";
|
|
12322
|
-
status: 404;
|
|
12320
|
+
output: string;
|
|
12321
|
+
outputFormat: "text";
|
|
12322
|
+
status: 200;
|
|
12323
12323
|
input: {
|
|
12324
12324
|
param: {
|
|
12325
12325
|
id: string;
|
|
@@ -12357,20 +12357,20 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
12357
12357
|
} & {
|
|
12358
12358
|
"/runs/:id/diff": {
|
|
12359
12359
|
$get: {
|
|
12360
|
-
output:
|
|
12361
|
-
|
|
12362
|
-
|
|
12360
|
+
output: {
|
|
12361
|
+
error: string;
|
|
12362
|
+
};
|
|
12363
|
+
outputFormat: "json";
|
|
12364
|
+
status: 404;
|
|
12363
12365
|
input: {
|
|
12364
12366
|
param: {
|
|
12365
12367
|
id: string;
|
|
12366
12368
|
};
|
|
12367
12369
|
};
|
|
12368
12370
|
} | {
|
|
12369
|
-
output:
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
outputFormat: "json";
|
|
12373
|
-
status: 404;
|
|
12371
|
+
output: string;
|
|
12372
|
+
outputFormat: "text";
|
|
12373
|
+
status: import("hono/utils/http-status").ContentfulStatusCode;
|
|
12374
12374
|
input: {
|
|
12375
12375
|
param: {
|
|
12376
12376
|
id: string;
|
|
@@ -15239,19 +15239,6 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
15239
15239
|
} & {
|
|
15240
15240
|
"/repo/commit/:sha": {
|
|
15241
15241
|
$get: {
|
|
15242
|
-
output: string;
|
|
15243
|
-
outputFormat: "text";
|
|
15244
|
-
status: import("hono/utils/http-status").ContentfulStatusCode;
|
|
15245
|
-
input: {
|
|
15246
|
-
query: {
|
|
15247
|
-
structured?: string | undefined;
|
|
15248
|
-
};
|
|
15249
|
-
} & {
|
|
15250
|
-
param: {
|
|
15251
|
-
sha: string;
|
|
15252
|
-
};
|
|
15253
|
-
};
|
|
15254
|
-
} | {
|
|
15255
15242
|
output: {
|
|
15256
15243
|
error: string;
|
|
15257
15244
|
code?: "invalid_input" | undefined;
|
|
@@ -15315,6 +15302,19 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
15315
15302
|
sha: string;
|
|
15316
15303
|
};
|
|
15317
15304
|
};
|
|
15305
|
+
} | {
|
|
15306
|
+
output: string;
|
|
15307
|
+
outputFormat: "text";
|
|
15308
|
+
status: import("hono/utils/http-status").ContentfulStatusCode;
|
|
15309
|
+
input: {
|
|
15310
|
+
query: {
|
|
15311
|
+
structured?: string | undefined;
|
|
15312
|
+
};
|
|
15313
|
+
} & {
|
|
15314
|
+
param: {
|
|
15315
|
+
sha: string;
|
|
15316
|
+
};
|
|
15317
|
+
};
|
|
15318
15318
|
};
|
|
15319
15319
|
};
|
|
15320
15320
|
} & {
|
|
@@ -23227,20 +23227,20 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
23227
23227
|
} & {
|
|
23228
23228
|
"/runs/:id/handoff": {
|
|
23229
23229
|
$get: {
|
|
23230
|
-
output:
|
|
23231
|
-
|
|
23232
|
-
|
|
23230
|
+
output: {
|
|
23231
|
+
error: string;
|
|
23232
|
+
};
|
|
23233
|
+
outputFormat: "json";
|
|
23234
|
+
status: 404;
|
|
23233
23235
|
input: {
|
|
23234
23236
|
param: {
|
|
23235
23237
|
id: string;
|
|
23236
23238
|
};
|
|
23237
23239
|
};
|
|
23238
23240
|
} | {
|
|
23239
|
-
output:
|
|
23240
|
-
|
|
23241
|
-
|
|
23242
|
-
outputFormat: "json";
|
|
23243
|
-
status: 404;
|
|
23241
|
+
output: string;
|
|
23242
|
+
outputFormat: "text";
|
|
23243
|
+
status: 200;
|
|
23244
23244
|
input: {
|
|
23245
23245
|
param: {
|
|
23246
23246
|
id: string;
|
|
@@ -23278,20 +23278,20 @@ export declare function createApp(deps: ServerDeps): import("hono/hono-base").Ho
|
|
|
23278
23278
|
} & {
|
|
23279
23279
|
"/runs/:id/diff": {
|
|
23280
23280
|
$get: {
|
|
23281
|
-
output:
|
|
23282
|
-
|
|
23283
|
-
|
|
23281
|
+
output: {
|
|
23282
|
+
error: string;
|
|
23283
|
+
};
|
|
23284
|
+
outputFormat: "json";
|
|
23285
|
+
status: 404;
|
|
23284
23286
|
input: {
|
|
23285
23287
|
param: {
|
|
23286
23288
|
id: string;
|
|
23287
23289
|
};
|
|
23288
23290
|
};
|
|
23289
23291
|
} | {
|
|
23290
|
-
output:
|
|
23291
|
-
|
|
23292
|
-
|
|
23293
|
-
outputFormat: "json";
|
|
23294
|
-
status: 404;
|
|
23292
|
+
output: string;
|
|
23293
|
+
outputFormat: "text";
|
|
23294
|
+
status: import("hono/utils/http-status").ContentfulStatusCode;
|
|
23295
23295
|
input: {
|
|
23296
23296
|
param: {
|
|
23297
23297
|
id: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wjarka/cezarion",
|
|
3
|
-
"version": "0.14.9-dev.
|
|
3
|
+
"version": "0.14.9-dev.958",
|
|
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",
|
|
@@ -35,8 +35,8 @@
|
|
|
35
35
|
"server-uninstall": "tsx src/index.ts server-uninstall",
|
|
36
36
|
"test": "vitest run",
|
|
37
37
|
"test:watch": "vitest",
|
|
38
|
-
"test:unit": "node --import tsx --test test/unit/*.test.ts",
|
|
39
|
-
"test:package": "node --import tsx --test test/e2e/*.test.ts",
|
|
38
|
+
"test:unit": "node --import ../../scripts/test-git-env.mjs --import tsx --test test/unit/*.test.ts",
|
|
39
|
+
"test:package": "node --import ../../scripts/test-git-env.mjs --import tsx --test test/e2e/*.test.ts",
|
|
40
40
|
"typecheck": "tsc --noEmit -p tsconfig.test.json",
|
|
41
41
|
"postbuild": "node scripts/inline-contract.mjs"
|
|
42
42
|
},
|
|
@@ -51,8 +51,8 @@
|
|
|
51
51
|
"zod": "^4.4.3"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@open-mercato/cezar-api-client": "0.14.9-dev.
|
|
55
|
-
"@open-mercato/cezar-contract": "0.14.9-dev.
|
|
54
|
+
"@open-mercato/cezar-api-client": "0.14.9-dev.958",
|
|
55
|
+
"@open-mercato/cezar-contract": "0.14.9-dev.958",
|
|
56
56
|
"@types/node": "^20.14.0",
|
|
57
57
|
"@types/ws": "^8.18.1",
|
|
58
58
|
"esbuild": "^0.28.1",
|
package/scripts/mock-claude.mjs
CHANGED
|
@@ -8,6 +8,15 @@
|
|
|
8
8
|
import { createInterface } from 'node:readline';
|
|
9
9
|
import { appendFileSync, readFileSync, writeFileSync } from 'node:fs';
|
|
10
10
|
|
|
11
|
+
import * as parityGateFs from 'node:fs';
|
|
12
|
+
|
|
13
|
+
// #401: let the test observe the actual monitoring park before releasing late wire frames.
|
|
14
|
+
async function afterParityPark(prompt) {
|
|
15
|
+
const gate = /parity-release=([^\s"\\]+)/.exec(prompt)?.[1];
|
|
16
|
+
if (!gate) throw new Error('post-park scenario requires a release path');
|
|
17
|
+
while (!parityGateFs.existsSync(gate)) await new Promise(resolve => setTimeout(resolve, 10));
|
|
18
|
+
}
|
|
19
|
+
|
|
11
20
|
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12
21
|
const emit = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
|
|
13
22
|
|
|
@@ -105,6 +114,23 @@ async function respond(userText, imageCount, uuid) {
|
|
|
105
114
|
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
115
|
return;
|
|
107
116
|
}
|
|
117
|
+
// Native child attribution from __fixtures__/claude/subagent-task.ndjson.
|
|
118
|
+
if (!userText.includes('[cez-namer]') && userText.includes('mock:subagent-after-park')) {
|
|
119
|
+
const task = `toolu_park_${turn}`;
|
|
120
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'tool_use', id: task, name: 'Task', input: { description: 'Review' } }] } });
|
|
121
|
+
emit({ type: 'assistant', message: { role: 'assistant', content: [{ type: 'text', text: 'Watching the child.\nCEZ:MONITORING' }] } });
|
|
122
|
+
emit({ type: 'result', subtype: 'success', result: 'Watching the child.\nCEZ:MONITORING', usage: { input_tokens: 10, output_tokens: 5 } });
|
|
123
|
+
await afterParityPark(userText);
|
|
124
|
+
emit({ type: 'assistant', parent_tool_use_id: task, message: { role: 'assistant', content: [{ type: 'tool_use', id: 'late-read', name: 'Read', input: { file_path: 'README.md' } }] } });
|
|
125
|
+
emit({ type: 'user', parent_tool_use_id: task, message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'late-read', content: 'Read the file.' }] } });
|
|
126
|
+
emit({ type: 'assistant', parent_tool_use_id: task, message: { role: 'assistant', content: [{ type: 'text', text: 'Post-park child update processed.' }] } });
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
// #401: result-only full text; mapResult and v1 use the same fallback.
|
|
130
|
+
if (!userText.includes('[cez-namer]') && userText.includes('mock:ask-snapshot')) {
|
|
131
|
+
emit({ type: 'result', subtype: 'success', result: 'Choose a test library.\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}', usage: { input_tokens: 10, output_tokens: 5 } });
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
108
134
|
if (userText.includes('mock:ci-wait')) {
|
|
109
135
|
const { ciPrompt } = await import('./mock-ci-tool.mjs');
|
|
110
136
|
const text = await ciPrompt('claude', process.argv.slice(2), userText);
|
|
@@ -11,6 +11,15 @@
|
|
|
11
11
|
import { createInterface } from 'node:readline';
|
|
12
12
|
import { appendFileSync } from 'node:fs';
|
|
13
13
|
|
|
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 write = (obj) => process.stdout.write(`${JSON.stringify(obj)}\n`);
|
|
15
24
|
// #505: like the real app-server, any active main-thread turn accepts `turn/steer`.
|
|
16
25
|
// Steers accepted by an ordinary scripted turn are read just before it completes:
|
|
@@ -141,6 +150,23 @@ rl.on('line', async (line) => {
|
|
|
141
150
|
emit({ method: 'item/started', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
|
|
142
151
|
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: opening } });
|
|
143
152
|
}
|
|
153
|
+
// Native thread attribution from collab-agent-tool-call.ndjson (#121/#401).
|
|
154
|
+
if (turnText.includes('mock:subagent-after-park')) {
|
|
155
|
+
emit({ method: 'item/started', params: { threadId: 'th_mock_1', item: { type: 'collabAgentToolCall', id: 'park-spawn', tool: 'spawnAgent', status: 'inProgress', receiverThreadIds: ['th_park_child'] } } });
|
|
156
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', item: { type: 'agentMessage', id: 'park-parent', text: 'Watching the child.\nCEZ:MONITORING' } } });
|
|
157
|
+
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
158
|
+
await afterParityPark(turnText);
|
|
159
|
+
emit({ method: 'item/started', params: { threadId: 'th_park_child', item: { type: 'commandExecution', id: 'late-read', command: 'cat README.md', status: 'inProgress' } } });
|
|
160
|
+
emit({ method: 'item/completed', params: { threadId: 'th_park_child', item: { type: 'commandExecution', id: 'late-read', command: 'cat README.md', status: 'completed', exitCode: 0 } } });
|
|
161
|
+
emit({ method: 'item/completed', params: { threadId: 'th_park_child', item: { type: 'agentMessage', id: 'late-child', text: 'Post-park child update processed.' } } });
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
// #401: agentMessage completion without deltas (same item envelope as baseline).
|
|
165
|
+
if (turnText.includes('mock:ask-snapshot')) {
|
|
166
|
+
emit({ method: 'item/completed', params: { threadId: 'th_mock_1', turnId: 'turn_mock_1', item: { type: 'agentMessage', id: 'ask-snapshot', text: 'Choose a test library.\nCEZ:ASK {"questions":[{"header":"Library","question":"Which test library?","options":[{"label":"Vitest"},{"label":"Node test"}]}]}' } } });
|
|
167
|
+
emit({ method: 'turn/completed', params: { threadId: 'th_mock_1', turn: { id: 'turn_mock_1', status: 'completed' } } });
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
144
170
|
if (turnText.includes('mock:ci-wait')) {
|
|
145
171
|
const { ciPrompt } = await import('./mock-ci-tool.mjs');
|
|
146
172
|
const text = await ciPrompt('codex', ciWire, turnText);
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
// Offline Cursor ACP wire. Shapes: cursor.com/docs/cli/acp; ACP v1 schema.
|
|
3
3
|
import { createInterface } from 'node:readline';
|
|
4
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));
|
|
7
16
|
/** #529: remaining-count file. Each session/new or session/load decrements; at >0 the process writes stderr and exits 1 before the reply. */
|
|
@@ -62,11 +71,31 @@ async function prompt(id, content) {
|
|
|
62
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; }
|
|
63
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; }
|
|
64
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
|
+
}
|
|
65
78
|
if (input.includes('mock:ask-bad')) { pendingAsk = { id }; emit({ id: 'bad-question', method: 'cursor/ask_question', params: { questions: [] } }); return; }
|
|
66
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; }
|
|
67
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; }
|
|
68
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; }
|
|
69
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
|
+
}
|
|
70
99
|
if (input.includes('mock:subagent')) {
|
|
71
100
|
update({ sessionUpdate: 'tool_call', toolCallId: 'task-1', title: 'Review', kind: 'other', rawInput: { description: 'Review' }, status: 'in_progress' });
|
|
72
101
|
update({ sessionUpdate: 'subagent_spawned', subagentSessionId: 'child', name: 'Reviewer', task: 'Review', capabilities: {}, _meta: { cursor: { toolCallId: 'task-1', agentId: 'child-agent' } } });
|
|
@@ -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);
|
|
@@ -194,6 +203,23 @@ const server = createServer((req, res) => {
|
|
|
194
203
|
send({ type: 'session.idle', properties: { sessionID: SESSION_ID } });
|
|
195
204
|
return;
|
|
196
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
|
+
}
|
|
197
223
|
if (body.includes('mock:subagent')) {
|
|
198
224
|
// Wire shape from `__fixtures__/opencode/subtask-nested.ndjson`: a
|
|
199
225
|
// `subtask` part on the parent message, then a child message whose info
|
|
@@ -287,6 +313,13 @@ const server = createServer((req, res) => {
|
|
|
287
313
|
setTimeout(() => send({ type: 'session.idle', properties: { sessionID: SESSION_ID } }), 30);
|
|
288
314
|
return;
|
|
289
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
|
+
}
|
|
290
323
|
if (body.includes('mock:ask-bad')) {
|
|
291
324
|
// Malformed: `questions` is not an array, so the runner must REJECT the
|
|
292
325
|
// native question and finish the turn rather than wait for an answer to
|
package/scripts/mock-pi-rpc.mjs
CHANGED
|
@@ -181,6 +181,13 @@ async function handle(command) {
|
|
|
181
181
|
send({ type: 'turn_start' });
|
|
182
182
|
sendText(['parity done: the task is complete\n\nCEZ:DONE']);
|
|
183
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();
|
|
184
191
|
} else if (command.type === 'prompt' && command.message.includes('mock:ask-bad')) {
|
|
185
192
|
// A marker whose JSON body is invalid. `parseAskMarker` must render no card
|
|
186
193
|
// and the turn must still end (harness parity R4) — pi has no native ask
|