@astralyn/pi 0.82.4 → 0.82.6
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/CHANGELOG.md +52 -0
- package/dist/cli/project-trust.d.ts.map +1 -1
- package/dist/cli/project-trust.js +4 -1
- package/dist/cli/project-trust.js.map +1 -1
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +2 -2
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/bash-executor.d.ts.map +1 -1
- package/dist/core/bash-executor.js +4 -22
- package/dist/core/bash-executor.js.map +1 -1
- package/dist/core/extensions/index.d.ts +2 -2
- package/dist/core/extensions/index.d.ts.map +1 -1
- package/dist/core/extensions/index.js +1 -1
- package/dist/core/extensions/index.js.map +1 -1
- package/dist/core/extensions/loader.d.ts +1 -1
- package/dist/core/extensions/loader.d.ts.map +1 -1
- package/dist/core/extensions/loader.js +2 -3
- package/dist/core/extensions/loader.js.map +1 -1
- package/dist/core/extensions/types.d.ts +37 -5
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js +11 -0
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/tools/output-accumulator.d.ts +3 -13
- package/dist/core/tools/output-accumulator.d.ts.map +1 -1
- package/dist/core/tools/output-accumulator.js +6 -36
- package/dist/core/tools/output-accumulator.js.map +1 -1
- package/dist/extensions/plan/constants.d.ts +11 -2
- package/dist/extensions/plan/constants.d.ts.map +1 -1
- package/dist/extensions/plan/constants.js +14 -2
- package/dist/extensions/plan/constants.js.map +1 -1
- package/dist/extensions/plan/index.d.ts +0 -18
- package/dist/extensions/plan/index.d.ts.map +1 -1
- package/dist/extensions/plan/index.js +35 -3
- package/dist/extensions/plan/index.js.map +1 -1
- package/dist/extensions/plan/view.d.ts +36 -0
- package/dist/extensions/plan/view.d.ts.map +1 -0
- package/dist/extensions/plan/view.js +74 -0
- package/dist/extensions/plan/view.js.map +1 -0
- package/dist/extensions/question/dialog.d.ts +3 -1
- package/dist/extensions/question/dialog.d.ts.map +1 -1
- package/dist/extensions/question/dialog.js +106 -52
- package/dist/extensions/question/dialog.js.map +1 -1
- package/dist/extensions/question/index.d.ts.map +1 -1
- package/dist/extensions/question/index.js +2 -2
- package/dist/extensions/question/index.js.map +1 -1
- package/dist/extensions/question/render-cache.d.ts +9 -0
- package/dist/extensions/question/render-cache.d.ts.map +1 -1
- package/dist/extensions/question/render-cache.js +22 -0
- package/dist/extensions/question/render-cache.js.map +1 -1
- package/dist/extensions/question/results.d.ts.map +1 -1
- package/dist/extensions/question/results.js +6 -1
- package/dist/extensions/question/results.js.map +1 -1
- package/dist/extensions/question/schema.d.ts +4 -0
- package/dist/extensions/question/schema.d.ts.map +1 -1
- package/dist/extensions/question/schema.js +19 -28
- package/dist/extensions/question/schema.js.map +1 -1
- package/dist/extensions/question/types.d.ts +1 -1
- package/dist/extensions/question/types.d.ts.map +1 -1
- package/dist/extensions/question/types.js.map +1 -1
- package/dist/extensions/router/stream.d.ts.map +1 -1
- package/dist/extensions/router/stream.js +44 -0
- package/dist/extensions/router/stream.js.map +1 -1
- package/dist/extensions/subagent/activity.d.ts.map +1 -1
- package/dist/extensions/subagent/activity.js +25 -4
- package/dist/extensions/subagent/activity.js.map +1 -1
- package/dist/extensions/subagent/agents.d.ts.map +1 -1
- package/dist/extensions/subagent/agents.js +12 -6
- package/dist/extensions/subagent/agents.js.map +1 -1
- package/dist/extensions/subagent/constants.d.ts +1 -1
- package/dist/extensions/subagent/constants.d.ts.map +1 -1
- package/dist/extensions/subagent/constants.js +3 -2
- package/dist/extensions/subagent/constants.js.map +1 -1
- package/dist/extensions/subagent/index.d.ts.map +1 -1
- package/dist/extensions/subagent/index.js +14 -0
- package/dist/extensions/subagent/index.js.map +1 -1
- package/dist/extensions/subagent/render.d.ts.map +1 -1
- package/dist/extensions/subagent/render.js +243 -116
- package/dist/extensions/subagent/render.js.map +1 -1
- package/dist/extensions/subagent/resolve.d.ts.map +1 -1
- package/dist/extensions/subagent/resolve.js +13 -9
- package/dist/extensions/subagent/resolve.js.map +1 -1
- package/dist/extensions/subagent/runner.d.ts +1 -1
- package/dist/extensions/subagent/runner.d.ts.map +1 -1
- package/dist/extensions/subagent/runner.js +9 -3
- package/dist/extensions/subagent/runner.js.map +1 -1
- package/dist/extensions/subagent/schema.d.ts.map +1 -1
- package/dist/extensions/subagent/schema.js +4 -4
- package/dist/extensions/subagent/schema.js.map +1 -1
- package/dist/extensions/subagent/sdk-runner.d.ts.map +1 -1
- package/dist/extensions/subagent/sdk-runner.js +5 -0
- package/dist/extensions/subagent/sdk-runner.js.map +1 -1
- package/dist/extensions/subagent/types.d.ts +7 -0
- package/dist/extensions/subagent/types.d.ts.map +1 -1
- package/dist/extensions/subagent/types.js.map +1 -1
- package/dist/extensions/todo/constants.d.ts +17 -6
- package/dist/extensions/todo/constants.d.ts.map +1 -1
- package/dist/extensions/todo/constants.js +47 -9
- package/dist/extensions/todo/constants.js.map +1 -1
- package/dist/extensions/todo/index.d.ts +1 -15
- package/dist/extensions/todo/index.d.ts.map +1 -1
- package/dist/extensions/todo/index.js +35 -4
- package/dist/extensions/todo/index.js.map +1 -1
- package/dist/extensions/todo/schema.d.ts +59 -7
- package/dist/extensions/todo/schema.d.ts.map +1 -1
- package/dist/extensions/todo/schema.js +72 -12
- package/dist/extensions/todo/schema.js.map +1 -1
- package/dist/extensions/todo/state.d.ts +20 -5
- package/dist/extensions/todo/state.d.ts.map +1 -1
- package/dist/extensions/todo/state.js +1016 -134
- package/dist/extensions/todo/state.js.map +1 -1
- package/dist/extensions/todo/view.d.ts +16 -1
- package/dist/extensions/todo/view.d.ts.map +1 -1
- package/dist/extensions/todo/view.js +293 -7
- package/dist/extensions/todo/view.js.map +1 -1
- package/dist/modes/interactive/components/extension-selector.d.ts +6 -1
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/extension-selector.js +14 -6
- package/dist/modes/interactive/components/extension-selector.js.map +1 -1
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +6 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +2 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +41 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-mode.js +8 -1
- package/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/dist/utils/coalescing-terminal.d.ts +71 -17
- package/dist/utils/coalescing-terminal.d.ts.map +1 -1
- package/dist/utils/coalescing-terminal.js +136 -31
- package/dist/utils/coalescing-terminal.js.map +1 -1
- package/docs/bundled/extensions/plan.md +6 -4
- package/docs/bundled/extensions/question.md +9 -7
- package/docs/bundled/extensions/subagent.md +4 -4
- package/docs/bundled/extensions/todo.md +9 -5
- package/docs/bundled/tool-presentation.md +1 -1
- package/docs/extensions.md +14 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/dist/utils/output-decoder.d.ts +0 -52
- package/dist/utils/output-decoder.d.ts.map +0 -1
- package/dist/utils/output-decoder.js +0 -134
- package/dist/utils/output-decoder.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/extensions/subagent/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Dirent, existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../../config.ts\";\nimport { parseFrontmatter } from \"../../utils/frontmatter.ts\";\nimport { utf8Prefix } from \"./activity.ts\";\nimport { BUILTIN_TOOL_NAMES, DEFAULT_AGENT_TOOLS, EXPLORER_TOOLS } from \"./constants.ts\";\nimport type { AgentDefinition, AgentDiagnostic, AgentDiscoveryResult, AgentSource } from \"./types.ts\";\n\nconst AGENT_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/u;\nconst BUILTIN_TOOL_SET = new Set<string>(BUILTIN_TOOL_NAMES);\nconst TOOL_DESCRIPTION_AGENT_LIST_LIMIT = 8_000;\nconst TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT = 480;\n\nconst BUILTIN_AGENTS: AgentDefinition[] = [\n\t{\n\t\tname: \"general\",\n\t\tdescription: \"General-purpose implementation agent with coding tools\",\n\t\ttools: [...DEFAULT_AGENT_TOOLS],\n\t\tsystemPrompt: [\n\t\t\t\"Work independently on the delegated task from start to finish.\",\n\t\t\t\"Inspect relevant repository instructions before changing files.\",\n\t\t\t\"Prefer editing existing files; never create documentation files unless the task explicitly asks for them.\",\n\t\t\t\"Keep changes focused, run appropriate verification, and report exact paths, checks, blockers, and remaining risks.\",\n\t\t].join(\"\\n\"),\n\t\tsource: \"builtin\",\n\t\tfilePath: \"<builtin:general>\",\n\t\tbackend: \"sdk\",\n\t},\n\t{\n\t\tname: \"explorer\",\n\t\tdescription:\n\t\t\t'Fast read-only agent for finding files, searching code, and answering codebase questions. State thoroughness in the prompt: \"quick\" for a targeted lookup, \"medium\" for checking likely related locations, or \"very thorough\" for exhaustive sweeps across locations and naming conventions',\n\t\ttools: [...EXPLORER_TOOLS],\n\t\tsystemPrompt: [\n\t\t\t\"You are a fast read-only exploration agent; return findings as quickly as possible.\",\n\t\t\t\"Explore the delegated question without modifying files.\",\n\t\t\t\"Use read and search tools to gather exact evidence, batching independent searches and reads instead of running them one at a time.\",\n\t\t\t\"Match the depth the caller requested: quick targets the direct question, medium checks likely related locations, and very thorough sweeps multiple locations and naming conventions.\",\n\t\t\t\"Return concise findings with precise paths, symbols, relationships, and unresolved uncertainties.\",\n\t\t].join(\"\\n\"),\n\t\tsource: \"builtin\",\n\t\tfilePath: \"<builtin:explorer>\",\n\t\tbackend: \"sdk\",\n\t},\n];\n\nfunction isDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction findNearestProjectAgentsDir(cwd: string): string | undefined {\n\tlet current = cwd;\n\twhile (true) {\n\t\tconst candidate = join(current, CONFIG_DIR_NAME, \"agents\");\n\t\tif (isDirectory(candidate)) return candidate;\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return undefined;\n\t\tcurrent = parent;\n\t}\n}\n\nfunction parseTools(raw: string | undefined, filePath: string): string[] {\n\tif (!raw?.trim()) return [...DEFAULT_AGENT_TOOLS];\n\tconst tools = [\n\t\t...new Set(\n\t\t\traw\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((tool) => tool.trim())\n\t\t\t\t.filter(Boolean),\n\t\t),\n\t];\n\tconst unsupported = tools.filter((tool) => !BUILTIN_TOOL_SET.has(tool));\n\tif (unsupported.length > 0) {\n\t\tthrow new Error(`Unsupported tool(s) ${unsupported.join(\", \")} in ${filePath}.`);\n\t}\n\treturn tools;\n}\n\n// Frontmatter model/thinking keys are intentionally ignored: agent files\n// travel across machines (project repos), so a pinned model rarely exists\n// in the reader's environment. Model and thinking come from /agents\n// overrides or default to the parent session.\nfunction parseAgentFile(filePath: string, source: Exclude<AgentSource, \"builtin\">): AgentDefinition {\n\tconst content = readFileSync(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);\n\tconst name = frontmatter.name?.trim() ?? \"\";\n\tconst description = frontmatter.description?.trim() ?? \"\";\n\tif (!name) throw new Error(\"Missing required frontmatter field: name.\");\n\tif (!AGENT_NAME_PATTERN.test(name)) {\n\t\tthrow new Error(`Agent name \"${name}\" must use lowercase letters, digits, hyphens, or underscores.`);\n\t}\n\tif (!description) throw new Error(\"Missing required frontmatter field: description.\");\n\tif (!body.trim()) throw new Error(\"Agent prompt body must not be empty.\");\n\tconst backend = frontmatter.backend?.trim();\n\tif (backend && backend !== \"sdk\") throw new Error(`Backend \"${backend}\" is not implemented; use \"sdk\".`);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\ttools: parseTools(frontmatter.tools, filePath),\n\t\tsystemPrompt: body.trim(),\n\t\tsource,\n\t\tfilePath,\n\t\tbackend: \"sdk\",\n\t};\n}\n\nfunction loadAgentDirectory(\n\tdir: string,\n\tsource: Exclude<AgentSource, \"builtin\">,\n): { agents: AgentDefinition[]; diagnostics: AgentDiagnostic[] } {\n\tif (!existsSync(dir)) return { agents: [], diagnostics: [] };\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));\n\t} catch (error) {\n\t\treturn {\n\t\t\tagents: [],\n\t\t\tdiagnostics: [{ path: dir, source, message: error instanceof Error ? error.message : String(error) }],\n\t\t};\n\t}\n\tconst agents: AgentDefinition[] = [];\n\tconst diagnostics: AgentDiagnostic[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.name.endsWith(\".md\") || (!entry.isFile() && !entry.isSymbolicLink())) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\ttry {\n\t\t\tagents.push(parseAgentFile(filePath, source));\n\t\t} catch (error) {\n\t\t\tdiagnostics.push({\n\t\t\t\tpath: filePath,\n\t\t\t\tsource,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\treturn { agents, diagnostics };\n}\n\nexport function discoverAgents(\n\tcwd: string,\n\toptions: { projectTrusted: boolean; agentDir?: string },\n): AgentDiscoveryResult {\n\tconst userDir = join(options.agentDir ?? getAgentDir(), \"agents\");\n\tconst projectAgentsDir = findNearestProjectAgentsDir(cwd);\n\tconst user = loadAgentDirectory(userDir, \"user\");\n\tconst project =\n\t\toptions.projectTrusted && projectAgentsDir\n\t\t\t? loadAgentDirectory(projectAgentsDir, \"project\")\n\t\t\t: { agents: [], diagnostics: [] };\n\tconst agents = new Map(BUILTIN_AGENTS.map((agent) => [agent.name, { ...agent, tools: [...agent.tools] }]));\n\tfor (const agent of user.agents) agents.set(agent.name, agent);\n\tfor (const agent of project.agents) agents.set(agent.name, agent);\n\treturn {\n\t\tagents: [...agents.values()],\n\t\tdiagnostics: [...user.diagnostics, ...project.diagnostics],\n\t\tprojectAgentsDir,\n\t\tprojectAgentsTrusted: options.projectTrusted,\n\t};\n}\n\nexport function subagentToolDescription(discovery: AgentDiscoveryResult): string {\n\tconst agentLines: string[] = [];\n\tlet agentListLength = 0;\n\tfor (const [index, agent] of discovery.agents.entries()) {\n\t\tconst normalizedDescription = agent.description.replace(/\\s+/gu, \" \").trim();\n\t\tconst description =\n\t\t\tBuffer.byteLength(normalizedDescription, \"utf8\") <= TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT\n\t\t\t\t? normalizedDescription\n\t\t\t\t: `${utf8Prefix(normalizedDescription, TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT - Buffer.byteLength(\"…\", \"utf8\"))}…`;\n\t\tconst defaultLabel = agent.name === \"general\" ? \" (default)\" : \"\";\n\t\tconst tools = agent.tools.length > 0 ? agent.tools.join(\", \") : \"none\";\n\t\tconst line = `- ${agent.name}${defaultLabel}: ${description} (Tools: ${tools})`;\n\t\tconst lineLength = Buffer.byteLength(line, \"utf8\") + 1;\n\t\tif (agentListLength + lineLength > TOOL_DESCRIPTION_AGENT_LIST_LIMIT) {\n\t\t\tlet omitted = discovery.agents.length - index;\n\t\t\tlet notice = `- ${omitted} additional profile${omitted === 1 ? \"\" : \"s\"} omitted from this bounded description; /agents lists every profile.`;\n\t\t\twhile (\n\t\t\t\tagentLines.length > 0 &&\n\t\t\t\tagentListLength + Buffer.byteLength(notice, \"utf8\") + 1 > TOOL_DESCRIPTION_AGENT_LIST_LIMIT\n\t\t\t) {\n\t\t\t\tconst removed = agentLines.pop();\n\t\t\t\tif (!removed) break;\n\t\t\t\tagentListLength -= Buffer.byteLength(removed, \"utf8\") + 1;\n\t\t\t\tomitted++;\n\t\t\t\tnotice = `- ${omitted} additional profile${omitted === 1 ? \"\" : \"s\"} omitted from this bounded description; /agents lists every profile.`;\n\t\t\t}\n\t\t\tagentLines.push(notice);\n\t\t\tbreak;\n\t\t}\n\t\tagentLines.push(line);\n\t\tagentListLength += lineLength;\n\t}\n\n\treturn [\n\t\t\"Delegate bounded work to isolated one-shot subagents. Choose exactly one mode: prompt for one task, or tasks for independent parallel work. For sequential work, call this tool again with the previous result folded into the next briefing.\",\n\t\t\"\",\n\t\t\"Available agent profiles:\",\n\t\t...agentLines,\n\t\t\"\",\n\t\t\"When not to delegate:\",\n\t\t\"- Read a known file directly with read, or search for a known symbol directly with grep.\",\n\t\t\"- Do not delegate a trivial task when the coordination overhead exceeds the work.\",\n\t\t\"\",\n\t\t\"Writing the briefing:\",\n\t\t\"- Brief the worker like a capable colleague entering the project now: include the objective, relevant context, exact paths or symbols already known, constraints, and expected output.\",\n\t\t\"- Never delegate understanding with vague instructions such as 'fix this based on your findings'; state what must be investigated or changed and whether the worker should research, implement, or review.\",\n\t\t\"- Ask for a concise report when only findings are needed. Workers cannot see the parent conversation or ask the end user questions.\",\n\t\t\"\",\n\t\t\"Subagent results are reports for you to interpret and relay. Surface the important findings, changes, verification, and unresolved risks to the user.\",\n\t].join(\"\\n\");\n}\n"]}
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../../src/extensions/subagent/agents.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Dirent, existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { CONFIG_DIR_NAME, getAgentDir } from \"../../config.ts\";\nimport { parseFrontmatter } from \"../../utils/frontmatter.ts\";\nimport { utf8Prefix } from \"./activity.ts\";\nimport { BUILTIN_TOOL_NAMES, DEFAULT_AGENT_TOOLS, EXPLORER_TOOLS, MAX_CONCURRENCY, MAX_TASKS } from \"./constants.ts\";\nimport type { AgentDefinition, AgentDiagnostic, AgentDiscoveryResult, AgentSource } from \"./types.ts\";\n\nconst AGENT_NAME_PATTERN = /^[a-z0-9][a-z0-9_-]{0,79}$/u;\nconst BUILTIN_TOOL_SET = new Set<string>(BUILTIN_TOOL_NAMES);\nconst TOOL_DESCRIPTION_AGENT_LIST_LIMIT = 8_000;\nconst TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT = 480;\n\nconst BUILTIN_AGENTS: AgentDefinition[] = [\n\t{\n\t\tname: \"general\",\n\t\tdescription:\n\t\t\t\"Implementation agent for bounded coding tasks: edits, fixes, refactors, and verification in a scoped area. Use when the task changes files; use explorer for read-only questions\",\n\t\ttools: [...DEFAULT_AGENT_TOOLS],\n\t\tsystemPrompt: [\n\t\t\t\"Work independently on the delegated task from start to finish.\",\n\t\t\t\"Inspect relevant repository instructions before changing files.\",\n\t\t\t\"Prefer editing existing files; never create documentation files unless the task explicitly asks for them.\",\n\t\t\t\"Keep changes focused, run appropriate verification, and report exact paths, checks, blockers, and remaining risks.\",\n\t\t].join(\"\\n\"),\n\t\tsource: \"builtin\",\n\t\tfilePath: \"<builtin:general>\",\n\t\tbackend: \"sdk\",\n\t},\n\t{\n\t\tname: \"explorer\",\n\t\tdescription:\n\t\t\t'Fast read-only agent for finding files, searching code, and answering codebase questions. Can inspect git history (log, blame, diff) via read-only bash. State thoroughness in the prompt: \"quick\" for a targeted lookup, \"medium\" for checking likely related locations, or \"very thorough\" for exhaustive sweeps across locations and naming conventions',\n\t\ttools: [...EXPLORER_TOOLS],\n\t\tsystemPrompt: [\n\t\t\t\"You are a fast read-only exploration agent; return findings as quickly as possible.\",\n\t\t\t\"Explore the delegated question without modifying files.\",\n\t\t\t\"Use read and search tools to gather exact evidence, batching independent searches and reads instead of running them one at a time.\",\n\t\t\t\"bash is for read-only inspection only: git log/diff/blame/show/status, ls, wc, head, tail, cat, and similar.\",\n\t\t\t\"Never run anything that modifies state: no file creation or deletion, no redirect (>, >>) or heredoc writes, no temp files, no git commands that write (add, commit, checkout, restore, stash, clean), no installs, no network access.\",\n\t\t\t\"Match the depth the caller requested: quick targets the direct question, medium checks likely related locations, and very thorough sweeps multiple locations and naming conventions.\",\n\t\t\t\"Return concise findings with precise paths, symbols, relationships, and unresolved uncertainties.\",\n\t\t].join(\"\\n\"),\n\t\tsource: \"builtin\",\n\t\tfilePath: \"<builtin:explorer>\",\n\t\tbackend: \"sdk\",\n\t\tomitContextFiles: true,\n\t},\n];\n\nfunction isDirectory(path: string): boolean {\n\ttry {\n\t\treturn statSync(path).isDirectory();\n\t} catch {\n\t\treturn false;\n\t}\n}\n\nfunction findNearestProjectAgentsDir(cwd: string): string | undefined {\n\tlet current = cwd;\n\twhile (true) {\n\t\tconst candidate = join(current, CONFIG_DIR_NAME, \"agents\");\n\t\tif (isDirectory(candidate)) return candidate;\n\t\tconst parent = dirname(current);\n\t\tif (parent === current) return undefined;\n\t\tcurrent = parent;\n\t}\n}\n\nfunction parseTools(raw: string | undefined, filePath: string): string[] {\n\tif (!raw?.trim()) return [...DEFAULT_AGENT_TOOLS];\n\tconst tools = [\n\t\t...new Set(\n\t\t\traw\n\t\t\t\t.split(\",\")\n\t\t\t\t.map((tool) => tool.trim())\n\t\t\t\t.filter(Boolean),\n\t\t),\n\t];\n\tconst unsupported = tools.filter((tool) => !BUILTIN_TOOL_SET.has(tool));\n\tif (unsupported.length > 0) {\n\t\tthrow new Error(`Unsupported tool(s) ${unsupported.join(\", \")} in ${filePath}.`);\n\t}\n\treturn tools;\n}\n\n// Frontmatter model/thinking keys are intentionally ignored: agent files\n// travel across machines (project repos), so a pinned model rarely exists\n// in the reader's environment. Model and thinking come from /agents\n// overrides or default to the parent session.\nfunction parseAgentFile(filePath: string, source: Exclude<AgentSource, \"builtin\">): AgentDefinition {\n\tconst content = readFileSync(filePath, \"utf8\");\n\tconst { frontmatter, body } = parseFrontmatter<Record<string, string>>(content);\n\tconst name = frontmatter.name?.trim() ?? \"\";\n\tconst description = frontmatter.description?.trim() ?? \"\";\n\tif (!name) throw new Error(\"Missing required frontmatter field: name.\");\n\tif (!AGENT_NAME_PATTERN.test(name)) {\n\t\tthrow new Error(`Agent name \"${name}\" must use lowercase letters, digits, hyphens, or underscores.`);\n\t}\n\tif (!description) throw new Error(\"Missing required frontmatter field: description.\");\n\tif (!body.trim()) throw new Error(\"Agent prompt body must not be empty.\");\n\tconst backend = frontmatter.backend?.trim();\n\tif (backend && backend !== \"sdk\") throw new Error(`Backend \"${backend}\" is not implemented; use \"sdk\".`);\n\treturn {\n\t\tname,\n\t\tdescription,\n\t\ttools: parseTools(frontmatter.tools, filePath),\n\t\tsystemPrompt: body.trim(),\n\t\tsource,\n\t\tfilePath,\n\t\tbackend: \"sdk\",\n\t};\n}\n\nfunction loadAgentDirectory(\n\tdir: string,\n\tsource: Exclude<AgentSource, \"builtin\">,\n): { agents: AgentDefinition[]; diagnostics: AgentDiagnostic[] } {\n\tif (!existsSync(dir)) return { agents: [], diagnostics: [] };\n\tlet entries: Dirent[];\n\ttry {\n\t\tentries = readdirSync(dir, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name));\n\t} catch (error) {\n\t\treturn {\n\t\t\tagents: [],\n\t\t\tdiagnostics: [{ path: dir, source, message: error instanceof Error ? error.message : String(error) }],\n\t\t};\n\t}\n\tconst agents: AgentDefinition[] = [];\n\tconst diagnostics: AgentDiagnostic[] = [];\n\tfor (const entry of entries) {\n\t\tif (!entry.name.endsWith(\".md\") || (!entry.isFile() && !entry.isSymbolicLink())) continue;\n\t\tconst filePath = join(dir, entry.name);\n\t\ttry {\n\t\t\tagents.push(parseAgentFile(filePath, source));\n\t\t} catch (error) {\n\t\t\tdiagnostics.push({\n\t\t\t\tpath: filePath,\n\t\t\t\tsource,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t});\n\t\t}\n\t}\n\treturn { agents, diagnostics };\n}\n\nexport function discoverAgents(\n\tcwd: string,\n\toptions: { projectTrusted: boolean; agentDir?: string },\n): AgentDiscoveryResult {\n\tconst userDir = join(options.agentDir ?? getAgentDir(), \"agents\");\n\tconst projectAgentsDir = findNearestProjectAgentsDir(cwd);\n\tconst user = loadAgentDirectory(userDir, \"user\");\n\tconst project =\n\t\toptions.projectTrusted && projectAgentsDir\n\t\t\t? loadAgentDirectory(projectAgentsDir, \"project\")\n\t\t\t: { agents: [], diagnostics: [] };\n\tconst agents = new Map(BUILTIN_AGENTS.map((agent) => [agent.name, { ...agent, tools: [...agent.tools] }]));\n\tfor (const agent of user.agents) agents.set(agent.name, agent);\n\tfor (const agent of project.agents) agents.set(agent.name, agent);\n\treturn {\n\t\tagents: [...agents.values()],\n\t\tdiagnostics: [...user.diagnostics, ...project.diagnostics],\n\t\tprojectAgentsDir,\n\t\tprojectAgentsTrusted: options.projectTrusted,\n\t};\n}\n\nexport function subagentToolDescription(discovery: AgentDiscoveryResult): string {\n\tconst agentLines: string[] = [];\n\tlet agentListLength = 0;\n\tfor (const [index, agent] of discovery.agents.entries()) {\n\t\tconst normalizedDescription = agent.description.replace(/\\s+/gu, \" \").trim();\n\t\tconst description =\n\t\t\tBuffer.byteLength(normalizedDescription, \"utf8\") <= TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT\n\t\t\t\t? normalizedDescription\n\t\t\t\t: `${utf8Prefix(normalizedDescription, TOOL_DESCRIPTION_AGENT_SUMMARY_LIMIT - Buffer.byteLength(\"…\", \"utf8\"))}…`;\n\t\tconst defaultLabel = agent.name === \"general\" ? \" (default)\" : \"\";\n\t\tconst tools = agent.tools.length > 0 ? agent.tools.join(\", \") : \"none\";\n\t\tconst line = `- ${agent.name}${defaultLabel}: ${description} (Tools: ${tools})`;\n\t\tconst lineLength = Buffer.byteLength(line, \"utf8\") + 1;\n\t\tif (agentListLength + lineLength > TOOL_DESCRIPTION_AGENT_LIST_LIMIT) {\n\t\t\tlet omitted = discovery.agents.length - index;\n\t\t\tlet notice = `- ${omitted} additional profile${omitted === 1 ? \"\" : \"s\"} omitted from this bounded description; /agents lists every profile.`;\n\t\t\twhile (\n\t\t\t\tagentLines.length > 0 &&\n\t\t\t\tagentListLength + Buffer.byteLength(notice, \"utf8\") + 1 > TOOL_DESCRIPTION_AGENT_LIST_LIMIT\n\t\t\t) {\n\t\t\t\tconst removed = agentLines.pop();\n\t\t\t\tif (!removed) break;\n\t\t\t\tagentListLength -= Buffer.byteLength(removed, \"utf8\") + 1;\n\t\t\t\tomitted++;\n\t\t\t\tnotice = `- ${omitted} additional profile${omitted === 1 ? \"\" : \"s\"} omitted from this bounded description; /agents lists every profile.`;\n\t\t\t}\n\t\t\tagentLines.push(notice);\n\t\t\tbreak;\n\t\t}\n\t\tagentLines.push(line);\n\t\tagentListLength += lineLength;\n\t}\n\n\treturn [\n\t\t`Delegate bounded work to isolated one-shot subagents. Delegation keeps intermediate tool output (file dumps, search results) out of your context: you receive only the final report. Choose exactly one mode: prompt for one task, or tasks for independent parallel work (up to ${MAX_TASKS} per call; ${MAX_CONCURRENCY} run concurrently, the rest queue). For sequential work, call this tool again with the previous result folded into the next briefing.`,\n\t\t\"\",\n\t\t\"Available agent profiles:\",\n\t\t...agentLines,\n\t\t\"\",\n\t\t\"When not to delegate:\",\n\t\t\"- Read a known file directly with read, or search for a known symbol directly with grep.\",\n\t\t\"- Prefer direct tools when a directed lookup answers the question; delegate when the investigation will clearly take more than ~3 searches or would flood your context with intermediate output.\",\n\t\t\"- Do not delegate a trivial task when the coordination overhead exceeds the work.\",\n\t\t\"\",\n\t\t\"Writing the briefing:\",\n\t\t\"- Brief the worker like a capable colleague entering the project now: include the objective, relevant context, exact paths or symbols already known, constraints, and expected output.\",\n\t\t\"- Never delegate understanding with vague instructions such as 'fix this based on your findings'; state what must be investigated or changed and whether the worker should research, implement, or review.\",\n\t\t\"- Lookups: hand over the exact command or file. Investigations: hand over the question — prescribed steps become dead weight when the premise is wrong.\",\n\t\t'- If you need a short answer, say so (e.g. \"report in under 200 words\"). Workers cannot see the parent conversation or ask the end user questions.',\n\t\t'- Example briefing (explorer): \"Quick: which commit introduced the retry loop in src/net/client.ts? Use git log/blame; report the hash and a one-line summary.\"',\n\t\t\"\",\n\t\t\"The subagent's report is not shown to the user. Interpret it and relay the important findings, changes, verification, and unresolved risks in your own reply.\",\n\t].join(\"\\n\");\n}\n"]}
|
|
@@ -9,7 +9,7 @@ export declare const TASK_RETRY_LIMIT = 2;
|
|
|
9
9
|
export declare const TASK_RETRY_BASE_DELAY_MS = 1000;
|
|
10
10
|
export declare const BUILTIN_TOOL_NAMES: readonly ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
11
11
|
export declare const DEFAULT_AGENT_TOOLS: readonly ["read", "bash", "edit", "write"];
|
|
12
|
-
export declare const EXPLORER_TOOLS: readonly ["read", "grep", "find", "ls"];
|
|
12
|
+
export declare const EXPLORER_TOOLS: readonly ["read", "grep", "find", "ls", "bash"];
|
|
13
13
|
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
14
14
|
export declare const LIVE_TEXT_LIMIT: number;
|
|
15
15
|
export declare const ACTIVITY_LIMIT = 80;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAC9C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,eAAe,IAAI,CAAC;AAKjC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,eAAO,MAAM,kBAAkB,kEAAmE,CAAC;AAEnG,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,kBAAkB,aAAa,CAAC;AAC7C,eAAO,MAAM,mBAAmB,aAAa,CAAC;AAC9C,eAAO,MAAM,qBAAqB,WAAW,CAAC;AAC9C,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AAEpD,eAAO,MAAM,SAAS,IAAI,CAAC;AAC3B,eAAO,MAAM,eAAe,IAAI,CAAC;AAKjC,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,eAAO,MAAM,wBAAwB,OAAQ,CAAC;AAE9C,eAAO,MAAM,kBAAkB,kEAAmE,CAAC;AAEnG,eAAO,MAAM,mBAAmB,4CAA6C,CAAC;AAG9E,eAAO,MAAM,cAAc,iDAAkD,CAAC;AAE9E,eAAO,MAAM,eAAe,sEAQiB,CAAC;AAE9C,eAAO,MAAM,eAAe,QAAW,CAAC;AACxC,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAC7C,eAAO,MAAM,mBAAmB,QAAY,CAAC;AAC7C,eAAO,MAAM,0BAA0B,QAAY,CAAC;AACpD,eAAO,MAAM,qBAAqB,QAAY,CAAC;AAC/C,eAAO,MAAM,gBAAgB,QAAW,CAAC;AACzC,eAAO,MAAM,oBAAoB,QAAa,CAAC;AAG/C,eAAO,MAAM,sBAAsB,KAAK,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\n\nexport const SUBAGENT_TOOL_NAME = \"subagent\";\nexport const SUBAGENT_TOOL_LABEL = \"Subagent\";\nexport const SUBAGENT_COMMAND_NAME = \"agents\";\nexport const SUBAGENT_CONFIG_VERSION = 1;\nexport const SUBAGENT_CONFIG_FILE = \"subagent.json\";\n\nexport const MAX_TASKS = 8;\nexport const MAX_CONCURRENCY = 5;\n\n// Task-level retry for failures that bypass the session's own auto-retry\n// (preflight throws such as auth checks). Only runs that produced nothing\n// are retried, so a retry never discards partial work.\nexport const TASK_RETRY_LIMIT = 2;\nexport const TASK_RETRY_BASE_DELAY_MS = 1_000;\n\nexport const BUILTIN_TOOL_NAMES = [\"read\", \"bash\", \"edit\", \"write\", \"grep\", \"find\", \"ls\"] as const;\n// Matches the parent session's default toolset; bash covers search needs.\nexport const DEFAULT_AGENT_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"] as const;\n// Read-only agents get the standalone search tools plus bash for read-only\n// inspection (git history, counting); the system prompt constrains bash.\nexport const EXPLORER_TOOLS = [\"read\", \"grep\", \"find\", \"ls\", \"bash\"] as const;\n\nexport const THINKING_LEVELS = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n] as const satisfies readonly ThinkingLevel[];\n\nexport const LIVE_TEXT_LIMIT = 8 * 1024;\nexport const ACTIVITY_LIMIT = 80;\nexport const ACTIVITY_TEXT_LIMIT = 24 * 1024;\nexport const SINGLE_OUTPUT_LIMIT = 32 * 1024;\nexport const PARALLEL_TASK_OUTPUT_LIMIT = 12 * 1024;\nexport const PARALLEL_OUTPUT_LIMIT = 48 * 1024;\nexport const ERROR_TEXT_LIMIT = 8 * 1024;\nexport const DETAILS_OUTPUT_LIMIT = 120 * 1024;\n// Bounded per run inside DETAILS_OUTPUT_LIMIT; each activity costs up to\n// ~600 bytes, so 12 keeps an 8-run parallel batch from starving outputs.\nexport const DETAILS_ACTIVITY_LIMIT = 12;\n"]}
|
|
@@ -13,8 +13,9 @@ export const TASK_RETRY_BASE_DELAY_MS = 1_000;
|
|
|
13
13
|
export const BUILTIN_TOOL_NAMES = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
14
14
|
// Matches the parent session's default toolset; bash covers search needs.
|
|
15
15
|
export const DEFAULT_AGENT_TOOLS = ["read", "bash", "edit", "write"];
|
|
16
|
-
// Read-only agents get the standalone search tools
|
|
17
|
-
|
|
16
|
+
// Read-only agents get the standalone search tools plus bash for read-only
|
|
17
|
+
// inspection (git history, counting); the system prompt constrains bash.
|
|
18
|
+
export const EXPLORER_TOOLS = ["read", "grep", "find", "ls", "bash"];
|
|
18
19
|
export const THINKING_LEVELS = [
|
|
19
20
|
"off",
|
|
20
21
|
"minimal",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,yEAAyE;AACzE,0EAA0E;AAC1E,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AACnG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAC9E,
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/extensions/subagent/constants.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,UAAU,CAAC;AAC9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,QAAQ,CAAC;AAC9C,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAC3B,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,CAAC;AAEjC,yEAAyE;AACzE,0EAA0E;AAC1E,uDAAuD;AACvD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAClC,MAAM,CAAC,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAE9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAU,CAAC;AACnG,0EAA0E;AAC1E,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC;AAC9E,2EAA2E;AAC3E,yEAAyE;AACzE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAU,CAAC;AAE9E,MAAM,CAAC,MAAM,eAAe,GAAG;IAC9B,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,MAAM;IACN,OAAO;IACP,KAAK;CACuC,CAAC;AAE9C,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAG,IAAI,CAAC;AACxC,MAAM,CAAC,MAAM,cAAc,GAAG,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC7C,MAAM,CAAC,MAAM,0BAA0B,GAAG,EAAE,GAAG,IAAI,CAAC;AACpD,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,CAAC;AAC/C,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,GAAG,IAAI,CAAC;AACzC,MAAM,CAAC,MAAM,oBAAoB,GAAG,GAAG,GAAG,IAAI,CAAC;AAC/C,yEAAyE;AACzE,yEAAyE;AACzE,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAC","sourcesContent":["import type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\n\nexport const SUBAGENT_TOOL_NAME = \"subagent\";\nexport const SUBAGENT_TOOL_LABEL = \"Subagent\";\nexport const SUBAGENT_COMMAND_NAME = \"agents\";\nexport const SUBAGENT_CONFIG_VERSION = 1;\nexport const SUBAGENT_CONFIG_FILE = \"subagent.json\";\n\nexport const MAX_TASKS = 8;\nexport const MAX_CONCURRENCY = 5;\n\n// Task-level retry for failures that bypass the session's own auto-retry\n// (preflight throws such as auth checks). Only runs that produced nothing\n// are retried, so a retry never discards partial work.\nexport const TASK_RETRY_LIMIT = 2;\nexport const TASK_RETRY_BASE_DELAY_MS = 1_000;\n\nexport const BUILTIN_TOOL_NAMES = [\"read\", \"bash\", \"edit\", \"write\", \"grep\", \"find\", \"ls\"] as const;\n// Matches the parent session's default toolset; bash covers search needs.\nexport const DEFAULT_AGENT_TOOLS = [\"read\", \"bash\", \"edit\", \"write\"] as const;\n// Read-only agents get the standalone search tools plus bash for read-only\n// inspection (git history, counting); the system prompt constrains bash.\nexport const EXPLORER_TOOLS = [\"read\", \"grep\", \"find\", \"ls\", \"bash\"] as const;\n\nexport const THINKING_LEVELS = [\n\t\"off\",\n\t\"minimal\",\n\t\"low\",\n\t\"medium\",\n\t\"high\",\n\t\"xhigh\",\n\t\"max\",\n] as const satisfies readonly ThinkingLevel[];\n\nexport const LIVE_TEXT_LIMIT = 8 * 1024;\nexport const ACTIVITY_LIMIT = 80;\nexport const ACTIVITY_TEXT_LIMIT = 24 * 1024;\nexport const SINGLE_OUTPUT_LIMIT = 32 * 1024;\nexport const PARALLEL_TASK_OUTPUT_LIMIT = 12 * 1024;\nexport const PARALLEL_OUTPUT_LIMIT = 48 * 1024;\nexport const ERROR_TEXT_LIMIT = 8 * 1024;\nexport const DETAILS_OUTPUT_LIMIT = 120 * 1024;\n// Bounded per run inside DETAILS_OUTPUT_LIMIT; each activity costs up to\n// ~600 bytes, so 12 keeps an 8-run parallel batch from starving outputs.\nexport const DETAILS_ACTIVITY_LIMIT = 12;\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEX,YAAY,EAGZ,MAAM,gCAAgC,CAAC;AA8LxC,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAsGvD","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getSupportedThinkingLevels } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { discoverAgents, subagentToolDescription } from \"./agents.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from \"./constants.ts\";\nimport { type PickerItem, SearchPickerComponent } from \"./picker.ts\";\nimport { renderSubagentCall, renderSubagentResult } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation, statusSummary } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { loadSubagentConfig, resetProfileOverrides, updateProfileOverride } from \"./settings.ts\";\nimport type { AgentDefinition, AgentDiscoveryResult, SubagentDetails, SubagentProfileOverride } from \"./types.ts\";\n\nfunction formatParentModel(model: { provider: string; id: string } | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"none\";\n}\n\n// Settings resolve in two layers: a saved override wins, otherwise the\n// profile inherits the parent session. Inherited rows show the value\n// currently in effect so the menu doubles as a status view.\nfunction effectiveSettings(\n\toverride: SubagentProfileOverride | undefined,\n\tctx: ExtensionCommandContext,\n\tparentThinking: string,\n): { model: string; thinking: string } {\n\treturn {\n\t\tmodel: override?.model ?? `inherit (${formatParentModel(ctx.model)})`,\n\t\tthinking: override?.thinking ?? `inherit (${parentThinking})`,\n\t};\n}\n\n// The thinking picker lists what the effective model actually supports;\n// if the saved override names a model that is unavailable right now,\n// every level shows and the runtime clamp decides.\nfunction effectiveModelFor(\n\tctx: ExtensionCommandContext,\n\toverride: SubagentProfileOverride | undefined,\n): Model<Api> | undefined {\n\tif (override?.model) {\n\t\tconst separator = override.model.indexOf(\"/\");\n\t\tif (separator <= 0) return undefined;\n\t\treturn ctx.modelRegistry.find(override.model.slice(0, separator), override.model.slice(separator + 1));\n\t}\n\treturn ctx.model;\n}\n\nasync function pickModel(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n): Promise<void> {\n\tconst items: PickerItem<string | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${formatParentModel(ctx.model)})`,\n\t\t\tcurrent: !override?.model,\n\t\t},\n\t\t...ctx.modelRegistry.getAvailable().map((model) => ({\n\t\t\tvalue: `${model.provider}/${model.id}`,\n\t\t\tlabel: `${model.provider}/${model.id}`,\n\t\t\tdetail: model.name && model.name !== model.id ? model.name : undefined,\n\t\t\tcurrent: override?.model === `${model.provider}/${model.id}`,\n\t\t})),\n\t];\n\tconst selected = await ctx.ui.custom<string | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Model for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { model: selected ?? undefined }, getAgentDir());\n}\n\nasync function pickThinking(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n\tparentThinking: string,\n): Promise<void> {\n\tconst model = effectiveModelFor(ctx, override);\n\tconst levels = model ? (getSupportedThinkingLevels(model) as ThinkingLevel[]) : [...THINKING_LEVELS];\n\tconst items: PickerItem<ThinkingLevel | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${parentThinking})`,\n\t\t\tcurrent: !override?.thinking,\n\t\t},\n\t\t...levels.map((level) => ({ value: level, label: level, current: override?.thinking === level })),\n\t];\n\tconst selected = await ctx.ui.custom<ThinkingLevel | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Thinking for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { thinking: selected ?? undefined }, getAgentDir());\n}\n\nasync function configureProfile(\n\tctx: ExtensionCommandContext,\n\tagent: AgentDefinition,\n\tparentThinking: string,\n): Promise<void> {\n\tconst agentDir = getAgentDir();\n\twhile (true) {\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst override = config.profiles[agent.name];\n\t\tconst effective = effectiveSettings(override, ctx, parentThinking);\n\t\tconst action = await ctx.ui.select(`${agent.name} — ${agent.description}`, [\n\t\t\t`Model: ${effective.model}`,\n\t\t\t`Thinking: ${effective.thinking}`,\n\t\t\t\"Back\",\n\t\t]);\n\t\tif (!action || action === \"Back\") return;\n\t\tif (action.startsWith(\"Model:\")) {\n\t\t\tawait pickModel(ctx, agent.name, override);\n\t\t\tcontinue;\n\t\t}\n\t\tawait pickThinking(ctx, agent.name, override, parentThinking);\n\t}\n}\n\nasync function syncParentProviders(\n\truntime: ModelRuntime,\n\tregistry: ModelRegistry,\n\tsyncedIds: Set<string>,\n): Promise<void> {\n\tconst nextIds = new Set(registry.getRegisteredProviderIds());\n\tfor (const id of syncedIds) {\n\t\tif (!nextIds.has(id)) runtime.unregisterProvider(id);\n\t}\n\tfor (const id of nextIds) {\n\t\tconst native = registry.getRegisteredNativeProvider(id);\n\t\tif (native) runtime.registerNativeProvider(native);\n\t\telse {\n\t\t\tconst config = registry.getRegisteredProviderConfig(id);\n\t\t\tif (config) runtime.registerProvider(id, config);\n\t\t}\n\t\tconst auth = await registry.getProviderAuth(id);\n\t\tif (auth?.auth.apiKey) await runtime.setRuntimeApiKey(id, auth.auth.apiKey, { allowNetwork: false });\n\t}\n\tsyncedIds.clear();\n\tfor (const id of nextIds) syncedIds.add(id);\n}\n\nasync function showAgentsCommand(ctx: ExtensionCommandContext, parentThinking: string): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/agents requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst agentDir = getAgentDir();\n\tconst reset = \"Reset all overrides\";\n\tconst done = \"Done\";\n\twhile (true) {\n\t\tconst discovery = discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir });\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst rows = discovery.agents.map((agent) => {\n\t\t\tconst effective = effectiveSettings(config.profiles[agent.name], ctx, parentThinking);\n\t\t\treturn `${agent.name} — model: ${effective.model} · thinking: ${effective.thinking}`;\n\t\t});\n\t\tconst issueCount = discovery.diagnostics.length;\n\t\tconst issues = issueCount ? [`Show ${issueCount} agent file issue${issueCount === 1 ? \"\" : \"s\"}`] : [];\n\t\tconst action = await ctx.ui.select(\"Subagent profiles — select one to configure\", [\n\t\t\t...rows,\n\t\t\t...issues,\n\t\t\treset,\n\t\t\tdone,\n\t\t]);\n\t\tif (!action || action === done) return;\n\t\tif (action === reset) {\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t\"Reset profile overrides?\",\n\t\t\t\t\"Clear every saved Subagent model and thinking override?\",\n\t\t\t);\n\t\t\tif (confirmed) {\n\t\t\t\tawait resetProfileOverrides(agentDir);\n\t\t\t\tctx.ui.notify(\"All profiles now inherit the parent session.\", \"info\");\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (issues.length > 0 && action === issues[0]) {\n\t\t\tctx.ui.notify(\n\t\t\t\tdiscovery.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`).join(\"\\n\"),\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst agent = discovery.agents.find((candidate) => action.startsWith(`${candidate.name} — `));\n\t\tif (agent) await configureProfile(ctx, agent, parentThinking);\n\t}\n}\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\tconst syncedProviderIds = new Set<string>();\n\tlet modelRuntimePromise: Promise<ModelRuntime> | undefined;\n\n\tconst getModelRuntime = async (ctx: ExtensionContext): Promise<ModelRuntime> => {\n\t\tif (!modelRuntimePromise) {\n\t\t\tconst agentDir = getAgentDir();\n\t\t\tconst created = ModelRuntime.create({\n\t\t\t\tauthPath: join(agentDir, \"auth.json\"),\n\t\t\t\tmodelsPath: join(agentDir, \"models.json\"),\n\t\t\t});\n\t\t\t// A transient failure (network, filesystem) must not brick the tool\n\t\t\t// for the rest of the session: drop the rejected promise so the\n\t\t\t// next invocation retries creation.\n\t\t\tcreated.catch(() => {\n\t\t\t\tif (modelRuntimePromise === created) modelRuntimePromise = undefined;\n\t\t\t});\n\t\t\tmodelRuntimePromise = created;\n\t\t}\n\t\tconst runtime = await modelRuntimePromise;\n\t\tawait syncParentProviders(runtime, ctx.modelRegistry, syncedProviderIds);\n\t\treturn runtime;\n\t};\n\n\tconst registerSubagentTool = (discovery: AgentDiscoveryResult): void => {\n\t\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails>({\n\t\t\tname: SUBAGENT_TOOL_NAME,\n\t\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\t\tdescription: subagentToolDescription(discovery),\n\t\t\tpromptSnippet: \"Delegate focused research, review, or implementation tasks to isolated subagents\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use subagent when a bounded task benefits from isolated context or parallel investigation; choose a profile and write its briefing using the tool description.\",\n\t\t\t],\n\t\t\tparameters: SubagentParamsSchema,\n\t\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t\tconst discovery = discoverAgents(ctx.cwd, {\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t});\n\t\t\t\tconst runtime = await getModelRuntime(ctx);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tagents: discovery.agents,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: runtime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal,\n\t\t\t\t\tgate,\n\t\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t\t},\n\t\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t},\n\t\t\trenderCall(args, theme) {\n\t\t\t\treturn renderSubagentCall(args, theme);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderSubagentResult(result, options, theme, context.isError);\n\t\t\t},\n\t\t});\n\t};\n\n\tregisterSubagentTool(discoverAgents(process.cwd(), { projectTrusted: false, agentDir: getAgentDir() }));\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tregisterSubagentTool(\n\t\t\tdiscoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir: getAgentDir() }),\n\t\t);\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAEX,YAAY,EAGZ,MAAM,gCAAgC,CAAC;AA+LxC,MAAM,CAAC,OAAO,UAAU,QAAQ,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAmHvD","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getSupportedThinkingLevels } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { emptyUsage } from \"./activity.ts\";\nimport { discoverAgents, subagentToolDescription } from \"./agents.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from \"./constants.ts\";\nimport { type PickerItem, SearchPickerComponent } from \"./picker.ts\";\nimport { renderSubagentCall, renderSubagentResult } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation, statusSummary } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { loadSubagentConfig, resetProfileOverrides, updateProfileOverride } from \"./settings.ts\";\nimport type { AgentDefinition, AgentDiscoveryResult, SubagentDetails, SubagentProfileOverride } from \"./types.ts\";\n\nfunction formatParentModel(model: { provider: string; id: string } | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"none\";\n}\n\n// Settings resolve in two layers: a saved override wins, otherwise the\n// profile inherits the parent session. Inherited rows show the value\n// currently in effect so the menu doubles as a status view.\nfunction effectiveSettings(\n\toverride: SubagentProfileOverride | undefined,\n\tctx: ExtensionCommandContext,\n\tparentThinking: string,\n): { model: string; thinking: string } {\n\treturn {\n\t\tmodel: override?.model ?? `inherit (${formatParentModel(ctx.model)})`,\n\t\tthinking: override?.thinking ?? `inherit (${parentThinking})`,\n\t};\n}\n\n// The thinking picker lists what the effective model actually supports;\n// if the saved override names a model that is unavailable right now,\n// every level shows and the runtime clamp decides.\nfunction effectiveModelFor(\n\tctx: ExtensionCommandContext,\n\toverride: SubagentProfileOverride | undefined,\n): Model<Api> | undefined {\n\tif (override?.model) {\n\t\tconst separator = override.model.indexOf(\"/\");\n\t\tif (separator <= 0) return undefined;\n\t\treturn ctx.modelRegistry.find(override.model.slice(0, separator), override.model.slice(separator + 1));\n\t}\n\treturn ctx.model;\n}\n\nasync function pickModel(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n): Promise<void> {\n\tconst items: PickerItem<string | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${formatParentModel(ctx.model)})`,\n\t\t\tcurrent: !override?.model,\n\t\t},\n\t\t...ctx.modelRegistry.getAvailable().map((model) => ({\n\t\t\tvalue: `${model.provider}/${model.id}`,\n\t\t\tlabel: `${model.provider}/${model.id}`,\n\t\t\tdetail: model.name && model.name !== model.id ? model.name : undefined,\n\t\t\tcurrent: override?.model === `${model.provider}/${model.id}`,\n\t\t})),\n\t];\n\tconst selected = await ctx.ui.custom<string | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Model for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { model: selected ?? undefined }, getAgentDir());\n}\n\nasync function pickThinking(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n\tparentThinking: string,\n): Promise<void> {\n\tconst model = effectiveModelFor(ctx, override);\n\tconst levels = model ? (getSupportedThinkingLevels(model) as ThinkingLevel[]) : [...THINKING_LEVELS];\n\tconst items: PickerItem<ThinkingLevel | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${parentThinking})`,\n\t\t\tcurrent: !override?.thinking,\n\t\t},\n\t\t...levels.map((level) => ({ value: level, label: level, current: override?.thinking === level })),\n\t];\n\tconst selected = await ctx.ui.custom<ThinkingLevel | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Thinking for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { thinking: selected ?? undefined }, getAgentDir());\n}\n\nasync function configureProfile(\n\tctx: ExtensionCommandContext,\n\tagent: AgentDefinition,\n\tparentThinking: string,\n): Promise<void> {\n\tconst agentDir = getAgentDir();\n\twhile (true) {\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst override = config.profiles[agent.name];\n\t\tconst effective = effectiveSettings(override, ctx, parentThinking);\n\t\tconst action = await ctx.ui.select(`${agent.name} — ${agent.description}`, [\n\t\t\t`Model: ${effective.model}`,\n\t\t\t`Thinking: ${effective.thinking}`,\n\t\t\t\"Back\",\n\t\t]);\n\t\tif (!action || action === \"Back\") return;\n\t\tif (action.startsWith(\"Model:\")) {\n\t\t\tawait pickModel(ctx, agent.name, override);\n\t\t\tcontinue;\n\t\t}\n\t\tawait pickThinking(ctx, agent.name, override, parentThinking);\n\t}\n}\n\nasync function syncParentProviders(\n\truntime: ModelRuntime,\n\tregistry: ModelRegistry,\n\tsyncedIds: Set<string>,\n): Promise<void> {\n\tconst nextIds = new Set(registry.getRegisteredProviderIds());\n\tfor (const id of syncedIds) {\n\t\tif (!nextIds.has(id)) runtime.unregisterProvider(id);\n\t}\n\tfor (const id of nextIds) {\n\t\tconst native = registry.getRegisteredNativeProvider(id);\n\t\tif (native) runtime.registerNativeProvider(native);\n\t\telse {\n\t\t\tconst config = registry.getRegisteredProviderConfig(id);\n\t\t\tif (config) runtime.registerProvider(id, config);\n\t\t}\n\t\tconst auth = await registry.getProviderAuth(id);\n\t\tif (auth?.auth.apiKey) await runtime.setRuntimeApiKey(id, auth.auth.apiKey, { allowNetwork: false });\n\t}\n\tsyncedIds.clear();\n\tfor (const id of nextIds) syncedIds.add(id);\n}\n\nasync function showAgentsCommand(ctx: ExtensionCommandContext, parentThinking: string): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/agents requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst agentDir = getAgentDir();\n\tconst reset = \"Reset all overrides\";\n\tconst done = \"Done\";\n\twhile (true) {\n\t\tconst discovery = discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir });\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst rows = discovery.agents.map((agent) => {\n\t\t\tconst effective = effectiveSettings(config.profiles[agent.name], ctx, parentThinking);\n\t\t\treturn `${agent.name} — model: ${effective.model} · thinking: ${effective.thinking}`;\n\t\t});\n\t\tconst issueCount = discovery.diagnostics.length;\n\t\tconst issues = issueCount ? [`Show ${issueCount} agent file issue${issueCount === 1 ? \"\" : \"s\"}`] : [];\n\t\tconst action = await ctx.ui.select(\"Subagent profiles — select one to configure\", [\n\t\t\t...rows,\n\t\t\t...issues,\n\t\t\treset,\n\t\t\tdone,\n\t\t]);\n\t\tif (!action || action === done) return;\n\t\tif (action === reset) {\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t\"Reset profile overrides?\",\n\t\t\t\t\"Clear every saved Subagent model and thinking override?\",\n\t\t\t);\n\t\t\tif (confirmed) {\n\t\t\t\tawait resetProfileOverrides(agentDir);\n\t\t\t\tctx.ui.notify(\"All profiles now inherit the parent session.\", \"info\");\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (issues.length > 0 && action === issues[0]) {\n\t\t\tctx.ui.notify(\n\t\t\t\tdiscovery.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`).join(\"\\n\"),\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst agent = discovery.agents.find((candidate) => action.startsWith(`${candidate.name} — `));\n\t\tif (agent) await configureProfile(ctx, agent, parentThinking);\n\t}\n}\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\tconst syncedProviderIds = new Set<string>();\n\tlet modelRuntimePromise: Promise<ModelRuntime> | undefined;\n\n\tconst getModelRuntime = async (ctx: ExtensionContext): Promise<ModelRuntime> => {\n\t\tif (!modelRuntimePromise) {\n\t\t\tconst agentDir = getAgentDir();\n\t\t\tconst created = ModelRuntime.create({\n\t\t\t\tauthPath: join(agentDir, \"auth.json\"),\n\t\t\t\tmodelsPath: join(agentDir, \"models.json\"),\n\t\t\t});\n\t\t\t// A transient failure (network, filesystem) must not brick the tool\n\t\t\t// for the rest of the session: drop the rejected promise so the\n\t\t\t// next invocation retries creation.\n\t\t\tcreated.catch(() => {\n\t\t\t\tif (modelRuntimePromise === created) modelRuntimePromise = undefined;\n\t\t\t});\n\t\t\tmodelRuntimePromise = created;\n\t\t}\n\t\tconst runtime = await modelRuntimePromise;\n\t\tawait syncParentProviders(runtime, ctx.modelRegistry, syncedProviderIds);\n\t\treturn runtime;\n\t};\n\n\tconst registerSubagentTool = (discovery: AgentDiscoveryResult): void => {\n\t\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails>({\n\t\t\tname: SUBAGENT_TOOL_NAME,\n\t\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\t\tdescription: subagentToolDescription(discovery),\n\t\t\tpromptSnippet: \"Delegate focused research, review, or implementation tasks to isolated subagents\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use subagent when a bounded task benefits from isolated context or parallel investigation; choose a profile and write its briefing using the tool description.\",\n\t\t\t],\n\t\t\tparameters: SubagentParamsSchema,\n\t\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t\t// First paint before any await: model-runtime creation and task\n\t\t\t\t// resolution can stall on first use (network, filesystem), and\n\t\t\t\t// the transcript card must not sit empty while they do.\n\t\t\t\tonUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Initializing…\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tmode: params.tasks ? \"parallel\" : \"single\",\n\t\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\t\truns: [],\n\t\t\t\t\t\tstartedAt: Date.now(),\n\t\t\t\t\t\tusage: emptyUsage(),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tconst discovery = discoverAgents(ctx.cwd, {\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t});\n\t\t\t\tconst runtime = await getModelRuntime(ctx);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tagents: discovery.agents,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: runtime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal,\n\t\t\t\t\tgate,\n\t\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t\t},\n\t\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t},\n\t\t\trenderCall(args, theme) {\n\t\t\t\treturn renderSubagentCall(args, theme);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderSubagentResult(result, options, theme, context.isError);\n\t\t\t},\n\t\t});\n\t};\n\n\tregisterSubagentTool(discoverAgents(process.cwd(), { projectTrusted: false, agentDir: getAgentDir() }));\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tregisterSubagentTool(\n\t\t\tdiscoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir: getAgentDir() }),\n\t\t);\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
|
|
@@ -2,6 +2,7 @@ import { join } from "node:path";
|
|
|
2
2
|
import { getSupportedThinkingLevels } from "@earendil-works/pi-ai/compat";
|
|
3
3
|
import { getAgentDir } from "../../config.js";
|
|
4
4
|
import { ModelRuntime } from "../../core/model-runtime.js";
|
|
5
|
+
import { emptyUsage } from "./activity.js";
|
|
5
6
|
import { discoverAgents, subagentToolDescription } from "./agents.js";
|
|
6
7
|
import { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from "./constants.js";
|
|
7
8
|
import { SearchPickerComponent } from "./picker.js";
|
|
@@ -191,6 +192,19 @@ export default function subagent(pi) {
|
|
|
191
192
|
],
|
|
192
193
|
parameters: SubagentParamsSchema,
|
|
193
194
|
async execute(_toolCallId, params, signal, onUpdate, ctx) {
|
|
195
|
+
// First paint before any await: model-runtime creation and task
|
|
196
|
+
// resolution can stall on first use (network, filesystem), and
|
|
197
|
+
// the transcript card must not sit empty while they do.
|
|
198
|
+
onUpdate?.({
|
|
199
|
+
content: [{ type: "text", text: "Initializing…" }],
|
|
200
|
+
details: {
|
|
201
|
+
mode: params.tasks ? "parallel" : "single",
|
|
202
|
+
status: "running",
|
|
203
|
+
runs: [],
|
|
204
|
+
startedAt: Date.now(),
|
|
205
|
+
usage: emptyUsage(),
|
|
206
|
+
},
|
|
207
|
+
});
|
|
194
208
|
const discovery = discoverAgents(ctx.cwd, {
|
|
195
209
|
projectTrusted: ctx.isProjectTrusted(),
|
|
196
210
|
agentDir: getAgentDir(),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getSupportedThinkingLevels } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { discoverAgents, subagentToolDescription } from \"./agents.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from \"./constants.ts\";\nimport { type PickerItem, SearchPickerComponent } from \"./picker.ts\";\nimport { renderSubagentCall, renderSubagentResult } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation, statusSummary } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { loadSubagentConfig, resetProfileOverrides, updateProfileOverride } from \"./settings.ts\";\nimport type { AgentDefinition, AgentDiscoveryResult, SubagentDetails, SubagentProfileOverride } from \"./types.ts\";\n\nfunction formatParentModel(model: { provider: string; id: string } | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"none\";\n}\n\n// Settings resolve in two layers: a saved override wins, otherwise the\n// profile inherits the parent session. Inherited rows show the value\n// currently in effect so the menu doubles as a status view.\nfunction effectiveSettings(\n\toverride: SubagentProfileOverride | undefined,\n\tctx: ExtensionCommandContext,\n\tparentThinking: string,\n): { model: string; thinking: string } {\n\treturn {\n\t\tmodel: override?.model ?? `inherit (${formatParentModel(ctx.model)})`,\n\t\tthinking: override?.thinking ?? `inherit (${parentThinking})`,\n\t};\n}\n\n// The thinking picker lists what the effective model actually supports;\n// if the saved override names a model that is unavailable right now,\n// every level shows and the runtime clamp decides.\nfunction effectiveModelFor(\n\tctx: ExtensionCommandContext,\n\toverride: SubagentProfileOverride | undefined,\n): Model<Api> | undefined {\n\tif (override?.model) {\n\t\tconst separator = override.model.indexOf(\"/\");\n\t\tif (separator <= 0) return undefined;\n\t\treturn ctx.modelRegistry.find(override.model.slice(0, separator), override.model.slice(separator + 1));\n\t}\n\treturn ctx.model;\n}\n\nasync function pickModel(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n): Promise<void> {\n\tconst items: PickerItem<string | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${formatParentModel(ctx.model)})`,\n\t\t\tcurrent: !override?.model,\n\t\t},\n\t\t...ctx.modelRegistry.getAvailable().map((model) => ({\n\t\t\tvalue: `${model.provider}/${model.id}`,\n\t\t\tlabel: `${model.provider}/${model.id}`,\n\t\t\tdetail: model.name && model.name !== model.id ? model.name : undefined,\n\t\t\tcurrent: override?.model === `${model.provider}/${model.id}`,\n\t\t})),\n\t];\n\tconst selected = await ctx.ui.custom<string | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Model for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { model: selected ?? undefined }, getAgentDir());\n}\n\nasync function pickThinking(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n\tparentThinking: string,\n): Promise<void> {\n\tconst model = effectiveModelFor(ctx, override);\n\tconst levels = model ? (getSupportedThinkingLevels(model) as ThinkingLevel[]) : [...THINKING_LEVELS];\n\tconst items: PickerItem<ThinkingLevel | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${parentThinking})`,\n\t\t\tcurrent: !override?.thinking,\n\t\t},\n\t\t...levels.map((level) => ({ value: level, label: level, current: override?.thinking === level })),\n\t];\n\tconst selected = await ctx.ui.custom<ThinkingLevel | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Thinking for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { thinking: selected ?? undefined }, getAgentDir());\n}\n\nasync function configureProfile(\n\tctx: ExtensionCommandContext,\n\tagent: AgentDefinition,\n\tparentThinking: string,\n): Promise<void> {\n\tconst agentDir = getAgentDir();\n\twhile (true) {\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst override = config.profiles[agent.name];\n\t\tconst effective = effectiveSettings(override, ctx, parentThinking);\n\t\tconst action = await ctx.ui.select(`${agent.name} — ${agent.description}`, [\n\t\t\t`Model: ${effective.model}`,\n\t\t\t`Thinking: ${effective.thinking}`,\n\t\t\t\"Back\",\n\t\t]);\n\t\tif (!action || action === \"Back\") return;\n\t\tif (action.startsWith(\"Model:\")) {\n\t\t\tawait pickModel(ctx, agent.name, override);\n\t\t\tcontinue;\n\t\t}\n\t\tawait pickThinking(ctx, agent.name, override, parentThinking);\n\t}\n}\n\nasync function syncParentProviders(\n\truntime: ModelRuntime,\n\tregistry: ModelRegistry,\n\tsyncedIds: Set<string>,\n): Promise<void> {\n\tconst nextIds = new Set(registry.getRegisteredProviderIds());\n\tfor (const id of syncedIds) {\n\t\tif (!nextIds.has(id)) runtime.unregisterProvider(id);\n\t}\n\tfor (const id of nextIds) {\n\t\tconst native = registry.getRegisteredNativeProvider(id);\n\t\tif (native) runtime.registerNativeProvider(native);\n\t\telse {\n\t\t\tconst config = registry.getRegisteredProviderConfig(id);\n\t\t\tif (config) runtime.registerProvider(id, config);\n\t\t}\n\t\tconst auth = await registry.getProviderAuth(id);\n\t\tif (auth?.auth.apiKey) await runtime.setRuntimeApiKey(id, auth.auth.apiKey, { allowNetwork: false });\n\t}\n\tsyncedIds.clear();\n\tfor (const id of nextIds) syncedIds.add(id);\n}\n\nasync function showAgentsCommand(ctx: ExtensionCommandContext, parentThinking: string): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/agents requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst agentDir = getAgentDir();\n\tconst reset = \"Reset all overrides\";\n\tconst done = \"Done\";\n\twhile (true) {\n\t\tconst discovery = discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir });\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst rows = discovery.agents.map((agent) => {\n\t\t\tconst effective = effectiveSettings(config.profiles[agent.name], ctx, parentThinking);\n\t\t\treturn `${agent.name} — model: ${effective.model} · thinking: ${effective.thinking}`;\n\t\t});\n\t\tconst issueCount = discovery.diagnostics.length;\n\t\tconst issues = issueCount ? [`Show ${issueCount} agent file issue${issueCount === 1 ? \"\" : \"s\"}`] : [];\n\t\tconst action = await ctx.ui.select(\"Subagent profiles — select one to configure\", [\n\t\t\t...rows,\n\t\t\t...issues,\n\t\t\treset,\n\t\t\tdone,\n\t\t]);\n\t\tif (!action || action === done) return;\n\t\tif (action === reset) {\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t\"Reset profile overrides?\",\n\t\t\t\t\"Clear every saved Subagent model and thinking override?\",\n\t\t\t);\n\t\t\tif (confirmed) {\n\t\t\t\tawait resetProfileOverrides(agentDir);\n\t\t\t\tctx.ui.notify(\"All profiles now inherit the parent session.\", \"info\");\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (issues.length > 0 && action === issues[0]) {\n\t\t\tctx.ui.notify(\n\t\t\t\tdiscovery.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`).join(\"\\n\"),\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst agent = discovery.agents.find((candidate) => action.startsWith(`${candidate.name} — `));\n\t\tif (agent) await configureProfile(ctx, agent, parentThinking);\n\t}\n}\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\tconst syncedProviderIds = new Set<string>();\n\tlet modelRuntimePromise: Promise<ModelRuntime> | undefined;\n\n\tconst getModelRuntime = async (ctx: ExtensionContext): Promise<ModelRuntime> => {\n\t\tif (!modelRuntimePromise) {\n\t\t\tconst agentDir = getAgentDir();\n\t\t\tconst created = ModelRuntime.create({\n\t\t\t\tauthPath: join(agentDir, \"auth.json\"),\n\t\t\t\tmodelsPath: join(agentDir, \"models.json\"),\n\t\t\t});\n\t\t\t// A transient failure (network, filesystem) must not brick the tool\n\t\t\t// for the rest of the session: drop the rejected promise so the\n\t\t\t// next invocation retries creation.\n\t\t\tcreated.catch(() => {\n\t\t\t\tif (modelRuntimePromise === created) modelRuntimePromise = undefined;\n\t\t\t});\n\t\t\tmodelRuntimePromise = created;\n\t\t}\n\t\tconst runtime = await modelRuntimePromise;\n\t\tawait syncParentProviders(runtime, ctx.modelRegistry, syncedProviderIds);\n\t\treturn runtime;\n\t};\n\n\tconst registerSubagentTool = (discovery: AgentDiscoveryResult): void => {\n\t\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails>({\n\t\t\tname: SUBAGENT_TOOL_NAME,\n\t\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\t\tdescription: subagentToolDescription(discovery),\n\t\t\tpromptSnippet: \"Delegate focused research, review, or implementation tasks to isolated subagents\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use subagent when a bounded task benefits from isolated context or parallel investigation; choose a profile and write its briefing using the tool description.\",\n\t\t\t],\n\t\t\tparameters: SubagentParamsSchema,\n\t\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t\tconst discovery = discoverAgents(ctx.cwd, {\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t});\n\t\t\t\tconst runtime = await getModelRuntime(ctx);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tagents: discovery.agents,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: runtime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal,\n\t\t\t\t\tgate,\n\t\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t\t},\n\t\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t},\n\t\t\trenderCall(args, theme) {\n\t\t\t\treturn renderSubagentCall(args, theme);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderSubagentResult(result, options, theme, context.isError);\n\t\t\t},\n\t\t});\n\t};\n\n\tregisterSubagentTool(discoverAgents(process.cwd(), { projectTrusted: false, agentDir: getAgentDir() }));\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tregisterSubagentTool(\n\t\t\tdiscoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir: getAgentDir() }),\n\t\t);\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/extensions/subagent/index.ts"],"names":[],"mappings":"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","sourcesContent":["import { join } from \"node:path\";\nimport type { ThinkingLevel } from \"@earendil-works/pi-agent-core\";\nimport type { Api, Model } from \"@earendil-works/pi-ai\";\nimport { getSupportedThinkingLevels } from \"@earendil-works/pi-ai/compat\";\nimport { getAgentDir } from \"../../config.ts\";\nimport type {\n\tAgentToolResult,\n\tExtensionAPI,\n\tExtensionCommandContext,\n\tExtensionContext,\n} from \"../../core/extensions/types.ts\";\nimport type { ModelRegistry } from \"../../core/model-registry.ts\";\nimport { ModelRuntime } from \"../../core/model-runtime.ts\";\nimport { emptyUsage } from \"./activity.ts\";\nimport { discoverAgents, subagentToolDescription } from \"./agents.ts\";\nimport { SUBAGENT_COMMAND_NAME, SUBAGENT_TOOL_LABEL, SUBAGENT_TOOL_NAME, THINKING_LEVELS } from \"./constants.ts\";\nimport { type PickerItem, SearchPickerComponent } from \"./picker.ts\";\nimport { renderSubagentCall, renderSubagentResult } from \"./render.ts\";\nimport type { ParentModelContext } from \"./resolve.ts\";\nimport { ConcurrencyGate, isSubagentError, runSubagentInvocation, statusSummary } from \"./runner.ts\";\nimport { SubagentParamsSchema } from \"./schema.ts\";\nimport { loadSubagentConfig, resetProfileOverrides, updateProfileOverride } from \"./settings.ts\";\nimport type { AgentDefinition, AgentDiscoveryResult, SubagentDetails, SubagentProfileOverride } from \"./types.ts\";\n\nfunction formatParentModel(model: { provider: string; id: string } | undefined): string {\n\treturn model ? `${model.provider}/${model.id}` : \"none\";\n}\n\n// Settings resolve in two layers: a saved override wins, otherwise the\n// profile inherits the parent session. Inherited rows show the value\n// currently in effect so the menu doubles as a status view.\nfunction effectiveSettings(\n\toverride: SubagentProfileOverride | undefined,\n\tctx: ExtensionCommandContext,\n\tparentThinking: string,\n): { model: string; thinking: string } {\n\treturn {\n\t\tmodel: override?.model ?? `inherit (${formatParentModel(ctx.model)})`,\n\t\tthinking: override?.thinking ?? `inherit (${parentThinking})`,\n\t};\n}\n\n// The thinking picker lists what the effective model actually supports;\n// if the saved override names a model that is unavailable right now,\n// every level shows and the runtime clamp decides.\nfunction effectiveModelFor(\n\tctx: ExtensionCommandContext,\n\toverride: SubagentProfileOverride | undefined,\n): Model<Api> | undefined {\n\tif (override?.model) {\n\t\tconst separator = override.model.indexOf(\"/\");\n\t\tif (separator <= 0) return undefined;\n\t\treturn ctx.modelRegistry.find(override.model.slice(0, separator), override.model.slice(separator + 1));\n\t}\n\treturn ctx.model;\n}\n\nasync function pickModel(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n): Promise<void> {\n\tconst items: PickerItem<string | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${formatParentModel(ctx.model)})`,\n\t\t\tcurrent: !override?.model,\n\t\t},\n\t\t...ctx.modelRegistry.getAvailable().map((model) => ({\n\t\t\tvalue: `${model.provider}/${model.id}`,\n\t\t\tlabel: `${model.provider}/${model.id}`,\n\t\t\tdetail: model.name && model.name !== model.id ? model.name : undefined,\n\t\t\tcurrent: override?.model === `${model.provider}/${model.id}`,\n\t\t})),\n\t];\n\tconst selected = await ctx.ui.custom<string | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Model for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { model: selected ?? undefined }, getAgentDir());\n}\n\nasync function pickThinking(\n\tctx: ExtensionCommandContext,\n\tprofileName: string,\n\toverride: SubagentProfileOverride | undefined,\n\tparentThinking: string,\n): Promise<void> {\n\tconst model = effectiveModelFor(ctx, override);\n\tconst levels = model ? (getSupportedThinkingLevels(model) as ThinkingLevel[]) : [...THINKING_LEVELS];\n\tconst items: PickerItem<ThinkingLevel | null>[] = [\n\t\t{\n\t\t\tvalue: null,\n\t\t\tlabel: \"inherit\",\n\t\t\tdetail: `Follow the parent session (${parentThinking})`,\n\t\t\tcurrent: !override?.thinking,\n\t\t},\n\t\t...levels.map((level) => ({ value: level, label: level, current: override?.thinking === level })),\n\t];\n\tconst selected = await ctx.ui.custom<ThinkingLevel | null | undefined>(\n\t\t(_tui, theme, _keybindings, done) => new SearchPickerComponent(theme, `Thinking for ${profileName}`, items, done),\n\t);\n\tif (selected === undefined) return;\n\tawait updateProfileOverride(profileName, { thinking: selected ?? undefined }, getAgentDir());\n}\n\nasync function configureProfile(\n\tctx: ExtensionCommandContext,\n\tagent: AgentDefinition,\n\tparentThinking: string,\n): Promise<void> {\n\tconst agentDir = getAgentDir();\n\twhile (true) {\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst override = config.profiles[agent.name];\n\t\tconst effective = effectiveSettings(override, ctx, parentThinking);\n\t\tconst action = await ctx.ui.select(`${agent.name} — ${agent.description}`, [\n\t\t\t`Model: ${effective.model}`,\n\t\t\t`Thinking: ${effective.thinking}`,\n\t\t\t\"Back\",\n\t\t]);\n\t\tif (!action || action === \"Back\") return;\n\t\tif (action.startsWith(\"Model:\")) {\n\t\t\tawait pickModel(ctx, agent.name, override);\n\t\t\tcontinue;\n\t\t}\n\t\tawait pickThinking(ctx, agent.name, override, parentThinking);\n\t}\n}\n\nasync function syncParentProviders(\n\truntime: ModelRuntime,\n\tregistry: ModelRegistry,\n\tsyncedIds: Set<string>,\n): Promise<void> {\n\tconst nextIds = new Set(registry.getRegisteredProviderIds());\n\tfor (const id of syncedIds) {\n\t\tif (!nextIds.has(id)) runtime.unregisterProvider(id);\n\t}\n\tfor (const id of nextIds) {\n\t\tconst native = registry.getRegisteredNativeProvider(id);\n\t\tif (native) runtime.registerNativeProvider(native);\n\t\telse {\n\t\t\tconst config = registry.getRegisteredProviderConfig(id);\n\t\t\tif (config) runtime.registerProvider(id, config);\n\t\t}\n\t\tconst auth = await registry.getProviderAuth(id);\n\t\tif (auth?.auth.apiKey) await runtime.setRuntimeApiKey(id, auth.auth.apiKey, { allowNetwork: false });\n\t}\n\tsyncedIds.clear();\n\tfor (const id of nextIds) syncedIds.add(id);\n}\n\nasync function showAgentsCommand(ctx: ExtensionCommandContext, parentThinking: string): Promise<void> {\n\tif (!ctx.hasUI) {\n\t\tctx.ui.notify(\"/agents requires an interactive UI.\", \"warning\");\n\t\treturn;\n\t}\n\tconst agentDir = getAgentDir();\n\tconst reset = \"Reset all overrides\";\n\tconst done = \"Done\";\n\twhile (true) {\n\t\tconst discovery = discoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir });\n\t\tconst config = await loadSubagentConfig(agentDir);\n\t\tconst rows = discovery.agents.map((agent) => {\n\t\t\tconst effective = effectiveSettings(config.profiles[agent.name], ctx, parentThinking);\n\t\t\treturn `${agent.name} — model: ${effective.model} · thinking: ${effective.thinking}`;\n\t\t});\n\t\tconst issueCount = discovery.diagnostics.length;\n\t\tconst issues = issueCount ? [`Show ${issueCount} agent file issue${issueCount === 1 ? \"\" : \"s\"}`] : [];\n\t\tconst action = await ctx.ui.select(\"Subagent profiles — select one to configure\", [\n\t\t\t...rows,\n\t\t\t...issues,\n\t\t\treset,\n\t\t\tdone,\n\t\t]);\n\t\tif (!action || action === done) return;\n\t\tif (action === reset) {\n\t\t\tconst confirmed = await ctx.ui.confirm(\n\t\t\t\t\"Reset profile overrides?\",\n\t\t\t\t\"Clear every saved Subagent model and thinking override?\",\n\t\t\t);\n\t\t\tif (confirmed) {\n\t\t\t\tawait resetProfileOverrides(agentDir);\n\t\t\t\tctx.ui.notify(\"All profiles now inherit the parent session.\", \"info\");\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (issues.length > 0 && action === issues[0]) {\n\t\t\tctx.ui.notify(\n\t\t\t\tdiscovery.diagnostics.map((diagnostic) => `${diagnostic.path}: ${diagnostic.message}`).join(\"\\n\"),\n\t\t\t\t\"warning\",\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\t\tconst agent = discovery.agents.find((candidate) => action.startsWith(`${candidate.name} — `));\n\t\tif (agent) await configureProfile(ctx, agent, parentThinking);\n\t}\n}\n\nexport default function subagent(pi: ExtensionAPI): void {\n\tconst gate = new ConcurrencyGate();\n\tconst activeAborters = new Set<() => Promise<void>>();\n\tconst syncedProviderIds = new Set<string>();\n\tlet modelRuntimePromise: Promise<ModelRuntime> | undefined;\n\n\tconst getModelRuntime = async (ctx: ExtensionContext): Promise<ModelRuntime> => {\n\t\tif (!modelRuntimePromise) {\n\t\t\tconst agentDir = getAgentDir();\n\t\t\tconst created = ModelRuntime.create({\n\t\t\t\tauthPath: join(agentDir, \"auth.json\"),\n\t\t\t\tmodelsPath: join(agentDir, \"models.json\"),\n\t\t\t});\n\t\t\t// A transient failure (network, filesystem) must not brick the tool\n\t\t\t// for the rest of the session: drop the rejected promise so the\n\t\t\t// next invocation retries creation.\n\t\t\tcreated.catch(() => {\n\t\t\t\tif (modelRuntimePromise === created) modelRuntimePromise = undefined;\n\t\t\t});\n\t\t\tmodelRuntimePromise = created;\n\t\t}\n\t\tconst runtime = await modelRuntimePromise;\n\t\tawait syncParentProviders(runtime, ctx.modelRegistry, syncedProviderIds);\n\t\treturn runtime;\n\t};\n\n\tconst registerSubagentTool = (discovery: AgentDiscoveryResult): void => {\n\t\tpi.registerTool<typeof SubagentParamsSchema, SubagentDetails>({\n\t\t\tname: SUBAGENT_TOOL_NAME,\n\t\t\tlabel: SUBAGENT_TOOL_LABEL,\n\t\t\tdescription: subagentToolDescription(discovery),\n\t\t\tpromptSnippet: \"Delegate focused research, review, or implementation tasks to isolated subagents\",\n\t\t\tpromptGuidelines: [\n\t\t\t\t\"Use subagent when a bounded task benefits from isolated context or parallel investigation; choose a profile and write its briefing using the tool description.\",\n\t\t\t],\n\t\t\tparameters: SubagentParamsSchema,\n\t\t\tasync execute(_toolCallId, params, signal, onUpdate, ctx): Promise<AgentToolResult<SubagentDetails>> {\n\t\t\t\t// First paint before any await: model-runtime creation and task\n\t\t\t\t// resolution can stall on first use (network, filesystem), and\n\t\t\t\t// the transcript card must not sit empty while they do.\n\t\t\t\tonUpdate?.({\n\t\t\t\t\tcontent: [{ type: \"text\", text: \"Initializing…\" }],\n\t\t\t\t\tdetails: {\n\t\t\t\t\t\tmode: params.tasks ? \"parallel\" : \"single\",\n\t\t\t\t\t\tstatus: \"running\",\n\t\t\t\t\t\truns: [],\n\t\t\t\t\t\tstartedAt: Date.now(),\n\t\t\t\t\t\tusage: emptyUsage(),\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tconst discovery = discoverAgents(ctx.cwd, {\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t});\n\t\t\t\tconst runtime = await getModelRuntime(ctx);\n\t\t\t\tconst parent: ParentModelContext = {\n\t\t\t\t\tmodel: ctx.model,\n\t\t\t\t\tthinking: pi.getThinkingLevel(),\n\t\t\t\t\tmodelRegistry: ctx.modelRegistry,\n\t\t\t\t};\n\t\t\t\tconst execution = await runSubagentInvocation({\n\t\t\t\t\tparams,\n\t\t\t\t\tparentCwd: ctx.cwd,\n\t\t\t\t\tagents: discovery.agents,\n\t\t\t\t\tparent,\n\t\t\t\t\tmodelRuntime: runtime,\n\t\t\t\t\tagentDir: getAgentDir(),\n\t\t\t\t\tprojectTrusted: ctx.isProjectTrusted(),\n\t\t\t\t\tsignal,\n\t\t\t\t\tgate,\n\t\t\t\t\tonUpdate: (details) => {\n\t\t\t\t\t\tonUpdate?.({ content: [{ type: \"text\", text: statusSummary(details) }], details });\n\t\t\t\t\t},\n\t\t\t\t\tregisterAbort: (abort) => {\n\t\t\t\t\t\tactiveAborters.add(abort);\n\t\t\t\t\t\treturn () => activeAborters.delete(abort);\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: execution.content }],\n\t\t\t\t\tdetails: execution.details,\n\t\t\t\t\tusage: execution.usage,\n\t\t\t\t};\n\t\t\t},\n\t\t\trenderCall(args, theme) {\n\t\t\t\treturn renderSubagentCall(args, theme);\n\t\t\t},\n\t\t\trenderResult(result, options, theme, context) {\n\t\t\t\treturn renderSubagentResult(result, options, theme, context.isError);\n\t\t\t},\n\t\t});\n\t};\n\n\tregisterSubagentTool(discoverAgents(process.cwd(), { projectTrusted: false, agentDir: getAgentDir() }));\n\tpi.on(\"session_start\", (_event, ctx) => {\n\t\tregisterSubagentTool(\n\t\t\tdiscoverAgents(ctx.cwd, { projectTrusted: ctx.isProjectTrusted(), agentDir: getAgentDir() }),\n\t\t);\n\t});\n\n\tpi.registerCommand(SUBAGENT_COMMAND_NAME, {\n\t\tdescription: \"Configure Subagent profiles, models, and thinking levels\",\n\t\thandler: async (_args, ctx) => showAgentsCommand(ctx, pi.getThinkingLevel()),\n\t});\n\n\tpi.on(\"tool_result\", async (event) => {\n\t\tif (event.toolName !== SUBAGENT_TOOL_NAME) return;\n\t\tconst details = event.details as SubagentDetails | undefined;\n\t\tif (details && isSubagentError(details)) return { isError: true };\n\t});\n\n\tpi.on(\"session_shutdown\", async () => {\n\t\tawait Promise.allSettled([...activeAborters].map((abort) => abort()));\n\t\tactiveAborters.clear();\n\t});\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAqC,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE/F,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAEtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAyC,MAAM,YAAY,CAAC;AAsXzF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAqBhF;AAED,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EACxC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,GACd,SAAS,CAmDX","sourcesContent":["import { type Component, Container, Markdown, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport type { AgentToolResult, ToolRenderResultOptions } from \"../../core/extensions/types.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getMarkdownTheme, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { statusSummary } from \"./runner.ts\";\nimport type { SubagentParams } from \"./schema.ts\";\nimport type { SubagentDetails, SubagentRunDetails, SubagentRunStatus } from \"./types.ts\";\n\nconst SINGLE_EXCERPT_LIMIT = 200;\nconst RUN_LINE_EXCERPT_LIMIT = 64;\nconst LIVE_TAIL_LIMIT = 100;\nconst COLLAPSED_RUN_LIMIT = 4;\n\nconst ACTIVITY_GROUPS = [\n\t{ toolName: \"read\", verb: \"read\", singular: \"file\", plural: \"files\" },\n\t{ toolName: \"grep\", verb: \"searched\", singular: \"pattern\", plural: \"patterns\" },\n\t{ toolName: \"find\", verb: \"searched\", singular: \"path pattern\", plural: \"path patterns\" },\n\t{ toolName: \"ls\", verb: \"listed\", singular: \"directory\", plural: \"directories\" },\n\t{ toolName: \"bash\", verb: \"ran\", singular: \"command\", plural: \"commands\" },\n\t{ toolName: \"edit\", verb: \"edited\", singular: \"file\", plural: \"files\" },\n\t{ toolName: \"write\", verb: \"wrote\", singular: \"file\", plural: \"files\" },\n] as const;\n\n// Display-only: profile names stay lowercase everywhere the model sees\n// them; the transcript shows \"general\" as \"General\", \"code-reviewer\" as\n// \"Code Reviewer\".\nfunction displayAgentName(name: string): string {\n\treturn name\n\t\t.split(/[-_]/u)\n\t\t.filter(Boolean)\n\t\t.map((word) => `${word[0]?.toUpperCase() ?? \"\"}${word.slice(1)}`)\n\t\t.join(\" \");\n}\n\nfunction singleAgentName(args: { agent?: string | null }): string {\n\treturn displayAgentName(args.agent ?? \"general\");\n}\n\nfunction truncate(text: string, limit: number): string {\n\treturn text.length <= limit ? text : `${text.slice(0, Math.max(0, limit - 1))}…`;\n}\n\n// Transcript excerpts render inside plain Text components, so markdown\n// punctuation would show up literally; strip the common inline syntax.\nfunction stripInlineMarkdown(text: string): string {\n\treturn text\n\t\t.replace(/```[a-zA-Z0-9-]*/gu, \"\")\n\t\t.replace(/`([^`]*)`/gu, \"$1\")\n\t\t.replace(/\\*\\*([^*]+)\\*\\*/gu, \"$1\")\n\t\t.replace(/__([^_]+)__/gu, \"$1\")\n\t\t.replace(/\\[([^\\]]+)\\]\\([^)]*\\)/gu, \"$1\")\n\t\t.replace(/^#{1,6}\\s+/gmu, \"\");\n}\n\n// Markdown tables and horizontal rules turn into symbol soup when\n// flattened onto one line; drop those lines before inline stripping.\nfunction stripBlockMarkdown(text: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.filter((line) => {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (trimmed.startsWith(\"|\")) return false;\n\t\t\treturn !(trimmed.length >= 3 && /^[-—─═=*_|:\\s]+$/u.test(trimmed));\n\t\t})\n\t\t.join(\"\\n\");\n}\n\nfunction excerpt(text: string, limit: number): string {\n\t// finalOutput/liveText carry model-facing truncation notices from\n\t// boundText/tailText; in the transcript an ellipsis is enough.\n\tconst cleaned = text.replace(/\\[(?:Output truncated(?:: \\d+ bytes omitted\\.)?|Earlier output omitted\\.)\\]/gu, \"…\");\n\treturn truncate(stripInlineMarkdown(stripBlockMarkdown(cleaned)).replace(/\\s+/gu, \" \").trim(), limit);\n}\n\nfunction liveTail(run: SubagentRunDetails): string | undefined {\n\tconst lines = run.liveText\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n\tconst last = lines.at(-1);\n\treturn last ? excerpt(last, LIVE_TAIL_LIMIT) : undefined;\n}\n\n// Completed-run excerpts read better cut at a sentence boundary than\n// mid-word; fall back to a hard truncate when no boundary lands in the\n// second half of the window.\nfunction leadingSentences(text: string, limit: number): string {\n\tconst cleaned = excerpt(text, Number.MAX_SAFE_INTEGER);\n\tif (cleaned.length <= limit) return cleaned;\n\tconst window = cleaned.slice(0, limit);\n\tlet boundary = -1;\n\tfor (const match of window.matchAll(/[.!?。!?](?=\\s|$)/gu)) {\n\t\tboundary = (match.index ?? 0) + match[0].length;\n\t}\n\tif (boundary >= limit / 2) return window.slice(0, boundary).trimEnd();\n\treturn truncate(window, limit);\n}\n\nconst VERIFY_COMMAND_PATTERN = /\\b(test|lint|typecheck|tsc|vitest|jest|pytest|build|check|fmt|format|compile)\\b/iu;\nconst INSPECT_COMMAND_PATTERN = /\\b(rg|grep|find|git (?:diff|status|log|show)|ls|tree|cat|head|tail)\\b/iu;\nconst RETRY_MARKER_PATTERN = /^Retrying\\b/u;\nconst STREAM_MARKERS = new Set([\"Thinking…\", \"Writing response…\"]);\n\n// Translates raw tool traffic into what the worker is doing right now, so\n// the collapsed card reads as a phase instead of a command line. Modelled\n// on tool-type classification; the raw tool summary stays as evidence on\n// the next line.\nfunction runIntent(run: SubagentRunDetails): string {\n\tconst current = run.currentActivity;\n\tif (current && RETRY_MARKER_PATTERN.test(current)) return current;\n\tconst running = [...run.activities].reverse().find((activity) => activity.status === \"running\");\n\tif (running) {\n\t\tif (running.toolName === \"edit\" || running.toolName === \"write\") return \"Applying changes\";\n\t\tif (running.toolName === \"bash\") {\n\t\t\tif (VERIFY_COMMAND_PATTERN.test(running.summary)) return \"Verifying changes\";\n\t\t\treturn INSPECT_COMMAND_PATTERN.test(running.summary) ? \"Exploring code\" : \"Running commands\";\n\t\t}\n\t\treturn \"Exploring code\";\n\t}\n\tconst recent = run.activities.slice(-5);\n\tif (current && STREAM_MARKERS.has(current)) {\n\t\tif (recent.some((activity) => activity.status === \"failed\")) return \"Investigating a failure\";\n\t\treturn current;\n\t}\n\tif (recent.some((activity) => activity.status === \"failed\")) return \"Investigating a failure\";\n\tif (recent.some((activity) => activity.toolName === \"edit\" || activity.toolName === \"write\"))\n\t\treturn \"Reviewing changes\";\n\tif (recent.length > 0) return \"Exploring code\";\n\treturn current ?? \"Thinking…\";\n}\n\n// The raw tool summary backs up the intent line; suppress it when it would\n// just repeat a stream/retry marker.\nfunction runEvidence(run: SubagentRunDetails): string | undefined {\n\tconst current = run.currentActivity;\n\tif (!current || STREAM_MARKERS.has(current) || RETRY_MARKER_PATTERN.test(current)) return undefined;\n\treturn current;\n}\n\nfunction liveElapsed(run: SubagentRunDetails): string | undefined {\n\tif (run.startedAt === undefined) return undefined;\n\tconst end = run.endedAt ?? Date.now();\n\treturn formatDuration(Math.max(0, (end - run.startedAt) / 1000));\n}\n\n// The tool shell already paints a call-level status dot (● in\n// warning/success/error), so `●` is off-limits here: `›` marks where the\n// work currently is, and the remaining glyphs are terminal states.\nfunction statusMarker(status: SubagentRunStatus, theme: Theme): string {\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn theme.fg(\"success\", \"✓\");\n\t\tcase \"failed\":\n\t\t\treturn theme.fg(\"error\", \"×\");\n\t\tcase \"aborted\":\n\t\t\treturn theme.fg(\"warning\", \"■\");\n\t\tcase \"running\":\n\t\t\treturn theme.fg(\"accent\", \"›\");\n\t\tdefault:\n\t\t\treturn theme.fg(\"muted\", \"○\");\n\t}\n}\n\nfunction runTitle(run: SubagentRunDetails, theme: Theme, index?: number): string {\n\tconst number = index === undefined ? \"\" : theme.fg(\"dim\", `${index} · `);\n\treturn `${statusMarker(run.status, theme)} ${number}${theme.fg(\"accent\", displayAgentName(run.agent))}${theme.fg(\"dim\", ` · ${truncate(run.description, 48)}`)}`;\n}\n\nfunction runProgressText(run: SubagentRunDetails): string {\n\tconst items = [`${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? \"\" : \"s\"}`];\n\tif (run.usage.totalTokens) items.push(`${formatTokens(run.usage.totalTokens)} tokens`);\n\treturn items.join(\" · \");\n}\n\nfunction runLine(run: SubagentRunDetails, theme: Theme, index?: number): string {\n\tlet line = runTitle(run, theme, index);\n\tconst detail =\n\t\trun.status === \"running\"\n\t\t\t? runIntent(run)\n\t\t\t: run.status === \"failed\" || run.status === \"aborted\"\n\t\t\t\t? run.error && excerpt(run.error, RUN_LINE_EXCERPT_LIMIT)\n\t\t\t\t: run.finalOutput && excerpt(run.finalOutput, RUN_LINE_EXCERPT_LIMIT);\n\tif (detail) line += theme.fg(run.status === \"failed\" ? \"error\" : \"dim\", ` — ${detail}`);\n\tif (run.status === \"running\") {\n\t\tline += theme.fg(\"dim\", ` · ${runProgressText(run)}`);\n\t\tconst elapsed = liveElapsed(run);\n\t\tif (elapsed) line += theme.fg(\"dim\", ` · ${elapsed}`);\n\t}\n\tconst settled = run.status !== \"running\" && run.status !== \"queued\";\n\tif (settled && run.startedAt !== undefined && run.endedAt !== undefined) {\n\t\tline += theme.fg(\"dim\", ` · ${formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))}`);\n\t}\n\treturn line;\n}\n\n// Keep active work visible while a batch is in flight: completed runs\n// may otherwise crowd a still-running task out of the collapsed window.\nfunction selectCollapsedRuns(runs: SubagentRunDetails[], isPartial: boolean): SubagentRunDetails[] {\n\tif (runs.length <= COLLAPSED_RUN_LIMIT) return runs;\n\tif (!isPartial) return runs.slice(0, COLLAPSED_RUN_LIMIT);\n\tconst active = runs.filter((run) => run.status === \"running\" || run.status === \"queued\");\n\tconst settled = runs.filter((run) => run.status !== \"running\" && run.status !== \"queued\");\n\treturn [...active, ...settled].slice(0, COLLAPSED_RUN_LIMIT);\n}\n\nfunction singleCollapsedLines(details: SubagentDetails, theme: Theme): string[] {\n\tconst run = details.runs[0];\n\tif (!run) return [theme.fg(\"muted\", \"Initializing…\")];\n\tif (run.status === \"running\" || run.status === \"queued\") {\n\t\tconst isInitializing =\n\t\t\trun.status === \"running\" && run.usage.turns === 0 && run.usage.toolUses === 0 && run.liveText.length === 0;\n\t\tconst intent =\n\t\t\trun.status === \"queued\"\n\t\t\t\t? (run.currentActivity ?? \"queued\")\n\t\t\t\t: isInitializing\n\t\t\t\t\t? \"Initializing…\"\n\t\t\t\t\t: runIntent(run);\n\t\t// No status marker here: the shell's call-level dot already covers\n\t\t// the whole card, and a single run has nothing to disambiguate.\n\t\tlet status = theme.fg(\"dim\", intent);\n\t\tif (run.status === \"running\" && !isInitializing) {\n\t\t\tstatus += theme.fg(\"dim\", ` · ${runProgressText(run)}`);\n\t\t\tconst elapsed = liveElapsed(run);\n\t\t\tif (elapsed) status += theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t}\n\t\tconst lines = [status];\n\t\tconst evidence = runEvidence(run);\n\t\tif (evidence && evidence !== intent)\n\t\t\tlines.push(`${theme.fg(\"accent\", \"›\")} ${theme.fg(\"dim\", excerpt(evidence, LIVE_TAIL_LIMIT))}`);\n\t\tconst tail = liveTail(run);\n\t\tif (tail) lines.push(theme.fg(\"dim\", tail));\n\t\treturn lines;\n\t}\n\tif (run.error) return [theme.fg(\"error\", excerpt(run.error, SINGLE_EXCERPT_LIMIT))];\n\tif (run.finalOutput) return [theme.fg(\"toolOutput\", leadingSentences(run.finalOutput, SINGLE_EXCERPT_LIMIT))];\n\treturn [theme.fg(\"muted\", run.status)];\n}\n\nfunction activitySummaryText(details: SubagentDetails): string {\n\tconst toolCounts = new Map<string, number>();\n\tlet activityCount = 0;\n\tfor (const run of details.runs) {\n\t\tfor (const activity of run.activities) {\n\t\t\tactivityCount++;\n\t\t\ttoolCounts.set(activity.toolName, (toolCounts.get(activity.toolName) ?? 0) + 1);\n\t\t}\n\t}\n\tconst items: string[] = [];\n\tlet groupedCount = 0;\n\tfor (const group of ACTIVITY_GROUPS) {\n\t\tconst count = toolCounts.get(group.toolName) ?? 0;\n\t\tif (count === 0) continue;\n\t\tgroupedCount += count;\n\t\titems.push(`${group.verb} ${count} ${count === 1 ? group.singular : group.plural}`);\n\t}\n\tconst otherCount = activityCount - groupedCount;\n\tif (otherCount > 0) items.push(`used ${otherCount} other tool${otherCount === 1 ? \"\" : \"s\"}`);\n\tif (items.length === 0) return \"\";\n\tconst earlierCount = Math.max(0, details.usage.toolUses - activityCount);\n\tif (earlierCount > 0) items.push(`${earlierCount} earlier tool use${earlierCount === 1 ? \"\" : \"s\"}`);\n\treturn `${items[0]![0]!.toUpperCase()}${items[0]!.slice(1)}${items\n\t\t.slice(1)\n\t\t.map((item) => ` · ${item}`)\n\t\t.join(\"\")}`;\n}\n\nfunction usageText(details: SubagentDetails, includeToolUses = true): string {\n\tconst usage = details.usage;\n\tconst items: string[] = [];\n\tif (includeToolUses && usage.toolUses) items.push(`${usage.toolUses} tool use${usage.toolUses === 1 ? \"\" : \"s\"}`);\n\tif (usage.turns) items.push(`${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}`);\n\tif (usage.output) items.push(`↓${formatTokens(usage.output)}`);\n\tif (usage.contextTokens) items.push(`ctx:${formatTokens(usage.contextTokens)}`);\n\tif (usage.cost) items.push(`$${usage.cost.toFixed(3)}`);\n\tconst duration = details.endedAt ? Math.max(0, (details.endedAt - details.startedAt) / 1000) : undefined;\n\tif (duration !== undefined) items.push(formatDuration(duration));\n\treturn items.join(\" · \");\n}\n\nfunction formatTokens(value: number): string {\n\tif (value < 1_000) return String(value);\n\tif (value < 1_000_000) return `${(value / 1_000).toFixed(value < 10_000 ? 1 : 0)}k`;\n\treturn `${(value / 1_000_000).toFixed(1)}M`;\n}\n\nfunction formatDuration(seconds: number): string {\n\tif (seconds >= 90) return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;\n\treturn `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;\n}\n\n// A single run's title and usage would duplicate the call header and\n// the call-level usage line, so `single` drops both and folds the\n// duration into the metadata line instead. Multi-run sections carry a\n// numbered `──` header that matches the table of contents above them.\nfunction renderRunDetails(run: SubagentRunDetails, theme: Theme, single: boolean, index?: number): Component {\n\tconst container = new Container();\n\tconst duration =\n\t\trun.startedAt !== undefined && run.endedAt !== undefined\n\t\t\t? formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))\n\t\t\t: undefined;\n\tif (!single) {\n\t\tconst label = index === undefined ? \"\" : `${index} · `;\n\t\tcontainer.addChild(\n\t\t\tnew Text(\n\t\t\t\t`${theme.fg(\"dim\", `── ${label}`)}${theme.fg(\"accent\", displayAgentName(run.agent))}${theme.fg(\"dim\", ` · ${truncate(run.description, 48)}`)}`,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t}\n\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Task\"), 0, 0));\n\tcontainer.addChild(new Text(theme.fg(\"dim\", run.prompt), 0, 0));\n\tif (run.activities.length > 0) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Activity\"), 0, 0));\n\t\tfor (const activity of run.activities) {\n\t\t\tconst marker =\n\t\t\t\tactivity.status === \"succeeded\"\n\t\t\t\t\t? theme.fg(\"success\", \"✓\")\n\t\t\t\t\t: activity.status === \"failed\"\n\t\t\t\t\t\t? theme.fg(\"error\", \"×\")\n\t\t\t\t\t\t: theme.fg(\"accent\", \"›\");\n\t\t\tlet text = `${marker} ${theme.fg(\"toolOutput\", excerpt(activity.summary, 96))}`;\n\t\t\tif (activity.resultSummary) {\n\t\t\t\tconst color = activity.status === \"failed\" ? \"error\" : \"dim\";\n\t\t\t\ttext += ` ${theme.fg(color, `· ${excerpt(activity.resultSummary, 64)}`)}`;\n\t\t\t}\n\t\t\tif (activity.endedAt !== undefined && activity.endedAt - activity.startedAt >= 1_000) {\n\t\t\t\ttext += ` ${theme.fg(\"dim\", `· ${formatDuration((activity.endedAt - activity.startedAt) / 1000)}`)}`;\n\t\t\t}\n\t\t\tcontainer.addChild(new Text(text, 0, 0));\n\t\t}\n\t}\n\t// The live tail is the \"now\" end of the timeline, so it sits after the\n\t// activity list; once the run settles, Response takes its place.\n\tif (run.status === \"running\" && run.liveText) {\n\t\tconst tail = run.liveText\n\t\t\t.split(\"\\n\")\n\t\t\t.map((line) => line.trim())\n\t\t\t.filter(Boolean)\n\t\t\t.slice(-4);\n\t\tif (tail.length > 0) {\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tfor (const line of tail) {\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"dim\", excerpt(line, LIVE_TAIL_LIMIT)), 0, 0));\n\t\t\t}\n\t\t}\n\t}\n\tif (run.error) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"error\", `Error: ${run.error}`), 0, 0));\n\t}\n\tif (run.finalOutput) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Response\"), 0, 0));\n\t\tcontainer.addChild(new Markdown(run.finalOutput, 0, 0, getMarkdownTheme()));\n\t}\n\tconst metadata = [\n\t\t`${run.model} · ${run.thinking}`,\n\t\trun.cwd ? `cwd: ${run.cwd}` : undefined,\n\t\trun.usage.toolUses ? `${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? \"\" : \"s\"}` : undefined,\n\t\trun.usage.turns ? `${run.usage.turns} turn${run.usage.turns === 1 ? \"\" : \"s\"}` : undefined,\n\t\trun.usage.output ? `↓${formatTokens(run.usage.output)}` : undefined,\n\t\trun.usage.contextTokens ? `ctx:${formatTokens(run.usage.contextTokens)}` : undefined,\n\t\trun.usage.cost ? `$${run.usage.cost.toFixed(3)}` : undefined,\n\t\tsingle ? duration : undefined,\n\t]\n\t\t.filter((item): item is string => Boolean(item))\n\t\t.join(\" · \");\n\tif (metadata) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"dim\", metadata), 0, 0));\n\t}\n\treturn container;\n}\n\n// The call header stays a single line: run rows in the result area take\n// over within the first update and carry richer per-task state, so a\n// task list here would render everything twice. The task count alone\n// distinguishes a batch from a single delegation.\nexport function renderSubagentCall(args: SubagentParams, theme: Theme): Component {\n\tconst modes = [args.prompt != null && \"prompt\", args.tasks != null && \"tasks\"].filter((mode): mode is string =>\n\t\tBoolean(mode),\n\t);\n\tif (modes.length > 1) {\n\t\treturn new Text(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"Subagent \"))}${theme.fg(\"error\", `invalid · ${modes.join(\" + \")}`)}`,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t}\n\tif (args.tasks) {\n\t\treturn new Text(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"Multi-Agent\"))}${theme.fg(\"dim\", ` · ${args.tasks.length} task${args.tasks.length === 1 ? \"\" : \"s\"}`)}`,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t}\n\tlet text = theme.fg(\"toolTitle\", theme.bold(`${singleAgentName(args)} Agent`));\n\tif (args.description) text += theme.fg(\"dim\", ` · ${truncate(args.description, 72)}`);\n\treturn new Text(text, 0, 0);\n}\n\nexport function renderSubagentResult(\n\tresult: AgentToolResult<SubagentDetails>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tisError: boolean,\n): Component {\n\tconst details = result.details;\n\tif (!details) {\n\t\tconst text = result.content.find((part) => part.type === \"text\");\n\t\treturn new Text(text?.type === \"text\" ? text.text : \"(no output)\", 0, 0);\n\t}\n\tif (!options.expanded) {\n\t\tconst lines: string[] = [];\n\t\tif (details.mode === \"single\") {\n\t\t\tlines.push(...singleCollapsedLines(details, theme));\n\t\t} else {\n\t\t\tlet header = statusSummary(details);\n\t\t\tif (options.isPartial) {\n\t\t\t\theader += ` · ${formatDuration(Math.max(0, (Date.now() - details.startedAt) / 1000))}`;\n\t\t\t}\n\t\t\tlines.push(theme.fg(isError ? \"error\" : options.isPartial ? \"accent\" : \"muted\", header));\n\t\t\tconst shown = selectCollapsedRuns(details.runs, Boolean(options.isPartial));\n\t\t\tfor (const run of shown) lines.push(runLine(run, theme));\n\t\t\tconst hidden = details.runs.length - shown.length;\n\t\t\tif (hidden > 0) lines.push(theme.fg(\"muted\", `+${hidden} more`));\n\t\t}\n\t\tconst activitySummary = activitySummaryText(details);\n\t\tif (activitySummary) lines.push(theme.fg(\"dim\", activitySummary));\n\t\tconst usage = usageText(details, !activitySummary);\n\t\tif (usage) lines.push(theme.fg(\"dim\", usage));\n\t\tif (!options.isPartial) lines.push(theme.fg(\"muted\", `(${keyHint(\"app.tools.expand\", \"to expand\")})`));\n\t\treturn new Text(lines.join(\"\\n\"), 0, 0);\n\t}\n\tconst container = new Container();\n\tconst single = details.mode === \"single\" && details.runs.length === 1;\n\tif (!single) {\n\t\tcontainer.addChild(new Text(theme.fg(isError ? \"error\" : \"toolTitle\", statusSummary(details)), 0, 0));\n\t\t// Numbered table of contents: the TUI cannot jump, but matching the\n\t\t// numbers against the `──` section headers below makes an 8-run\n\t\t// batch navigable by eye.\n\t\tdetails.runs.forEach((run, index) => {\n\t\t\tcontainer.addChild(new Text(runLine(run, theme, index + 1), 0, 0));\n\t\t});\n\t}\n\tdetails.runs.forEach((run, index) => {\n\t\tif (!single || index > 0) container.addChild(new Spacer(1));\n\t\tcontainer.addChild(renderRunDetails(run, theme, single, single ? undefined : index + 1));\n\t});\n\tif (!single) {\n\t\tconst usage = usageText(details);\n\t\tif (usage) {\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(theme.fg(\"dim\", usage), 0, 0));\n\t\t}\n\t}\n\treturn container;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/extensions/subagent/render.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAqC,MAAM,wBAAwB,CAAC;AAC3F,OAAO,KAAK,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE/F,OAAO,EAAoB,KAAK,KAAK,EAAE,MAAM,wCAAwC,CAAC;AAEtF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAyC,MAAM,YAAY,CAAC;AAuezF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,KAAK,GAAG,SAAS,CAqBhF;AAED,wBAAgB,oBAAoB,CACnC,MAAM,EAAE,eAAe,CAAC,eAAe,CAAC,EACxC,OAAO,EAAE,uBAAuB,EAChC,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,OAAO,GACd,SAAS,CA0DX","sourcesContent":["import { type Component, Container, Markdown, Spacer, Text } from \"@earendil-works/pi-tui\";\nimport type { AgentToolResult, ToolRenderResultOptions } from \"../../core/extensions/types.ts\";\nimport { keyHint } from \"../../modes/interactive/components/keybinding-hints.ts\";\nimport { getMarkdownTheme, type Theme } from \"../../modes/interactive/theme/theme.ts\";\nimport { statusSummary } from \"./runner.ts\";\nimport type { SubagentParams } from \"./schema.ts\";\nimport type { SubagentDetails, SubagentRunDetails, SubagentRunStatus } from \"./types.ts\";\n\nconst SINGLE_EXCERPT_LIMIT = 200;\nconst RUN_LINE_EXCERPT_LIMIT = 64;\nconst LIVE_TAIL_LIMIT = 100;\nconst COLLAPSED_RUN_LIMIT = 4;\nconst PROMPT_PREVIEW_LINES = 2;\nconst PROMPT_PREVIEW_LINE_LIMIT = 120;\nconst ACTIVITY_SUMMARY_LIMIT = 96;\nconst ACTIVITY_RESULT_LIMIT = 64;\n// Sub-10s tool calls are routine; a duration only earns ink when it\n// explains where the time went.\nconst ACTIVITY_DURATION_MIN_MS = 10_000;\nconst EXPANDED_LIVE_TAIL_LINES = 3;\n\n// Display-only: profile names stay lowercase everywhere the model sees\n// them; the transcript shows \"general\" as \"General\", \"code-reviewer\" as\n// \"Code Reviewer\".\nfunction displayAgentName(name: string): string {\n\treturn name\n\t\t.split(/[-_]/u)\n\t\t.filter(Boolean)\n\t\t.map((word) => `${word[0]?.toUpperCase() ?? \"\"}${word.slice(1)}`)\n\t\t.join(\" \");\n}\n\nfunction singleAgentName(args: { agent?: string | null }): string {\n\treturn displayAgentName(args.agent ?? \"general\");\n}\n\nfunction truncate(text: string, limit: number): string {\n\treturn text.length <= limit ? text : `${text.slice(0, Math.max(0, limit - 1))}…`;\n}\n\n// Transcript excerpts render inside plain Text components, so markdown\n// punctuation would show up literally; strip the common inline syntax.\nfunction stripInlineMarkdown(text: string): string {\n\treturn text\n\t\t.replace(/```[a-zA-Z0-9-]*/gu, \"\")\n\t\t.replace(/`([^`]*)`/gu, \"$1\")\n\t\t.replace(/\\*\\*([^*]+)\\*\\*/gu, \"$1\")\n\t\t.replace(/__([^_]+)__/gu, \"$1\")\n\t\t.replace(/\\[([^\\]]+)\\]\\([^)]*\\)/gu, \"$1\")\n\t\t.replace(/^#{1,6}\\s+/gmu, \"\");\n}\n\n// Markdown tables and horizontal rules turn into symbol soup when\n// flattened onto one line; drop those lines before inline stripping.\nfunction stripBlockMarkdown(text: string): string {\n\treturn text\n\t\t.split(\"\\n\")\n\t\t.filter((line) => {\n\t\t\tconst trimmed = line.trim();\n\t\t\tif (trimmed.startsWith(\"|\")) return false;\n\t\t\treturn !(trimmed.length >= 3 && /^[-—─═=*_|:\\s]+$/u.test(trimmed));\n\t\t})\n\t\t.join(\"\\n\");\n}\n\n// finalOutput/liveText carry model-facing truncation notices from\n// boundText/tailText. Only recognize their exact boundary lines so literal\n// discussion of the marker remains visible in the transcript.\nconst OUTPUT_TRUNCATION_NOTICE_PATTERN = /^\\[Output truncated(?:: \\d+ bytes omitted)?\\.\\]$/u;\nconst EARLIER_OUTPUT_NOTICE = \"[Earlier output omitted.]\";\n\nfunction terminalOutputNotice(text: string): { text: string; truncated: boolean } {\n\tconst lines = text.split(\"\\n\");\n\tlet index = lines.length - 1;\n\twhile (index >= 0 && !lines[index]?.trim()) index--;\n\tif (index < 0 || !OUTPUT_TRUNCATION_NOTICE_PATTERN.test(lines[index]?.trim() ?? \"\")) {\n\t\treturn { text, truncated: false };\n\t}\n\tlines.splice(index, 1);\n\treturn { text: lines.join(\"\\n\").trimEnd(), truncated: true };\n}\n\nfunction replaceBoundaryNotices(text: string): string {\n\tconst lines = text.split(\"\\n\");\n\tconst first = lines.findIndex((line) => Boolean(line.trim()));\n\tif (first >= 0 && lines[first]?.trim() === EARLIER_OUTPUT_NOTICE) lines[first] = \"…\";\n\tlet last = lines.length - 1;\n\twhile (last >= 0 && !lines[last]?.trim()) last--;\n\tif (last >= 0 && OUTPUT_TRUNCATION_NOTICE_PATTERN.test(lines[last]?.trim() ?? \"\")) lines[last] = \"…\";\n\treturn lines.join(\"\\n\");\n}\n\nfunction plainExcerpt(text: string, limit: number): string {\n\treturn truncate(stripInlineMarkdown(stripBlockMarkdown(text)).replace(/\\s+/gu, \" \").trim(), limit);\n}\n\nfunction excerpt(text: string, limit: number): string {\n\treturn plainExcerpt(replaceBoundaryNotices(text), limit);\n}\n\n// Generated notice-only boundary lines must never become the live line the\n// user watches; identical text in the body remains ordinary model output.\nfunction liveTailLines(run: SubagentRunDetails): string[] {\n\tconst { text } = terminalOutputNotice(run.liveText);\n\tconst lines = text\n\t\t.split(\"\\n\")\n\t\t.map((line) => line.trim())\n\t\t.filter(Boolean);\n\tif (lines[0] === EARLIER_OUTPUT_NOTICE) lines.shift();\n\treturn lines;\n}\n\nfunction liveTail(run: SubagentRunDetails): string | undefined {\n\tconst last = liveTailLines(run).at(-1);\n\treturn last ? excerpt(last, LIVE_TAIL_LIMIT) : undefined;\n}\n\n// Completed-run excerpts read better cut at a sentence boundary than\n// mid-word; fall back to a hard truncate when no boundary lands in the\n// second half of the window.\nfunction leadingSentences(text: string, limit: number): string {\n\tconst cleaned = excerpt(text, Number.MAX_SAFE_INTEGER);\n\tif (cleaned.length <= limit) return cleaned;\n\tconst window = cleaned.slice(0, limit);\n\tlet boundary = -1;\n\tfor (const match of window.matchAll(/[.!?。!?](?=\\s|$)/gu)) {\n\t\tboundary = (match.index ?? 0) + match[0].length;\n\t}\n\tif (boundary >= limit / 2) return window.slice(0, boundary).trimEnd();\n\treturn truncate(window, limit);\n}\n\nconst VERIFY_COMMAND_PATTERN = /\\b(test|lint|typecheck|tsc|vitest|jest|pytest|build|check|fmt|format|compile)\\b/iu;\nconst INSPECT_COMMAND_PATTERN = /\\b(rg|grep|find|git (?:diff|status|log|show)|ls|tree|cat|head|tail)\\b/iu;\nconst RETRY_MARKER_PATTERN = /^Retrying\\b/u;\nconst STREAM_MARKERS = new Set([\"Thinking…\", \"Writing response…\"]);\n\n// Translates raw tool traffic into what the worker is doing right now, so\n// the collapsed card reads as a phase instead of a command line. Modelled\n// on tool-type classification; the raw tool summary stays as evidence on\n// the next line.\nfunction runIntent(run: SubagentRunDetails): string {\n\tconst current = run.currentActivity;\n\tif (current && RETRY_MARKER_PATTERN.test(current)) return current;\n\tconst running = [...run.activities].reverse().find((activity) => activity.status === \"running\");\n\tif (running) {\n\t\tif (running.toolName === \"edit\" || running.toolName === \"write\") return \"Applying changes\";\n\t\tif (running.toolName === \"bash\") {\n\t\t\tif (VERIFY_COMMAND_PATTERN.test(running.summary)) return \"Verifying changes\";\n\t\t\treturn INSPECT_COMMAND_PATTERN.test(running.summary) ? \"Exploring code\" : \"Running commands\";\n\t\t}\n\t\treturn \"Exploring code\";\n\t}\n\tconst recent = run.activities.slice(-5);\n\tif (current && STREAM_MARKERS.has(current)) {\n\t\tif (recent.some((activity) => activity.status === \"failed\")) return \"Investigating a failure\";\n\t\treturn current;\n\t}\n\tif (recent.some((activity) => activity.status === \"failed\")) return \"Investigating a failure\";\n\tif (recent.some((activity) => activity.toolName === \"edit\" || activity.toolName === \"write\"))\n\t\treturn \"Reviewing changes\";\n\tif (recent.length > 0) return \"Exploring code\";\n\treturn current ?? \"Thinking…\";\n}\n\n// The raw tool summary backs up the intent line; suppress it when it would\n// just repeat a stream/retry marker.\nfunction runEvidence(run: SubagentRunDetails): string | undefined {\n\tconst current = run.currentActivity;\n\tif (!current || STREAM_MARKERS.has(current) || RETRY_MARKER_PATTERN.test(current)) return undefined;\n\treturn current;\n}\n\nfunction liveElapsed(run: SubagentRunDetails): string | undefined {\n\tif (run.startedAt === undefined) return undefined;\n\tconst end = run.endedAt ?? Date.now();\n\treturn formatDuration(Math.max(0, (end - run.startedAt) / 1000));\n}\n\nfunction statusColor(status: SubagentRunStatus): \"success\" | \"error\" | \"warning\" | \"accent\" | \"muted\" {\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn \"success\";\n\t\tcase \"failed\":\n\t\t\treturn \"error\";\n\t\tcase \"aborted\":\n\t\t\treturn \"warning\";\n\t\tcase \"running\":\n\t\t\treturn \"accent\";\n\t\tdefault:\n\t\t\treturn \"muted\";\n\t}\n}\n\n// The tool shell already paints a call-level status dot (● in\n// warning/success/error), so `●` is off-limits here: `›` marks where the\n// work currently is, and the remaining glyphs are terminal states.\nfunction statusMarker(status: SubagentRunStatus, theme: Theme): string {\n\tconst glyph =\n\t\tstatus === \"completed\"\n\t\t\t? \"✓\"\n\t\t\t: status === \"failed\"\n\t\t\t\t? \"×\"\n\t\t\t\t: status === \"aborted\"\n\t\t\t\t\t? \"■\"\n\t\t\t\t\t: status === \"running\"\n\t\t\t\t\t\t? \"›\"\n\t\t\t\t\t\t: \"○\";\n\treturn theme.fg(statusColor(status), glyph);\n}\n\nfunction statusWord(status: SubagentRunStatus): string {\n\tswitch (status) {\n\t\tcase \"completed\":\n\t\t\treturn \"Completed\";\n\t\tcase \"failed\":\n\t\t\treturn \"Failed\";\n\t\tcase \"aborted\":\n\t\t\treturn \"Aborted\";\n\t\tcase \"running\":\n\t\t\treturn \"Running\";\n\t\tdefault:\n\t\t\treturn \"Queued\";\n\t}\n}\n\nfunction runTitle(run: SubagentRunDetails, theme: Theme, index?: number): string {\n\tconst number = index === undefined ? \"\" : theme.fg(\"dim\", `${index} · `);\n\treturn `${statusMarker(run.status, theme)} ${number}${theme.fg(\"accent\", displayAgentName(run.agent))}${theme.fg(\"dim\", ` · ${truncate(run.description, 48)}`)}`;\n}\n\n// Mid-run token totals are cache-inflated (totalTokens sums cache reads\n// across turns), so live lines quote tool uses and the context watermark\n// instead; settled lines get the real total.\nfunction runProgressText(run: SubagentRunDetails): string {\n\tconst items = [`${run.usage.toolUses} tool use${run.usage.toolUses === 1 ? \"\" : \"s\"}`];\n\tif (run.usage.contextTokens) items.push(`ctx:${formatTokens(run.usage.contextTokens)}`);\n\treturn items.join(\" · \");\n}\n\nfunction runLine(run: SubagentRunDetails, theme: Theme, index?: number): string {\n\tlet line = runTitle(run, theme, index);\n\tconst detail =\n\t\trun.status === \"running\"\n\t\t\t? runIntent(run)\n\t\t\t: run.status === \"failed\" || run.status === \"aborted\"\n\t\t\t\t? run.error && excerpt(run.error, RUN_LINE_EXCERPT_LIMIT)\n\t\t\t\t: run.finalOutput && excerpt(run.finalOutput, RUN_LINE_EXCERPT_LIMIT);\n\tif (detail) line += theme.fg(run.status === \"failed\" ? \"error\" : \"dim\", ` — ${detail}`);\n\tif (run.status === \"running\") {\n\t\tline += theme.fg(\"dim\", ` · ${runProgressText(run)}`);\n\t\tconst elapsed = liveElapsed(run);\n\t\tif (elapsed) line += theme.fg(\"dim\", ` · ${elapsed}`);\n\t}\n\tconst settled = run.status !== \"running\" && run.status !== \"queued\";\n\tif (settled && run.startedAt !== undefined && run.endedAt !== undefined) {\n\t\tline += theme.fg(\"dim\", ` · ${formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000))}`);\n\t}\n\treturn line;\n}\n\n// Keep active work visible while a batch is in flight: completed runs\n// may otherwise crowd a still-running task out of the collapsed window.\nfunction selectCollapsedRuns(runs: SubagentRunDetails[], isPartial: boolean): SubagentRunDetails[] {\n\tif (runs.length <= COLLAPSED_RUN_LIMIT) return runs;\n\tif (!isPartial) return runs.slice(0, COLLAPSED_RUN_LIMIT);\n\tconst active = runs.filter((run) => run.status === \"running\" || run.status === \"queued\");\n\tconst settled = runs.filter((run) => run.status !== \"running\" && run.status !== \"queued\");\n\treturn [...active, ...settled].slice(0, COLLAPSED_RUN_LIMIT);\n}\n\nfunction singleCollapsedLines(details: SubagentDetails, theme: Theme): string[] {\n\tconst run = details.runs[0];\n\tif (!run) return [theme.fg(\"muted\", \"Initializing…\")];\n\tif (run.status === \"running\" || run.status === \"queued\") {\n\t\tconst isInitializing =\n\t\t\trun.status === \"running\" && run.usage.turns === 0 && run.usage.toolUses === 0 && run.liveText.length === 0;\n\t\tconst intent =\n\t\t\trun.status === \"queued\"\n\t\t\t\t? (run.currentActivity ?? \"queued\")\n\t\t\t\t: isInitializing\n\t\t\t\t\t? \"Initializing…\"\n\t\t\t\t\t: runIntent(run);\n\t\t// No status marker here: the shell's call-level dot already covers\n\t\t// the whole card, and a single run has nothing to disambiguate.\n\t\tlet status = theme.fg(\"dim\", intent);\n\t\tif (run.status === \"running\" && !isInitializing) {\n\t\t\tstatus += theme.fg(\"dim\", ` · ${runProgressText(run)}`);\n\t\t\tif (run.usage.cost) status += theme.fg(\"dim\", ` · $${run.usage.cost.toFixed(3)}`);\n\t\t\tconst elapsed = liveElapsed(run);\n\t\t\tif (elapsed) status += theme.fg(\"dim\", ` · ${elapsed}`);\n\t\t}\n\t\tconst lines = [status];\n\t\tconst evidence = runEvidence(run);\n\t\tif (evidence && evidence !== intent)\n\t\t\tlines.push(`${theme.fg(\"accent\", \"›\")} ${theme.fg(\"dim\", excerpt(evidence, LIVE_TAIL_LIMIT))}`);\n\t\tconst tail = liveTail(run);\n\t\tif (tail) lines.push(theme.fg(\"dim\", tail));\n\t\treturn lines;\n\t}\n\tif (run.error) return [theme.fg(\"error\", excerpt(run.error, SINGLE_EXCERPT_LIMIT))];\n\tif (run.finalOutput) return [theme.fg(\"toolOutput\", leadingSentences(run.finalOutput, SINGLE_EXCERPT_LIMIT))];\n\treturn [theme.fg(\"muted\", run.status)];\n}\n\n// Deep-trimmed settled footer: turns and per-run breakdowns belong to the\n// expanded view. Parallel batches keep only whole-call numbers — the run\n// rows above already localize the work.\nfunction collapsedUsageText(details: SubagentDetails, extended: boolean): string {\n\tconst usage = details.usage;\n\tconst items: string[] = [];\n\tif (usage.totalTokens) items.push(`${formatTokens(usage.totalTokens)} tok`);\n\tif (extended && usage.toolUses) items.push(`${usage.toolUses} tool use${usage.toolUses === 1 ? \"\" : \"s\"}`);\n\tif (extended && usage.contextTokens) items.push(`ctx:${formatTokens(usage.contextTokens)}`);\n\tif (usage.cost) items.push(`$${usage.cost.toFixed(3)}`);\n\tif (details.endedAt !== undefined) {\n\t\titems.push(formatDuration(Math.max(0, (details.endedAt - details.startedAt) / 1000)));\n\t}\n\treturn items.join(\" · \");\n}\n\nfunction batchUsageText(details: SubagentDetails, isPartial: boolean): string {\n\tconst usage = details.usage;\n\tconst items: string[] = [];\n\t// The aggregate mixes settled and running runs while partial, and the\n\t// running share is cache-inflated — quote the total only once settled.\n\tif (!isPartial && usage.totalTokens) items.push(`${formatTokens(usage.totalTokens)} tok`);\n\tif (usage.toolUses) items.push(`${usage.toolUses} tool use${usage.toolUses === 1 ? \"\" : \"s\"}`);\n\tif (usage.turns) items.push(`${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}`);\n\tif (usage.contextTokens) items.push(`ctx:${formatTokens(usage.contextTokens)}`);\n\tif (usage.cost) items.push(`$${usage.cost.toFixed(3)}`);\n\tif (details.endedAt !== undefined) {\n\t\titems.push(formatDuration(Math.max(0, (details.endedAt - details.startedAt) / 1000)));\n\t}\n\treturn items.join(\" · \");\n}\n\nfunction formatTokens(value: number): string {\n\tif (value < 1_000) return String(value);\n\tif (value < 1_000_000) return `${(value / 1_000).toFixed(value < 10_000 ? 1 : 0)}k`;\n\treturn `${(value / 1_000_000).toFixed(1)}M`;\n}\n\nfunction formatDuration(seconds: number): string {\n\tif (seconds >= 90) return `${Math.floor(seconds / 60)}m ${Math.round(seconds % 60)}s`;\n\treturn `${seconds.toFixed(seconds < 10 ? 1 : 0)}s`;\n}\n\nfunction runDurationText(run: SubagentRunDetails): string | undefined {\n\tif (run.startedAt === undefined || run.endedAt === undefined) return undefined;\n\treturn formatDuration(Math.max(0, (run.endedAt - run.startedAt) / 1000));\n}\n\n// One dim line per run: what it cost. Settled runs quote the real token\n// total; running runs skip it (cache-inflated mid-run) and tick elapsed\n// time instead. Batch sections fold model/thinking/cwd in here because\n// their `──` header already carries verdict and duration.\nfunction runMetricsText(run: SubagentRunDetails, includeIdentity: boolean): string {\n\tconst usage = run.usage;\n\tconst settled = run.status !== \"running\" && run.status !== \"queued\";\n\tconst items: string[] = [];\n\tif (includeIdentity) {\n\t\titems.push(run.model);\n\t\tif (run.thinking !== \"off\") items.push(run.thinking);\n\t\tif (run.cwd) items.push(`cwd: ${run.cwd}`);\n\t}\n\tif (settled && usage.totalTokens) items.push(`${formatTokens(usage.totalTokens)} tok`);\n\tif (usage.toolUses) items.push(`${usage.toolUses} tool use${usage.toolUses === 1 ? \"\" : \"s\"}`);\n\tif (usage.turns) items.push(`${usage.turns} turn${usage.turns === 1 ? \"\" : \"s\"}`);\n\tif (usage.contextTokens) items.push(`ctx:${formatTokens(usage.contextTokens)}`);\n\tif (usage.cost) items.push(`$${usage.cost.toFixed(3)}`);\n\tconst duration = settled ? (includeIdentity ? undefined : runDurationText(run)) : liveElapsed(run);\n\tif (duration) items.push(duration);\n\treturn items.join(\" · \");\n}\n\n// The details prompt is a 1KB bounded copy of the briefing; the preview\n// shows the first content lines (blank lines carry nothing at two lines\n// of budget) and never pretends to more fidelity than that.\nfunction promptSection(run: SubagentRunDetails, theme: Theme): string[] {\n\tconst boundedPrompt = terminalOutputNotice(run.prompt);\n\tconst lines = boundedPrompt.text\n\t\t.split(\"\\n\")\n\t\t.map((line) => plainExcerpt(line.trim(), Number.MAX_SAFE_INTEGER))\n\t\t.filter(Boolean);\n\tconst shown = lines.slice(0, PROMPT_PREVIEW_LINES);\n\tconst preview = shown.map((line) => theme.fg(\"dim\", truncate(line, PROMPT_PREVIEW_LINE_LIMIT)));\n\t// A one-line briefing clipped mid-sentence must still say it continues.\n\tconst clipped = shown.some((line) => line.length > PROMPT_PREVIEW_LINE_LIMIT);\n\tconst remaining = lines.length - shown.length;\n\tconst capped = boundedPrompt.truncated;\n\tconst section = [theme.fg(\"muted\", \"Prompt\"), ...preview];\n\tif (remaining > 0 || capped || clipped) {\n\t\tlet note = \"… continues\";\n\t\tif (remaining > 0) note += `, ${remaining} more line${remaining === 1 ? \"\" : \"s\"}`;\n\t\tif (capped) note += \" · capped at 1KB\";\n\t\tsection.push(theme.fg(\"muted\", note));\n\t}\n\treturn section;\n}\n\n// Each run expands into a report cover sheet: verdict and cost up top, a\n// bounded Prompt/Activity digest in the middle, then the full Report —\n// the one unbounded element, placed last so the card ends where reading\n// begins. Single runs skip the `──` header (the call header already names\n// them); batch sections carry a numbered header matching the contents\n// list above.\nfunction renderRunDetails(run: SubagentRunDetails, theme: Theme, single: boolean, index?: number): Component {\n\tconst container = new Container();\n\tconst settled = run.status !== \"running\" && run.status !== \"queued\";\n\tif (single) {\n\t\tconst identity = [run.model];\n\t\tif (run.thinking !== \"off\") identity.push(`${run.thinking} thinking`);\n\t\tif (run.cwd) identity.push(`cwd: ${run.cwd}`);\n\t\tcontainer.addChild(\n\t\t\tnew Text(\n\t\t\t\t`${statusMarker(run.status, theme)} ${theme.fg(statusColor(run.status), statusWord(run.status))}${theme.fg(\"dim\", ` · ${identity.join(\" · \")}`)}`,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t} else {\n\t\tconst label = index === undefined ? \"\" : `${index} `;\n\t\tconst duration = settled ? runDurationText(run) : undefined;\n\t\tcontainer.addChild(\n\t\t\tnew Text(\n\t\t\t\t`${theme.fg(\"dim\", `── ${label}`)}${statusMarker(run.status, theme)} ${theme.fg(\"accent\", displayAgentName(run.agent))}${theme.fg(\"dim\", ` · ${truncate(run.description, 48)}`)}${duration ? theme.fg(\"dim\", ` · ${duration}`) : \"\"}`,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t}\n\tif (run.status !== \"queued\") {\n\t\tconst metrics = runMetricsText(run, !single);\n\t\tif (metrics) container.addChild(new Text(theme.fg(\"dim\", metrics), 0, 0));\n\t}\n\tcontainer.addChild(new Spacer(1));\n\tfor (const line of promptSection(run, theme)) container.addChild(new Text(line, 0, 0));\n\tif (run.activities.length > 0) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tconst total = run.usage.toolUses;\n\t\tconst label = total > run.activities.length ? `Activity · last ${run.activities.length} of ${total}` : \"Activity\";\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", label), 0, 0));\n\t\tfor (const activity of run.activities) {\n\t\t\t// Deviation earns ink: × and › are marked, success rows are quiet\n\t\t\t// one-liners without result echoes.\n\t\t\tlet text: string;\n\t\t\tif (activity.status === \"failed\") {\n\t\t\t\ttext = `${theme.fg(\"error\", \"×\")} ${theme.fg(\"toolOutput\", excerpt(activity.summary, ACTIVITY_SUMMARY_LIMIT))}`;\n\t\t\t\tif (activity.resultSummary) {\n\t\t\t\t\ttext += ` ${theme.fg(\"error\", `· ${excerpt(activity.resultSummary, ACTIVITY_RESULT_LIMIT)}`)}`;\n\t\t\t\t}\n\t\t\t} else if (activity.status === \"running\") {\n\t\t\t\ttext = `${theme.fg(\"accent\", \"›\")} ${theme.fg(\"toolOutput\", excerpt(activity.summary, ACTIVITY_SUMMARY_LIMIT))}`;\n\t\t\t} else {\n\t\t\t\ttext = theme.fg(\"toolOutput\", excerpt(activity.summary, ACTIVITY_SUMMARY_LIMIT));\n\t\t\t}\n\t\t\tif (activity.endedAt !== undefined && activity.endedAt - activity.startedAt >= ACTIVITY_DURATION_MIN_MS) {\n\t\t\t\ttext += ` ${theme.fg(\"dim\", `· ${formatDuration((activity.endedAt - activity.startedAt) / 1000)}`)}`;\n\t\t\t}\n\t\t\tcontainer.addChild(new Text(text, 0, 0));\n\t\t}\n\t}\n\t// The live tail is the \"now\" end of the timeline; once the run settles,\n\t// Report takes this slot without moving anything above it.\n\tif (run.status === \"running\" && run.liveText) {\n\t\tconst tail = liveTailLines(run).slice(-EXPANDED_LIVE_TAIL_LINES);\n\t\tif (tail.length > 0) {\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Working\"), 0, 0));\n\t\t\tfor (const line of tail) {\n\t\t\t\tcontainer.addChild(new Text(theme.fg(\"dim\", excerpt(line, LIVE_TAIL_LIMIT)), 0, 0));\n\t\t\t}\n\t\t}\n\t}\n\tif (run.error) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", \"Error\"), 0, 0));\n\t\tcontainer.addChild(new Text(theme.fg(\"error\", run.error), 0, 0));\n\t}\n\tif (run.finalOutput) {\n\t\tcontainer.addChild(new Spacer(1));\n\t\tconst label = settled && run.status !== \"completed\" ? \"Report · partial\" : \"Report\";\n\t\tcontainer.addChild(new Text(theme.fg(\"muted\", label), 0, 0));\n\t\tcontainer.addChild(new Markdown(run.finalOutput, 0, 0, getMarkdownTheme()));\n\t}\n\treturn container;\n}\n\n// The call header stays a single line: run rows in the result area take\n// over within the first update and carry richer per-task state, so a\n// task list here would render everything twice. The task count alone\n// distinguishes a batch from a single delegation.\nexport function renderSubagentCall(args: SubagentParams, theme: Theme): Component {\n\tconst modes = [args.prompt != null && \"prompt\", args.tasks != null && \"tasks\"].filter((mode): mode is string =>\n\t\tBoolean(mode),\n\t);\n\tif (modes.length > 1) {\n\t\treturn new Text(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"Subagent \"))}${theme.fg(\"error\", `invalid · ${modes.join(\" + \")}`)}`,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t}\n\tif (args.tasks) {\n\t\treturn new Text(\n\t\t\t`${theme.fg(\"toolTitle\", theme.bold(\"Multi-Agent\"))}${theme.fg(\"dim\", ` · ${args.tasks.length} task${args.tasks.length === 1 ? \"\" : \"s\"}`)}`,\n\t\t\t0,\n\t\t\t0,\n\t\t);\n\t}\n\tlet text = theme.fg(\"toolTitle\", theme.bold(`${singleAgentName(args)} Agent`));\n\tif (args.description) text += theme.fg(\"dim\", ` · ${truncate(args.description, 72)}`);\n\treturn new Text(text, 0, 0);\n}\n\nexport function renderSubagentResult(\n\tresult: AgentToolResult<SubagentDetails>,\n\toptions: ToolRenderResultOptions,\n\ttheme: Theme,\n\tisError: boolean,\n): Component {\n\tconst details = result.details;\n\tif (!details) {\n\t\tconst text = result.content.find((part) => part.type === \"text\");\n\t\treturn new Text(text?.type === \"text\" ? text.text : \"(no output)\", 0, 0);\n\t}\n\t// The pre-resolution first paint carries no runs yet, in either mode.\n\tif (details.runs.length === 0) {\n\t\treturn new Text(theme.fg(\"muted\", \"Initializing…\"), 0, 0);\n\t}\n\tif (!options.expanded) {\n\t\tconst lines: string[] = [];\n\t\tif (details.mode === \"single\") {\n\t\t\tlines.push(...singleCollapsedLines(details, theme));\n\t\t} else {\n\t\t\tlet header = statusSummary(details);\n\t\t\tif (options.isPartial) {\n\t\t\t\theader += ` · ${formatDuration(Math.max(0, (Date.now() - details.startedAt) / 1000))}`;\n\t\t\t}\n\t\t\tlines.push(theme.fg(isError ? \"error\" : options.isPartial ? \"accent\" : \"muted\", header));\n\t\t\tconst shown = selectCollapsedRuns(details.runs, Boolean(options.isPartial));\n\t\t\t// Stable ordinals (the run's task position, not its display slot)\n\t\t\t// keep identity readable while active-first ordering reshuffles\n\t\t\t// rows, and match the expanded section numbers.\n\t\t\tfor (const run of shown) lines.push(runLine(run, theme, details.runs.indexOf(run) + 1));\n\t\t\tconst hidden = details.runs.length - shown.length;\n\t\t\tif (hidden > 0) lines.push(theme.fg(\"muted\", `+${hidden} more`));\n\t\t}\n\t\tif (!options.isPartial) {\n\t\t\tconst usage = collapsedUsageText(details, details.mode === \"single\");\n\t\t\tif (usage) lines.push(theme.fg(\"dim\", usage));\n\t\t\tlines.push(theme.fg(\"muted\", `(${keyHint(\"app.tools.expand\", \"to expand\")})`));\n\t\t}\n\t\treturn new Text(lines.join(\"\\n\"), 0, 0);\n\t}\n\tconst container = new Container();\n\tconst single = details.mode === \"single\" && details.runs.length === 1;\n\tif (!single) {\n\t\tcontainer.addChild(new Text(theme.fg(isError ? \"error\" : \"toolTitle\", statusSummary(details)), 0, 0));\n\t\t// Numbered table of contents: the TUI cannot jump, but matching the\n\t\t// numbers against the `──` section headers below makes an 8-run\n\t\t// batch navigable by eye.\n\t\tdetails.runs.forEach((run, index) => {\n\t\t\tcontainer.addChild(new Text(runLine(run, theme, index + 1), 0, 0));\n\t\t});\n\t}\n\tdetails.runs.forEach((run, index) => {\n\t\tif (!single || index > 0) container.addChild(new Spacer(1));\n\t\tcontainer.addChild(renderRunDetails(run, theme, single, single ? undefined : index + 1));\n\t});\n\tif (!single) {\n\t\tconst usage = batchUsageText(details, Boolean(options.isPartial));\n\t\tif (usage) {\n\t\t\tcontainer.addChild(new Spacer(1));\n\t\t\tcontainer.addChild(new Text(theme.fg(\"dim\", usage), 0, 0));\n\t\t}\n\t}\n\treturn container;\n}\n"]}
|