@adhdev/daemon-core 0.7.46 → 0.8.0
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/cli-adapters/provider-cli-adapter.d.ts +33 -0
- package/dist/cli-adapters/session-host-transport.d.ts +1 -0
- package/dist/config/chat-history.d.ts +3 -0
- package/dist/config/config.d.ts +1 -1
- package/dist/daemon/dev-auto-implement.d.ts +18 -2
- package/dist/daemon/dev-cli-debug.d.ts +82 -0
- package/dist/daemon/dev-server.d.ts +7 -0
- package/dist/index.js +1625 -191
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1625 -191
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +5 -0
- package/dist/providers/contracts.d.ts +8 -0
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-adapters/provider-cli-adapter.ts +417 -6
- package/src/cli-adapters/session-host-transport.ts +13 -1
- package/src/commands/chat-commands.ts +8 -0
- package/src/config/chat-history.ts +5 -1
- package/src/config/config.ts +2 -2
- package/src/daemon/dev-auto-implement.ts +371 -38
- package/src/daemon/dev-cli-debug.ts +839 -0
- package/src/daemon/dev-server.ts +29 -1
- package/src/providers/cli-provider-instance.ts +79 -1
- package/src/providers/contracts.ts +8 -0
- package/src/providers/provider-loader.ts +39 -0
|
@@ -13,6 +13,48 @@ import type * as http from 'http';
|
|
|
13
13
|
import type { DevServerContext, ProviderCategory } from './dev-server-types.js';
|
|
14
14
|
import { DEV_SERVER_PORT } from './dev-server.js';
|
|
15
15
|
import { LOG } from '../logging/logger.js';
|
|
16
|
+
import { runCliAutoImplVerification } from './dev-cli-debug.js';
|
|
17
|
+
|
|
18
|
+
type CliExerciseVerification = {
|
|
19
|
+
request?: Record<string, any>;
|
|
20
|
+
mustContainAny?: string[];
|
|
21
|
+
mustNotContainAny?: string[];
|
|
22
|
+
mustMatchAny?: string[];
|
|
23
|
+
mustNotMatchAny?: string[];
|
|
24
|
+
lastAssistantMustContainAny?: string[];
|
|
25
|
+
lastAssistantMustNotContainAny?: string[];
|
|
26
|
+
lastAssistantMustMatchAny?: string[];
|
|
27
|
+
lastAssistantMustNotMatchAny?: string[];
|
|
28
|
+
inspectFields?: string[];
|
|
29
|
+
description?: string;
|
|
30
|
+
fixtureName?: string;
|
|
31
|
+
fixtureNames?: string[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function getAutoImplPid(ctx: DevServerContext): number | null {
|
|
35
|
+
const proc: any = ctx.autoImplProcess;
|
|
36
|
+
return proc && typeof proc.pid === 'number' && proc.pid > 0 ? proc.pid : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function isPidAlive(pid: number): boolean {
|
|
40
|
+
try {
|
|
41
|
+
process.kill(pid, 0);
|
|
42
|
+
return true;
|
|
43
|
+
} catch (error: any) {
|
|
44
|
+
return error?.code === 'EPERM';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function clearStaleAutoImplState(ctx: DevServerContext, reason: string): void {
|
|
49
|
+
if (!ctx.autoImplStatus.running && !ctx.autoImplProcess) return;
|
|
50
|
+
|
|
51
|
+
const pid = getAutoImplPid(ctx);
|
|
52
|
+
if (pid && isPidAlive(pid)) return;
|
|
53
|
+
|
|
54
|
+
ctx.log(`Clearing stale auto-implement state: ${reason}${pid ? ` (pid ${pid})` : ''}`);
|
|
55
|
+
ctx.autoImplProcess = null;
|
|
56
|
+
ctx.autoImplStatus.running = false;
|
|
57
|
+
}
|
|
16
58
|
|
|
17
59
|
export function getDefaultAutoImplReference(ctx: DevServerContext, category: string, type: string): string {
|
|
18
60
|
if (category === 'cli') {
|
|
@@ -118,12 +160,21 @@ export function loadAutoImplReferenceScripts(ctx: DevServerContext, referenceTyp
|
|
|
118
160
|
|
|
119
161
|
export async function handleAutoImplement(ctx: DevServerContext, type: string, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
120
162
|
const body = await ctx.readBody(req);
|
|
121
|
-
const {
|
|
163
|
+
const {
|
|
164
|
+
agent = 'claude-cli',
|
|
165
|
+
functions,
|
|
166
|
+
reference,
|
|
167
|
+
model,
|
|
168
|
+
comment,
|
|
169
|
+
providerDir: requestedProviderDir,
|
|
170
|
+
verification,
|
|
171
|
+
} = body;
|
|
122
172
|
if (!functions || !Array.isArray(functions) || functions.length === 0) {
|
|
123
173
|
ctx.json(res, 400, { error: 'functions[] is required (e.g. ["readChat", "sendMessage"])' });
|
|
124
174
|
return;
|
|
125
175
|
}
|
|
126
176
|
|
|
177
|
+
clearStaleAutoImplState(ctx, 'new auto-implement request');
|
|
127
178
|
if (ctx.autoImplStatus.running) {
|
|
128
179
|
ctx.json(res, 409, { error: 'Auto-implement already in progress', type: ctx.autoImplStatus.type });
|
|
129
180
|
return;
|
|
@@ -141,7 +192,57 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
141
192
|
}
|
|
142
193
|
const providerDir = writableProvider.dir;
|
|
143
194
|
|
|
195
|
+
ctx.autoImplStatus = { running: false, type, progress: [] };
|
|
196
|
+
|
|
197
|
+
if (provider.category === 'cli' && verification && (verification.fixtureName || (verification.fixtureNames && verification.fixtureNames.length > 0))) {
|
|
198
|
+
sendAutoImplSSE(ctx, {
|
|
199
|
+
event: 'progress',
|
|
200
|
+
data: {
|
|
201
|
+
function: '_preflight',
|
|
202
|
+
status: 'verifying',
|
|
203
|
+
message: 'Running preflight verification before spawning agent...',
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
try {
|
|
207
|
+
const preflight = await runCliAutoImplVerification(ctx, type, verification);
|
|
208
|
+
sendAutoImplSSE(ctx, { event: 'verification', data: preflight });
|
|
209
|
+
if (preflight.pass) {
|
|
210
|
+
sendAutoImplSSE(ctx, {
|
|
211
|
+
event: 'complete',
|
|
212
|
+
data: {
|
|
213
|
+
success: true,
|
|
214
|
+
exitCode: 0,
|
|
215
|
+
functions,
|
|
216
|
+
message: `✅ No-op: exact ${preflight.mode} already passes`,
|
|
217
|
+
verification: preflight,
|
|
218
|
+
skipped: true,
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
ctx.json(res, 200, {
|
|
222
|
+
started: false,
|
|
223
|
+
skipped: true,
|
|
224
|
+
type,
|
|
225
|
+
functions,
|
|
226
|
+
providerDir,
|
|
227
|
+
verification: preflight,
|
|
228
|
+
message: 'Preflight verification already passes. No auto-implement run needed.',
|
|
229
|
+
});
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
} catch (error: any) {
|
|
233
|
+
sendAutoImplSSE(ctx, {
|
|
234
|
+
event: 'progress',
|
|
235
|
+
data: {
|
|
236
|
+
function: '_preflight',
|
|
237
|
+
status: 'verify_failed',
|
|
238
|
+
message: `Preflight verification errored, continuing to agent run: ${error?.message || error}`,
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
144
244
|
try {
|
|
245
|
+
ctx.autoImplStatus = { running: true, type, progress: ctx.autoImplStatus.progress };
|
|
145
246
|
// 1. Collect DOM context
|
|
146
247
|
// 1. Skip heavy DOM pre-parsing (Agent will use cURL to explore via CDP!)
|
|
147
248
|
const resolvedReference = resolveAutoImplReference(ctx, provider.category, reference, type);
|
|
@@ -170,7 +271,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
170
271
|
const referenceScripts = loadAutoImplReferenceScripts(ctx, resolvedReference);
|
|
171
272
|
|
|
172
273
|
// 3. Build the prompt
|
|
173
|
-
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference);
|
|
274
|
+
const prompt = buildAutoImplPrompt(ctx, type, provider, providerDir, functions, domContext, referenceScripts, comment, resolvedReference, verification);
|
|
174
275
|
|
|
175
276
|
// 4. Write prompt to temp file (avoids shell escaping issues with special chars)
|
|
176
277
|
const tmpDir = path.join(os.tmpdir(), 'adhdev-autoimpl');
|
|
@@ -193,7 +294,8 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
193
294
|
// ─── ACP Agent: use ACP SDK (JSON-RPC protocol) ───
|
|
194
295
|
if (agentCategory === 'acp') {
|
|
195
296
|
sendAutoImplSSE(ctx, { event: 'progress', data: { function: '_init', status: 'spawning', message: `Spawning ACP agent: ${spawn.command} ${(spawn.args || []).join(' ')}` } });
|
|
196
|
-
ctx.autoImplStatus =
|
|
297
|
+
ctx.autoImplStatus.running = true;
|
|
298
|
+
ctx.autoImplStatus.type = type;
|
|
197
299
|
|
|
198
300
|
// Dynamic import ACP SDK
|
|
199
301
|
const { ClientSideConnection, ndJsonStream, PROTOCOL_VERSION } = await import('@agentclientprotocol/sdk');
|
|
@@ -366,7 +468,8 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
366
468
|
|
|
367
469
|
sendAutoImplSSE(ctx, { event: 'progress', data: { function: '_init', status: 'spawning', message: `Spawning agent: ${shellCmd.substring(0, 200)}... (prompt: ${prompt.length} chars)` } });
|
|
368
470
|
|
|
369
|
-
ctx.autoImplStatus =
|
|
471
|
+
ctx.autoImplStatus.running = true;
|
|
472
|
+
ctx.autoImplStatus.type = type;
|
|
370
473
|
const spawnedAt = Date.now();
|
|
371
474
|
|
|
372
475
|
let child: any;
|
|
@@ -412,6 +515,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
412
515
|
let approvalKeys: Record<number, string> = { 0: 'y\r' };
|
|
413
516
|
let approvalBuffer = '';
|
|
414
517
|
let lastApprovalTime = 0;
|
|
518
|
+
let completionSignalSeen = false;
|
|
415
519
|
|
|
416
520
|
try {
|
|
417
521
|
const { normalizeCliProviderForRuntime } = await import('../cli-adapters/provider-cli-adapter.js');
|
|
@@ -435,6 +539,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
435
539
|
// Force exit on completion signal (check cleanData directly to avoid stale buffer echo matches)
|
|
436
540
|
const elapsed = Date.now() - spawnedAt;
|
|
437
541
|
if (elapsed > 15000 && cleanData.includes('_PIPELINE_COMPLETE_SIGNAL_')) {
|
|
542
|
+
completionSignalSeen = true;
|
|
438
543
|
ctx.log(`Agent finished task after ${Math.round(elapsed/1000)}s. Terminating interactive CLI session to unblock pipeline.`);
|
|
439
544
|
sendAutoImplSSE(ctx, { event: 'output', data: { chunk: `\n[🤖 ADHDev Pipeline] Completion token detected. Proceeding...\n`, stream: 'stdout' } });
|
|
440
545
|
approvalBuffer = '';
|
|
@@ -461,6 +566,57 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
461
566
|
}
|
|
462
567
|
};
|
|
463
568
|
|
|
569
|
+
const finalizeCliAutoImpl = async (code: number | null) => {
|
|
570
|
+
ctx.autoImplProcess = null;
|
|
571
|
+
let success = completionSignalSeen || code === 0;
|
|
572
|
+
let message = success
|
|
573
|
+
? (completionSignalSeen && code !== 0 ? '✅ Auto-implement complete (completion signal)' : '✅ Auto-implement complete')
|
|
574
|
+
: `❌ Agent exited (code: ${code})`;
|
|
575
|
+
let verificationSummary: any = null;
|
|
576
|
+
|
|
577
|
+
try { ctx.providerLoader.reload(); } catch { /* ignore */ }
|
|
578
|
+
|
|
579
|
+
if (provider.category === 'cli' && verification) {
|
|
580
|
+
sendAutoImplSSE(ctx, {
|
|
581
|
+
event: 'progress',
|
|
582
|
+
data: {
|
|
583
|
+
function: '_verify',
|
|
584
|
+
status: 'running',
|
|
585
|
+
message: 'Running exact post-patch verification...',
|
|
586
|
+
},
|
|
587
|
+
});
|
|
588
|
+
try {
|
|
589
|
+
verificationSummary = await runCliAutoImplVerification(ctx, type, verification);
|
|
590
|
+
sendAutoImplSSE(ctx, { event: 'verification', data: verificationSummary });
|
|
591
|
+
success = verificationSummary.pass;
|
|
592
|
+
message = verificationSummary.pass
|
|
593
|
+
? `✅ Auto-implement complete (${verificationSummary.mode})`
|
|
594
|
+
: `❌ Post-patch verification failed (${verificationSummary.mode}): ${verificationSummary.failures.join('; ') || 'unknown failure'}`;
|
|
595
|
+
} catch (error: any) {
|
|
596
|
+
success = false;
|
|
597
|
+
message = `❌ Post-patch verification error: ${error?.message || error}`;
|
|
598
|
+
sendAutoImplSSE(ctx, {
|
|
599
|
+
event: 'verification',
|
|
600
|
+
data: { pass: false, error: error?.message || String(error) },
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
ctx.autoImplStatus.running = false;
|
|
606
|
+
sendAutoImplSSE(ctx, {
|
|
607
|
+
event: 'complete',
|
|
608
|
+
data: {
|
|
609
|
+
success,
|
|
610
|
+
exitCode: code,
|
|
611
|
+
functions,
|
|
612
|
+
message,
|
|
613
|
+
verification: verificationSummary,
|
|
614
|
+
},
|
|
615
|
+
});
|
|
616
|
+
try { fs.unlinkSync(promptFile); } catch { /* ignore */ }
|
|
617
|
+
ctx.log(`Auto-implement ${success ? 'completed' : 'failed'}: ${type} (exit: ${code})${verificationSummary ? ` verify=${verificationSummary.pass ? 'pass' : 'fail'}` : ''}`);
|
|
618
|
+
};
|
|
619
|
+
|
|
464
620
|
if (isPty) {
|
|
465
621
|
child.onData((data: string) => {
|
|
466
622
|
stdout += data;
|
|
@@ -472,15 +628,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
472
628
|
sendAutoImplSSE(ctx, { event: 'output', data: { chunk: data, stream: 'stdout' } });
|
|
473
629
|
});
|
|
474
630
|
child.onExit(({ exitCode: code }: { exitCode: number }) => {
|
|
475
|
-
|
|
476
|
-
ctx.autoImplStatus.running = false;
|
|
477
|
-
const success = code === 0;
|
|
478
|
-
sendAutoImplSSE(ctx, {
|
|
479
|
-
event: 'complete',
|
|
480
|
-
data: { success, exitCode: code, functions, message: success ? '✅ Auto-implement complete' : `❌ Agent exited (code: ${code})` },
|
|
481
|
-
});
|
|
482
|
-
try { ctx.providerLoader.reload(); } catch { /* ignore */ }
|
|
483
|
-
try { fs.unlinkSync(promptFile); } catch { /* ignore */ }
|
|
631
|
+
void finalizeCliAutoImpl(code);
|
|
484
632
|
});
|
|
485
633
|
} else {
|
|
486
634
|
child.stdout?.on('data', (d: Buffer) => {
|
|
@@ -497,21 +645,7 @@ export async function handleAutoImplement(ctx: DevServerContext, type: string, r
|
|
|
497
645
|
sendAutoImplSSE(ctx, { event: 'output', data: { chunk, stream: 'stderr' } });
|
|
498
646
|
});
|
|
499
647
|
child.on('exit', (code: number) => {
|
|
500
|
-
|
|
501
|
-
ctx.autoImplStatus.running = false;
|
|
502
|
-
const success = code === 0;
|
|
503
|
-
sendAutoImplSSE(ctx, {
|
|
504
|
-
event: 'complete',
|
|
505
|
-
data: {
|
|
506
|
-
success,
|
|
507
|
-
exitCode: code,
|
|
508
|
-
functions,
|
|
509
|
-
message: success ? '✅ Auto-implement complete' : `❌ Agent exited (code: ${code})`,
|
|
510
|
-
},
|
|
511
|
-
});
|
|
512
|
-
try { ctx.providerLoader.reload(); } catch { /* ignore */ }
|
|
513
|
-
try { fs.unlinkSync(promptFile); } catch { /* ignore */ }
|
|
514
|
-
ctx.log(`Auto-implement ${success ? 'completed' : 'failed'}: ${type} (exit: ${code})`);
|
|
648
|
+
void finalizeCliAutoImpl(code);
|
|
515
649
|
});
|
|
516
650
|
}
|
|
517
651
|
ctx.json(res, 202, {
|
|
@@ -538,9 +672,10 @@ export function buildAutoImplPrompt(ctx: DevServerContext,
|
|
|
538
672
|
referenceScripts: Record<string, string>,
|
|
539
673
|
userComment?: string,
|
|
540
674
|
referenceType?: string | null,
|
|
675
|
+
verification?: CliExerciseVerification,
|
|
541
676
|
): string {
|
|
542
677
|
if (provider.category === 'cli') {
|
|
543
|
-
return buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, referenceScripts, userComment, referenceType);
|
|
678
|
+
return buildCliAutoImplPrompt(ctx, type, provider, providerDir, functions, referenceScripts, userComment, referenceType, verification);
|
|
544
679
|
}
|
|
545
680
|
|
|
546
681
|
const lines: string[] = [];
|
|
@@ -833,8 +968,74 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
833
968
|
referenceScripts: Record<string, string>,
|
|
834
969
|
userComment?: string,
|
|
835
970
|
referenceType?: string | null,
|
|
971
|
+
verification?: CliExerciseVerification,
|
|
836
972
|
): string {
|
|
837
973
|
const lines: string[] = [];
|
|
974
|
+
const defaultExercisePayload = {
|
|
975
|
+
type,
|
|
976
|
+
workingDir: providerDir,
|
|
977
|
+
freshSession: true,
|
|
978
|
+
autoLaunch: true,
|
|
979
|
+
autoResolveApprovals: true,
|
|
980
|
+
approvalButtonIndex: 0,
|
|
981
|
+
timeoutMs: 45000,
|
|
982
|
+
traceLimit: 200,
|
|
983
|
+
text: 'Create a file at tmp/adhdev_provider_fix_test.py that prints the current working directory and the squares of 1 through 5, then run python3 tmp/adhdev_provider_fix_test.py and tell me the exact output.',
|
|
984
|
+
};
|
|
985
|
+
const exercisePayload = {
|
|
986
|
+
...defaultExercisePayload,
|
|
987
|
+
...(verification?.request || {}),
|
|
988
|
+
type,
|
|
989
|
+
workingDir: providerDir,
|
|
990
|
+
};
|
|
991
|
+
const exerciseJson = JSON.stringify(exercisePayload).replace(/\\/g, '\\\\').replace(/'/g, `'\\''`);
|
|
992
|
+
const verificationInspectFields = verification?.inspectFields?.length
|
|
993
|
+
? verification.inspectFields
|
|
994
|
+
: [
|
|
995
|
+
'debug.messages',
|
|
996
|
+
'trace.entries[].payload.parsedLastAssistant',
|
|
997
|
+
'trace.entries[].payload.lastAssistant',
|
|
998
|
+
];
|
|
999
|
+
const verificationMustContainAny = verification?.mustContainAny || [];
|
|
1000
|
+
const verificationMustNotContainAny = verification?.mustNotContainAny || [];
|
|
1001
|
+
const verificationMustMatchAny = verification?.mustMatchAny || [];
|
|
1002
|
+
const verificationMustNotMatchAny = verification?.mustNotMatchAny || [];
|
|
1003
|
+
const verificationLastAssistantMustContainAny = verification?.lastAssistantMustContainAny || [];
|
|
1004
|
+
const verificationLastAssistantMustNotContainAny = verification?.lastAssistantMustNotContainAny || [];
|
|
1005
|
+
const verificationLastAssistantMustMatchAny = verification?.lastAssistantMustMatchAny || [];
|
|
1006
|
+
const verificationLastAssistantMustNotMatchAny = verification?.lastAssistantMustNotMatchAny || [];
|
|
1007
|
+
const quotedMustContain = verificationMustContainAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1008
|
+
const quotedMustNotContain = verificationMustNotContainAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1009
|
+
const quotedMustMatch = verificationMustMatchAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1010
|
+
const quotedMustNotMatch = verificationMustNotMatchAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1011
|
+
const quotedLastAssistantMustContain = verificationLastAssistantMustContainAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1012
|
+
const quotedLastAssistantMustNotContain = verificationLastAssistantMustNotContainAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1013
|
+
const quotedLastAssistantMustMatch = verificationLastAssistantMustMatchAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1014
|
+
const quotedLastAssistantMustNotMatch = verificationLastAssistantMustNotMatchAny.map((value) => JSON.stringify(value)).join(', ');
|
|
1015
|
+
const fixtureName = verification?.fixtureName || `${type}-provider-fix`;
|
|
1016
|
+
const fixtureNames = Array.isArray(verification?.fixtureNames)
|
|
1017
|
+
? verification!.fixtureNames.map((value) => String(value || '').trim()).filter(Boolean)
|
|
1018
|
+
: [];
|
|
1019
|
+
const fixtureCaptureJson = JSON.stringify({
|
|
1020
|
+
type,
|
|
1021
|
+
name: fixtureName,
|
|
1022
|
+
request: exercisePayload,
|
|
1023
|
+
assertions: {
|
|
1024
|
+
mustContainAny: verificationMustContainAny,
|
|
1025
|
+
mustNotContainAny: verificationMustNotContainAny,
|
|
1026
|
+
mustMatchAny: verificationMustMatchAny,
|
|
1027
|
+
mustNotMatchAny: verificationMustNotMatchAny,
|
|
1028
|
+
lastAssistantMustContainAny: verificationLastAssistantMustContainAny,
|
|
1029
|
+
lastAssistantMustNotContainAny: verificationLastAssistantMustNotContainAny,
|
|
1030
|
+
lastAssistantMustMatchAny: verificationLastAssistantMustMatchAny,
|
|
1031
|
+
lastAssistantMustNotMatchAny: verificationLastAssistantMustNotMatchAny,
|
|
1032
|
+
requireNotTimedOut: true,
|
|
1033
|
+
},
|
|
1034
|
+
}).replace(/\\/g, '\\\\').replace(/'/g, `'\\''`);
|
|
1035
|
+
const fixtureReplayJson = JSON.stringify({
|
|
1036
|
+
type,
|
|
1037
|
+
name: fixtureName,
|
|
1038
|
+
}).replace(/\\/g, '\\\\').replace(/'/g, `'\\''`);
|
|
838
1039
|
|
|
839
1040
|
lines.push('You are implementing PTY parsing scripts for a CLI provider.');
|
|
840
1041
|
lines.push('Be concise. Do NOT explain your reasoning. Edit files directly and verify with the local DevServer.');
|
|
@@ -934,7 +1135,7 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
934
1135
|
};
|
|
935
1136
|
|
|
936
1137
|
const providerGuide = loadGuide('PROVIDER_GUIDE.md');
|
|
937
|
-
if (providerGuide) {
|
|
1138
|
+
if (providerGuide && provider.category !== 'cli') {
|
|
938
1139
|
lines.push('## Documentation: PROVIDER_GUIDE.md');
|
|
939
1140
|
lines.push('```markdown');
|
|
940
1141
|
lines.push(providerGuide);
|
|
@@ -978,6 +1179,11 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
978
1179
|
lines.push('13. After the first successful live repro, stop broad diagnosis. Edit the scripts, reload, and verify. Do not burn tokens on repeated re-inspection without code changes.');
|
|
979
1180
|
lines.push('14. If the visible current screen is clean and sufficient, do NOT fall back to complex buffer heuristics. Simpler current-screen parsing is preferred.');
|
|
980
1181
|
lines.push('15. Before changing parser logic, verify whether `provider.json` submit/approval behavior (`sendDelayMs`, `approvalKeys`, submit strategy) is the simpler and more correct fix.');
|
|
1182
|
+
lines.push('16. Do NOT patch transcript bugs by piling up one-off literal string exceptions (`includes("foo")`, `=== "bar"`, ad hoc allowlists/denylists) for every observed variant. Model the UI as PATTERN FAMILIES using reusable regex classifiers and normalization first.');
|
|
1183
|
+
lines.push('17. If you find yourself adding a second or third near-duplicate literal check for spinner words, tool headers, approval prompts, footer chrome, or OSC residue, STOP and replace them with a broader regex or helper classifier.');
|
|
1184
|
+
lines.push('18. Prefer a small number of named classifiers such as "status line", "tool header", "tool detail", "footer chrome", "approval cue", "prompt line", and "OSC residue" over a long chain of unrelated string checks.');
|
|
1185
|
+
lines.push('19. Literal string checks are allowed only for stable proper nouns or exact product chrome that cannot be expressed safely as a broader pattern. Everything else should generalize.');
|
|
1186
|
+
lines.push('20. When a bug comes from noisy PTY text, first normalize and classify the line family; do NOT just append another special-case substring to the parser.');
|
|
981
1187
|
lines.push('');
|
|
982
1188
|
|
|
983
1189
|
lines.push('## Task');
|
|
@@ -987,35 +1193,154 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
987
1193
|
lines.push('## Verification API');
|
|
988
1194
|
lines.push('Use the DevServer CLI debug endpoints, not DOM/CDP routes.');
|
|
989
1195
|
lines.push('');
|
|
990
|
-
lines.push('### 1.
|
|
1196
|
+
lines.push('### 1. Preferred: run a full autonomous repro');
|
|
1197
|
+
lines.push('Use the exercise endpoint first. It launches a fresh CLI session, sends the repro prompt, auto-resolves approvals, waits for the session to settle, and returns the final debug + trace payload in one response.');
|
|
991
1198
|
lines.push('```bash');
|
|
992
|
-
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/
|
|
1199
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/exercise \\`);
|
|
993
1200
|
lines.push(' -H "Content-Type: application/json" \\');
|
|
994
|
-
lines.push(` -d '
|
|
1201
|
+
lines.push(` -d '${exerciseJson}'`);
|
|
995
1202
|
lines.push('```');
|
|
996
1203
|
lines.push('');
|
|
1204
|
+
if (verification?.description) {
|
|
1205
|
+
lines.push('Verification intent:');
|
|
1206
|
+
lines.push(verification.description);
|
|
1207
|
+
lines.push('');
|
|
1208
|
+
}
|
|
1209
|
+
lines.push('Read the JSON response carefully. It already includes:');
|
|
1210
|
+
lines.push('1. `instanceId`');
|
|
1211
|
+
lines.push('2. `statusesSeen` and `approvalsResolved`');
|
|
1212
|
+
lines.push('3. `debug` for the final settled state');
|
|
1213
|
+
lines.push('4. `trace.entries` for the repro turn');
|
|
1214
|
+
lines.push('');
|
|
1215
|
+
lines.push('Save the response to a temp file and inspect the exact parsed transcript fields before editing:');
|
|
1216
|
+
lines.push('```bash');
|
|
1217
|
+
lines.push(`EXERCISE_JSON=$(mktemp)`);
|
|
1218
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/exercise \\`);
|
|
1219
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1220
|
+
lines.push(` -d '${exerciseJson}' > "$EXERCISE_JSON"`);
|
|
1221
|
+
lines.push(`jq '{timedOut,statusesSeen,approvalsResolved,inspect:{${verificationInspectFields.map((field, index) => `f${index + 1}: .${field}`).join(', ')}}}' "$EXERCISE_JSON"`);
|
|
1222
|
+
lines.push('```');
|
|
1223
|
+
lines.push('');
|
|
1224
|
+
if (
|
|
1225
|
+
verificationMustContainAny.length > 0
|
|
1226
|
+
|| verificationMustNotContainAny.length > 0
|
|
1227
|
+
|| verificationMustMatchAny.length > 0
|
|
1228
|
+
|| verificationMustNotMatchAny.length > 0
|
|
1229
|
+
|| verificationLastAssistantMustContainAny.length > 0
|
|
1230
|
+
|| verificationLastAssistantMustNotContainAny.length > 0
|
|
1231
|
+
|| verificationLastAssistantMustMatchAny.length > 0
|
|
1232
|
+
|| verificationLastAssistantMustNotMatchAny.length > 0
|
|
1233
|
+
) {
|
|
1234
|
+
lines.push('The exact repro below is mandatory. Do NOT declare success unless these transcript assertions pass on the exercise JSON from the PATCHED provider.');
|
|
1235
|
+
lines.push('```bash');
|
|
1236
|
+
if (verificationMustContainAny.length > 0) {
|
|
1237
|
+
lines.push(`node -e 'const fs=require(\"fs\");const text=fs.readFileSync(process.argv[1],\"utf8\");const required=[${quotedMustContain}];const missing=required.filter(v=>!text.includes(v));if(missing.length){console.error(\"Missing required substrings:\\n\"+missing.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1238
|
+
}
|
|
1239
|
+
if (verificationMustNotContainAny.length > 0) {
|
|
1240
|
+
lines.push(`node -e 'const fs=require(\"fs\");const text=fs.readFileSync(process.argv[1],\"utf8\");const banned=[${quotedMustNotContain}];const hits=banned.filter(v=>text.includes(v));if(hits.length){console.error(\"Found banned substrings:\\n\"+hits.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1241
|
+
}
|
|
1242
|
+
if (verificationMustMatchAny.length > 0) {
|
|
1243
|
+
lines.push(`node -e 'const fs=require(\"fs\");const text=fs.readFileSync(process.argv[1],\"utf8\");const required=[${quotedMustMatch}].map(v=>new RegExp(v,\"m\"));const missing=required.filter(v=>!v.test(text)).map(v=>String(v));if(missing.length){console.error(\"Missing required regex matches:\\n\"+missing.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1244
|
+
}
|
|
1245
|
+
if (verificationMustNotMatchAny.length > 0) {
|
|
1246
|
+
lines.push(`node -e 'const fs=require(\"fs\");const text=fs.readFileSync(process.argv[1],\"utf8\");const banned=[${quotedMustNotMatch}].map(v=>new RegExp(v,\"m\"));const hits=banned.filter(v=>v.test(text)).map(v=>String(v));if(hits.length){console.error(\"Found banned regex matches:\\n\"+hits.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1247
|
+
}
|
|
1248
|
+
if (verificationLastAssistantMustContainAny.length > 0) {
|
|
1249
|
+
lines.push(`node -e 'const fs=require(\"fs\");const payload=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const text=String(payload.lastAssistant||\"\");const required=[${quotedLastAssistantMustContain}];const missing=required.filter(v=>!text.includes(v));if(missing.length){console.error(\"Missing required lastAssistant substrings:\\n\"+missing.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1250
|
+
}
|
|
1251
|
+
if (verificationLastAssistantMustNotContainAny.length > 0) {
|
|
1252
|
+
lines.push(`node -e 'const fs=require(\"fs\");const payload=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const text=String(payload.lastAssistant||\"\");const banned=[${quotedLastAssistantMustNotContain}];const hits=banned.filter(v=>text.includes(v));if(hits.length){console.error(\"Found banned lastAssistant substrings:\\n\"+hits.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1253
|
+
}
|
|
1254
|
+
if (verificationLastAssistantMustMatchAny.length > 0) {
|
|
1255
|
+
lines.push(`node -e 'const fs=require(\"fs\");const payload=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const text=String(payload.lastAssistant||\"\");const required=[${quotedLastAssistantMustMatch}].map(v=>new RegExp(v,\"m\"));const missing=required.filter(v=>!v.test(text)).map(v=>String(v));if(missing.length){console.error(\"Missing required lastAssistant regex matches:\\n\"+missing.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1256
|
+
}
|
|
1257
|
+
if (verificationLastAssistantMustNotMatchAny.length > 0) {
|
|
1258
|
+
lines.push(`node -e 'const fs=require(\"fs\");const payload=JSON.parse(fs.readFileSync(process.argv[1],\"utf8\"));const text=String(payload.lastAssistant||\"\");const banned=[${quotedLastAssistantMustNotMatch}].map(v=>new RegExp(v,\"m\"));const hits=banned.filter(v=>v.test(text)).map(v=>String(v));if(hits.length){console.error(\"Found banned lastAssistant regex matches:\\n\"+hits.join(\"\\n\"));process.exit(1);}' "$EXERCISE_JSON"`);
|
|
1259
|
+
}
|
|
1260
|
+
lines.push('```');
|
|
1261
|
+
lines.push('');
|
|
1262
|
+
}
|
|
1263
|
+
lines.push('If you need a manual follow-up repro after patching, use the SAME endpoint again with the SAME prompt and compare the new trace to the previous one.');
|
|
1264
|
+
lines.push('');
|
|
1265
|
+
lines.push('### 1b. Persist or replay the exact repro as a reusable fixture');
|
|
1266
|
+
if (fixtureNames.length > 0) {
|
|
1267
|
+
lines.push(`Replay this exact fixture suite before editing, and replay the SAME suite again after patching. Do not declare success unless EVERY fixture passes: ${fixtureNames.map((name) => `\`${name}\``).join(', ')}.`);
|
|
1268
|
+
for (const name of fixtureNames) {
|
|
1269
|
+
const replayJson = JSON.stringify({ type, name }).replace(/\\/g, '\\\\').replace(/'/g, `'\\''`);
|
|
1270
|
+
lines.push('```bash');
|
|
1271
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/replay \\`);
|
|
1272
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1273
|
+
lines.push(` -d '${replayJson}'`);
|
|
1274
|
+
lines.push('```');
|
|
1275
|
+
lines.push('');
|
|
1276
|
+
}
|
|
1277
|
+
lines.push('Do not create new fixtures unless one of the listed fixtures is missing or stale.');
|
|
1278
|
+
} else if (verification?.fixtureName) {
|
|
1279
|
+
lines.push(`Replay the EXISTING saved fixture \`${fixtureName}\` before editing, and replay the SAME fixture again after patching. Do not declare success unless that exact fixture passes.`);
|
|
1280
|
+
lines.push('```bash');
|
|
1281
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/replay \\`);
|
|
1282
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1283
|
+
lines.push(` -d '${fixtureReplayJson}'`);
|
|
1284
|
+
lines.push('```');
|
|
1285
|
+
lines.push('');
|
|
1286
|
+
lines.push('Only if the named fixture is missing or outdated should you recapture it. Prefer replaying the existing failing fixture over creating a new one.');
|
|
1287
|
+
lines.push('```bash');
|
|
1288
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/capture \\`);
|
|
1289
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1290
|
+
lines.push(` -d '${fixtureCaptureJson}'`);
|
|
1291
|
+
lines.push('```');
|
|
1292
|
+
} else {
|
|
1293
|
+
lines.push('Capture the exact exercise once before editing. After patching, replay THIS fixture and do not declare success unless replay passes.');
|
|
1294
|
+
lines.push('```bash');
|
|
1295
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/capture \\`);
|
|
1296
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1297
|
+
lines.push(` -d '${fixtureCaptureJson}'`);
|
|
1298
|
+
lines.push('');
|
|
1299
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/fixture/replay \\`);
|
|
1300
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1301
|
+
lines.push(` -d '${fixtureReplayJson}'`);
|
|
1302
|
+
lines.push('```');
|
|
1303
|
+
}
|
|
1304
|
+
lines.push('');
|
|
1305
|
+
lines.push('The capture endpoint saves the exact request, initial result, and transcript assertions into the provider directory. The replay endpoint reruns the SAME exercise against your patched scripts and returns pass/fail.');
|
|
1306
|
+
lines.push('');
|
|
997
1307
|
lines.push('### 2. Inspect parsed + raw adapter state');
|
|
998
1308
|
lines.push('```bash');
|
|
1309
|
+
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/launch \\`);
|
|
1310
|
+
lines.push(' -H "Content-Type: application/json" \\');
|
|
1311
|
+
lines.push(` -d '{"type":"${type}","workingDir":"${providerDir.replace(/\\/g, '\\\\')}"}'`);
|
|
999
1312
|
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/debug/${type}`);
|
|
1313
|
+
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/trace/${type}`);
|
|
1000
1314
|
lines.push(`curl -sS http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/status`);
|
|
1001
1315
|
lines.push('```');
|
|
1002
1316
|
lines.push('');
|
|
1317
|
+
lines.push('The CLI trace endpoint is the primary debugging source. Read it BEFORE editing any parser code.');
|
|
1318
|
+
lines.push('Use the trace timeline to find the latest `settled` or `commit_transcript` frame for the repro turn and inspect these fields first:');
|
|
1319
|
+
lines.push('1. `payload.screenText`');
|
|
1320
|
+
lines.push('2. `payload.detectStatus` and `payload.parsedStatus`');
|
|
1321
|
+
lines.push('3. `payload.parsedLastAssistant`');
|
|
1322
|
+
lines.push('4. `payload.approval` / `payload.parsedActiveModal`');
|
|
1323
|
+
lines.push('5. `payload.rawPreview` only when control-sequence residue matters');
|
|
1324
|
+
lines.push('');
|
|
1003
1325
|
lines.push('The debug payload should be read in this priority order:');
|
|
1004
1326
|
lines.push('1. `screenText` / current visible state');
|
|
1005
1327
|
lines.push('2. parsed `status`, `messages`, `activeModal`');
|
|
1006
1328
|
lines.push('3. `rawBuffer` only for style/control-sequence cues');
|
|
1007
1329
|
lines.push('4. `buffer` only when the current screen is insufficient');
|
|
1008
1330
|
lines.push('');
|
|
1009
|
-
lines.push('
|
|
1331
|
+
lines.push('If the bug is transcript corruption, quote the exact bad `parsedLastAssistant` or bad committed assistant message from the trace and patch against that concrete failure.');
|
|
1332
|
+
lines.push('Do NOT guess based only on the final chat bubble or a truncated UI preview.');
|
|
1333
|
+
lines.push('');
|
|
1334
|
+
lines.push('Extract the current `instanceId` from the exercise, launch, or status response and keep using it below.');
|
|
1010
1335
|
lines.push('');
|
|
1011
|
-
lines.push('### 3.
|
|
1336
|
+
lines.push('### 3. Manual fallback only: send a realistic approval-triggering prompt');
|
|
1012
1337
|
lines.push('```bash');
|
|
1013
1338
|
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/send \\`);
|
|
1014
1339
|
lines.push(' -H "Content-Type: application/json" \\');
|
|
1015
1340
|
lines.push(` -d '{"type":"${type}","instanceId":"<INSTANCE_ID>","text":"Create a file at tmp/adhdev_provider_fix_test.py that prints the current working directory and the squares of 1 through 5, then run python3 tmp/adhdev_provider_fix_test.py and tell me the exact output."}'`);
|
|
1016
1341
|
lines.push('```');
|
|
1017
1342
|
lines.push('');
|
|
1018
|
-
lines.push('### 4.
|
|
1343
|
+
lines.push('### 4. Manual fallback only: if approval appears, resolve it until the CLI reaches idle');
|
|
1019
1344
|
lines.push('```bash');
|
|
1020
1345
|
lines.push(`curl -sS -X POST http://127.0.0.1:${DEV_SERVER_PORT}/api/cli/resolve \\`);
|
|
1021
1346
|
lines.push(' -H "Content-Type: application/json" \\');
|
|
@@ -1025,10 +1350,14 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
1025
1350
|
lines.push(` -d '{"type":"${type}","instanceId":"<INSTANCE_ID>","keys":"1"}'`);
|
|
1026
1351
|
lines.push('```');
|
|
1027
1352
|
lines.push('');
|
|
1028
|
-
lines.push('Use `resolve` when the parsed modal buttons are correct. Use `raw` when the CLI expects a literal keystroke like `1`, `y`, or Enter. Repeat until idle.');
|
|
1353
|
+
lines.push('Use `resolve` when the parsed modal buttons are correct. Use `raw` when the CLI expects a literal keystroke like `1`, `y`, or Enter. Repeat until idle. Prefer the exercise endpoint instead of doing this by hand.');
|
|
1029
1354
|
lines.push('');
|
|
1030
1355
|
lines.push('### Patch Discipline');
|
|
1031
1356
|
lines.push('Once the repro is confirmed, immediately edit the target files. Avoid loops where you keep re-reading long files or re-running the same debug commands without changing code.');
|
|
1357
|
+
lines.push('For CLI transcript bugs, reproduce once with the exercise endpoint, inspect the returned trace once, patch immediately, then re-run the SAME exercise and compare the new `commit_transcript` frame.');
|
|
1358
|
+
lines.push('If the patched run still fails the exact required/banned substring checks above, the task is NOT complete even if the CLI exits normally.');
|
|
1359
|
+
lines.push('When you patch, write down the pattern family you are fixing: e.g. spinner/status, tool block, approval modal, footer chrome, OSC/control residue, prompt echo, or long-output continuation. Patch that family once instead of adding case-by-case literals.');
|
|
1360
|
+
lines.push('Bad fix pattern: add another `includes("Drizzling")` or `includes("Show more (")` check. Good fix pattern: broaden the regex/helper that recognizes spinner words, collapsed tool overflow lines, or footer chrome as a family.');
|
|
1032
1361
|
lines.push('');
|
|
1033
1362
|
lines.push('### 5. Verify the side effects outside the CLI');
|
|
1034
1363
|
lines.push('```bash');
|
|
@@ -1054,6 +1383,8 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
1054
1383
|
lines.push('7. Re-run the debug endpoints after edits. Do NOT finish until the parsed result looks correct.');
|
|
1055
1384
|
lines.push('8. Confirm the parser still works after a redraw or scroll change without duplicating transcript history.');
|
|
1056
1385
|
lines.push('9. Confirm the implementation prefers current-screen signals over stale history when both are present.');
|
|
1386
|
+
lines.push('10. For transcript-cleanliness bugs, confirm the latest `commit_transcript` trace frame no longer contains tool headers, approval prompts, OSC residue like `0;`, or footer chrome unless they are truly user-facing answer content.');
|
|
1387
|
+
lines.push('11. Confirm the implementation uses generalized pattern classifiers or regexes for noisy UI families instead of accumulating one-off literal string exceptions for each observed sample.');
|
|
1057
1388
|
lines.push('');
|
|
1058
1389
|
|
|
1059
1390
|
if (userComment) {
|
|
@@ -1064,12 +1395,13 @@ export function buildCliAutoImplPrompt(ctx: DevServerContext,
|
|
|
1064
1395
|
lines.push('');
|
|
1065
1396
|
}
|
|
1066
1397
|
|
|
1067
|
-
lines.push('Start NOW. Launch the CLI, inspect PTY state, edit the scripts, and verify via the CLI debug endpoints.');
|
|
1398
|
+
lines.push('Start NOW. Launch the CLI, inspect the trace and PTY state, edit the scripts, and verify via the CLI debug + trace endpoints.');
|
|
1068
1399
|
|
|
1069
1400
|
return lines.join('\n');
|
|
1070
1401
|
}
|
|
1071
1402
|
|
|
1072
1403
|
export function handleAutoImplSSE(ctx: DevServerContext, type: string, req: http.IncomingMessage, res: http.ServerResponse): void {
|
|
1404
|
+
clearStaleAutoImplState(ctx, 'SSE connection opened');
|
|
1073
1405
|
res.writeHead(200, {
|
|
1074
1406
|
'Content-Type': 'text/event-stream',
|
|
1075
1407
|
'Cache-Control': 'no-cache',
|
|
@@ -1090,6 +1422,7 @@ export function handleAutoImplSSE(ctx: DevServerContext, type: string, req: http
|
|
|
1090
1422
|
}
|
|
1091
1423
|
|
|
1092
1424
|
export function handleAutoImplCancel(ctx: DevServerContext, _type: string, _req: http.IncomingMessage, res: http.ServerResponse): void {
|
|
1425
|
+
clearStaleAutoImplState(ctx, 'cancel request');
|
|
1093
1426
|
if (ctx.autoImplProcess) {
|
|
1094
1427
|
ctx.autoImplProcess.kill('SIGTERM');
|
|
1095
1428
|
setTimeout(() => { if (ctx.autoImplProcess) ctx.autoImplProcess.kill('SIGKILL'); }, 3000);
|