imprint-mcp 0.7.0 → 0.7.2
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 +66 -2
- package/package.json +1 -1
- package/prompts/auth-compile-agent.md +173 -22
- package/prompts/compile-agent.md +45 -8
- package/prompts/component-recovery.md +40 -0
- package/prompts/master-teach-decision.md +28 -3
- package/prompts/master-teach-focused-planner.md +18 -5
- package/src/cli.ts +26 -18
- package/src/imprint/agent-tool.ts +15 -0
- package/src/imprint/agent.ts +2 -11
- package/src/imprint/audit.ts +35 -13
- package/src/imprint/auth-browser-discovery.ts +276 -0
- package/src/imprint/auth-compile-agent.ts +513 -224
- package/src/imprint/auth-compile-tools.ts +213 -25
- package/src/imprint/auth-discovery-evidence.ts +81 -0
- package/src/imprint/auth-plan.ts +28 -0
- package/src/imprint/auth-verifier.ts +234 -20
- package/src/imprint/authentication-interaction.ts +137 -0
- package/src/imprint/backend-ladder.ts +271 -43
- package/src/imprint/build-plan.ts +2 -22
- package/src/imprint/cdp-browser-fetch.ts +242 -60
- package/src/imprint/chromium.ts +93 -29
- package/src/imprint/claude-cli-compile.ts +2 -0
- package/src/imprint/codex-cli-compile.ts +99 -46
- package/src/imprint/compile-agent-types.ts +9 -5
- package/src/imprint/compile-agent.ts +33 -0
- package/src/imprint/compile-tool-executor.ts +783 -0
- package/src/imprint/compile-tools.ts +42 -9
- package/src/imprint/compile-verification.ts +2 -0
- package/src/imprint/compile.ts +3 -0
- package/src/imprint/compiler-log.ts +16 -0
- package/src/imprint/component-recovery.ts +120 -0
- package/src/imprint/cookie-jar.ts +117 -7
- package/src/imprint/credential-extract.ts +9 -2
- package/src/imprint/credential-lock.ts +147 -0
- package/src/imprint/credential-observation.ts +261 -0
- package/src/imprint/credential-store.ts +284 -44
- package/src/imprint/live-component-recovery.ts +73 -0
- package/src/imprint/llm.ts +4 -3
- package/src/imprint/log.ts +16 -1
- package/src/imprint/login.ts +9 -0
- package/src/imprint/master-teach-agent-contracts.ts +40 -3
- package/src/imprint/master-teach-agents.ts +229 -14
- package/src/imprint/master-teach-checks.ts +8 -1
- package/src/imprint/master-teach-controller.ts +681 -138
- package/src/imprint/master-teach-prompt-projections.ts +2 -0
- package/src/imprint/mcp-cdp-sessions.ts +7 -2
- package/src/imprint/mcp-compile-server.ts +62 -850
- package/src/imprint/mcp-server.ts +667 -91
- package/src/imprint/mcp-session-recovery.ts +167 -0
- package/src/imprint/native-teach-agents.ts +232 -108
- package/src/imprint/runtime.ts +416 -47
- package/src/imprint/stealth-chromium.ts +67 -7
- package/src/imprint/teach-browser-resources.ts +88 -0
- package/src/imprint/teach-stage-progress.ts +38 -0
- package/src/imprint/terminal-user-prompt.ts +42 -0
- package/src/imprint/types.ts +51 -18
- package/src/imprint/user-interaction.ts +836 -0
package/src/imprint/chromium.ts
CHANGED
|
@@ -6,12 +6,15 @@ import { type ChildProcess, spawnSync as nodeSpawnSync, spawn } from 'node:child
|
|
|
6
6
|
import {
|
|
7
7
|
closeSync,
|
|
8
8
|
existsSync,
|
|
9
|
+
mkdtempSync,
|
|
9
10
|
openSync,
|
|
10
11
|
readFileSync,
|
|
11
12
|
readdirSync,
|
|
13
|
+
rmSync,
|
|
12
14
|
statSync,
|
|
13
15
|
unlinkSync,
|
|
14
16
|
} from 'node:fs';
|
|
17
|
+
import { rm } from 'node:fs/promises';
|
|
15
18
|
import { createRequire } from 'node:module';
|
|
16
19
|
import { createServer } from 'node:net';
|
|
17
20
|
import { homedir, tmpdir } from 'node:os';
|
|
@@ -26,6 +29,7 @@ import { isDebug } from './log.ts';
|
|
|
26
29
|
* Chrome-launch module, a dependency leaf) rather than in backend-ladder so the
|
|
27
30
|
* ladder→cdp-browser-fetch import does not form a cycle. */
|
|
28
31
|
const chromeProcessPids = new Set<number>();
|
|
32
|
+
const chromeTemporaryProfiles = new Map<number, string>();
|
|
29
33
|
|
|
30
34
|
/** Register a Chrome PID for exit-time cleanup. Called on launch. */
|
|
31
35
|
export function __registerChromePid(pid: number): void {
|
|
@@ -47,8 +51,16 @@ process.on('exit', () => {
|
|
|
47
51
|
for (const pid of chromeProcessPids) {
|
|
48
52
|
try {
|
|
49
53
|
process.kill(pid, 'SIGKILL');
|
|
50
|
-
} catch {
|
|
51
|
-
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if ((error as NodeJS.ErrnoException).code !== 'ESRCH') continue;
|
|
56
|
+
}
|
|
57
|
+
const profile = chromeTemporaryProfiles.get(pid);
|
|
58
|
+
if (profile) {
|
|
59
|
+
try {
|
|
60
|
+
rmSync(profile, { recursive: true, force: true });
|
|
61
|
+
} catch {
|
|
62
|
+
// A forced host exit cannot await cleanup; normal close reports errors.
|
|
63
|
+
}
|
|
52
64
|
}
|
|
53
65
|
}
|
|
54
66
|
});
|
|
@@ -556,17 +568,18 @@ async function pickFreePort(): Promise<number> {
|
|
|
556
568
|
});
|
|
557
569
|
}
|
|
558
570
|
|
|
559
|
-
async function waitForCdp(port: number, timeoutMs = 10_000): Promise<void> {
|
|
571
|
+
async function waitForCdp(port: number, timeoutMs = 10_000, signal?: AbortSignal): Promise<void> {
|
|
560
572
|
const deadline = Date.now() + timeoutMs;
|
|
561
573
|
let lastError: unknown;
|
|
562
574
|
while (Date.now() < deadline) {
|
|
575
|
+
signal?.throwIfAborted();
|
|
563
576
|
try {
|
|
564
|
-
const res = await fetch(`http://127.0.0.1:${port}/json/version
|
|
577
|
+
const res = await fetch(`http://127.0.0.1:${port}/json/version`, { signal });
|
|
565
578
|
if (res.ok) return;
|
|
566
579
|
} catch (err) {
|
|
567
580
|
lastError = err;
|
|
568
581
|
}
|
|
569
|
-
await sleep(100);
|
|
582
|
+
await sleep(100, undefined, { signal });
|
|
570
583
|
}
|
|
571
584
|
throw new Error(
|
|
572
585
|
`Chromium did not open CDP on port ${port} within ${timeoutMs}ms (${String(lastError)})`,
|
|
@@ -578,8 +591,9 @@ export async function launchChromium(opts: LaunchOptions = {}): Promise<Launched
|
|
|
578
591
|
log: (message) => process.stderr.write(`[imprint] ${message}\n`),
|
|
579
592
|
}).path;
|
|
580
593
|
const port = opts.port ?? (await pickFreePort());
|
|
594
|
+
const ownsProfile = opts.userDataDir === undefined;
|
|
581
595
|
const userDataDir =
|
|
582
|
-
opts.userDataDir ?? pathJoin(tmpdir(), `imprint-chrome-${
|
|
596
|
+
opts.userDataDir ?? mkdtempSync(pathJoin(tmpdir(), `imprint-chrome-${process.pid}-`));
|
|
583
597
|
|
|
584
598
|
const args = [
|
|
585
599
|
`--remote-debugging-port=${port}`,
|
|
@@ -614,38 +628,88 @@ export async function launchChromium(opts: LaunchOptions = {}): Promise<Launched
|
|
|
614
628
|
let xvfb: XvfbHandle | undefined;
|
|
615
629
|
let display = opts.display ?? process.env.DISPLAY;
|
|
616
630
|
if (process.platform === 'linux' && !opts.headless && !display) {
|
|
617
|
-
|
|
618
|
-
|
|
631
|
+
try {
|
|
632
|
+
xvfb = await startXvfb();
|
|
633
|
+
display = xvfb.display;
|
|
634
|
+
} catch (error) {
|
|
635
|
+
if (ownsProfile) await rm(userDataDir, { recursive: true, force: true });
|
|
636
|
+
throw error;
|
|
637
|
+
}
|
|
619
638
|
}
|
|
620
639
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
640
|
+
let child: ChildProcess;
|
|
641
|
+
try {
|
|
642
|
+
child = spawn(exe, args, {
|
|
643
|
+
stdio: ['ignore', 'ignore', 'pipe'],
|
|
644
|
+
detached: false,
|
|
645
|
+
env: display ? { ...process.env, DISPLAY: display } : process.env,
|
|
646
|
+
});
|
|
647
|
+
} catch (error) {
|
|
648
|
+
await xvfb?.close().catch(() => {});
|
|
649
|
+
if (ownsProfile) await rm(userDataDir, { recursive: true, force: true });
|
|
650
|
+
throw error;
|
|
651
|
+
}
|
|
652
|
+
if (child.pid !== undefined) {
|
|
653
|
+
__registerChromePid(child.pid);
|
|
654
|
+
if (ownsProfile) chromeTemporaryProfiles.set(child.pid, userDataDir);
|
|
655
|
+
}
|
|
626
656
|
|
|
627
657
|
// Chromium is noisy — only surface stderr under IMPRINT_DEBUG.
|
|
628
658
|
if (isDebug()) {
|
|
629
659
|
child.stderr?.on('data', (chunk) => process.stderr.write(chunk));
|
|
630
660
|
}
|
|
631
661
|
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
let
|
|
635
|
-
const
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
sleep(2000)
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
662
|
+
const startup = new AbortController();
|
|
663
|
+
let stopped = false;
|
|
664
|
+
let spawnError: Error | undefined;
|
|
665
|
+
const exit = Promise.withResolvers<void>();
|
|
666
|
+
let closing: Promise<void> | undefined;
|
|
667
|
+
const close = (): Promise<void> => {
|
|
668
|
+
closing ??= (async () => {
|
|
669
|
+
startup.abort(new Error('Chromium closed before CDP became ready'));
|
|
670
|
+
if (!stopped) {
|
|
671
|
+
child.kill('SIGTERM');
|
|
672
|
+
await Promise.race([exit.promise, sleep(2000)]);
|
|
673
|
+
if (!stopped) {
|
|
674
|
+
child.kill('SIGKILL');
|
|
675
|
+
await Promise.race([exit.promise, sleep(2000)]);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
await xvfb?.close().catch(() => {});
|
|
679
|
+
if (!stopped) throw new Error('Chromium did not exit; preserving its profile');
|
|
680
|
+
if (ownsProfile) await rm(userDataDir, { recursive: true, force: true });
|
|
681
|
+
if (child.pid !== undefined) {
|
|
682
|
+
chromeTemporaryProfiles.delete(child.pid);
|
|
683
|
+
__unregisterChromePid(child.pid);
|
|
684
|
+
}
|
|
685
|
+
})();
|
|
686
|
+
return closing;
|
|
648
687
|
};
|
|
688
|
+
child.once('error', (error) => {
|
|
689
|
+
spawnError = error;
|
|
690
|
+
if (child.pid === undefined) {
|
|
691
|
+
stopped = true;
|
|
692
|
+
exit.resolve();
|
|
693
|
+
}
|
|
694
|
+
startup.abort(error);
|
|
695
|
+
});
|
|
696
|
+
child.once('exit', () => {
|
|
697
|
+
stopped = true;
|
|
698
|
+
if (child.pid !== undefined) __unregisterChromePid(child.pid);
|
|
699
|
+
exit.resolve();
|
|
700
|
+
void close().catch((error) => {
|
|
701
|
+
process.stderr.write(`[imprint] browser cleanup: ${String(error)}\n`);
|
|
702
|
+
});
|
|
703
|
+
});
|
|
704
|
+
const ready = Promise.race([
|
|
705
|
+
waitForCdp(port, 10_000, startup.signal),
|
|
706
|
+
exit.promise.then(() => {
|
|
707
|
+
throw spawnError ?? new Error('Chromium exited before CDP became ready');
|
|
708
|
+
}),
|
|
709
|
+
]).catch(async (error) => {
|
|
710
|
+
await close();
|
|
711
|
+
throw error;
|
|
712
|
+
});
|
|
649
713
|
|
|
650
714
|
return { process: child, port, userDataDir, ready, close };
|
|
651
715
|
}
|
|
@@ -85,6 +85,8 @@ const MAX_VERIFICATION_CYCLES = 5;
|
|
|
85
85
|
/** Data-compile tools exposed to claude-cli. Keep this list in one exported
|
|
86
86
|
* place so tests catch prompt/tool drift before a real teach run does. */
|
|
87
87
|
export const DATA_COMPILE_TOOL_NAMES = [
|
|
88
|
+
'prompt_user',
|
|
89
|
+
'authenticate',
|
|
88
90
|
'read_session_summary',
|
|
89
91
|
'search_requests',
|
|
90
92
|
'read_request',
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import type { CallToolResult, Tool } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
+
import { AuthToolPlanSchema } from './auth-plan.ts';
|
|
4
|
+
import { createCompileToolExecutor } from './compile-tool-executor.ts';
|
|
3
5
|
import { currentNativeTeachAgents } from './native-teach-agents.ts';
|
|
4
6
|
import { SHARED_RESEARCH_INSTRUCTIONS } from './teach-research-memory.ts';
|
|
5
7
|
/**
|
|
@@ -12,17 +14,14 @@ import { SHARED_RESEARCH_INSTRUCTIONS } from './teach-research-memory.ts';
|
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
import type { ChildProcess } from 'node:child_process';
|
|
15
|
-
import {
|
|
16
|
-
appendFileSync,
|
|
17
|
-
existsSync,
|
|
18
|
-
mkdirSync,
|
|
19
|
-
readFileSync,
|
|
20
|
-
unlinkSync,
|
|
21
|
-
writeFileSync,
|
|
22
|
-
} from 'node:fs';
|
|
17
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
23
18
|
import { isAbsolute as pathIsAbsolute, join as pathJoin } from 'node:path';
|
|
24
19
|
import { type Span, context as otelContext } from '@opentelemetry/api';
|
|
25
|
-
import
|
|
20
|
+
import {
|
|
21
|
+
AUTH_CHECKPOINT_TOOLS,
|
|
22
|
+
type AuthCliCompileMode,
|
|
23
|
+
parseAuthCheckpoint,
|
|
24
|
+
} from './auth-compile-tools.ts';
|
|
26
25
|
import { type SharedModuleManifestEntry, resolvePlanSliceFromFile } from './build-plan.ts';
|
|
27
26
|
import type {
|
|
28
27
|
AuthCheckpoint,
|
|
@@ -43,7 +42,7 @@ import {
|
|
|
43
42
|
watchCompileProviderDeadline,
|
|
44
43
|
} from './compile-provider-control.ts';
|
|
45
44
|
import type { CompileStrategyKind } from './compile-strategy.ts';
|
|
46
|
-
import { recordCompilerHostError } from './compiler-log.ts';
|
|
45
|
+
import { compilerToolFailure, recordCompilerHostError } from './compiler-log.ts';
|
|
47
46
|
import {
|
|
48
47
|
collectOwnedProcess,
|
|
49
48
|
spawnOwnedProcess,
|
|
@@ -392,28 +391,30 @@ Use the imprint-compile MCP tools to inspect the session, write artifacts, run t
|
|
|
392
391
|
|
|
393
392
|
const family = currentNativeTeachAgents();
|
|
394
393
|
if (family) {
|
|
395
|
-
const
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
394
|
+
const executor = createCompileToolExecutor(
|
|
395
|
+
{
|
|
396
|
+
sessionPath: sessionPathAbs,
|
|
397
|
+
toolDir: opts.absoluteToolDir,
|
|
398
|
+
site: opts.authMode?.site,
|
|
399
|
+
authToolPlan: opts.authMode
|
|
400
|
+
? AuthToolPlanSchema.parse(JSON.parse(opts.authMode.authPlanJson))
|
|
401
|
+
: undefined,
|
|
402
|
+
provider: 'codex-cli',
|
|
403
|
+
candidate: opts.candidate,
|
|
404
|
+
sharedContext: opts.sharedContext,
|
|
405
|
+
buildPlanPath: opts.buildPlanPath,
|
|
406
|
+
sharedModules: opts.sharedModules,
|
|
407
|
+
strategyKind: opts.strategyKind,
|
|
408
|
+
revisionMode: opts.revisionMode,
|
|
409
|
+
verificationMode: opts.verificationMode,
|
|
410
|
+
sharedTriageSelection: opts.sharedTriageSelection,
|
|
411
|
+
},
|
|
412
|
+
{ session: opts.session, signal: opts.signal, runDeadline },
|
|
402
413
|
);
|
|
403
|
-
const transport = new StdioClientTransport({
|
|
404
|
-
env,
|
|
405
|
-
command: bunPath,
|
|
406
|
-
args: mcpArgs,
|
|
407
|
-
cwd: opts.absoluteToolDir,
|
|
408
|
-
stderr: 'pipe',
|
|
409
|
-
});
|
|
410
414
|
const conversationLogPath = pathJoin(opts.absoluteToolDir, '.compile-log.json');
|
|
411
415
|
const events: unknown[] = existsSync(conversationLogPath)
|
|
412
416
|
? JSON.parse(readFileSync(conversationLogPath, 'utf8'))
|
|
413
417
|
: [];
|
|
414
|
-
transport.stderr?.on('data', (chunk) =>
|
|
415
|
-
appendFileSync(pathJoin(opts.absoluteToolDir, '.native-mcp-stderr.log'), chunk),
|
|
416
|
-
);
|
|
417
418
|
const sharedResearch = family.sharedResearch
|
|
418
419
|
? {
|
|
419
420
|
runId: family.sharedResearch.runId,
|
|
@@ -428,30 +429,83 @@ Use the imprint-compile MCP tools to inspect the session, write artifacts, run t
|
|
|
428
429
|
? `${SHARED_RESEARCH_INSTRUCTIONS}\n${JSON.stringify({ sharedResearch })}\nUse the native shared_research tool with this exchange schema to publish/query findings; do not embed publications in your final compiler summary.\n`
|
|
429
430
|
: '';
|
|
430
431
|
try {
|
|
431
|
-
await client.connect(transport);
|
|
432
432
|
const response = await family.submit(
|
|
433
|
-
`${sharedPrompt}${initialPrompt}\n\nNative compiler: use list_assignment_tools and call_assignment_tool for this assignment in place of direct imprint-compile MCP names. All writes and tests must go through those tools. After done, give_up, or
|
|
433
|
+
`${sharedPrompt}${initialPrompt}\n\nNative compiler: use list_assignment_tools and call_assignment_tool for this assignment in place of direct imprint-compile MCP names. All writes and tests must go through those tools. ${opts.authMode?.executeCheckpoint ? 'Auth host tools execute inline: await each verification, browser operation, human question, or cooldown result and continue this same assignment. Submit a short summary only after done or give_up returns.' : 'After done, give_up, or a recorded auth checkpoint returns, submit a short summary to the host and stop this turn.'} Native subagents can assist with independent implementation questions; retain separate workspaces and do not substitute their prose for test evidence.`,
|
|
434
434
|
{
|
|
435
435
|
// The family already scopes this key to the run. Revision directories
|
|
436
436
|
// change; the public tool's retained compiler conversation must not.
|
|
437
437
|
conversation: `compiler:${opts.candidate?.toolName ?? opts.absoluteToolDir}`,
|
|
438
|
+
contextRef: opts.authMode?.contextRef,
|
|
438
439
|
signal: opts.signal,
|
|
439
|
-
call: async (name, args) => {
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
440
|
+
call: async (name, args, requestSignal) => {
|
|
441
|
+
const signal = requestSignal ?? opts.signal;
|
|
442
|
+
const requestId = randomUUID();
|
|
443
|
+
const started = performance.now();
|
|
444
|
+
const save = () =>
|
|
445
|
+
writeFileSync(conversationLogPath, JSON.stringify(events), { mode: 0o600 });
|
|
446
|
+
events.push({
|
|
447
|
+
type: 'tool.started',
|
|
448
|
+
timestamp: new Date().toISOString(),
|
|
449
|
+
name,
|
|
450
|
+
requestId,
|
|
451
|
+
});
|
|
452
|
+
save();
|
|
453
|
+
let failure: ReturnType<typeof compilerToolFailure> | undefined;
|
|
454
|
+
let result: CallToolResult | { tools: Tool[] };
|
|
455
|
+
try {
|
|
456
|
+
if (name === '__list') {
|
|
457
|
+
const listed = { tools: executor.tools };
|
|
458
|
+
result = opts.authMode?.executeCheckpoint
|
|
459
|
+
? {
|
|
460
|
+
...listed,
|
|
461
|
+
tools: listed.tools.map((tool) => {
|
|
462
|
+
const checkpoint = AUTH_CHECKPOINT_TOOLS.find(
|
|
463
|
+
(definition) => definition.name === tool.name,
|
|
464
|
+
);
|
|
465
|
+
return checkpoint
|
|
466
|
+
? {
|
|
467
|
+
...tool,
|
|
468
|
+
description: `${checkpoint.description} Await the host result and continue in this same assignment.`,
|
|
469
|
+
}
|
|
470
|
+
: tool;
|
|
471
|
+
}),
|
|
472
|
+
}
|
|
473
|
+
: listed;
|
|
474
|
+
} else if (
|
|
475
|
+
opts.authMode?.executeCheckpoint &&
|
|
476
|
+
AUTH_CHECKPOINT_TOOLS.some((tool) => tool.name === name)
|
|
477
|
+
) {
|
|
478
|
+
const executed = await opts.authMode.executeCheckpoint(
|
|
479
|
+
parseAuthCheckpoint(name, args),
|
|
480
|
+
signal,
|
|
481
|
+
);
|
|
482
|
+
result = {
|
|
483
|
+
content: [{ type: 'text', text: executed.result }],
|
|
484
|
+
...(executed.isError ? { isError: true } : {}),
|
|
485
|
+
};
|
|
486
|
+
} else {
|
|
487
|
+
result = await executor.callTool(name, args, signal);
|
|
488
|
+
}
|
|
489
|
+
events.push({ timestamp: new Date().toISOString(), name, args, result });
|
|
490
|
+
return result;
|
|
491
|
+
} catch (error) {
|
|
492
|
+
failure = compilerToolFailure(error);
|
|
493
|
+
throw error;
|
|
494
|
+
} finally {
|
|
495
|
+
events.push({
|
|
496
|
+
type: 'tool.settled',
|
|
497
|
+
timestamp: new Date().toISOString(),
|
|
498
|
+
name,
|
|
499
|
+
requestId,
|
|
500
|
+
durationMs: performance.now() - started,
|
|
501
|
+
status: signal?.aborted ? 'cancelled' : failure ? 'threw' : 'returned',
|
|
502
|
+
...(failure ? { error: failure } : {}),
|
|
503
|
+
});
|
|
504
|
+
save();
|
|
505
|
+
}
|
|
450
506
|
},
|
|
451
507
|
},
|
|
452
508
|
);
|
|
453
|
-
const interruption = providerControl.interruption();
|
|
454
|
-
if (interruption) throw compileProviderInterruptionError(interruption);
|
|
455
509
|
events.push({ type: 'native.completed', ...response });
|
|
456
510
|
writeFileSync(conversationLogPath, JSON.stringify(events), { mode: 0o600 });
|
|
457
511
|
return completedCompileResult(opts, {
|
|
@@ -477,8 +531,7 @@ Use the imprint-compile MCP tools to inspect the session, write artifacts, run t
|
|
|
477
531
|
),
|
|
478
532
|
});
|
|
479
533
|
} finally {
|
|
480
|
-
|
|
481
|
-
await client.close();
|
|
534
|
+
await executor.close();
|
|
482
535
|
}
|
|
483
536
|
}
|
|
484
537
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { AgentProgress } from './agent.ts';
|
|
10
|
+
import type { AuthBrowserAction } from './auth-browser-discovery.ts';
|
|
10
11
|
import { type AssignedSharedModule, describeAssignedModules } from './build-plan.ts';
|
|
11
12
|
import type { ProviderInterruptionReason, ProviderReportedError } from './provider-retry.ts';
|
|
12
13
|
import type { SharedCompileContext, ToolCandidate } from './tool-candidates.ts';
|
|
@@ -123,12 +124,10 @@ export interface CompileAgentProgress extends AgentProgress {
|
|
|
123
124
|
};
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
/** A
|
|
127
|
-
*
|
|
128
|
-
* (teach) performs the action — it owns the live browser session, the TUI, and
|
|
129
|
-
* the cooldown — then resumes the agent (`claude --resume`) with the result as a
|
|
130
|
-
* follow-up user message. Site/channel-agnostic. */
|
|
127
|
+
/** A host operation shared by inline native/API tools and CLI checkpoint/resume.
|
|
128
|
+
* The host owns the browser, prompt references, and declared continuation. */
|
|
131
129
|
export type AuthCheckpoint =
|
|
130
|
+
| ({ kind: 'interact_verification_browser' } & AuthBrowserAction)
|
|
132
131
|
| {
|
|
133
132
|
kind: 'run_verification';
|
|
134
133
|
action: string;
|
|
@@ -144,6 +143,11 @@ export type AuthCheckpoint =
|
|
|
144
143
|
| { kind: 'prompt_user'; message: string; options?: string[] }
|
|
145
144
|
| { kind: 'wait_for_cooldown'; minutes: number; reason?: string };
|
|
146
145
|
|
|
146
|
+
export interface AuthCheckpointResult {
|
|
147
|
+
result: string;
|
|
148
|
+
isError?: boolean;
|
|
149
|
+
}
|
|
150
|
+
|
|
147
151
|
export interface CompileAgentResult {
|
|
148
152
|
/** True only if external verification passed. */
|
|
149
153
|
success: boolean;
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
giveUpTool,
|
|
17
17
|
runAgentLoop,
|
|
18
18
|
} from './agent.ts';
|
|
19
|
+
import { attachRecordedAuthentication } from './authentication-interaction.ts';
|
|
19
20
|
import { type SharedModuleManifestEntry, resolvePlanSliceFromFile } from './build-plan.ts';
|
|
20
21
|
import { compileViaClaudeCli } from './claude-cli-compile.ts';
|
|
21
22
|
import { compileViaCodexCli } from './codex-cli-compile.ts';
|
|
@@ -70,6 +71,12 @@ import {
|
|
|
70
71
|
minimalSharedTriageSelection,
|
|
71
72
|
} from './triage-selection.ts';
|
|
72
73
|
import { type Session, SessionSchema, WorkflowSchema } from './types.ts';
|
|
74
|
+
import {
|
|
75
|
+
type OnUserPrompt,
|
|
76
|
+
UserInteraction,
|
|
77
|
+
currentUserInteraction,
|
|
78
|
+
withUserInteraction,
|
|
79
|
+
} from './user-interaction.ts';
|
|
73
80
|
|
|
74
81
|
export type { CompileAgentProgress } from './compile-agent-types.ts';
|
|
75
82
|
|
|
@@ -105,6 +112,7 @@ function removeEphemeralTests(toolDir: string): void {
|
|
|
105
112
|
}
|
|
106
113
|
|
|
107
114
|
interface CompileAgentOptions {
|
|
115
|
+
onPrompt?: OnUserPrompt;
|
|
108
116
|
/** Path to the recorded session JSON (absolute or relative). */
|
|
109
117
|
sessionPath: string;
|
|
110
118
|
/** Hard wall-clock budget. Default 20 minutes. */
|
|
@@ -172,6 +180,11 @@ function formatRevisionMode(enabled: boolean | undefined): string {
|
|
|
172
180
|
}
|
|
173
181
|
|
|
174
182
|
export async function compileAgent(opts: CompileAgentOptions): Promise<CompileAgentResult> {
|
|
183
|
+
if (!currentUserInteraction()) {
|
|
184
|
+
return await withUserInteraction(new UserInteraction(opts.onPrompt, opts.signal), () =>
|
|
185
|
+
compileAgent(opts),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
175
188
|
const startTime = Date.now();
|
|
176
189
|
// Build-plan fields are bounded proposals for the compile/master agents.
|
|
177
190
|
const { assignedSharedModules } = resolvePlanSliceFromFile(
|
|
@@ -192,6 +205,12 @@ export async function compileAgent(opts: CompileAgentOptions): Promise<CompileAg
|
|
|
192
205
|
'session',
|
|
193
206
|
);
|
|
194
207
|
|
|
208
|
+
const extractedAuthValues = Object.fromEntries(
|
|
209
|
+
extractCredentials(session).replacements.flatMap(({ originalValue, placeholder }) => {
|
|
210
|
+
const match = /^\$\{credential\.([^}]+)\}$/.exec(placeholder);
|
|
211
|
+
return match?.[1] ? [[match[1], originalValue]] : [];
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
195
214
|
// 2. Auto-redact raw direct-generate inputs. Teach normally supplies an
|
|
196
215
|
// already-redacted focused recording.
|
|
197
216
|
const looksRedacted = JSON.stringify(session).includes('[REDACTED:');
|
|
@@ -265,6 +284,7 @@ export async function compileAgent(opts: CompileAgentOptions): Promise<CompileAg
|
|
|
265
284
|
sharedModules: opts.sharedModules,
|
|
266
285
|
strategyKind: opts.strategyKind,
|
|
267
286
|
revisionMode: opts.revisionMode,
|
|
287
|
+
signal: opts.signal,
|
|
268
288
|
}),
|
|
269
289
|
doneTool(),
|
|
270
290
|
giveUpTool(),
|
|
@@ -289,6 +309,19 @@ Begin by calling read_session_summary to orient yourself, then proceed per the s
|
|
|
289
309
|
opts.deadlineMs ?? Date.now() + (opts.maxDurationMs ?? 20 * 60 * 1000),
|
|
290
310
|
);
|
|
291
311
|
|
|
312
|
+
if (!currentUserInteraction()?.hasAuthentication) {
|
|
313
|
+
currentUserInteraction()?.setRunControl(runDeadline, opts.onDeadlineReached);
|
|
314
|
+
attachRecordedAuthentication({
|
|
315
|
+
session,
|
|
316
|
+
sessionPath: sessionPathAbs,
|
|
317
|
+
root: pathJoin(absoluteToolDir, '.authentication'),
|
|
318
|
+
values: { ...extractedAuthValues, ...opts.teachCredentials?.values },
|
|
319
|
+
llmConfig: opts.llmConfig,
|
|
320
|
+
runDeadline,
|
|
321
|
+
report: log,
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
|
|
292
325
|
// 8. Instantiate provider (or use injected one for testing).
|
|
293
326
|
// CLI providers take a different path: they don't implement Anthropic
|
|
294
327
|
// messageWithTools, so we shell out with the same toolset registered as a
|