{"version":3,"file":"print-mode.d.ts","sourceRoot":"","sources":["../../src/modes/print-mode.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAoB,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAS7D,OAAO,EAGN,KAAK,4BAA4B,EACjC,MAAM,4BAA4B,CAAC;AAUpC;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,wDAAwD;IACxD,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,8CAA8C;IAC9C,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;CAC/B;AAED,UAAU,4BACT,SAAQ,IAAI,CACX,YAAY,EACV,WAAW,GACX,cAAc,GACd,eAAe,GACf,yBAAyB,GACzB,kBAAkB,GAClB,+BAA+B,GAC/B,wBAAwB,GACxB,cAAc,GACd,oBAAoB,GACpB,gBAAgB,CAClB;IACD,cAAc,CAAC,EAAE,4BAA4B,CAAC;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,KAAK,CAAC,EAAE;QAAE,QAAQ,EAAE,aAAa,CAAC;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAC,CAAA;KAAE,CAAC;IACtD,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,mBAAmB,CAAC,EAAE,MAAM,SAAS,MAAM,EAAE,CAAC;IAC9C,KAAK,CAAC,EAAE;QAAE,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;CACtC;AAED,wBAAsB,8BAA8B,CACnD,OAAO,EAAE,4BAA4B,EACrC,IAAI,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmI7B;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAyGlG","sourcesContent":["/**\n * Print mode (single-shot): Send prompts, output result, exit.\n *\n * Used for:\n * - `pi -p \"prompt\"` - text output\n * - `pi --mode json \"prompt\"` - JSON event stream\n */\n\nimport type { AssistantMessage, ImageContent } from \"@apholdings/jensen-ai\";\nimport type { AgentSession } from \"../core/agent-session.js\";\nimport { BRIEF_ONLY_COMMAND_USAGE, parseBriefOnlyCommand, runBriefOnlyCommand } from \"../core/brief-only-command.js\";\nimport { BTW_COMMAND_USAGE, parseBtwCommand, runBtwCommand } from \"../core/btw-command.js\";\nimport { initializeProjectScaffold } from \"../core/init-project.js\";\nimport {\n\tformatMemoryDiffOutput,\n\tformatMemoryHistoryOutput,\n\tformatRelativeAgeLabel,\n} from \"../core/memory-compare-output.js\";\nimport {\n\tformatProtocolStatusOutput,\n\tgetProtocolWorkspaceStatus,\n\ttype ProtocolStatusResourceLoader,\n} from \"../core/protocol-status.js\";\nimport {\n\tparseSteerCommand,\n\trunSteerCommand,\n\tSTEER_COMMAND_ACTIVE_WORK_REQUIRED,\n\tSTEER_COMMAND_USAGE,\n} from \"../core/steer-command.js\";\nimport { searchDuckDuckGoLite } from \"../core/tools/web-search.js\";\nimport { formatUltraplanShowOutput } from \"../core/ultraplan.js\";\n\n/**\n * Options for print mode.\n */\nexport interface PrintModeOptions {\n\t/** Output mode: \"text\" for final response only, \"json\" for all events */\n\tmode: \"text\" | \"json\";\n\t/** Array of additional prompts to send after initialMessage */\n\tmessages?: string[];\n\t/** First message to send (may contain @file content) */\n\tinitialMessage?: string;\n\t/** Images to attach to the initial message */\n\tinitialImages?: ImageContent[];\n}\n\ninterface PrintModeLocalCommandSession\n\textends Pick<\n\t\tAgentSession,\n\t\t| \"briefOnly\"\n\t\t| \"setBriefOnly\"\n\t\t| \"queueByTheWay\"\n\t\t| \"getPendingByTheWayNotes\"\n\t\t| \"getMemoryHistory\"\n\t\t| \"resolveMemorySnapshotSelector\"\n\t\t| \"getLatestUltraplanPlan\"\n\t\t| \"runUltraplan\"\n\t\t| \"runUltraplanRevise\"\n\t\t| \"applyUltraplan\"\n\t> {\n\tresourceLoader?: ProtocolStatusResourceLoader;\n\tisStreaming?: boolean;\n\tstate?: { messages: ReadonlyArray<{ role: string }> };\n\tsteer?: (message: string) => Promise<void>;\n\tgetSteeringMessages?: () => readonly string[];\n\tagent?: { continue(): Promise<void> };\n}\n\nexport async function getPrintModeLocalCommandOutput(\n\tsession: PrintModeLocalCommandSession,\n\ttext: string,\n): Promise<string | undefined> {\n\tconst parts = text.trim().split(/\\s+/);\n\tconst command = parts[0]?.toLowerCase();\n\tconst subcommand = parts[1]?.toLowerCase();\n\tconst briefOnlyAction = parseBriefOnlyCommand(text);\n\tconst btwNote = parseBtwCommand(text);\n\tconst steerMessage = parseSteerCommand(text);\n\n\tif (command === \"/brief\") {\n\t\treturn briefOnlyAction ? runBriefOnlyCommand(session, briefOnlyAction) : BRIEF_ONLY_COMMAND_USAGE;\n\t}\n\n\tif (command === \"/btw\") {\n\t\treturn btwNote ? runBtwCommand(session, btwNote) : BTW_COMMAND_USAGE;\n\t}\n\n\tif (command === \"/steer\") {\n\t\tif (!steerMessage) {\n\t\t\treturn STEER_COMMAND_USAGE;\n\t\t}\n\t\tif (!session.state || !session.steer || !session.getSteeringMessages || !session.agent) {\n\t\t\treturn STEER_COMMAND_ACTIVE_WORK_REQUIRED;\n\t\t}\n\t\ttry {\n\t\t\treturn await runSteerCommand(\n\t\t\t\t{\n\t\t\t\t\tisStreaming: session.isStreaming ?? false,\n\t\t\t\t\tstate: session.state,\n\t\t\t\t\tsteer: (message) => session.steer!(message),\n\t\t\t\t\tcontinueCurrentWork: () => session.agent!.continue(),\n\t\t\t\t\tgetSteeringMessages: () => session.getSteeringMessages!(),\n\t\t\t\t},\n\t\t\t\tsteerMessage,\n\t\t\t);\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn message || STEER_COMMAND_ACTIVE_WORK_REQUIRED;\n\t\t}\n\t}\n\n\tif (command === \"/memory\") {\n\t\tconst snapshots = session.getMemoryHistory();\n\t\tif (subcommand === \"history\") {\n\t\t\treturn formatMemoryHistoryOutput(snapshots, {\n\t\t\t\tgetRelativeAgeLabel: formatRelativeAgeLabel,\n\t\t\t}).join(\"\\n\");\n\t\t}\n\n\t\tif (subcommand === \"diff\") {\n\t\t\treturn formatMemoryDiffOutput(\n\t\t\t\tsnapshots,\n\t\t\t\t{\n\t\t\t\t\tgetRelativeAgeLabel: formatRelativeAgeLabel,\n\t\t\t\t\tresolveSnapshotSelector: (input) => session.resolveMemorySnapshotSelector(input),\n\t\t\t\t},\n\t\t\t\t{ baseline: parts[2], target: parts[3] },\n\t\t\t).join(\"\\n\");\n\t\t}\n\t}\n\n\tif (command === \"/init-project\") {\n\t\tconst includeProtocol = parts[1] === \"--protocol\";\n\t\tif (parts.length > 2 || (parts[1] !== undefined && !includeProtocol)) {\n\t\t\treturn \"Usage: /init-project [--protocol]\";\n\t\t}\n\t\treturn initializeProjectScaffold(process.cwd(), { includeProtocol }).output;\n\t}\n\n\tif (command === \"/protocol-status\") {\n\t\treturn formatProtocolStatusOutput(\n\t\t\tgetProtocolWorkspaceStatus({\n\t\t\t\tcwd: process.cwd(),\n\t\t\t\tresourceLoader: session.resourceLoader,\n\t\t\t}),\n\t\t);\n\t}\n\n\tif (command === \"/ultraplan\") {\n\t\tif (subcommand === \"show\") {\n\t\t\treturn formatUltraplanShowOutput(session.getLatestUltraplanPlan());\n\t\t}\n\n\t\tif (subcommand === \"apply\") {\n\t\t\treturn session.applyUltraplan().displayText;\n\t\t}\n\n\t\tif (subcommand === \"revise\" || subcommand === \"regenerate\") {\n\t\t\tconst instruction = text.slice(`/ultraplan ${subcommand}`.length).trim();\n\t\t\tif (!instruction) {\n\t\t\t\treturn \"Usage: /ultraplan <objective> | /ultraplan show | /ultraplan apply | /ultraplan revise <instruction> | /ultraplan regenerate <instruction>\";\n\t\t\t}\n\t\t\tconst result = await session.runUltraplanRevise(instruction);\n\t\t\treturn result.displayText;\n\t\t}\n\n\t\tconst objective = text.slice(\"/ultraplan\".length).trim();\n\t\tif (!objective) {\n\t\t\treturn \"Usage: /ultraplan <objective> | /ultraplan show | /ultraplan apply | /ultraplan revise <instruction> | /ultraplan regenerate <instruction>\";\n\t\t}\n\n\t\tconst result = await session.runUltraplan(objective);\n\t\treturn result.displayText;\n\t}\n\n\tif (command === \"/websearch\") {\n\t\tconst query = text.slice(\"/websearch\".length).trim();\n\t\tif (!query) {\n\t\t\treturn \"Usage: /websearch <query>\";\n\t\t}\n\t\ttry {\n\t\t\tconst results = await searchDuckDuckGoLite(query);\n\t\t\tif (results.length === 0) {\n\t\t\t\treturn `No web results found for \"${query}\".`;\n\t\t\t}\n\t\t\tconst lines = [`Web Search: ${query}`, \"\"];\n\t\t\tfor (const [index, result] of results.entries()) {\n\t\t\t\tlines.push(`${index + 1}. ${result.title}`);\n\t\t\t\tlines.push(`   ${result.url}`);\n\t\t\t\tif (result.snippet) {\n\t\t\t\t\tlines.push(`   ${result.snippet}`);\n\t\t\t\t}\n\t\t\t\tlines.push(\"\");\n\t\t\t}\n\t\t\treturn lines.join(\"\\n\");\n\t\t} catch (error) {\n\t\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\t\treturn `Search failed: ${message}`;\n\t\t}\n\t}\n\n\treturn undefined;\n}\n\n/**\n * Run in print (single-shot) mode.\n * Sends prompts to the agent and outputs the result.\n */\nexport async function runPrintMode(session: AgentSession, options: PrintModeOptions): Promise<void> {\n\tconst { mode, messages = [], initialMessage, initialImages } = options;\n\tif (mode === \"json\") {\n\t\tconst header = session.sessionManager.getHeader();\n\t\tif (header) {\n\t\t\tconsole.log(JSON.stringify(header));\n\t\t}\n\t}\n\t// Set up extensions for print mode (no UI)\n\tawait session.bindExtensions({\n\t\tcommandContextActions: {\n\t\t\twaitForIdle: () => session.agent.waitForIdle(),\n\t\t\tnewSession: async (options) => {\n\t\t\t\tconst success = await session.newSession({ parentSession: options?.parentSession });\n\t\t\t\tif (success && options?.setup) {\n\t\t\t\t\tawait options.setup(session.sessionManager);\n\t\t\t\t}\n\t\t\t\treturn { cancelled: !success };\n\t\t\t},\n\t\t\tfork: async (entryId) => {\n\t\t\t\tconst result = await session.fork(entryId);\n\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t},\n\t\t\tnavigateTree: async (targetId, options) => {\n\t\t\t\tconst result = await session.navigateTree(targetId, {\n\t\t\t\t\tsummarize: options?.summarize,\n\t\t\t\t\tcustomInstructions: options?.customInstructions,\n\t\t\t\t\treplaceInstructions: options?.replaceInstructions,\n\t\t\t\t\tlabel: options?.label,\n\t\t\t\t});\n\t\t\t\treturn { cancelled: result.cancelled };\n\t\t\t},\n\t\t\tswitchSession: async (sessionPath) => {\n\t\t\t\tconst success = await session.switchSession(sessionPath);\n\t\t\t\treturn { cancelled: !success };\n\t\t\t},\n\t\t\treload: async () => {\n\t\t\t\tawait session.reload();\n\t\t\t},\n\t\t},\n\t\tonError: (err) => {\n\t\t\tconsole.error(`Extension error (${err.extensionPath}): ${err.error}`);\n\t\t},\n\t});\n\n\t// Always subscribe to enable session persistence via _handleAgentEvent\n\tsession.subscribe((event) => {\n\t\t// In JSON mode, output all events\n\t\tif (mode === \"json\") {\n\t\t\tconsole.log(JSON.stringify(event));\n\t\t}\n\t});\n\n\tconst handleTextOrLocalCommand = async (text: string, images?: ImageContent[]): Promise<void> => {\n\t\tif (mode === \"text\") {\n\t\t\tconst localOutput = await getPrintModeLocalCommandOutput(session, text);\n\t\t\tif (localOutput !== undefined) {\n\t\t\t\tconsole.log(localOutput);\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tawait session.prompt(text, { images });\n\t};\n\n\t// Send initial message with attachments\n\tif (initialMessage) {\n\t\tawait handleTextOrLocalCommand(initialMessage, initialImages);\n\t}\n\n\t// Send remaining messages\n\tfor (const message of messages) {\n\t\tawait handleTextOrLocalCommand(message);\n\t}\n\n\t// In text mode, output final response\n\tif (mode === \"text\") {\n\t\tconst state = session.state;\n\t\tconst lastMessage = state.messages[state.messages.length - 1];\n\n\t\tif (lastMessage?.role === \"assistant\") {\n\t\t\tconst assistantMsg = lastMessage as AssistantMessage;\n\n\t\t\t// Check for error/aborted\n\t\t\tif (assistantMsg.stopReason === \"error\" || assistantMsg.stopReason === \"aborted\") {\n\t\t\t\tconsole.error(assistantMsg.errorMessage || `Request ${assistantMsg.stopReason}`);\n\t\t\t\tprocess.exit(1);\n\t\t\t}\n\n\t\t\t// Output text content\n\t\t\tfor (const content of assistantMsg.content) {\n\t\t\t\tif (content.type === \"text\") {\n\t\t\t\t\tconsole.log(content.text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Ensure stdout is fully flushed before returning\n\t// This prevents race conditions where the process exits before all output is written\n\tawait new Promise<void>((resolve, reject) => {\n\t\tprocess.stdout.write(\"\", (err) => {\n\t\t\tif (err) reject(err);\n\t\t\telse resolve();\n\t\t});\n\t});\n}\n"]}