@ai-agent-forge/agent-forge 0.88.5 → 0.88.7
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 +36 -0
- package/bundled-plugins/agent-loop-policy/agent-forge.json +1 -1
- package/bundled-plugins/agent-loop-policy/plugin.json +1 -1
- package/bundled-plugins/alignment/agent-forge.json +19 -0
- package/bundled-plugins/alignment/dist/baseline.d.ts +12 -0
- package/bundled-plugins/alignment/dist/baseline.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/baseline.js +68 -0
- package/bundled-plugins/alignment/dist/baseline.js.map +1 -0
- package/bundled-plugins/alignment/dist/engine.d.ts +102 -0
- package/bundled-plugins/alignment/dist/engine.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/engine.js +654 -0
- package/bundled-plugins/alignment/dist/engine.js.map +1 -0
- package/bundled-plugins/alignment/dist/entry.d.ts +14 -0
- package/bundled-plugins/alignment/dist/entry.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/entry.js +23 -0
- package/bundled-plugins/alignment/dist/entry.js.map +1 -0
- package/bundled-plugins/alignment/dist/export.d.ts +15 -0
- package/bundled-plugins/alignment/dist/export.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/export.js +122 -0
- package/bundled-plugins/alignment/dist/export.js.map +1 -0
- package/bundled-plugins/alignment/dist/http-server.d.ts +33 -0
- package/bundled-plugins/alignment/dist/http-server.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/http-server.js +119 -0
- package/bundled-plugins/alignment/dist/http-server.js.map +1 -0
- package/bundled-plugins/alignment/dist/index.d.ts +24 -0
- package/bundled-plugins/alignment/dist/index.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/index.js +719 -0
- package/bundled-plugins/alignment/dist/index.js.map +1 -0
- package/bundled-plugins/alignment/dist/page.d.ts +15 -0
- package/bundled-plugins/alignment/dist/page.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/page.js +857 -0
- package/bundled-plugins/alignment/dist/page.js.map +1 -0
- package/bundled-plugins/alignment/dist/render.d.ts +40 -0
- package/bundled-plugins/alignment/dist/render.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/render.js +194 -0
- package/bundled-plugins/alignment/dist/render.js.map +1 -0
- package/bundled-plugins/alignment/dist/types.d.ts +217 -0
- package/bundled-plugins/alignment/dist/types.d.ts.map +1 -0
- package/bundled-plugins/alignment/dist/types.js +30 -0
- package/bundled-plugins/alignment/dist/types.js.map +1 -0
- package/bundled-plugins/alignment/plugin.json +12 -0
- package/bundled-plugins/browser-playwright/MANUAL.md +60 -0
- package/bundled-plugins/browser-playwright/agent-forge.json +25 -0
- package/bundled-plugins/browser-playwright/dist/capability.d.ts +25 -0
- package/bundled-plugins/browser-playwright/dist/capability.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/capability.js +612 -0
- package/bundled-plugins/browser-playwright/dist/capability.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/entry.d.ts +13 -0
- package/bundled-plugins/browser-playwright/dist/entry.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/entry.js +14 -0
- package/bundled-plugins/browser-playwright/dist/entry.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/index.d.ts +7 -0
- package/bundled-plugins/browser-playwright/dist/index.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/index.js +5 -0
- package/bundled-plugins/browser-playwright/dist/index.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/operations.d.ts +69 -0
- package/bundled-plugins/browser-playwright/dist/operations.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/operations.js +149 -0
- package/bundled-plugins/browser-playwright/dist/operations.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts +275 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.js +953 -0
- package/bundled-plugins/browser-playwright/dist/playwright-driver.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/setup.d.ts +88 -0
- package/bundled-plugins/browser-playwright/dist/setup.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/setup.js +252 -0
- package/bundled-plugins/browser-playwright/dist/setup.js.map +1 -0
- package/bundled-plugins/browser-playwright/dist/types.d.ts +270 -0
- package/bundled-plugins/browser-playwright/dist/types.d.ts.map +1 -0
- package/bundled-plugins/browser-playwright/dist/types.js +2 -0
- package/bundled-plugins/browser-playwright/dist/types.js.map +1 -0
- package/bundled-plugins/browser-playwright/plugin.json +20 -0
- package/bundled-plugins/coding-tools/agent-forge.json +1 -1
- package/bundled-plugins/coding-tools/dist/bash.d.ts +2 -7
- package/bundled-plugins/coding-tools/dist/bash.d.ts.map +1 -1
- package/bundled-plugins/coding-tools/dist/bash.js +2 -34
- package/bundled-plugins/coding-tools/dist/bash.js.map +1 -1
- package/bundled-plugins/coding-tools/dist/read.d.ts +2 -0
- package/bundled-plugins/coding-tools/dist/read.d.ts.map +1 -1
- package/bundled-plugins/coding-tools/dist/read.js +11 -0
- package/bundled-plugins/coding-tools/dist/read.js.map +1 -1
- package/bundled-plugins/coding-tools/plugin.json +1 -1
- package/bundled-plugins/compaction-policy/agent-forge.json +1 -1
- package/bundled-plugins/compaction-policy/plugin.json +1 -1
- package/bundled-plugins/mcp-http/dist/mcp-http.d.ts.map +1 -1
- package/bundled-plugins/mcp-http/dist/mcp-http.js +84 -31
- package/bundled-plugins/mcp-http/dist/mcp-http.js.map +1 -1
- package/bundled-plugins/overflow-policy/agent-forge.json +1 -1
- package/bundled-plugins/overflow-policy/plugin.json +1 -1
- package/bundled-plugins/provider-failover/agent-forge.json +1 -1
- package/bundled-plugins/provider-failover/plugin.json +1 -1
- package/bundled-plugins/retry-policy/agent-forge.json +1 -1
- package/bundled-plugins/retry-policy/plugin.json +1 -1
- package/dist/bundle/.build-manifest.json +60 -58
- package/dist/bundle/chunks/{anthropic-messages-3FJHRAJL.js → anthropic-messages-NL4Y6WPB.js} +1 -1
- package/dist/bundle/chunks/{azure-openai-responses-GCL47IDS.js → azure-openai-responses-CPXC42HO.js} +1 -1
- package/dist/bundle/chunks/bedrock-converse-stream.js +1 -1
- package/dist/bundle/chunks/chunk-5LRQFZFE.js +2 -0
- package/dist/bundle/chunks/{chunk-HTEVUY7F.js → chunk-7HSJPQKN.js} +1 -1
- package/dist/bundle/chunks/{chunk-AC2FIS4G.js → chunk-EDO7Q6XR.js} +1 -1
- package/dist/bundle/chunks/{chunk-755VNWIF.js → chunk-ISLHFRER.js} +6 -6
- package/dist/bundle/chunks/{chunk-HV64ASMT.js → chunk-K3S6GTL7.js} +1 -1
- package/dist/bundle/chunks/{chunk-HEL6TAWE.js → chunk-L2SXIPI5.js} +37 -36
- package/dist/bundle/chunks/{chunk-V3ACW7VD.js → chunk-WJSKB3FC.js} +1 -1
- package/dist/bundle/chunks/{chunk-32YDZI4J.js → chunk-ZS5JEI76.js} +1 -1
- package/dist/bundle/chunks/{google-generative-ai-6BMXWA44.js → google-generative-ai-UMZ53EFA.js} +1 -1
- package/dist/bundle/chunks/{google-vertex-6H7NLRTK.js → google-vertex-ZNR5NS6H.js} +1 -1
- package/dist/bundle/chunks/image-resize-worker.js +1 -1
- package/dist/bundle/chunks/{mistral-conversations-IXUHSFSJ.js → mistral-conversations-IT66WUNN.js} +1 -1
- package/dist/bundle/chunks/{openai-codex-responses-YHUAEVCM.js → openai-codex-responses-FOVCVG6I.js} +1 -1
- package/dist/bundle/chunks/{openai-completions-ULQCFC72.js → openai-completions-FLQ7CBOL.js} +1 -1
- package/dist/bundle/chunks/{openai-responses-NLLAJDPS.js → openai-responses-ABGYHS2T.js} +1 -1
- package/dist/bundle/chunks/{pi-messages-EJTI2EJ3.js → pi-messages-6K5W6LUN.js} +1 -1
- package/dist/bundle/chunks/{virtual-modules-TQWEGNNZ.js → virtual-modules-2QGILFV7.js} +1 -1
- package/dist/bundle/cli.js +1 -1
- package/dist/bundle/client.js +1 -1
- package/dist/bundle/index.js +1 -1
- package/dist/bundle/rpc-entry.js +1 -1
- package/dist/client/remote-session.d.ts.map +1 -1
- package/dist/client/remote-session.js +65 -6
- package/dist/client/remote-session.js.map +1 -1
- package/dist/core/agent-session-store.d.ts.map +1 -1
- package/dist/core/agent-session-store.js +20 -3
- package/dist/core/agent-session-store.js.map +1 -1
- package/dist/core/extensions/agent-composition.d.ts.map +1 -1
- package/dist/core/extensions/agent-composition.js +20 -1
- package/dist/core/extensions/agent-composition.js.map +1 -1
- package/dist/core/i18n/locales/zh-cn/core-errors.d.ts.map +1 -1
- package/dist/core/i18n/locales/zh-cn/core-errors.js +1 -0
- package/dist/core/i18n/locales/zh-cn/core-errors.js.map +1 -1
- package/dist/core/i18n/locales/zh-cn/interactive-mode.d.ts.map +1 -1
- package/dist/core/i18n/locales/zh-cn/interactive-mode.js +10 -3
- package/dist/core/i18n/locales/zh-cn/interactive-mode.js.map +1 -1
- package/dist/core/keybindings.d.ts +35 -0
- package/dist/core/keybindings.d.ts.map +1 -1
- package/dist/core/keybindings.js +34 -0
- package/dist/core/keybindings.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +62 -15
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +11 -3
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/system-prompt.d.ts.map +1 -1
- package/dist/core/system-prompt.js +2 -2
- package/dist/core/system-prompt.js.map +1 -1
- package/dist/core/tools/capability-mount.d.ts.map +1 -1
- package/dist/core/tools/capability-mount.js +4 -21
- package/dist/core/tools/capability-mount.js.map +1 -1
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/dist/modes/interactive/components/assistant-message.js +2 -2
- package/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/dist/modes/interactive/components/bash-execution.d.ts +2 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/bash-execution.js +25 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -1
- package/dist/modes/interactive/components/changelog-pager.d.ts +53 -0
- package/dist/modes/interactive/components/changelog-pager.d.ts.map +1 -0
- package/dist/modes/interactive/components/changelog-pager.js +239 -0
- package/dist/modes/interactive/components/changelog-pager.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-execution.js +12 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/dist/modes/interactive/components/tool-run-group.d.ts +41 -11
- package/dist/modes/interactive/components/tool-run-group.d.ts.map +1 -1
- package/dist/modes/interactive/components/tool-run-group.js +126 -14
- package/dist/modes/interactive/components/tool-run-group.js.map +1 -1
- package/dist/modes/interactive/interactive-commands.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-commands.js +23 -13
- package/dist/modes/interactive/interactive-commands.js.map +1 -1
- package/dist/modes/interactive/interactive-events.d.ts +14 -4
- package/dist/modes/interactive/interactive-events.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-events.js +94 -12
- package/dist/modes/interactive/interactive-events.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +6 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +21 -5
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/plugins/ecosystem/node-local-package-source.d.ts +2 -1
- package/dist/plugins/ecosystem/node-local-package-source.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-local-package-source.js +110 -4
- package/dist/plugins/ecosystem/node-local-package-source.js.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-package-install-host.js +106 -39
- package/dist/plugins/ecosystem/node-package-install-host.js.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-bootstrap.d.ts.map +1 -1
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js +85 -2
- package/dist/plugins/ecosystem/node-plugin-bootstrap.js.map +1 -1
- package/dist/suites/builtin-suites.d.ts.map +1 -1
- package/dist/suites/builtin-suites.js +2 -2
- package/dist/suites/builtin-suites.js.map +1 -1
- package/dist/utils/changelog.d.ts +27 -0
- package/dist/utils/changelog.d.ts.map +1 -1
- package/dist/utils/changelog.js +110 -0
- package/dist/utils/changelog.js.map +1 -1
- package/docs/keybindings.md +14 -0
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/examples/extensions/with-deps/plugin.json +1 -1
- package/package.json +1 -1
- package/dist/bundle/chunks/chunk-A3JYRWB6.js +0 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAexE,YAAY,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEhG,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAuM3E","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport type { BuildSystemPromptOptions } from \"@agent-forge/plugin-sdk\";\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\n// Capability tool guides (memory / task management) moved to their owning\n// plugins in D-071 (the memory capability itself moved to the\n// @agent-forge/plugin-memory package in D-075 S4-4): the plugin's\n// MEMORY_TOOLS_GUIDE rides memory_recall's promptGuidelines and the goal\n// plugin's TASK_TOOLS_GUIDE rides goal_set's (宪法 §3 — prompt text lives with\n// the plugin that owns the tools). The kernel composes whatever the active\n// tools declare.\n\n// BuildSystemPromptOptions authority moved to @agent-forge/plugin-sdk\n// (D-075 S2); re-exported here so existing consumers keep resolving. The SDK\n// type projects skills structurally; hosts always pass their full Skill objects.\nexport type { BuildSystemPromptOptions, BuildSystemPromptSkill } from \"@agent-forge/plugin-sdk\";\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\torientationPrompt,\n\t\tpreferenceCard,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tcapabilityCards,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\tconst orientationSection = orientationPrompt ? `\\n\\n${orientationPrompt}` : \"\";\n\tconst preferenceCardSection = preferenceCard ? `\\n\\n${preferenceCard}` : \"\";\n\t// Mountable capability cards (插件按需挂载与能力手册设计 §4): one line per\n\t// deferred on-demand plugin. capability_mount loads the full tool set and\n\t// returns the plugin's manual; mounted capabilities drop off this section\n\t// on the next prompt rebuild.\n\tconst capabilityCardsSection =\n\t\tcapabilityCards === undefined || capabilityCards.length === 0\n\t\t\t? \"\"\n\t\t\t: `\\n\\nMountable capabilities (deferred plugins; call capability_mount with the plugin id to load the full tool set and its usage manual):\\n${capabilityCards\n\t\t\t\t\t.map((card) => `- ${card.pluginId}${card.description ? `: ${card.description}` : \"\"}`)\n\t\t\t\t\t.join(\"\\n\")}`;\n\n\tconst contextFiles = providedContextFiles ?? [];\n\t// SDK options type projects skills structurally; narrow to the host Skill\n\t// for the formatter (D-075 S2 move).\n\tconst skills = (providedSkills as Skill[] | undefined) ?? [];\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst skillFileReadTool = ([\"read\", \"bash\"] as const).find((tool) => tools.includes(tool));\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\t// The orientation is a session-nature declaration (设计 §6.2): it rides\n\t\t// the customPrompt branch too, ahead of the appended extras. The\n\t\t// preference card follows it (H2: same M2 injection pattern).\n\t\tif (orientationSection) {\n\t\t\tprompt += orientationSection;\n\t\t}\n\t\tif (preferenceCardSection) {\n\t\t\tprompt += preferenceCardSection;\n\t\t}\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\t\tprompt += capabilityCardsSection;\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills when a tool capable of reading their files is available.\n\t\tif (skillFileReadTool && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}\\n`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// A tool appears in Available tools only when the caller provides a one-line snippet.\n\tconst visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join(\"\\n\") : \"(none)\";\n\n\t// Read-only tool surface (提示词设计宪法 §6 2026-10-02): when no known\n\t// mutating tool is active (--tools narrowing, readonly embeds), the intro\n\t// must not promise file editing and a work rule reframes execution requests\n\t// as change-plan requests. New mutating capability tools must be added here.\n\tconst MUTATING_TOOL_NAMES = new Set([\n\t\t\"write\",\n\t\t\"edit\",\n\t\t\"bash\",\n\t\t\"powershell\",\n\t\t\"process_exec\",\n\t\t\"workspace_write\",\n\t\t\"test_run\",\n\t\t\"formatter_linter_check\",\n\t\t\"desktop_execute\",\n\t\t\"image_edit\",\n\t]);\n\tconst readOnlySession = !tools.some((name) => MUTATING_TOOL_NAMES.has(name));\n\tconst introScope = readOnlySession\n\t\t? \"you complete the user's task in the current working directory by reading code and running non-mutating commands. This session has no tools that modify files or run state-changing commands, so you cannot make changes here\"\n\t\t: \"you complete the user's task in the current working directory by reading code, running commands, and editing or creating files\";\n\tconst readOnlyWorkRule = readOnlySession\n\t\t? \"\\n- Treat execution requests as requests for a change plan: this session cannot make changes, so when the task needs them, delivering the exact plan — which files, what edits, how to verify — completes the task; do not attempt workarounds for the missing tools.\"\n\t\t: \"\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasPowerShell = tools.includes(\"powershell\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\n\t// File exploration guidelines\n\tif ((hasBash || hasPowerShell) && !hasGrep && !hasFind && !hasLs) {\n\t\tif (hasBash && hasPowerShell) {\n\t\t\taddGuideline(\"Use bash or PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else if (hasPowerShell) {\n\t\t\taddGuideline(\"Use PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else {\n\t\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t\t}\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include this\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are Agent Forge, a coding agent: ${introScope}. Agent Forge is a minimal, extensible runtime: capabilities beyond the built-in tools come from installed plugins, skills, MCP servers, and CLI bridges — choose and compose them yourself when they serve the goal. The runtime defines only the readonly and full-control host modes; approval and safety policy belongs to the user's installed extensions, not to you.\n\nWork rules:\n- Work toward the stated outcome: start multi-step requests with a tool call, keep working through tools until the task is complete, and end with a summary of results — never with a plan or a promise of future work.\n- Make the smallest change that fully satisfies the task; add nothing unsolicited — no extra features, refactors, warnings, or disclaimers.\n- Report outcomes faithfully: failures with their output, skipped steps as skipped, finished and verified work as finished.\n- Lead replies with the outcome and keep only the evidence needed to assess it; drop filler.${readOnlyWorkRule}\n- The user's instructions and project context files take precedence over these defaults.${orientationSection}${preferenceCardSection}\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAgent Forge documentation (read only when the user asks about Agent Forge itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When reading Agent Forge docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), packages (docs/packages.md), environment variables (docs/environment-variables.md)\n- When working on Agent Forge topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read the relevant .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\tprompt += capabilityCardsSection;\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t}\n\t\tprompt += \"</project_context>\\n\";\n\t}\n\n\t// Append skills when a tool capable of reading their files is available.\n\tif (skillFileReadTool && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t}\n\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AAexE,YAAY,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEhG,kEAAkE;AAClE,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,wBAAwB,GAAG,MAAM,CAuM3E","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport type { BuildSystemPromptOptions } from \"@agent-forge/plugin-sdk\";\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\n// Capability tool guides (memory / task management) moved to their owning\n// plugins in D-071 (the memory capability itself moved to the\n// @agent-forge/plugin-memory package in D-075 S4-4): the plugin's\n// MEMORY_TOOLS_GUIDE rides memory_recall's promptGuidelines and the goal\n// plugin's TASK_TOOLS_GUIDE rides goal_set's (宪法 §3 — prompt text lives with\n// the plugin that owns the tools). The kernel composes whatever the active\n// tools declare.\n\n// BuildSystemPromptOptions authority moved to @agent-forge/plugin-sdk\n// (D-075 S2); re-exported here so existing consumers keep resolving. The SDK\n// type projects skills structurally; hosts always pass their full Skill objects.\nexport type { BuildSystemPromptOptions, BuildSystemPromptSkill } from \"@agent-forge/plugin-sdk\";\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\torientationPrompt,\n\t\tpreferenceCard,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tcapabilityCards,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\tconst orientationSection = orientationPrompt ? `\\n\\n${orientationPrompt}` : \"\";\n\tconst preferenceCardSection = preferenceCard ? `\\n\\n${preferenceCard}` : \"\";\n\t// Mountable capability cards (插件按需挂载与能力手册设计 §4): one line per\n\t// deferred on-demand plugin. capability_mount loads the full tool set and\n\t// returns the plugin's manual; mounted capabilities drop off this section\n\t// on the next prompt rebuild.\n\tconst capabilityCardsSection =\n\t\tcapabilityCards === undefined || capabilityCards.length === 0\n\t\t\t? \"\"\n\t\t\t: `\\n\\nMountable capabilities (deferred plugins; call capability_mount with the plugin id to load the full tool set and its usage manual):\\n${capabilityCards\n\t\t\t\t\t.map((card) => `- ${card.pluginId}${card.description ? `: ${card.description}` : \"\"}`)\n\t\t\t\t\t.join(\"\\n\")}`;\n\n\tconst contextFiles = providedContextFiles ?? [];\n\t// SDK options type projects skills structurally; narrow to the host Skill\n\t// for the formatter (D-075 S2 move).\n\tconst skills = (providedSkills as Skill[] | undefined) ?? [];\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst skillFileReadTool = ([\"read\", \"bash\"] as const).find((tool) => tools.includes(tool));\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\t// The orientation is a session-nature declaration (设计 §6.2): it rides\n\t\t// the customPrompt branch too, ahead of the appended extras. The\n\t\t// preference card follows it (H2: same M2 injection pattern).\n\t\tif (orientationSection) {\n\t\t\tprompt += orientationSection;\n\t\t}\n\t\tif (preferenceCardSection) {\n\t\t\tprompt += preferenceCardSection;\n\t\t}\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\t\tprompt += capabilityCardsSection;\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills when a tool capable of reading their files is available.\n\t\tif (skillFileReadTool && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}\\n`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// A tool appears in Available tools only when the caller provides a one-line snippet.\n\tconst visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join(\"\\n\") : \"(none)\";\n\n\t// Read-only tool surface (提示词设计宪法 §6 2026-10-02): when no known\n\t// mutating tool is active (--tools narrowing, readonly embeds), the intro\n\t// must not promise file editing and a work rule reframes execution requests\n\t// as change-plan requests. New mutating capability tools must be added here.\n\tconst MUTATING_TOOL_NAMES = new Set([\n\t\t\"write\",\n\t\t\"edit\",\n\t\t\"bash\",\n\t\t\"powershell\",\n\t\t\"process_exec\",\n\t\t\"workspace_write\",\n\t\t\"test_run\",\n\t\t\"formatter_linter_check\",\n\t\t\"desktop_execute\",\n\t\t\"image_edit\",\n\t]);\n\tconst readOnlySession = !tools.some((name) => MUTATING_TOOL_NAMES.has(name));\n\tconst introScope = readOnlySession\n\t\t? \"you complete the user's task in the current working directory by reading code and running non-mutating commands. This session has no tools that modify files or run state-changing commands, so you cannot make changes here\"\n\t\t: \"you complete the user's task in the current working directory by reading code, running commands, and editing or creating files\";\n\tconst readOnlyWorkRule = readOnlySession\n\t\t? \"\\n- Treat execution requests as requests for a change plan: this session cannot make changes, so when the task needs them, delivering the exact plan — which files, what edits, how to verify — completes the task; do not attempt workarounds for the missing tools.\"\n\t\t: \"\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasPowerShell = tools.includes(\"powershell\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\n\t// File exploration guidelines\n\tif ((hasBash || hasPowerShell) && !hasGrep && !hasFind && !hasLs) {\n\t\tif (hasBash && hasPowerShell) {\n\t\t\taddGuideline(\"Use bash or PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else if (hasPowerShell) {\n\t\t\taddGuideline(\"Use PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else {\n\t\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t\t}\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include this\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are Agent Forge, a coding agent: ${introScope}. Agent Forge is a minimal, extensible runtime: capabilities beyond the built-in tools come from installed plugins, skills, MCP servers, and CLI bridges — choose and compose them yourself when they serve the goal. The runtime defines only the readonly and full-control host modes; approval and safety policy belongs to the user's installed extensions, not to you.\n\nWork rules:\n- Work toward the stated outcome: start multi-step requests with a tool call, keep working through tools until the task is complete, and end with a summary of results — never with a plan or a promise of future work.\n- Make the smallest change that fully satisfies the task; add nothing unsolicited — no extra features, refactors, warnings, or disclaimers beyond the task itself. \"Smallest\" bounds what you add, never how deeply you fix: when the proper fix exceeds the task's scope or risk, surface the trade-off and your recommendation instead of silently downgrading to a symptom patch; when no in-scope fix exists, delivering that decision-ready trade-off completes the task.\n- Report outcomes faithfully: failures with their output, skipped steps as skipped, finished and verified work as finished.\n- Lead replies with the outcome and keep only the evidence needed to assess it; drop filler.${readOnlyWorkRule}\n- The user's instructions and project context files take precedence over these defaults.${orientationSection}${preferenceCardSection}\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAgent Forge documentation (read only when the user asks about Agent Forge itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When reading Agent Forge docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), packages (docs/packages.md), environment variables (docs/environment-variables.md)\n- When working on Agent Forge topics, read the docs and examples, and follow .md cross-references before implementing\n- When working on Agent Forge topics, read the relevant .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\tprompt += capabilityCardsSection;\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t}\n\t\tprompt += \"</project_context>\\n\";\n\t}\n\n\t// Append skills when a tool capable of reading their files is available.\n\tif (skillFileReadTool && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t}\n\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
@@ -127,7 +127,7 @@ export function buildSystemPrompt(options) {
|
|
|
127
127
|
|
|
128
128
|
Work rules:
|
|
129
129
|
- Work toward the stated outcome: start multi-step requests with a tool call, keep working through tools until the task is complete, and end with a summary of results — never with a plan or a promise of future work.
|
|
130
|
-
- Make the smallest change that fully satisfies the task; add nothing unsolicited — no extra features, refactors, warnings, or disclaimers.
|
|
130
|
+
- Make the smallest change that fully satisfies the task; add nothing unsolicited — no extra features, refactors, warnings, or disclaimers beyond the task itself. "Smallest" bounds what you add, never how deeply you fix: when the proper fix exceeds the task's scope or risk, surface the trade-off and your recommendation instead of silently downgrading to a symptom patch; when no in-scope fix exists, delivering that decision-ready trade-off completes the task.
|
|
131
131
|
- Report outcomes faithfully: failures with their output, skipped steps as skipped, finished and verified work as finished.
|
|
132
132
|
- Lead replies with the outcome and keep only the evidence needed to assess it; drop filler.${readOnlyWorkRule}
|
|
133
133
|
- The user's instructions and project context files take precedence over these defaults.${orientationSection}${preferenceCardSection}
|
|
@@ -147,7 +147,7 @@ Agent Forge documentation (read only when the user asks about Agent Forge itself
|
|
|
147
147
|
- When reading Agent Forge docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory
|
|
148
148
|
- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), packages (docs/packages.md), environment variables (docs/environment-variables.md)
|
|
149
149
|
- When working on Agent Forge topics, read the docs and examples, and follow .md cross-references before implementing
|
|
150
|
-
-
|
|
150
|
+
- When working on Agent Forge topics, read the relevant .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;
|
|
151
151
|
if (appendSection) {
|
|
152
152
|
prompt += appendSection;
|
|
153
153
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport type { BuildSystemPromptOptions } from \"@agent-forge/plugin-sdk\";\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\n// Capability tool guides (memory / task management) moved to their owning\n// plugins in D-071 (the memory capability itself moved to the\n// @agent-forge/plugin-memory package in D-075 S4-4): the plugin's\n// MEMORY_TOOLS_GUIDE rides memory_recall's promptGuidelines and the goal\n// plugin's TASK_TOOLS_GUIDE rides goal_set's (宪法 §3 — prompt text lives with\n// the plugin that owns the tools). The kernel composes whatever the active\n// tools declare.\n\n// BuildSystemPromptOptions authority moved to @agent-forge/plugin-sdk\n// (D-075 S2); re-exported here so existing consumers keep resolving. The SDK\n// type projects skills structurally; hosts always pass their full Skill objects.\nexport type { BuildSystemPromptOptions, BuildSystemPromptSkill } from \"@agent-forge/plugin-sdk\";\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\torientationPrompt,\n\t\tpreferenceCard,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tcapabilityCards,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\tconst orientationSection = orientationPrompt ? `\\n\\n${orientationPrompt}` : \"\";\n\tconst preferenceCardSection = preferenceCard ? `\\n\\n${preferenceCard}` : \"\";\n\t// Mountable capability cards (插件按需挂载与能力手册设计 §4): one line per\n\t// deferred on-demand plugin. capability_mount loads the full tool set and\n\t// returns the plugin's manual; mounted capabilities drop off this section\n\t// on the next prompt rebuild.\n\tconst capabilityCardsSection =\n\t\tcapabilityCards === undefined || capabilityCards.length === 0\n\t\t\t? \"\"\n\t\t\t: `\\n\\nMountable capabilities (deferred plugins; call capability_mount with the plugin id to load the full tool set and its usage manual):\\n${capabilityCards\n\t\t\t\t\t.map((card) => `- ${card.pluginId}${card.description ? `: ${card.description}` : \"\"}`)\n\t\t\t\t\t.join(\"\\n\")}`;\n\n\tconst contextFiles = providedContextFiles ?? [];\n\t// SDK options type projects skills structurally; narrow to the host Skill\n\t// for the formatter (D-075 S2 move).\n\tconst skills = (providedSkills as Skill[] | undefined) ?? [];\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst skillFileReadTool = ([\"read\", \"bash\"] as const).find((tool) => tools.includes(tool));\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\t// The orientation is a session-nature declaration (设计 §6.2): it rides\n\t\t// the customPrompt branch too, ahead of the appended extras. The\n\t\t// preference card follows it (H2: same M2 injection pattern).\n\t\tif (orientationSection) {\n\t\t\tprompt += orientationSection;\n\t\t}\n\t\tif (preferenceCardSection) {\n\t\t\tprompt += preferenceCardSection;\n\t\t}\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\t\tprompt += capabilityCardsSection;\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills when a tool capable of reading their files is available.\n\t\tif (skillFileReadTool && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}\\n`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// A tool appears in Available tools only when the caller provides a one-line snippet.\n\tconst visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join(\"\\n\") : \"(none)\";\n\n\t// Read-only tool surface (提示词设计宪法 §6 2026-10-02): when no known\n\t// mutating tool is active (--tools narrowing, readonly embeds), the intro\n\t// must not promise file editing and a work rule reframes execution requests\n\t// as change-plan requests. New mutating capability tools must be added here.\n\tconst MUTATING_TOOL_NAMES = new Set([\n\t\t\"write\",\n\t\t\"edit\",\n\t\t\"bash\",\n\t\t\"powershell\",\n\t\t\"process_exec\",\n\t\t\"workspace_write\",\n\t\t\"test_run\",\n\t\t\"formatter_linter_check\",\n\t\t\"desktop_execute\",\n\t\t\"image_edit\",\n\t]);\n\tconst readOnlySession = !tools.some((name) => MUTATING_TOOL_NAMES.has(name));\n\tconst introScope = readOnlySession\n\t\t? \"you complete the user's task in the current working directory by reading code and running non-mutating commands. This session has no tools that modify files or run state-changing commands, so you cannot make changes here\"\n\t\t: \"you complete the user's task in the current working directory by reading code, running commands, and editing or creating files\";\n\tconst readOnlyWorkRule = readOnlySession\n\t\t? \"\\n- Treat execution requests as requests for a change plan: this session cannot make changes, so when the task needs them, delivering the exact plan — which files, what edits, how to verify — completes the task; do not attempt workarounds for the missing tools.\"\n\t\t: \"\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasPowerShell = tools.includes(\"powershell\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\n\t// File exploration guidelines\n\tif ((hasBash || hasPowerShell) && !hasGrep && !hasFind && !hasLs) {\n\t\tif (hasBash && hasPowerShell) {\n\t\t\taddGuideline(\"Use bash or PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else if (hasPowerShell) {\n\t\t\taddGuideline(\"Use PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else {\n\t\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t\t}\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include this\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are Agent Forge, a coding agent: ${introScope}. Agent Forge is a minimal, extensible runtime: capabilities beyond the built-in tools come from installed plugins, skills, MCP servers, and CLI bridges — choose and compose them yourself when they serve the goal. The runtime defines only the readonly and full-control host modes; approval and safety policy belongs to the user's installed extensions, not to you.\n\nWork rules:\n- Work toward the stated outcome: start multi-step requests with a tool call, keep working through tools until the task is complete, and end with a summary of results — never with a plan or a promise of future work.\n- Make the smallest change that fully satisfies the task; add nothing unsolicited — no extra features, refactors, warnings, or disclaimers.\n- Report outcomes faithfully: failures with their output, skipped steps as skipped, finished and verified work as finished.\n- Lead replies with the outcome and keep only the evidence needed to assess it; drop filler.${readOnlyWorkRule}\n- The user's instructions and project context files take precedence over these defaults.${orientationSection}${preferenceCardSection}\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAgent Forge documentation (read only when the user asks about Agent Forge itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When reading Agent Forge docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), packages (docs/packages.md), environment variables (docs/environment-variables.md)\n- When working on Agent Forge topics, read the docs and examples, and follow .md cross-references before implementing\n- Always read the relevant .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\tprompt += capabilityCardsSection;\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t}\n\t\tprompt += \"</project_context>\\n\";\n\t}\n\n\t// Append skills when a tool capable of reading their files is available.\n\tif (skillFileReadTool && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t}\n\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"system-prompt.js","sourceRoot":"","sources":["../../src/core/system-prompt.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * System prompt construction and project context loading\n */\n\nimport type { BuildSystemPromptOptions } from \"@agent-forge/plugin-sdk\";\nimport { getDocsPath, getExamplesPath, getReadmePath } from \"../config.ts\";\nimport { formatSkillsForPrompt, type Skill } from \"./skills.ts\";\n\n// Capability tool guides (memory / task management) moved to their owning\n// plugins in D-071 (the memory capability itself moved to the\n// @agent-forge/plugin-memory package in D-075 S4-4): the plugin's\n// MEMORY_TOOLS_GUIDE rides memory_recall's promptGuidelines and the goal\n// plugin's TASK_TOOLS_GUIDE rides goal_set's (宪法 §3 — prompt text lives with\n// the plugin that owns the tools). The kernel composes whatever the active\n// tools declare.\n\n// BuildSystemPromptOptions authority moved to @agent-forge/plugin-sdk\n// (D-075 S2); re-exported here so existing consumers keep resolving. The SDK\n// type projects skills structurally; hosts always pass their full Skill objects.\nexport type { BuildSystemPromptOptions, BuildSystemPromptSkill } from \"@agent-forge/plugin-sdk\";\n\n/** Build the system prompt with tools, guidelines, and context */\nexport function buildSystemPrompt(options: BuildSystemPromptOptions): string {\n\tconst {\n\t\tcustomPrompt,\n\t\torientationPrompt,\n\t\tpreferenceCard,\n\t\tselectedTools,\n\t\ttoolSnippets,\n\t\tpromptGuidelines,\n\t\tcapabilityCards,\n\t\tappendSystemPrompt,\n\t\tcwd,\n\t\tcontextFiles: providedContextFiles,\n\t\tskills: providedSkills,\n\t} = options;\n\tconst promptCwd = cwd.replace(/\\\\/g, \"/\");\n\n\tconst appendSection = appendSystemPrompt ? `\\n\\n${appendSystemPrompt}` : \"\";\n\tconst orientationSection = orientationPrompt ? `\\n\\n${orientationPrompt}` : \"\";\n\tconst preferenceCardSection = preferenceCard ? `\\n\\n${preferenceCard}` : \"\";\n\t// Mountable capability cards (插件按需挂载与能力手册设计 §4): one line per\n\t// deferred on-demand plugin. capability_mount loads the full tool set and\n\t// returns the plugin's manual; mounted capabilities drop off this section\n\t// on the next prompt rebuild.\n\tconst capabilityCardsSection =\n\t\tcapabilityCards === undefined || capabilityCards.length === 0\n\t\t\t? \"\"\n\t\t\t: `\\n\\nMountable capabilities (deferred plugins; call capability_mount with the plugin id to load the full tool set and its usage manual):\\n${capabilityCards\n\t\t\t\t\t.map((card) => `- ${card.pluginId}${card.description ? `: ${card.description}` : \"\"}`)\n\t\t\t\t\t.join(\"\\n\")}`;\n\n\tconst contextFiles = providedContextFiles ?? [];\n\t// SDK options type projects skills structurally; narrow to the host Skill\n\t// for the formatter (D-075 S2 move).\n\tconst skills = (providedSkills as Skill[] | undefined) ?? [];\n\tconst tools = selectedTools || [\"read\", \"bash\", \"edit\", \"write\"];\n\tconst skillFileReadTool = ([\"read\", \"bash\"] as const).find((tool) => tools.includes(tool));\n\n\tif (customPrompt) {\n\t\tlet prompt = customPrompt;\n\n\t\t// The orientation is a session-nature declaration (设计 §6.2): it rides\n\t\t// the customPrompt branch too, ahead of the appended extras. The\n\t\t// preference card follows it (H2: same M2 injection pattern).\n\t\tif (orientationSection) {\n\t\t\tprompt += orientationSection;\n\t\t}\n\t\tif (preferenceCardSection) {\n\t\t\tprompt += preferenceCardSection;\n\t\t}\n\n\t\tif (appendSection) {\n\t\t\tprompt += appendSection;\n\t\t}\n\t\tprompt += capabilityCardsSection;\n\n\t\t// Append project context files\n\t\tif (contextFiles.length > 0) {\n\t\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t\t}\n\t\t\tprompt += \"</project_context>\\n\";\n\t\t}\n\n\t\t// Append skills when a tool capable of reading their files is available.\n\t\tif (skillFileReadTool && skills.length > 0) {\n\t\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t\t}\n\n\t\tprompt += `\\nCurrent working directory: ${promptCwd}\\n`;\n\n\t\treturn prompt;\n\t}\n\n\t// Get absolute paths to documentation and examples\n\tconst readmePath = getReadmePath();\n\tconst docsPath = getDocsPath();\n\tconst examplesPath = getExamplesPath();\n\n\t// Build tools list based on selected tools.\n\t// A tool appears in Available tools only when the caller provides a one-line snippet.\n\tconst visibleTools = tools.filter((name) => !!toolSnippets?.[name]);\n\tconst toolsList =\n\t\tvisibleTools.length > 0 ? visibleTools.map((name) => `- ${name}: ${toolSnippets![name]}`).join(\"\\n\") : \"(none)\";\n\n\t// Read-only tool surface (提示词设计宪法 §6 2026-10-02): when no known\n\t// mutating tool is active (--tools narrowing, readonly embeds), the intro\n\t// must not promise file editing and a work rule reframes execution requests\n\t// as change-plan requests. New mutating capability tools must be added here.\n\tconst MUTATING_TOOL_NAMES = new Set([\n\t\t\"write\",\n\t\t\"edit\",\n\t\t\"bash\",\n\t\t\"powershell\",\n\t\t\"process_exec\",\n\t\t\"workspace_write\",\n\t\t\"test_run\",\n\t\t\"formatter_linter_check\",\n\t\t\"desktop_execute\",\n\t\t\"image_edit\",\n\t]);\n\tconst readOnlySession = !tools.some((name) => MUTATING_TOOL_NAMES.has(name));\n\tconst introScope = readOnlySession\n\t\t? \"you complete the user's task in the current working directory by reading code and running non-mutating commands. This session has no tools that modify files or run state-changing commands, so you cannot make changes here\"\n\t\t: \"you complete the user's task in the current working directory by reading code, running commands, and editing or creating files\";\n\tconst readOnlyWorkRule = readOnlySession\n\t\t? \"\\n- Treat execution requests as requests for a change plan: this session cannot make changes, so when the task needs them, delivering the exact plan — which files, what edits, how to verify — completes the task; do not attempt workarounds for the missing tools.\"\n\t\t: \"\";\n\n\t// Build guidelines based on which tools are actually available\n\tconst guidelinesList: string[] = [];\n\tconst guidelinesSet = new Set<string>();\n\tconst addGuideline = (guideline: string): void => {\n\t\tif (guidelinesSet.has(guideline)) {\n\t\t\treturn;\n\t\t}\n\t\tguidelinesSet.add(guideline);\n\t\tguidelinesList.push(guideline);\n\t};\n\n\tconst hasBash = tools.includes(\"bash\");\n\tconst hasPowerShell = tools.includes(\"powershell\");\n\tconst hasGrep = tools.includes(\"grep\");\n\tconst hasFind = tools.includes(\"find\");\n\tconst hasLs = tools.includes(\"ls\");\n\n\t// File exploration guidelines\n\tif ((hasBash || hasPowerShell) && !hasGrep && !hasFind && !hasLs) {\n\t\tif (hasBash && hasPowerShell) {\n\t\t\taddGuideline(\"Use bash or PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else if (hasPowerShell) {\n\t\t\taddGuideline(\"Use PowerShell for file operations like listing, searching, and finding files\");\n\t\t} else {\n\t\t\taddGuideline(\"Use bash for file operations like ls, rg, find\");\n\t\t}\n\t}\n\n\tfor (const guideline of promptGuidelines ?? []) {\n\t\tconst normalized = guideline.trim();\n\t\tif (normalized.length > 0) {\n\t\t\taddGuideline(normalized);\n\t\t}\n\t}\n\n\t// Always include this\n\taddGuideline(\"Show file paths clearly when working with files\");\n\n\tconst guidelines = guidelinesList.map((g) => `- ${g}`).join(\"\\n\");\n\n\tlet prompt = `You are Agent Forge, a coding agent: ${introScope}. Agent Forge is a minimal, extensible runtime: capabilities beyond the built-in tools come from installed plugins, skills, MCP servers, and CLI bridges — choose and compose them yourself when they serve the goal. The runtime defines only the readonly and full-control host modes; approval and safety policy belongs to the user's installed extensions, not to you.\n\nWork rules:\n- Work toward the stated outcome: start multi-step requests with a tool call, keep working through tools until the task is complete, and end with a summary of results — never with a plan or a promise of future work.\n- Make the smallest change that fully satisfies the task; add nothing unsolicited — no extra features, refactors, warnings, or disclaimers beyond the task itself. \"Smallest\" bounds what you add, never how deeply you fix: when the proper fix exceeds the task's scope or risk, surface the trade-off and your recommendation instead of silently downgrading to a symptom patch; when no in-scope fix exists, delivering that decision-ready trade-off completes the task.\n- Report outcomes faithfully: failures with their output, skipped steps as skipped, finished and verified work as finished.\n- Lead replies with the outcome and keep only the evidence needed to assess it; drop filler.${readOnlyWorkRule}\n- The user's instructions and project context files take precedence over these defaults.${orientationSection}${preferenceCardSection}\n\nAvailable tools:\n${toolsList}\n\nIn addition to the tools above, you may have access to other custom tools depending on the project.\n\nGuidelines:\n${guidelines}\n\nAgent Forge documentation (read only when the user asks about Agent Forge itself, its SDK, extensions, themes, skills, or TUI):\n- Main documentation: ${readmePath}\n- Additional docs: ${docsPath}\n- Examples: ${examplesPath} (extensions, custom tools, SDK)\n- When reading Agent Forge docs or examples, resolve docs/... under Additional docs and examples/... under Examples, not the current working directory\n- When asked about: extensions (docs/extensions.md, examples/extensions/), themes (docs/themes.md), skills (docs/skills.md), prompt templates (docs/prompt-templates.md), TUI components (docs/tui.md), keybindings (docs/keybindings.md), SDK integrations (docs/sdk.md), custom providers (docs/custom-provider.md), adding models (docs/models.md), packages (docs/packages.md), environment variables (docs/environment-variables.md)\n- When working on Agent Forge topics, read the docs and examples, and follow .md cross-references before implementing\n- When working on Agent Forge topics, read the relevant .md files completely and follow links to related docs (e.g., tui.md for TUI API details)`;\n\n\tif (appendSection) {\n\t\tprompt += appendSection;\n\t}\n\tprompt += capabilityCardsSection;\n\n\t// Append project context files\n\tif (contextFiles.length > 0) {\n\t\tprompt += \"\\n\\n<project_context>\\n\\n\";\n\t\tprompt += \"Project-specific instructions and guidelines:\\n\\n\";\n\t\tfor (const { path: filePath, content } of contextFiles) {\n\t\t\tprompt += `<project_instructions path=\"${filePath}\">\\n${content}\\n</project_instructions>\\n\\n`;\n\t\t}\n\t\tprompt += \"</project_context>\\n\";\n\t}\n\n\t// Append skills when a tool capable of reading their files is available.\n\tif (skillFileReadTool && skills.length > 0) {\n\t\tprompt += formatSkillsForPrompt(skills, skillFileReadTool);\n\t}\n\n\tprompt += `\\nCurrent working directory: ${promptCwd}`;\n\n\treturn prompt;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability-mount.d.ts","sourceRoot":"","sources":["../../../src/core/tools/capability-mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,IAAI,SAAS,wBAAwB,EAAE,CAAC;IAChD,0DAA0D;IAC1D,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/C,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAAC;IACxC,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAChD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AAID,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;AAC7D,eAAO,MAAM,4BAA4B,uBAAuB,CAAC;AAEjE,QAAA,MAAM,eAAe;;EAInB,CAAC;AAEH,QAAA,MAAM,iBAAiB;;EAErB,CAAC;AAEH,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,mBAAmB,GACvB,SAAS,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,EAAE,cAAc,CAAC,OAAO,iBAAiB,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"capability-mount.d.ts","sourceRoot":"","sources":["../../../src/core/tools/capability-mount.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,EAAe,IAAI,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yEAAyE;IACzE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,IAAI,SAAS,wBAAwB,EAAE,CAAC;IAChD,0DAA0D;IAC1D,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC/C,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAAC;IACxC,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAChD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC1C;AAID,eAAO,MAAM,0BAA0B,qBAAqB,CAAC;AAC7D,eAAO,MAAM,4BAA4B,uBAAuB,CAAC;AAEjE,QAAA,MAAM,eAAe;;EAInB,CAAC;AAEH,QAAA,MAAM,iBAAiB;;EAErB,CAAC;AAEH,wBAAgB,0BAA0B,CACzC,IAAI,EAAE,mBAAmB,GACvB,SAAS,CAAC,cAAc,CAAC,OAAO,eAAe,CAAC,EAAE,cAAc,CAAC,OAAO,iBAAiB,CAAC,CAAC,CAgF7F","sourcesContent":["/**\n * capability_mount / capability_unmount: plugin-granular on-demand tool\n * loading (插件按需挂载与能力手册设计 §5.2).\n *\n * Manifests declaring `activation: \"on-demand\"` register their tools into the\n * registry but keep them out of the active set; the model sees a one-line\n * capability card in the system prompt instead. `capability_mount` pulls the\n * plugin's full tool set into the active surface (next request) and returns\n * the plugin's AI manual; `capability_unmount` removes it from the active\n * surface without touching any runtime state — session teardown remains the\n * primary cleanup path. Kernel mechanism only: no plugin-specific branches.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"./tool-definition.ts\";\n\nexport interface DeferredCapabilityCardV1 {\n\treadonly pluginId: string;\n\t/** One-line capability summary from the manifest `description` field. */\n\treadonly description?: string;\n\t/** Absolute path to the plugin's AI manual (manifest `manual`, resolved). */\n\treadonly manualPath?: string;\n}\n\nexport interface CapabilityMountDeps {\n\tgetCards(): readonly DeferredCapabilityCardV1[];\n\t/** Registry tool names whose source is this plugin id. */\n\ttoolNamesForPlugin(pluginId: string): string[];\n\tgetActiveToolNames(): readonly string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\treadManual(path: string): Promise<string>;\n}\n\nconst MANUAL_MAX_CHARS = 16_000;\n\nexport const CAPABILITY_MOUNT_TOOL_NAME = \"capability_mount\";\nexport const CAPABILITY_UNMOUNT_TOOL_NAME = \"capability_unmount\";\n\nconst mountParameters = Type.Object({\n\tplugin: Type.String({\n\t\tdescription: \"Plugin id from the mountable-capabilities card, e.g. agent-forge.plugin.browser-playwright\",\n\t}),\n});\n\nconst unmountParameters = Type.Object({\n\tplugin: Type.String({ description: \"Plugin id to unmount, e.g. agent-forge.plugin.browser-playwright\" }),\n});\n\nexport function createCapabilityMountTools(\n\tdeps: CapabilityMountDeps,\n): readonly [ToolDefinition<typeof mountParameters>, ToolDefinition<typeof unmountParameters>] {\n\tconst mountTool: ToolDefinition<typeof mountParameters> = {\n\t\tname: CAPABILITY_MOUNT_TOOL_NAME,\n\t\tlabel: \"Capability mount\",\n\t\tdescription:\n\t\t\t\"Load a deferred plugin capability on demand: activates all of its tools for subsequent turns and returns \" +\n\t\t\t\"the plugin's usage manual. Mountable capabilities are listed in the system prompt's mountable-capabilities section.\",\n\t\tpromptSnippet:\n\t\t\t\"Activate a deferred plugin capability (browser tools, desktop control, image generation, ...) when its one-line card in the mountable-capabilities section matches the task\",\n\t\tparameters: mountParameters,\n\t\texecute: async (_toolCallId, params: Static<typeof mountParameters>) => {\n\t\t\tconst pluginId = params.plugin.trim();\n\t\t\tconst card = deps.getCards().find((entry) => entry.pluginId === pluginId);\n\t\t\tif (card === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Unknown capability \"${pluginId}\". Mountable capabilities are listed in the system prompt's mountable-capabilities section.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst toolNames = deps.toolNamesForPlugin(pluginId);\n\t\t\tif (toolNames.length === 0) {\n\t\t\t\tthrow new Error(`Capability \"${pluginId}\" has no registered tools to activate.`);\n\t\t\t}\n\t\t\tconst active = new Set(deps.getActiveToolNames());\n\t\t\tconst missing = toolNames.filter((name) => !active.has(name));\n\t\t\tif (missing.length > 0) {\n\t\t\t\tdeps.setActiveToolsByName([...deps.getActiveToolNames(), ...toolNames]);\n\t\t\t}\n\t\t\tconst manual =\n\t\t\t\tcard.manualPath === undefined ? undefined : await deps.readManual(card.manualPath).catch(() => undefined);\n\t\t\tconst activationLine =\n\t\t\t\tmissing.length === 0\n\t\t\t\t\t? `Capability \"${pluginId}\" is already active (${toolNames.length} tool(s)).`\n\t\t\t\t\t: `Activated ${missing.length} tool(s) for \"${pluginId}\": ${toolNames.join(\", \")}. They are available from the next turn.`;\n\t\t\tconst manualBlock =\n\t\t\t\tmanual === undefined\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: `\\n\\n---\\n\\n${manual.slice(0, MANUAL_MAX_CHARS)}${manual.length > MANUAL_MAX_CHARS ? \"\\n\\n(manual truncated)\" : \"\"}`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `${activationLine}${manualBlock}` }],\n\t\t\t\tdetails: { activated: missing.length > 0 },\n\t\t\t};\n\t\t},\n\t};\n\n\tconst unmountTool: ToolDefinition<typeof unmountParameters> = {\n\t\tname: CAPABILITY_UNMOUNT_TOOL_NAME,\n\t\tlabel: \"Capability unmount\",\n\t\tdescription:\n\t\t\t\"Remove a mounted plugin capability's tools from the active surface when the task that needed them is done. \" +\n\t\t\t\"Runtime state (e.g. an open browser session) is not torn down; it follows the plugin's own idle timeout. \" +\n\t\t\t\"Session teardown clears all mounts anyway, so unmounting is an optional long-session optimization.\",\n\t\tpromptSnippet:\n\t\t\t\"Deactivate a mounted plugin capability after finishing the task that needed it (optional; sessions clear mounts on teardown)\",\n\t\tparameters: unmountParameters,\n\t\texecute: async (_toolCallId, params: Static<typeof unmountParameters>) => {\n\t\t\tconst pluginId = params.plugin.trim();\n\t\t\tconst card = deps.getCards().find((entry) => entry.pluginId === pluginId);\n\t\t\tif (card === undefined) {\n\t\t\t\tthrow new Error(`Unknown capability \"${pluginId}\".`);\n\t\t\t}\n\t\t\tconst toolNames = deps.toolNamesForPlugin(pluginId);\n\t\t\tconst active = new Set(deps.getActiveToolNames());\n\t\t\tconst mountedNames = toolNames.filter((name) => active.has(name));\n\t\t\tif (mountedNames.length === 0) {\n\t\t\t\tthrow new Error(`Capability \"${pluginId}\" is not mounted.`);\n\t\t\t}\n\t\t\tdeps.setActiveToolsByName(deps.getActiveToolNames().filter((name) => !mountedNames.includes(name)));\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Unmounted \"${pluginId}\": removed ${mountedNames.length} tool(s) from the active surface. Runtime state was not touched.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { unmounted: true },\n\t\t\t};\n\t\t},\n\t};\n\n\treturn [mountTool, unmountTool] as const;\n}\n"]}
|
|
@@ -34,22 +34,11 @@ export function createCapabilityMountTools(deps) {
|
|
|
34
34
|
const pluginId = params.plugin.trim();
|
|
35
35
|
const card = deps.getCards().find((entry) => entry.pluginId === pluginId);
|
|
36
36
|
if (card === undefined) {
|
|
37
|
-
|
|
38
|
-
content: [
|
|
39
|
-
{
|
|
40
|
-
type: "text",
|
|
41
|
-
text: `Unknown capability "${pluginId}". Mountable capabilities are listed in the system prompt's mountable-capabilities section.`,
|
|
42
|
-
},
|
|
43
|
-
],
|
|
44
|
-
details: { activated: false },
|
|
45
|
-
};
|
|
37
|
+
throw new Error(`Unknown capability "${pluginId}". Mountable capabilities are listed in the system prompt's mountable-capabilities section.`);
|
|
46
38
|
}
|
|
47
39
|
const toolNames = deps.toolNamesForPlugin(pluginId);
|
|
48
40
|
if (toolNames.length === 0) {
|
|
49
|
-
|
|
50
|
-
content: [{ type: "text", text: `Capability "${pluginId}" has no registered tools to activate.` }],
|
|
51
|
-
details: { activated: false },
|
|
52
|
-
};
|
|
41
|
+
throw new Error(`Capability "${pluginId}" has no registered tools to activate.`);
|
|
53
42
|
}
|
|
54
43
|
const active = new Set(deps.getActiveToolNames());
|
|
55
44
|
const missing = toolNames.filter((name) => !active.has(name));
|
|
@@ -81,19 +70,13 @@ export function createCapabilityMountTools(deps) {
|
|
|
81
70
|
const pluginId = params.plugin.trim();
|
|
82
71
|
const card = deps.getCards().find((entry) => entry.pluginId === pluginId);
|
|
83
72
|
if (card === undefined) {
|
|
84
|
-
|
|
85
|
-
content: [{ type: "text", text: `Unknown capability "${pluginId}".` }],
|
|
86
|
-
details: { unmounted: false },
|
|
87
|
-
};
|
|
73
|
+
throw new Error(`Unknown capability "${pluginId}".`);
|
|
88
74
|
}
|
|
89
75
|
const toolNames = deps.toolNamesForPlugin(pluginId);
|
|
90
76
|
const active = new Set(deps.getActiveToolNames());
|
|
91
77
|
const mountedNames = toolNames.filter((name) => active.has(name));
|
|
92
78
|
if (mountedNames.length === 0) {
|
|
93
|
-
|
|
94
|
-
content: [{ type: "text", text: `Capability "${pluginId}" is not mounted.` }],
|
|
95
|
-
details: { unmounted: false },
|
|
96
|
-
};
|
|
79
|
+
throw new Error(`Capability "${pluginId}" is not mounted.`);
|
|
97
80
|
}
|
|
98
81
|
deps.setActiveToolsByName(deps.getActiveToolNames().filter((name) => !mountedNames.includes(name)));
|
|
99
82
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capability-mount.js","sourceRoot":"","sources":["../../../src/core/tools/capability-mount.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * capability_mount / capability_unmount: plugin-granular on-demand tool\n * loading (插件按需挂载与能力手册设计 §5.2).\n *\n * Manifests declaring `activation: \"on-demand\"` register their tools into the\n * registry but keep them out of the active set; the model sees a one-line\n * capability card in the system prompt instead. `capability_mount` pulls the\n * plugin's full tool set into the active surface (next request) and returns\n * the plugin's AI manual; `capability_unmount` removes it from the active\n * surface without touching any runtime state — session teardown remains the\n * primary cleanup path. Kernel mechanism only: no plugin-specific branches.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"./tool-definition.ts\";\n\nexport interface DeferredCapabilityCardV1 {\n\treadonly pluginId: string;\n\t/** One-line capability summary from the manifest `description` field. */\n\treadonly description?: string;\n\t/** Absolute path to the plugin's AI manual (manifest `manual`, resolved). */\n\treadonly manualPath?: string;\n}\n\nexport interface CapabilityMountDeps {\n\tgetCards(): readonly DeferredCapabilityCardV1[];\n\t/** Registry tool names whose source is this plugin id. */\n\ttoolNamesForPlugin(pluginId: string): string[];\n\tgetActiveToolNames(): readonly string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\treadManual(path: string): Promise<string>;\n}\n\nconst MANUAL_MAX_CHARS = 16_000;\n\nexport const CAPABILITY_MOUNT_TOOL_NAME = \"capability_mount\";\nexport const CAPABILITY_UNMOUNT_TOOL_NAME = \"capability_unmount\";\n\nconst mountParameters = Type.Object({\n\tplugin: Type.String({\n\t\tdescription: \"Plugin id from the mountable-capabilities card, e.g. agent-forge.plugin.browser-playwright\",\n\t}),\n});\n\nconst unmountParameters = Type.Object({\n\tplugin: Type.String({ description: \"Plugin id to unmount, e.g. agent-forge.plugin.browser-playwright\" }),\n});\n\nexport function createCapabilityMountTools(\n\tdeps: CapabilityMountDeps,\n): readonly [ToolDefinition<typeof mountParameters>, ToolDefinition<typeof unmountParameters>] {\n\tconst mountTool: ToolDefinition<typeof mountParameters> = {\n\t\tname: CAPABILITY_MOUNT_TOOL_NAME,\n\t\tlabel: \"Capability mount\",\n\t\tdescription:\n\t\t\t\"Load a deferred plugin capability on demand: activates all of its tools for subsequent turns and returns \" +\n\t\t\t\"the plugin's usage manual. Mountable capabilities are listed in the system prompt's mountable-capabilities section.\",\n\t\tpromptSnippet:\n\t\t\t\"Activate a deferred plugin capability (browser tools, desktop control, image generation, ...) when its one-line card in the mountable-capabilities section matches the task\",\n\t\tparameters: mountParameters,\n\t\texecute: async (_toolCallId, params: Static<typeof mountParameters>) => {\n\t\t\tconst pluginId = params.plugin.trim();\n\t\t\tconst card = deps.getCards().find((entry) => entry.pluginId === pluginId);\n\t\t\tif (card === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\t\ttext: `Unknown capability \"${pluginId}\". Mountable capabilities are listed in the system prompt's mountable-capabilities section.`,\n\t\t\t\t\t\t},\n\t\t\t\t\t],\n\t\t\t\t\tdetails: { activated: false },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst toolNames = deps.toolNamesForPlugin(pluginId);\n\t\t\tif (toolNames.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Capability \"${pluginId}\" has no registered tools to activate.` }],\n\t\t\t\t\tdetails: { activated: false },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst active = new Set(deps.getActiveToolNames());\n\t\t\tconst missing = toolNames.filter((name) => !active.has(name));\n\t\t\tif (missing.length > 0) {\n\t\t\t\tdeps.setActiveToolsByName([...deps.getActiveToolNames(), ...toolNames]);\n\t\t\t}\n\t\t\tconst manual =\n\t\t\t\tcard.manualPath === undefined ? undefined : await deps.readManual(card.manualPath).catch(() => undefined);\n\t\t\tconst activationLine =\n\t\t\t\tmissing.length === 0\n\t\t\t\t\t? `Capability \"${pluginId}\" is already active (${toolNames.length} tool(s)).`\n\t\t\t\t\t: `Activated ${missing.length} tool(s) for \"${pluginId}\": ${toolNames.join(\", \")}. They are available from the next turn.`;\n\t\t\tconst manualBlock =\n\t\t\t\tmanual === undefined\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: `\\n\\n---\\n\\n${manual.slice(0, MANUAL_MAX_CHARS)}${manual.length > MANUAL_MAX_CHARS ? \"\\n\\n(manual truncated)\" : \"\"}`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `${activationLine}${manualBlock}` }],\n\t\t\t\tdetails: { activated: missing.length > 0 },\n\t\t\t};\n\t\t},\n\t};\n\n\tconst unmountTool: ToolDefinition<typeof unmountParameters> = {\n\t\tname: CAPABILITY_UNMOUNT_TOOL_NAME,\n\t\tlabel: \"Capability unmount\",\n\t\tdescription:\n\t\t\t\"Remove a mounted plugin capability's tools from the active surface when the task that needed them is done. \" +\n\t\t\t\"Runtime state (e.g. an open browser session) is not torn down; it follows the plugin's own idle timeout. \" +\n\t\t\t\"Session teardown clears all mounts anyway, so unmounting is an optional long-session optimization.\",\n\t\tpromptSnippet:\n\t\t\t\"Deactivate a mounted plugin capability after finishing the task that needed it (optional; sessions clear mounts on teardown)\",\n\t\tparameters: unmountParameters,\n\t\texecute: async (_toolCallId, params: Static<typeof unmountParameters>) => {\n\t\t\tconst pluginId = params.plugin.trim();\n\t\t\tconst card = deps.getCards().find((entry) => entry.pluginId === pluginId);\n\t\t\tif (card === undefined) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Unknown capability \"${pluginId}\".` }],\n\t\t\t\t\tdetails: { unmounted: false },\n\t\t\t\t};\n\t\t\t}\n\t\t\tconst toolNames = deps.toolNamesForPlugin(pluginId);\n\t\t\tconst active = new Set(deps.getActiveToolNames());\n\t\t\tconst mountedNames = toolNames.filter((name) => active.has(name));\n\t\t\tif (mountedNames.length === 0) {\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: \"text\", text: `Capability \"${pluginId}\" is not mounted.` }],\n\t\t\t\t\tdetails: { unmounted: false },\n\t\t\t\t};\n\t\t\t}\n\t\t\tdeps.setActiveToolsByName(deps.getActiveToolNames().filter((name) => !mountedNames.includes(name)));\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Unmounted \"${pluginId}\": removed ${mountedNames.length} tool(s) from the active surface. Runtime state was not touched.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { unmounted: true },\n\t\t\t};\n\t\t},\n\t};\n\n\treturn [mountTool, unmountTool] as const;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"capability-mount.js","sourceRoot":"","sources":["../../../src/core/tools/capability-mount.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * capability_mount / capability_unmount: plugin-granular on-demand tool\n * loading (插件按需挂载与能力手册设计 §5.2).\n *\n * Manifests declaring `activation: \"on-demand\"` register their tools into the\n * registry but keep them out of the active set; the model sees a one-line\n * capability card in the system prompt instead. `capability_mount` pulls the\n * plugin's full tool set into the active surface (next request) and returns\n * the plugin's AI manual; `capability_unmount` removes it from the active\n * surface without touching any runtime state — session teardown remains the\n * primary cleanup path. Kernel mechanism only: no plugin-specific branches.\n */\n\nimport { type Static, Type } from \"typebox\";\nimport type { ToolDefinition } from \"./tool-definition.ts\";\n\nexport interface DeferredCapabilityCardV1 {\n\treadonly pluginId: string;\n\t/** One-line capability summary from the manifest `description` field. */\n\treadonly description?: string;\n\t/** Absolute path to the plugin's AI manual (manifest `manual`, resolved). */\n\treadonly manualPath?: string;\n}\n\nexport interface CapabilityMountDeps {\n\tgetCards(): readonly DeferredCapabilityCardV1[];\n\t/** Registry tool names whose source is this plugin id. */\n\ttoolNamesForPlugin(pluginId: string): string[];\n\tgetActiveToolNames(): readonly string[];\n\tsetActiveToolsByName(toolNames: string[]): void;\n\treadManual(path: string): Promise<string>;\n}\n\nconst MANUAL_MAX_CHARS = 16_000;\n\nexport const CAPABILITY_MOUNT_TOOL_NAME = \"capability_mount\";\nexport const CAPABILITY_UNMOUNT_TOOL_NAME = \"capability_unmount\";\n\nconst mountParameters = Type.Object({\n\tplugin: Type.String({\n\t\tdescription: \"Plugin id from the mountable-capabilities card, e.g. agent-forge.plugin.browser-playwright\",\n\t}),\n});\n\nconst unmountParameters = Type.Object({\n\tplugin: Type.String({ description: \"Plugin id to unmount, e.g. agent-forge.plugin.browser-playwright\" }),\n});\n\nexport function createCapabilityMountTools(\n\tdeps: CapabilityMountDeps,\n): readonly [ToolDefinition<typeof mountParameters>, ToolDefinition<typeof unmountParameters>] {\n\tconst mountTool: ToolDefinition<typeof mountParameters> = {\n\t\tname: CAPABILITY_MOUNT_TOOL_NAME,\n\t\tlabel: \"Capability mount\",\n\t\tdescription:\n\t\t\t\"Load a deferred plugin capability on demand: activates all of its tools for subsequent turns and returns \" +\n\t\t\t\"the plugin's usage manual. Mountable capabilities are listed in the system prompt's mountable-capabilities section.\",\n\t\tpromptSnippet:\n\t\t\t\"Activate a deferred plugin capability (browser tools, desktop control, image generation, ...) when its one-line card in the mountable-capabilities section matches the task\",\n\t\tparameters: mountParameters,\n\t\texecute: async (_toolCallId, params: Static<typeof mountParameters>) => {\n\t\t\tconst pluginId = params.plugin.trim();\n\t\t\tconst card = deps.getCards().find((entry) => entry.pluginId === pluginId);\n\t\t\tif (card === undefined) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t`Unknown capability \"${pluginId}\". Mountable capabilities are listed in the system prompt's mountable-capabilities section.`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tconst toolNames = deps.toolNamesForPlugin(pluginId);\n\t\t\tif (toolNames.length === 0) {\n\t\t\t\tthrow new Error(`Capability \"${pluginId}\" has no registered tools to activate.`);\n\t\t\t}\n\t\t\tconst active = new Set(deps.getActiveToolNames());\n\t\t\tconst missing = toolNames.filter((name) => !active.has(name));\n\t\t\tif (missing.length > 0) {\n\t\t\t\tdeps.setActiveToolsByName([...deps.getActiveToolNames(), ...toolNames]);\n\t\t\t}\n\t\t\tconst manual =\n\t\t\t\tcard.manualPath === undefined ? undefined : await deps.readManual(card.manualPath).catch(() => undefined);\n\t\t\tconst activationLine =\n\t\t\t\tmissing.length === 0\n\t\t\t\t\t? `Capability \"${pluginId}\" is already active (${toolNames.length} tool(s)).`\n\t\t\t\t\t: `Activated ${missing.length} tool(s) for \"${pluginId}\": ${toolNames.join(\", \")}. They are available from the next turn.`;\n\t\t\tconst manualBlock =\n\t\t\t\tmanual === undefined\n\t\t\t\t\t? \"\"\n\t\t\t\t\t: `\\n\\n---\\n\\n${manual.slice(0, MANUAL_MAX_CHARS)}${manual.length > MANUAL_MAX_CHARS ? \"\\n\\n(manual truncated)\" : \"\"}`;\n\t\t\treturn {\n\t\t\t\tcontent: [{ type: \"text\", text: `${activationLine}${manualBlock}` }],\n\t\t\t\tdetails: { activated: missing.length > 0 },\n\t\t\t};\n\t\t},\n\t};\n\n\tconst unmountTool: ToolDefinition<typeof unmountParameters> = {\n\t\tname: CAPABILITY_UNMOUNT_TOOL_NAME,\n\t\tlabel: \"Capability unmount\",\n\t\tdescription:\n\t\t\t\"Remove a mounted plugin capability's tools from the active surface when the task that needed them is done. \" +\n\t\t\t\"Runtime state (e.g. an open browser session) is not torn down; it follows the plugin's own idle timeout. \" +\n\t\t\t\"Session teardown clears all mounts anyway, so unmounting is an optional long-session optimization.\",\n\t\tpromptSnippet:\n\t\t\t\"Deactivate a mounted plugin capability after finishing the task that needed it (optional; sessions clear mounts on teardown)\",\n\t\tparameters: unmountParameters,\n\t\texecute: async (_toolCallId, params: Static<typeof unmountParameters>) => {\n\t\t\tconst pluginId = params.plugin.trim();\n\t\t\tconst card = deps.getCards().find((entry) => entry.pluginId === pluginId);\n\t\t\tif (card === undefined) {\n\t\t\t\tthrow new Error(`Unknown capability \"${pluginId}\".`);\n\t\t\t}\n\t\t\tconst toolNames = deps.toolNamesForPlugin(pluginId);\n\t\t\tconst active = new Set(deps.getActiveToolNames());\n\t\t\tconst mountedNames = toolNames.filter((name) => active.has(name));\n\t\t\tif (mountedNames.length === 0) {\n\t\t\t\tthrow new Error(`Capability \"${pluginId}\" is not mounted.`);\n\t\t\t}\n\t\t\tdeps.setActiveToolsByName(deps.getActiveToolNames().filter((name) => !mountedNames.includes(name)));\n\t\t\treturn {\n\t\t\t\tcontent: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"text\",\n\t\t\t\t\t\ttext: `Unmounted \"${pluginId}\": removed ${mountedNames.length} tool(s) from the active surface. Runtime state was not touched.`,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdetails: { unmounted: true },\n\t\t\t};\n\t\t},\n\t};\n\n\treturn [mountTool, unmountTool] as const;\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAkB,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,kBAAkB,CAAC;AAIzG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAmB7D;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAgB;IAKrC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAI5B,OAAO,CAAC,gBAAgB,CAAS;IAIjC,OAAO,CAAC,eAAe,CAAC,CAAwD;IAEhF,YACC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,mBAAmB,SAAmB,EACtC,SAAS,SAAI,EACb,oBAAoB,GAAE,SAAS,mBAAmB,EAAO,EAiBzD;IAEQ,UAAU,IAAI,IAAI,CAM1B;IAED,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAMxC;IAED,oEAAoE;IACpE,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CASnC;IAED,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM1C;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMlC;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CASvC;IAED,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,UAAmB,GAAG,IAAI,CAwC7E;IAED,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,oBAAoB;CAoF5B","sourcesContent":["import type { AssistantMessage } from \"@agent-forge/ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport { getMarkdownTheme, theme } from \"../../../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\nimport type { MarkdownTransformer } from \"./render-types.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * One rendered unit of an assistant message in emission order. updateContent\n * diffs consecutive spec lists by kind (structure) and text so unchanged\n * prefix segments reuse their component instances across streaming deltas;\n * a component's render output depends only on (kind, text) plus the fixed\n * padding/theme/transformers, which keeps incremental output identical to a\n * full rebuild.\n */\ninterface ContentSegmentSpec {\n\tkind: \"spacer\" | \"text\" | \"thinking\" | \"thinking-summary\" | \"status\";\n\ttext: string;\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\t// D-083: the finished-thinking render no longer reads this label (the\n\t// summary line is the i18n key \"✻ thinking\"); the field and its setter\n\t// stay for the constructor signature callers pass positionally.\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: kept write-only for the preserved constructor/setter surface (D-083)\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\t// D-083 quiet transcript: thinking collapses to one dim summary line once\n\t// the turn ends; detail mode (setExpanded(true), driven by the ctrl+o\n\t// setToolsExpanded loop) renders the full thinking text again.\n\tprivate thinkingExpanded = false;\n\t// Segment specs of the last updateContent build. Undefined forces a full\n\t// rebuild (first build, setters, invalidate); otherwise the diff reuses\n\t// unchanged prefix components and rebuilds only the changed tail.\n\tprivate contentSegments?: { specs: ContentSegmentSpec[]; isStreaming: boolean };\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = t(\"Thinking...\"),\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\t/** Expandable face for the ctrl+o setToolsExpanded loop (D-083). */\n\tsetExpanded(expanded: boolean): void {\n\t\tif (expanded === this.thinkingExpanded) {\n\t\t\treturn;\n\t\t}\n\t\tthis.thinkingExpanded = expanded;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\n\t\tconst specs = this.buildContentSegments(message, isStreaming);\n\n\t\t// Diff against the previous build: reuse component instances for the\n\t\t// structurally unchanged prefix and update only changed text in place,\n\t\t// so the per-delta cost scales with the changed tail instead of the\n\t\t// whole message. An isStreaming flip changes the transform captured by\n\t\t// every Markdown, so it rebuilds all segments.\n\t\tconst previous = this.contentSegments;\n\t\tlet reuseCount = 0;\n\t\tif (previous && previous.isStreaming === isStreaming) {\n\t\t\tconst shared = Math.min(previous.specs.length, specs.length);\n\t\t\twhile (reuseCount < shared && previous.specs[reuseCount].kind === specs[reuseCount].kind) {\n\t\t\t\treuseCount++;\n\t\t\t}\n\t\t\tfor (let i = 0; i < reuseCount; i++) {\n\t\t\t\tif (specs[i].text === previous.specs[i].text) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst component = this.contentContainer.children[i];\n\t\t\t\tif (component instanceof Markdown || component instanceof Text) {\n\t\t\t\t\tcomponent.setText(specs[i].text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reused = this.contentContainer.children.slice(0, reuseCount);\n\t\tthis.contentContainer.clear();\n\t\tfor (const component of reused) {\n\t\t\tthis.contentContainer.addChild(component);\n\t\t}\n\t\tfor (let i = reuseCount; i < specs.length; i++) {\n\t\t\tthis.contentContainer.addChild(this.createSegmentComponent(specs[i], isStreaming));\n\t\t}\n\t\tthis.contentSegments = { specs, isStreaming };\n\n\t\tthis.hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t}\n\n\tprivate createSegmentComponent(spec: ContentSegmentSpec, isStreaming: boolean): Component {\n\t\tswitch (spec.kind) {\n\t\t\tcase \"spacer\":\n\t\t\t\treturn new Spacer(1);\n\t\t\tcase \"text\":\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\treturn new Markdown(spec.text, this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", isStreaming, this.markdownTransformers),\n\t\t\t\t});\n\t\t\tcase \"thinking\":\n\t\t\t\treturn new Markdown(\n\t\t\t\t\tspec.text,\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\tcase \"thinking-summary\":\n\t\t\tcase \"status\":\n\t\t\t\treturn new Text(spec.text, this.outputPad, 0);\n\t\t}\n\t}\n\n\tprivate buildContentSegments(message: AssistantMessage, isStreaming: boolean): ContentSegmentSpec[] {\n\t\tconst specs: ContentSegmentSpec[] = [];\n\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\t\tif (hasVisibleContent) {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t}\n\n\t\t// Render content in order\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tspecs.push({ kind: \"text\", text: content.text.trim() });\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tthinkingBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (thinkingBlocks.length === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\t// This avoids a superfluous blank line before separately-rendered tool execution blocks.\n\t\t\t\tconst hasVisibleContentAfter = message.content\n\t\t\t\t\t.slice(i + 1)\n\t\t\t\t\t// v8 ignore next -- thinking runs consume all consecutive thinking blocks, none remain after the run\n\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()));\n\n\t\t\t\t// D-083 quiet transcript: while streaming, thinking stays fully\n\t\t\t\t// visible (process transparency, regardless of hideThinkingBlock).\n\t\t\t\t// After the turn ends: hideThinkingBlock renders nothing at all,\n\t\t\t\t// detail mode (thinkingExpanded) restores the full text, and the\n\t\t\t\t// quiet default collapses to one dim summary line.\n\t\t\t\tif (isStreaming || (!this.hideThinkingBlock && this.thinkingExpanded)) {\n\t\t\t\t\t// Render each run of thinking blocks as one Markdown section.\n\t\t\t\t\tspecs.push({ kind: \"thinking\", text: thinkingBlocks.join(\"\\n\\n\") });\n\t\t\t\t} else if (!this.hideThinkingBlock) {\n\t\t\t\t\tspecs.push({\n\t\t\t\t\t\tkind: \"thinking-summary\",\n\t\t\t\t\t\ttext: theme.italic(theme.fg(\"thinkingText\", t(\"✻ thinking\"))),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tif (message.stopReason === \"length\") {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Response was truncated before completion.\")) });\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: t(\"Operation aborted\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", abortMessage) });\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || t(\"Unknown error\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Error: {message}\", { message: errorMsg })) });\n\t\t\t}\n\t\t}\n\n\t\treturn specs;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-message.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAkB,SAAS,EAAY,KAAK,aAAa,EAAgB,MAAM,kBAAkB,CAAC;AAIzG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAmB7D;;GAEG;AACH,qBAAa,yBAA0B,SAAQ,SAAS;IACvD,OAAO,CAAC,gBAAgB,CAAY;IACpC,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,aAAa,CAAgB;IAKrC,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,oBAAoB,CAAiC;IAC7D,OAAO,CAAC,WAAW,CAAC,CAAmB;IACvC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAI5B,OAAO,CAAC,gBAAgB,CAAS;IAIjC,OAAO,CAAC,eAAe,CAAC,CAAwD;IAEhF,YACC,OAAO,CAAC,EAAE,gBAAgB,EAC1B,iBAAiB,UAAQ,EACzB,aAAa,GAAE,aAAkC,EACjD,mBAAmB,SAAmB,EACtC,SAAS,SAAI,EACb,oBAAoB,GAAE,SAAS,mBAAmB,EAAO,EAiBzD;IAEQ,UAAU,IAAI,IAAI,CAM1B;IAED,oBAAoB,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAMxC;IAED,oEAAoE;IACpE,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CASnC;IAED,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAM1C;IAED,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAMlC;IAEQ,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CASvC;IAED,aAAa,CAAC,OAAO,EAAE,gBAAgB,EAAE,WAAW,UAAmB,GAAG,IAAI,CAwC7E;IAED,OAAO,CAAC,sBAAsB;IA8B9B,OAAO,CAAC,oBAAoB;CAoF5B","sourcesContent":["import type { AssistantMessage } from \"@agent-forge/ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport { getMarkdownTheme, theme } from \"../../../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\nimport type { MarkdownTransformer } from \"./render-types.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * One rendered unit of an assistant message in emission order. updateContent\n * diffs consecutive spec lists by kind (structure) and text so unchanged\n * prefix segments reuse their component instances across streaming deltas;\n * a component's render output depends only on (kind, text) plus the fixed\n * padding/theme/transformers, which keeps incremental output identical to a\n * full rebuild.\n */\ninterface ContentSegmentSpec {\n\tkind: \"spacer\" | \"text\" | \"thinking\" | \"thinking-summary\" | \"status\";\n\ttext: string;\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\t// D-083: the finished-thinking render no longer reads this label (the\n\t// summary line is the i18n key \"※ thinking\"); the field and its setter\n\t// stay for the constructor signature callers pass positionally.\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: kept write-only for the preserved constructor/setter surface (D-083)\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\t// D-083 quiet transcript: thinking collapses to one dim summary line once\n\t// the turn ends; detail mode (setExpanded(true), driven by the ctrl+o\n\t// setToolsExpanded loop) renders the full thinking text again.\n\tprivate thinkingExpanded = false;\n\t// Segment specs of the last updateContent build. Undefined forces a full\n\t// rebuild (first build, setters, invalidate); otherwise the diff reuses\n\t// unchanged prefix components and rebuilds only the changed tail.\n\tprivate contentSegments?: { specs: ContentSegmentSpec[]; isStreaming: boolean };\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = t(\"Thinking...\"),\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\t/** Expandable face for the ctrl+o setToolsExpanded loop (D-083). */\n\tsetExpanded(expanded: boolean): void {\n\t\tif (expanded === this.thinkingExpanded) {\n\t\t\treturn;\n\t\t}\n\t\tthis.thinkingExpanded = expanded;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\n\t\tconst specs = this.buildContentSegments(message, isStreaming);\n\n\t\t// Diff against the previous build: reuse component instances for the\n\t\t// structurally unchanged prefix and update only changed text in place,\n\t\t// so the per-delta cost scales with the changed tail instead of the\n\t\t// whole message. An isStreaming flip changes the transform captured by\n\t\t// every Markdown, so it rebuilds all segments.\n\t\tconst previous = this.contentSegments;\n\t\tlet reuseCount = 0;\n\t\tif (previous && previous.isStreaming === isStreaming) {\n\t\t\tconst shared = Math.min(previous.specs.length, specs.length);\n\t\t\twhile (reuseCount < shared && previous.specs[reuseCount].kind === specs[reuseCount].kind) {\n\t\t\t\treuseCount++;\n\t\t\t}\n\t\t\tfor (let i = 0; i < reuseCount; i++) {\n\t\t\t\tif (specs[i].text === previous.specs[i].text) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst component = this.contentContainer.children[i];\n\t\t\t\tif (component instanceof Markdown || component instanceof Text) {\n\t\t\t\t\tcomponent.setText(specs[i].text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reused = this.contentContainer.children.slice(0, reuseCount);\n\t\tthis.contentContainer.clear();\n\t\tfor (const component of reused) {\n\t\t\tthis.contentContainer.addChild(component);\n\t\t}\n\t\tfor (let i = reuseCount; i < specs.length; i++) {\n\t\t\tthis.contentContainer.addChild(this.createSegmentComponent(specs[i], isStreaming));\n\t\t}\n\t\tthis.contentSegments = { specs, isStreaming };\n\n\t\tthis.hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t}\n\n\tprivate createSegmentComponent(spec: ContentSegmentSpec, isStreaming: boolean): Component {\n\t\tswitch (spec.kind) {\n\t\t\tcase \"spacer\":\n\t\t\t\treturn new Spacer(1);\n\t\t\tcase \"text\":\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\treturn new Markdown(spec.text, this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", isStreaming, this.markdownTransformers),\n\t\t\t\t});\n\t\t\tcase \"thinking\":\n\t\t\t\treturn new Markdown(\n\t\t\t\t\tspec.text,\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\tcase \"thinking-summary\":\n\t\t\tcase \"status\":\n\t\t\t\treturn new Text(spec.text, this.outputPad, 0);\n\t\t}\n\t}\n\n\tprivate buildContentSegments(message: AssistantMessage, isStreaming: boolean): ContentSegmentSpec[] {\n\t\tconst specs: ContentSegmentSpec[] = [];\n\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\t\tif (hasVisibleContent) {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t}\n\n\t\t// Render content in order\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tspecs.push({ kind: \"text\", text: content.text.trim() });\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tthinkingBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (thinkingBlocks.length === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\t// This avoids a superfluous blank line before separately-rendered tool execution blocks.\n\t\t\t\tconst hasVisibleContentAfter = message.content\n\t\t\t\t\t.slice(i + 1)\n\t\t\t\t\t// v8 ignore next -- thinking runs consume all consecutive thinking blocks, none remain after the run\n\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()));\n\n\t\t\t\t// D-083 quiet transcript: while streaming, thinking stays fully\n\t\t\t\t// visible (process transparency, regardless of hideThinkingBlock).\n\t\t\t\t// After the turn ends: hideThinkingBlock renders nothing at all,\n\t\t\t\t// detail mode (thinkingExpanded) restores the full text, and the\n\t\t\t\t// quiet default collapses to one dim summary line.\n\t\t\t\tif (isStreaming || (!this.hideThinkingBlock && this.thinkingExpanded)) {\n\t\t\t\t\t// Render each run of thinking blocks as one Markdown section.\n\t\t\t\t\tspecs.push({ kind: \"thinking\", text: thinkingBlocks.join(\"\\n\\n\") });\n\t\t\t\t} else if (!this.hideThinkingBlock) {\n\t\t\t\t\tspecs.push({\n\t\t\t\t\t\tkind: \"thinking-summary\",\n\t\t\t\t\t\ttext: theme.italic(theme.fg(\"thinkingText\", t(\"※ thinking\"))),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tif (message.stopReason === \"length\") {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Response was truncated before completion.\")) });\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: t(\"Operation aborted\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", abortMessage) });\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || t(\"Unknown error\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Error: {message}\", { message: errorMsg })) });\n\t\t\t}\n\t\t}\n\n\t\treturn specs;\n\t}\n}\n"]}
|
|
@@ -13,7 +13,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
13
13
|
hideThinkingBlock;
|
|
14
14
|
markdownTheme;
|
|
15
15
|
// D-083: the finished-thinking render no longer reads this label (the
|
|
16
|
-
// summary line is the i18n key "
|
|
16
|
+
// summary line is the i18n key "※ thinking"); the field and its setter
|
|
17
17
|
// stay for the constructor signature callers pass positionally.
|
|
18
18
|
// biome-ignore lint/correctness/noUnusedPrivateClassMembers: kept write-only for the preserved constructor/setter surface (D-083)
|
|
19
19
|
hiddenThinkingLabel;
|
|
@@ -197,7 +197,7 @@ export class AssistantMessageComponent extends Container {
|
|
|
197
197
|
else if (!this.hideThinkingBlock) {
|
|
198
198
|
specs.push({
|
|
199
199
|
kind: "thinking-summary",
|
|
200
|
-
text: theme.italic(theme.fg("thinkingText", t("
|
|
200
|
+
text: theme.italic(theme.fg("thinkingText", t("※ thinking"))),
|
|
201
201
|
});
|
|
202
202
|
}
|
|
203
203
|
if (hasVisibleContentAfter) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@agent-forge/ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport { getMarkdownTheme, theme } from \"../../../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\nimport type { MarkdownTransformer } from \"./render-types.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * One rendered unit of an assistant message in emission order. updateContent\n * diffs consecutive spec lists by kind (structure) and text so unchanged\n * prefix segments reuse their component instances across streaming deltas;\n * a component's render output depends only on (kind, text) plus the fixed\n * padding/theme/transformers, which keeps incremental output identical to a\n * full rebuild.\n */\ninterface ContentSegmentSpec {\n\tkind: \"spacer\" | \"text\" | \"thinking\" | \"thinking-summary\" | \"status\";\n\ttext: string;\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\t// D-083: the finished-thinking render no longer reads this label (the\n\t// summary line is the i18n key \"✻ thinking\"); the field and its setter\n\t// stay for the constructor signature callers pass positionally.\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: kept write-only for the preserved constructor/setter surface (D-083)\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\t// D-083 quiet transcript: thinking collapses to one dim summary line once\n\t// the turn ends; detail mode (setExpanded(true), driven by the ctrl+o\n\t// setToolsExpanded loop) renders the full thinking text again.\n\tprivate thinkingExpanded = false;\n\t// Segment specs of the last updateContent build. Undefined forces a full\n\t// rebuild (first build, setters, invalidate); otherwise the diff reuses\n\t// unchanged prefix components and rebuilds only the changed tail.\n\tprivate contentSegments?: { specs: ContentSegmentSpec[]; isStreaming: boolean };\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = t(\"Thinking...\"),\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\t/** Expandable face for the ctrl+o setToolsExpanded loop (D-083). */\n\tsetExpanded(expanded: boolean): void {\n\t\tif (expanded === this.thinkingExpanded) {\n\t\t\treturn;\n\t\t}\n\t\tthis.thinkingExpanded = expanded;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\n\t\tconst specs = this.buildContentSegments(message, isStreaming);\n\n\t\t// Diff against the previous build: reuse component instances for the\n\t\t// structurally unchanged prefix and update only changed text in place,\n\t\t// so the per-delta cost scales with the changed tail instead of the\n\t\t// whole message. An isStreaming flip changes the transform captured by\n\t\t// every Markdown, so it rebuilds all segments.\n\t\tconst previous = this.contentSegments;\n\t\tlet reuseCount = 0;\n\t\tif (previous && previous.isStreaming === isStreaming) {\n\t\t\tconst shared = Math.min(previous.specs.length, specs.length);\n\t\t\twhile (reuseCount < shared && previous.specs[reuseCount].kind === specs[reuseCount].kind) {\n\t\t\t\treuseCount++;\n\t\t\t}\n\t\t\tfor (let i = 0; i < reuseCount; i++) {\n\t\t\t\tif (specs[i].text === previous.specs[i].text) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst component = this.contentContainer.children[i];\n\t\t\t\tif (component instanceof Markdown || component instanceof Text) {\n\t\t\t\t\tcomponent.setText(specs[i].text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reused = this.contentContainer.children.slice(0, reuseCount);\n\t\tthis.contentContainer.clear();\n\t\tfor (const component of reused) {\n\t\t\tthis.contentContainer.addChild(component);\n\t\t}\n\t\tfor (let i = reuseCount; i < specs.length; i++) {\n\t\t\tthis.contentContainer.addChild(this.createSegmentComponent(specs[i], isStreaming));\n\t\t}\n\t\tthis.contentSegments = { specs, isStreaming };\n\n\t\tthis.hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t}\n\n\tprivate createSegmentComponent(spec: ContentSegmentSpec, isStreaming: boolean): Component {\n\t\tswitch (spec.kind) {\n\t\t\tcase \"spacer\":\n\t\t\t\treturn new Spacer(1);\n\t\t\tcase \"text\":\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\treturn new Markdown(spec.text, this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", isStreaming, this.markdownTransformers),\n\t\t\t\t});\n\t\t\tcase \"thinking\":\n\t\t\t\treturn new Markdown(\n\t\t\t\t\tspec.text,\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\tcase \"thinking-summary\":\n\t\t\tcase \"status\":\n\t\t\t\treturn new Text(spec.text, this.outputPad, 0);\n\t\t}\n\t}\n\n\tprivate buildContentSegments(message: AssistantMessage, isStreaming: boolean): ContentSegmentSpec[] {\n\t\tconst specs: ContentSegmentSpec[] = [];\n\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\t\tif (hasVisibleContent) {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t}\n\n\t\t// Render content in order\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tspecs.push({ kind: \"text\", text: content.text.trim() });\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tthinkingBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (thinkingBlocks.length === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\t// This avoids a superfluous blank line before separately-rendered tool execution blocks.\n\t\t\t\tconst hasVisibleContentAfter = message.content\n\t\t\t\t\t.slice(i + 1)\n\t\t\t\t\t// v8 ignore next -- thinking runs consume all consecutive thinking blocks, none remain after the run\n\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()));\n\n\t\t\t\t// D-083 quiet transcript: while streaming, thinking stays fully\n\t\t\t\t// visible (process transparency, regardless of hideThinkingBlock).\n\t\t\t\t// After the turn ends: hideThinkingBlock renders nothing at all,\n\t\t\t\t// detail mode (thinkingExpanded) restores the full text, and the\n\t\t\t\t// quiet default collapses to one dim summary line.\n\t\t\t\tif (isStreaming || (!this.hideThinkingBlock && this.thinkingExpanded)) {\n\t\t\t\t\t// Render each run of thinking blocks as one Markdown section.\n\t\t\t\t\tspecs.push({ kind: \"thinking\", text: thinkingBlocks.join(\"\\n\\n\") });\n\t\t\t\t} else if (!this.hideThinkingBlock) {\n\t\t\t\t\tspecs.push({\n\t\t\t\t\t\tkind: \"thinking-summary\",\n\t\t\t\t\t\ttext: theme.italic(theme.fg(\"thinkingText\", t(\"✻ thinking\"))),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tif (message.stopReason === \"length\") {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Response was truncated before completion.\")) });\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: t(\"Operation aborted\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", abortMessage) });\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || t(\"Unknown error\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Error: {message}\", { message: errorMsg })) });\n\t\t\t}\n\t\t}\n\n\t\treturn specs;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"assistant-message.js","sourceRoot":"","sources":["../../../../src/modes/interactive/components/assistant-message.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AssistantMessage } from \"@agent-forge/ai\";\nimport { type Component, Container, Markdown, type MarkdownTheme, Spacer, Text } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport { getMarkdownTheme, theme } from \"../../../theme/theme.ts\";\nimport { createMarkdownTransform } from \"./markdown-transform.ts\";\nimport type { MarkdownTransformer } from \"./render-types.ts\";\n\nconst OSC133_ZONE_START = \"\\x1b]133;A\\x07\";\nconst OSC133_ZONE_END = \"\\x1b]133;B\\x07\";\nconst OSC133_ZONE_FINAL = \"\\x1b]133;C\\x07\";\n\n/**\n * One rendered unit of an assistant message in emission order. updateContent\n * diffs consecutive spec lists by kind (structure) and text so unchanged\n * prefix segments reuse their component instances across streaming deltas;\n * a component's render output depends only on (kind, text) plus the fixed\n * padding/theme/transformers, which keeps incremental output identical to a\n * full rebuild.\n */\ninterface ContentSegmentSpec {\n\tkind: \"spacer\" | \"text\" | \"thinking\" | \"thinking-summary\" | \"status\";\n\ttext: string;\n}\n\n/**\n * Component that renders a complete assistant message\n */\nexport class AssistantMessageComponent extends Container {\n\tprivate contentContainer: Container;\n\tprivate hideThinkingBlock: boolean;\n\tprivate markdownTheme: MarkdownTheme;\n\t// D-083: the finished-thinking render no longer reads this label (the\n\t// summary line is the i18n key \"※ thinking\"); the field and its setter\n\t// stay for the constructor signature callers pass positionally.\n\t// biome-ignore lint/correctness/noUnusedPrivateClassMembers: kept write-only for the preserved constructor/setter surface (D-083)\n\tprivate hiddenThinkingLabel: string;\n\tprivate outputPad: number;\n\tprivate markdownTransformers: readonly MarkdownTransformer[];\n\tprivate lastMessage?: AssistantMessage;\n\tprivate hasToolCalls = false;\n\tprivate isStreaming = false;\n\t// D-083 quiet transcript: thinking collapses to one dim summary line once\n\t// the turn ends; detail mode (setExpanded(true), driven by the ctrl+o\n\t// setToolsExpanded loop) renders the full thinking text again.\n\tprivate thinkingExpanded = false;\n\t// Segment specs of the last updateContent build. Undefined forces a full\n\t// rebuild (first build, setters, invalidate); otherwise the diff reuses\n\t// unchanged prefix components and rebuilds only the changed tail.\n\tprivate contentSegments?: { specs: ContentSegmentSpec[]; isStreaming: boolean };\n\n\tconstructor(\n\t\tmessage?: AssistantMessage,\n\t\thideThinkingBlock = false,\n\t\tmarkdownTheme: MarkdownTheme = getMarkdownTheme(),\n\t\thiddenThinkingLabel = t(\"Thinking...\"),\n\t\toutputPad = 1,\n\t\tmarkdownTransformers: readonly MarkdownTransformer[] = [],\n\t) {\n\t\tsuper();\n\n\t\tthis.hideThinkingBlock = hideThinkingBlock;\n\t\tthis.markdownTheme = markdownTheme;\n\t\tthis.hiddenThinkingLabel = hiddenThinkingLabel;\n\t\tthis.outputPad = outputPad;\n\t\tthis.markdownTransformers = markdownTransformers;\n\n\t\t// Container for text/thinking content\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\tif (message) {\n\t\t\tthis.updateContent(message);\n\t\t}\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHideThinkingBlock(hide: boolean): void {\n\t\tthis.hideThinkingBlock = hide;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\t/** Expandable face for the ctrl+o setToolsExpanded loop (D-083). */\n\tsetExpanded(expanded: boolean): void {\n\t\tif (expanded === this.thinkingExpanded) {\n\t\t\treturn;\n\t\t}\n\t\tthis.thinkingExpanded = expanded;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetHiddenThinkingLabel(label: string): void {\n\t\tthis.hiddenThinkingLabel = label;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\tsetOutputPad(padding: number): void {\n\t\tthis.outputPad = padding;\n\t\tif (this.lastMessage) {\n\t\t\tthis.contentSegments = undefined;\n\t\t\tthis.updateContent(this.lastMessage);\n\t\t}\n\t}\n\n\toverride render(width: number): string[] {\n\t\tconst lines = super.render(width);\n\t\tif (this.hasToolCalls || lines.length === 0) {\n\t\t\treturn lines;\n\t\t}\n\n\t\tlines[0] = OSC133_ZONE_START + lines[0];\n\t\tlines[lines.length - 1] = OSC133_ZONE_END + OSC133_ZONE_FINAL + lines[lines.length - 1];\n\t\treturn lines;\n\t}\n\n\tupdateContent(message: AssistantMessage, isStreaming = this.isStreaming): void {\n\t\tthis.lastMessage = message;\n\t\tthis.isStreaming = isStreaming;\n\n\t\tconst specs = this.buildContentSegments(message, isStreaming);\n\n\t\t// Diff against the previous build: reuse component instances for the\n\t\t// structurally unchanged prefix and update only changed text in place,\n\t\t// so the per-delta cost scales with the changed tail instead of the\n\t\t// whole message. An isStreaming flip changes the transform captured by\n\t\t// every Markdown, so it rebuilds all segments.\n\t\tconst previous = this.contentSegments;\n\t\tlet reuseCount = 0;\n\t\tif (previous && previous.isStreaming === isStreaming) {\n\t\t\tconst shared = Math.min(previous.specs.length, specs.length);\n\t\t\twhile (reuseCount < shared && previous.specs[reuseCount].kind === specs[reuseCount].kind) {\n\t\t\t\treuseCount++;\n\t\t\t}\n\t\t\tfor (let i = 0; i < reuseCount; i++) {\n\t\t\t\tif (specs[i].text === previous.specs[i].text) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst component = this.contentContainer.children[i];\n\t\t\t\tif (component instanceof Markdown || component instanceof Text) {\n\t\t\t\t\tcomponent.setText(specs[i].text);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst reused = this.contentContainer.children.slice(0, reuseCount);\n\t\tthis.contentContainer.clear();\n\t\tfor (const component of reused) {\n\t\t\tthis.contentContainer.addChild(component);\n\t\t}\n\t\tfor (let i = reuseCount; i < specs.length; i++) {\n\t\t\tthis.contentContainer.addChild(this.createSegmentComponent(specs[i], isStreaming));\n\t\t}\n\t\tthis.contentSegments = { specs, isStreaming };\n\n\t\tthis.hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t}\n\n\tprivate createSegmentComponent(spec: ContentSegmentSpec, isStreaming: boolean): Component {\n\t\tswitch (spec.kind) {\n\t\t\tcase \"spacer\":\n\t\t\t\treturn new Spacer(1);\n\t\t\tcase \"text\":\n\t\t\t\t// Assistant text messages with no background - trim the text\n\t\t\t\t// Set paddingY=0 to avoid extra spacing before tool executions\n\t\t\t\treturn new Markdown(spec.text, this.outputPad, 0, this.markdownTheme, undefined, {\n\t\t\t\t\ttransform: createMarkdownTransform(\"assistant\", isStreaming, this.markdownTransformers),\n\t\t\t\t});\n\t\t\tcase \"thinking\":\n\t\t\t\treturn new Markdown(\n\t\t\t\t\tspec.text,\n\t\t\t\t\tthis.outputPad,\n\t\t\t\t\t0,\n\t\t\t\t\tthis.markdownTheme,\n\t\t\t\t\t{\n\t\t\t\t\t\tcolor: (text: string) => theme.fg(\"thinkingText\", text),\n\t\t\t\t\t\titalic: true,\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttransform: createMarkdownTransform(\"assistant-thinking\", isStreaming, this.markdownTransformers),\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\tcase \"thinking-summary\":\n\t\t\tcase \"status\":\n\t\t\t\treturn new Text(spec.text, this.outputPad, 0);\n\t\t}\n\t}\n\n\tprivate buildContentSegments(message: AssistantMessage, isStreaming: boolean): ContentSegmentSpec[] {\n\t\tconst specs: ContentSegmentSpec[] = [];\n\n\t\tconst hasVisibleContent = message.content.some(\n\t\t\t(c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()),\n\t\t);\n\t\tif (hasVisibleContent) {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t}\n\n\t\t// Render content in order\n\t\tfor (let i = 0; i < message.content.length; i++) {\n\t\t\tconst content = message.content[i];\n\t\t\tif (content.type === \"text\" && content.text.trim()) {\n\t\t\t\tspecs.push({ kind: \"text\", text: content.text.trim() });\n\t\t\t} else if (content.type === \"thinking\") {\n\t\t\t\tconst thinkingBlocks: string[] = [];\n\t\t\t\tfor (; i < message.content.length; i++) {\n\t\t\t\t\tconst thinkingContent = message.content[i];\n\t\t\t\t\tif (thinkingContent.type !== \"thinking\") {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tconst thinking = thinkingContent.thinking.trim();\n\t\t\t\t\tif (thinking) {\n\t\t\t\t\t\tthinkingBlocks.push(thinking);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti--;\n\n\t\t\t\tif (thinkingBlocks.length === 0) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t// Add spacing only when another visible assistant content block follows.\n\t\t\t\t// This avoids a superfluous blank line before separately-rendered tool execution blocks.\n\t\t\t\tconst hasVisibleContentAfter = message.content\n\t\t\t\t\t.slice(i + 1)\n\t\t\t\t\t// v8 ignore next -- thinking runs consume all consecutive thinking blocks, none remain after the run\n\t\t\t\t\t.some((c) => (c.type === \"text\" && c.text.trim()) || (c.type === \"thinking\" && c.thinking.trim()));\n\n\t\t\t\t// D-083 quiet transcript: while streaming, thinking stays fully\n\t\t\t\t// visible (process transparency, regardless of hideThinkingBlock).\n\t\t\t\t// After the turn ends: hideThinkingBlock renders nothing at all,\n\t\t\t\t// detail mode (thinkingExpanded) restores the full text, and the\n\t\t\t\t// quiet default collapses to one dim summary line.\n\t\t\t\tif (isStreaming || (!this.hideThinkingBlock && this.thinkingExpanded)) {\n\t\t\t\t\t// Render each run of thinking blocks as one Markdown section.\n\t\t\t\t\tspecs.push({ kind: \"thinking\", text: thinkingBlocks.join(\"\\n\\n\") });\n\t\t\t\t} else if (!this.hideThinkingBlock) {\n\t\t\t\t\tspecs.push({\n\t\t\t\t\t\tkind: \"thinking-summary\",\n\t\t\t\t\t\ttext: theme.italic(theme.fg(\"thinkingText\", t(\"※ thinking\"))),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tif (hasVisibleContentAfter) {\n\t\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Check if incomplete/failed - show after partial content.\n\t\t// For aborted/error tool calls, tool execution components show the error.\n\t\t// Length stops can happen before a tool call is complete, so surface them here too.\n\t\tconst hasToolCalls = message.content.some((c) => c.type === \"toolCall\");\n\t\tif (message.stopReason === \"length\") {\n\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Response was truncated before completion.\")) });\n\t\t} else if (!hasToolCalls) {\n\t\t\tif (message.stopReason === \"aborted\") {\n\t\t\t\tconst abortMessage =\n\t\t\t\t\tmessage.errorMessage && message.errorMessage !== \"Request was aborted\"\n\t\t\t\t\t\t? message.errorMessage\n\t\t\t\t\t\t: t(\"Operation aborted\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", abortMessage) });\n\t\t\t} else if (message.stopReason === \"error\") {\n\t\t\t\tconst errorMsg = message.errorMessage || t(\"Unknown error\");\n\t\t\t\tspecs.push({ kind: \"spacer\", text: \"\" });\n\t\t\t\tspecs.push({ kind: \"status\", text: theme.fg(\"error\", t(\"Error: {message}\", { message: errorMsg })) });\n\t\t\t}\n\t\t}\n\n\t\treturn specs;\n\t}\n}\n"]}
|
|
@@ -20,6 +20,8 @@ export declare class BashExecutionComponent extends Container {
|
|
|
20
20
|
setExpanded(expanded: boolean): void;
|
|
21
21
|
invalidate(): void;
|
|
22
22
|
appendOutput(chunk: string): void;
|
|
23
|
+
/** Keep only the tail within {@link MAX_RETAINED_OUTPUT_CHARS}, mirroring the executor's rolling buffer. */
|
|
24
|
+
private enforceRetention;
|
|
23
25
|
setComplete(exitCode: number | undefined, cancelled: boolean, truncationResult?: TruncationResult, fullOutputPath?: string): void;
|
|
24
26
|
private updateDisplay;
|
|
25
27
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/bash-execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAwB,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAGN,KAAK,gBAAgB,EAErB,MAAM,iCAAiC,CAAC;AAUzC,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,MAAM,CAA6D;IAC3E,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAY;IAEpC,YAAY,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,kBAAkB,UAAQ,EAiC/D;IAED;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAgBhC;IAED,WAAW,CACV,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,OAAO,EAClB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,cAAc,CAAC,EAAE,MAAM,GACrB,IAAI,CAcN;IAED,OAAO,CAAC,aAAa;IAgGrB;;OAEG;IACH,SAAS,IAAI,MAAM,CAElB;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,CAEnB;CACD","sourcesContent":["/**\n * Component for displaying bash command execution with streaming output.\n */\n\nimport { Container, Loader, Spacer, Text, type TUI } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\ttype TruncationResult,\n\ttruncateTail,\n} from \"../../../core/tools/truncate.ts\";\nimport { theme } from \"../../../theme/theme.ts\";\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"./visual-truncate.ts\";\n\n// Preview line limit when not expanded (matches tool execution behavior)\nconst PREVIEW_LINES = 20;\n\nexport class BashExecutionComponent extends Container {\n\tprivate command: string;\n\tprivate outputLines: string[] = [];\n\tprivate status: \"running\" | \"complete\" | \"cancelled\" | \"error\" = \"running\";\n\tprivate exitCode: number | undefined = undefined;\n\tprivate loader: Loader;\n\tprivate truncationResult?: TruncationResult;\n\tprivate fullOutputPath?: string;\n\tprivate expanded = false;\n\tprivate contentContainer: Container;\n\n\tconstructor(command: string, ui: TUI, excludeFromContext = false) {\n\t\tsuper();\n\t\tthis.command = command;\n\n\t\t// Use dim border for excluded-from-context commands (!! prefix)\n\t\tconst colorKey = excludeFromContext ? \"dim\" : \"bashMode\";\n\t\tconst borderColor = (str: string) => theme.fg(colorKey, str);\n\n\t\t// Add spacer\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\n\t\t// Content container (holds dynamic content between borders)\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(colorKey, theme.bold(`$ ${command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Loader\n\t\tthis.loader = new Loader(\n\t\t\tui,\n\t\t\t(spinner) => theme.fg(colorKey, spinner),\n\t\t\t(text) => theme.fg(\"muted\", text),\n\t\t\tt(\"Running... ({key} to cancel)\", { key: keyText(\"tui.select.cancel\") }), // Plain text for loader\n\t\t);\n\t\tthis.contentContainer.addChild(this.loader);\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\t}\n\n\t/**\n\t * Set whether the output is expanded (shows full output) or collapsed (preview only).\n\t */\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tappendOutput(chunk: string): void {\n\t\t// Strip ANSI codes and normalize line endings\n\t\t// Note: binary data is already sanitized in tui-renderer.ts executeBashCommand\n\t\tconst clean = stripAnsi(chunk).replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\t\t// Append to output lines\n\t\tconst newLines = clean.split(\"\\n\");\n\t\tif (this.outputLines.length > 0 && newLines.length > 0) {\n\t\t\t// Append first chunk to last line (incomplete line continuation)\n\t\t\tthis.outputLines[this.outputLines.length - 1] += newLines[0];\n\t\t\tthis.outputLines.push(...newLines.slice(1));\n\t\t} else {\n\t\t\tthis.outputLines.push(...newLines);\n\t\t}\n\n\t\tthis.updateDisplay();\n\t}\n\n\tsetComplete(\n\t\texitCode: number | undefined,\n\t\tcancelled: boolean,\n\t\ttruncationResult?: TruncationResult,\n\t\tfullOutputPath?: string,\n\t): void {\n\t\tthis.exitCode = exitCode;\n\t\tthis.status = cancelled\n\t\t\t? \"cancelled\"\n\t\t\t: exitCode !== 0 && exitCode !== undefined && exitCode !== null\n\t\t\t\t? \"error\"\n\t\t\t\t: \"complete\";\n\t\tthis.truncationResult = truncationResult;\n\t\tthis.fullOutputPath = fullOutputPath;\n\n\t\t// Stop loader\n\t\tthis.loader.stop();\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Apply truncation for LLM context limits (same limits as bash tool)\n\t\tconst fullOutput = this.outputLines.join(\"\\n\");\n\t\tconst contextTruncation = truncateTail(fullOutput, {\n\t\t\tmaxLines: DEFAULT_MAX_LINES,\n\t\t\tmaxBytes: DEFAULT_MAX_BYTES,\n\t\t});\n\n\t\t// Get the lines to potentially display (after context truncation)\n\t\tconst availableLines = contextTruncation.content ? contextTruncation.content.split(\"\\n\") : [];\n\n\t\t// Apply preview truncation based on expanded state\n\t\tconst previewLogicalLines = availableLines.slice(-PREVIEW_LINES);\n\t\tconst hiddenLineCount = availableLines.length - previewLogicalLines.length;\n\n\t\t// Rebuild content container\n\t\tthis.contentContainer.clear();\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(\"bashMode\", theme.bold(`$ ${this.command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Output\n\t\tif (availableLines.length > 0) {\n\t\t\tif (this.expanded) {\n\t\t\t\t// Show all lines\n\t\t\t\tconst displayText = availableLines.map((line) => theme.fg(\"muted\", line)).join(\"\\n\");\n\t\t\t\tthis.contentContainer.addChild(new Text(`\\n${displayText}`, 1, 0));\n\t\t\t} else {\n\t\t\t\t// Use shared visual truncation utility with width-aware caching\n\t\t\t\tconst styledOutput = previewLogicalLines.map((line) => theme.fg(\"muted\", line)).join(\"\\n\");\n\t\t\t\tconst styledInput = `\\n${styledOutput}`;\n\t\t\t\tlet cachedWidth: number | undefined;\n\t\t\t\tlet cachedLines: string[] | undefined;\n\t\t\t\tthis.contentContainer.addChild({\n\t\t\t\t\trender: (width: number) => {\n\t\t\t\t\t\tif (cachedLines === undefined || cachedWidth !== width) {\n\t\t\t\t\t\t\tconst result = truncateToVisualLines(styledInput, PREVIEW_LINES, width, 1);\n\t\t\t\t\t\t\tcachedLines = result.visualLines;\n\t\t\t\t\t\t\tcachedWidth = width;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// v8 ignore next -- cached lines are always computed before this render callback returns\n\t\t\t\t\t\treturn cachedLines ?? [];\n\t\t\t\t\t},\n\t\t\t\t\tinvalidate: () => {\n\t\t\t\t\t\tcachedWidth = undefined;\n\t\t\t\t\t\tcachedLines = undefined;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Loader or status\n\t\tif (this.status === \"running\") {\n\t\t\tthis.contentContainer.addChild(this.loader);\n\t\t} else {\n\t\t\tconst statusParts: string[] = [];\n\n\t\t\t// Show how many lines are hidden (collapsed preview)\n\t\t\tif (hiddenLineCount > 0) {\n\t\t\t\tif (this.expanded) {\n\t\t\t\t\t// i18n: 原三段样式拼接合并为整句单 key,键位片段的 dim 样式损失按批次④特殊裁决 1 接受\n\t\t\t\t\tstatusParts.push(theme.fg(\"muted\", t(\"({key} to collapse)\", { key: keyText(\"app.tools.expand\") })));\n\t\t\t\t} else {\n\t\t\t\t\tstatusParts.push(\n\t\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\t\"muted\",\n\t\t\t\t\t\t\tt(\"... {count} more lines ({key} to expand)\", {\n\t\t\t\t\t\t\t\tcount: hiddenLineCount,\n\t\t\t\t\t\t\t\tkey: keyText(\"app.tools.expand\"),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.status === \"cancelled\") {\n\t\t\t\tstatusParts.push(theme.fg(\"warning\", t(\"(cancelled)\")));\n\t\t\t} else if (this.status === \"error\") {\n\t\t\t\tstatusParts.push(theme.fg(\"error\", t(\"(exit {code})\", { code: this.exitCode ?? \"unknown\" })));\n\t\t\t}\n\n\t\t\t// Add truncation warning (context truncation, not preview truncation)\n\t\t\tconst wasTruncated = this.truncationResult?.truncated || contextTruncation.truncated;\n\t\t\tif (wasTruncated && this.fullOutputPath) {\n\t\t\t\tstatusParts.push(\n\t\t\t\t\ttheme.fg(\"warning\", t(\"Output truncated. Full output: {path}\", { path: this.fullOutputPath })),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (statusParts.length > 0) {\n\t\t\t\tthis.contentContainer.addChild(new Text(`\\n${statusParts.join(\"\\n\")}`, 1, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the raw output for creating BashExecutionMessage.\n\t */\n\tgetOutput(): string {\n\t\treturn this.outputLines.join(\"\\n\");\n\t}\n\n\t/**\n\t * Get the command that was executed.\n\t */\n\tgetCommand(): string {\n\t\treturn this.command;\n\t}\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bash-execution.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/bash-execution.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,SAAS,EAAwB,KAAK,GAAG,EAAE,MAAM,kBAAkB,CAAC;AAE7E,OAAO,EAGN,KAAK,gBAAgB,EAErB,MAAM,iCAAiC,CAAC;AAmBzC,qBAAa,sBAAuB,SAAQ,SAAS;IACpD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,WAAW,CAAgB;IACnC,OAAO,CAAC,MAAM,CAA6D;IAC3E,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAC,CAAmB;IAC5C,OAAO,CAAC,cAAc,CAAC,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAY;IAEpC,YAAY,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,EAAE,kBAAkB,UAAQ,EAiC/D;IAED;;OAEG;IACH,WAAW,CAAC,QAAQ,EAAE,OAAO,GAAG,IAAI,CAGnC;IAEQ,UAAU,IAAI,IAAI,CAG1B;IAED,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAiBhC;IAED,4GAA4G;IAC5G,OAAO,CAAC,gBAAgB;IAexB,WAAW,CACV,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,SAAS,EAAE,OAAO,EAClB,gBAAgB,CAAC,EAAE,gBAAgB,EACnC,cAAc,CAAC,EAAE,MAAM,GACrB,IAAI,CAcN;IAED,OAAO,CAAC,aAAa;IAgGrB;;OAEG;IACH,SAAS,IAAI,MAAM,CAElB;IAED;;OAEG;IACH,UAAU,IAAI,MAAM,CAEnB;CACD","sourcesContent":["/**\n * Component for displaying bash command execution with streaming output.\n */\n\nimport { Container, Loader, Spacer, Text, type TUI } from \"@agent-forge/tui\";\nimport { t } from \"../../../core/i18n/index.ts\";\nimport {\n\tDEFAULT_MAX_BYTES,\n\tDEFAULT_MAX_LINES,\n\ttype TruncationResult,\n\ttruncateTail,\n} from \"../../../core/tools/truncate.ts\";\nimport { theme } from \"../../../theme/theme.ts\";\nimport { stripAnsi } from \"../../../utils/ansi.ts\";\nimport { DynamicBorder } from \"./dynamic-border.ts\";\nimport { keyText } from \"./keybinding-hints.ts\";\nimport { truncateToVisualLines } from \"./visual-truncate.ts\";\n\n// Preview line limit when not expanded (matches tool execution behavior)\nconst PREVIEW_LINES = 20;\n\n/**\n * Component-side retention budget for streaming output. The bash executor\n * already keeps only a rolling tail buffer (bash-executor.ts) and streams\n * every chunk here; without a cap the UI-side accumulation is unbounded for\n * long-running, chatty commands and outlives the command. Same budget and\n * char-length accounting as the executor's rolling buffer.\n */\nconst MAX_RETAINED_OUTPUT_CHARS = DEFAULT_MAX_BYTES;\n\nexport class BashExecutionComponent extends Container {\n\tprivate command: string;\n\tprivate outputLines: string[] = [];\n\tprivate status: \"running\" | \"complete\" | \"cancelled\" | \"error\" = \"running\";\n\tprivate exitCode: number | undefined = undefined;\n\tprivate loader: Loader;\n\tprivate truncationResult?: TruncationResult;\n\tprivate fullOutputPath?: string;\n\tprivate expanded = false;\n\tprivate contentContainer: Container;\n\n\tconstructor(command: string, ui: TUI, excludeFromContext = false) {\n\t\tsuper();\n\t\tthis.command = command;\n\n\t\t// Use dim border for excluded-from-context commands (!! prefix)\n\t\tconst colorKey = excludeFromContext ? \"dim\" : \"bashMode\";\n\t\tconst borderColor = (str: string) => theme.fg(colorKey, str);\n\n\t\t// Add spacer\n\t\tthis.addChild(new Spacer(1));\n\n\t\t// Top border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\n\t\t// Content container (holds dynamic content between borders)\n\t\tthis.contentContainer = new Container();\n\t\tthis.addChild(this.contentContainer);\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(colorKey, theme.bold(`$ ${command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Loader\n\t\tthis.loader = new Loader(\n\t\t\tui,\n\t\t\t(spinner) => theme.fg(colorKey, spinner),\n\t\t\t(text) => theme.fg(\"muted\", text),\n\t\t\tt(\"Running... ({key} to cancel)\", { key: keyText(\"tui.select.cancel\") }), // Plain text for loader\n\t\t);\n\t\tthis.contentContainer.addChild(this.loader);\n\n\t\t// Bottom border\n\t\tthis.addChild(new DynamicBorder(borderColor));\n\t}\n\n\t/**\n\t * Set whether the output is expanded (shows full output) or collapsed (preview only).\n\t */\n\tsetExpanded(expanded: boolean): void {\n\t\tthis.expanded = expanded;\n\t\tthis.updateDisplay();\n\t}\n\n\toverride invalidate(): void {\n\t\tsuper.invalidate();\n\t\tthis.updateDisplay();\n\t}\n\n\tappendOutput(chunk: string): void {\n\t\t// Strip ANSI codes and normalize line endings\n\t\t// Note: binary data is already sanitized in tui-renderer.ts executeBashCommand\n\t\tconst clean = stripAnsi(chunk).replace(/\\r\\n/g, \"\\n\").replace(/\\r/g, \"\\n\");\n\n\t\t// Append to output lines\n\t\tconst newLines = clean.split(\"\\n\");\n\t\tif (this.outputLines.length > 0 && newLines.length > 0) {\n\t\t\t// Append first chunk to last line (incomplete line continuation)\n\t\t\tthis.outputLines[this.outputLines.length - 1] += newLines[0];\n\t\t\tthis.outputLines.push(...newLines.slice(1));\n\t\t} else {\n\t\t\tthis.outputLines.push(...newLines);\n\t\t}\n\n\t\tthis.enforceRetention();\n\t\tthis.updateDisplay();\n\t}\n\n\t/** Keep only the tail within {@link MAX_RETAINED_OUTPUT_CHARS}, mirroring the executor's rolling buffer. */\n\tprivate enforceRetention(): void {\n\t\tlet total = 0;\n\t\tfor (const line of this.outputLines) total += line.length + 1;\n\t\tif (total <= MAX_RETAINED_OUTPUT_CHARS) return;\n\t\tlet drop = 0;\n\t\twhile (\n\t\t\tdrop < this.outputLines.length - 1 &&\n\t\t\ttotal - (this.outputLines[drop].length + 1) > MAX_RETAINED_OUTPUT_CHARS\n\t\t) {\n\t\t\ttotal -= this.outputLines[drop].length + 1;\n\t\t\tdrop++;\n\t\t}\n\t\tif (drop > 0) this.outputLines.splice(0, drop);\n\t}\n\n\tsetComplete(\n\t\texitCode: number | undefined,\n\t\tcancelled: boolean,\n\t\ttruncationResult?: TruncationResult,\n\t\tfullOutputPath?: string,\n\t): void {\n\t\tthis.exitCode = exitCode;\n\t\tthis.status = cancelled\n\t\t\t? \"cancelled\"\n\t\t\t: exitCode !== 0 && exitCode !== undefined && exitCode !== null\n\t\t\t\t? \"error\"\n\t\t\t\t: \"complete\";\n\t\tthis.truncationResult = truncationResult;\n\t\tthis.fullOutputPath = fullOutputPath;\n\n\t\t// Stop loader\n\t\tthis.loader.stop();\n\n\t\tthis.updateDisplay();\n\t}\n\n\tprivate updateDisplay(): void {\n\t\t// Apply truncation for LLM context limits (same limits as bash tool)\n\t\tconst fullOutput = this.outputLines.join(\"\\n\");\n\t\tconst contextTruncation = truncateTail(fullOutput, {\n\t\t\tmaxLines: DEFAULT_MAX_LINES,\n\t\t\tmaxBytes: DEFAULT_MAX_BYTES,\n\t\t});\n\n\t\t// Get the lines to potentially display (after context truncation)\n\t\tconst availableLines = contextTruncation.content ? contextTruncation.content.split(\"\\n\") : [];\n\n\t\t// Apply preview truncation based on expanded state\n\t\tconst previewLogicalLines = availableLines.slice(-PREVIEW_LINES);\n\t\tconst hiddenLineCount = availableLines.length - previewLogicalLines.length;\n\n\t\t// Rebuild content container\n\t\tthis.contentContainer.clear();\n\n\t\t// Command header\n\t\tconst header = new Text(theme.fg(\"bashMode\", theme.bold(`$ ${this.command}`)), 1, 0);\n\t\tthis.contentContainer.addChild(header);\n\n\t\t// Output\n\t\tif (availableLines.length > 0) {\n\t\t\tif (this.expanded) {\n\t\t\t\t// Show all lines\n\t\t\t\tconst displayText = availableLines.map((line) => theme.fg(\"muted\", line)).join(\"\\n\");\n\t\t\t\tthis.contentContainer.addChild(new Text(`\\n${displayText}`, 1, 0));\n\t\t\t} else {\n\t\t\t\t// Use shared visual truncation utility with width-aware caching\n\t\t\t\tconst styledOutput = previewLogicalLines.map((line) => theme.fg(\"muted\", line)).join(\"\\n\");\n\t\t\t\tconst styledInput = `\\n${styledOutput}`;\n\t\t\t\tlet cachedWidth: number | undefined;\n\t\t\t\tlet cachedLines: string[] | undefined;\n\t\t\t\tthis.contentContainer.addChild({\n\t\t\t\t\trender: (width: number) => {\n\t\t\t\t\t\tif (cachedLines === undefined || cachedWidth !== width) {\n\t\t\t\t\t\t\tconst result = truncateToVisualLines(styledInput, PREVIEW_LINES, width, 1);\n\t\t\t\t\t\t\tcachedLines = result.visualLines;\n\t\t\t\t\t\t\tcachedWidth = width;\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// v8 ignore next -- cached lines are always computed before this render callback returns\n\t\t\t\t\t\treturn cachedLines ?? [];\n\t\t\t\t\t},\n\t\t\t\t\tinvalidate: () => {\n\t\t\t\t\t\tcachedWidth = undefined;\n\t\t\t\t\t\tcachedLines = undefined;\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\n\t\t// Loader or status\n\t\tif (this.status === \"running\") {\n\t\t\tthis.contentContainer.addChild(this.loader);\n\t\t} else {\n\t\t\tconst statusParts: string[] = [];\n\n\t\t\t// Show how many lines are hidden (collapsed preview)\n\t\t\tif (hiddenLineCount > 0) {\n\t\t\t\tif (this.expanded) {\n\t\t\t\t\t// i18n: 原三段样式拼接合并为整句单 key,键位片段的 dim 样式损失按批次④特殊裁决 1 接受\n\t\t\t\t\tstatusParts.push(theme.fg(\"muted\", t(\"({key} to collapse)\", { key: keyText(\"app.tools.expand\") })));\n\t\t\t\t} else {\n\t\t\t\t\tstatusParts.push(\n\t\t\t\t\t\ttheme.fg(\n\t\t\t\t\t\t\t\"muted\",\n\t\t\t\t\t\t\tt(\"... {count} more lines ({key} to expand)\", {\n\t\t\t\t\t\t\t\tcount: hiddenLineCount,\n\t\t\t\t\t\t\t\tkey: keyText(\"app.tools.expand\"),\n\t\t\t\t\t\t\t}),\n\t\t\t\t\t\t),\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (this.status === \"cancelled\") {\n\t\t\t\tstatusParts.push(theme.fg(\"warning\", t(\"(cancelled)\")));\n\t\t\t} else if (this.status === \"error\") {\n\t\t\t\tstatusParts.push(theme.fg(\"error\", t(\"(exit {code})\", { code: this.exitCode ?? \"unknown\" })));\n\t\t\t}\n\n\t\t\t// Add truncation warning (context truncation, not preview truncation)\n\t\t\tconst wasTruncated = this.truncationResult?.truncated || contextTruncation.truncated;\n\t\t\tif (wasTruncated && this.fullOutputPath) {\n\t\t\t\tstatusParts.push(\n\t\t\t\t\ttheme.fg(\"warning\", t(\"Output truncated. Full output: {path}\", { path: this.fullOutputPath })),\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tif (statusParts.length > 0) {\n\t\t\t\tthis.contentContainer.addChild(new Text(`\\n${statusParts.join(\"\\n\")}`, 1, 0));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Get the raw output for creating BashExecutionMessage.\n\t */\n\tgetOutput(): string {\n\t\treturn this.outputLines.join(\"\\n\");\n\t}\n\n\t/**\n\t * Get the command that was executed.\n\t */\n\tgetCommand(): string {\n\t\treturn this.command;\n\t}\n}\n"]}
|