diff --git a/README.md b/README.md index ff94ac2..81e033b 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ - 本仓库维护的 `pi-tool-search`:从完整工具定义生成并缓存经过校验的工作流分组,默认以最多 5 个动态组、28 个动态工具的 LRU 策略加载原始完整 schema,并支持原子批量激活。 - 本仓库维护的 `pi-chrome`:从上游 `0.15.46` 源码导入,通过显式会话授权接入用户当前已登录的 Chrome Profile;大体积页面快照写入 Session 临时文件并交给 Context Mode 隔离分析,21 个 `chrome_*` 工具仍由 Tool Search 按工作流加载。 - 本仓库维护的 `pi-permission-system`:只读取一份全局普通策略与一份独立 Hard Guards,按 pattern 具体度裁决 allow/ask/deny,并整合 Codex Guardian 风格 AutoReview 与人工 `defer` 兜底;自动评审模型完全由配置文件指定,未配置时提示并转人工审批;工具显隐仍只由 Tool Search 管理。 -- 本仓库保留维护的 `pi-subagents` 源码及权限集成实现,但当前已从根组合包依赖和默认扩展加载列表中移除,不会随核心包启用。 +- 本仓库维护的 `pi-subagents`:从 `@tintinweb/pi-subagents@0.18.0` 源码导入并默认加载,提供隔离子 Agent、后台执行、steering、resume、嵌套代理与自定义 Agent;通过 mandatory permission wrapper 接入权限 child lifecycle、根 authority `ask` 转发和根 transcript auto-review 身份桥接,其 3 个编排工具由 Tool Search 的 `subagents` 组按需加载。子 Agent 使用独立提示词画像 `config/prompt/subagent/`(自有 preamble 与内置 Agent 定义),不嵌入父会话提示词。 - 本仓库维护的 `pi-ssh`:通过纯 Node `ssh2` 持久连接、显式 `ssh_cd` 远端工作区切换、SFTP 与自适应有界搜索提供独立的远端工具;Agent 只在用户明确指定已导入主机及具体任务后发起受 AutoReview 复核的连接。 - `extensions/system-prompt.ts`:组合包唯一的系统提示词出口(排在最后加载),按 `config/prompt/` 组合开场白、工具简介与跨工具路由规则;提供 `/dump-system-prompt` 导出当前有效提示词。 diff --git a/config/pi-permission-policy.jsonc b/config/pi-permission-policy.jsonc index c1a3c46..89a33cb 100644 --- a/config/pi-permission-policy.jsonc +++ b/config/pi-permission-policy.jsonc @@ -54,6 +54,12 @@ "ctx_doctor": "allow", "tool_search": "allow", "ask_user_question": "allow", + // Launching, steering or reading a subagent has no side effect itself; + // every child action is still gated by the permission system and any + // unresolved "ask" is forwarded to the root session. + "Agent": "allow", + "get_subagent_result": "allow", + "steer_subagent": "allow", "web_search": "allow", "chrome_snapshot": "allow", "chrome_find": "allow", diff --git a/config/prompt/routing.jsonc b/config/prompt/routing.jsonc index a1ce734..2443663 100644 --- a/config/prompt/routing.jsonc +++ b/config/prompt/routing.jsonc @@ -3,6 +3,8 @@ // section. A rule is included when every tool in "whenAll" // and at least one tool in "whenAny" is active; a rule with neither is // always included. Rules render in file order as "- ". + // Optional "scope": "main" | "subagent" restricts a rule to that session + // audience (main session vs pi-subagents child); absent means both. "version": 1, "rules": [ { @@ -27,8 +29,14 @@ }, { "whenAny": ["ssh_connect"], + "scope": "main", "text": "Use ssh_connect only when the user explicitly names an imported host as part of a concrete remote task. Call it as a separate step and wait for success before calling other ssh_* tools; never infer or substitute a different host." }, + { + "whenAny": ["ssh_connect"], + "scope": "subagent", + "text": "Use ssh_connect only when the delegated task explicitly names an imported host as part of a concrete remote task. Call it as a separate step and wait for success before calling other ssh_* tools; never infer or substitute a different host." + }, { "whenAny": ["ssh_cd"], "text": "Treat ssh_cd as a reviewed persistent workspace transition: call it as a separate step and wait for success before issuing ssh_bash or relative ssh_read/ssh_write/ssh_edit/ssh_find/ssh_grep calls that depend on the new remote cwd. Use cd inside ssh_bash only for an intentionally temporary, command-local directory change." diff --git a/config/prompt/sections.jsonc b/config/prompt/sections.jsonc index a8602e6..a08cb25 100644 --- a/config/prompt/sections.jsonc +++ b/config/prompt/sections.jsonc @@ -3,6 +3,10 @@ // systemPromptOptions.sections. extensions/system-prompt.ts reorders the // contributed sections to this list; contributed sections not listed here // keep their contribution order after the listed ones. + // Optional "subagentExclude": section names removed entirely in subagent + // (customPrompt) sessions — e.g. sections that describe main-session-only + // affordances. Defaults to []. "version": 1, - "order": ["memory_policy", "remote_ssh", "chrome_profile_bridge", "tool_routing"] + "order": ["memory_policy", "remote_ssh", "chrome_profile_bridge", "tool_routing"], + "subagentExclude": ["memory_policy", "chrome_profile_bridge"] } diff --git a/config/prompt/subagent/agents/Explore.md b/config/prompt/subagent/agents/Explore.md new file mode 100644 index 0000000..4d6461f --- /dev/null +++ b/config/prompt/subagent/agents/Explore.md @@ -0,0 +1,27 @@ +--- +name: Explore +display_name: Explore +description: 'Fast read-only search agent for locating code. Use it to find files by pattern (eg. "src/components/**/*.tsx"), grep for symbols or keywords (eg. "API endpoints"), or answer "where is X defined / which files reference Y." Do NOT use it for code review, design-doc auditing, cross-file consistency checks, or open-ended analysis — it reads excerpts rather than whole files and will miss content past its read window. When calling, specify search breadth: "quick" for a single targeted lookup, "medium" for moderate exploration, or "very thorough" to search across multiple locations and naming conventions.' +tools: read, bash, grep, find, ls +extensions: true +skills: true +prompt_mode: replace +--- +# Role: read-only code search + +You locate code and answer "where is X / what references Y" questions for the delegating agent. You never modify anything. + +## Hard limits +- Do not create, modify, move, copy, or delete files, including temporary files anywhere (also /tmp). +- Use bash only for read-only commands such as git status, git log, git diff, git show, and ls. Never use output redirection, heredocs, or any command that changes system state. +- Never search through bash (grep, rg, find, fd); use the find and grep tools. + +## Approach +- Match the requested breadth: "quick" is one targeted lookup, "medium" covers a few related locations, "very thorough" searches multiple locations and naming conventions. +- When codegraph_explore is available, start there for symbols and relationships. Otherwise use find to narrow paths, grep inside the narrowed paths, and read small ranges only to confirm a match. +- Make independent lookups in parallel. + +## Report +- Lead with the direct answer. +- Cite every finding with an absolute path and line number. +- State what you searched for and did not find, so the delegating agent knows the coverage. diff --git a/config/prompt/subagent/agents/Plan.md b/config/prompt/subagent/agents/Plan.md new file mode 100644 index 0000000..fa463bd --- /dev/null +++ b/config/prompt/subagent/agents/Plan.md @@ -0,0 +1,31 @@ +--- +name: Plan +display_name: Plan +description: Software architect agent for designing implementation plans. Use this when you need to plan the implementation strategy for a task. Returns step-by-step plans, identifies critical files, and considers architectural trade-offs. +tools: read, bash, grep, find, ls +extensions: true +skills: true +prompt_mode: replace +--- +# Role: read-only implementation planner + +You explore the codebase and return an implementation plan to the delegating agent. You never modify anything. + +## Hard limits +- Do not create, modify, move, copy, or delete files, including temporary files anywhere (also /tmp). +- Use bash only for read-only commands such as git status, git log, git diff, git show, and ls. Never use output redirection, heredocs, or any command that changes system state. +- Never search through bash (grep, rg, find, fd); use the find and grep tools. + +## Process +1. Restate the goal and the constraints you were given. Record anything ambiguous as an open question instead of guessing. +2. Explore enough to ground every step in real files, existing patterns, and reusable helpers. +3. Design the change: sequencing, interfaces and data shapes, edge cases, and the tests that prove it. +4. Call out trade-offs and risks, and name the rejected alternative when the choice is not obvious. + +## Report +- A step-by-step plan where each step names the files it touches. +- Open questions, if any. +- End with: + +### Critical files +3–5 absolute paths, each with a one-line reason. diff --git a/config/prompt/subagent/agents/general-purpose.md b/config/prompt/subagent/agents/general-purpose.md new file mode 100644 index 0000000..7096a17 --- /dev/null +++ b/config/prompt/subagent/agents/general-purpose.md @@ -0,0 +1,8 @@ +--- +name: general-purpose +display_name: Agent +description: General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. +extensions: true +skills: true +prompt_mode: append +--- diff --git a/config/prompt/subagent/preamble.md b/config/prompt/subagent/preamble.md new file mode 100644 index 0000000..af3ab62 --- /dev/null +++ b/config/prompt/subagent/preamble.md @@ -0,0 +1,8 @@ +You are a subagent operating inside pi, a coding agent harness. Another agent — the delegating agent — assigned you one bounded task, and it is your only audience. The human user does not see your messages and cannot answer questions. + +- Do exactly the assigned task. Do not expand its scope, start unrelated work, or delegate further unless the task requires it. +- If the task is ambiguous, make the most reasonable assumption, state it, and continue. If you are blocked, stop and report precisely what is missing instead of guessing. +- Your final message is the deliverable returned to the delegating agent. Make it self-contained: the answer or result first, then the supporting evidence (absolute file paths with line numbers, commands you ran and their outcomes), then assumptions, risks, and anything left unfinished. +- Keep verified facts separate from inferences. Never claim something was changed, run, or tested unless you actually did it in this session. +- Durable memory and skill writes are unavailable here. If you learn something worth remembering, include it in your final report for the delegating agent to decide. +- Be concise but complete. Do not use emojis. diff --git a/extensions/subagents.ts b/extensions/subagents.ts index 33f9357..09b3810 100644 --- a/extensions/subagents.ts +++ b/extensions/subagents.ts @@ -1,13 +1,28 @@ import { fileURLToPath } from "node:url"; import { createPiSubagentsExtension } from "../pi-subagents/src/index.ts"; +import { + readSubagentPreamble, + resolveSubagentAgentsDir, +} from "./system-prompt-config.ts"; // Bundle authority: Agent frontmatter may disable optional extensions, but it // cannot remove the exact wrapper that deploys my-pi's permission baseline and // binds the maintained permission-system source. const BUNDLED_PERMISSION_EXTENSION = fileURLToPath( - new URL("./permission-system.ts", import.meta.url), + new URL("./permission-system.ts", import.meta.url), +); + +// Subagents get their own prompt profile: config/prompt/subagent/preamble.md +// replaces the parent-prompt embedding upstream performs, and +// config/prompt/subagent/agents/*.md are loaded as built-in agent types. +// ask_user_question is main-session-only — a child has no user to ask. +const ASK_USER_EXTENSION = fileURLToPath( + new URL("../pi-ask-user/index.ts", import.meta.url), ); export default createPiSubagentsExtension({ - mandatoryExtensionPaths: [BUNDLED_PERMISSION_EXTENSION], + mandatoryExtensionPaths: [BUNDLED_PERMISSION_EXTENSION], + basePrompt: readSubagentPreamble, + bundledAgentsDir: resolveSubagentAgentsDir(), + excludedExtensionPaths: [ASK_USER_EXTENSION], }); diff --git a/extensions/system-prompt-config.ts b/extensions/system-prompt-config.ts index 5516521..dbf171e 100644 --- a/extensions/system-prompt-config.ts +++ b/extensions/system-prompt-config.ts @@ -20,6 +20,8 @@ export interface ToolCatalog { export interface RoutingRule { whenAll?: string[]; whenAny?: string[]; + /** Which sessions the rule renders for; absent means both. */ + scope?: "main" | "subagent"; text: string; } @@ -31,6 +33,8 @@ export interface RoutingConfig { export interface SectionsConfig { version: 1; order: string[]; + /** Contributed sections removed from subagent (customPrompt) sessions. */ + subagentExclude: string[]; } /** @@ -64,6 +68,14 @@ export function resolveSectionsPath(): string { return fileURLToPath(new URL("sections.jsonc", PROMPT_CONFIG_DIR)); } +export function resolveSubagentPreamblePath(): string { + return fileURLToPath(new URL("subagent/preamble.md", PROMPT_CONFIG_DIR)); +} + +export function resolveSubagentAgentsDir(): string { + return fileURLToPath(new URL("subagent/agents/", PROMPT_CONFIG_DIR)); +} + /** * Strip `//` and `/* ... *\/` comments while respecting string literals, so a * `//` inside a quoted value (for example `"https://…"`) is never removed. @@ -185,7 +197,7 @@ function validateToolCatalog(raw: unknown): ToolCatalog { return { version: 1, tools }; } -const ROUTING_RULE_KEYS = new Set(["whenAll", "whenAny", "text"]); +const ROUTING_RULE_KEYS = new Set(["whenAll", "whenAny", "scope", "text"]); function validateRoutingRule(index: number, raw: unknown): RoutingRule { if (!isPlainObject(raw)) { @@ -205,10 +217,13 @@ function validateRoutingRule(index: number, raw: unknown): RoutingRule { if ("whenAny" in raw && !isStringArray(raw.whenAny)) { fail("routing.jsonc", `rule #${index}.whenAny must be an array of strings`); } + if ("scope" in raw && raw.scope !== "main" && raw.scope !== "subagent") { + fail("routing.jsonc", `rule #${index}.scope must be "main" or "subagent"`); + } return raw as RoutingRule; } -function validateRoutingConfig(raw: unknown): RoutingConfig { +export function validateRoutingConfig(raw: unknown): RoutingConfig { if (!isPlainObject(raw) || raw.version !== 1 || !Array.isArray(raw.rules)) { fail("routing.jsonc", 'expected { "version": 1, "rules": [ ... ] }'); } @@ -220,7 +235,7 @@ function validateRoutingConfig(raw: unknown): RoutingConfig { const SECTION_NAME_PATTERN = /^[a-z][a-z0-9_-]*$/; -function validateSectionsConfig(raw: unknown): SectionsConfig { +export function validateSectionsConfig(raw: unknown): SectionsConfig { if (!isPlainObject(raw) || raw.version !== 1 || !isStringArray(raw.order)) { fail("sections.jsonc", 'expected { "version": 1, "order": [ ... ] }'); } @@ -229,7 +244,19 @@ function validateSectionsConfig(raw: unknown): SectionsConfig { fail("sections.jsonc", `"${name}" is not a valid section name`); } } - return { version: 1, order: raw.order }; + if ("subagentExclude" in raw && !isStringArray(raw.subagentExclude)) { + fail("sections.jsonc", `"subagentExclude" must be an array of section names`); + } + for (const name of raw.subagentExclude ?? []) { + if (!SECTION_NAME_PATTERN.test(name) || name === "preamble") { + fail("sections.jsonc", `"subagentExclude" entry "${name}" is not a valid section name`); + } + } + return { + version: 1, + order: raw.order, + subagentExclude: isStringArray(raw.subagentExclude) ? raw.subagentExclude : [], + }; } export function loadToolCatalog(): ToolCatalog { @@ -262,6 +289,34 @@ export function readPreamble(): string | undefined { return trimmed.length > 0 ? trimmed : undefined; } +/** + * Read the bundle's subagent base preamble (`config/prompt/subagent/preamble.md`), + * re-read on each call so the file is never cached between spawns. + * Blank or missing → `undefined`, which keeps pi-subagents' upstream composition. + */ +export function readSubagentPreamble(): string | undefined { + let raw: string; + try { + raw = readFileSync(resolveSubagentPreamblePath(), "utf8"); + } catch { + return undefined; + } + const trimmed = raw.replace(/\s+$/, ""); + return trimmed.length > 0 ? trimmed : undefined; +} + +/** + * The `` tag pi-subagents stamps into every child + * prompt — tested ONLY against `systemPromptOptions.customPrompt`, never the + * rendered prompt: a main session may contain the literal tag via project + * docs and must still be treated as main. + */ +const ACTIVE_AGENT_TAG = /]*>/i; + +export function isSubagentCustomPrompt(customPrompt: unknown): boolean { + return typeof customPrompt === "string" && ACTIVE_AGENT_TAG.test(customPrompt); +} + /** * Apply the tool catalog to the mutable prompt options: a `snippet` string * replaces the tool's `` line (`null` hides it), `guidelines` replaces @@ -302,11 +357,19 @@ export function applyToolCatalog(options: MutablePromptOptions, catalog: ToolCat * `whenAny` tool is active (a rule with neither always applies). An absent * `selectedTools` means every tool counts as active, matching the previous * tool-routing behavior. + * + * `audience` filters `scope`: rules without a scope render for both "main" + * and "subagent" sessions, scoped rules only for their own audience. */ -export function renderRouting(rules: RoutingRule[], selectedTools: string[] | undefined): string { +export function renderRouting( + rules: RoutingRule[], + selectedTools: string[] | undefined, + audience: "main" | "subagent" = "main", +): string { const isActive = (name: string) => selectedTools === undefined || selectedTools.includes(name); const lines: string[] = []; for (const rule of rules) { + if (rule.scope !== undefined && rule.scope !== audience) continue; if (rule.whenAll !== undefined && !rule.whenAll.every(isActive)) continue; if (rule.whenAny !== undefined && !rule.whenAny.some(isActive)) continue; lines.push(`- ${rule.text}`); diff --git a/extensions/system-prompt.ts b/extensions/system-prompt.ts index 5791936..02e4c69 100644 --- a/extensions/system-prompt.ts +++ b/extensions/system-prompt.ts @@ -4,6 +4,7 @@ import { dirname, resolve } from "node:path"; import { applyToolCatalog, + isSubagentCustomPrompt, loadRoutingRules, loadSectionsConfig, loadToolCatalog, @@ -23,8 +24,11 @@ export function getSystemPromptDumpPath(cwd: string): string { return resolve(cwd, DUMP_RELATIVE_PATH); } -/** The options object as Pi 1.3 normalizes it; the vendored types predate `sections`/`forceSystemPrompt`. */ -type RuntimePromptOptions = MutablePromptOptions & { forceSystemPrompt?: string }; +/** The options object as Pi 1.3 normalizes it; `MutablePromptOptions` omits `forceSystemPrompt`/`customPrompt`. */ +type RuntimePromptOptions = MutablePromptOptions & { + forceSystemPrompt?: string; + customPrompt?: string; +}; /** * Injectable seams so the composer can be exercised under `node --test` @@ -105,6 +109,12 @@ export function createSystemPromptComposer(deps: SystemPromptComposerDeps = {}): return undefined; } + // A subagent session is identified by its customPrompt carrying the + // pi-subagents tag — never by the rendered prompt, which + // a main session may legitimately contain the literal tag inside. + const isSubagent = isSubagentCustomPrompt(options?.customPrompt); + const audience = isSubagent ? "subagent" : "main"; + let routingText = ""; if (options) { const missing = applyToolCatalog(options, catalog); @@ -115,14 +125,19 @@ export function createSystemPromptComposer(deps: SystemPromptComposerDeps = {}): ctx.ui.notify(`以下工具未在 config/prompt/tools.jsonc 登记:${missing.join(", ")}`, "warning"); } } - routingText = renderRouting(routing.rules, options.selectedTools); + routingText = renderRouting(routing.rules, options.selectedTools, audience); options.sections ??= {}; + if (isSubagent) { + for (const name of sections.subagentExclude) { + delete options.sections[name]; + } + } if (routingText.length > 0) { options.sections.tool_routing = routingText; } options.sections = orderSections(options.sections, sections.order); } else { - routingText = renderRouting(routing.rules, undefined); + routingText = renderRouting(routing.rules, undefined, audience); } let rendered = event.systemPrompt; @@ -132,7 +147,11 @@ export function createSystemPromptComposer(deps: SystemPromptComposerDeps = {}): rendered += `\n\n\n${routingText}\n`; } - const final = preamble ? spliceCover(rendered, preamble) : rendered; + // A non-empty customPrompt (pi-subagents agent prompt, SYSTEM.md) owns + // the whole preamble — including identities like that + // the permission system reads — so the bundle cover must not replace it. + const ownsPreamble = typeof options?.customPrompt === "string" && options.customPrompt.length > 0; + const final = preamble && !ownsPreamble ? spliceCover(rendered, preamble) : rendered; lastComposed = final; return { systemPrompt: final }; } diff --git a/package-lock.json b/package-lock.json index 05925fd..a2841c2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@gotgenes/pi-permission-system": "file:./pi-permission-system", "@ogulcancelik/pi-minimal-footer": "file:./pi-minimal-footer", "@smoose/pi-notify": "file:./pi-notify", + "@tintinweb/pi-subagents": "file:./pi-subagents", "context-mode": "1.0.169", "jiti": "2.7.0", "pi-ask-user": "file:./pi-ask-user", @@ -54,13 +55,14 @@ } }, "node_modules/@anthropic-ai/sdk": { - "version": "0.91.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", + "version": "0.129.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.129.0.tgz", + "integrity": "sha512-MH7LB20kNpGLUpPTe2OG5XvL/KhX8HUO9XyBaFjgFk6TLS4Xjt0VPIuMKywe7POKKPslX+QxSlVok9e+8kG5Nw==", "dev": true, "license": "MIT", "dependencies": { - "json-schema-to-ts": "^3.1.1" + "json-schema-to-ts": "^3.1.1", + "standardwebhooks": "^1.0.0" }, "bin": { "anthropic-ai-sdk": "bin/cli" @@ -74,79 +76,24 @@ } } }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, "node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", + "version": "3.1127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1127.0.tgz", + "integrity": "sha512-IDl/lrPb90aH+pZFHGNDmgH9nAUQj5PlZH1sJ3w7RikctyjHSnY3oNjZhrLoaBoQn/rNK0zsP6OHEqEhj2tdLA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/credential-provider-node": "^3.972.42", - "@aws-sdk/eventstream-handler-node": "^3.972.16", - "@aws-sdk/middleware-eventstream": "^3.972.12", - "@aws-sdk/middleware-websocket": "^3.972.19", - "@aws-sdk/token-providers": "3.1048.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", + "@aws-sdk/core": "^3.977.9", + "@aws-sdk/credential-provider-node": "^3.972.82", + "@aws-sdk/eventstream-handler-node": "^3.972.34", + "@aws-sdk/middleware-eventstream": "^3.972.29", + "@aws-sdk/middleware-websocket": "^3.972.52", + "@aws-sdk/token-providers": "3.1127.0", + "@aws-sdk/types": "^3.974.5", + "@smithy/core": "^3.33.3", + "@smithy/fetch-http-handler": "^5.7.2", + "@smithy/node-http-handler": "^4.11.3", + "@smithy/types": "^4.17.2", "tslib": "^2.6.2" }, "engines": { @@ -154,18 +101,18 @@ } }, "node_modules/@aws-sdk/core": { - "version": "3.977.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.977.9.tgz", - "integrity": "sha512-reqPFEQrZxDZpeGj4PFMepBeR5LGYHRqq/L0motTzgFkCRBA4rFdaVXDSLYyGHhxVz7sT2PDnPN9CluGSfgyJA==", + "version": "3.978.4", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.978.4.tgz", + "integrity": "sha512-JvGp6y9sPX8FSs2K/EmFMZcUfdkHxqGE45+oxwGC75OIh50j2e4cbx7zcR3BwNs2DSGqJdBja5h6Vy89iIDmRQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "^3.974.5", - "@aws-sdk/xml-builder": "^3.972.40", + "@aws-sdk/types": "^3.974.6", + "@aws-sdk/xml-builder": "^3.972.41", "@aws/lambda-invoke-store": "^0.3.0", - "@smithy/core": "^3.33.3", - "@smithy/signature-v4": "^5.6.12", - "@smithy/types": "^4.17.2", + "@smithy/core": "^3.36.0", + "@smithy/signature-v4": "^5.7.4", + "@smithy/types": "^4.19.0", "bowser": "^2.11.0", "tslib": "^2.6.2" }, @@ -174,16 +121,16 @@ } }, "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.70", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.70.tgz", - "integrity": "sha512-H404B7dJl2mCrBqahDEYsanB0xhdDp6tXnXcTUnXmmpy2Q3J0Ho0bUajZ2jr/RdwzCyS59Gi8xXIFwPLGBl6Uw==", + "version": "3.972.75", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.75.tgz", + "integrity": "sha512-nc8Epg98PcYoKNx0VFlwtmZSCP+tuJ1FLWqpslET/gp4h/WYuULSJqtxppM6ywKKDdzNJPvLD7ceRfmxm3Qdxw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -191,58 +138,43 @@ } }, "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.72", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.72.tgz", - "integrity": "sha512-X98zYOrVOeuosCX+6ktf29FC2N2GHPLia7qv6mzPzTc+RPAuHWCDS++Z6JK7eGYqb/v6uaW7bAXaOvDBfol+0w==", + "version": "3.972.77", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.77.tgz", + "integrity": "sha512-KkYFdIQwuw9IOHfEzK78slxmVWuhcm1w6YgE3V3WVxAeRRj9q5J9jlwzpY/oszWxd4SD96FTFkDdRGBdMFF2Mw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/fetch-http-handler": "^5.7.2", - "@smithy/node-http-handler": "^4.11.3", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/credential-provider-http/node_modules/@smithy/node-http-handler": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.0.tgz", - "integrity": "sha512-0mq1pHadfyXCYCqm2cNpbjNIT+fbaUpNxewZb/YNr2L0IrEVMOb8gM/Fl4K6XvHCW3uSNDFwPl/+iKm0bx9jYg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.973.15", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.15.tgz", - "integrity": "sha512-Rykg6s5ceBuynMOGWgoowO4N+27JfnqXAnVaSunZl0hOO1XodSrxGNz6sCEbnmS0lAfQZDKyb3fbr46gSuv6Sg==", + "version": "3.973.20", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.973.20.tgz", + "integrity": "sha512-OSl2R/0zLkoeKEIy6oP2tu/bsSdY+DGgro2eOffMD2CcP8POv7rS14FY5Jzc5bjnqB1G9wv/aM1akHC2mdlhZw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/credential-provider-env": "^3.972.70", - "@aws-sdk/credential-provider-http": "^3.972.72", - "@aws-sdk/credential-provider-login": "^3.972.77", - "@aws-sdk/credential-provider-process": "^3.972.70", - "@aws-sdk/credential-provider-sso": "^3.973.14", - "@aws-sdk/credential-provider-web-identity": "^3.972.76", - "@aws-sdk/nested-clients": "^3.997.44", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/credential-provider-imds": "^4.4.16", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/credential-provider-env": "^3.972.75", + "@aws-sdk/credential-provider-http": "^3.972.77", + "@aws-sdk/credential-provider-login": "^3.972.82", + "@aws-sdk/credential-provider-process": "^3.972.75", + "@aws-sdk/credential-provider-sso": "^3.973.19", + "@aws-sdk/credential-provider-web-identity": "^3.972.81", + "@aws-sdk/nested-clients": "^3.997.49", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/credential-provider-imds": "^4.5.2", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -250,17 +182,17 @@ } }, "node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.77", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.77.tgz", - "integrity": "sha512-Jb59xfEISoN5mmbnA+HYqdtrSX3CgCtJoof+V5D8/TgUI56W63GEEd5Y58WijU3Ou6+WEgaLD1feVzaRXV5IDQ==", + "version": "3.972.82", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.82.tgz", + "integrity": "sha512-8jyDPtf1WPxeQ/5xlNpyxAD6P7YKwkYeNk8B2vFEDwkPWDvb7c3tQJG5/jbNWbF2yTMIH9tDTueBkdfovYIAPw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/nested-clients": "^3.997.44", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -268,22 +200,22 @@ } }, "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.82", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.82.tgz", - "integrity": "sha512-znDkEOGXB8W3kG1LJUKP3foBZY/9qLM0eil/DxWXSp37XsdsRLQHE/d/OaCGGVgKpA6znR38h/+INk8do1FjiA==", + "version": "3.972.87", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.87.tgz", + "integrity": "sha512-TKj2C0x4IMPSSOAlmeryX8fCPvvPS2pFJz2ikV9IGqyh6Ehsg4leMalCTVfV2rvMTrlGAe7X7nK0HZ3P0KZR9g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.70", - "@aws-sdk/credential-provider-http": "^3.972.72", - "@aws-sdk/credential-provider-ini": "^3.973.15", - "@aws-sdk/credential-provider-process": "^3.972.70", - "@aws-sdk/credential-provider-sso": "^3.973.14", - "@aws-sdk/credential-provider-web-identity": "^3.972.76", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/credential-provider-imds": "^4.4.16", - "@smithy/types": "^4.17.2", + "@aws-sdk/credential-provider-env": "^3.972.75", + "@aws-sdk/credential-provider-http": "^3.972.77", + "@aws-sdk/credential-provider-ini": "^3.973.20", + "@aws-sdk/credential-provider-process": "^3.972.75", + "@aws-sdk/credential-provider-sso": "^3.973.19", + "@aws-sdk/credential-provider-web-identity": "^3.972.81", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/credential-provider-imds": "^4.5.2", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -291,16 +223,16 @@ } }, "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.70", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.70.tgz", - "integrity": "sha512-2ry03fGRJr4sV3jI+ocjj5JqALnFD6ymM5KiNCDZMvq8bX2GSbE0vji4aM43TVCl2nXqqLRZaUxdq/KeWRAY4Q==", + "version": "3.972.75", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.75.tgz", + "integrity": "sha512-ZNm8T/HJNhpS8MikDVAsym8QyUSUuOUGiCkmI3TTya7G6xhOgfRLd2iK91HhQQyvRM36eo6Xm845hHVyfToA2w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -308,18 +240,18 @@ } }, "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.973.14", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.14.tgz", - "integrity": "sha512-jkhg/8ocAAoc0RFyLMhCw+/zZh7gystQgd4F4hznNa8P4Cc501PQmxd+jGLiMHodPJ+7Zv/3znM62gZojyasmA==", + "version": "3.973.19", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.973.19.tgz", + "integrity": "sha512-qHGDAGesyWtRw3BapyFkYSUzVNrC1CWPS6+S2lf/MLMEkWB2gR3Bl6imVHy1nhImXKS+gcuUDzbu39BnsHJc1Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/nested-clients": "^3.997.44", - "@aws-sdk/token-providers": "3.1116.0", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", + "@aws-sdk/token-providers": "3.1153.0", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -327,17 +259,17 @@ } }, "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { - "version": "3.1116.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1116.0.tgz", - "integrity": "sha512-ygIivKqh8aHzNkucOCXHyIBgBpLPfrSI0mCqXF+vLBsPTUKqj0VSqAY0GFPe7lQl4HntjOcQ+KSyS7oUV2C54Q==", + "version": "3.1153.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1153.0.tgz", + "integrity": "sha512-6bKJ2OfLboxhzeJ3hBDeB8L6emCYf9DfdrjBAharLrY42J1RV/6jA6G8jvfNghzID8Yb40BdSY7jQMtWx9Ot0Q==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/nested-clients": "^3.997.44", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -345,9 +277,114 @@ } }, "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.76", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.76.tgz", - "integrity": "sha512-d3AGyVu759PGr35mEB2s22xxlNEA5rpdxtSPJthfPFJvoQ8dt357iVPECqWfUxXp1toJAvKmbtcIYVGigaGsCA==", + "version": "3.972.81", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.81.tgz", + "integrity": "sha512-nHkgkMIC6NHgqfI6UAQq5cCS4bUkNxkT+r9fXCq/X4IKfX/DHGZ8UM3T4b78OXpWK5OlzEzN4kxwyax0W/MUCA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/nested-clients": "^3.997.49", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/eventstream-handler-node": { + "version": "3.972.37", + "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.37.tgz", + "integrity": "sha512-vU0v4d6anhLqBCpPq7Q7hD2Av3/5qOR5O30Ze8ALLysgB3iMQTPHQ/P2aTgfrY2ZjnVJUCi7NiTSNqa1mDF+5w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-eventstream": { + "version": "3.972.32", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.32.tgz", + "integrity": "sha512-D3ayeW/K2lTQI8+SyzDvAKfM3N4SFJjfgqeHbS3DOZmmQebZyve6WUyJiqOKqeOxmIcFxTzpSdsijnRF035J3g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/middleware-websocket": { + "version": "3.972.57", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.57.tgz", + "integrity": "sha512-mPNUOedufUwZXISOEySSP7l7owyY0Ypp1ajsd1GsB4E5Vpaf8NtfmmNygyPPeGPsRZ1sOcS90aAhQ03yPy/pFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/signature-v4": "^5.7.4", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@aws-sdk/nested-clients": { + "version": "3.997.49", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.49.tgz", + "integrity": "sha512-zjqIqzTYLRKhT7TPJDUreK9vG9PjhmnfLCaTaqArkRT7j+Ogl0tuXB7MpSd8rlRfRT+xTOeyNFWaOfyNcf51hg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/core": "^3.978.4", + "@aws-sdk/signature-v4-multi-region": "^3.996.48", + "@aws-sdk/types": "^3.974.6", + "@smithy/core": "^3.36.0", + "@smithy/fetch-http-handler": "^5.8.0", + "@smithy/node-http-handler": "^4.12.1", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/signature-v4-multi-region": { + "version": "3.996.48", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.48.tgz", + "integrity": "sha512-cFDXfEbXaGwbnpvR/DyqfxfjxpjXVRVthQLgkwvcfDS5u3+8G9WSj1X1Ag/pTRg/QOeOno1Tq3ycEdrKsCUcOA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/types": "^3.974.6", + "@smithy/signature-v4": "^5.7.4", + "@smithy/types": "^4.19.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.1127.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1127.0.tgz", + "integrity": "sha512-Dv2TMWBshJ+tF6ahs2Sy5bh4Iabsd4GAQqVvE9XZmYmnoaVbpS2QKIKE/HRacc7bTtbjEEvP+laGzHvHlf1CiQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -362,147 +399,14 @@ "node": ">=20.0.0" } }, - "node_modules/@aws-sdk/eventstream-handler-node": { - "version": "3.972.34", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.34.tgz", - "integrity": "sha512-cTeVzpu1xEAkryTZBYhGwnQ6gOGyp8ZYZvmn0Sg/nI/ABmy/CRHHxPDJDUi9PxwxUtGGaatvfRUB3FCgT/rSWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-eventstream": { - "version": "3.972.29", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.29.tgz", - "integrity": "sha512-dlRzHCgyB8W6hLuDC5pcT5q+ziPt00n4QGgGBE17ucLVU4zMa6lsbuUdQ2Pm75Z5VA8GF+R/+SgrRcaTdIzSIQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/middleware-websocket": { - "version": "3.972.52", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.52.tgz", - "integrity": "sha512-vsPPM+nMbKJlUCFU+eoGZbdxdxDIAX9LbpjSXaR5Ufpmqgp8TdYQnoExhLu4T3umW/JIIPny1ydbhWidZZYokQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/fetch-http-handler": "^5.7.2", - "@smithy/signature-v4": "^5.6.12", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.997.44", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.44.tgz", - "integrity": "sha512-NhEgryjlBF9w38ZXqGymQV28IhkYa1mKhlbYnqIis57AYwWGVYfUPgg/qC2rLRqOUfblxx++irvju10kVTa8Vw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.977.9", - "@aws-sdk/signature-v4-multi-region": "^3.996.46", - "@aws-sdk/types": "^3.974.5", - "@smithy/core": "^3.33.3", - "@smithy/fetch-http-handler": "^5.7.2", - "@smithy/node-http-handler": "^4.11.3", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients/node_modules/@smithy/node-http-handler": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.0.tgz", - "integrity": "sha512-0mq1pHadfyXCYCqm2cNpbjNIT+fbaUpNxewZb/YNr2L0IrEVMOb8gM/Fl4K6XvHCW3uSNDFwPl/+iKm0bx9jYg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.46", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.46.tgz", - "integrity": "sha512-L+2xZTye/2T96f3lwCws0Zw6GG2JHZW9e8FpVgGBeeExSKyeoZ6CWRpBml/7DNiK/O26jrgPM9F+Ay8VkgzUWQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.974.5", - "@smithy/signature-v4": "^5.6.12", - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, "node_modules/@aws-sdk/types": { - "version": "3.974.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.5.tgz", - "integrity": "sha512-LkwLL2BLbC6wNNm4JaH9mbEqBMdOZCct6VAYqhdN4U1xrWM+fUJQEfbHwQgDypapOWTRtlk25akb5afM0P8CIQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.17.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.10", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.10.tgz", - "integrity": "sha512-ycwH6Zd2GhuSqdXX9ihbCjeGTB6xOJs+O3+Jb8/zDG9978XU80qs75dfkPJRMNKe5MvBZPuNeFpd4JZKPoUF4g==", + "version": "3.974.6", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.974.6.tgz", + "integrity": "sha512-v/clNZzZnDxGyvpHMOGpJKVXFAExJzUNAAjaWGdcx8QAcXLGwTaOkw33p5SHAi0YAioK32xB3hWwOekRVfmfKg==", "dev": true, "license": "Apache-2.0", "dependencies": { + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -510,13 +414,13 @@ } }, "node_modules/@aws-sdk/xml-builder": { - "version": "3.972.40", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.40.tgz", - "integrity": "sha512-wlFmCIGUlwF4zx/kncw+bmxTQh1HeSJq4mYV/V5cZUSJadDP3kXvGW8Rn21cimj/7y9ju+47oYWXi97vF7czaA==", + "version": "3.972.41", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.41.tgz", + "integrity": "sha512-ctjVSyCMegrWfXlx6VqzSBFI6UqmQ5ZlnfMhdLIiWmhoH8UAQxSCP5N3OpG7X3k4LnS7ou74C4mt20+bfTW2aQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.17.2", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -539,7 +443,6 @@ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", - "optional": true, "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", @@ -549,27 +452,436 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-validator-identifier": { "version": "7.29.7", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/runtime": { + "node_modules/@babel/helper-validator-option": { "version": "7.29.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", - "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.9", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.9.tgz", + "integrity": "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.5.15.tgz", + "integrity": "sha512-WZTW4slm/pdkh92K6t/3aEN++44JD1PQ7squ7RCMLI1flHGl43DVOIGXvCjHiZgBMO1V4uYxoFNtqrIA4qGuIQ==", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.5.15", + "@biomejs/cli-darwin-x64": "2.5.15", + "@biomejs/cli-linux-arm64": "2.5.15", + "@biomejs/cli-linux-arm64-musl": "2.5.15", + "@biomejs/cli-linux-x64": "2.5.15", + "@biomejs/cli-linux-x64-musl": "2.5.15", + "@biomejs/cli-win32-arm64": "2.5.15", + "@biomejs/cli-win32-x64": "2.5.15" + } + }, + "node_modules/@biomejs/cli-darwin-arm64": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.5.15.tgz", + "integrity": "sha512-BZVzFhJ/mUvTLMYbc9x6el0o2Uv5zP7bACYyhFb6fSc1giroXm7PL0a5KqMJ9F+/BnkYRjagD/ROSDVOZ6eapg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-darwin-x64": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.5.15.tgz", + "integrity": "sha512-V5Kw63V+fVGNFhFrizDxMbGXAzZCh8kYzJpy3GI6gTapWDg7bZK9oYC9CbVgQSEdTnupz0U5Efz8Ev+5vdBBXw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.5.15.tgz", + "integrity": "sha512-XaG7P7eeSLYETD3K9grfB0mQmpLQZjygdbsxekWBJAg4am79fGAtRpfRyNnTvXB2fZFNnJZG3nXm7HBat0+VUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-arm64-musl": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.5.15.tgz", + "integrity": "sha512-tGzZUTcJCV7tj5Adh/Gn6nR4MycqA3iohq2LEcrarAgRBkU0h0OKd2UzCRkOsOYOuUGaUJb4YmqRqyy2feGGSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.5.15.tgz", + "integrity": "sha512-xE4iEW/3LqlYj9GFgGrFsFSH51dEEpUbYWBfeOv0q87WkUDxK2o/HhreSb7qMJqck70RVM6Lg96hgedcAYcOkA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-linux-x64-musl": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.5.15.tgz", + "integrity": "sha512-IlxUcyxilVGPsE008x13pWdkTbU3nQpP2i9b5UrbOYj6goBkKsRX1XB7yJLcV+O1H9LBchIIm4adOaVLZBX0ZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-arm64": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.5.15.tgz", + "integrity": "sha512-2kPKzhNlm8C+Ru3GcO0Me2ODkCBLrVOUNuyi84RJyWDVKAA4+Kjj3jMjL938lF6BRfvltC4vB2nPY9SvN51Ovg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@biomejs/cli-win32-x64": { + "version": "2.5.15", + "resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.5.15.tgz", + "integrity": "sha512-yAzh4UqEImV6Hcy0zjUqNfJAsUhoD64FBQCdTG8Md/Z2wQeVP/ZmJ3XRYAs1g8J9fjTFyl2fCtc5FB5til47+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=14.21.3" + } + }, "node_modules/@borewit/text-codec": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", @@ -608,24 +920,44 @@ "node": ">= 20.12.0" } }, - "node_modules/@earendil-works/pi-ai": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.84.2.tgz", - "integrity": "sha512-6MzsrYIYNVlE7SfpbL2yYb67Qo58p/7Q+xWG1RZvoX1P80aRCHSod2/13aFpxkow1lPO2LEh3c495J0Gwmyjig==", + "node_modules/@earendil-works/pi-agent-core": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-1.3.0.tgz", + "integrity": "sha512-2jvpxZs47PFD5mSQb/yK0pGHJ5yO7Nu22u9mFjHbSLRgu62vZ9rupimDSIp4arnqOadyv0AWvYHtCSpFoJV7ag==", "dev": true, "license": "MIT", "dependencies": { - "@anthropic-ai/sdk": "0.91.1", - "@aws-sdk/client-bedrock-runtime": "3.1048.0", - "@earendil-works/pi-telemetry": "^0.84.2", - "@google/genai": "1.52.0", - "@opentelemetry/api": "1.9.0", - "@smithy/node-http-handler": "4.7.3", - "http-proxy-agent": "7.0.2", - "https-proxy-agent": "7.0.6", - "openai": "6.40.0", + "@earendil-works/pi-ai": "1.3.0", + "typebox": "1.3.27" + }, + "engines": { + "node": ">=22.19.0" + } + }, + "node_modules/@earendil-works/pi-agent-core/node_modules/typebox": { + "version": "1.3.27", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.27.tgz", + "integrity": "sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-ai": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-1.3.0.tgz", + "integrity": "sha512-KaBI8x7q7sPoFUXICNGD9efHzDG+oRNwVvkA9jdIsSQWH1RjFssOlEGsbLtewUPPgCrvmz0l2KTgSLXTyj3acw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@anthropic-ai/sdk": "0.129.0", + "@aws-sdk/client-bedrock-runtime": "3.1127.0", + "@earendil-works/pi-telemetry": "1.3.0", + "@google/genai": "2.21.0", + "@smithy/node-http-handler": "4.12.1", + "http-proxy-agent": "9.1.0", + "https-proxy-agent": "9.1.0", + "openai": "7.19.0", "partial-json": "0.1.7", - "typebox": "1.3.7" + "typebox": "1.3.27" }, "bin": { "pi-ai": "dist/cli.js" @@ -634,900 +966,63 @@ "node": ">=22.19.0" } }, + "node_modules/@earendil-works/pi-ai/node_modules/typebox": { + "version": "1.3.27", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.27.tgz", + "integrity": "sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@earendil-works/pi-codemode": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-codemode/-/pi-codemode-1.3.0.tgz", + "integrity": "sha512-2G9tmbsPhgzwfz7cmzJpbLSLqOx3eu22Gbzm+Wc3HqeZMjLQS4F3KfAabozVhv7uEjq3k2O1dZTHmhhWr2xbfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quickjs-wasi": "3.6.2" + }, + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/@earendil-works/pi-coding-agent": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-0.84.2.tgz", - "integrity": "sha512-l4E+B7hgXKWddRo8bC/eSue2aWZjEgJ9xIpf5p0Og+lq8a2TArCwJ0HCoCPCgaBP/tN4zbYH/wOwvx9pJpeLCA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-coding-agent/-/pi-coding-agent-1.3.0.tgz", + "integrity": "sha512-V/WoCjA2hQSe3QLWIUB24cTRdtEdeLiIzkDqU5RMXV6VWuD+KGZnzVBMhglwdnQTySkehZsjt8a3g6Jd2g4nnw==", "dev": true, - "hasShrinkwrap": true, "license": "MIT", "dependencies": { - "@earendil-works/pi-agent-core": "^0.84.2", - "@earendil-works/pi-ai": "^0.84.2", - "@earendil-works/pi-client": "^0.84.2", - "@earendil-works/pi-protocol": "^0.84.2", - "@earendil-works/pi-tui": "^0.84.2", + "@earendil-works/pi-agent-core": "1.3.0", + "@earendil-works/pi-ai": "1.3.0", + "@earendil-works/pi-codemode": "1.3.0", + "@earendil-works/pi-mcp": "1.3.0", + "@earendil-works/pi-tui": "1.3.0", "@silvia-odwyer/photon-node": "0.3.4", - "chalk": "5.6.2", + "brace-expansion": "5.0.12", + "chalk": "6.0.0", "cross-spawn": "7.0.6", "diff": "8.0.4", - "glob": "13.0.6", - "grok-mermaid": "0.2.2", + "grok-mermaid": "0.2.3", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", - "ignore": "7.0.5", + "ignore": "7.0.8", "jiti": "2.7.0", - "minimatch": "10.2.5", + "minimatch": "10.2.6", "proper-lockfile": "4.1.2", - "semver": "7.8.0", - "typebox": "1.3.7", - "undici": "8.9.0", + "quickjs-wasi": "3.6.2", + "semver": "7.8.5", + "typebox": "1.3.27", + "undici": "8.10.2", "yaml": "2.9.0" }, "bin": { - "pi": "dist/cli.js" - }, - "engines": { - "node": ">=22.19.0" - }, - "optionalDependencies": { - "@mariozechner/clipboard": "0.3.9" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@anthropic-ai/sdk": { - "version": "0.91.1", - "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.91.1.tgz", - "integrity": "sha512-LAmu761tSN9r66ixvmciswUj/ZC+1Q4iAfpedTfSVLeswRwnY3n2Nb6Tsk+cLPP28aLOPWeMgIuTuCcMC6W/iw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-schema-to-ts": "^3.1.1" - }, - "bin": { - "anthropic-ai-sdk": "bin/cli" - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" - }, - "peerDependenciesMeta": { - "zod": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/client-bedrock-runtime": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1048.0.tgz", - "integrity": "sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/credential-provider-node": "^3.972.42", - "@aws-sdk/eventstream-handler-node": "^3.972.16", - "@aws-sdk/middleware-eventstream": "^3.972.12", - "@aws-sdk/middleware-websocket": "^3.972.19", - "@aws-sdk/token-providers": "3.1048.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/core": { - "version": "3.974.11", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.974.11.tgz", - "integrity": "sha512-QpnINq5FZH6EOaDEkmHdT7eUunbvD27pDNQypaWjFyYz7Zl1q3UCMQErBZxpmfGfI7MvI2TlK8KTkgNpv8b1ug==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@aws-sdk/xml-builder": "^3.972.24", - "@aws/lambda-invoke-store": "^0.2.2", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.37.tgz", - "integrity": "sha512-/jpPvEh6f7ntmIzf7dNxoNX6Q8vt8UpesCjbW6mFfk4V1NW6bIy9qxcQ6WbA8As5yQhsZOe+xeNd4xHX8kdY2Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.972.39", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.39.tgz", - "integrity": "sha512-pIgTpisWyWg7X1bUbzSjuUYosYTD0Ghz2M0hkSTmb3a6i3qV3uU+NYJPI/E2XSC0HcsZh5rsLPzeXrkb2DS0Cg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.41.tgz", - "integrity": "sha512-u2tyjaxJJzW8UtW4SM1ZcPMDwO6y+kV+llvou+Adts0FAKyzes5jG4izQN+KX3yE8ZROpS5y1LJ//xL2iSf76w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/credential-provider-env": "^3.972.37", - "@aws-sdk/credential-provider-http": "^3.972.39", - "@aws-sdk/credential-provider-login": "^3.972.41", - "@aws-sdk/credential-provider-process": "^3.972.37", - "@aws-sdk/credential-provider-sso": "^3.972.41", - "@aws-sdk/credential-provider-web-identity": "^3.972.41", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/credential-provider-imds": "^4.3.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-login": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.41.tgz", - "integrity": "sha512-0LBitxXiAiaE5nlFPfpNIww/8FRY/I7WIndWsc9GmNFOM7cE1wNpVNQEGEk9Outg5l8xl+3vybxFyUy4l9q/LQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.972.42", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.42.tgz", - "integrity": "sha512-D4oon2zbqqsWOJUM99Gm3/ZyJ0IJvTXVN3PyloGb3kQEyI36fjCZheZj422lAgTWWd6TSHgiImLt3RIaLdv3dQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "^3.972.37", - "@aws-sdk/credential-provider-http": "^3.972.39", - "@aws-sdk/credential-provider-ini": "^3.972.41", - "@aws-sdk/credential-provider-process": "^3.972.37", - "@aws-sdk/credential-provider-sso": "^3.972.41", - "@aws-sdk/credential-provider-web-identity": "^3.972.41", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/credential-provider-imds": "^4.3.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.972.37", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.37.tgz", - "integrity": "sha512-7nVaHBUaWIddASYfVaA9O4D5ZVjewU3sCol9WqZPGfW0nR+0WqE0xHZnD/U2L33PlOB8KNXGKZ6wOES/QijKzg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.41.tgz", - "integrity": "sha512-IOWAWEHe5LkjSKkkUUX9ciV6Y1scHTsnfEkdt5yyC4Slrc7AGbkLPrpntjqh18ksJAMOaVhoBsO8p2WyTcY2wQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/token-providers": "3.1048.0", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.972.41", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.41.tgz", - "integrity": "sha512-mbACk9Yypa8nm4iGZLs0PofOXEcTDOUw6wDnsPXNDNSd2WNXs1tSo+6nc/fh0jLYdfVZThhBL98PHW4aXFsG5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/eventstream-handler-node": { - "version": "3.972.16", - "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.16.tgz", - "integrity": "sha512-yedpPgKftqjU5SlPFHfqWpOw6xSCRieWRG1euWOlXn4WJxt2VX92VprCa2PpSOXjVCAeK6dTjW9eJRXVig9yGA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-eventstream": { - "version": "3.972.12", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.12.tgz", - "integrity": "sha512-tHTHHCHNrq6XklQvlzHBDJG4Iuhh7NVPRdtmvP+nHFA+5sxPlIDzlAHHgfoYHGvT3NXP1yVP/L5c3opUn6T3Qg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/middleware-websocket": { - "version": "3.972.19", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.19.tgz", - "integrity": "sha512-mkEhOGYozqKQkbFaVrjwr0faiwwZza1v5/jSY6Tucm3bD+uKTazIUH/4Yo6aMnQD2ua2W9cMP6s8mvwTcjtqHw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/nested-clients": { - "version": "3.997.9", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.997.9.tgz", - "integrity": "sha512-jPR3rnmRI4hWYyzfmTGBr7NblMp8QYYeflHXba1H6+7CGrWVqWKQzaXFQ4qbExqPRsXN3T3L3JxFhr6aouXUGQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/signature-v4-multi-region": "^3.996.27", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/fetch-http-handler": "^5.4.2", - "@smithy/node-http-handler": "^4.7.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.996.27", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.996.27.tgz", - "integrity": "sha512-0Phbz4t6HI3D3skxvG2uI+VWU034/nSIw1T8d+FPzzQG9EQTrw94o9mOKO2Gv3n3Oc8P7JD7RAUxkoneLWv5Eg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/signature-v4": "^5.4.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/token-providers": { - "version": "3.1048.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1048.0.tgz", - "integrity": "sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "^3.974.11", - "@aws-sdk/nested-clients": "^3.997.9", - "@aws-sdk/types": "^3.973.8", - "@smithy/core": "^3.24.2", - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/types": { - "version": "3.973.8", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.8.tgz", - "integrity": "sha512-gjlAdtHMbtR9X5iIhVUvbVcy55KnznpC6bkDUWW9z915bi0ckdUr5cjf16Kp6xq0bP5HBD2xzgbL9F9Quv5vUw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.14.1", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/util-locate-window": { - "version": "3.965.5", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", - "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws-sdk/xml-builder": { - "version": "3.972.24", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.24.tgz", - "integrity": "sha512-V8z5YcDPfsvzrBlj0xR1vhRtocblhYbqdreCJB/voGd4Sr5zjNAeWxexbnqVtskTJe0vFb5KMqbSL++ePl+zRw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@nodable/entities": "2.1.0", - "@smithy/types": "^4.14.1", - "fast-xml-parser": "5.7.3", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=20.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@aws/lambda-invoke-store": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", - "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-agent-core": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-agent-core/-/pi-agent-core-0.84.2.tgz", - "dev": true, - "license": "MIT", - "dependencies": { - "@earendil-works/pi-ai": "^0.84.2", - "@earendil-works/pi-telemetry": "^0.84.2", - "diff": "8.0.4", - "ignore": "7.0.5", - "typebox": "1.3.7", - "yaml": "2.9.0" + "pi": "dist/bundle/cli.js" }, "engines": { "node": ">=22.19.0" } }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-ai": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-ai/-/pi-ai-0.84.2.tgz", - "dev": true, - "license": "MIT", - "dependencies": { - "@anthropic-ai/sdk": "0.91.1", - "@aws-sdk/client-bedrock-runtime": "3.1048.0", - "@earendil-works/pi-telemetry": "^0.84.2", - "@google/genai": "1.52.0", - "@opentelemetry/api": "1.9.0", - "@smithy/node-http-handler": "4.7.3", - "http-proxy-agent": "7.0.2", - "https-proxy-agent": "7.0.6", - "openai": "6.40.0", - "partial-json": "0.1.7", - "typebox": "1.3.7" - }, - "bin": { - "pi-ai": "dist/cli.js" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-client": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-client/-/pi-client-0.84.2.tgz", - "dev": true, - "license": "MIT", - "dependencies": { - "@earendil-works/pi-protocol": "^0.84.2" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-protocol": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-protocol/-/pi-protocol-0.84.2.tgz", - "dev": true, - "license": "MIT", - "dependencies": { - "typebox": "1.3.7" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-telemetry": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-0.84.2.tgz", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@earendil-works/pi-tui": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.84.2.tgz", - "dev": true, - "license": "MIT", - "dependencies": { - "get-east-asian-width": "1.6.0", - "marked": "18.0.5" - }, - "engines": { - "node": ">=22.19.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@google/genai": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", - "dev": true, - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "google-auth-library": "^10.3.0", - "p-retry": "^4.6.2", - "protobufjs": "^7.5.4", - "ws": "^8.18.0" - }, - "engines": { - "node": ">=20.0.0" - }, - "peerDependencies": { - "@modelcontextprotocol/sdk": "^1.25.2" - }, - "peerDependenciesMeta": { - "@modelcontextprotocol/sdk": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.9.tgz", - "integrity": "sha512-ABnA53mdfkGZwOFUdZNv2S0CWGO/EIuPj8Vv9xmBFmSYg/qFc7ihO6q5FcQjvoE67kZpWkEc4AhD6B/os04yuA==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 10" - }, - "optionalDependencies": { - "@mariozechner/clipboard-darwin-arm64": "0.3.9", - "@mariozechner/clipboard-darwin-universal": "0.3.9", - "@mariozechner/clipboard-darwin-x64": "0.3.9", - "@mariozechner/clipboard-linux-arm64-gnu": "0.3.9", - "@mariozechner/clipboard-linux-arm64-musl": "0.3.9", - "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.9", - "@mariozechner/clipboard-linux-x64-gnu": "0.3.9", - "@mariozechner/clipboard-linux-x64-musl": "0.3.9", - "@mariozechner/clipboard-win32-arm64-msvc": "0.3.9", - "@mariozechner/clipboard-win32-x64-msvc": "0.3.9" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-darwin-arm64": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.9.tgz", - "integrity": "sha512-BfgV7vCEWZwJwZJw03r6bP5+tf0iI/ANuQYCxi9RNn7FrWB3yzGuMKCrNLRl6V761vXRdL8+OqZ0wd4TqlsNOQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-darwin-universal": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.9.tgz", - "integrity": "sha512-BGGR4iA9Z2shAjI65eI5xtyb3LYNlDW9X3gxKxDbqtbnREohsrqznov6zpKoIrsRWpzlYVEdKphS7ksJ0/ndSQ==", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-darwin-x64": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.9.tgz", - "integrity": "sha512-4kURmCbS6nt8uYhtmWpUcJWyPHfmAr5dTpXD1nO3pIfa+TSQ9DbrGOYCKH+aEFW47XhQ4Vp8ZTszie+wfFvDKg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-arm64-gnu": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.9.tgz", - "integrity": "sha512-g59OkUGP2DDfCOIKypHeYgv2M55u/cKvXa5dSxFbEJ34XvIQMdcVmpKCkGUro3ZgefXiGVdwguvTMQGpHWzIXw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-arm64-musl": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.9.tgz", - "integrity": "sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.9.tgz", - "integrity": "sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-x64-gnu": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.9.tgz", - "integrity": "sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-linux-x64-musl": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.9.tgz", - "integrity": "sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-win32-arm64-msvc": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.9.tgz", - "integrity": "sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@mariozechner/clipboard-win32-x64-msvc": { - "version": "0.3.9", - "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.9.tgz", - "integrity": "sha512-ihQC3EufqEY81vhXBgVBtK4prL+wc62zJsSvxrgz7K1hsdt6OObz6v9p3Rn1OG3GJksTTKMJF0u/guMISHPhSA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@nodable/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/nodable" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/aspromise": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", - "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/codegen": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz", - "integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/eventemitter": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz", - "integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/fetch": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz", - "integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/float": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", - "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/path": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", - "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/pool": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", - "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@protobufjs/utf8": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.1.tgz", - "integrity": "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", @@ -1535,471 +1030,6 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/core": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.24.3.tgz", - "integrity": "sha512-Ep/7tPamGY8mgESE3LyLKtxJyy6U52WWAqr/3wial47Sj4u3PiIF73AOGI27UyLy9duTkhZbgzodOfLV4TduZg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/credential-provider-imds": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.3.3.tgz", - "integrity": "sha512-I2Bti0DKFo2IJyN28ijCsx51BAumEYR4/1yZ1FXyBygy9MqbnMqCev4JPth/MbpRfBSRAX35hITSnAdJRo1u5w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/fetch-http-handler": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.4.3.tgz", - "integrity": "sha512-F+DRf8IJazRJgYog2A/yJK7eYVc0rqTlRzO+5ZxjJd4WkZoKz0IJRncf7G6t1pdVT3kryJcwuTFhN1c5m6N47A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/node-http-handler": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/signature-v4": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.4.3.tgz", - "integrity": "sha512-53+75QuPl6DL+ct6vVEB51FDO5oulXr20TPV46VvJZg76lIlXNWfxi8j+G2V/t0I2qxCBOa3vX/8bmjrpFVo9g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/types": { - "version": "4.14.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.14.2.tgz", - "integrity": "sha512-P+otAxbV4CqBybp7EkcJCrig63yE2E7PuNVOmilVMRcx/O+QDzGULTrKsq4DV13gSfak9ObPrWaHl/9bL5YcWw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/@types/node": { - "version": "22.19.19", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz", - "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "18 || 20 || >=22" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/bignumber.js": { - "version": "9.3.1", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", - "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/bowser": { - "version": "2.14.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", - "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/brace-expansion": { - "version": "5.0.9", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", - "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/chalk": { - "version": "5.6.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", - "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/diff": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", - "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/fast-xml-builder": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", - "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "path-expression-matcher": "^1.5.0", - "xml-naming": "^0.1.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/fast-xml-parser": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.7.3.tgz", - "integrity": "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "@nodable/entities": "^2.1.0", - "fast-xml-builder": "^1.1.7", - "path-expression-matcher": "^1.5.0", - "strnum": "^2.2.3" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/gaxios": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", - "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "node-fetch": "^3.3.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/gcp-metadata": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", - "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "gaxios": "^7.0.0", - "google-logging-utils": "^1.0.0", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/get-east-asian-width": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", - "integrity": "sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/glob": { - "version": "13.0.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", - "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "minimatch": "^10.2.2", - "minipass": "^7.1.3", - "path-scurry": "^2.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/google-auth-library": { - "version": "10.6.2", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", - "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^7.1.4", - "gcp-metadata": "8.1.2", - "google-logging-utils": "1.1.3", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/google-logging-utils": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", - "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -2007,16 +1037,6 @@ "dev": true, "license": "ISC" }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/grok-mermaid": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/grok-mermaid/-/grok-mermaid-0.2.2.tgz", - "integrity": "sha512-XcJEP5dDC8liHBh52mlLjU18fNvu1ckFsu0QpIG3+APZ270fsj9wxpiA6cOURmbUEuoMVgjbC2+UYgTdCqqgzA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18" - } - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", @@ -2040,115 +1060,6 @@ "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/jiti": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", - "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "lib/jiti-cli.mjs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/json-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/json-schema-to-ts": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", - "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "ts-algebra": "^2.0.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/jws": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", - "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jwa": "^2.0.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/long": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", - "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", - "dev": true, - "license": "Apache-2.0" - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/lru-cache": { "version": "11.4.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.4.0.tgz", @@ -2159,182 +1070,6 @@ "node": "20 || >=22" } }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/marked": { - "version": "18.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", - "dev": true, - "license": "MIT", - "bin": { - "marked": "bin/marked.js" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/minipass": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", - "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", - "dev": true, - "license": "BlueOak-1.0.0", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/openai": { - "version": "6.40.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.40.0.tgz", - "integrity": "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==", - "dev": true, - "license": "Apache-2.0", - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.25 || ^4.0" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", - "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/p-retry/node_modules/@types/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/partial-json": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", - "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/path-expression-matcher": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", - "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/path-scurry": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", - "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", @@ -2357,97 +1092,6 @@ "node": ">= 4" } }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/protobufjs": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.5.tgz", - "integrity": "sha512-/FPD0nUc9jH6rfFjji9IBqOz4pcSE3CsT1m7Ep6Mdb0LxSUMj8hgl6GomOvZzpNpAqqGaXA0P3VSrZLFzIhQrw==", - "dev": true, - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "@protobufjs/aspromise": "^1.1.2", - "@protobufjs/base64": "^1.1.2", - "@protobufjs/codegen": "^2.0.5", - "@protobufjs/eventemitter": "^1.1.1", - "@protobufjs/fetch": "^1.1.1", - "@protobufjs/float": "^1.0.2", - "@protobufjs/path": "^1.1.2", - "@protobufjs/pool": "^1.1.0", - "@protobufjs/utf8": "^1.1.1", - "@types/node": ">=13.7.0", - "long": "^5.3.2" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", @@ -2455,141 +1099,30 @@ "dev": true, "license": "ISC" }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/strnum": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.3.0.tgz", - "integrity": "sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ts-algebra": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", - "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, "node_modules/@earendil-works/pi-coding-agent/node_modules/typebox": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.7.tgz", - "integrity": "sha512-meKuifc33Pccx0O6PdIzYMq3Og8zvP4TIi/a+Bw3AEMZMxOD0+RHGQvpglEe6Zdy3wZ8nqn/j95h8LUZLk/6Hg==", + "version": "1.3.27", + "resolved": "https://registry.npmjs.org/typebox/-/typebox-1.3.27.tgz", + "integrity": "sha512-zu+jc1pcy4UiNThxikUr36f0Rybk9PEeCg/NE6adeWr/SKsdNO4EzZHYRDlv2YCVAfj3Odq3dESSo/jNyoBXzA==", "dev": true, "license": "MIT" }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/undici": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-8.9.0.tgz", - "integrity": "sha512-aWZpUj7XoGonMClx4gdDRfgBjqeA+F473aDmROQQbM9n6PRfK/u1q/a0X4wMTgcHfT8H6fpbt98PFuDUwFg2YA==", + "node_modules/@earendil-works/pi-mcp": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-mcp/-/pi-mcp-1.3.0.tgz", + "integrity": "sha512-lbktLplmdGamM2t460Q0ziNEfpUEHDTVNf79g/AialJ3XZ+SHgNqa/OJXRR+fU+GV/l+gBhRPcyP8XkQlv8Avw==", "dev": true, "license": "MIT", + "dependencies": { + "cross-spawn": "7.0.6" + }, "engines": { "node": ">=22.19.0" } }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/xml-naming": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", - "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@earendil-works/pi-coding-agent/node_modules/yaml": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", - "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - }, - "funding": { - "url": "https://github.com/sponsors/eemeli" - } - }, "node_modules/@earendil-works/pi-telemetry": { - "version": "0.84.4", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-0.84.4.tgz", - "integrity": "sha512-8e2CuxM+ht+hedQXTZmi5JVl6/xDK9RpSDL2+MbITevKYQhMZ/z6lJOTFgox3HQyGxO8mOZEtYGVeQNaD4OzqA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-telemetry/-/pi-telemetry-1.3.0.tgz", + "integrity": "sha512-ERGZr+aXjNJzoUbB7ZzHVH3g5otzMHjHLs2Fyizm12By5X+gBEhod9cJXFOk13S7M8a29Am+Mq21zm73mRJFLg==", "dev": true, "license": "MIT", "engines": { @@ -2597,14 +1130,14 @@ } }, "node_modules/@earendil-works/pi-tui": { - "version": "0.84.2", - "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-0.84.2.tgz", - "integrity": "sha512-ds2TLihOnM5sLJB3VpXV6y0uR5efVuHf4MN7yDpsty6hA2DUO/EDVzjp/0od0G2JslzVLMjT8T8zavtxVb+qbg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@earendil-works/pi-tui/-/pi-tui-1.3.0.tgz", + "integrity": "sha512-AlKvEwEWyVtnuw7SVb4N/mUhlfIohyFslUNWarZVdLHHSdt3ca5EhMLwTzblBpDpaofSz+f+tAHie6CfxVrWKA==", "dev": true, "license": "MIT", "dependencies": { "get-east-asian-width": "1.6.0", - "marked": "18.0.5" + "marked": "18.0.11" }, "engines": { "node": ">=22.19.0" @@ -3248,9 +1781,9 @@ } }, "node_modules/@google/genai": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.52.0.tgz", - "integrity": "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q==", + "version": "2.21.0", + "resolved": "https://registry.npmjs.org/@google/genai/-/genai-2.21.0.tgz", + "integrity": "sha512-+PDtco2/Z0ONdzCGekCoCT+O1VJS9xJQNN4XzQpXG/t3El/SWWMkCWlFRO1KmivOHPa4Q0VjUYu1HBKCZ/v33Q==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -3288,6 +1821,16 @@ "hono": "^4" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", @@ -3388,16 +1931,6 @@ "resolved": "pi-minimal-footer", "link": true }, - "node_modules/@opentelemetry/api": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", - "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@oxc-project/types": { "version": "0.144.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.144.0.tgz", @@ -4126,14 +2659,20 @@ "win32" ] }, + "node_modules/@sinclair/typebox": { + "version": "0.34.52", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.52.tgz", + "integrity": "sha512-XiMQh7qqVlxZzcVD+kkGMNGMzcTrDMLWI7S4x7z1MkCkbDPrekpZXEUK0eZqZFMuHQg2a2DZOcDIh9o5v3Gonw==", + "license": "MIT" + }, "node_modules/@smithy/core": { - "version": "3.33.3", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.33.3.tgz", - "integrity": "sha512-CsOeKq/9kA3y6VJHt+/+VTCtBaxJ4OTFpgrjIUhPpDIKxBci1k2bJaQASF2h/ELWrulGp+t97DZ0mevfAD8idg==", + "version": "3.36.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.36.1.tgz", + "integrity": "sha512-WC5W1Jrsfxu1y6LeqXMie7N14qmcdafij/Haye4feVlyMMXLGpYxBeOX6Ee1iRJbDzGG5LVman8U1rrt9MYFHA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.17.2", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -4156,42 +2695,29 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.7.2.tgz", - "integrity": "sha512-nZyWTmSpJEXl6VtWVMBJve/7x12DZu6sIX1z1a+ZMaHlQQRs9Zpu6NbTe/gmxYXVRpkjxyDYpZ5gx2IM6f/Wkw==", + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.8.0.tgz", + "integrity": "sha512-ycSJu3tFAQ4v04CBB0agqFMVsSQ1iG3yw+SpgxRqKfaURpQD4CZ8Wn0zPMmSnOuTpTh65Vz+EA0rMrw089wvkA==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.33.2", - "@smithy/types": "^4.17.2", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, - "node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@smithy/node-http-handler": { - "version": "4.7.3", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.7.3.tgz", - "integrity": "sha512-/jPhevcTFPMVl6KNjbaI47iOg1zxC7IsnX4PQDGVZKMFceOXtB8IEYaB7a9VvkP/3oC60WzTeKocvSI7vLT0vA==", + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.12.1.tgz", + "integrity": "sha512-ThMkboGeONWXAelq9FvGsuJC4rOi+qyC4/zhUF58xYpxUg5sQKx2VXZYJmtNjr4dSuBJ1HeJXETQILCz3wOHvw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.24.3", - "@smithy/types": "^4.14.2", + "@smithy/core": "^3.33.3", + "@smithy/types": "^4.18.0", "tslib": "^2.6.2" }, "engines": { @@ -4199,14 +2725,14 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.3.tgz", - "integrity": "sha512-7ImGm+FkHRLcBaRttIAMZ6bzJZWb2cJGoYjq46F2UjycujWzrL9GEN9h4w7eQyXJYnltrUhxbbieBAIRrdqpow==", + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.7.4.tgz", + "integrity": "sha512-tHy0K0VtqNd5Y7Y41h0a0Lhh0L1GzC08dTWg0F7vRJWFtTENg7IZikf3wQkanYIRdb7ngoIPMTmqgUi401fEeQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.33.3", - "@smithy/types": "^4.17.2", + "@smithy/core": "^3.35.0", + "@smithy/types": "^4.19.0", "tslib": "^2.6.2" }, "engines": { @@ -4214,9 +2740,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.17.2.tgz", - "integrity": "sha512-FOKpVZob9MPTn2znRzGrnsMHv7BOsKVw3XiP/cOyYLDVZ9qKp4nifIiSCuUU/fIj5Vu0UOAxCFr+qRAtG0NUkA==", + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.19.0.tgz", + "integrity": "sha512-r7jh49VJxGerfAcTQA6gXcKc+98zOp/tqRwzYjgOE+iSQsP6cEU1hq2QzbuipmP68QtYdY9wKEhiCQZIzHgZ4Q==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -4226,38 +2752,17 @@ "node": ">=18.0.0" } }, - "node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/@smoose/pi-notify": { "resolved": "pi-notify", "link": true }, + "node_modules/@stablelib/base64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@stablelib/base64/-/base64-1.0.1.tgz", + "integrity": "sha512-1bnPQqSxSuc3Ii6MhBysoWCg58j97aUjuCSZrGSmDxNqtytIi0k8utUenAwTZN4V5mXXYGsVUI9zeBqy+jBOSQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -4265,6 +2770,10 @@ "dev": true, "license": "MIT" }, + "node_modules/@tintinweb/pi-subagents": { + "resolved": "pi-subagents", + "link": true + }, "node_modules/@tokenizer/inflate": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", @@ -4340,6 +2849,31 @@ "dev": true, "license": "MIT" }, + "node_modules/@vitest/coverage-istanbul": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@vitest/coverage-istanbul/-/coverage-istanbul-4.1.12.tgz", + "integrity": "sha512-f98TiCqF7IriBQEpm6usi56wyD4G1cRYabI15f3Jm7R355q9tlwqJ0j1oPf+tWZ7D0LPnCh3dU70duRt10erHg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.0", + "@istanbuljs/schema": "^0.1.3", + "@jridgewell/gen-mapping": "^0.3.13", + "@jridgewell/trace-mapping": "0.3.31", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.2.0", + "magicast": "^0.5.2", + "obug": "^2.1.1", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.12" + } + }, "node_modules/@vitest/expect": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.10.tgz", @@ -4644,13 +3178,13 @@ } }, "node_modules/agent-base": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", - "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-9.0.0.tgz", + "integrity": "sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==", "dev": true, "license": "MIT", "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/ajv": { @@ -4717,6 +3251,16 @@ "node": ">=12" } }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", @@ -4737,6 +3281,19 @@ ], "license": "MIT" }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.30.tgz", + "integrity": "sha512-YJs6Pz17RMNv8HoyKREtCY38E3BV1nPkWVmIb+XN1uHC1Vqt3KZCH/uJH9ZKRoiKb/icZ6tvehyee6RZNbFfRA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", @@ -4834,6 +3391,53 @@ "dev": true, "license": "MIT" }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.29.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz", + "integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.29", + "caniuse-lite": "^1.0.30001816", + "electron-to-chromium": "^1.5.459", + "node-releases": "^2.0.59", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -4912,6 +3516,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/caniuse-lite": { + "version": "1.0.30001817", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001817.tgz", + "integrity": "sha512-uZ1JG80BwhtMalIAGPxPfmNhHyd/B/nTdr3hITWlRamSyAdUStP1Gop01e7SD5q6J90ixJvWDbvmiFVfDgn4bw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", @@ -4922,6 +3547,19 @@ "node": ">=18" } }, + "node_modules/chalk": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-6.0.0.tgz", + "integrity": "sha512-2uNTXIuTTxk7ciZgAU1BQcgnchcG0xXnrs6jzkQfj9SsRa9M2s5zE8WT96hS6KmG4MzWHSrvH43DF1m4XRkrFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", @@ -5038,6 +3676,25 @@ "node": ">=10.0.0" } }, + "node_modules/croner": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", + "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", + "funding": [ + { + "type": "other", + "url": "https://paypal.me/hexagonpp" + }, + { + "type": "github", + "url": "https://github.com/sponsors/hexagon" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -5166,6 +3823,13 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/electron-to-chromium": { + "version": "1.5.462", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.462.tgz", + "integrity": "sha512-9qpCBlXPy5v7n1Q7WTO5rW2StI4WNQhl96HpgXRZLPlNoXAD0LbsQUflovQYAO44Yia073ISusqohncvGSMbGA==", + "dev": true, + "license": "ISC" + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -5263,6 +3927,16 @@ "@esbuild/win32-x64": "0.28.2" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", @@ -5403,6 +4077,13 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-sha256": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-sha256/-/fast-sha256-1.3.0.tgz", + "integrity": "sha512-n11RGP/lrWEFI/bWdygLxhI+pVeo1ZYIVwvvPkW7azl/rOy+F3HYRZ2K5zeE9mmkhQppyv9sQFx0JM9UabnpPQ==", + "dev": true, + "license": "Unlicense" + }, "node_modules/fast-string-truncated-width": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/fast-string-truncated-width/-/fast-string-truncated-width-3.0.3.tgz", @@ -5625,6 +4306,30 @@ "node": ">=18" } }, + "node_modules/gaxios/node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/gaxios/node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/gcp-metadata": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", @@ -5640,6 +4345,16 @@ "node": ">=18" } }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/get-east-asian-width": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.6.0.tgz", @@ -5736,6 +4451,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/grok-mermaid": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/grok-mermaid/-/grok-mermaid-0.2.3.tgz", + "integrity": "sha512-/4KopAbsjvuRP9MdPtlDjOHUmUVEohOX73JNcsWpzAtFxh+bq5+Dhb6gzvRieLDwIPQIR3/vy8V1NNTuz4Zsmg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -5769,6 +4504,13 @@ "node": ">=16.9.0" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -5790,31 +4532,33 @@ } }, "node_modules/http-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", - "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-9.1.0.tgz", + "integrity": "sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.0", - "debug": "^4.3.4" + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-9.1.0.tgz", + "integrity": "sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" + "agent-base": "9.0.0", + "debug": "^4.3.4", + "proxy-agent-negotiate": "1.1.0" }, "engines": { - "node": ">= 14" + "node": ">= 20" } }, "node_modules/iconv-lite": { @@ -5853,6 +4597,16 @@ ], "license": "BSD-3-Clause" }, + "node_modules/ignore": { + "version": "7.0.8", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.8.tgz", + "integrity": "sha512-YYNsSlXBjMk92SKnkwvB5LOVSa6OznlFUGcsvrFgNJbJCd0M1XKeFVRc8ZByeCqz32FivYNHJVooLmdqrmvp/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -5895,6 +4649,45 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jiti": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", @@ -5918,8 +4711,20 @@ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, "license": "MIT", - "optional": true + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } }, "node_modules/json-bigint": { "version": "1.0.0", @@ -5957,6 +4762,19 @@ "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", "license": "BSD-2-Clause" }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", @@ -6260,6 +5078,16 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -6270,10 +5098,38 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/marked": { - "version": "18.0.5", - "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.5.tgz", - "integrity": "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==", + "version": "18.0.11", + "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.11.tgz", + "integrity": "sha512-HnslJfsZkRPBDJRHvVtAaWlZHEpSu7u8LgQuJCELjRKuWR+hpq4A7sLq3p8HaI9ypVoXDXxV34CsQJEe1+J5Aw==", "dev": true, "license": "MIT", "bin": { @@ -6354,6 +5210,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -6488,6 +5360,16 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-releases": { + "version": "2.0.59", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.59.tgz", + "integrity": "sha512-5qr2okC4ItAI0hG5SaV0HoYMpnM/Y6+Oqr7AmPYy/2uEoGvS+GbSKLeRxSS1uAdu59yz0ouzcuXievSgg///WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6545,16 +5427,35 @@ } }, "node_modules/openai": { - "version": "6.40.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-6.40.0.tgz", - "integrity": "sha512-MWtTjd/gQt4jpbji61NTgFWJLoY/PdRJ6wG9/ZDRMYNMlBKrCrSlkLI+KgHP1vR1qT6LKSAyAqIxno6lcK9JiA==", + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/openai/-/openai-7.19.0.tgz", + "integrity": "sha512-MX2s3u2L5racTO0CC/SWpCOasJQBCJrqLKXK+l82cAhdeF8mPMBEe/gxMm0ZFa2xpKpOFLRjxv5afYEZbBXmbQ==", "dev": true, "license": "Apache-2.0", + "engines": { + "node": ">=22.0.0" + }, "peerDependencies": { - "ws": "^8.18.0", + "@aws-sdk/credential-provider-node": ">=3.972.0 <4", + "@smithy/hash-node": ">=4.3.0 <5", + "@smithy/signature-v4": ">=5.4.0 <6", + "undici": ">=5 <9", + "ws": "^8.21.0", "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { + "@aws-sdk/credential-provider-node": { + "optional": true + }, + "@smithy/hash-node": { + "optional": true + }, + "@smithy/signature-v4": { + "optional": true + }, + "undici": { + "optional": true + }, "ws": { "optional": true }, @@ -6807,6 +5708,24 @@ "node": ">= 0.10" } }, + "node_modules/proxy-agent-negotiate": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-agent-negotiate/-/proxy-agent-negotiate-1.1.0.tgz", + "integrity": "sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "peerDependencies": { + "kerberos": "^2.0.0" + }, + "peerDependenciesMeta": { + "kerberos": { + "optional": true + } + } + }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", @@ -6833,6 +5752,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quickjs-wasi": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/quickjs-wasi/-/quickjs-wasi-3.6.2.tgz", + "integrity": "sha512-FCqGtGOrMgzUiIrMNMA2YnsOxCNwo31dzqXvclXUC6xeT35NJLKXQJsvbeCTjvoFAwZgEAPg8U6+KAPDGXn8Mg==", + "dev": true, + "license": "MIT" + }, "node_modules/range-parser": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.3.0.tgz", @@ -7312,6 +6238,17 @@ "dev": true, "license": "MIT" }, + "node_modules/standardwebhooks": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/standardwebhooks/-/standardwebhooks-1.1.1.tgz", + "integrity": "sha512-bCbX9ZEyFkWPsRz7Bl3NuQUJohmwGSev/yhr7vhaGPlc4AfIrspIRa6cPTBuI1ItmrTDJ4d/S2hCsfe4+vQGnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@stablelib/base64": "^1.0.0", + "fast-sha256": "^1.3.0" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -7368,6 +6305,19 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar-fs": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", @@ -7631,6 +6581,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/undici": { + "version": "8.10.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.2.tgz", + "integrity": "sha512-/y4/bH9YNU5hi9NIrpOuvGXFcxrj3CMrV+/AYpowAYTpHn8gX/XPFjNy766FPoYY0miQhdW977JFWKGNhBdwyQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22.19.0" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -7647,6 +6607,37 @@ "node": ">= 0.8" } }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7919,9 +6910,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.21.3", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", - "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.0.tgz", + "integrity": "sha512-Ydggc987+RO0AnWtZ/7Wq9FtNvcrL1b/RO0ud9mWjUPgDrsAAwQSF51sm2hm1XofbU/4jkpGEsLFsZZxU+1DOg==", "dev": true, "license": "MIT", "engines": { @@ -7949,6 +6940,29 @@ "cuint": "^0.2.2" } }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/zod": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", @@ -8028,9 +7042,9 @@ "strip-ansi": "7.2.0" }, "devDependencies": { - "@earendil-works/pi-ai": "0.84.2", - "@earendil-works/pi-coding-agent": "0.84.2", - "@earendil-works/pi-tui": "0.84.2", + "@earendil-works/pi-ai": "latest", + "@earendil-works/pi-coding-agent": "latest", + "@earendil-works/pi-tui": "latest", "@types/better-sqlite3": "7.6.13", "tsx": "4.23.15", "typebox": "1.3.7", @@ -8068,7 +7082,7 @@ "version": "0.1.1-my-pi.1", "license": "MIT", "devDependencies": { - "@earendil-works/pi-coding-agent": "0.84.2", + "@earendil-works/pi-coding-agent": "latest", "@types/node": "24.10.13", "typescript": "6.0.3" }, @@ -8089,9 +7103,9 @@ "zod": "4.4.3" }, "devDependencies": { - "@earendil-works/pi-ai": "0.84.2", - "@earendil-works/pi-coding-agent": "0.84.2", - "@earendil-works/pi-tui": "0.84.2", + "@earendil-works/pi-ai": "latest", + "@earendil-works/pi-coding-agent": "latest", + "@earendil-works/pi-tui": "latest", "@types/node": "24.10.13", "rollup": "4.62.2", "rollup-plugin-dts": "6.4.1", @@ -8130,6 +7144,77 @@ "@gotgenes/pi-permission-system": "*" } }, + "pi-subagents": { + "name": "@tintinweb/pi-subagents", + "version": "0.18.0", + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.49", + "croner": "^10.0.1", + "nanoid": "^5.1.16" + }, + "devDependencies": { + "@biomejs/biome": "^2.4.14", + "@earendil-works/pi-ai": "latest", + "@earendil-works/pi-coding-agent": "latest", + "@earendil-works/pi-tui": "latest", + "@types/node": "^25.5.0", + "@vitest/coverage-istanbul": "^4.1.10", + "typescript": "^6.0.0", + "vitest": "^4.0.18" + }, + "peerDependencies": { + "@earendil-works/pi-ai": ">=0.81.0", + "@earendil-works/pi-coding-agent": ">=0.81.0", + "@earendil-works/pi-tui": ">=0.81.0" + }, + "peerDependenciesMeta": { + "@earendil-works/pi-ai": { + "optional": true + }, + "@earendil-works/pi-coding-agent": { + "optional": true + }, + "@earendil-works/pi-tui": { + "optional": true + } + } + }, + "pi-subagents/node_modules/@types/node": { + "version": "25.9.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.9.tgz", + "integrity": "sha512-b4e2xxj/yMeT2hNlD6x7zNFiQK2Dlmkb9CQ6v3/1G2RezKN4YS2lcEv8lzGm1mhs4f2lM4yNAMI/zUDBZBBt7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "pi-subagents/node_modules/nanoid": { + "version": "5.1.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.16.tgz", + "integrity": "sha512-kVrnsrJqMR8+oLJnGEmSWw9BivK5mt7H3FZatVRjrc5wGqFYuBxX1yG7+A7Gi5AefkX6t/oCkizcQgpu0cY1dQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, + "pi-subagents/node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, "pi-tool-search": { "version": "0.3.6", "license": "MIT", diff --git a/package.json b/package.json index e1f61f7..f2c1621 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,7 @@ "./pi-minimal-footer/index.ts", "./pi-notify/src/index.ts", "./extensions/permission-system.ts", + "./extensions/subagents.ts", "./pi-ssh/index.ts", "./pi-ask-user/index.ts", "./pi-worktree/index.ts", @@ -76,6 +77,7 @@ "@gotgenes/pi-permission-system": "file:./pi-permission-system", "@ogulcancelik/pi-minimal-footer": "file:./pi-minimal-footer", "@smoose/pi-notify": "file:./pi-notify", + "@tintinweb/pi-subagents": "file:./pi-subagents", "context-mode": "1.0.169", "jiti": "2.7.0", "pi-ask-user": "file:./pi-ask-user", diff --git a/pi-hermes-memory/CHANGELOG.md b/pi-hermes-memory/CHANGELOG.md index 6c19638..a6ce359 100644 --- a/pi-hermes-memory/CHANGELOG.md +++ b/pi-hermes-memory/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +- **Subagent (pi-subagents child) sessions are read-only**: sessions whose `systemPromptOptions.customPrompt` carries the `` identity tag (detected in `before_agent_start`, tracked per `sessionManager`) get a fixed error result from `memory_add`, `memory_replace`, `memory_remove`, and `skill_manage` — no writes — and skip background review, correction detection, compact/shutdown flush, live and shutdown session indexing, and the write side of initialization (markdown↔SQLite sync, legacy migration, pruning, backfill). `memory_search` and `session_search` stay fully usable. Sessions started from a tag-leading rendered prompt are detected as early as `session_start`; otherwise the authoritative `customPrompt` check at `before_agent_start` applies before any turn-level work. + - **Opt-in chunked subprocess consolidation** (`consolidationChunking`, default `false`): stores above `consolidationChunkChars` (new, default `4000`) consolidate in bounded rounds — a head slice of entries per child run, reloading from disk between rounds, so a killed run resumes from partial progress instead of restarting. The rounds share one overall time budget (`consolidationTimeoutMs`, same worst case as the single call this replaces), the loop stops at the target's capacity goal (failure memory is a 2× tier and is no longer over-merged down to the slice size), a slice that cannot shrink is skipped rather than blocking later slices, A store that fits one prompt runs a single unscoped decisive round with the whole remaining store; a child exiting 0 without shrinking is reported as an honest partial instead of silent success, and an over-chunk store already within its capacity goal is a clean no-op. Partial runs report `partial: true` with the reason and are surfaced by the warning path, the store's add-retry error, and `/memory-consolidate`; disappearances outside a round's slice (rogue child deletions, legitimate cross-slice dedup, or concurrent writers — indistinguishable to the parent) are detected and reported rather than resurrected, because re-adding through the cap-enforcing add path cannot converge and resurrection ping-pongs across triggers. An over-chunk store already within its capacity goal is a clean no-op. Single-shot behavior below the threshold is unchanged. ### Added diff --git a/pi-hermes-memory/src/handlers/background-review.ts b/pi-hermes-memory/src/handlers/background-review.ts index 72bb232..8d4100e 100644 --- a/pi-hermes-memory/src/handlers/background-review.ts +++ b/pi-hermes-memory/src/handlers/background-review.ts @@ -17,6 +17,7 @@ import { MemoryStore } from "../store/memory-store.js"; import { DatabaseManager } from "../store/db.js"; import type { MemoryConfig } from "../types.js"; import type { EnsureMemoryReady } from "../memory-initialization.js"; +import { isSubagentSession } from "../subagent-mode.js"; import { applyRecentMessageLimit, collectMessageParts } from "./message-parts.js"; import { execChildPrompt, resolveChildPiModel } from "./pi-child-process.js"; import { REVIEW_COMPLETION_TIMEOUT_MS, runDirectMemoryCompletion, usesDirectTransport, type DirectReviewResult } from "./review-memory-ops.js"; @@ -196,6 +197,7 @@ export function setupBackgroundReview( }); pi.on("turn_end", async (event, ctx) => { + if (isSubagentSession(ctx)) return; turnsSinceReview++; if (!config.reviewEnabled) return; diff --git a/pi-hermes-memory/src/handlers/correction-detector.ts b/pi-hermes-memory/src/handlers/correction-detector.ts index 3c84cd7..7d2b8f4 100644 --- a/pi-hermes-memory/src/handlers/correction-detector.ts +++ b/pi-hermes-memory/src/handlers/correction-detector.ts @@ -23,6 +23,7 @@ import { } from "../constants.js"; import type { MemoryConfig } from "../types.js"; import type { EnsureMemoryReady } from "../memory-initialization.js"; +import { isSubagentSession } from "../subagent-mode.js"; import { getMessageText } from "../types.js"; import { execChildPrompt, resolveChildPiModel } from "./pi-child-process.js"; import { resolveProjectName, resolveProjectStore, type ProjectNameRef, type ProjectStoreRef } from "../project-context.js"; @@ -151,6 +152,7 @@ export function setupCorrectionDetector( // Trigger on turn_end (we need full context: user correction + what agent said) pi.on("turn_end", async (event, ctx) => { + if (isSubagentSession(ctx)) return; if (!pendingCorrection) { turnsSinceLastCorrection++; return; diff --git a/pi-hermes-memory/src/handlers/session-flush.ts b/pi-hermes-memory/src/handlers/session-flush.ts index 0d21a8f..efa49e3 100644 --- a/pi-hermes-memory/src/handlers/session-flush.ts +++ b/pi-hermes-memory/src/handlers/session-flush.ts @@ -37,6 +37,7 @@ import { import type { MemoryConfig } from "../types.js"; import type { EnsureMemoryReady } from "../memory-initialization.js"; import { measureLifecycle } from "../lifecycle-timing.js"; +import { isSubagentSession } from "../subagent-mode.js"; import { collectMessageParts } from "./message-parts.js"; import { execChildPrompt, resolveChildPiModel } from "./pi-child-process.js"; import { runDirectMemoryCompletion, usesDirectTransport } from "./review-memory-ops.js"; @@ -167,6 +168,7 @@ export function setupSessionFlush( kind: FlushKind, ): Promise { try { + if (isSubagentSession(ctx)) return; if (userTurnCount < config.flushMinTurns) return; let entries; diff --git a/pi-hermes-memory/src/index.ts b/pi-hermes-memory/src/index.ts index c7c8be1..e7035b6 100644 --- a/pi-hermes-memory/src/index.ts +++ b/pi-hermes-memory/src/index.ts @@ -61,6 +61,7 @@ import { AGENT_ROOT } from "./paths.js"; import { isDatabaseMigrationPending } from "./extension-root-migration.js"; import { measureLifecycle, measureLifecycleSync } from "./lifecycle-timing.js"; import { createMemoryInitializer, withMemoryInitialization, type EnsureMemoryReady } from "./memory-initialization.js"; +import { isSubagentSession, markSubagentSession, markSubagentSessionFromPrompt } from "./subagent-mode.js"; export function resolveProjectSkillDiscovery( skillStore: SkillStore, @@ -195,8 +196,12 @@ export default function (pi: ExtensionAPI) { const initialization = createMemoryInitializer(async (ctx) => { const timingPrefix = lazy ? "memory-init" : "session-start"; - if (lazy) migrateLegacyProjectMemoryDirs(agentRoot, config.projectsMemoryDir); - if (!persistenceInitialized) { + // Subagent sessions are read-only: skip the write side of initialization + // (legacy-dir migration, markdown↔SQLite sync, pruning, backfill) while + // still loading the store so memory_search/session_search keep working. + const subagent = isSubagentSession(ctx); + if (lazy && !subagent) migrateLegacyProjectMemoryDirs(agentRoot, config.projectsMemoryDir); + if (!persistenceInitialized && !subagent) { try { await measureLifecycle(`${timingPrefix}.persistence-sync`, async () => { // Startup reconciles the global files plus the current project only: @@ -290,18 +295,25 @@ export default function (pi: ExtensionAPI) { // Skills and pinned instructions must be available even without a lookup. pi.on("session_start", async (_event, ctx) => { sessionContext = ctx; + // session_start runs before before_agent_start; a child's rendered prompt + // leads with the tag, so detect it here early enough to + // skip the session_start writes below. + markSubagentSessionFromPrompt(ctx.getSystemPrompt?.() ?? "", ctx); // Pinned directives must not depend on migration/SQLite being healthy. if (standingStore) await standingStore.load(); if (!lazy) await ensureMemoryReady(ctx); refreshSkillProjectContext(ctx.cwd); - await skillStore.migrateLegacySkills(); - await skillStore.ensureDiscoveredRoots(); + if (!isSubagentSession(ctx)) { + await skillStore.migrateLegacySkills(); + await skillStore.ensureDiscoveredRoots(); + } }); registerProjectSkillDiscoveryHandler(pi, skillStore, config.projectsMemoryDir); // ── 2. Inject memory policy by default; legacy mode keeps full frozen memory blocks ── - pi.on("before_agent_start", async (event, _ctx) => { + pi.on("before_agent_start", async (event, ctx) => { + markSubagentSession(event, ctx); const promptContext = await buildPromptContext(config, store, projectStoreRef(), projectNameRef(), standingStore); if (!promptContext) return; @@ -400,6 +412,7 @@ export default function (pi: ExtensionAPI) { // ── 10. Live session indexing ── pi.on("message_end", async (_event, ctx) => { + if (isSubagentSession(ctx)) return; // Cold lazy sessions remain in Pi's JSONL files and are backfilled on use. if (lazy && !initialization.isReady()) return; scheduleLiveSessionIndex(dbManager, ctx.sessionManager, { @@ -432,6 +445,7 @@ export default function (pi: ExtensionAPI) { return; } try { + if (isSubagentSession(ctx)) return; measureLifecycleSync("shutdown.active-index", () => { const sessionFile = ctx.sessionManager.getSessionFile(); if (sessionFile && fs.existsSync(sessionFile)) { diff --git a/pi-hermes-memory/src/subagent-mode.ts b/pi-hermes-memory/src/subagent-mode.ts new file mode 100644 index 0000000..de70b0b --- /dev/null +++ b/pi-hermes-memory/src/subagent-mode.ts @@ -0,0 +1,58 @@ +/** + * subagent-mode.ts — subagent sessions are read-only for memory and skill + * writes. All automatic write-related work (background review, correction + * detection, session flushes, session indexing) is skipped as well. + * + * A child session is identified by the `` identity + * tag that pi-subagents stamps into every child prompt + * (permission-system-bridge.ts): the tag arrives via + * `systemPromptOptions.customPrompt`. The tag is only trusted on that field — + * never in the rendered system prompt, which a main session can legitimately + * contain through project docs. + * + * Membership is tracked per session by its sessionManager identity: the same + * process hosts the parent and its children concurrently, so a global boolean + * would let a child's turn flip the parent's session into read-only mid-turn. + */ +import type { BeforeAgentStartEvent, ExtensionContext } from "@earendil-works/pi-coding-agent"; + +/** pi-subagents `` identity tag contract. */ +const ACTIVE_AGENT_TAG = /]*>/i; +const LEADING_ACTIVE_AGENT_TAG = /^\s*]*>/i; + +export const SUBAGENT_WRITE_DISABLED_MESSAGE = + "Memory and skill writes are disabled in subagent sessions; include this in your final report so the delegating agent can decide."; + +const subagentSessionManagers = new WeakSet(); + +/** Same regex as the pi-subagents `` contract, applied only to customPrompt. */ +export function isSubagentCustomPrompt(customPrompt: string | undefined | null): boolean { + return typeof customPrompt === "string" && ACTIVE_AGENT_TAG.test(customPrompt); +} + +/** + * before_agent_start hook: authoritative detection via the event's + * systemPromptOptions.customPrompt. + */ +export function markSubagentSession(event: BeforeAgentStartEvent, ctx: ExtensionContext): void { + if (isSubagentCustomPrompt(event.systemPromptOptions?.customPrompt)) { + subagentSessionManagers.add(ctx.sessionManager); + } +} + +/** + * session_start runs before before_agent_start; detect tag-leading prompts + * early so session_start writes (skill migration, root setup) can be skipped. + * A main prompt cannot lead with the tag — without customPrompt the rendered + * prompt always opens with the base preamble. + */ +export function markSubagentSessionFromPrompt(systemPrompt: string, ctx: ExtensionContext): void { + if (LEADING_ACTIVE_AGENT_TAG.test(systemPrompt)) { + subagentSessionManagers.add(ctx.sessionManager); + } +} + +export function isSubagentSession(ctx: unknown): boolean { + const sm = (ctx as { sessionManager?: unknown } | undefined | null)?.sessionManager; + return typeof sm === "object" && sm !== null && subagentSessionManagers.has(sm); +} diff --git a/pi-hermes-memory/src/tools/memory-tool.ts b/pi-hermes-memory/src/tools/memory-tool.ts index 5ebc489..94c6ef1 100644 --- a/pi-hermes-memory/src/tools/memory-tool.ts +++ b/pi-hermes-memory/src/tools/memory-tool.ts @@ -25,6 +25,7 @@ import type { MemoryCategory, MemoryResult } from "../types.js"; import { normalizeMemoryLookupText } from "../store/memory-lookup.js"; import { createSharedToolResultRenderer } from "./shared-output-view.js"; import { memoryResultView } from "./tool-result-views.js"; +import { isSubagentSession, SUBAGENT_WRITE_DISABLED_MESSAGE } from "../subagent-mode.js"; function appendSyncWarning(result: MemoryResult, warning: string): MemoryResult { const warnings = [...(((result as any).warnings ?? []) as string[]), warning]; @@ -425,7 +426,13 @@ This action-specific tool accepts only the parameters listed in its schema.`; ], renderResult: createSharedToolResultRenderer(memoryResultView), parameters, - async execute(_toolCallId, params, signal, _onUpdate, ctx?: { cwd?: string }) { + async execute(_toolCallId, params, signal, _onUpdate, ctx?: { cwd?: string; sessionManager?: object }) { + if (isSubagentSession(ctx)) { + return { + content: [{ type: "text" as const, text: SUBAGENT_WRITE_DISABLED_MESSAGE }], + details: { success: false, error: SUBAGENT_WRITE_DISABLED_MESSAGE }, + }; + } if (bindProjectFromCwd && ctx?.cwd) { await bindProjectFromCwd(ctx.cwd); } diff --git a/pi-hermes-memory/src/tools/skill-tool.ts b/pi-hermes-memory/src/tools/skill-tool.ts index 25f7267..9c90a17 100644 --- a/pi-hermes-memory/src/tools/skill-tool.ts +++ b/pi-hermes-memory/src/tools/skill-tool.ts @@ -10,6 +10,7 @@ import { SkillStore } from "../store/skill-store.js"; import { SKILL_TOOL_DESCRIPTION } from "../constants.js"; import { createSharedToolResultRenderer } from "./shared-output-view.js"; import { skillResultView } from "./tool-result-views.js"; +import { isSubagentSession, SUBAGENT_WRITE_DISABLED_MESSAGE } from "../subagent-mode.js"; function normalizeTextList(value: unknown): string[] { if (!Array.isArray(value)) return []; @@ -90,6 +91,13 @@ const SKILL_TOOL_PARAMETERS = Type.Object({ export const SKILL_MANAGE_TOOL_NAME = "skill_manage"; export function registerSkillTool(pi: ExtensionAPI, store: SkillStore): void { + if (typeof pi.on === "function") { + pi.on("tool_result", (event) => { + if (event.toolName !== SKILL_MANAGE_TOOL_NAME) return; + const details = event.details as { success?: unknown } | undefined; + if (details?.success === false) return { isError: true }; + }); + } pi.registerTool({ name: SKILL_MANAGE_TOOL_NAME, label: "Skill Manager", @@ -108,6 +116,12 @@ export function registerSkillTool(pi: ExtensionAPI, store: SkillStore): void { renderResult: createSharedToolResultRenderer(skillResultView), parameters: SKILL_TOOL_PARAMETERS, async execute(toolCallId, params, signal, onUpdate, ctx) { + if (isSubagentSession(ctx)) { + return { + content: [{ type: "text", text: SUBAGENT_WRITE_DISABLED_MESSAGE }], + details: { success: false, error: SUBAGENT_WRITE_DISABLED_MESSAGE }, + }; + } const skillParams = params as { action: "create" | "view" | "patch" | "update" | "edit" | "delete"; name?: string; diff --git a/pi-hermes-memory/tests/subagent-mode.test.ts b/pi-hermes-memory/tests/subagent-mode.test.ts new file mode 100644 index 0000000..249bbe9 --- /dev/null +++ b/pi-hermes-memory/tests/subagent-mode.test.ts @@ -0,0 +1,228 @@ +/** + * Subagent (pi-subagents child) sessions are read-only: memory/skill write + * tools return a fixed error and all automatic writes are skipped. + * + * Child detection keys on the `` tag pi-subagents + * stamps into `systemPromptOptions.customPrompt`; membership is tracked per + * sessionManager so a child in the same process never flips the parent. + */ +import { describe, it, beforeEach, afterEach } from "node:test"; +import assert from "node:assert/strict"; +import fs from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import { + isSubagentCustomPrompt, + isSubagentSession, + markSubagentSession, + markSubagentSessionFromPrompt, + SUBAGENT_WRITE_DISABLED_MESSAGE, +} from "../src/subagent-mode.js"; +import { registerMemoryTool } from "../src/tools/memory-tool.js"; +import { registerSkillTool } from "../src/tools/skill-tool.js"; +import { setupSessionFlush } from "../src/handlers/session-flush.js"; +import { MemoryStore } from "../src/store/memory-store.js"; +import { SkillStore } from "../src/store/skill-store.js"; +import type { MemoryConfig } from "../src/types.js"; + +const CHILD_PROMPT = 'You are the Explore agent.\n'; + +function beforeAgentStartEvent(customPrompt?: string) { + return { systemPromptOptions: customPrompt === undefined ? {} : { customPrompt } } as any; +} + +describe("subagent detection", () => { + it("matches the pi-subagents contract on customPrompt only", () => { + assert.ok(isSubagentCustomPrompt(CHILD_PROMPT)); + assert.ok(isSubagentCustomPrompt("intro tail")); + assert.ok(!isSubagentCustomPrompt("a normal custom prompt")); + assert.ok(!isSubagentCustomPrompt("")); + assert.ok(!isSubagentCustomPrompt(undefined)); + assert.ok(!isSubagentCustomPrompt(null)); + }); + + it("marks only sessions whose before_agent_start customPrompt carries the tag", () => { + const childManager = {}; + const mainManager = {}; + markSubagentSession(beforeAgentStartEvent(CHILD_PROMPT), { sessionManager: childManager } as any); + markSubagentSession(beforeAgentStartEvent(), { sessionManager: mainManager } as any); + markSubagentSession(beforeAgentStartEvent("no tag here"), { sessionManager: {} } as any); + + assert.ok(isSubagentSession({ sessionManager: childManager })); + assert.ok(!isSubagentSession({ sessionManager: mainManager })); + assert.ok(!isSubagentSession(undefined)); + assert.ok(!isSubagentSession({})); + }); + + it("detects tag-leading rendered prompts early (session_start), not mid-prompt tags", () => { + const childManager = {}; + const mainManager = {}; + markSubagentSessionFromPrompt( + `\n\nYou are the Explore agent.\n\nrest`, + { sessionManager: childManager } as any, + ); + markSubagentSessionFromPrompt( + `preamble\n\nliteral inside project docs`, + { sessionManager: mainManager } as any, + ); + assert.ok(isSubagentSession({ sessionManager: childManager })); + assert.ok(!isSubagentSession({ sessionManager: mainManager })); + }); +}); + +describe("subagent write gating", () => { + let tmpDir: string; + + beforeEach(() => { + tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "subagent-mode-test-")); + }); + + afterEach(() => { + fs.rmSync(tmpDir, { recursive: true, force: true }); + }); + + function makeMemoryStore(): MemoryStore { + return new MemoryStore({ + memoryMode: "policy-only", + memoryCharLimit: 5000, + userCharLimit: 5000, + projectCharLimit: 5000, + nudgeInterval: 10, + reviewEnabled: false, + flushOnCompact: false, + flushOnShutdown: false, + flushMinTurns: 6, + autoConsolidate: false, + correctionDetection: false, + failureInjectionEnabled: false, + failureInjectionMaxAgeDays: 7, + failureInjectionMaxEntries: 5, + nudgeToolCalls: 15, + consolidationTimeoutMs: 60000, + memoryDir: tmpDir, + }); + } + + it("memory_add/replace/remove return the disabled error and perform no writes", async () => { + const tools = new Map(); + const mockPi = { registerTool: (def: any) => tools.set(def.name, def) } as any; + const store = makeMemoryStore(); + await store.loadFromDisk(); + let bindCalls = 0; + registerMemoryTool(mockPi, store, null, null, null, () => { bindCalls++; }); + + const sessionManager = {}; + markSubagentSession(beforeAgentStartEvent(CHILD_PROMPT), { sessionManager } as any); + const ctx = { cwd: tmpDir, sessionManager }; + + for (const name of ["memory_add", "memory_replace", "memory_remove"]) { + const result = await tools.get(name).execute("tc", { target: "memory", content: "x", old_text: "x" }, undefined, undefined, ctx); + assert.strictEqual(result.content[0].text, SUBAGENT_WRITE_DISABLED_MESSAGE, name); + assert.strictEqual(result.details.success, false, name); + assert.strictEqual(result.details.error, SUBAGENT_WRITE_DISABLED_MESSAGE, name); + } + assert.strictEqual(bindCalls, 0, "blocked writes must not bind projects"); + assert.deepStrictEqual(store.getMemoryEntries(), [], "blocked writes must not mutate the store"); + assert.ok(!fs.existsSync(path.join(tmpDir, "MEMORY.md")), "blocked writes must not write files"); + }); + + it("memory tools still write in an unmarked (main) session", async () => { + const tools = new Map(); + const mockPi = { registerTool: (def: any) => tools.set(def.name, def) } as any; + const store = makeMemoryStore(); + await store.loadFromDisk(); + registerMemoryTool(mockPi, store, null); + + const result = await tools.get("memory_add").execute( + "tc", + { target: "memory", content: "main session entry" }, + undefined, + undefined, + { cwd: tmpDir, sessionManager: {} }, + ); + assert.strictEqual(result.details.success, true); + assert.deepStrictEqual(store.getMemoryEntries(), ["main session entry"]); + }); + + it("skill_manage returns the disabled error in a subagent session", async () => { + let tool: any; + const mockPi = { registerTool: (def: any) => { tool = def; } } as any; + const store = new SkillStore({ + globalSkillsDir: path.join(tmpDir, "skills"), + piGlobalSkillsDir: path.join(tmpDir, "pi-skills"), + projectSkillsDir: null, + projectName: null, + legacySkillsDir: path.join(tmpDir, "legacy-skills"), + migrationSentinelPath: path.join(tmpDir, ".migrated"), + }); + registerSkillTool(mockPi, store); + + const sessionManager = {}; + markSubagentSession(beforeAgentStartEvent(CHILD_PROMPT), { sessionManager } as any); + const result = await tool.execute( + "tc", + { action: "create", name: "demo", description: "d", scope: "global", content: "body" }, + undefined, + undefined, + { sessionManager }, + ); + assert.strictEqual(result.content[0].text, SUBAGENT_WRITE_DISABLED_MESSAGE); + assert.strictEqual(result.details.success, false); + assert.ok(!fs.existsSync(path.join(tmpDir, "skills")), "blocked skill writes must not create the skills dir"); + }); + + it("session flush skips compact and shutdown flushes in a subagent session", async () => { + const handlers: Record = {}; + const mockPi = { + on(event: string, handler: Function) { + (handlers[event] ??= []).push(handler); + }, + async exec() { + throw new Error("subagent flush must not spawn a child pi"); + }, + registerTool() {}, + registerCommand() {}, + } as any; + + let directCalls = 0; + const config = { + flushOnCompact: true, + flushOnShutdown: true, + flushMinTurns: 1, + flushCompactTimeoutMs: 60000, + reviewTransport: "direct", + } as MemoryConfig; + + const sessionManager = { getBranch: () => [] }; + markSubagentSession(beforeAgentStartEvent(CHILD_PROMPT), { sessionManager } as any); + + setupSessionFlush(mockPi, { getMemoryEntries: () => [], getUserEntries: () => [] } as any, null, config, null, null, { + runDirectMemoryCompletion: async () => { + directCalls++; + return { ok: true } as any; + }, + }); + + const ctx = { sessionManager, cwd: tmpDir }; + for (const handler of handlers["message_end"] ?? []) { + await handler({ message: { role: "user", content: "hi" } }, ctx); + } + for (const handler of handlers["session_before_compact"] ?? []) { + await handler({ signal: undefined }, ctx); + } + for (const handler of handlers["session_shutdown"] ?? []) { + await handler({ reason: "exit" }, ctx); + } + assert.strictEqual(directCalls, 0, "subagent sessions must not flush"); + + // Control: the same handlers must still flush an unmarked (main) session. + const mainCtx = { sessionManager: { getBranch: () => [] }, cwd: tmpDir }; + for (const handler of handlers["message_end"] ?? []) { + await handler({ message: { role: "user", content: "hi" } }, mainCtx); + } + for (const handler of handlers["session_before_compact"] ?? []) { + await handler({ signal: undefined }, mainCtx); + } + assert.ok(directCalls > 0, "main sessions must still flush"); + }); +}); diff --git a/pi-subagents/README.md b/pi-subagents/README.md index dd04a81..e033a1e 100644 --- a/pi-subagents/README.md +++ b/pi-subagents/README.md @@ -2,13 +2,13 @@ A [pi](https://pi.dev) extension that brings **Claude Code-style autonomous sub-agents** to pi. Spawn specialized agents that run in isolated sessions — each with its own tools, system prompt, model, and thinking level. Run them in the background (the default) or block on them, steer them mid-run, resume completed sessions, and define your own custom agent types. -> **my-pi maintenance note:** This directory was imported from upstream tag `v0.18.0` at commit `3f9d35cd078d18a141eb5a6d8f4fc5010d756280` and is maintained directly from source. Its source, tests, host-owned `extensions/subagents.ts` wrapper, and `pi-permission-system` child lifecycle bridge are retained, but the root bundle currently declares no `pi-subagents` runtime dependency and does not default-load the wrapper. See [`UPSTREAM.md`](UPSTREAM.md). +> **my-pi maintenance note:** This directory was imported from upstream tag `v0.18.0` at commit `3f9d35cd078d18a141eb5a6d8f4fc5010d756280` and is maintained directly from source. The root bundle installs it through `file:./pi-subagents`, default-loads the host-owned `extensions/subagents.ts` wrapper, and exposes its orchestration tools through Tool Search's checked-in `subagents` group. The local source implements the `pi-permission-system` child lifecycle bridge described below. See [`UPSTREAM.md`](UPSTREAM.md). ## my-pi permission integration Before a child calls `bindExtensions()`, it publishes `subagents:child:session-created` with the child session id and the interactive root session id. This lets `pi-permission-system` classify the headless session as a registered child before its own `session_start`, prevents the child permission service from replacing the root process-global service, and routes unresolved `ask` requests to the root authority. Nested descendants retain the same interactive root instead of forwarding to an intermediate child with no UI. The matching `subagents:child:disposed` event is published once, after the child has actually closed. -The retained bundle wrapper configures this extension through `createPiSubagentsExtension({ mandatoryExtensionPaths })`, although that wrapper is currently disabled in the root extension list. If re-enabled, these exact, host-owned absolute paths are canonicalized and merged *after* Agent frontmatter, so `extensions: false`, `isolated: true`, and `exclude_extensions:` cannot remove the permission wrapper. The child aborts before session creation if an exact mandatory entry is unavailable or does not survive loader filtering. Mandatory means lifecycle handlers bind; it does **not** expose that extension's tools when normal Agent extension policy hid them. Agent files cannot add to or alter this list. +The bundle loads this extension through `createPiSubagentsExtension({ mandatoryExtensionPaths })`. These exact, host-owned absolute paths are canonicalized and merged *after* Agent frontmatter, so `extensions: false`, `isolated: true`, and `exclude_extensions:` cannot remove the permission wrapper. The child aborts before session creation if an exact mandatory entry is unavailable or does not survive loader filtering. Mandatory means lifecycle handlers bind; it does **not** expose that extension's tools when normal Agent extension policy hid them. Agent files cannot add to or alter this list. Each child system prompt carries one sanitized `` identity. Any inherited parent identity is removed first, so per-agent permission configuration and forwarded-request attribution resolve against the child definition rather than the orchestrator. diff --git a/pi-subagents/src/agent-manager.ts b/pi-subagents/src/agent-manager.ts index a1c0f5f..5c37b65 100644 --- a/pi-subagents/src/agent-manager.ts +++ b/pi-subagents/src/agent-manager.ts @@ -237,7 +237,14 @@ async function shutdownChildSession(session: AgentSession | undefined): Promise< finally { unregisterPermissionChildSession(session); } } -export interface AgentManagerOptions extends MandatoryExtensionPolicy {} +export interface AgentManagerOptions extends MandatoryExtensionPolicy { + /** Canonical bundle-owned extension entries removed from every child session regardless of Agent frontmatter. */ + excludedExtensionPaths?: readonly string[]; + /** Per-spawn base prompt supplier — non-blank replaces the inherited parent prompt in buildAgentPrompt. */ + basePrompt?: () => string | undefined; + /** Bundle-shipped agent .md directory forwarded to nested-delegation registries. */ + bundledAgentsDir?: string; +} export class AgentManager { private agents = new Map(); @@ -248,6 +255,9 @@ export class AgentManager { private onUsage?: OnAgentUsage; private maxConcurrent: number; private readonly mandatoryExtensionPaths: readonly string[]; + private readonly excludedExtensionPaths: readonly string[]; + private readonly basePrompt?: () => string | undefined; + private readonly bundledAgentsDir?: string; /** Base repos worktrees were created from — so dispose() can prune them all, * not just the parent repo (caller-supplied cwd can target other repos). */ private worktreeRepos = new Set(); @@ -281,6 +291,11 @@ export class AgentManager { this.mandatoryExtensionPaths = normalizeMandatoryExtensionPaths( options.mandatoryExtensionPaths, ); + this.excludedExtensionPaths = normalizeMandatoryExtensionPaths( + options.excludedExtensionPaths, + ); + this.basePrompt = options.basePrompt; + this.bundledAgentsDir = options.bundledAgentsDir; // Cleanup completed agents after 10 minutes (but keep sessions for resume) this.cleanupInterval = setInterval(() => this.cleanup(), 60_000); this.cleanupInterval.unref(); @@ -442,6 +457,9 @@ export class AgentManager { // rootSessionId; programmatic callers fall back to their current context. authoritySessionId: record.rootSessionId ?? ctx.sessionManager?.getSessionId?.(), mandatoryExtensionPaths: this.mandatoryExtensionPaths, + excludedExtensionPaths: this.excludedExtensionPaths, + basePrompt: this.basePrompt, + bundledAgentsDir: this.bundledAgentsDir, // Worktree wins for the working dir (the agent must run in the copy — // which, with a custom cwd, was created from that target). Config stays // with the parent project when a caller-supplied cwd is in play; it must diff --git a/pi-subagents/src/agent-runner.ts b/pi-subagents/src/agent-runner.ts index 2c176ad..1dc0199 100644 --- a/pi-subagents/src/agent-runner.ts +++ b/pi-subagents/src/agent-runner.ts @@ -24,6 +24,7 @@ import { DEFAULT_AGENTS } from "./default-agents.js"; import { detectEnv } from "./env.js"; import { assertMandatoryExtensionsLoaded, + extensionPathMatches, isMandatoryExtensionPath, normalizeMandatoryExtensionPaths, } from "./mandatory-extensions.js"; @@ -432,6 +433,21 @@ export interface RunOptions { authoritySessionId?: string; /** Canonical bundle-owned extension entries that Agent config cannot disable. */ mandatoryExtensionPaths?: readonly string[]; + /** + * Canonical bundle-owned extension entries removed from every child session + * regardless of Agent frontmatter — the host-level counterpart of + * `exclude_extensions`. Overlap with `mandatoryExtensionPaths` is rejected + * by `createPiSubagentsExtension`. + */ + excludedExtensionPaths?: readonly string[]; + /** + * Per-spawn base prompt supplier. A non-blank return replaces the inherited + * parent prompt and the `` bridge / replace-mode header + * inside `buildAgentPrompt`; undefined or blank keeps upstream composition. + */ + basePrompt?: () => string | undefined; + /** Bundle-shipped agent .md directory forwarded to nested-delegation registries. */ + bundledAgentsDir?: string; /** Override working directory (e.g. for worktree isolation). */ cwd?: string; /** @@ -651,16 +667,18 @@ export async function runAgent( } } - // Build system prompt from agent config + // Build system prompt from agent config. `basePrompt` resolves once per + // spawn — a host-owned base owns the prompt base instead of the parent's. + const basePrompt = options.basePrompt?.(); let systemPrompt: string; if (agentConfig) { - systemPrompt = buildAgentPrompt(agentConfig, effectiveCwd, env, parentSystemPrompt, extras); + systemPrompt = buildAgentPrompt(agentConfig, effectiveCwd, env, parentSystemPrompt, extras, basePrompt); } else { // Unknown type fallback: spread the canonical general-purpose config (defensive — // unreachable in practice since index.ts resolves unknown types before calling runAgent). const fallback = DEFAULT_AGENTS.get("general-purpose"); if (!fallback) throw new Error(`No fallback config available for unknown type "${type}"`); - systemPrompt = buildAgentPrompt({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras); + systemPrompt = buildAgentPrompt({ ...fallback, name: type }, effectiveCwd, env, parentSystemPrompt, extras, basePrompt); } // Child permission policy is selected by the agent-definition filename, // which is also where pi-permission-system reads `permission:` frontmatter. @@ -701,6 +719,11 @@ export async function runAgent( ); const optionalExtensionsDisabled = extensions === false; const mandatoryExtensionPaths = normalizeMandatoryExtensionPaths(options.mandatoryExtensionPaths); + // Bundle-level exclusion: canonicalized like mandatory paths, removed from + // every child's loaded set regardless of the Agent's own extension policy. + const excludedExtensionPaths = normalizeMandatoryExtensionPaths(options.excludedExtensionPaths); + const isExcludedExtension = (extensionPath: string) => + excludedExtensionPaths.some((path) => extensionPathMatches(extensionPath, path)); const noExtensions = optionalExtensionsDisabled && mandatoryExtensionPaths.length === 0; const extensionsSpec = Array.isArray(extensions) @@ -718,10 +741,12 @@ export async function runAgent( if (canons.some((name) => excludeNames.has(name))) return false; return loadAll || canons.some((name) => keepNames.has(name)); }; + // An excluded path stays out even when an `extensions:` path entry asks for + // it — host policy outranks Agent frontmatter, same as everywhere else. const requestedPaths = [ ...(extensionsSpec?.paths ?? []), ...mandatoryExtensionPaths, - ]; + ].filter((path) => !isExcludedExtension(path)); const additionalExtensionPaths = requestedPaths.length ? [...new Set(requestedPaths)] : undefined; @@ -729,16 +754,25 @@ export async function runAgent( // against discovery, while mandatory identity is checked by exact canonical path. let discoveredNames: Set | undefined; const extensionsOverride: ((base: LoadExtensionsResult) => LoadExtensionsResult) | undefined = - noExtensions || (loadAll && !hasExcludes) + (noExtensions || (loadAll && !hasExcludes)) && excludedExtensionPaths.length === 0 ? undefined : (base) => { discoveredNames = new Set(base.extensions.flatMap((e) => extensionCanonicalNames(e.path))); return { ...base, - extensions: base.extensions.filter((extension) => - isMandatoryExtensionPath(extension.path, mandatoryExtensionPaths) - || normalPolicySelects(extension.path) - ), + extensions: base.extensions.filter((extension) => { + if (isExcludedExtension(extension.path)) { + if (normalPolicySelects(extension.path)) { + options.onToolActivity?.({ + type: "end", + toolName: `extension-error:extension "${basename(extension.path)}" is excluded from subagent sessions by bundle policy`, + }); + } + return false; + } + return isMandatoryExtensionPath(extension.path, mandatoryExtensionPaths) + || normalPolicySelects(extension.path); + }), }; }; @@ -880,6 +914,7 @@ export async function runAgent( maxSubagentDepth: effectiveMaxDepth, allowedSubagents: agentConfig.allowedSubagents, configCwd, + bundledAgentsDir: options.bundledAgentsDir, }) : []; const nestedToolNames = new Set(nestedTools.map(tool => tool.name)); diff --git a/pi-subagents/src/agent-types.ts b/pi-subagents/src/agent-types.ts index b6c544a..abb4d40 100644 --- a/pi-subagents/src/agent-types.ts +++ b/pi-subagents/src/agent-types.ts @@ -55,14 +55,20 @@ export function setFallbackSubagent(v: string | undefined): void { fallbackSubag /** * Build a registry map: DEFAULT_AGENTS first (unless disabled via settings), - * then user agents overlaid on top (same name overrides the default). + * then bundle-shipped defaults (bundledAgentsDir — they replace same-name + * embedded defaults and are themselves stamped `isDefault`), then user agents + * overlaid on top (same name overrides everything before it). * Pure — callers that must not disturb the process-wide registry (nested * delegation resolving agents from its own config root) build their own map. */ -export function buildAgentRegistry(userAgents: Map): Map { +export function buildAgentRegistry( + userAgents: Map, + bundledAgents?: Map, +): Map { const registry = new Map(); if (!disableDefaults) { for (const [name, config] of DEFAULT_AGENTS) registry.set(name, config); + if (bundledAgents) for (const [name, config] of bundledAgents) registry.set(name, config); } for (const [name, config] of userAgents) registry.set(name, config); return registry; @@ -70,12 +76,16 @@ export function buildAgentRegistry(userAgents: Map): Map): void { +export function registerAgents( + userAgents: Map, + bundledAgents?: Map, +): void { agents.clear(); - for (const [name, config] of buildAgentRegistry(userAgents)) { + for (const [name, config] of buildAgentRegistry(userAgents, bundledAgents)) { agents.set(name, config); } } diff --git a/pi-subagents/src/custom-agents.ts b/pi-subagents/src/custom-agents.ts index d7978ea..0deeee6 100644 --- a/pi-subagents/src/custom-agents.ts +++ b/pi-subagents/src/custom-agents.ts @@ -79,66 +79,124 @@ function loadFromDir(dir: string, agents: Map, source: "pro } const { frontmatter: fm, body } = parsed; - // Claude Code's rule: `name:` IS the agent type, and the filename need not - // match. Absent, the filename stands in — Claude Code requires the field, - // but most files here predate it and must keep loading. - const declared = str(fm.name)?.trim(); - if (declared?.includes(RESERVED_IN_TYPE)) { - // Refusing beats silently substituting: the file would otherwise load - // under its filename, so `Agent({subagent_type})` would succeed against - // an agent whose declared identity nothing honoured. - warnIfNew( - `Agent file ${path} declares name "${declared}", which contains "${RESERVED_IN_TYPE}" — reserved for ` - + "plugin-scoped identifiers. Rename it, or move the label to `display_name:`. Skipping.", - ); - // No `warnSkippedOverride`: this file would have registered under its - // *declared* name, which nothing else can hold (a colon keeps it out of - // the registry), so it shadowed nothing. Passing the filename instead - // would report a substitution of an unrelated agent that never happened. - continue; - } - // `||`, not `??`: a quoted empty or all-whitespace `name:` would otherwise - // register the agent under the empty type — unspawnable, and it takes the - // filename-derived one down with it. - const name = declared || filenameType; + // A reserved-name skip carries no `warnSkippedOverride`: this file would + // have registered under its *declared* name, which nothing else can hold + // (a colon keeps it out of the registry), so it shadowed nothing. Passing + // the filename instead would report a substitution of an unrelated agent + // that never happened. + const name = resolveDeclaredName(path, filenameType, fm); + if (name === undefined) continue; - const { builtinToolNames, extSelectors } = parseToolsField(fm.tools); - - agents.set(name, { - name, - // Only `display_name` now: `name` is the type, and `getConfig` already - // falls back to the type when no label is set — so a Claude Code file - // with `name: code-reviewer` still badges as "code-reviewer". - displayName: str(fm.display_name), - color: str(fm.color), - description: str(fm.description) ?? name, - builtinToolNames, - extSelectors, - disallowedTools: csvListOptional(fm.disallowed_tools), - extensions: inheritField(fm.extensions ?? fm.inherit_extensions), - excludeExtensions: csvListOptional(fm.exclude_extensions), - skills: inheritField(fm.skills ?? fm.inherit_skills), - model: str(fm.model), - thinking: str(fm.thinking) as ThinkingLevel | undefined, - maxTurns: nonNegativeInt(fm.max_turns), - persistSession: fm.persist_session != null ? fm.persist_session === true : undefined, - outputTranscript: fm.output_transcript != null ? fm.output_transcript !== false : undefined, - sessionDir: str(fm.session_dir), - allowedSubagents: parseAllowedSubagents(fm.allowed_subagents), - systemPrompt: body.trim(), - promptMode: fm.prompt_mode === "append" ? "append" : "replace", - inheritContext: fm.inherit_context != null ? fm.inherit_context === true : undefined, - runInBackground: fm.run_in_background != null ? fm.run_in_background === true : undefined, - isolated: fm.isolated != null ? fm.isolated === true : undefined, - memory: parseMemory(fm.memory), - isolation: parseIsolation(fm.isolation), - enabled: fm.enabled !== false, // default true; explicitly false disables - source, - sourcePath: path, - }); + agents.set(name, configFromFrontmatter(name, fm, body, { source, sourcePath: path })); } } +/** + * Claude Code's rule: `name:` IS the agent type, and the filename need not + * match. Absent, the filename stands in — Claude Code requires the field, but + * most files here predate it and must keep loading. `||`, not `??`: a quoted + * empty or all-whitespace `name:` would otherwise register the agent under the + * empty type — unspawnable, and it takes the filename-derived one down with it. + * + * A declared name containing ":" is refused — reserved for plugin-scoped + * identifiers. Refusing beats silently substituting: the file would otherwise + * load under its filename, so `Agent({subagent_type})` would succeed against + * an agent whose declared identity nothing honoured. + */ +function resolveDeclaredName( + path: string, + filenameType: string, + fm: Record, +): string | undefined { + const declared = str(fm.name)?.trim(); + if (declared?.includes(RESERVED_IN_TYPE)) { + warnIfNew( + `Agent file ${path} declares name "${declared}", which contains "${RESERVED_IN_TYPE}" — reserved for ` + + "plugin-scoped identifiers. Rename it, or move the label to `display_name:`. Skipping.", + ); + return undefined; + } + return declared || filenameType; +} + +/** + * Build an AgentConfig from parsed frontmatter. `provenance` is the + * project/global source record; bundled agents pass none and are stamped as + * embedded defaults instead — `isDefault` with no `source`/`sourcePath`, so + * UIs treat them as built-ins rather than editable/deletable files. + */ +function configFromFrontmatter( + name: string, + fm: Record, + body: string, + provenance?: { source: "project" | "global"; sourcePath: string }, +): AgentConfig { + const { builtinToolNames, extSelectors } = parseToolsField(fm.tools); + return { + name, + // Only `display_name` now: `name` is the type, and `getConfig` already + // falls back to the type when no label is set — so a Claude Code file + // with `name: code-reviewer` still badges as "code-reviewer". + displayName: str(fm.display_name), + color: str(fm.color), + description: str(fm.description) ?? name, + builtinToolNames, + extSelectors, + disallowedTools: csvListOptional(fm.disallowed_tools), + extensions: inheritField(fm.extensions ?? fm.inherit_extensions), + excludeExtensions: csvListOptional(fm.exclude_extensions), + skills: inheritField(fm.skills ?? fm.inherit_skills), + model: str(fm.model), + thinking: str(fm.thinking) as ThinkingLevel | undefined, + maxTurns: nonNegativeInt(fm.max_turns), + persistSession: fm.persist_session != null ? fm.persist_session === true : undefined, + outputTranscript: fm.output_transcript != null ? fm.output_transcript !== false : undefined, + sessionDir: str(fm.session_dir), + allowedSubagents: parseAllowedSubagents(fm.allowed_subagents), + systemPrompt: body.trim(), + promptMode: fm.prompt_mode === "append" ? "append" : "replace", + inheritContext: fm.inherit_context != null ? fm.inherit_context === true : undefined, + runInBackground: fm.run_in_background != null ? fm.run_in_background === true : undefined, + isolated: fm.isolated != null ? fm.isolated === true : undefined, + memory: parseMemory(fm.memory), + isolation: parseIsolation(fm.isolation), + enabled: fm.enabled !== false, // default true; explicitly false disables + isDefault: provenance === undefined ? true : undefined, + source: provenance?.source, + sourcePath: provenance?.sourcePath, + }; +} + +/** + * Load bundle-shipped agent .md files as additional embedded defaults + * (the `bundledAgentsDir` extension option). Same frontmatter parser as + * user agent files; entries carry `isDefault` with no `source`/`sourcePath` + * so UIs treat them as built-ins rather than editable files. Files are read + * in sorted order so registration order is deterministic. + */ +export function loadBundledAgents(dir: string, strict = false): Map { + const agents = new Map(); + if (!existsSync(dir)) return agents; + + let files: string[]; + try { + files = readdirSync(dir).filter(f => f.endsWith(".md")).sort(); + } catch { + return agents; + } + + for (const file of files) { + const filenameType = basename(file, ".md"); + const path = join(dir, file); + const parsed = readAgentFile(path, strict); + if (!parsed) continue; + const name = resolveDeclaredName(path, filenameType, parsed.frontmatter); + if (name === undefined) continue; + agents.set(name, configFromFrontmatter(name, parsed.frontmatter, parsed.body)); + } + return agents; +} + /** * Read and parse one agent file, or warn and return undefined for the caller to * skip. One bad file must not take the whole extension down with it — an diff --git a/pi-subagents/src/index.ts b/pi-subagents/src/index.ts index 2d0f67a..753b16c 100644 --- a/pi-subagents/src/index.ts +++ b/pi-subagents/src/index.ts @@ -18,15 +18,15 @@ import { Type } from "@sinclair/typebox"; import { abortable } from "./abortable.js"; import { hasAgentBadge, renderAgentName } from "./agent-color.js"; import { buildNewAgentFile, disableInContent, enableInContent, isEmptyStub, locateAgentFile, personalAgentsDir, projectAgentsDir, serializeAgentFile } from "./agent-file-toggle.js"; -import { AgentManager } from "./agent-manager.js"; +import { AgentManager, type AgentManagerOptions } from "./agent-manager.js"; import { getAgentConversation, getDefaultMaxTurns, getGraceTurns, getRememberAgents, normalizeMaxTurns, resolveEffectiveMaxTurns, SUBAGENT_TOOL_NAMES, setDefaultMaxTurns, setGraceTurns, setRememberAgents, steerAgent } from "./agent-runner.js"; import { BUILTIN_TOOL_NAMES, getAgentConfig, getAllTypes, getAvailableTypes, getConfig, getFallbackSubagent, isDefaultsDisabled, NO_FALLBACK, registerAgents, resolveSpawnType, resolveType, setDefaultsDisabled, setFallbackSubagent } from "./agent-types.js"; import { inChildSessionContext } from "./child-context.js"; import { type RpcHandle, registerRpcHandlers } from "./cross-extension-rpc.js"; -import { loadCustomAgents } from "./custom-agents.js"; +import { loadBundledAgents, loadCustomAgents } from "./custom-agents.js"; import { GroupJoinManager } from "./group-join.js"; import { isolationParam, resolveAgentInvocationConfig, resolveJoinMode } from "./invocation-config.js"; -import type { MandatoryExtensionPolicy } from "./mandatory-extensions.js"; +import { canonicalExistingPath, isMandatoryExtensionPath, normalizeMandatoryExtensionPaths } from "./mandatory-extensions.js"; import { describeMention, handleBase, isReservedHandle, parseMention, resolveHandleToType, stripAgentPrefix } from "./mention.js"; import { runMentionClone } from "./mention-clone.js"; import { type ModelRegistry, resolveModel } from "./model-resolver.js"; @@ -281,13 +281,53 @@ export function formatToolsSuffix(cfg: AgentConfig | undefined): string { return isFullSet ? "*" : tools.join(", "); } -export interface PiSubagentsExtensionOptions extends MandatoryExtensionPolicy {} +export interface PiSubagentsExtensionOptions extends AgentManagerOptions { + /** + * Per-spawn base prompt supplier. A non-blank return owns the child's prompt + * base: `buildAgentPrompt` drops the inherited parent prompt, the + * `` bridge and the replace-mode header, keeping the + * env/worktree blocks, the `` section and extras. + * Undefined or blank keeps upstream composition byte-for-byte. + */ + basePrompt?: () => string | undefined; + /** + * Directory of bundle-shipped agent `.md` files, loaded with the same + * frontmatter parser as user agents and registered as embedded defaults + * (`isDefault`, no `source`/`sourcePath`). Priority: embedded < bundled < + * global < workspace < project. + */ + bundledAgentsDir?: string; + /** + * Bundle-owned extension entry paths removed from every child session + * regardless of Agent frontmatter — the host-level counterpart of + * `exclude_extensions`. Canonicalized like `mandatoryExtensionPaths`; + * overlap with it is rejected at creation. + */ + excludedExtensionPaths?: readonly string[]; +} /** Create a host-configured extension; Agent frontmatter cannot mutate these options. */ export function createPiSubagentsExtension( options: PiSubagentsExtensionOptions = {}, ): (pi: ExtensionAPI) => void { - return (pi) => installPiSubagentsExtension(pi, options); + // Fail fast at bundle-configuration time: a mandatory entry is the one thing + // an excluded entry can never be, so an overlap is a broken bundle policy. + const mandatoryExtensionPaths = normalizeMandatoryExtensionPaths(options.mandatoryExtensionPaths); + const excludedExtensionPaths = normalizeMandatoryExtensionPaths(options.excludedExtensionPaths); + for (const path of excludedExtensionPaths) { + if (isMandatoryExtensionPath(path, mandatoryExtensionPaths)) { + throw new Error(`Extension path is both mandatory and excluded: ${path}`); + } + } + const bundledAgentsDir = options.bundledAgentsDir === undefined + ? undefined + : canonicalExistingPath(options.bundledAgentsDir); + return (pi) => installPiSubagentsExtension(pi, { + ...options, + mandatoryExtensionPaths, + excludedExtensionPaths, + bundledAgentsDir, + }); } const defaultExtension = createPiSubagentsExtension(); @@ -371,10 +411,13 @@ function installPiSubagentsExtension( // the initial load, which happens hundreds of lines before settings are applied. let strictAgentFiles = loadSettings(process.cwd()).strictAgentFiles === true; - /** Reload agents from project/global custom agent dirs and merge with defaults (called on init and each Agent invocation). */ + /** Reload agents from bundled/project/global dirs and merge with defaults (called on init and each Agent invocation). */ const reloadCustomAgents = (strict = false) => { + const bundledAgents = options.bundledAgentsDir === undefined + ? undefined + : loadBundledAgents(options.bundledAgentsDir, strict); const userAgents = loadCustomAgents(process.cwd(), strict); - registerAgents(userAgents); + registerAgents(userAgents, bundledAgents); }; // Initial load — the only strict one. A bad edit mid-session must not kill the @@ -599,7 +642,12 @@ function installPiSubagentsExtension( // see `PendingUsagePool`. Skipped entirely when the feature is off, so no // pool grows in a session that will never drain it. if (reportUsage) pendingUsage.add(usage); - }, { mandatoryExtensionPaths: options.mandatoryExtensionPaths }); + }, { + mandatoryExtensionPaths: options.mandatoryExtensionPaths, + excludedExtensionPaths: options.excludedExtensionPaths, + basePrompt: options.basePrompt, + bundledAgentsDir: options.bundledAgentsDir, + }); // Expose manager via Symbol.for() global registry for cross-package access. // Standard Node.js pattern for cross-package singletons (used by OpenTelemetry, etc.). diff --git a/pi-subagents/src/mandatory-extensions.ts b/pi-subagents/src/mandatory-extensions.ts index d54cfd1..14d6119 100644 --- a/pi-subagents/src/mandatory-extensions.ts +++ b/pi-subagents/src/mandatory-extensions.ts @@ -6,7 +6,7 @@ export interface MandatoryExtensionPolicy { mandatoryExtensionPaths?: readonly string[]; } -function canonicalExistingPath(path: string): string { +export function canonicalExistingPath(path: string): string { if (!isAbsolute(path)) { throw new Error(`Mandatory extension path must be absolute: ${path}`); } diff --git a/pi-subagents/src/nested-tools.ts b/pi-subagents/src/nested-tools.ts index 5f003a6..bba7a35 100644 --- a/pi-subagents/src/nested-tools.ts +++ b/pi-subagents/src/nested-tools.ts @@ -15,7 +15,7 @@ import { resolveEnabledTypeIn, resolveTypeIn, } from "./agent-types.js"; -import { loadCustomAgents } from "./custom-agents.js"; +import { loadBundledAgents, loadCustomAgents } from "./custom-agents.js"; import { isolationParam, resolveAgentInvocationConfig } from "./invocation-config.js"; import { resolveModel } from "./model-resolver.js"; import { checkModelScope } from "./model-scope.js"; @@ -100,6 +100,8 @@ export interface NestedToolContext { allowedSubagents: "all" | string[]; /** Root used for agent/config discovery; may differ from the agent's working directory. */ configCwd: string; + /** Bundle-shipped agent .md directory — registered as embedded defaults, like the top-level registry. */ + bundledAgentsDir?: string; } function textResult(text: string, isError = false) { @@ -145,7 +147,10 @@ export function createNestedSubagentTools(context: NestedToolContext): ToolDefin // Agents resolve from a registry built for THIS branch's config root (under // worktree isolation, the copy). Never via registerAgents — that is // process-global state shared with the main session and every other agent. - const loadRegistry = () => buildAgentRegistry(loadCustomAgents(context.configCwd)); + const loadRegistry = () => buildAgentRegistry( + loadCustomAgents(context.configCwd), + context.bundledAgentsDir === undefined ? undefined : loadBundledAgents(context.bundledAgentsDir), + ); const allowedTypesIn = (registry: Map): Set | undefined => context.allowedSubagents === "all" ? undefined diff --git a/pi-subagents/src/prompts.ts b/pi-subagents/src/prompts.ts index c74b518..a76ddc6 100644 --- a/pi-subagents/src/prompts.ts +++ b/pi-subagents/src/prompts.ts @@ -32,8 +32,18 @@ export interface PromptExtras { * parent prompt forms an identical, cacheable byte prefix with the parent * session (the LLM's KV cache can then reuse those tokens across every spawn). * + * A non-blank `base` (the `basePrompt` extension option, resolved per spawn) + * owns the child's prompt base instead of the inherited parent prompt: the + * `` bridge and the replace-mode header are omitted, while + * the env/worktree blocks, agent-instructions section and extras keep their + * usual positions. Without an inherited prefix to keep cacheable, the + * active_agent tag leads the prompt in both modes — same as upstream replace + * mode — so session hooks can identify the child before before_agent_start. + * Undefined/blank keeps upstream composition byte-for-byte. + * * @param parentSystemPrompt The parent agent's effective system prompt (for append mode). * @param extras Optional extra sections to inject (memory, preloaded skills). + * @param base Host-owned base prompt that replaces the inherited parent prompt when non-blank. */ export function buildAgentPrompt( config: AgentConfig, @@ -41,6 +51,7 @@ export function buildAgentPrompt( env: EnvInfo, parentSystemPrompt?: string, extras?: PromptExtras, + base?: string, ): string { const activeAgentTag = `\n\n`; @@ -72,7 +83,22 @@ Work only inside it — never in ${extras.worktreeBase}, even if other instructi } const extrasSuffix = extraSections.length > 0 ? "\n\n" + extraSections.join("\n") : ""; + const bundleBase = base?.trim(); + if (config.promptMode === "append") { + const customSection = config.systemPrompt?.trim() + ? `\n\n\n${config.systemPrompt}\n` + : ""; + + if (bundleBase) { + // Host-owned base: replaces both the inherited parent prompt and the + // sub_agent_context bridge (the bundle preamble already encodes how a + // sub-agent works). With no parent prompt to keep cacheable, the tag + // leads the prompt — same position as replace mode — so early session + // hooks can spot the child before before_agent_start. + return activeAgentTag + bundleBase + "\n\n" + envBlock + worktreeBlock + customSection + extrasSuffix; + } + const identity = parentSystemPrompt || genericBase; const bridge = ` @@ -88,10 +114,6 @@ You are operating as a sub-agent invoked to handle a specific task. - Be concise but complete `; - const customSection = config.systemPrompt?.trim() - ? `\n\n\n${config.systemPrompt}\n` - : ""; - // Place shared/stable content first so the LLM's KV cache can reuse the // inherited prefix across all subagent invocations. The parent prompt is // placed verbatim (no wrapper tag) so it forms an identical byte prefix @@ -101,6 +123,13 @@ You are operating as a sub-agent invoked to handle a specific task. } // "replace" mode — env header + the config's full system prompt + if (bundleBase) { + // Host-owned base: drops the "You are a pi coding agent sub-agent" header + // (it duplicates the base's own role statement); env/worktree stay. + const configSection = config.systemPrompt?.trim() ? `\n\n${config.systemPrompt}` : ""; + return activeAgentTag + bundleBase + "\n\n" + envBlock + worktreeBlock + configSection + extrasSuffix; + } + const replaceHeader = `You are a pi coding agent sub-agent. You have been invoked to handle a specific task autonomously. diff --git a/pi-subagents/test/agent-runner.test.ts b/pi-subagents/test/agent-runner.test.ts index 6fe5eb3..c926a2b 100644 --- a/pi-subagents/test/agent-runner.test.ts +++ b/pi-subagents/test/agent-runner.test.ts @@ -1763,6 +1763,148 @@ describe("mandatory extension policy", () => { }); }); +describe("excluded extension paths", () => { + function excludedEntry(name = "pi-ask-user"): { dir: string; path: string } { + const dir = mkdtempSync(join(tmpdir(), "subagents-excluded-runner-")); + const extDir = join(dir, name); + mkdirSync(extDir, { recursive: true }); + const path = join(extDir, "index.ts"); + writeFileSync(path, "export default () => {};\n"); + return { dir, path }; + } + + function setup(extensions: true | string[] | false, excludeExtensions?: string[]) { + vi.mocked(getConfig).mockReturnValueOnce(makeConfig({ extensions, excludeExtensions })); + vi.mocked(getAgentConfig).mockReturnValueOnce( + makeAgentConfig({ extensions, excludeExtensions }), + ); + vi.mocked(getToolNamesForType).mockReturnValueOnce(BUILTINS_7); + } + + it("removes the extension under extensions:true, tools and all", async () => { + const fixture = excludedEntry(); + try { + setup(true); + withExtensions({ + [fixture.path]: ["ask_user_question"], + "/ext/other.ts": ["other_tool"], + }); + const { session } = createSession("OK"); + createAgentSession.mockResolvedValue({ session }); + const onToolActivity = vi.fn(); + + await runAgent(ctx, "Explore", "go", { + pi, + excludedExtensionPaths: [fixture.path], + onToolActivity, + }); + + // Exclusion forces the override on even though extensions:true + no + // frontmatter excludes would otherwise skip it. + expect(lastLoaderOpts().extensionsOverride).toBeDefined(); + expect(loaderExtensionsRef.current.extensions.map((e) => e.path)) + .not.toContain(fixture.path); + const tools = lastToolsPassed(); + expect(tools).not.toContain("ask_user_question"); + expect(tools).toContain("other_tool"); + expect(onToolActivity).toHaveBeenCalledWith(expect.objectContaining({ + toolName: expect.stringContaining("excluded from subagent sessions"), + })); + } finally { + rmSync(fixture.dir, { recursive: true, force: true }); + } + }); + + it("removes the extension even when an `extensions:` name entry selects it", async () => { + const fixture = excludedEntry(); + try { + setup(["pi-ask-user"]); + withExtensions({ [fixture.path]: ["ask_user_question"] }); + const { session } = createSession("OK"); + createAgentSession.mockResolvedValue({ session }); + const onToolActivity = vi.fn(); + + await runAgent(ctx, "Explore", "go", { + pi, + excludedExtensionPaths: [fixture.path], + onToolActivity, + }); + + expect(loaderExtensionsRef.current.extensions.map((e) => e.path)) + .not.toContain(fixture.path); + expect(lastToolsPassed()).not.toContain("ask_user_question"); + expect(onToolActivity).toHaveBeenCalledWith(expect.objectContaining({ + toolName: expect.stringContaining("excluded from subagent sessions"), + })); + } finally { + rmSync(fixture.dir, { recursive: true, force: true }); + } + }); + + it("removes the extension even when an `extensions:` path entry requests it", async () => { + const fixture = excludedEntry(); + try { + setup([fixture.path]); + withExtensions({ [fixture.path]: ["ask_user_question"] }); + const { session } = createSession("OK"); + createAgentSession.mockResolvedValue({ session }); + + await runAgent(ctx, "Explore", "go", { + pi, + excludedExtensionPaths: [fixture.path], + }); + + expect(lastLoaderOpts().additionalExtensionPaths).toBeUndefined(); + expect(loaderExtensionsRef.current.extensions.map((e) => e.path)) + .not.toContain(fixture.path); + expect(lastToolsPassed()).not.toContain("ask_user_question"); + } finally { + rmSync(fixture.dir, { recursive: true, force: true }); + } + }); + + it("is a no-op when empty — no override installed for wildcard agents", async () => { + setup(["*"]); + withExtensions({ "/ext/a.ts": ["tool_a"] }); + const { session } = createSession("OK"); + createAgentSession.mockResolvedValue({ session }); + + await runAgent(ctx, "Explore", "go", { pi, excludedExtensionPaths: [] }); + + expect(lastLoaderOpts().extensionsOverride).toBeUndefined(); + expect(lastToolsPassed()).toContain("tool_a"); + }); + + it("keeps mandatory entries while removing excluded ones", async () => { + const mandatoryDir = mkdtempSync(join(tmpdir(), "subagents-mandatory-runner-")); + const mandatoryPath = join(mandatoryDir, "permission-system.ts"); + writeFileSync(mandatoryPath, "export default () => {};\n"); + const fixture = excludedEntry(); + try { + setup(true); + withExtensions({ + [mandatoryPath]: ["permission_admin"], + [fixture.path]: ["ask_user_question"], + }); + const { session } = createSession("OK"); + createAgentSession.mockResolvedValue({ session }); + + await runAgent(ctx, "Explore", "go", { + pi, + mandatoryExtensionPaths: [mandatoryPath], + excludedExtensionPaths: [fixture.path], + }); + + const loaded = loaderExtensionsRef.current.extensions.map((e) => e.path); + expect(loaded).toContain(mandatoryPath); + expect(loaded).not.toContain(fixture.path); + } finally { + rmSync(mandatoryDir, { recursive: true, force: true }); + rmSync(fixture.dir, { recursive: true, force: true }); + } + }); +}); + describe("agent-runner extension allowlist", () => { function setupArrayAgent(extensions: string[]) { vi.mocked(getConfig).mockReturnValueOnce(makeConfig({ extensions })); diff --git a/pi-subagents/test/bundled-agents.test.ts b/pi-subagents/test/bundled-agents.test.ts new file mode 100644 index 0000000..a3ff370 --- /dev/null +++ b/pi-subagents/test/bundled-agents.test.ts @@ -0,0 +1,147 @@ +/** + * bundled-agents.test.ts — `bundledAgentsDir` extension option: bundle-shipped + * agent .md files register as embedded defaults (isDefault, no source/path), + * sit between embedded defaults and user agents in precedence, and stay out of + * the registry when the option is unset. + */ +import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { buildAgentRegistry, getAgentConfig, getDefaultAgentNames, registerAgents } from "../src/agent-types.js"; +import { loadBundledAgents } from "../src/custom-agents.js"; +import { createPiSubagentsExtension } from "../src/index.js"; + +let tmpDir: string; +let bundledDir: string; +let originalHome: string | undefined; +let originalAgentDir: string | undefined; + +beforeEach(() => { + tmpDir = mkdtempSync(join(tmpdir(), "pi-bundled-agents-")); + bundledDir = join(tmpDir, "bundled"); + mkdirSync(bundledDir, { recursive: true }); + originalHome = process.env.HOME; + originalAgentDir = process.env.PI_CODING_AGENT_DIR; + process.env.HOME = join(tmpDir, "home"); + delete process.env.PI_CODING_AGENT_DIR; +}); + +afterEach(() => { + if (originalHome == null) delete process.env.HOME; + else process.env.HOME = originalHome; + if (originalAgentDir == null) delete process.env.PI_CODING_AGENT_DIR; + else process.env.PI_CODING_AGENT_DIR = originalAgentDir; + rmSync(tmpDir, { recursive: true, force: true }); + registerAgents(new Map()); +}); + +function writeBundled(name: string, content: string) { + writeFileSync(join(bundledDir, `${name}.md`), content); +} + +describe("loadBundledAgents", () => { + it("parses agent files with the custom-agent parser and stamps them as built-ins", () => { + writeBundled("scout", `--- +name: scout +description: Bundle scout +prompt_mode: append +tools: read, grep +--- + +Bundle prompt body.`); + + const agents = loadBundledAgents(bundledDir); + const scout = agents.get("scout"); + expect(scout).toBeDefined(); + expect(scout?.description).toBe("Bundle scout"); + expect(scout?.systemPrompt).toBe("Bundle prompt body."); + expect(scout?.promptMode).toBe("append"); + expect(scout?.builtinToolNames).toEqual(["read", "grep"]); + expect(scout?.isDefault).toBe(true); + expect(scout?.source).toBeUndefined(); + expect(scout?.sourcePath).toBeUndefined(); + }); + + it("returns an empty map for a missing or empty directory", () => { + expect(loadBundledAgents(join(tmpDir, "missing")).size).toBe(0); + expect(loadBundledAgents(bundledDir).size).toBe(0); + }); + + it("skips non-.md files and reserved ':' names", () => { + writeBundled("fine", "---\ndescription: ok\n---\nbody"); + writeFileSync(join(bundledDir, "notes.txt"), "not an agent"); + writeBundled("bad", "---\nname: 'plugin:scout'\ndescription: bad\n---\nbody"); + const agents = loadBundledAgents(bundledDir); + expect([...agents.keys()]).toEqual(["fine"]); + }); +}); + +describe("bundled agent precedence", () => { + it("replaces a same-name embedded default and reports as a default", () => { + writeBundled("Explore", `--- +name: Explore +description: Bundled Explore override +prompt_mode: replace +--- + +Bundled Explore body.`); + + const bundled = loadBundledAgents(bundledDir); + const registry = buildAgentRegistry(new Map(), bundled); + expect(registry.get("Explore")?.systemPrompt).toBe("Bundled Explore body."); + expect(registry.get("Explore")?.isDefault).toBe(true); + // Unrelated embedded defaults are untouched. + expect(registry.get("general-purpose")?.systemPrompt).not.toBe("Bundled Explore body."); + }); + + it("new bundled names join the default set", () => { + writeBundled("bundle-only", "---\ndescription: only in bundle\n---\nbody"); + const registry = buildAgentRegistry(new Map(), loadBundledAgents(bundledDir)); + expect(registry.get("bundle-only")?.isDefault).toBe(true); + registerAgents(new Map(), loadBundledAgents(bundledDir)); + expect(getDefaultAgentNames()).toContain("bundle-only"); + expect(getAgentConfig("bundle-only")?.description).toBe("only in bundle"); + }); + + it("user agents still override bundled definitions", () => { + writeBundled("scout", "---\ndescription: bundled\n---\nbundled body"); + const user = new Map([ + ["scout", { + name: "scout", + description: "project override", + systemPrompt: "project body", + promptMode: "replace" as const, + extensions: true as const, + skills: true as const, + source: "project" as const, + sourcePath: "/proj/.pi/agents/scout.md", + }], + ]); + const registry = buildAgentRegistry(user, loadBundledAgents(bundledDir)); + expect(registry.get("scout")?.description).toBe("project override"); + expect(registry.get("scout")?.isDefault).not.toBe(true); + }); +}); + +describe("extension option validation", () => { + it("rejects a path that is both mandatory and excluded", () => { + const dir = mkdtempSync(join(tmpdir(), "pi-subagents-overlap-")); + const path = join(dir, "permission-system.ts"); + writeFileSync(path, "export default () => {};\n"); + expect(() => + createPiSubagentsExtension({ + mandatoryExtensionPaths: [path], + excludedExtensionPaths: [path], + }), + ).toThrow("both mandatory and excluded"); + rmSync(dir, { recursive: true, force: true }); + }); + + it("canonicalizes bundledAgentsDir and rejects missing directories", () => { + expect(() => + createPiSubagentsExtension({ bundledAgentsDir: join(tmpDir, "missing") }), + ).toThrow("is unavailable"); + expect(() => createPiSubagentsExtension({ bundledAgentsDir: bundledDir })).not.toThrow(); + }); +}); diff --git a/pi-subagents/test/prompts.test.ts b/pi-subagents/test/prompts.test.ts index f882597..170b7b9 100644 --- a/pi-subagents/test/prompts.test.ts +++ b/pi-subagents/test/prompts.test.ts @@ -433,4 +433,116 @@ describe("buildAgentPrompt", () => { expect(prompt.indexOf("")).toBeGreaterThan(prompt.indexOf("")); }); }); + + describe("host-owned base prompt", () => { + const BASE = "You are a subagent operating inside pi. The human user does not see your messages."; + + function baseConfig(promptMode: "append" | "replace", systemPrompt = "Custom instructions."): AgentConfig { + return { + name: "bundled-agent", + description: "Bundled", + builtinToolNames: [], + extensions: true, + skills: true, + systemPrompt, + promptMode, + inheritContext: false, + runInBackground: false, + isolated: false, + }; + } + + it("append mode: replaces parent prompt and drops the bridge, keeps env/instructions/tag", () => { + const prompt = buildAgentPrompt( + baseConfig("append"), + "/workspace", + env, + "SECRET parent prompt content", + undefined, + BASE, + ); + // No inherited parent prefix to keep cacheable, so the tag leads the + // prompt — same position as replace mode — and lets early session hooks + // identify the child before before_agent_start. + expect(prompt.startsWith('')).toBe(true); + expect(prompt).not.toContain("SECRET parent prompt content"); + expect(prompt).not.toContain(""); + expect(prompt).not.toContain("You are a pi coding agent sub-agent"); + expect(prompt).toContain("Working directory: /workspace"); + expect(prompt).toContain(""); + expect(prompt).toContain("Custom instructions."); + expect(prompt.match(//g)).toHaveLength(1); + const order = [ + '', + BASE, + "# Environment", + "", + ].map((s) => prompt.indexOf(s)); + expect(order.every((i) => i >= 0)).toBe(true); + expect(order).toEqual([...order].sort((a, b) => a - b)); + }); + + it("replace mode: drops the sub-agent header, keeps env + config prompt + tag", () => { + const prompt = buildAgentPrompt( + baseConfig("replace", "You are a read-only search agent."), + "/workspace", + env, + "SECRET parent prompt content", + undefined, + BASE, + ); + expect(prompt).not.toContain("You are a pi coding agent sub-agent"); + expect(prompt).not.toContain("SECRET parent prompt content"); + expect(prompt).not.toContain(""); + expect(prompt).toContain("Working directory: /workspace"); + expect(prompt).toContain("You are a read-only search agent."); + expect(prompt.match(//g)).toHaveLength(1); + // The tag leads, then base, then env, then the config's system prompt. + const order = [ + '', + BASE, + "# Environment", + "You are a read-only search agent.", + ].map((s) => prompt.indexOf(s)); + expect(order.every((i) => i >= 0)).toBe(true); + expect(order).toEqual([...order].sort((a, b) => a - b)); + }); + + it("keeps worktree and extras blocks", () => { + const prompt = buildAgentPrompt( + baseConfig("append"), + "/wt/copy", + env, + undefined, + { worktreeBase: "/repo", memoryBlock: "# Agent Memory\nRemember this." }, + BASE, + ); + expect(prompt).toContain("isolated git worktree copy of /repo"); + expect(prompt).toContain("# Agent Memory"); + expect(prompt).not.toContain(""); + }); + + it("empty agent systemPrompt yields no agent_instructions in append mode", () => { + const prompt = buildAgentPrompt( + baseConfig("append", ""), + "/workspace", + env, + "Parent.", + undefined, + BASE, + ); + expect(prompt).not.toContain(""); + }); + + it("blank base keeps upstream composition byte-for-byte", () => { + for (const promptMode of ["append", "replace"] as const) { + const config = baseConfig(promptMode); + for (const blank of [undefined, "", " \n "]) { + expect(buildAgentPrompt(config, "/workspace", env, "Parent.", undefined, blank)).toBe( + buildAgentPrompt(config, "/workspace", env, "Parent."), + ); + } + } + }); + }); }); diff --git a/tests/bundle-config.test.ts b/tests/bundle-config.test.ts index 59b14a9..1615755 100644 --- a/tests/bundle-config.test.ts +++ b/tests/bundle-config.test.ts @@ -136,3 +136,26 @@ test("every package-local @earendil-works/* devDependency is pinned to latest", } } }); + +test("subagent prompt profile: preamble non-blank, agent names match filename stems", async () => { + const subagentDir = join(bundleConfigDir, "prompt", "subagent"); + const preamble = await readFile(join(subagentDir, "preamble.md"), "utf8"); + assert.ok( + preamble.trim().length > 0, + "config/prompt/subagent/preamble.md must not be blank", + ); + const agentsDir = join(subagentDir, "agents"); + const agentFiles = (await readdir(agentsDir)).filter((name) => name.endsWith(".md")); + assert.ok(agentFiles.length > 0, "config/prompt/subagent/agents/ must ship agent files"); + for (const file of agentFiles) { + const raw = await readFile(join(agentsDir, file), "utf8"); + const frontmatter = raw.match(/^---\r?\n([\s\S]*?)\r?\n---/); + assert.ok(frontmatter, `${file} must open with a frontmatter block`); + const name = frontmatter[1].match(/^name:[ \t]*["']?([^"'\r\n]+?)["']?[ \t]*$/m)?.[1]; + assert.equal( + name, + file.slice(0, -".md".length), + `config/prompt/subagent/agents/${file} frontmatter name must match its filename stem`, + ); + } +}); diff --git a/tests/extension-load.test.ts b/tests/extension-load.test.ts index acdb704..62f2b70 100644 --- a/tests/extension-load.test.ts +++ b/tests/extension-load.test.ts @@ -10,19 +10,38 @@ import test from "node:test"; const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const hasPi = spawnSync("pi", ["--version"], { encoding: "utf8" }).status === 0; -test("subagents source is retained but disabled in the root bundle", async () => { +test("enabled subagents wrapper loads with its bundle-owned mandatory permission entry", { skip: !hasPi }, async () => { const packageJson = JSON.parse(await readFile(join(repositoryRoot, "package.json"), "utf8")) as { dependencies: Record; files: string[]; pi: { extensions: string[] }; }; - assert.ok(packageJson.files.includes("pi-subagents"), "the bundle must retain the maintained subagents source"); - assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], undefined); - assert.equal( + assert.ok(packageJson.files.includes("pi-subagents")); + assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], "file:./pi-subagents"); + assert.ok( packageJson.pi.extensions.includes("./extensions/subagents.ts"), - false, - "the bundle must not default-load the disabled subagents wrapper", + "the bundle must default-load the locally maintained subagents wrapper", ); + + const home = await mkdtemp(join(tmpdir(), "my-pi-subagents-wrapper-load-")); + const result = spawnSync( + "pi", + ["--no-extensions", "-e", join(repositoryRoot, "extensions", "subagents.ts"), "--mode", "rpc"], + { + encoding: "utf8", + timeout: 60_000, + env: { + ...process.env, + HOME: home, + XDG_CONFIG_HOME: join(home, ".config"), + PI_CODING_AGENT_DIR: join(home, ".pi-agent"), + PI_OFFLINE: "1", + }, + }, + ); + + assert.equal(result.status, 0, `${result.stdout}\n${result.stderr}`); + assert.doesNotMatch(`${result.stdout}\n${result.stderr}`, /Failed to load extension/u); }); test("all package extensions load together without global registration conflicts", { skip: !hasPi }, async () => { @@ -117,6 +136,9 @@ test("all package extensions load together without global registration conflicts assert.equal(permissionConfig.permission.ssh_connect, "ask", "SSH connection must enter the permission gate"); assert.equal(permissionConfig.permission.ssh_cd, "ask", "remote workspace changes must enter the permission gate"); assert.deepEqual(permissionConfig.authorizerChain, ["auto-review"], "SSH connection asks must reach AutoReview"); + assert.equal(permissionConfig.permission.Agent, "allow", "launching a subagent is side-effect free; child actions stay gated"); + assert.equal(permissionConfig.permission.get_subagent_result, "allow"); + assert.equal(permissionConfig.permission.steer_subagent, "allow"); assert.equal(hardGuardConfig.permission.path, undefined, "sensitive paths must not be hard-denied by default"); assert.equal(hardGuardConfig.permission.bash["rm -rf /"], "deny"); assert.equal( @@ -149,8 +171,18 @@ test("all package extensions load together without global registration conflicts "the packed bundle must include the locally maintained notification source", ); assert.equal(packageJson.dependencies["@ogulcancelik/pi-minimal-footer"], "file:./pi-minimal-footer"); - assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], undefined); - assert.equal(packageJson.pi.extensions.includes("./extensions/subagents.ts"), false); + assert.equal(packageJson.dependencies["@tintinweb/pi-subagents"], "file:./pi-subagents"); + assert.ok(packageJson.pi.extensions.includes("./extensions/subagents.ts")); + assert.ok( + packageJson.pi.extensions.indexOf("./extensions/permission-system.ts") < + packageJson.pi.extensions.indexOf("./extensions/subagents.ts"), + "the permission service must load before the subagents wrapper injects it as a mandatory child extension", + ); + assert.ok( + packageJson.pi.extensions.indexOf("./extensions/subagents.ts") < + packageJson.pi.extensions.indexOf("./pi-tool-search/extensions/index.ts"), + "the subagent orchestration tools must register before Tool Search builds its catalog", + ); assert.ok( packageJson.pi.extensions.includes("./pi-hermes-memory/src/index.ts"), "the bundle must load the locally maintained Hermes memory extension", diff --git a/tests/system-prompt.test.ts b/tests/system-prompt.test.ts index 661d48d..8f04abf 100644 --- a/tests/system-prompt.test.ts +++ b/tests/system-prompt.test.ts @@ -9,6 +9,7 @@ import { fileURLToPath } from "node:url"; import { applyToolCatalog, + isSubagentCustomPrompt, loadRoutingRules, loadSectionsConfig, loadToolCatalog, @@ -18,6 +19,8 @@ import { resolvePreamblePath, spliceCover, stripJsoncComments, + validateRoutingConfig, + validateSectionsConfig, type MutablePromptOptions, type RoutingRule, } from "../extensions/system-prompt-config.ts"; @@ -74,6 +77,40 @@ test("config/prompt files load with the expected shapes", () => { "chrome_profile_bridge", "tool_routing", ]); + assert.deepEqual(sections.subagentExclude, ["memory_policy", "chrome_profile_bridge"]); +}); + +test("routing scope validation: main/subagent accepted, other values rejected", () => { + assert.doesNotThrow(() => + validateRoutingConfig({ + version: 1, + rules: [ + { text: "both" }, + { scope: "main", text: "m" }, + { scope: "subagent", text: "s" }, + ], + }), + ); + assert.throws( + () => validateRoutingConfig({ version: 1, rules: [{ scope: "everyone", text: "x" }] }), + /scope must be "main" or "subagent"/, + ); +}); + +test("sections.jsonc validation: subagentExclude defaults to [] and rejects bad shapes", () => { + assert.deepEqual(validateSectionsConfig({ version: 1, order: ["tool_routing"] }), { + version: 1, + order: ["tool_routing"], + subagentExclude: [], + }); + assert.throws( + () => validateSectionsConfig({ version: 1, order: [], subagentExclude: "memory_policy" }), + /subagentExclude.*array of section names/, + ); + assert.throws( + () => validateSectionsConfig({ version: 1, order: [], subagentExclude: ["Bad Name"] }), + /not a valid section name/, + ); }); test("readPreamble reads the bundle preamble from config/prompt/", () => { @@ -185,6 +222,40 @@ test("renderRouting honours whenAll, whenAny and unconditional rules", () => { assert.equal(renderRouting([], ["a"]), ""); }); +test("renderRouting filters scoped rules by audience, unscoped rules render for both", () => { + const rules: RoutingRule[] = [ + { text: "both audiences" }, + { scope: "main", text: "main only" }, + { scope: "subagent", text: "subagent only" }, + ]; + + assert.equal(renderRouting(rules, undefined, "main"), "- both audiences\n- main only"); + assert.equal(renderRouting(rules, undefined, "subagent"), "- both audiences\n- subagent only"); + // The omitted-audience default stays "main" for callers that predate scopes. + assert.equal(renderRouting(rules, undefined), "- both audiences\n- main only"); +}); + +test("isSubagentCustomPrompt matches the contract only on customPrompt", () => { + assert.ok(isSubagentCustomPrompt('a\n')); + assert.ok(isSubagentCustomPrompt("")); + assert.ok(!isSubagentCustomPrompt("plain main-session prompt")); + assert.ok(!isSubagentCustomPrompt(undefined)); + assert.ok(!isSubagentCustomPrompt(42)); +}); + +test("bundled ssh_connect rules differ per audience", () => { + const routing = loadRoutingRules(); + const tools = ["ssh_connect", "ssh_bash"]; + + const main = renderRouting(routing.rules, tools, "main"); + assert.match(main, /the user explicitly names an imported host/); + assert.doesNotMatch(main, /the delegated task explicitly names/); + + const subagent = renderRouting(routing.rules, tools, "subagent"); + assert.match(subagent, /the delegated task explicitly names an imported host/); + assert.doesNotMatch(subagent, /the user explicitly names an imported host/); +}); + test("bundled routing rules select the same guidance the retired tool-routing extension emitted", () => { const routing = loadRoutingRules(); @@ -275,12 +346,18 @@ test("spliceCover degenerates to the preamble when the prompt has no sections", interface FakePromptOptions extends MutablePromptOptions { forceSystemPrompt?: string; + customPrompt?: string; } /** Simplified re-render of Pi's prompt from the mutable options object. */ function renderPiPrompt(options: FakePromptOptions): string { if (options.forceSystemPrompt !== undefined) return options.forceSystemPrompt; - const parts = ["PI DEFAULT PREAMBLE"]; + // Like the real host: a non-empty customPrompt owns the preamble section. + const preamble = + typeof options.customPrompt === "string" && options.customPrompt.length > 0 + ? options.customPrompt + : "PI DEFAULT PREAMBLE"; + const parts = [preamble]; const snippets = Object.entries(options.toolSnippets ?? {}); if (snippets.length > 0) { parts.push(`\n${snippets.map(([n, s]) => `- ${n}: ${s}`).join("\n")}\n`); @@ -378,6 +455,86 @@ test("composer degrades to text splicing when an earlier extension froze the pro ); }); +test("composer keeps a customPrompt-owned preamble (subagent / SYSTEM.md) intact", () => { + const composer = createSystemPromptComposer(); + const customPrompt = 'You are the Explore agent.\n'; + const options: FakePromptOptions = { + customPrompt, + selectedTools: ["read"], + toolSnippets: { read: "reads files" }, + }; + const { ctx } = makeUiCtx(); + const event = { + systemPromptOptions: options, + systemPrompt: `${customPrompt}\n\n\n- read: reads files\n`, + }; + + const result = composer.handleBeforeAgentStart(event as never, ctx); + const output = result?.systemPrompt ?? ""; + + assert.ok(output.startsWith(customPrompt), "the agent's own preamble is kept verbatim"); + assert.ok( + output.includes(''), + "the identity the permission system reads survives", + ); + assert.ok( + !output.includes(BUNDLE_PREAMBLE), + "the bundle preamble must not be spliced over a customPrompt-owned preamble", + ); + assert.match(output, //, "routing still degrades to an appended section"); +}); + +test("composer treats an customPrompt as a subagent session", () => { + const composer = createSystemPromptComposer(); + const customPrompt = + 'You are a subagent.\n\n# Environment\nWorking directory: /work'; + const options: FakePromptOptions = { + customPrompt, + selectedTools: ["read", "ssh_connect"], + toolSnippets: { read: "reads files", ssh_connect: "connects" }, + sections: { + memory_policy: "memory guidance for main sessions", + chrome_profile_bridge: "chrome bridge preamble", + remote_ssh: "remote ssh preamble", + }, + }; + const { ctx } = makeUiCtx(); + + const result = composer.handleBeforeAgentStart(makeEvent(options) as never, ctx); + const output = result?.systemPrompt ?? ""; + + assert.ok(output.startsWith(customPrompt), "the child preamble stays untouched"); + assert.doesNotMatch(output, //, "subagentExclude removes memory_policy"); + assert.doesNotMatch(output, //, "subagentExclude removes chrome_profile_bridge"); + assert.match(output, //, "unexcluded sections are kept"); + assert.match(output, /the delegated task explicitly names an imported host/, "subagent-scope routing renders"); + assert.doesNotMatch(output, /the user explicitly names an imported host/, "main-scope routing stays out"); +}); + +test("a main session keeps excluded sections and main-scope routing even with a literal tag", () => { + const composer = createSystemPromptComposer(); + // The tag appears inside a contributed section (project docs can carry the + // literal text) but there is no customPrompt — this is a main session. + const options: FakePromptOptions = { + selectedTools: ["read", "ssh_connect"], + toolSnippets: { read: "reads files", ssh_connect: "connects" }, + sections: { + memory_policy: 'docs mention ', + chrome_profile_bridge: "chrome bridge preamble", + }, + }; + const { ctx } = makeUiCtx(); + + const result = composer.handleBeforeAgentStart(makeEvent(options) as never, ctx); + const output = result?.systemPrompt ?? ""; + + assert.ok(output.startsWith(BUNDLE_PREAMBLE), "the bundle preamble still splices for a main session"); + assert.match(output, //, "main sessions are unaffected by subagentExclude"); + assert.match(output, //); + assert.match(output, /the user explicitly names an imported host/, "main-scope routing renders"); + assert.doesNotMatch(output, /the delegated task explicitly names/, "subagent-scope routing stays out"); +}); + test("composer falls back to text appends when the host exposes no prompt options", () => { const composer = createSystemPromptComposer(); const { ctx } = makeUiCtx();