imprint-mcp 0.7.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/README.md +64 -2
  2. package/package.json +1 -1
  3. package/prompts/auth-compile-agent.md +167 -22
  4. package/prompts/compile-agent.md +45 -8
  5. package/prompts/component-recovery.md +40 -0
  6. package/prompts/master-teach-decision.md +28 -3
  7. package/prompts/master-teach-focused-planner.md +18 -5
  8. package/src/cli.ts +26 -18
  9. package/src/imprint/agent-tool.ts +15 -0
  10. package/src/imprint/agent.ts +2 -11
  11. package/src/imprint/audit.ts +35 -13
  12. package/src/imprint/auth-browser-discovery.ts +276 -0
  13. package/src/imprint/auth-compile-agent.ts +513 -224
  14. package/src/imprint/auth-compile-tools.ts +213 -25
  15. package/src/imprint/auth-discovery-evidence.ts +81 -0
  16. package/src/imprint/auth-plan.ts +28 -0
  17. package/src/imprint/auth-verifier.ts +234 -20
  18. package/src/imprint/authentication-interaction.ts +137 -0
  19. package/src/imprint/backend-ladder.ts +218 -34
  20. package/src/imprint/build-plan.ts +2 -22
  21. package/src/imprint/cdp-browser-fetch.ts +192 -53
  22. package/src/imprint/chromium.ts +93 -29
  23. package/src/imprint/claude-cli-compile.ts +2 -0
  24. package/src/imprint/codex-cli-compile.ts +99 -46
  25. package/src/imprint/compile-agent-types.ts +9 -5
  26. package/src/imprint/compile-agent.ts +33 -0
  27. package/src/imprint/compile-tool-executor.ts +783 -0
  28. package/src/imprint/compile-tools.ts +42 -9
  29. package/src/imprint/compile-verification.ts +2 -0
  30. package/src/imprint/compile.ts +3 -0
  31. package/src/imprint/compiler-log.ts +16 -0
  32. package/src/imprint/component-recovery.ts +120 -0
  33. package/src/imprint/cookie-jar.ts +117 -7
  34. package/src/imprint/credential-extract.ts +9 -2
  35. package/src/imprint/credential-lock.ts +146 -0
  36. package/src/imprint/credential-observation.ts +261 -0
  37. package/src/imprint/credential-store.ts +247 -44
  38. package/src/imprint/live-component-recovery.ts +73 -0
  39. package/src/imprint/llm.ts +4 -3
  40. package/src/imprint/log.ts +16 -1
  41. package/src/imprint/login.ts +9 -0
  42. package/src/imprint/master-teach-agent-contracts.ts +40 -3
  43. package/src/imprint/master-teach-agents.ts +229 -14
  44. package/src/imprint/master-teach-checks.ts +8 -1
  45. package/src/imprint/master-teach-controller.ts +681 -138
  46. package/src/imprint/master-teach-prompt-projections.ts +2 -0
  47. package/src/imprint/mcp-compile-server.ts +62 -850
  48. package/src/imprint/native-teach-agents.ts +232 -108
  49. package/src/imprint/runtime.ts +357 -36
  50. package/src/imprint/stealth-chromium.ts +67 -7
  51. package/src/imprint/teach-browser-resources.ts +88 -0
  52. package/src/imprint/teach-stage-progress.ts +38 -0
  53. package/src/imprint/terminal-user-prompt.ts +42 -0
  54. package/src/imprint/types.ts +46 -18
  55. package/src/imprint/user-interaction.ts +836 -0
@@ -1,5 +1,7 @@
1
- import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
- import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
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 type { AuthCliCompileMode } from './auth-compile-tools.ts';
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 client = new Client({ name: 'imprint-native-compiler', version: '1.0.0' });
396
- const providerControl = createCompileProviderControl(runDeadline ?? opts.deadlineMs);
397
- const env = Object.fromEntries(
398
- Object.entries({ ...process.env, ...providerControl.env }).filter(
399
- (entry): entry is [string, string] =>
400
- entry[0] !== 'IMPRINT_TEACH_CREDENTIALS' && entry[1] !== undefined,
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 an 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.`,
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 result =
441
- name === '__list'
442
- ? await client.listTools()
443
- : await client.callTool({ name, arguments: args }, undefined, {
444
- signal: opts.signal,
445
- timeout: MAX_MCP_TOOL_TIMEOUT_SEC * 1000,
446
- });
447
- events.push({ timestamp: new Date().toISOString(), name, args, result });
448
- writeFileSync(conversationLogPath, JSON.stringify(events), { mode: 0o600 });
449
- return result;
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
- providerControl.dispose();
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 mid-loop checkpoint the auth compile agent reaches: it calls a checkpoint
127
- * tool (which writes a sentinel) and then STOPS its turn. The orchestrator
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