@prismshadow/penguin-core 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +41 -0
- package/dist/chunk-FX4CCIN7.js +379 -0
- package/dist/chunk-FX4CCIN7.js.map +1 -0
- package/dist/chunk-HRIXWKKE.js +1 -0
- package/dist/chunk-HRIXWKKE.js.map +1 -0
- package/dist/chunk-JC6SC6KF.js +336 -0
- package/dist/chunk-JC6SC6KF.js.map +1 -0
- package/dist/index.d.ts +1462 -0
- package/dist/index.js +5187 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces-CMSN7-pO.d.ts +487 -0
- package/dist/interfaces.d.ts +2 -0
- package/dist/interfaces.js +2 -0
- package/dist/interfaces.js.map +1 -0
- package/dist/model-catalog-DTu0IPjs.d.ts +266 -0
- package/dist/omnimessage/index.d.ts +110 -0
- package/dist/omnimessage/index.js +69 -0
- package/dist/omnimessage/index.js.map +1 -0
- package/dist/state/model-catalog.d.ts +1 -0
- package/dist/state/model-catalog.js +19 -0
- package/dist/state/model-catalog.js.map +1 -0
- package/dist/types-D6FERSdT.d.ts +293 -0
- package/package.json +58 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/state/paths.ts","../src/state/default-config.ts","../src/state/builtin-agents.ts","../src/state/project-config.ts","../src/state/agent-state.ts","../src/state/example-benchmark.ts","../src/state/agent-vault.ts","../src/state/index.ts","../src/llm/generative-model.ts","../src/llm/tool-call-ids.ts","../src/environment/tools/exec-command.ts","../src/environment/tools/command/session.ts","../src/environment/tools/background/registry.ts","../src/environment/tools/background/limits.ts","../src/environment/tools/background/wake-signal.ts","../src/environment/tools/background/capped-buffer.ts","../src/environment/tools/command/session-manager.ts","../src/environment/tools/command/limits.ts","../src/environment/tools/input-command.ts","../src/environment/tools/subagent/session-manager.ts","../src/environment/tools/subagent/session.ts","../src/environment/tools/subagent/limits.ts","../src/environment/tools/subagent/collect.ts","../src/environment/tools/run-subagent.ts","../src/environment/tools/input-subagent.ts","../src/environment/tools/read-image.ts","../src/environment/tools/describe-image.ts","../src/environment/tools/registry.ts","../src/environment/environment.ts","../src/trace/writer.ts","../src/internal/dates.ts","../src/trace/resume.ts","../src/engine/context-engine.ts","../src/internal/session-support.ts","../src/session-title.ts","../src/session.ts","../src/agent.ts","../src/index.ts"],"sourcesContent":["/**\n * Local directory layout for Agent State and Project config.\n *\n * Strictly follows the `~/.penguin/data/<project>/agents/<agent>/...` structure.\n * This module only provides constants and pure path functions; it never creates directories or reads/writes files.\n * Docs: /docs/sessions-and-traces § \"Data layout\".\n */\nimport os from \"node:os\";\nimport path from \"node:path\";\n\n/** Default Project id used when none is specified. */\nexport const DEFAULT_PROJECT_ID = \"default_project\";\n\n/** Default Agent id used when none is specified. */\nexport const DEFAULT_AGENT_ID = \"default_agent\";\n\n/**\n * Resolves the local data root directory.\n * Prefers the `PENGUIN_HOME` environment variable, otherwise falls back to `~/.penguin/data`\n * (under the hidden `~/.penguin` home so it never collides with unrelated folders, and in a\n * `data/` subdir kept separate from the installer's binaries under `~/.penguin`).\n */\nexport function resolveRoot(): string {\n return process.env.PENGUIN_HOME ?? path.join(os.homedir(), \".penguin\", \"data\");\n}\n\n/** `<root>/<projectId>`. */\nexport function projectDir(root: string, projectId: string): string {\n return path.join(root, projectId);\n}\n\n/** `<projectDir>/agents`, the container directory holding every Agent in the Project. */\nexport function agentsDir(root: string, projectId: string): string {\n return path.join(projectDir(root, projectId), \"agents\");\n}\n\n/** `<projectDir>/agents/<agentId>`. */\nexport function agentDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentsDir(root, projectId), agentId);\n}\n\n/** `<agentDir>/agent_state`. */\nexport function agentStateDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentDir(root, projectId, agentId), \"agent_state\");\n}\n\n/** `<agentDir>/traces`. */\nexport function tracesDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentDir(root, projectId, agentId), \"traces\");\n}\n\n/** `<agentDir>/scratchpad`, the Agent's temporary/draft file directory (the model creates a subdirectory per Session id). */\nexport function scratchpadDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentDir(root, projectId, agentId), \"scratchpad\");\n}\n\n/** `<agentDir>/workspaces`. */\nexport function workspacesDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentDir(root, projectId, agentId), \"workspaces\");\n}\n\n/**\n * `<projectDir>/.project_config.toml`, the Project's single config file (a hidden file, not\n * shown by default `ls`, written with mode 0600; model entries are inlined with their credential,\n * see state/project-config.ts).\n */\nexport function projectConfigPath(root: string, projectId: string): string {\n return path.join(projectDir(root, projectId), \".project_config.toml\");\n}\n\n/** `<agentStateDir>/system_config.yaml`. */\nexport function systemConfigPath(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \"system_config.yaml\");\n}\n\n/** `<agentStateDir>/AGENTS.md`. */\nexport function agentsMdPath(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \"AGENTS.md\");\n}\n\n/** `<agentStateDir>/.vault.toml`, the Agent-level environment-variable vault (see state/agent-vault.ts). */\nexport function agentVaultPath(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \".vault.toml\");\n}\n\n/** `<agentStateDir>/tools`, reserved for user-defined Tool config. */\nexport function toolsDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \"tools\");\n}\n\n/** `<agentStateDir>/memory`. */\nexport function memoryDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \"memory\");\n}\n\n/** `<agentStateDir>/skills`. */\nexport function skillsDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \"skills\");\n}\n\n/** `<agentStateDir>/schedule`, the scheduled-task directory (doesn't exist when unconfigured). */\nexport function scheduleDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentStateDir(root, projectId, agentId), \"schedule\");\n}\n\n/** `<agentDir>/benchmarks`, the capability-evaluation question bank and scores (doesn't exist when unconfigured). */\nexport function benchmarksDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentDir(root, projectId, agentId), \"benchmarks\");\n}\n\n/** `<agentDir>/snapshots`, Agent State version snapshots (doesn't exist when unconfigured). */\nexport function snapshotsDir(root: string, projectId: string, agentId: string): string {\n return path.join(agentDir(root, projectId, agentId), \"snapshots\");\n}\n","/**\n * Default system configuration for Agent State (written to `system_config.yaml`) and the\n * default `AGENTS.md` (empty).\n *\n * Runtime Prompt and tool configuration should come from editable files;\n * code only supplies the initial defaults. `system_config.yaml` holds the relatively stable\n * system-level Prompt, built-in tools, and MCP Server configuration; `AGENTS.md` is injected\n * via a system Prompt placeholder.\n *\n * The system Prompt is sectioned and trimmed as needed (Role/Personality/Success\n * criteria/Constraints/Stop rules/File system/Suggested workflows); it does not describe\n * specific tools (that comes from the tool schema). AGENTS.md, Vault/Skills, and Environment\n * injection go at the end.\n *\n * Placeholders (`{{...}}`) appear only in the trailing injection zones (AGENTS.md / Vault /\n * Skills / Environment); elsewhere the body uses angle-bracket notation such as\n * \\`<project_dir>\\`, \\`<agent_id>\\`, \\`<session_id>\\` — these are **not substituted**; the model\n * fills in the actual values from the Environment section itself.\n */\nimport type { MCPServerConfig, ThinkingLevelName, ToolDefinitionConfig } from \"../interfaces.js\";\nimport type { CompactionMode } from \"../omnimessage/types.js\";\n\n/** Docs: /docs/configuration § \"System prompt placeholders\". */\nexport const AGENTS_MD_PLACEHOLDER = \"{{AGENTS_MD}}\";\nexport const VAULT_KEYS_PLACEHOLDER = \"{{VAULT_KEYS}}\";\nexport const SKILL_METADATA_PLACEHOLDER = \"{{SKILL_METADATA}}\";\nexport const SESSION_ID_PLACEHOLDER = \"{{SESSION_ID}}\";\nexport const CWD_PLACEHOLDER = \"{{CWD}}\";\nexport const AGENT_ID_PLACEHOLDER = \"{{AGENT_ID}}\";\nexport const PROJECT_DIR_PLACEHOLDER = \"{{PROJECT_DIR}}\";\nexport const PLATFORM_PLACEHOLDER = \"{{PLATFORM}}\";\nexport const OS_VERSION_PLACEHOLDER = \"{{OS_VERSION}}\";\nexport const DATE_PLACEHOLDER = \"{{DATE}}\";\n\n/**\n * Context compaction config (the `compaction` section of `system_config.yaml`).\n * Docs: /docs/configuration § \"Agent config\".\n */\nexport interface CompactionConfig {\n /** Context Token threshold (taken from the most recent token_usage's request.total); defaults to 128000, <=0 disables. */\n max_context_length?: number;\n /** Session cumulative turn threshold (counted in LLM Requests, across Tasks); defaults to -1, <=0 means no limit. */\n max_session_turns?: number;\n /** Compaction mode; defaults to summarize. */\n mode?: CompactionMode;\n /** Prompt template for summarize compaction; defaults to the built-in value (editable config, not hardcoded). */\n prompt?: string;\n}\n\n/**\n * System-level config for Agent State, serialized as `system_config.yaml`.\n * Docs: /docs/configuration § \"Agent config\".\n */\nexport interface SystemConfig {\n /** Agent display name (display name is separate from id; falls back to id when unset). */\n name?: string;\n /** Agent description. */\n description?: string;\n /** Agent State version number: a natural number, 1 on creation, incremented on successful optimization; a missing field is treated as 1. */\n version?: number;\n /** System-level Prompt (relatively stable; should not be modified frequently). */\n system_prompt: string;\n /** Max LLM turns per Task (a runtime parameter that belongs to Agent config, not specified when creating a Session). */\n max_turns?: number;\n model?: {\n max_tokens?: number;\n thinking_level?: ThinkingLevelName;\n timeoutMs?: number;\n };\n /** Context compaction (enabled by default, max_context_length 128k, mode summarize). */\n compaction?: CompactionConfig;\n tools?: {\n /** Built-in system tool configuration. */\n builtin?: ToolDefinitionConfig[];\n /** MCP Server configuration. */\n mcpServers?: MCPServerConfig[];\n };\n}\n\nconst DEFAULT_SYSTEM_PROMPT = `# Role\nYou are PenguinHarness, an agent that completes the user's requests on their machine with the tools available to you.\n\n# Personality\nCommunicate with the user precisely and concisely, yet with warmth. Do not repeatedly explain your tools or restate their results.\n\n# Success criteria\n- Before delivering the result, check that every problem in the request has been solved.\n- Verify your work through every available means; never claim a result you did not observe.\n\n# Constraints\n- Make the smallest change that satisfies the request; do not modify unrelated files.\n- Destructive operations are forbidden.\n- Never kill a process you did not start yourself (e.g. to free a busy port) unless the user explicitly asks you to.\n- If a tool call fails, read the error, adjust, and retry; never repeat the same failing input.\n\n# Stop rules\n- Stop and give the final answer once the success criteria are met.\n- If the request is ambiguous, stop and ask the user for clarification instead of guessing their intent.\n- If you hit an error you cannot resolve, stop and report the blocker to the user.\n\n# Tool use\n- Prefer solving problems with your tools: inspect the real files and environment and run real commands instead of answering from memory or guessing.\n- When you need information from the internet, browse it with your shell tool — \\`curl\\` for pages and APIs, or Playwright (if installed) for dynamic sites.\n\n# System markers\nSome user-side messages are system-synthesized records, not user text to answer directly:\n- \\`<turn_aborted>\\`: the previous round was interrupted. Inside are the original request, your partial thinking/text, and the tool calls already issued with their results. Continue from where it left off; do not re-run tools whose results are already included.\n- \\`<turn_retried>\\`: the previous attempt of this round failed on a transport error (timeout or malformed response) — the user did NOT interrupt — and this request is the automatic retry. Inside are your partial thinking/text and the tool calls already executed with their results. Continue from them; do not re-run tools whose results are already included.\n- \\`<context_summary>\\`: earlier conversation was compacted. This summary replaced the raw transcript and is its only record; treat it as established context and continue the task from it.\n\n# File system\n- Angle-bracket markers such as \\`<project_dir>\\`, \\`<agent_id>\\` and \\`<session_id>\\` are not literal paths — substitute the matching values from the Environment section.\n- You run inside the user's working folder (\\`CWD\\` in Environment).\n- The project directory is \\`<project_dir>\\`; every agent of this project lives under \\`<project_dir>/agents/\\`, so another agent's assets are at \\`<project_dir>/agents/<its_agent_id>/agent_state/\\`.\n- Your own Agent State is \\`<project_dir>/agents/<agent_id>/agent_state/\\` — it holds your assets such as \\`skills/\\`, and its \\`AGENTS.md\\` is already included in your context. Reach these paths directly.\n- For temporary and scratch files, create a subdirectory named after the current Session ID under your scratchpad: \\`<project_dir>/agents/<agent_id>/scratchpad/<session_id>/\\`. Build intermediates there, but always place final deliverables in the workspace (under \\`CWD\\`) — files left in the scratchpad are not part of your output.\n- When you create or update a file in the workspace, mention its workspace-relative path in backticks (e.g. \\`src/app.py\\`) in your reply, so the user can open it from the message.\n- Never read, copy, print or otherwise access \\`<project_dir>/.project_config.toml\\` or any agent's \\`agent_state/.vault.toml\\` — they hold the user's API keys and other secrets, which are none of your business. Configuration is CLI-only: change models or credentials with \\`penguin config ...\\` commands. If a task seems to require these files, say so and ask the user instead.\n\n# Suggested workflows\nThese are recommendations, not requirements; adapt them as the task demands.\n- For a long-horizon task, first write a plan in Markdown to \\`<project_dir>/agents/<agent_id>/scratchpad/<session_id>/PLAN.md\\`, containing a task overview and an itemized step-by-step plan; update it after each completed step to keep execution consistent.\n- Delegate self-contained subtasks to other agents with the \\`run_subagent\\` tool; dispatch independent subtasks in parallel. Start every delegation prompt with your own agent id (e.g. \"Caller agent: <agent_id>\") and name the skill the subagent should use when the task matches one. Subagents share your Workspace — exchange data through files. If \\`run_subagent\\` is not in your tool list, you are the subagent: do the work yourself.\n- To visit web pages, prefer Playwright when installed; otherwise \\`curl\\`. When building a web app or frontend, prefer React.\n\n<developer_instructions>\nCustom instructions from the developer-editable AGENTS.md.\n\n{{AGENTS_MD}}\n</developer_instructions>\n\n# Vault\nThe vault holds this agent's per-agent secrets (agent_state/.vault.toml). Each entry is injected into your shell subprocesses as an environment variable — values never appear in your context. Use the variable names below in commands when a task needs them.\n{{VAULT_KEYS}}\n\n# Skills\nSkills are reusable instruction packages stored under <project_dir>/agents/<agent_id>/agent_state/skills/<skill_name>/SKILL.md. There is no skill tool: when a task matches an installed skill below, or the user asks to use one (a message may start with a <use_skills> block listing skill names), first read that skill's SKILL.md in full with a shell command, then follow it. If a request only names a skill without a concrete task, ask the user what they need before starting.\n{{SKILL_METADATA}}\n\n# Environment\n- Platform: {{PLATFORM}}\n- OS Version: {{OS_VERSION}}\n- Date: {{DATE}}\n- CWD: {{CWD}}\n- Agent ID: {{AGENT_ID}}\n- Project Dir: {{PROJECT_DIR}}\n- Session ID: {{SESSION_ID}}`;\n\n/**\n * Built-in default compaction Prompt (summarize mode): tells the model that after\n * compaction the raw transcript is no longer visible and the\n * summary is the only record, so it must include everything needed to continue the task,\n * and no tools may be called while writing the summary.\n */\nexport const DEFAULT_COMPACTION_PROMPT =\n \"You have a partial transcript of the task above. Write a summary of it wrapped in \" +\n \"`<summary></summary>` tags. This summary will replace the transcript: in the next \" +\n \"context window the raw transcript above will no longer be visible and this summary \" +\n \"will be its only record, so include everything needed to continue the task — the \" +\n \"original request, current state, next steps, and any learnings. Do not call any \" +\n \"tools while writing the summary; respond with text only.\";\n\n/**\n * Default built-in system tools: bash execution and subagent spawning.\n * Docs: /docs/tools § \"Built-in tools\".\n */\nfunction defaultBuiltinTools(): ToolDefinitionConfig[] {\n return [\n {\n name: \"exec_command\",\n description:\n \"Run a shell command in the workspace to read, write, edit files and run programs. \" +\n \"Run long-lived commands (servers, watchers, builds) in the foreground: past yield_time_ms \" +\n \"they keep running in the background with a process_id. Do not background them with `&` — \" +\n \"the whole process group is cleaned up when the foreground command exits.\",\n parameters: {\n type: \"object\",\n properties: {\n cmd: {\n type: \"string\",\n description: \"Shell command to execute.\",\n },\n workdir: {\n type: \"string\",\n description:\n \"Working directory for the command; defaults to the cwd. Optionally a path relative to the cwd, or an absolute path.\",\n },\n yield_time_ms: {\n type: \"number\",\n description:\n \"How long to wait for the command before yielding. If it is still running when this elapses, the tool returns the output so far plus a process_id, and the command keeps running in the background (drive it with input_command). Defaults to 60000; minimum 250, capped below the tool timeout.\",\n },\n },\n required: [\"cmd\"],\n },\n permission: \"rw\",\n timeoutMs: 120000,\n maxOutputLength: 16000,\n },\n {\n name: \"input_command\",\n description:\n \"Interact with a running command session started by exec_command: write to its stdin, send Ctrl-C, or poll for new output. Identify the session with its process_id.\",\n parameters: {\n type: \"object\",\n properties: {\n process_id: {\n type: \"string\",\n description: \"The process_id returned by exec_command for the running command session.\",\n },\n chars: {\n type: \"string\",\n description:\n 'Characters to write to the command\\'s stdin. Send \"\\\\u0003\" alone to deliver Ctrl-C (SIGINT); mixing it with other characters is an error. Empty (the default) writes nothing and only polls for new output and exit status.',\n },\n yield_time_ms: {\n type: \"number\",\n description:\n \"How long to wait for new output or exit before returning. Non-empty writes default to 250; empty polls default to 5000. Minimum 250, capped below the tool timeout.\",\n },\n },\n required: [\"process_id\"],\n },\n permission: \"rw\",\n // An empty poll can wait out a build/test run (the yield ceiling is derived from timeoutMs, clamped inside the tool).\n timeoutMs: 130000,\n maxOutputLength: 16000,\n },\n {\n name: \"run_subagent\",\n description:\n \"Delegate a self-contained subtask to a subagent that runs autonomously in the same workspace and returns its final answer. Use it for focused sub-tasks you can fully specify in one prompt. Optionally choose a specific agent via `agent_id` and a model via `model_id`. \" +\n 'Begin the prompt by identifying yourself with your own agent id (from the Environment section), e.g. \"Caller agent: default_agent\" — the subagent cannot otherwise tell who invoked it.',\n parameters: {\n type: \"object\",\n properties: {\n prompt: {\n type: \"string\",\n description:\n \"The complete task for the subagent: include all context it needs and the exact final output you expect back.\",\n },\n agent_id: {\n type: \"string\",\n description:\n \"Which agent to run as the subagent; defaults to the current agent when omitted.\",\n },\n model_id: {\n type: \"string\",\n description:\n \"Which model the subagent should use; defaults to the Project default model when omitted.\",\n },\n yield_time_ms: {\n type: \"number\",\n description:\n \"How long to wait for the subagent before yielding. If it is still working when this elapses, the tool returns the output so far plus a subagent_id, and the subagent keeps running in the background (drive it with input_subagent). Defaults to 300000; minimum 250, capped below the tool timeout.\",\n },\n },\n required: [\"prompt\"],\n },\n permission: \"rw\",\n // Subagent tasks typically run far longer than a single command, so the timeout ceiling is raised accordingly.\n timeoutMs: 600000,\n maxOutputLength: 16000,\n },\n {\n name: \"input_subagent\",\n description:\n \"Interact with a background subagent started by run_subagent: poll for new output, or send a follow-up prompt once it is idle to continue the same subagent session. Identify the session with its subagent_id. Pending tool approvals of the subagent are surfaced while this tool is waiting.\",\n parameters: {\n type: \"object\",\n properties: {\n subagent_id: {\n type: \"string\",\n description: \"The subagent_id returned by run_subagent for the background subagent.\",\n },\n prompt: {\n type: \"string\",\n description:\n \"A follow-up task for the subagent, delivered as a new user message on the same session. Only accepted when the subagent is idle (its previous run finished). Empty (the default) sends nothing and only polls for new output and status.\",\n },\n yield_time_ms: {\n type: \"number\",\n description:\n \"How long to wait for new output or completion before returning. Follow-up prompts default to 300000; empty polls default to 10000. Minimum 250, capped below the tool timeout.\",\n },\n },\n required: [\"subagent_id\"],\n },\n permission: \"rw\",\n // Same generous timeout tier as run_subagent: an empty poll can wait a long time for the subagent to wrap up.\n timeoutMs: 600000,\n maxOutputLength: 16000,\n },\n // The image-reading tools are mutually exclusive based on the session model's type\n // (marked via each entry's forModel, filtered at assembly time): read_image is designed\n // for vision models (the image is fed back as image content); describe_image is designed\n // for text-only models (the image plus the prompt are sent to the Project's configured\n // vision model, vision_model, whose text answer becomes the tool output).\n {\n name: \"read_image\",\n forModel: \"vision\",\n description:\n \"Read an image and return it as image content for you to view. Accepts an http(s) URL \" +\n \"or a local file path (relative paths resolve against the workspace). \" +\n \"Supports png/jpeg/gif/webp up to 5MB.\",\n parameters: {\n type: \"object\",\n properties: {\n source: {\n type: \"string\",\n description:\n \"Image to read: an http(s) URL, or a local file path (absolute, or relative to the workspace).\",\n },\n },\n required: [\"source\"],\n },\n permission: \"r\",\n timeoutMs: 60000,\n maxOutputLength: 16000,\n },\n {\n name: \"describe_image\",\n forModel: \"text-only\",\n description:\n \"Describe an image and return a TEXT description of it. The current model does not accept \" +\n \"images directly, so the image is analyzed by the project's configured vision model and \" +\n \"you get its text answer back. Use `prompt` to ask exactly what you need to know about \" +\n \"the image (e.g. transcribe text, describe a chart, locate a UI element). Accepts an \" +\n \"http(s) URL or a local file path (relative paths resolve against the workspace). \" +\n \"Supports png/jpeg/gif/webp up to 5MB.\",\n parameters: {\n type: \"object\",\n properties: {\n source: {\n type: \"string\",\n description:\n \"Image to read: an http(s) URL, or a local file path (absolute, or relative to the workspace).\",\n },\n prompt: {\n type: \"string\",\n description:\n \"What to ask about the image; the vision model answers this. Defaults to a detailed description.\",\n },\n },\n required: [\"source\"],\n },\n permission: \"r\",\n // Includes one vision-model request, so the timeout is slightly wider than plain image reading.\n timeoutMs: 90000,\n maxOutputLength: 16000,\n },\n ];\n}\n\n/** Agent State version number: an invalid or missing field is always treated as 1. */\nexport function agentStateVersion(config: Pick<SystemConfig, \"version\">): number {\n const v = config.version;\n return typeof v === \"number\" && Number.isInteger(v) && v >= 1 ? v : 1;\n}\n\n/** Returns the default system configuration for Agent State. */\nexport function defaultSystemConfig(): SystemConfig {\n return {\n version: 1,\n system_prompt: DEFAULT_SYSTEM_PROMPT,\n max_turns: 100,\n model: {\n max_tokens: 32000,\n thinking_level: \"medium\",\n timeoutMs: 120000,\n },\n compaction: {\n max_context_length: 128000,\n max_session_turns: -1,\n mode: \"summarize\",\n prompt: DEFAULT_COMPACTION_PROMPT,\n },\n tools: {\n builtin: defaultBuiltinTools(),\n mcpServers: [],\n },\n };\n}\n\n/**\n * Returns the default editable `AGENTS.md` content: an empty string — no guidance is\n * preprovisioned by default; Subagent delegation conventions and general task practices\n * live in the default template's Suggested workflows section as a soft convention.\n * Kept so initialization can still write an empty AGENTS.md file.\n */\nexport function defaultAgentsMd(): string {\n return \"\";\n}\n","/**\n * Preset content for builtin Agents; Skill documentation lives in @prismshadow/penguin-skills\n * (the library files are read live when building the preset).\n *\n * - Every Project comes with a single builtin Agent: `default_agent` (the General Agent, the\n * default conversational Agent), which has every Skill in the library installed at\n * initialization. Dedicated capabilities (creating an Agent, optimizing an Agent, etc.)\n * are carried by Skills rather than dedicated builtin Agents.\n * - The preset carries no AGENTS.md: the default AGENTS.md is empty, with delegation and task\n * conventions living in the default template's Suggested Workflows section.\n * - Skill metadata is auto-injected into the system Prompt via the `{{SKILL_METADATA}}`\n * placeholder; it's not registered in AGENTS.md.\n */\nimport { loadLibrarySkills, type LibrarySkill } from \"@prismshadow/penguin-skills\";\nimport { DEFAULT_AGENT_ID } from \"./paths.js\";\n\n/** The set of Project builtin Agent ids (supplied along with the Project, cannot be deleted from Web). */\nexport const BUILTIN_AGENT_IDS: readonly string[] = [DEFAULT_AGENT_ID];\n\n/** Agent initialization preset (only takes effect at initialization; ignored when loading an existing Agent). */\nexport interface AgentPreset {\n /** Display name written to system_config.yaml. */\n name?: string;\n /** Description written to system_config.yaml. */\n description?: string;\n /** Overrides the default AGENTS.md content. */\n agentsMd?: string;\n /** Skills installed at initialization (installs none by default). */\n skills?: LibrarySkill[];\n}\n\n/**\n * The preset list for a Project's builtin Agents (each initialized in turn when the Project is\n * created; an existing Agent is never overwritten). The only builtin Agent is default_agent:\n * installs every Skill in the library, with no preset AGENTS.md.\n */\nexport function builtinProjectAgentPresets(): Array<{ agentId: string; preset: AgentPreset }> {\n return [\n {\n agentId: DEFAULT_AGENT_ID,\n preset: {\n name: \"General Agent\",\n description: \"General-purpose agent that completes the user's requests with its tools.\",\n skills: loadLibrarySkills(),\n },\n },\n ];\n}\n","/**\n * Project config storage (`<project>/.project_config.toml`).\n *\n * Records the available Models, the default Model, and each Model's credential (Model\n * is decoupled from Agent — the Model selection isn't stored in Agent State, but maintained by\n * the Project). Config is persisted as TOML.\n *\n * `.project_config.toml` is the Project's **single config file**: a hidden file (not shown by\n * default `ls`), written to disk with mode 0600; credentials (api_key / base_url) are **inlined\n * on the model entry** rather than split into a supplementary area and a separate secrets file.\n * It can only be read/written via the system interfaces (CLI / Web) — never hand-edited by the\n * model or the user; the system Prompt is forbidden from reading this file, `loadProjectConfig`\n * returns plaintext, and masking is applied at the interface layer (when shown by server / cli).\n *\n * Model references are **fully split into separate fields**: an entry stores\n * `provider` and `model_id` as two independent fields, with the `(provider, model_id)` pair as\n * the unique key — string concatenation like `<provider>/<id>` is forbidden anywhere in the\n * pipeline. `model_id` is the upstream request id, sent to AgentHub unchanged; `default_model` /\n * `vision_model` are paired `{ provider, model_id }` references (a TOML inline table).\n */\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { parse as parseToml, stringify as stringifyToml } from \"smol-toml\";\nimport { inferProviderForUpstream, presetModelEntries } from \"./model-catalog.js\";\nimport { projectConfigPath } from \"./paths.js\";\n\n/** Model reference: a `(provider, model_id)` pair (never string-concatenated anywhere). */\nexport interface ModelRef {\n provider: string;\n /** Upstream model id (the request id sent to AgentHub unchanged). */\n model_id: string;\n}\n\n/**\n * Display form of a paired reference (shared by error messages and CLI output):\n * `(provider=..., model_id=...)`. For display only — it isn't any storage or addressing format.\n */\nexport function formatModelRef(ref: ModelRef): string {\n return `(provider=${ref.provider}, model_id=${ref.model_id})`;\n}\n\n/**\n * Pricing for a single Model: three price buckets, in USD per million tokens.\n * Docs: /docs/configuration § \"Project config\".\n */\nexport interface ModelPricing {\n /** Pricing unit tag; currently only `usd_per_mtok` (USD per million tokens). */\n unit: \"usd_per_mtok\";\n cache_read: number;\n cache_write: number;\n output: number;\n}\n\n/**\n * A single available Model entry (credential inlined, single config file).\n * Docs: /docs/models § \"The per-Project model table\".\n */\nexport interface ModelEntry {\n /** provider group (stored separately from `model_id`; the pair is the entry's unique key). */\n provider: string;\n /** Upstream model id: the actual request id sent to AgentHub, used paired with provider for display, pricing, and stats. */\n model_id: string;\n context_window?: number;\n /**\n * AgentHub client protocol (`openai` / `claude-4-8` / `deepseek-v4` / …); defaults to being\n * inferred by AgentHub from the request id (`model_id`). A third-party model speaking the\n * OpenAI protocol should set this to `openai`.\n */\n client_type?: string;\n /**\n * Display name (the model page card title): only persisted when it differs from the builtin\n * catalog (the user renamed it / a custom model); when not persisted, it's inferred from the\n * builtin catalog by `(provider, model_id)`, falling back to displaying model_id if it can't be\n * inferred.\n */\n display_name?: string;\n /**\n * Whether image input is supported (vision/multimodal); defaults to supported. For a model\n * tagged `false` (e.g. DeepSeek): images from conversation input are saved to the session\n * scratchpad and handed over as a file path spliced into the text, and the image-reading tool\n * switches to describe_image (a vision model reads on its behalf) — the image never directly\n * enters that session's history.\n */\n vision?: boolean;\n /** Pricing info; absent means this Model's cost isn't counted. */\n pricing?: ModelPricing;\n /** API key (inlined credential); left empty falls back to the vendor's environment variable. */\n api_key?: string;\n /** Custom base URL (inlined credential); preset for gateway models. */\n base_url?: string;\n /** api_key's write timestamp (ISO 8601; a display field maintained by the interface layer). */\n created_at?: string;\n}\n\n/**\n * Project-level config.\n * Docs: /docs/configuration § \"Project config\".\n */\nexport interface ProjectConfig {\n /** Project display name (the display name is separate from the id, shown as the id when unset). */\n name?: string;\n /** Paired reference to the default Model; must point to an entry in `models`. */\n default_model?: ModelRef;\n /**\n * The vision model used by read_image to read on behalf of a session model (when a session\n * model with `vision=false` reads an image, it's handed to this model to describe and the tool\n * returns text); must point to an entry in `models` (a paired reference). Unconfigured by\n * default — models that don't support images won't be able to read images.\n */\n vision_model?: ModelRef;\n models: ModelEntry[];\n}\n\n/**\n * Returns the Project's default config: every entry from the preset builtin model catalog\n * (including context_window / pricing / vision tags and the preset base_url for gateway models,\n * with no keys included) — the user only needs to fill in an API key as needed (left empty falls\n * back to the vendor's environment variable).\n */\nexport function defaultProjectConfig(): ProjectConfig {\n return {\n default_model: { provider: \"deepseek\", model_id: \"deepseek-v4-pro\" },\n models: presetModelEntries(),\n };\n}\n\n/** The old format (concatenated storage id / string reference) is never migrated: reading it reports a clear error immediately (the product hasn't shipped yet). */\nconst OLD_FORMAT_HINT =\n \"产品未发布不做迁移:请删除该配置文件后用 `penguin config model add/default` 重建。\";\n\n/** Validates the default_model / vision_model fields: must be a { provider, model_id } paired reference. */\nfunction parseRefField(file: string, name: string, value: unknown): ModelRef | undefined {\n if (value === undefined) return undefined;\n const ref = value as { provider?: unknown; model_id?: unknown };\n if (\n typeof value !== \"object\" ||\n value === null ||\n typeof ref.provider !== \"string\" ||\n typeof ref.model_id !== \"string\"\n ) {\n throw new Error(\n `.project_config.toml 的 ${name} 是旧版本/非法格式(须为 { provider = \"...\", model_id = \"...\" } 成对引用):${file}。${OLD_FORMAT_HINT}`,\n );\n }\n return { provider: ref.provider, model_id: ref.model_id };\n}\n\n/** Validates a model entry: both provider and model_id must be strings (an old-format entry is missing provider). */\nfunction assertModelEntry(file: string, entry: unknown): ModelEntry {\n const m = entry as { provider?: unknown; model_id?: unknown };\n if (\n typeof entry !== \"object\" ||\n entry === null ||\n typeof m.provider !== \"string\" ||\n typeof m.model_id !== \"string\"\n ) {\n throw new Error(\n `.project_config.toml 的 models 条目是旧版本/非法格式(provider 与 model_id 须为两个独立字段):${file}。${OLD_FORMAT_HINT}`,\n );\n }\n return entry as ModelEntry;\n}\n\n/**\n * Loads the Project config; returns the default config (without writing to disk) if\n * `.project_config.toml` doesn't exist. Returns plaintext (masking is applied at the interface\n * layer); reports a clear error when the old format (a string reference / an entry missing\n * provider) is read.\n */\nexport async function loadProjectConfig(root: string, projectId: string): Promise<ProjectConfig> {\n const file = projectConfigPath(root, projectId);\n let raw: string;\n try {\n raw = await fs.readFile(file, \"utf8\");\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === \"ENOENT\") return defaultProjectConfig();\n throw err;\n }\n // Defensive: parseToml may return null/undefined for an empty file, and destructuring it would throw a TypeError.\n const parsed = (parseToml(raw) ?? {}) as Record<string, unknown>;\n const defaultModel = parseRefField(file, \"default_model\", parsed.default_model);\n const visionModel = parseRefField(file, \"vision_model\", parsed.vision_model);\n return {\n ...(parsed.name !== undefined ? { name: parsed.name as string } : {}),\n ...(defaultModel !== undefined ? { default_model: defaultModel } : {}),\n ...(visionModel !== undefined ? { vision_model: visionModel } : {}),\n models: ((parsed.models as unknown[] | undefined) ?? []).map((m) => assertModelEntry(file, m)),\n };\n}\n\n/** A TOML inline table for a paired reference (reuses smol-toml's string serialization, guaranteeing correct escaping). */\nfunction tomlInlineRef(ref: ModelRef): string {\n const kv = (obj: Record<string, string>): string => stringifyToml(obj).trim();\n return `{ ${kv({ provider: ref.provider })}, ${kv({ model_id: ref.model_id })} }`;\n}\n\n/** Whether a value has the paired-reference shape ({ provider, model_id }, two string fields). */\nfunction isModelRefShape(v: unknown): v is ModelRef {\n if (v === null || typeof v !== \"object\" || Array.isArray(v)) return false;\n const o = v as Record<string, unknown>;\n return typeof o.provider === \"string\" && typeof o.model_id === \"string\";\n}\n\n/**\n * Renders the full text of `.project_config.toml` — the **single source of the write format\n * site-wide** (shared by core's saveProjectConfig and the interface layer's full-table write, to\n * avoid the same file ending up in two different formats).\n *\n * Paired references (default_model / vision_model) are rendered as a TOML inline table\n * `{ provider = \"...\", model_id = \"...\" }`; `models` is always\n * placed last, since any table header after `[[models]]` would be read as its sub-table. Unknown\n * extension fields are kept as-is.\n */\nexport function renderProjectConfigToml(data: Record<string, unknown>): string {\n const head: string[] = [];\n for (const [key, value] of Object.entries(data)) {\n if (value === undefined || key === \"models\") continue;\n head.push(\n isModelRefShape(value)\n ? `${key} = ${tomlInlineRef(value)}`\n : stringifyToml({ [key]: value }).trim(),\n );\n }\n const models = Array.isArray(data.models) ? data.models : [];\n return [...head, stringifyToml({ models })].join(\"\\n\");\n}\n\n/**\n * Saves the Project config: writes the full table to the single config file\n * `.project_config.toml`. The file contains secrets like api_key, so it's written to disk with\n * mode 0600 (a hidden file blocks `ls`, not reads; mode only takes effect on creation, so chmod\n * converges an existing file too).\n */\nexport async function saveProjectConfig(\n root: string,\n projectId: string,\n cfg: ProjectConfig,\n): Promise<void> {\n const file = projectConfigPath(root, projectId);\n await fs.mkdir(path.dirname(file), { recursive: true });\n await fs.writeFile(file, renderProjectConfigToml({ ...cfg }), {\n encoding: \"utf8\",\n mode: 0o600,\n });\n await fs.chmod(file, 0o600);\n}\n\n/**\n * Adds or updates a Model:\n * - Upserts into `models`, deduplicated by the `(provider, model_id)` pair (provider may be\n * omitted — the builtin catalog is used to infer the upstream id's group, falling back to\n * custom if it can't be inferred);\n * - If `api_key`/`base_url` are provided, they're written inline into the entry;\n * - Set as the default Model (a paired reference) when `opts.setDefault` is true.\n * Reads the existing config (or the default), saves after the change, and returns the updated\n * config.\n */\nexport async function addModel(\n root: string,\n projectId: string,\n entry: {\n /** provider group; inferred from the builtin catalog when omitted (`inferProviderForUpstream`, falling back to custom if it can't be inferred). */\n provider?: string;\n /** Upstream model id (sent to AgentHub unchanged). */\n model_id: string;\n context_window?: number;\n client_type?: string;\n /** Whether image input is supported (vision/multimodal); keeps the existing value by default (treated as supported if never set). */\n vision?: boolean;\n /** Price input may cover only some buckets; merged and written as a complete `ModelPricing`. */\n pricing?: Partial<ModelPricing>;\n api_key?: string;\n base_url?: string;\n },\n opts?: { setDefault?: boolean },\n): Promise<ProjectConfig> {\n const cfg = await loadProjectConfig(root, projectId);\n const provider = entry.provider ?? inferProviderForUpstream(entry.model_id);\n\n // upsert: layers new fields on top of the existing entry; fields not explicitly provided\n // (e.g. context_window) keep their existing value, so a call like \"just add an api_key\"\n // doesn't wipe out the prior config.\n const idx = cfg.models.findIndex((m) => m.provider === provider && m.model_id === entry.model_id);\n const existing = idx >= 0 ? cfg.models[idx] : undefined;\n const modelEntry: ModelEntry = {\n provider,\n model_id: entry.model_id,\n };\n const contextWindow = entry.context_window ?? existing?.context_window;\n if (contextWindow !== undefined) {\n modelEntry.context_window = contextWindow;\n }\n const clientType = entry.client_type ?? existing?.client_type;\n if (clientType !== undefined) {\n modelEntry.client_type = clientType;\n }\n // The display name and api_key write timestamp are not set by this function; kept as-is on upsert.\n if (existing?.display_name !== undefined) {\n modelEntry.display_name = existing.display_name;\n }\n const vision = entry.vision ?? existing?.vision;\n if (vision !== undefined) {\n modelEntry.vision = vision;\n }\n // The three price buckets are merged field by field: an unspecified bucket keeps its existing\n // value (the same policy as context_window/credential); the unit is fixed to usd_per_mtok, and\n // the complete pricing is written as long as any bucket is present.\n const mergedPricing: Partial<ModelPricing> = {\n ...existing?.pricing,\n ...entry.pricing,\n };\n if (\n mergedPricing.cache_read !== undefined ||\n mergedPricing.cache_write !== undefined ||\n mergedPricing.output !== undefined\n ) {\n modelEntry.pricing = {\n unit: \"usd_per_mtok\",\n cache_read: mergedPricing.cache_read ?? 0,\n cache_write: mergedPricing.cache_write ?? 0,\n output: mergedPricing.output ?? 0,\n };\n }\n // Inline credential entry: fields not provided keep their existing value.\n const apiKey = entry.api_key ?? existing?.api_key;\n if (apiKey !== undefined) {\n modelEntry.api_key = apiKey;\n }\n const baseUrl = entry.base_url ?? existing?.base_url;\n if (baseUrl !== undefined) {\n modelEntry.base_url = baseUrl;\n }\n if (existing?.created_at !== undefined) {\n modelEntry.created_at = existing.created_at;\n }\n if (idx >= 0) {\n cfg.models[idx] = modelEntry;\n } else {\n cfg.models.push(modelEntry);\n }\n\n if (opts?.setDefault) {\n cfg.default_model = { provider, model_id: entry.model_id };\n }\n\n await saveProjectConfig(root, projectId, cfg);\n return cfg;\n}\n\n/**\n * Sets the default Model and saves. The target reference must exist in `models` (a reference\n * pointing outside the config would make createSession error immediately); throws otherwise.\n */\nexport async function setDefaultModel(\n root: string,\n projectId: string,\n ref: ModelRef,\n): Promise<ProjectConfig> {\n const cfg = await loadProjectConfig(root, projectId);\n if (!getModel(cfg, ref)) {\n throw new Error(\n `default_model 必须指向已配置的模型:${formatModelRef(ref)} 不在 models 中。用 \\`penguin config model list\\` 查看已配置的模型。`,\n );\n }\n cfg.default_model = { provider: ref.provider, model_id: ref.model_id };\n await saveProjectConfig(root, projectId, cfg);\n return cfg;\n}\n\n/**\n * Sets the vision model used to read images on behalf of read_image, and saves. The target\n * reference must exist in `models` and not be tagged `vision=false` (a model that doesn't support\n * images can't read on someone's behalf); throws otherwise.\n */\nexport async function setVisionModel(\n root: string,\n projectId: string,\n ref: ModelRef,\n): Promise<ProjectConfig> {\n const cfg = await loadProjectConfig(root, projectId);\n const entry = getModel(cfg, ref);\n if (!entry) {\n throw new Error(\n `vision_model 必须指向已配置的模型:${formatModelRef(ref)} 不在 models 中。用 \\`penguin config model list\\` 查看已配置的模型。`,\n );\n }\n if (entry.vision === false) {\n throw new Error(`vision_model 不能指向标注为不支持图片的模型:${formatModelRef(ref)}。`);\n }\n cfg.vision_model = { provider: ref.provider, model_id: ref.model_id };\n await saveProjectConfig(root, projectId, cfg);\n return cfg;\n}\n\n/** Looks up a Model entry exactly by its `(provider, model_id)` paired reference; returns `undefined` if it doesn't exist. */\nexport function getModel(cfg: ProjectConfig, ref: ModelRef): ModelEntry | undefined {\n return cfg.models.find((m) => m.provider === ref.provider && m.model_id === ref.model_id);\n}\n\n/**\n * Resolves a model reference (the **single entry point for \"provider omitted\"**, shared by core\n * and CLI/server — never set up a second one):\n * - `provider` given: validated for existence by exact paired reference;\n * - `provider` omitted: an exact-match lookup on `model_id` (no fuzzy matching of any kind) —\n * resolvable only when **exactly one** entry matches; 0 or multiple matches always report a\n * clear error (an ambiguity error lists the candidate paired references).\n */\nexport function resolveModelRef(cfg: ProjectConfig, modelId: string, provider?: string): ModelRef {\n if (provider !== undefined) {\n const ref: ModelRef = { provider, model_id: modelId };\n if (!getModel(cfg, ref)) {\n throw new Error(\n `Model 不在 Project 配置中:${formatModelRef(ref)}。请用 \\`penguin config model list\\` 查看已配置的模型,或用 \\`penguin config model add\\` 添加。`,\n );\n }\n return ref;\n }\n const candidates = cfg.models.filter((m) => m.model_id === modelId);\n if (candidates.length === 1) {\n return { provider: candidates[0]!.provider, model_id: modelId };\n }\n if (candidates.length === 0) {\n throw new Error(\n `Model 不在 Project 配置中:没有 model_id 为 ${modelId} 的条目。请用 \\`penguin config model list\\` 查看已配置的模型,或用 \\`penguin config model add\\` 添加。`,\n );\n }\n throw new Error(\n `模型引用有歧义:model_id ${modelId} 命中多个条目——${candidates\n .map((m) => formatModelRef({ provider: m.provider, model_id: m.model_id }))\n .join(\"、\")}。请补充 provider 以给出成对引用。`,\n );\n}\n","/**\n * Loading and initialization of Agent State (semantics modeled on Hugging Face model loading).\n *\n * - Initializes when the target Agent directory is empty (no `system_config.yaml`): creates\n * `agent_state/`, `tools/`, `memory/`, `skills/`, and the sibling `scratchpad/`, and writes\n * the default `system_config.yaml` and `AGENTS.md`.\n * - Otherwise loads the existing system config and editable Prompt for the given `agentId`.\n *\n * The full runtime Prompt is rendered from the system-level Prompt template in\n * `system_config.yaml`; placeholders in the template are replaced with `AGENTS.md` and the\n * concrete Session runtime environment fields. Built-in tools and MCP Server config\n * come from `system_config.yaml`.\n */\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { parse as parseYaml, stringify as stringifyYaml } from \"yaml\";\nimport {\n loadLibrarySkills,\n parseSkillFrontmatter,\n type SkillMetadata,\n} from \"@prismshadow/penguin-skills\";\nimport type { ToolConfig, ToolDefinitionConfig } from \"../interfaces.js\";\nimport {\n AGENT_ID_PLACEHOLDER,\n AGENTS_MD_PLACEHOLDER,\n VAULT_KEYS_PLACEHOLDER,\n SKILL_METADATA_PLACEHOLDER,\n CWD_PLACEHOLDER,\n DATE_PLACEHOLDER,\n defaultAgentsMd,\n defaultSystemConfig,\n OS_VERSION_PLACEHOLDER,\n PLATFORM_PLACEHOLDER,\n PROJECT_DIR_PLACEHOLDER,\n SESSION_ID_PLACEHOLDER,\n type SystemConfig,\n} from \"./default-config.js\";\nimport { builtinProjectAgentPresets, type AgentPreset } from \"./builtin-agents.js\";\nimport { provisionExampleBenchmark } from \"./example-benchmark.js\";\nimport {\n agentsMdPath,\n agentStateDir,\n DEFAULT_AGENT_ID,\n DEFAULT_PROJECT_ID,\n memoryDir,\n resolveRoot,\n scratchpadDir,\n skillsDir,\n systemConfigPath,\n toolsDir,\n} from \"./paths.js\";\n\n/** project_id / agent_id / skill_name only allow letters, digits, underscore `_`, and hyphen `-` (prevents path traversal). */\nconst ID_PATTERN = /^[A-Za-z0-9_-]+$/;\nexport type IdKind = \"project_id\" | \"agent_id\" | \"skill_name\";\n\nexport function isValidId(id: string): boolean {\n return ID_PATTERN.test(id);\n}\n\nexport function assertValidId(kind: IdKind, id: string): void {\n if (!ID_PATTERN.test(id)) {\n throw new Error(\n `Invalid ${kind} ${JSON.stringify(id)}: only letters, digits, \"_\" and \"-\" are allowed.`,\n );\n }\n}\n\n/** A loaded Agent State handle. */\nexport interface AgentState {\n root: string;\n projectId: string;\n agentId: string;\n stateDir: string;\n systemConfig: SystemConfig;\n agentsMd: string;\n}\n\nexport interface SessionEnvironmentValues {\n sessionId: string;\n cwd: string;\n /** The Agent id this Session belongs to (system Prompt placeholder {{AGENT_ID}}). */\n agentId: string;\n /** Absolute path to this Project's directory (system Prompt placeholder {{PROJECT_DIR}}; Agent State/scratchpad paths are derived from it). */\n projectDir: string;\n platform: string;\n osVersion: string;\n date: string;\n}\n\n/**\n * Loads or initializes Agent State.\n *\n * When root/project/agent are omitted, `resolveRoot()` and the default constants are used. If\n * `system_config.yaml` doesn't exist, the directory is treated as empty and initialized;\n * otherwise the existing content is loaded. `preset` only takes effect on the initialization\n * path (name/description/AGENTS.md overrides and extra Skills) and is ignored when loading an\n * existing Agent — existing config is never overwritten.\n */\nexport async function loadOrInitAgentState(opts?: {\n agentId?: string;\n projectId?: string;\n root?: string;\n preset?: AgentPreset;\n}): Promise<AgentState> {\n const root = opts?.root ?? resolveRoot();\n const projectId = opts?.projectId ?? DEFAULT_PROJECT_ID;\n const agentId = opts?.agentId ?? DEFAULT_AGENT_ID;\n\n // Validate before building paths, to prevent path traversal.\n assertValidId(\"project_id\", projectId);\n assertValidId(\"agent_id\", agentId);\n\n const stateDir = agentStateDir(root, projectId, agentId);\n const configPath = systemConfigPath(root, projectId, agentId);\n const mdPath = agentsMdPath(root, projectId, agentId);\n\n let systemConfig: SystemConfig;\n let agentsMd: string;\n\n if (await fileExists(configPath)) {\n // Load path: read the existing system_config.yaml and AGENTS.md.\n const rawConfig = await fs.readFile(configPath, \"utf8\");\n const parsed = parseYaml(rawConfig) as unknown;\n // Defensive check: if the file is empty/corrupted, parseYaml may return null/a non-object,\n // or system_prompt may be missing — otherwise \"undefined\" would get spliced into the system\n // Prompt. Throw a clear error when validation fails.\n if (\n parsed === null ||\n typeof parsed !== \"object\" ||\n typeof (parsed as SystemConfig).system_prompt !== \"string\"\n ) {\n throw new Error(`Agent State 配置非法:${configPath} 为空、损坏或缺少 system_prompt 字段。`);\n }\n systemConfig = parsed as SystemConfig;\n agentsMd = (await fileExists(mdPath)) ? await fs.readFile(mdPath, \"utf8\") : defaultAgentsMd();\n } else {\n // Init path: create the directory structure and write default config (preset only takes effect here).\n await Promise.all([\n fs.mkdir(stateDir, { recursive: true }),\n fs.mkdir(toolsDir(root, projectId, agentId), { recursive: true }),\n fs.mkdir(memoryDir(root, projectId, agentId), { recursive: true }),\n fs.mkdir(skillsDir(root, projectId, agentId), { recursive: true }),\n fs.mkdir(scratchpadDir(root, projectId, agentId), { recursive: true }),\n ]);\n const preset = opts?.preset;\n systemConfig = {\n ...defaultSystemConfig(),\n ...(preset?.name !== undefined ? { name: preset.name } : {}),\n ...(preset?.description !== undefined ? { description: preset.description } : {}),\n };\n agentsMd = preset?.agentsMd ?? defaultAgentsMd();\n // Only installs the Skills specified by preset (a plain newly created Agent gets none\n // pre-installed). A default_agent with no\n // preset (e.g. created on first CLI run) still gets every Skill in the library pre-installed\n // — the install policy follows Agent identity, not whether creation came from the server or\n // was done directly via SDK/CLI.\n // Skills have no dedicated tool: metadata is injected via {{SKILL_METADATA}}, and the model\n // reads SKILL.md with shell and follows it.\n const skills =\n opts?.preset === undefined && agentId === DEFAULT_AGENT_ID\n ? loadLibrarySkills()\n : (opts?.preset?.skills ?? []);\n await Promise.all([\n fs.writeFile(mdPath, agentsMd, \"utf8\"),\n ...skills.map((skill) => installSkill(root, projectId, agentId, skill)),\n // The example Benchmark is only provisioned alongside default_agent (so the evaluation\n // center has data out of the box): idempotently skipped if benchmarks/ already exists,\n // and not created for plain Agents.\n ...(agentId === DEFAULT_AGENT_ID\n ? [provisionExampleBenchmark(root, projectId, agentId)]\n : []),\n ]);\n // system_config.yaml is written last: its existence is the \"initialization complete\" marker\n // (the load/init decision point). If this fails partway (disk full / crash), the next run\n // still takes the init path and self-heals, so no half-initialized state with missing Skills is left behind.\n await fs.writeFile(configPath, stringifyYaml(systemConfig), \"utf8\");\n }\n\n return { root, projectId, agentId, stateDir, systemConfig, agentsMd };\n}\n\n/**\n * Initializes a Project's built-in Agent (the only built-in Agent: default_agent).\n *\n * Calls loadOrInitAgentState for each one: an Agent whose directory already exists (including a\n * default_agent created earlier by the CLI) is only loaded, never overwritten (preset only\n * takes effect on initialization). Returns the list of built-in Agent ids.\n */\nexport async function provisionProjectAgents(opts?: {\n root?: string;\n projectId?: string;\n}): Promise<string[]> {\n const agentIds: string[] = [];\n for (const { agentId, preset } of builtinProjectAgentPresets()) {\n await loadOrInitAgentState({\n ...(opts?.root !== undefined ? { root: opts.root } : {}),\n ...(opts?.projectId !== undefined ? { projectId: opts.projectId } : {}),\n agentId,\n preset,\n });\n agentIds.push(agentId);\n }\n return agentIds;\n}\n\n/**\n * The vault key-name list: the replacement value for `{{VAULT_KEYS}}`, one `- KEY` per line;\n * returns an empty string when there are no keys.\n * **Contains only key names, never values** — values are only injected into the exec_command\n * subprocess environment, never the model context. The statement of the vault's purpose is part\n * of the default template body (the # Vault section) and is kept even with no vault.\n */\nfunction vaultKeysList(keys: string[]): string {\n return keys.map((key) => `- ${key}`).join(\"\\n\");\n}\n\n/**\n * Installs a Skill into the target Agent: writes `skills/<name>/SKILL.md` verbatim (the full\n * SKILL.md content including frontmatter, ensuring a trailing newline); if the directory\n * already exists, it's overwritten (reinstalling = updating to the latest content). An optional\n * icon.svg is written alongside SKILL.md; if this install doesn't\n * include an icon, any old icon.svg is removed, preserving \"overwrite update\" semantics (the\n * directory content matches the Skill being installed).\n * Docs: /docs/skills § \"Installation and storage\".\n */\nexport async function installSkill(\n root: string,\n projectId: string,\n agentId: string,\n skill: { name: string; content: string; icon?: string },\n): Promise<void> {\n assertValidId(\"project_id\", projectId);\n assertValidId(\"agent_id\", agentId);\n assertValidId(\"skill_name\", skill.name);\n const dir = path.join(skillsDir(root, projectId, agentId), skill.name);\n await fs.mkdir(dir, { recursive: true });\n const content = skill.content.endsWith(\"\\n\") ? skill.content : `${skill.content}\\n`;\n const iconPath = path.join(dir, \"icon.svg\");\n await Promise.all([\n fs.writeFile(path.join(dir, \"SKILL.md\"), content, \"utf8\"),\n skill.icon !== undefined\n ? fs.writeFile(iconPath, skill.icon, \"utf8\")\n : fs.rm(iconPath, { force: true }),\n ]);\n}\n\n/** Uninstalls a Skill: deletes the entire `skills/<name>/` directory; idempotent, no error if it doesn't exist. */\nexport async function removeSkill(\n root: string,\n projectId: string,\n agentId: string,\n name: string,\n): Promise<void> {\n assertValidId(\"project_id\", projectId);\n assertValidId(\"agent_id\", agentId);\n assertValidId(\"skill_name\", name);\n await fs.rm(path.join(skillsDir(root, projectId, agentId), name), {\n recursive: true,\n force: true,\n });\n}\n\n/** An installed Skill entry: frontmatter metadata (including an optional short description) + the optional icon.svg content in the directory. */\nexport interface InstalledSkill extends SkillMetadata {\n /** The raw content of `skills/<name>/icon.svg` (a custom icon copied alongside SKILL.md at install time); the field is omitted when missing (the frontend falls back to a default book icon). */\n icon?: string;\n}\n\n/**\n * Lists the metadata of Skills installed on the target Agent: scans `skills/<name>/SKILL.md` and\n * parses its frontmatter (optional fields like short_description(_zh) pass through as parsed),\n * also reading the optional icon.svg content in the directory. Tolerant: a directory whose\n * frontmatter fails to parse or is missing `name` falls back to\n * `{ name: <directory name>, description: \"\", version: 1, updated: \"\" }`; a directory with no\n * SKILL.md doesn't count as a Skill; returns [] if skills/ doesn't exist. Results are sorted by\n * name (a stable order for both Prompt injection and API responses).\n * Docs: /docs/skills § \"Installation and storage\".\n */\nexport async function listInstalledSkills(\n root: string,\n projectId: string,\n agentId: string,\n): Promise<InstalledSkill[]> {\n assertValidId(\"project_id\", projectId);\n assertValidId(\"agent_id\", agentId);\n const dir = skillsDir(root, projectId, agentId);\n let entries;\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n const skills: InstalledSkill[] = [];\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n let raw: string;\n try {\n raw = await fs.readFile(path.join(dir, entry.name, \"SKILL.md\"), \"utf8\");\n } catch {\n continue;\n }\n let icon: string | undefined;\n try {\n icon = await fs.readFile(path.join(dir, entry.name, \"icon.svg\"), \"utf8\");\n } catch {\n // icon.svg is optional: missing means no custom icon.\n }\n // The directory name is the Skill's identity (install / uninstall / Prompt read guidance all\n // address by directory name): frontmatter only supplies display fields like description; when\n // its `name` doesn't match the directory name (a hand-written or network-sourced Skill), the\n // directory name always wins — otherwise the model would read a nonexistent path using the\n // injected name, and the API couldn't uninstall it either.\n const parsed = parseSkillFrontmatter(raw);\n skills.push({\n ...(parsed ?? { description: \"\", version: 1, updated: \"\" }),\n name: entry.name,\n ...(icon !== undefined ? { icon } : {}),\n });\n }\n return skills.sort((a, b) => a.name.localeCompare(b.name));\n}\n\n/**\n * Skill metadata section: the replacement value for `{{SKILL_METADATA}}`, one line per Skill in\n * the form `- \\`name\\` — description` (just the name when description is empty); an empty array\n * returns an empty string. The full body is read by the model on demand via shell.\n */\nexport function skillMetadataSection(skills: SkillMetadata[]): string {\n return skills\n .map((s) => (s.description ? `- \\`${s.name}\\` — ${s.description}` : `- \\`${s.name}\\``))\n .join(\"\\n\");\n}\n\n/**\n * Renders the complete runtime system Prompt: substitutes `AGENTS.md`, vault key names, Skill\n * metadata, and the concrete Session runtime environment placeholders into the system Prompt\n * template. The assembly layer only does placeholder substitution and adds no extra text —\n * wrapper text such as `<developer_instructions>` and the # Vault / # Skills statements are\n * written directly into the system Prompt template itself (the Prompt is fully\n * transparent and editable via `system_config.yaml`). Other files in Agent State / Workspace are\n * never auto-injected.\n *\n * `{{VAULT_KEYS}}` is replaced with the vault key-name list (an empty string if empty/not\n * provided): this lets the model know which APIs requiring a key it can call; values are never\n * injected. `{{SKILL_METADATA}}` is replaced with the installed Skills' metadata lines (an empty\n * string if empty/not provided). A custom template that removes a placeholder gets no\n * corresponding content injected.\n * Docs: /docs/configuration § \"System prompt placeholders\".\n */\nexport function assembleSystemPrompt(\n state: AgentState,\n sessionEnvironment?: SessionEnvironmentValues,\n vaultKeys?: string[],\n skillMetadata?: SkillMetadata[],\n): string {\n return state.systemConfig.system_prompt\n .split(AGENTS_MD_PLACEHOLDER)\n .join(state.agentsMd.trim())\n .split(VAULT_KEYS_PLACEHOLDER)\n .join(vaultKeysList(vaultKeys ?? []))\n .split(SKILL_METADATA_PLACEHOLDER)\n .join(skillMetadataSection(skillMetadata ?? []))\n .split(AGENT_ID_PLACEHOLDER)\n .join(sessionEnvironment?.agentId ?? state.agentId)\n .split(PROJECT_DIR_PLACEHOLDER)\n .join(sessionEnvironment?.projectDir ?? \"\")\n .split(SESSION_ID_PLACEHOLDER)\n .join(sessionEnvironment?.sessionId ?? \"\")\n .split(CWD_PLACEHOLDER)\n .join(sessionEnvironment?.cwd ?? \"\")\n .split(PLATFORM_PLACEHOLDER)\n .join(sessionEnvironment?.platform ?? \"\")\n .split(OS_VERSION_PLACEHOLDER)\n .join(sessionEnvironment?.osVersion ?? \"\")\n .split(DATE_PLACEHOLDER)\n .join(sessionEnvironment?.date ?? \"\")\n .trim();\n}\n\n/**\n * Builds the `ToolConfig` needed by Environment from Agent State.\n *\n * Both builtin tools and MCP Server config are taken from `system_config.yaml`; falls back to the\n * default config when builtin tools are missing.\n */\n/**\n * Filters builtin tool entries by the session model's type: entries with `forModel: \"vision\"` are\n * only used for models that support images (vision models), `forModel: \"text-only\"` is only for\n * text-only models (e.g. choosing between read_image / describe_image); unlabeled entries are\n * available to all models.\n * Docs: /docs/tools § \"Image tools\".\n */\nexport function selectBuiltinToolsForModel(\n tools: ToolDefinitionConfig[],\n modelVision: boolean,\n): ToolDefinitionConfig[] {\n const kind = modelVision ? \"vision\" : \"text-only\";\n return tools.filter((t) => t.forModel === undefined || t.forModel === kind);\n}\n\nexport function buildToolConfig(state: AgentState): ToolConfig {\n const systemTools = state.systemConfig.tools;\n const builtin = systemTools?.builtin ?? defaultSystemConfig().tools?.builtin ?? [];\n return {\n customTools: builtin,\n mcpServers: systemTools?.mcpServers ?? [],\n };\n}\n\nasync function fileExists(filePath: string): Promise<boolean> {\n try {\n await fs.access(filePath);\n return true;\n } catch {\n return false;\n }\n}\n","/**\n * Provisioning of the example Benchmark.\n *\n * When default_agent is initialized, it preprovisions `benchmarks/example-benchmark/`: two\n * sample cases (each with statement/ and rubric/ indexed by a README.md),\n * benchmark_config.toml (runs = 2), and a scoreboard.yaml with three sample evaluations —\n * so the evaluation center has data out of the box. Its description states plainly that this\n * is a built-in example and the whole directory can be deleted or replaced. Only\n * default_agent gets this; ordinary Agents do not.\n *\n * Scoring numbers are self-consistent: each case's score / cost / duration_ms is the\n * **average** computed from its runs array, and each evaluation's totals are the sum over\n * its cases (written this way so it already satisfies the scoreboard v2 convention, and\n * tests can verify it).\n */\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { stringify as stringifyToml } from \"smol-toml\";\nimport { stringify as stringifyYaml } from \"yaml\";\nimport { benchmarksDir } from \"./paths.js\";\n\n/** Directory name of the example Benchmark (the directory name is also its identifier). */\nexport const EXAMPLE_BENCHMARK_ID = \"example-benchmark\";\n\n/** Contents of benchmark_config.toml (no model reference here — the model is recorded on each evaluation instead). */\nconst EXAMPLE_BENCHMARK_CONFIG = {\n title: \"Example Benchmark\",\n description:\n \"A built-in example benchmark so the evaluation charts have data out of the box. \" +\n \"Replace it with your own.\",\n runs: 2,\n};\n\n/** Two sample cases: statement and scoring rubric (in English, 3-5 lines each). */\nconst EXAMPLE_CASES: Array<{ id: string; statement: string; rubric: string }> = [\n {\n id: \"CASE-001-file-summary\",\n statement: `# Task: Summarize a project file\n\nRead the provided \\`notes.txt\\` in your workspace and write \\`summary.md\\` containing:\n1. A one-paragraph overview of at most 3 sentences.\n2. A bullet list of the three most important facts.\nKeep the whole summary under 150 words.\n`,\n rubric: `# Scoring rubric (max 5 points)\n\n- 2 pts: \\`summary.md\\` exists and stays under 150 words.\n- 2 pts: The three bullet facts are accurate and taken from \\`notes.txt\\`.\n- 1 pt: The overview paragraph is coherent and at most 3 sentences.\nAward partial credit per item; the case score is the sum.\n`,\n },\n {\n id: \"CASE-002-data-cleanup\",\n statement: `# Task: Clean up a CSV dataset\n\nThe workspace contains \\`users.csv\\` with duplicate rows and inconsistent casing in the email column.\nProduce \\`users_clean.csv\\` where:\n1. Emails are lowercased and rows with an empty email are removed.\n2. Exact duplicate rows are dropped, keeping the first occurrence.\nDo not change the column order.\n`,\n rubric: `# Scoring rubric (max 5 points)\n\n- 2 pts: \\`users_clean.csv\\` exists and keeps the original column order.\n- 2 pts: Emails are lowercased, empty-email rows removed, duplicates dropped (first kept).\n- 1 pt: No unrelated rows or columns were modified.\nAward partial credit per item; the case score is the sum.\n`,\n },\n];\n\n/** Raw result of a single run (a runs element in scoreboard v2). */\ninterface ExampleRun {\n score: number;\n cost: number;\n duration_ms: number;\n session_id: string;\n}\n\n/**\n * Raw runs for the three sample evaluations (case-level and evaluation-level metrics are\n * computed from these, keeping the numbers self-consistent). Each carries the model actually\n * used for that round (paired, since the evaluation center's chart splits series by model);\n * the examples all use deepseek-v4-pro (a single model, single series).\n */\nconst EXAMPLE_EVALUATIONS: Array<{\n time: string;\n version: number;\n provider: string;\n model_id: string;\n summary_title: string;\n summary: string;\n cases: Array<{ case: string; runs: ExampleRun[] }>;\n}> = [\n {\n time: \"2026-07-14T09:30:00Z\",\n version: 1,\n provider: \"deepseek\",\n model_id: \"deepseek-v4-pro\",\n summary_title: \"Baseline before any optimization\",\n summary:\n \"Example data (not a real evaluation): baseline scores of the built-in sample \" +\n \"benchmark before any optimization. Hypothesis for the next round: the agent skips \" +\n \"a final self-check, losing points on completeness.\",\n cases: [\n {\n case: \"CASE-001-file-summary\",\n runs: [\n {\n score: 2.5,\n cost: 0.012,\n duration_ms: 42000,\n session_id: \"session-2026-07-14-09-05-11-1a2b3c01\",\n },\n {\n score: 3.5,\n cost: 0.014,\n duration_ms: 48000,\n session_id: \"session-2026-07-14-09-13-27-1a2b3c02\",\n },\n ],\n },\n {\n case: \"CASE-002-data-cleanup\",\n runs: [\n {\n score: 3.0,\n cost: 0.018,\n duration_ms: 66000,\n session_id: \"session-2026-07-14-09-21-45-1a2b3c03\",\n },\n {\n score: 3.0,\n cost: 0.022,\n duration_ms: 74000,\n session_id: \"session-2026-07-14-09-28-52-1a2b3c04\",\n },\n ],\n },\n ],\n },\n {\n time: \"2026-07-15T09:30:00Z\",\n version: 2,\n provider: \"deepseek\",\n model_id: \"deepseek-v4-pro\",\n summary_title: \"Added an explicit planning step\",\n summary:\n \"Example data (not a real evaluation): after adding an explicit planning step to the \" +\n \"system prompt (hypothesis: written plans reduce missed requirements), both cases \" +\n \"improved. Next: tighten output formatting.\",\n cases: [\n {\n case: \"CASE-001-file-summary\",\n runs: [\n {\n score: 3.5,\n cost: 0.011,\n duration_ms: 39000,\n session_id: \"session-2026-07-15-09-04-33-2b3c4d01\",\n },\n {\n score: 4.0,\n cost: 0.013,\n duration_ms: 45000,\n session_id: \"session-2026-07-15-09-12-08-2b3c4d02\",\n },\n ],\n },\n {\n case: \"CASE-002-data-cleanup\",\n runs: [\n {\n score: 3.5,\n cost: 0.016,\n duration_ms: 60000,\n session_id: \"session-2026-07-15-09-19-40-2b3c4d03\",\n },\n {\n score: 4.0,\n cost: 0.02,\n duration_ms: 68000,\n session_id: \"session-2026-07-15-09-26-59-2b3c4d04\",\n },\n ],\n },\n ],\n },\n {\n time: \"2026-07-16T09:30:00Z\",\n version: 3,\n provider: \"deepseek\",\n model_id: \"deepseek-v4-pro\",\n summary_title: \"Verify deliverables before finishing\",\n summary:\n \"Example data (not a real evaluation): after instructing the agent to verify its \" +\n \"deliverables against the statement before finishing (hypothesis: a final check \" +\n \"catches formatting slips), scores improved again. Replace this benchmark with your \" +\n \"own to track real progress.\",\n cases: [\n {\n case: \"CASE-001-file-summary\",\n runs: [\n {\n score: 4.0,\n cost: 0.01,\n duration_ms: 36000,\n session_id: \"session-2026-07-16-09-03-21-3c4d5e01\",\n },\n {\n score: 4.5,\n cost: 0.012,\n duration_ms: 40000,\n session_id: \"session-2026-07-16-09-10-46-3c4d5e02\",\n },\n ],\n },\n {\n case: \"CASE-002-data-cleanup\",\n runs: [\n {\n score: 4.5,\n cost: 0.015,\n duration_ms: 55000,\n session_id: \"session-2026-07-16-09-18-02-3c4d5e03\",\n },\n {\n score: 4.0,\n cost: 0.017,\n duration_ms: 61000,\n session_id: \"session-2026-07-16-09-25-30-3c4d5e04\",\n },\n ],\n },\n ],\n },\n];\n\n/** Round floats to 1e-6 (so binary error from averaging/summing isn't persisted to disk). */\nfunction round(v: number): number {\n return Math.round(v * 1e6) / 1e6;\n}\n\nfunction average(values: number[]): number {\n return round(values.reduce((a, b) => a + b, 0) / values.length);\n}\n\nfunction sum(values: number[]): number {\n return round(values.reduce((a, b) => a + b, 0));\n}\n\n/**\n * Builds the scoreboard object from raw runs data: each case's three metrics are the\n * average of its runs, and each evaluation's metrics are the sum of its cases' averages\n * (following the scoreboard v2 convention). Exported so tests can verify the numbers\n * are self-consistent.\n */\nexport function buildExampleScoreboard(): {\n evaluations: Array<{\n time: string;\n version: number;\n provider: string;\n model_id: string;\n summary_title: string;\n summary: string;\n score: number;\n cost: number;\n duration_ms: number;\n cases: Array<{\n case: string;\n score: number;\n cost: number;\n duration_ms: number;\n runs: ExampleRun[];\n }>;\n }>;\n} {\n return {\n evaluations: EXAMPLE_EVALUATIONS.map((e) => {\n const cases = e.cases.map((c) => ({\n case: c.case,\n score: average(c.runs.map((r) => r.score)),\n cost: average(c.runs.map((r) => r.cost)),\n duration_ms: average(c.runs.map((r) => r.duration_ms)),\n runs: c.runs,\n }));\n return {\n time: e.time,\n version: e.version,\n provider: e.provider,\n model_id: e.model_id,\n summary_title: e.summary_title,\n summary: e.summary,\n score: sum(cases.map((c) => c.score)),\n cost: sum(cases.map((c) => c.cost)),\n duration_ms: sum(cases.map((c) => c.duration_ms)),\n cases,\n };\n }),\n };\n}\n\n/**\n * Provisions the example Benchmark: if `benchmarks/` already exists (the user already has a\n * case library), does nothing; otherwise creates `benchmarks/example-benchmark/` (config, the\n * two sample cases, and the scoreboard). Callers are restricted to the default_agent\n * initialization path (see agent-state.ts).\n */\nexport async function provisionExampleBenchmark(\n root: string,\n projectId: string,\n agentId: string,\n): Promise<void> {\n const dir = benchmarksDir(root, projectId, agentId);\n try {\n await fs.access(dir);\n return;\n } catch {\n // benchmarks/ does not exist: proceed with provisioning.\n }\n const benchDir = path.join(dir, EXAMPLE_BENCHMARK_ID);\n await Promise.all(\n EXAMPLE_CASES.flatMap((c) => [\n fs.mkdir(path.join(benchDir, c.id, \"statement\"), { recursive: true }),\n fs.mkdir(path.join(benchDir, c.id, \"rubric\"), { recursive: true }),\n ]),\n );\n await Promise.all([\n fs.writeFile(\n path.join(benchDir, \"benchmark_config.toml\"),\n `${stringifyToml(EXAMPLE_BENCHMARK_CONFIG)}\\n`,\n \"utf8\",\n ),\n fs.writeFile(\n path.join(benchDir, \"scoreboard.yaml\"),\n stringifyYaml(buildExampleScoreboard()),\n \"utf8\",\n ),\n ...EXAMPLE_CASES.flatMap((c) => [\n fs.writeFile(path.join(benchDir, c.id, \"statement\", \"README.md\"), c.statement, \"utf8\"),\n fs.writeFile(path.join(benchDir, c.id, \"rubric\", \"README.md\"), c.rubric, \"utf8\"),\n ]),\n ]);\n}\n","/**\n * Agent-level environment-variable vault (`<project>/agents/<agent_id>/agent_state/.vault.toml`).\n *\n * Key-value pairs such as third-party API keys, configured per Agent: injected into that Agent\n * session's `exec_command` / `input_command` child-process environment, with key names disclosed\n * to the model via the system Prompt while values never enter the model context. Carries the same\n * trade-offs as a credential: stored in plaintext on disk, masked at the API layer. The file is\n * created/removed together with the Agent directory; its absence is treated as an empty table;\n * once emptied, the file is removed to avoid leaving a stray empty .vault.toml.\n * Docs: /docs/configuration § \"Vault\".\n */\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { parse as parseToml, stringify as stringifyToml } from \"smol-toml\";\nimport { agentVaultPath } from \"./paths.js\";\nimport { assertValidId } from \"./agent-state.js\";\n\n/** Vault key-name constraint: matches shell environment variable names (starts with a letter or underscore, followed by letters/digits/underscores only). */\nconst VAULT_KEY_PATTERN = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Vault value length cap: since values are injected into the child-process environment, Linux\n * caps a single env entry at roughly 128KB, and an oversized value would make every\n * exec_command spawn for that Agent fail (E2BIG) — so it's rejected on the write side (core and\n * the API layer share this same cap).\n */\nexport const VAULT_VALUE_MAX_LENGTH = 8192;\n\n/** Whether a vault key name is valid (shell environment variable name rules). */\nexport function isValidVaultKey(key: string): boolean {\n return VAULT_KEY_PATTERN.test(key);\n}\n\n/** Validates a vault key name, throwing if invalid (core and the API layer share this same rule). */\nexport function assertValidVaultKey(key: string): void {\n if (!isValidVaultKey(key)) {\n throw new Error(\n `Invalid vault key ${JSON.stringify(key)}: only letters, digits and \"_\" are allowed, and it must not start with a digit.`,\n );\n }\n}\n\n/** Validates a vault value's length (see `VAULT_VALUE_MAX_LENGTH`), throwing if it exceeds the cap. */\nexport function assertValidVaultValue(key: string, value: string): void {\n if (value.length > VAULT_VALUE_MAX_LENGTH) {\n throw new Error(\n `Vault value for ${key} is too long: ${value.length} > ${VAULT_VALUE_MAX_LENGTH} characters.`,\n );\n }\n}\n\n/**\n * Reads the Agent vault: returns an empty table if the file doesn't exist.\n * A hand-edited file is filtered by the same rule as the write side: only string values are\n * accepted (numbers/dates etc. are ignored), and key names must follow shell variable name rules\n * (invalid keys are always ignored — otherwise they'd get injected into the Prompt/child-process\n * environment, and an invalid key surfaced by a GET view would make a full-table PUT 400, leaving\n * the vault page unable to add or remove any further entries).\n */\nexport async function loadAgentVault(\n root: string,\n projectId: string,\n agentId: string,\n): Promise<Record<string, string>> {\n assertValidId(\"project_id\", projectId);\n assertValidId(\"agent_id\", agentId);\n let raw: string;\n try {\n raw = await fs.readFile(agentVaultPath(root, projectId, agentId), \"utf8\");\n } catch {\n return {};\n }\n const parsed: unknown = parseToml(raw) ?? {};\n const vault: Record<string, string> = {};\n if (parsed !== null && typeof parsed === \"object\" && !Array.isArray(parsed)) {\n for (const [k, v] of Object.entries(parsed)) {\n if (typeof v === \"string\" && isValidVaultKey(k)) vault[k] = v;\n }\n }\n return vault;\n}\n\n/**\n * Writes the full table to the Agent vault: validates all key names first; an empty table\n * deletes the file (idempotent if it doesn't exist).\n * The directory is created automatically if it doesn't exist (the vault can be configured even\n * before the Agent is initialized).\n */\nexport async function saveAgentVault(\n root: string,\n projectId: string,\n agentId: string,\n vault: Record<string, string>,\n): Promise<void> {\n assertValidId(\"project_id\", projectId);\n assertValidId(\"agent_id\", agentId);\n for (const key of Object.keys(vault)) assertValidVaultKey(key);\n const file = agentVaultPath(root, projectId, agentId);\n if (Object.keys(vault).length === 0) {\n await fs.rm(file, { force: true });\n return;\n }\n await fs.mkdir(path.dirname(file), { recursive: true });\n // The secret file is written to disk with mode 0600 (a hidden file blocks `ls`, not reads; mode\n // only takes effect on creation, so chmod is applied to converge an existing file too).\n await fs.writeFile(file, `${stringifyToml(vault)}\\n`, { encoding: \"utf8\", mode: 0o600 });\n await fs.chmod(file, 0o600);\n}\n\n/**\n * Writes or updates one vault entry (added if it doesn't exist, overwritten if it does).\n * The key name must follow shell environment variable name rules (see `isValidVaultKey`), and the\n * value length is constrained by `VAULT_VALUE_MAX_LENGTH`; throws if invalid. Returns the updated\n * vault.\n */\nexport async function setVaultEntry(\n root: string,\n projectId: string,\n agentId: string,\n key: string,\n value: string,\n): Promise<Record<string, string>> {\n assertValidVaultKey(key);\n assertValidVaultValue(key, value);\n const vault = await loadAgentVault(root, projectId, agentId);\n vault[key] = value;\n await saveAgentVault(root, projectId, agentId, vault);\n return vault;\n}\n\n/**\n * Removes one vault entry; idempotent if the key doesn't exist (no write happens). Once emptied,\n * the whole .vault.toml is removed. Returns the updated vault.\n */\nexport async function removeVaultEntry(\n root: string,\n projectId: string,\n agentId: string,\n key: string,\n): Promise<Record<string, string>> {\n const vault = await loadAgentVault(root, projectId, agentId);\n if (!(key in vault)) return vault;\n delete vault[key];\n await saveAgentVault(root, projectId, agentId, vault);\n return vault;\n}\n","/**\n * Agent State and Project config storage.\n *\n * Directory layout, default config, Project config read/write, Agent State load/init.\n */\nexport * from \"./paths.js\";\nexport * from \"./default-config.js\";\nexport * from \"./builtin-agents.js\";\nexport * from \"./model-catalog.js\";\nexport * from \"./project-config.js\";\nexport * from \"./agent-state.js\";\nexport * from \"./agent-vault.js\";\nexport * from \"./example-benchmark.js\";\n\n// Skill library types and frontmatter parser (from the skills package; server reuses the same implementation via core).\nexport { parseSkillFrontmatter, type SkillMetadata } from \"@prismshadow/penguin-skills\";\n","/**\n * GenerativeModel —— the SDK's LLM interface implementation.\n *\n * Responsibilities (protocol translation + streaming aggregation):\n * 1. Merge a group of OmniMessages that **share the same role** into a single AgentHub `UniMessage`;\n * 2. Issue the request via `AutoLLMClient.streamingResponseStateful` (stateful — AgentHub\n * maintains history internally), translating streamed `UniEvent`s back into OmniMessages:\n * - text/thinking/tool-call deltas → `partial_*` (before the first delta of each segment\n * `yield` a `start`; after the segment ends `yield` a `stop`);\n * - after a segment ends, append the full `model_msg` (thinking / text / tool_call);\n * - a `token_usage` event_msg is produced **only on normal completion**\n * (observability/Token).\n * 3. Interruption/error handling: `finishInterrupted` first closes any open\n * streaming segments and backfills the complete message, then the output ends — never\n * leaking a malformed structure. This interface **never retries internally** — retryable\n * errors (network/timeout/429/5xx, see `isRetryableError`) end with `timeout`; AgentHub\n * JSON parse errors end with `malformed`; both are handed to `context_engine` to reconnect\n * within the same run. User interruption ends with `aborted`; non-retryable errors\n * (auth/parameters) end with `failed`.\n *\n * `context_engine` only consumes OmniMessage; all Uni* protocol details are encapsulated here.\n * Docs: /docs/interfaces § \"The built-in implementation: GenerativeModel\".\n */\nimport { AutoLLMClient, ThinkingLevel } from \"@prismshadow/agenthub\";\nimport type {\n ContentItem,\n FinishReason,\n ToolSchema,\n UniConfig,\n UniEvent,\n UniMessage,\n UsageMetadata,\n} from \"@prismshadow/agenthub\";\n\nimport {\n addTokenCounts,\n assistantText,\n emptyTokenCounts,\n partialText,\n partialThinking,\n partialToolCall,\n thinkingMessage,\n tokenUsage,\n toolCall,\n} from \"../omnimessage/index.js\";\nimport type {\n CompleteModelPayload,\n OmniMessage,\n StopReason,\n TokenCounts,\n} from \"../omnimessage/index.js\";\nimport type {\n GenerativeModelConfig,\n GenerativeModelParameters,\n LLMInterface,\n LLMOutcome,\n ThinkingLevelName,\n ToolDefinition,\n} from \"../interfaces.js\";\nimport { ToolCallIdAllocator, stripToolCallIdSuffix } from \"./tool-call-ids.js\";\n\n// ---------------------------------------------------------------------------\n// Pure conversion function: OmniMessage[] → a single UniMessage (unit-testable, no network)\n// ---------------------------------------------------------------------------\n\n/**\n * Tool arguments JSON string → object. History only ever contains tool_calls from committed\n * turns (non-completed turns are discarded on replay); bad JSON already throws during AgentHub\n * parsing and reconnects via malformed, so it never enters history — hence we parse directly\n * with no fallback tolerance; an empty string is treated as no arguments.\n */\nfunction parseToolArguments(raw: string): Record<string, unknown> {\n if (!raw) return {};\n const parsed: unknown = JSON.parse(raw);\n return parsed !== null && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : {};\n}\n\n/**\n * Maps a complete OmniMessage payload to an AgentHub `ContentItem`.\n * Only complete model_msg payloads are supported; `partial_*` is an output-only protocol.\n */\nfunction payloadToContentItem(payload: CompleteModelPayload): ContentItem {\n // Provider-fidelity fields (signature / phase) are restored verbatim — some models require\n // them when history is replayed back (e.g. Claude thinking signatures, GPT-5 encrypted\n // reasoning and phase segmentation); losing them would break Session recovery.\n switch (payload.type) {\n case \"text\":\n return {\n type: \"text\",\n text: payload.text,\n ...(payload.phase != null ? { phase: payload.phase } : {}),\n ...(payload.signature !== undefined ? { signature: payload.signature } : {}),\n };\n case \"image_url\":\n return { type: \"image_url\", image_url: payload.image_url };\n case \"inline_data\":\n return {\n type: \"inline_data\",\n data: Buffer.from(payload.data, \"base64\"),\n mime_type: payload.mime_type,\n ...(payload.signature !== undefined ? { signature: payload.signature } : {}),\n };\n case \"inline_thinking\":\n return {\n type: \"inline_thinking\",\n data: Buffer.from(payload.data, \"base64\"),\n mime_type: payload.mime_type,\n ...(payload.signature !== undefined ? { signature: payload.signature } : {}),\n };\n case \"thinking\":\n return {\n type: \"thinking\",\n thinking: payload.thinking,\n ...(payload.signature !== undefined ? { signature: payload.signature } : {}),\n };\n case \"tool_call\":\n return {\n type: \"tool_call\",\n name: payload.name,\n // OmniMessage stores arguments as a JSON string; UniMessage uses an object.\n arguments: parseToolArguments(payload.arguments),\n // On the way back, strip the uniqueness suffix to restore the provider's original id (see tool-call-ids.ts).\n tool_call_id: stripToolCallIdSuffix(payload.tool_call_id),\n ...(payload.signature !== undefined ? { signature: payload.signature } : {}),\n };\n case \"tool_call_output\":\n return {\n type: \"tool_result\",\n text: payload.output,\n // Images carried by the tool output (data URL array) → AgentHub tool_result.images\n // (natively supported).\n ...(payload.images && payload.images.length > 0 ? { images: payload.images } : {}),\n // Gemini pairs by using tool_call_id as functionResponse.name, so it must be restored to the original id (the function name).\n tool_call_id: stripToolCallIdSuffix(payload.tool_call_id),\n };\n default: {\n // Exhaustiveness check: compile-time error when a new payload type is added.\n const _exhaustive: never = payload;\n throw new Error(\n `streamGenerate: unsupported message type: ${(_exhaustive as { type?: string }).type}`,\n );\n }\n }\n}\n\n/**\n * Groups a replayed, complete OmniMessage history into a sequence of UniMessages by\n * **adjacent same-role** runs (used for the setHistory injection during Session recovery).\n * One committed turn = a group of user-side input + a group of\n * assistant output, matching exactly the adjacent user / assistant UniMessages in AgentHub history.\n */\nexport function groupHistoryToUniMessages(history: OmniMessage[]): UniMessage[] {\n const groups: OmniMessage[][] = [];\n let currentRole: string | null = null;\n for (const msg of history) {\n const role = (msg.payload as { role?: string }).role;\n if (role !== \"user\" && role !== \"assistant\") {\n throw new Error(`setHistory: unsupported message without role: ${JSON.stringify(msg.type)}`);\n }\n if (role !== currentRole) {\n groups.push([]);\n currentRole = role;\n }\n groups[groups.length - 1]!.push(msg);\n }\n return groups.map(mergeOmniToUniMessage);\n}\n\n/**\n * Merges a group of OmniMessages into **a single** UniMessage.\n *\n * Constraint: all messages in the array must share the same\n * role; the role of the first payload is used as the UniMessage's role (a tool_call_output\n * group has role \"user\"). Throws if roles are mixed.\n */\nexport function mergeOmniToUniMessage(messages: OmniMessage[]): UniMessage {\n if (messages.length === 0) {\n throw new Error(\"streamGenerate requires at least one input message\");\n }\n\n const payloads = messages.map((m) => m.payload as CompleteModelPayload);\n // Each payload carries its own role (tool_call_output is fixed to \"user\"); take the first one's role.\n const role = payloads[0]!.role;\n\n const contentItems: ContentItem[] = [];\n for (const payload of payloads) {\n if (payload.role !== role) {\n throw new Error(\n \"streamGenerate does not accept mixed roles: all messages merged into one UniMessage must share the same role\",\n );\n }\n contentItems.push(payloadToContentItem(payload));\n }\n\n return { role, content_items: contentItems };\n}\n\n// ---------------------------------------------------------------------------\n// Token accounting (as defined by SKILL.md)\n// ---------------------------------------------------------------------------\n\n/**\n * Converts AgentHub `UsageMetadata` into PenguinHarness `TokenCounts`.\n *\n * Conversion rules (AgentHub UsageMetadata → OmniMessage TokenCounts, null treated as 0):\n * - `cache_read = cached_tokens` (input tokens served from cache hits);\n * - `cache_write = prompt_tokens` (input tokens on a cache miss);\n * - `output = thoughts_tokens + response_tokens`;\n * - `total = cache_read + cache_write + output`.\n * That is, `input = cache_read + cache_write = cached_tokens + prompt_tokens`,\n * and `total = input + output` (consistent with SKILL.md's input/output accounting).\n */\nexport function usageToTokenCounts(usage: UsageMetadata): TokenCounts {\n const cached = usage.cached_tokens ?? 0;\n const prompt = usage.prompt_tokens ?? 0;\n const thoughts = usage.thoughts_tokens ?? 0;\n const response = usage.response_tokens ?? 0;\n const cacheRead = cached;\n const cacheWrite = prompt;\n const output = thoughts + response;\n return {\n cache_read: cacheRead,\n cache_write: cacheWrite,\n output,\n total: cacheRead + cacheWrite + output,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Pure translator: UniEvent[] → OmniMessage[] (unit-testable, no network)\n// ---------------------------------------------------------------------------\n\ninterface ToolCallAccumulator {\n name: string;\n /** Accumulated arguments delta fragments (JSON string); used as a fallback when no complete tool_call arrives. */\n argsBuffer: string;\n /** If a complete tool_call appears in an event, its JSON.stringify'd arguments are recorded here. */\n completeArgs: string | null;\n /** Session-unique id emitted to OmniMessage (with a `#n` suffix on provider id collisions). */\n toolCallId: string;\n /** The original tool_call_id reported by the provider (the attribution key for inbound events). */\n providerKey: string;\n /** Provider-fidelity field: signature (kept verbatim, produced alongside the complete tool_call). */\n signature: string | undefined;\n /** Whether this tool_call's complete message has already been emitted eagerly in `pushEvent` (avoids duplicate emission in finish). */\n emitted: boolean;\n}\n\n/**\n * Streaming translator. Feed `UniEvent`s one at a time into `pushEvent`, which yields\n * incremental OmniMessages (`partial_*`); after the stream ends, call `finish` to produce\n * `stop`, the complete `model_msg`, and `token_usage`.\n *\n * Split into its own class to make unit testing easier (feed a constructed array of UniEvents,\n * assert on emission order / aggregation / token counts).\n * Docs: /docs/omni-message § \"The streaming discipline\".\n */\nexport class EventTranslator {\n /**\n * tool_call_id uniqueness registry. By default each translator creates its own (unit tests /\n * one-off translation); in production `GenerativeModel` injects a Session-level shared instance so\n * the uniqueness scope spans Requests and survives compaction rebuilds.\n */\n constructor(private readonly toolCallIds: ToolCallIdAllocator = new ToolCallIdAllocator()) {}\n\n // Whether each segment type has already yielded its `start`.\n private textStarted = false;\n private thinkingStarted = false;\n // Tool-call partial starts, tracked by the (uniqueness-resolved) tool_call_id.\n private toolStarted = new Set<string>();\n // Some providers' tool argument deltas don't carry a tool_call_id; attribute them to the most recently opened tool call (by provider id key).\n private activeToolCallId: string | null = null;\n\n // Buffers needed for the complete message.\n private textBuffer = \"\";\n private thinkingBuffer = \"\";\n // Provider-fidelity fields: the thinking block's signature (a signature\n // marks the end of a block) and the current text segment's phase (sticky across segments;\n // a differing phase marker starts a new segment) and signature.\n private thinkingSignature: string | undefined;\n private textPhase: string | null = null;\n private textSignature: string | undefined;\n /** Provider id keys saved in order of appearance, so complete tool_calls are emitted in a stable order. */\n private toolOrder: string[] = [];\n /** provider's original tool_call_id → the accumulator for the **latest** call under that id. */\n private tools = new Map<string, ToolCallAccumulator>();\n\n private finishReason: FinishReason | null = null;\n /** Token usage for this request (a snapshot from the most recent usage report). */\n private requestTokens: TokenCounts = emptyTokenCounts();\n\n /** Consumes one UniEvent, yielding 0..n streaming OmniMessages. */\n *pushEvent(event: UniEvent): Generator<OmniMessage> {\n if (event.finish_reason != null) {\n this.finishReason = event.finish_reason;\n }\n if (event.usage_metadata) {\n // The same request may report usage multiple times, always as a **cumulative snapshot**\n // (Gemini reports one per chunk, as do some OpenAI-compatible endpoints; Claude/GPT-5,\n // aggregated by AgentHub, report only once at the end). Overwrite with the latest snapshot\n // — never accumulate: summing snapshots chunk by chunk would inflate usage by roughly the\n // number of chunks.\n this.requestTokens = this.usageOnce(event.usage_metadata);\n }\n\n for (const item of event.content_items) {\n switch (item.type) {\n case \"text\": {\n // Provider fidelity: a phase marker can arrive as an increment with **empty text**\n // (e.g. GPT-5's segment markers). A phase differing from the current segment's phase\n // starts a new segment — providers split by phase when replaying history, so mixing\n // segments would break fidelity. Phase is sticky across segments (a subsequent segment\n // with the same phase isn't re-marked).\n if (item.phase != null && item.phase !== this.textPhase) {\n yield* this.flushThinking(\"completed\");\n yield* this.flushText(\"completed\");\n this.textPhase = item.phase;\n }\n if (item.signature) this.textSignature = item.signature;\n if (!item.text) break;\n // Type boundary: before a text segment starts, flush any unclosed thinking\n // segment, so the complete-message order matches generation order (thinking → text).\n // The boundary flush uses completed: that thinking segment's stop reason is \"switched\n // to text\", not \"Request ended\".\n yield* this.flushThinking(\"completed\");\n if (!this.textStarted) {\n this.textStarted = true;\n yield partialText(\"start\");\n }\n this.textBuffer += item.text;\n yield partialText(\"delta\", item.text);\n break;\n }\n case \"thinking\": {\n // Provider fidelity: a thinking block ends with a signature (Claude's signature_delta\n // is empty text + signature; redacted blocks carry sentinel text + signature; GPT-5\n // encrypted reasoning is empty text + signature). If thinking content/a new signature\n // arrives after a signature is already set, that's a new block — close the current\n // segment first, so each block's signature stays independently faithful and blocks\n // don't bleed into each other when history is replayed.\n if (this.thinkingSignature !== undefined && (item.thinking || item.signature)) {\n yield* this.flushThinking(\"completed\");\n }\n if (item.signature) this.thinkingSignature = item.signature;\n if (!item.thinking) break;\n // Type boundary: before a thinking segment starts, flush any unclosed text\n // segment, so the complete-message order matches generation order (text → thinking).\n // The boundary flush uses completed: that text segment's stop reason is \"switched to\n // thinking\", not \"Request ended\".\n yield* this.flushText(\"completed\");\n if (!this.thinkingStarted) {\n this.thinkingStarted = true;\n yield partialThinking(\"start\");\n }\n this.thinkingBuffer += item.thinking;\n yield partialThinking(\"delta\", item.thinking);\n break;\n }\n case \"partial_tool_call\": {\n // Some providers' argument deltas don't carry a tool_call_id; attribute them to the most\n // recently opened tool call. If there's no open tool call yet, skip — don't fabricate a\n // tool_call with an empty id.\n const providerKey = item.tool_call_id || this.activeToolCallId;\n if (!providerKey) break;\n if (item.tool_call_id) this.activeToolCallId = item.tool_call_id;\n const acc = this.ensureTool(providerKey, item.name);\n if (item.name) acc.name = item.name;\n if (item.signature) acc.signature = item.signature;\n // Externally always use the uniqueness-resolved id (with a `#n` suffix on provider id collisions), matching the complete tool_call.\n if (!this.toolStarted.has(acc.toolCallId)) {\n // Type boundary: before a new tool_call starts, flush any unclosed thinking/text\n // segment. Only triggered on the tool's first delta (!toolStarted.has); continuation\n // deltas (including id-less increments attributed via activeToolCallId) don't re-trigger the flush.\n yield* this.flushThinking(\"completed\");\n yield* this.flushText(\"completed\");\n this.toolStarted.add(acc.toolCallId);\n yield partialToolCall({\n eventType: \"start\",\n name: acc.name,\n toolCallId: acc.toolCallId,\n });\n }\n // Only emit a delta when there's an arguments increment (an empty increment carries\n // no information, consistent with how empty text/thinking increments are handled).\n // The delta doesn't repeat the name — leave it blank; tool identity is established by\n // the start segment and tool_call_id.\n if (item.arguments) {\n acc.argsBuffer += item.arguments;\n yield partialToolCall({\n eventType: \"delta\",\n name: \"\",\n arguments: item.arguments,\n toolCallId: acc.toolCallId,\n });\n }\n break;\n }\n case \"tool_call\": {\n // Complete tool-call content item: record the authoritative name/arguments and\n // **eagerly emit** the tool's partial(stop) and complete tool_call. This lets the\n // engine start approval/execution as soon as the first tool arrives, without waiting\n // for the whole turn to finish — key for async/incremental tool calls (see comment\n // #24). An empty id is invalid; skip it.\n if (!item.tool_call_id) break;\n // The model may think/output text before calling a tool: flush any buffered\n // thinking/text complete messages before emitting the complete tool_call, so the\n // complete-message order is thinking → text → tool_call. finish_reason isn't known\n // yet here; the boundary flush uses completed, with the stop reason attributed to\n // the tool_call itself.\n yield* this.flushThinking(\"completed\");\n yield* this.flushText(\"completed\");\n // The same provider id already emitted a complete call and now another complete tool_call\n // arrives: not a duplicate delivery but **another** call from a name-as-id provider (e.g.\n // Gemini using the function name as id) — start a fresh accumulator, allocate a new unique id,\n // and emit as usual; never drop it (otherwise parallel same-name calls in one turn would lack\n // tool execution and paired output).\n let acc = this.tools.get(item.tool_call_id);\n if (!acc || acc.emitted) acc = this.createTool(item.tool_call_id, item.name);\n acc.name = item.name;\n acc.completeArgs = JSON.stringify(item.arguments ?? {});\n if (item.signature) acc.signature = item.signature;\n yield* this.emitCompleteTool(acc);\n break;\n }\n // Other content items (image_url / inline_data / inline_thinking /\n // tool_result / embedding) are not treated as model streaming output.\n default:\n break;\n }\n }\n }\n\n /**\n * Stream-end finalization: first `yield` the `stop` and complete message for the text/thinking\n * segments, then backfill partial(stop) + complete tool_call for tool_calls that **haven't\n * been emitted eagerly yet** (i.e. the fallback case — no complete tool_call content item was\n * received, only deltas). Tools already emitted eagerly in `pushEvent` are not repeated here.\n */\n *finish(): Generator<OmniMessage> {\n const stopReason = this.omniStopReason();\n\n // 1. Close out the **last** unflushed thinking / text segment (earlier segments were already\n // flushed at their respective type boundaries or before the first tool_call).\n // Since boundaries already flush, at most one buffer is non-empty here, so call\n // order doesn't matter — the other call is a no-op; the final finish_reason is used as\n // the stop_reason here.\n yield* this.flushThinking(stopReason);\n yield* this.flushText(stopReason);\n\n // 2. Fallback path: for tools that never received a complete tool_call content item,\n // backfill using the accumulated deltas.\n for (const id of this.toolOrder) {\n if (!id) continue; // Defensive: an invalid tool call with an empty id (its result can't be routed).\n const acc = this.tools.get(id)!;\n if (acc.emitted) continue; // Already emitted eagerly in pushEvent; don't repeat.\n yield* this.emitCompleteTool(acc);\n }\n }\n\n /**\n * Interruption finalization: even when interrupted or on error, close the structure\n * as `start → delta → stop → complete message`. Closes any unclosed thinking/text segments and\n * backfills their complete messages, then backfills partial(stop) + complete tool_call for\n * tool_calls that only have deltas and were never emitted eagerly. All backfilled messages are\n * uniformly tagged with the interruption `stopReason` (`aborted` / `timeout` / `failed`), to\n * distinguish them from normal completion (`completed`).\n *\n * Differs from `finish`: doesn't read `finish_reason`, and doesn't produce `token_usage` (an\n * interrupted Request has no usage to report); backfilled incomplete tool_calls carry the\n * interruption stop_reason, so `context_engine` won't dispatch them for execution.\n */\n *finishInterrupted(stopReason: StopReason): Generator<OmniMessage> {\n yield* this.flushThinking(stopReason);\n yield* this.flushText(stopReason);\n for (const id of this.toolOrder) {\n if (!id) continue;\n const acc = this.tools.get(id)!;\n if (acc.emitted) continue; // A tool_call already emitted eagerly keeps its `tool_call` semantics and is left unchanged.\n yield* this.emitCompleteTool(acc, stopReason);\n }\n }\n\n /**\n * Eagerly emits the finalization of a tool_call: partial(stop) (without name) + complete\n * tool_call. Marks `emitted` to prevent duplicate emission in finish. `stopReason` defaults to\n * `completed` (a normal request); when called during interruption finalization\n * (`finishInterrupted`), the interruption reason is passed in, letting `context_engine`\n * distinguish \"a real tool request\" from \"an incomplete tool_call backfilled to close the\n * structure on interruption\" by stop_reason, and dispatch only the former.\n */\n private *emitCompleteTool(\n acc: ToolCallAccumulator,\n stopReason: StopReason = \"completed\",\n ): Generator<OmniMessage> {\n acc.emitted = true;\n if (this.toolStarted.has(acc.toolCallId)) {\n // stop doesn't carry name (tool identity is established by start and tool_call_id).\n yield partialToolCall({\n eventType: \"stop\",\n name: \"\",\n toolCallId: acc.toolCallId,\n stopReason,\n });\n }\n yield toolCall({\n name: acc.name,\n arguments: acc.completeArgs ?? acc.argsBuffer,\n toolCallId: acc.toolCallId,\n stopReason,\n ...(acc.signature !== undefined ? { signature: acc.signature } : {}),\n });\n // activeToolCallId holds the provider id key (used to attribute id-less deltas); reset it by providerKey.\n if (this.activeToolCallId === acc.providerKey) {\n this.activeToolCallId = null;\n }\n }\n\n /**\n * Closes out the currently buffered thinking segment and appends the complete thinking\n * message, then clears the buffer and resets the start flag. May be called before eagerly\n * emitting the first complete tool_call (`pushEvent`) or at stream end (`finish`), which\n * guarantees the complete-message order is thinking → text → tool_call.\n *\n * \"Flush then reset\" rather than a one-shot guard: once the buffer is cleared, a repeated call\n * is a no-op (each segment is emitted exactly once); if the model outputs new thinking after a\n * tool_call (interleaved/multi-segment models), that new segment accumulates again and gets\n * correctly flushed at the next tool_call or `finish`, without being lost.\n *\n * The complete thinking message passes through the given stop_reason just like partial(stop)\n * (aligned with flushText — streamed concatenation == complete message): at type boundaries the\n * caller passes completed (the stop reason belongs to the following tool_call/text);\n * finish/finishInterrupted follow the actual end reason.\n */\n private *flushThinking(stopReason: StopReason): Generator<OmniMessage> {\n if (this.thinkingStarted) {\n yield partialThinking(\"stop\", \"\", stopReason);\n this.thinkingStarted = false;\n }\n // A thinking block with empty text but a signature (GPT-5 encrypted reasoning) still\n // produces a complete message — the signature is required when replaying history.\n if (this.thinkingBuffer || this.thinkingSignature !== undefined) {\n yield thinkingMessage(this.thinkingBuffer, stopReason, {\n ...(this.thinkingSignature !== undefined ? { signature: this.thinkingSignature } : {}),\n });\n this.thinkingBuffer = \"\";\n this.thinkingSignature = undefined;\n }\n }\n\n /**\n * Closes out the currently buffered text segment and appends the complete text message, then\n * clears the buffer and resets the start flag. Uses the same \"flush then reset\" approach as\n * `flushThinking` to support new text segments after a tool_call. When emitted before the\n * first complete tool_call, finish_reason is unknown and the caller passes completed; when\n * emitted in `finish`, the final `omniStopReason()` is passed (consistent with prior behavior).\n */\n private *flushText(stopReason: StopReason): Generator<OmniMessage> {\n if (this.textStarted) {\n yield partialText(\"stop\", \"\", stopReason);\n this.textStarted = false;\n }\n // A text segment with empty text but a signature (e.g. Gemini carrying a thoughtSignature on\n // a text part) still produces a complete message — aligned with flushThinking, so the\n // signature isn't lost or leaked into a later segment just because the buffer is empty.\n if (this.textBuffer || this.textSignature !== undefined) {\n yield assistantText(this.textBuffer, stopReason, {\n ...(this.textPhase != null ? { phase: this.textPhase } : {}),\n ...(this.textSignature !== undefined ? { signature: this.textSignature } : {}),\n });\n this.textBuffer = \"\";\n this.textSignature = undefined;\n // textPhase is sticky across segments: a later segment with the same phase isn't\n // re-marked; it's updated when a different phase marker appears.\n }\n }\n\n /** Whether finish_reason (the terminal event) has been received: signals a fully delivered response (see the defensive branch in streamGenerate). */\n sawFinishReason(): boolean {\n return this.finishReason !== null;\n }\n\n /** Token usage for this request (read after finish). */\n getRequestTokens(): TokenCounts {\n return this.requestTokens;\n }\n\n private ensureTool(providerKey: string, name: string): ToolCallAccumulator {\n return this.tools.get(providerKey) ?? this.createTool(providerKey, name);\n }\n\n /**\n * Create an accumulator for a new call: the emitted id is made unique via the Session-level registry\n * (kept as-is when the provider id is free, with a `#n` suffix on collision). Creating again under the\n * same provider id (another call with a duplicate id) replaces the old Map entry — the old call has\n * finished emitting, so later inbound events are attributed to the latest call.\n */\n private createTool(providerKey: string, name: string): ToolCallAccumulator {\n const acc: ToolCallAccumulator = {\n name: name ?? \"\",\n argsBuffer: \"\",\n completeArgs: null,\n toolCallId: this.toolCallIds.allocate(providerKey),\n providerKey,\n signature: undefined,\n emitted: false,\n };\n this.tools.set(providerKey, acc);\n this.toolOrder.push(providerKey);\n return acc;\n }\n\n private usageOnce(usage: UsageMetadata): TokenCounts {\n return usageToTokenCounts(usage);\n }\n\n /**\n * Converts an AgentHub finish_reason into an OmniMessage stop_reason (the five-value protocol).\n * \"stop\", \"tool_call\", and null are treated as completed; length or unknown reasons map to failed.\n */\n private omniStopReason(): StopReason {\n if (\n this.finishReason == null ||\n this.finishReason === \"stop\" ||\n this.finishReason === \"tool_call\"\n ) {\n return \"completed\";\n }\n return \"failed\";\n }\n}\n\n/**\n * One-shot translation: folds a batch of UniEvents into an OmniMessage sequence (including\n * complete messages and token_usage). A pure function for easy unit testing; the live streaming\n * path is wired up by `GenerativeModel.streamGenerate`.\n *\n * @param events The event sequence\n * @param sessionTokensBefore The session's cumulative tokens before this translation (used to produce token_usage.session)\n * @returns `{ messages, requestTokens, sessionTokens }`\n */\nexport function translateEvents(\n events: UniEvent[],\n sessionTokensBefore: TokenCounts = emptyTokenCounts(),\n): {\n messages: OmniMessage[];\n requestTokens: TokenCounts;\n sessionTokens: TokenCounts;\n} {\n const translator = new EventTranslator();\n const out: OmniMessage[] = [];\n for (const event of events) {\n for (const msg of translator.pushEvent(event)) out.push(msg);\n }\n for (const msg of translator.finish()) out.push(msg);\n\n const requestTokens = translator.getRequestTokens();\n const sessionTokens = addTokenCounts(sessionTokensBefore, requestTokens);\n out.push(tokenUsage(sessionTokens, requestTokens));\n\n return { messages: out, requestTokens, sessionTokens };\n}\n\n// ---------------------------------------------------------------------------\n// Retry policy\n// ---------------------------------------------------------------------------\n\n/**\n * Determines whether an error is an AgentHub / Provider response JSON parse error.\n *\n * AgentHub uses `JSON.parse` internally to parse response bodies, and a parse failure throws a\n * `SyntaxError`; hence we judge directly by exception type (the `name` check also covers\n * cross-realm or deserialization-reconstructed errors, and we probe down the `cause` chain for\n * wrapped errors). This is not an auth/parameter failure but an incomplete LLM Request, and\n * should end with `malformed` and be handed to the engine to retry.\n */\nexport function isMalformedJsonParseError(error: unknown): boolean {\n if (error == null) return false;\n if (error instanceof SyntaxError) return true;\n const err = error as { name?: string; cause?: unknown };\n if (err.name === \"SyntaxError\") return true;\n if (err.cause && err.cause !== error) {\n return isMalformedJsonParseError(err.cause);\n }\n return false;\n}\n\n/**\n * Determines whether an error is AgentHub's \"incomplete stream\" validation error: when a\n * server/proxy terminates the stream early **cleanly** at an event boundary (no network error\n * thrown), AgentHub's `_validateLastEvent` reports the missing or incomplete final event as a\n * plain `Error` (\"Streaming response yielded no events\" / \"Last event must carry\n * usage_metadata|finish_reason\"). This is not an auth/parameter failure but an incomplete LLM\n * Request, and should end with `malformed` and be handed to the engine to reconnect and retry.\n * AgentHub doesn't provide an error type for this, so we match by message prefix\n * (@prismshadow/agenthub 0.3.x), probing down the `cause` chain.\n */\nexport function isIncompleteStreamError(error: unknown): boolean {\n if (error == null) return false;\n const err = error as { message?: string; cause?: unknown };\n const msg = err.message ?? \"\";\n if (\n msg.startsWith(\"Streaming response yielded no events\") ||\n msg.startsWith(\"Last event must carry\")\n ) {\n return true;\n }\n if (err.cause && err.cause !== error) {\n return isIncompleteStreamError(err.cause);\n }\n return false;\n}\n\n/**\n * Determines whether an error is retryable.\n *\n * Retryable: network errors, timeouts, connection reset, HTTP 429 / 5xx.\n * Not retryable: HTTP 4xx auth/parameter errors (401/403/400/404, etc.).\n * JSON parse errors are classified separately as `malformed` by `isMalformedJsonParseError`.\n *\n * Since AgentHub doesn't guarantee the shape of error objects, this uses a lenient check: first\n * the status code, then error codes / message keywords. When undeterminable, treat it as\n * **non-retryable** to avoid pointless retries.\n */\nexport function isRetryableError(error: unknown): boolean {\n if (error == null) return false;\n\n const err = error as {\n status?: number;\n statusCode?: number;\n code?: string;\n name?: string;\n message?: string;\n };\n\n // 1. HTTP status code takes priority.\n const status = err.status ?? err.statusCode;\n if (typeof status === \"number\") {\n if (status === 429 || status === 408) return true; // Rate limited / request timeout (transient)\n if (status >= 500 && status <= 599) return true; // Server error\n if (status >= 400 && status <= 499) return false; // Other 4xx auth/parameter errors, not retryable\n }\n\n // 2. Network-layer error codes.\n const retryableCodes = new Set([\n \"ECONNRESET\",\n \"ECONNREFUSED\",\n \"ETIMEDOUT\",\n \"EPIPE\",\n \"EAI_AGAIN\",\n \"ENOTFOUND\",\n \"ECONNABORTED\",\n ]);\n if (err.code && retryableCodes.has(err.code)) return true;\n\n // 3. Error name / message keywords (timeout, network, rate limit).\n if (err.name === \"AbortError\") return false; // User interruption, not retryable\n const text = `${err.name ?? \"\"} ${err.message ?? \"\"}`.toLowerCase();\n if (\n /timeout|timed out|network|socket hang up|econnreset|connection reset|too many requests|rate limit|temporarily unavailable|503|502|504/.test(\n text,\n )\n ) {\n return true;\n }\n\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// GenerativeModel\n// ---------------------------------------------------------------------------\n\n/**\n * A stateful LLM object attached to a Session. AgentHub's `streamingResponseStateful` maintains\n * conversation history internally; this class is only responsible for protocol translation,\n * streaming aggregation, and token accumulation. **It never retries internally** — retries are\n * handled by `context_engine`.\n */\nexport class GenerativeModel implements LLMInterface {\n private readonly client: AutoLLMClient;\n private readonly uniConfig: UniConfig;\n /** Streaming idle timeout (milliseconds); <= 0 disables it. A timeout is treated as needing reconnection. */\n private readonly requestTimeoutMs: number;\n /**\n * tool_call_id uniqueness registry (see tool-call-ids.ts): when a name-as-id provider (e.g. Gemini)\n * calls the same tool repeatedly, it assigns a `#n` suffix to later calls so engine pairing and the\n * frontend tool cards don't collide on id. Injected via config so it can be shared across the new\n * instance rebuilt on compaction; defaults to a fresh one.\n */\n private readonly toolCallIds: ToolCallIdAllocator;\n\n /** Cumulative session tokens. */\n sessionTokens: TokenCounts = emptyTokenCounts();\n\n constructor(config: GenerativeModelConfig) {\n // Omit apiKey / baseUrl when undefined, letting AgentHub read them from environment\n // variables. clientType determines which protocol to speak (`openai` means OpenAI Chat\n // Completions compatible); when omitted, AgentHub infers it from model_id, so it only needs\n // to be specified explicitly for custom-named models.\n this.client = new AutoLLMClient({\n model: config.modelId,\n ...(config.apiKey !== undefined ? { apiKey: config.apiKey } : {}),\n ...(config.baseUrl !== undefined ? { baseUrl: config.baseUrl } : {}),\n ...(config.clientType !== undefined ? { clientType: config.clientType } : {}),\n });\n\n this.uniConfig = buildUniConfig(config);\n this.requestTimeoutMs = config.requestTimeoutMs ?? 120000;\n this.toolCallIds = config.toolCallIds ?? new ToolCallIdAllocator();\n }\n\n /**\n * Streaming generation (a single attempt, no internal retry). Merges\n * `params.newMessages` into one UniMessage to issue a stateful request, translating streamed\n * UniEvents into OmniMessages.\n *\n * **Never throws to `context_engine`**: whether it ends normally or is interrupted/errors out,\n * every `partial_*` segment is closed as `start → delta → stop → complete message`,\n * and the terminal state is then returned as `LLMOutcome`:\n * - **Normal completion**: `finish()` closes out and produces `token_usage` (usage is only\n * produced in this case) → `completed`;\n * - **Idle timeout / network drop** (retryable errors like network/429/5xx):\n * `finishInterrupted(\"timeout\")` closes out, produces no usage → `timeout`, reconnected by\n * `context_engine` within the same run;\n * - **AgentHub JSON parse error**: `finishInterrupted(\"malformed\")` closes out, produces no\n * usage → `malformed`, likewise reconnected by `context_engine` within the same run;\n * - **User interruption**: `finishInterrupted(\"aborted\")` closes out, produces no usage →\n * `aborted`;\n * - **Other non-retryable errors** (auth/parameters etc.): `finishInterrupted(\"failed\")`\n * closes out, produces no usage → `failed` (carrying `message`), handed to\n * `context_engine` to stop and return control to the user.\n *\n * Timeout detection: the idle timer resets on every event received; once idle exceeds\n * `requestTimeoutMs`, the underlying stream is aborted and handled as needing reconnection\n * (merged with user interruption into a single internal AbortController).\n */\n async *streamGenerate(\n params: GenerativeModelParameters,\n ): AsyncGenerator<OmniMessage, LLMOutcome> {\n const userSignal = params.signal;\n\n // Already interrupted before issuing: no streaming segment has been opened, so nothing to close out.\n if (userSignal?.aborted) return { status: \"aborted\" };\n\n // Input merging is placed inside a guarded block: build failures such as empty input /\n // mixed roles / argument JSON also collapse to a failed outcome, never throwing to\n // context_engine.\n let uniMessage: UniMessage;\n try {\n uniMessage = mergeOmniToUniMessage(params.newMessages);\n } catch (err) {\n return {\n status: \"failed\",\n message: err instanceof Error ? err.message : String(err),\n };\n }\n\n const translator = new EventTranslator(this.toolCallIds);\n\n // Merges \"user interruption\" and \"idle timeout\" into a single internal AbortController: either triggering aborts the underlying stream.\n const ac = new AbortController();\n const onUserAbort = (): void => ac.abort();\n userSignal?.addEventListener(\"abort\", onUserAbort, { once: true });\n\n let timedOut = false;\n let timer: ReturnType<typeof setTimeout> | null = null;\n const clearTimer = (): void => {\n if (timer) {\n clearTimeout(timer);\n timer = null;\n }\n };\n const armTimer = (): void => {\n if (this.requestTimeoutMs <= 0) return; // Timeout disabled\n clearTimer();\n timer = setTimeout(() => {\n timedOut = true;\n ac.abort();\n }, this.requestTimeoutMs);\n };\n\n // Terminal-state classification: timeout (timed out/network drop) / malformed (response\n // parse error) / aborted (user) / failed (other). null means it ended normally.\n let outcome: LLMOutcome | null = null;\n try {\n const it = this.openStream(uniMessage, ac.signal)[Symbol.asyncIterator]();\n for (;;) {\n // The interruption check must happen **before pulling from upstream**: the user may\n // interrupt while this generator is suspended at the `yield` below (the typical case —\n // the engine is blocked on `await approve(tc)` waiting for human approval). By then,\n // onUserAbort has already called `ac.abort()`, cutting off the upstream stream; when the\n // consumer pulls again and we come back here to call `it.next()` on an **already-aborted\n // stream**, that promise will never settle. The idle timer can't save us either: once it\n // fires, it just calls `ac.abort()` again (already aborted, a no-op), and the pending\n // `it.next()` still hangs forever. The consequence is that `run` never closes out and the\n // Session stays stuck running forever — after interruption, it can neither send messages\n // nor compact.\n if (userSignal?.aborted) {\n outcome = { status: \"aborted\" };\n break;\n }\n // Timing runs **only while waiting on an upstream event** (excluding consumer/yield\n // time), measuring upstream idleness — this avoids a slow consumer (e.g. a slow Trace\n // sink) falsely triggering the timeout.\n armTimer();\n let res: IteratorResult<UniEvent>;\n try {\n res = await it.next();\n } finally {\n clearTimer();\n }\n if (res.done) break;\n if (userSignal?.aborted) {\n outcome = { status: \"aborted\" };\n break;\n }\n for (const msg of translator.pushEvent(res.value)) yield msg;\n }\n } catch (error) {\n // User interruption **takes priority**: even if the idle timer fires at the same time,\n // it's classified as aborted (user intent outweighs a coincidental timeout).\n if (userSignal?.aborted) {\n outcome = { status: \"aborted\" };\n } else if (timedOut) {\n outcome = { status: \"timeout\" }; // Idle timeout -> needs reconnection\n } else if (isMalformedJsonParseError(error) || isIncompleteStreamError(error)) {\n // A response JSON parse error, or a cleanly truncated stream (AgentHub's final-event\n // validation failed): both are an incomplete LLM Request, handed to the engine as\n // malformed to reconnect and retry — must not be classified as failed.\n outcome = {\n status: \"malformed\",\n message: error instanceof Error ? error.message : String(error),\n };\n } else if (isRetryableError(error)) {\n outcome = { status: \"timeout\" }; // Network drop/network error -> needs reconnection\n } else if ((error as { name?: string })?.name === \"AbortError\") {\n outcome = { status: \"aborted\" }; // Fallback: an unexpected abort (neither timeout nor user)\n } else {\n outcome = {\n status: \"failed\",\n message: error instanceof Error ? error.message : String(error),\n };\n }\n } finally {\n clearTimer();\n userSignal?.removeEventListener(\"abort\", onUserAbort);\n }\n\n // Defensive: when the underlying stream responds to an abort with a **graceful end (done)**\n // rather than throwing, it must still be closed out as interrupted/timed out, and must not be\n // misjudged as completed (priority matches the catch classification: user interruption >\n // timeout). Exception: if finish_reason was already received before the stream ended (the\n // response was fully delivered and AgentHub has already committed this turn into stateful\n // history), close out as completed — if the interruption race lands exactly during the wait\n // on the final next() and gets misjudged as aborted, the already-committed tool_use turn\n // would be cleaned up by context_engine as \"incomplete\" flatten, losing the tool_result\n // pairing; subsequent requests would all be rejected by the provider as an unanswered\n // tool_use (400), and the engine has no fix-up path left that touches LLM history.\n if (!outcome && !translator.sawFinishReason()) {\n if (userSignal?.aborted) outcome = { status: \"aborted\" };\n else if (timedOut) outcome = { status: \"timeout\" };\n }\n\n if (outcome) {\n // Interrupted/errored: close any opened streaming segments and backfill the complete message, producing no token_usage.\n const reason: StopReason = outcome.status === \"completed\" ? \"failed\" : outcome.status;\n for (const msg of translator.finishInterrupted(reason)) yield msg;\n return outcome;\n }\n\n // Normal completion: backfill stop + the complete model_msg, and produce token_usage.\n for (const msg of translator.finish()) yield msg;\n const requestTokens = translator.getRequestTokens();\n this.sessionTokens = addTokenCounts(this.sessionTokens, requestTokens);\n yield tokenUsage(this.sessionTokens, requestTokens);\n return { status: \"completed\" };\n }\n\n /**\n * Injects the replayed history in one shot when resuming a Session: converts the complete\n * OmniMessage history, grouped by adjacent same role, into\n * AgentHub UniMessages and calls AgentHub's setHistory, so subsequent Requests continue from a\n * history exactly matching the original conversation. **Called only once, on a fresh context\n * object, during resumption**; not used during normal operation, where the incremental context\n * is maintained by AgentHub itself.\n * Docs: /docs/sessions-and-traces § \"Session recovery\".\n */\n setHistory(history: OmniMessage[]): void {\n if (history.length === 0) return;\n // Resume seeding: register tool_call_ids already used in history into the uniqueness registry. A\n // name-as-id provider (e.g. Gemini) only gets a new suffix when it calls the same tool again after\n // resume, so it won't collide with the history tool cards the frontend already rendered.\n for (const msg of history) {\n const p = msg.payload as { type?: string; tool_call_id?: string };\n if (p.type === \"tool_call\" && p.tool_call_id) {\n this.toolCallIds.markUsed(p.tool_call_id);\n }\n }\n this.client.setHistory(groupHistoryToUniMessages(history));\n }\n\n /**\n * Opens the underlying AgentHub stream (a testing seam): defaults to\n * `streamingResponseStateful`; unit tests can subclass and override this method, feeding in a\n * controlled UniEvent stream to verify the outcome classification for timeout/network\n * drop/interruption/error (without a real API).\n */\n protected openStream(uniMessage: UniMessage, signal: AbortSignal): AsyncIterable<UniEvent> {\n return this.client.streamingResponseStateful({\n message: uniMessage,\n config: this.uniConfig,\n signal,\n });\n }\n}\n\n// ---------------------------------------------------------------------------\n// UniConfig pre-construction\n// ---------------------------------------------------------------------------\n\n/** Maps a ThinkingLevelName to the AgentHub ThinkingLevel enum; returns undefined if not found. */\nexport function mapThinkingLevel(name: ThinkingLevelName | undefined): ThinkingLevel | undefined {\n if (name === undefined) return undefined;\n const table: Record<ThinkingLevelName, ThinkingLevel> = {\n none: ThinkingLevel.NONE,\n low: ThinkingLevel.LOW,\n medium: ThinkingLevel.MEDIUM,\n high: ThinkingLevel.HIGH,\n xhigh: ThinkingLevel.XHIGH,\n };\n return table[name];\n}\n\n/** Maps ToolDefinition[] to AgentHub ToolSchema[]. */\nexport function toolDefinitionsToSchemas(tools: ToolDefinition[]): ToolSchema[] {\n return tools.map((tool) => ({\n name: tool.name,\n description: tool.description,\n ...(tool.parameters !== undefined ? { parameters: tool.parameters } : {}),\n }));\n}\n\n/** Pre-builds UniConfig from GenerativeModelConfig (called once at construction time). */\nexport function buildUniConfig(config: GenerativeModelConfig): UniConfig {\n const uniConfig: UniConfig = {\n tools: toolDefinitionsToSchemas(config.tools),\n };\n if (config.systemPrompt !== undefined) {\n uniConfig.system_prompt = config.systemPrompt;\n }\n if (config.maxTokens !== undefined) {\n uniConfig.max_tokens = config.maxTokens;\n }\n const thinking = mapThinkingLevel(config.thinkingLevel);\n if (thinking !== undefined) {\n uniConfig.thinking_level = thinking;\n }\n return uniConfig;\n}\n","/**\n * Session-level uniqueness for tool_call_id.\n *\n * Some providers don't produce a real call id: e.g. Gemini's functionCall has no id, so AgentHub\n * uses the **function name** as the `tool_call_id` — consecutive/parallel calls to the same tool then\n * all share one id. But the OmniMessage world (engine dispatch/pairing, approval routing, frontend\n * tool-card attribution) keys on `tool_call_id`, and a collision lets a later call overwrite the\n * earlier one (parallel same-name calls in one turn can even be dropped entirely).\n *\n * Approach: inbound, `EventTranslator` disambiguates duplicate ids with a `#n` suffix (the first keeps\n * the original id); outbound (returning tool_result, replaying history on resume) uses\n * `stripToolCallIdSuffix` to strip the suffix and restore the original — Gemini's functionResponse\n * pairs by using `tool_call_id` as the name, so it must be restored to the function name. The registry\n * lives at Session level (the new GenerativeModel rebuilt on compaction shares the same instance), and\n * on resume `setHistory` seeds it with historical ids, so the uniqueness scope covers the entire\n * context the frontend renders.\n * Docs: /docs/interfaces § \"The built-in implementation: GenerativeModel\".\n */\nexport class ToolCallIdAllocator {\n /** OmniMessage-level tool_call_ids already taken in this Session (history-seeded + allocated). */\n private used = new Set<string>();\n\n /** Register an already-used id (for resume seeding); registering twice is harmless. */\n markUsed(id: string): void {\n this.used.add(id);\n }\n\n /**\n * Allocate a Session-unique id for a provider-reported tool_call_id: if unused, keep the original;\n * if already used (a repeat call from a name-as-id provider), take the first free `origId#n` (n from 2).\n * Providers with truly unique ids (OpenAI `call_*` / Claude `toolu_*`) never collide, so they pass through unchanged.\n */\n allocate(providerId: string): string {\n let id = providerId;\n for (let n = 2; this.used.has(id); n += 1) {\n id = `${providerId}#${n}`;\n }\n this.used.add(id);\n return id;\n }\n}\n\n/**\n * Strip the `#n` suffix added by `allocate`, restoring the provider's original id (returns as-is when\n * there's no suffix; idempotent). On resume there's no registry to compare against, so it trims by\n * shape: real ids from known providers (OpenAI/Claude `call_*`/`toolu_*`, Gemini function names — `#`\n * isn't a valid function-name char) never end in `#<digits>`, so they aren't harmed.\n */\nexport function stripToolCallIdSuffix(id: string): string {\n return id.replace(/#\\d+$/, \"\");\n}\n","/**\n * exec_command —— local shell executor, a built-in tool implementation (BuiltinTool).\n *\n * Spawns a process inside the Workspace via `bash -lc <cmd>` and streams content deltas as\n * stdout/stderr chunks arrive. Waits up to `yield_time_ms`: if the command finishes in time,\n * returns the full output and exit status; if it's still running when the deadline hits,\n * returns the output collected so far plus a `process_id` — the process moves to background,\n * managed by `CommandSessionManager`, and is interacted with afterward via `input_command`.\n * Completion is decided by **the foreground\n * process exiting**, not by waiting for EOF on the output stream — background children (e.g.\n * `node server.js &`) that inherit the pipes won't hold up the tool.\n *\n * Division of responsibility with Environment (see environment.ts): this tool only produces\n * content deltas; exit code/signal/spawn errors and `process_id` are reported via the return\n * value's `note` (appended outside the maxOutputLength truncation, so it survives even when\n * long output gets truncated). Whether it ends normally or abnormally, it always finishes via\n * the return value, **never throws**; on interruption it only reports `aborted` — the\n * interruption note itself is appended by Environment.\n * Docs: /docs/tools § \"Command sessions\".\n */\nimport path from \"node:path\";\nimport { partialToolCallOutput } from \"../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../omnimessage/index.js\";\nimport type { EnvironmentServices, ToolDefinitionConfig } from \"../../interfaces.js\";\nimport type { BuiltinTool, ToolExecutionContext, ToolResult } from \"./types.js\";\nimport { DEFAULT_EXEC_YIELD_MS, resultForExit } from \"./command/index.js\";\nimport { clampYield } from \"./background/index.js\";\n\n/** Tool name constant (used only inside this tool module, not exposed to Environment). */\nexport const EXEC_COMMAND_NAME = \"exec_command\";\n\n/**\n * exec_command built-in tool: parses arguments, resolves workdir, and delegates to\n * `CommandSessionManager` to spawn the process and collect output.\n * `definition` is overridden by Environment at construction time with the matching entry\n * from ToolConfig (description/parameters/permission/limits).\n * `services.commandSessions` is injected by Environment (shares the same registry with\n * input_command).\n */\nexport function createExecCommandTool(\n definition: ToolDefinitionConfig,\n services?: EnvironmentServices,\n): BuiltinTool {\n const manager = services?.commandSessions;\n return {\n name: EXEC_COMMAND_NAME,\n definition,\n async *execute(\n args: Record<string, unknown>,\n ctx: ToolExecutionContext,\n ): AsyncGenerator<OmniMessage, ToolResult | void> {\n const { toolCallId, signal } = ctx;\n const delta = (output: string): OmniMessage =>\n partialToolCallOutput({ eventType: \"delta\", output, toolCallId });\n\n if (!manager) {\n yield delta(\"[exec_command unavailable: no command session manager configured]\");\n return { stopReason: \"failed\" };\n }\n\n const cmd = args[\"cmd\"];\n if (typeof cmd !== \"string\" || cmd.length === 0) {\n yield delta('Missing required argument \"cmd\" for exec_command.');\n return { stopReason: \"failed\" };\n }\n // workdir defaults to workspaceDir; relative paths are resolved against workspaceDir.\n const rawWorkdir = args[\"workdir\"];\n const workdir =\n typeof rawWorkdir === \"string\" && rawWorkdir.length > 0\n ? path.resolve(ctx.workspaceDir, rawWorkdir)\n : ctx.workspaceDir;\n const yieldMs = clampYield(\n args[\"yield_time_ms\"],\n DEFAULT_EXEC_YIELD_MS,\n definition.timeoutMs,\n );\n\n // Caller already aborted: finish immediately with aborted.\n if (signal?.aborted) return { stopReason: \"aborted\" };\n\n let session;\n try {\n session = manager.spawn({ cmd, cwd: workdir });\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n yield delta(`[spawn error: ${message}]`);\n return { stopReason: \"failed\" };\n }\n\n // On interruption, kill the whole process group (background children included) to\n // avoid orphans; once the process moves to background this listener is removed in finally.\n const onAbort = (): void => session.kill();\n let registered = false;\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n for await (const chunk of session.collect(yieldMs, signal)) yield delta(chunk);\n\n if (signal?.aborted) return { stopReason: \"aborted\" };\n if (session.running) {\n // Still running at the deadline: register as a background process, returning\n // process_id so input_command can continue accessing it.\n const id = manager.register(session);\n registered = true;\n return {\n stopReason: \"completed\",\n note: `[process running with process_id ${id}; use input_command to send input or poll for output]`,\n };\n }\n // Already exited: report exit status; process group cleanup (reaping any leftover\n // background children) is handled uniformly in finally.\n if (session.error) {\n return { stopReason: \"failed\", note: `[spawn error: ${session.error.message}]` };\n }\n return resultForExit(session.exit);\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!registered) session.kill();\n }\n },\n };\n}\n","/**\n * ManagedSession — runtime state and collection logic for a single command session.\n *\n * Spawns the process with `bash -lc <cmd>`, with stdout/stderr going through plain pipes (no\n * native dependency, clean output; an interactive program that detects no TTY falls back to\n * non-interactive mode, which parses more cleanly for the Agent anyway). `detached` makes the\n * child process the process-group leader, so both Ctrl-C and killing the whole group rely on\n * **process-group signals** (sending a signal to `-pid` also reaches background child processes).\n *\n * Key semantics:\n * - **Termination is determined by the foreground process exiting (the exit event, waitpid\n * semantics), not by waiting for stream EOF**: background child processes that inherit the\n * pipe don't hold things up;\n * - `collect(yieldMs)` **streams** output deltas within the budget: data is yielded as soon as it\n * arrives, without waiting for the window to end; if the command exits mid-window, the trailing\n * output is yielded along with it (with a capped drain window); if it's still running once the\n * window expires, whatever output exists is yielded and collection ends, with the process\n * switching to background; if `signal` aborts, whatever output exists is yielded and collection\n * ends immediately;\n * - Unread output has a cap (memory safety); when exceeded, the oldest part is dropped and\n * counted, with a marker shown on read;\n * - `kill()` sends SIGTERM to the process group, then SIGKILL after a grace period, reaping any\n * leftover background child processes; idempotent.\n */\nimport { spawn, type ChildProcess } from \"node:child_process\";\nimport type { ToolResult } from \"../types.js\";\nimport { CappedTextBuffer, WakeSignal } from \"../background/index.js\";\n\n/** Process-group semantics are available on POSIX; Windows falls back to signaling the child process directly. */\nconst SUPPORTS_PROCESS_GROUP = process.platform !== \"win32\";\n\n/** Extra wait cap (ms) after the command exits to collect trailing output: enough to drain the last flush, without hanging. */\nconst POST_EXIT_DRAIN_MS = 50;\n/** Capacity cap (characters) for a single session's unread output: prevents a chatty background process from blowing up memory. */\nconst OUTPUT_BUFFER_CAP = 1024 * 1024; // 1 MiB\n/**\n * Grace period (ms) before escalating from SIGTERM to SIGKILL: gives a process that needs to\n * clean up (flush data, remove temp files) some time. The timer is unref'd, so it won't hold up\n * the host process from exiting; the process-exit path sends SIGKILL directly as a fallback.\n */\nconst SIGKILL_GRACE_MS = 1_000;\n\n/** Foreground process exit info. At most one of `code`/`signal` is set (consistent with Node child's exit event). */\nexport interface ProcessExit {\n code: number | null;\n signal: NodeJS.Signals | null;\n}\n\n/** Arguments required to start a command. */\nexport interface SpawnOptions {\n /** Command string handed to `bash -lc`. */\n cmd: string;\n /** Working directory (absolute path). */\n cwd: string;\n /** Child process environment variables (the caller has already injected hardening entries like PAGER/TERM). */\n env: NodeJS.ProcessEnv;\n}\n\nexport class ManagedSession {\n /** Timestamp of the last access (used for LRU / idle reaping). */\n lastUsed: number = Date.now();\n\n private readonly child: ChildProcess;\n private readonly buffer = new CappedTextBuffer(OUTPUT_BUFFER_CAP, \"earlier output\");\n private exited = false;\n private exitInfo: ProcessExit | null = null;\n private spawnError: Error | null = null;\n private killed = false;\n private killTimer: ReturnType<typeof setTimeout> | null = null;\n // Single wake point: data arrival / process exit / spawn error all wake a waiting collect through it.\n private readonly wakeSignal = new WakeSignal();\n\n constructor(opts: SpawnOptions) {\n this.child = spawn(\"bash\", [\"-lc\", opts.cmd], {\n cwd: opts.cwd,\n env: opts.env,\n detached: SUPPORTS_PROCESS_GROUP, // Become the process-group leader, so the whole group can be signaled\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n this.child.stdout?.setEncoding(\"utf8\");\n this.child.stderr?.setEncoding(\"utf8\");\n // stdin may already be closed by the command before input_command writes to it;\n // EPIPE/ERR_STREAM_DESTROYED are an expected race and must not bubble up to the host process\n // as an unhandled error.\n this.child.stdin?.on(\"error\", () => {});\n this.child.stdout?.on(\"data\", (c: string) => this.handleData(c));\n this.child.stderr?.on(\"data\", (c: string) => this.handleData(c));\n // exit follows waitpid semantics: it fires as soon as bash exits, without waiting for\n // stdout/stderr pipe EOF — background child processes that inherit and hold the pipe open\n // won't hold up termination.\n this.child.on(\"exit\", (code, signal) => this.handleExit({ code, signal }));\n this.child.on(\"error\", (err) => this.handleError(err));\n }\n\n /** Signals the process group; ignores the case where the process/group has already exited (ESRCH). */\n private signalGroup(sig: NodeJS.Signals): void {\n try {\n if (SUPPORTS_PROCESS_GROUP && typeof this.child.pid === \"number\" && this.child.pid > 0) {\n process.kill(-this.child.pid, sig); // Negative pid = the whole process group\n } else {\n this.child.kill(sig);\n }\n } catch {\n // ESRCH etc., ignored.\n }\n }\n\n private handleData(chunk: string): void {\n this.buffer.append(chunk);\n this.wakeSignal.notify();\n }\n private handleExit(exit: ProcessExit): void {\n if (this.exited) return;\n this.exited = true;\n this.exitInfo = exit;\n this.wakeSignal.notify();\n }\n private handleError(err: Error): void {\n if (this.exited) return;\n this.spawnError = err;\n this.exited = true; // A spawn failure is also treated as a terminal state\n this.wakeSignal.notify();\n }\n\n /** Whether the command is still running (hasn't exited, spawn hasn't failed). */\n get running(): boolean {\n return !this.exited;\n }\n get exit(): ProcessExit | null {\n return this.exitInfo;\n }\n get error(): Error | null {\n return this.spawnError;\n }\n\n /**\n * Streams output deltas within `yieldMs` (data is yielded as soon as it arrives). Once done,\n * the terminal state is determined via `running`/`exit`/`error`:\n * - Exits mid-window -> the trailing output is yielded along with it (extra ≤POST_EXIT_DRAIN_MS drain);\n * - Still running once the window expires -> whatever output exists is yielded and collection ends, with the process switching to background;\n * - `signal` aborts -> whatever output exists is yielded and collection ends immediately (the process isn't killed; the caller decides whether to keep it).\n */\n async *collect(yieldMs: number, signal?: AbortSignal): AsyncGenerator<string> {\n const start = Date.now();\n const onAbort = (): void => this.wakeSignal.notify();\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n // Phase one: running, data is yielded as soon as it arrives, until exit / abort / yield expires.\n while (!this.exited) {\n const chunk = this.buffer.drain();\n if (chunk) yield chunk;\n if (signal?.aborted) return;\n const remaining = yieldMs - (Date.now() - start);\n if (remaining <= 0) {\n const tail = this.buffer.drain();\n if (tail) yield tail;\n return; // Still running -> yield\n }\n // Re-check the predicate before sleeping: data that arrives while `yield` is suspended\n // wakes at a point before this wait begins, and would otherwise be missed.\n if (!this.buffer.isEmpty) continue;\n await this.wakeSignal.wait(remaining);\n }\n // Phase two: already exited (or spawn failed) -> drain the trailing output, with a cap.\n const head = this.buffer.drain();\n if (head) yield head;\n const drainStart = Date.now();\n for (;;) {\n if (!this.buffer.isEmpty) {\n yield this.buffer.drain();\n continue;\n }\n const left = POST_EXIT_DRAIN_MS - (Date.now() - drainStart);\n if (left <= 0) break;\n await this.wakeSignal.wait(left);\n if (this.buffer.isEmpty) break; // Woke with no new data (or timed out) -> draining is done\n }\n const tail = this.buffer.drain();\n if (tail) yield tail;\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n }\n }\n\n write(chars: string): void {\n this.lastUsed = Date.now();\n try {\n if (!this.child.stdin || this.child.stdin.destroyed) return;\n this.child.stdin.write(chars, () => {});\n } catch {\n // stdin may already be closed, ignored.\n }\n }\n interrupt(): void {\n this.lastUsed = Date.now();\n this.signalGroup(\"SIGINT\");\n }\n\n /** Closes out: sends SIGTERM to the process group, then SIGKILL after a grace period (reaping leftover background child processes); idempotent. */\n kill(): void {\n if (this.killed) return;\n this.killed = true;\n this.signalGroup(\"SIGTERM\");\n // Unconditionally escalates to SIGKILL: the foreground has exited but background child\n // processes may still be around; killpg on an already-vanished group is ESRCH (harmless).\n this.killTimer = setTimeout(() => this.signalGroup(\"SIGKILL\"), SIGKILL_GRACE_MS);\n this.killTimer.unref?.();\n }\n\n /** Synchronous hard kill (process 'exit' fallback: the event loop has already stopped at this point, so timers aren't available). */\n killHard(): void {\n this.killed = true;\n if (this.killTimer) {\n clearTimeout(this.killTimer);\n this.killTimer = null;\n }\n this.signalGroup(\"SIGKILL\");\n }\n}\n\n/** Converts exit info into a tool result (the terminal marker is appended via `note`, outside the truncation, so it isn't lost with long output). */\nexport function resultForExit(exit: ProcessExit | null): ToolResult {\n if (!exit) return { stopReason: \"completed\" };\n if (exit.signal) return { stopReason: \"failed\", note: `[terminated by signal ${exit.signal}]` };\n if (exit.code !== 0)\n return { stopReason: \"failed\", note: `[exit code: ${exit.code ?? \"unknown\"}]` };\n return { stopReason: \"completed\" };\n}\n","/**\n * BackgroundRegistry —— generic registry for background sessions, shared by command sessions\n * and subagent sessions.\n *\n * Responsibilities: allocating and managing background session ids, enforcing the concurrency\n * cap, reclaiming idle sessions, uniform finalization when the Session/Environment ends, and a\n * hard kill on process 'exit' as a fallback (JS has no destructors, so cleanup must be explicit).\n * Background sessions living for days at a time are a legitimate form; idle reclamation is only\n * a leak fallback — sessions unaccessed for longer than `IDLE_TTL_MS` are finalized by a\n * periodic sweep.\n *\n * The two concurrency-cap strategies are expressed by how `makeRoom` is called:\n * - Command sessions: if full at registration time, prefer evicting exited sessions, otherwise\n * evict LRU (killing a background process is an acceptable cost);\n * - Subagent sessions: `makeRoom` is called before launch (only evicting completed, idle ones);\n * if there's no room, spawning is rejected — evicting a running subagent is equivalent to\n * discarding in-progress work, which is semantically unacceptable.\n *\n * No lock is needed under the single-threaded event loop; but note the registry may change\n * across an `await`, so check before using an entry.\n * Docs: /docs/tools § \"Background session caps\".\n */\nimport { randomUUID } from \"node:crypto\";\n\n/** Idle reclamation TTL (milliseconds): a session unaccessed for longer than this is treated as a leak and reclaimed. */\nconst IDLE_TTL_MS = 10 * 24 * 60 * 60_000; // 10 days\n/** Idle reclamation check interval (milliseconds): TTL is measured in days, so an hourly sweep is sufficient. */\nconst IDLE_SWEEP_MS = 60 * 60_000;\n\n/** Minimal contract a background session must satisfy to be managed by the registry. */\nexport interface BackgroundTask {\n /** Timestamp of the most recent access (used for LRU eviction); refreshed by the registry on register/get. */\n lastUsed: number;\n /** Whether the session is still running (determines eviction priority). */\n running: boolean;\n /** Asynchronous finalization (SIGTERM -> SIGKILL / abort); idempotent. */\n kill(): void;\n /** Synchronous hard kill (process 'exit' fallback path: the event loop has stopped, timers are unavailable). */\n killHard(): void;\n}\n\n// process 'exit' fallback: use a single module-level listener to manage all registries, avoiding\n// each Session adding its own listener and triggering EventEmitter's MaxListeners warning.\nconst LIVE_REGISTRIES = new Set<BackgroundRegistry<BackgroundTask>>();\nlet exitHookInstalled = false;\nfunction ensureExitHook(): void {\n if (exitHookInstalled) return;\n exitHookInstalled = true;\n process.on(\"exit\", () => {\n for (const r of LIVE_REGISTRIES) r.killAllHard();\n });\n}\n\nexport class BackgroundRegistry<T extends BackgroundTask> {\n private readonly tasks = new Map<string, T>();\n private readonly idPrefix: string;\n private readonly maxTasks: number;\n private readonly reapTimer: ReturnType<typeof setInterval>;\n private disposed = false;\n\n constructor(opts: { idPrefix: string; maxTasks: number }) {\n this.idPrefix = opts.idPrefix;\n this.maxTasks = opts.maxTasks;\n LIVE_REGISTRIES.add(this as unknown as BackgroundRegistry<BackgroundTask>);\n ensureExitHook();\n this.reapTimer = setInterval(() => this.reapIdle(), IDLE_SWEEP_MS);\n this.reapTimer.unref?.();\n }\n\n get size(): number {\n return this.tasks.size;\n }\n\n /**\n * Makes room for a new session. Returns true immediately if not full; when full, evicts per\n * `evictRunning`:\n * - false (subagent): only evicts the least-recently-used **completed** session; if all are\n * running, returns false (the caller rejects spawning);\n * - true (command): evicts exited sessions first, otherwise LRU-evicts a running one.\n */\n makeRoom(evictRunning: boolean): boolean {\n if (this.tasks.size < this.maxTasks) return true;\n let lruId: string | null = null;\n let lruUsed = Number.POSITIVE_INFINITY;\n for (const [id, t] of this.tasks) {\n if (!t.running) {\n this.remove(id); // Prefer evicting sessions that have already ended\n return true;\n }\n if (t.lastUsed < lruUsed) {\n lruUsed = t.lastUsed;\n lruId = id;\n }\n }\n if (!evictRunning) return false;\n if (lruId) this.remove(lruId);\n return this.tasks.size < this.maxTasks;\n }\n\n /**\n * Registers a session, allocating and returning a unique id (`<prefix>-xxxxxxxx`). The caller\n * must first free up room via `makeRoom`. `preferredSuffix` is the preferred id suffix (e.g.\n * the tail of a child Session id, so the tool handle correlates with the message origin/\n * frontend nesting label); falls back to random if omitted or on collision.\n */\n register(task: T, preferredSuffix?: string): string {\n this.ensureActive();\n let id = preferredSuffix ? `${this.idPrefix}-${preferredSuffix}` : this.randomId();\n while (this.tasks.has(id)) id = this.randomId();\n task.lastUsed = Date.now();\n this.tasks.set(id, task);\n return id;\n }\n\n private randomId(): string {\n return `${this.idPrefix}-${randomUUID().replace(/-/g, \"\").slice(0, 8)}`;\n }\n\n /** Looks up a session by id and refreshes its access time; returns undefined if not found. */\n get(id: string): T | undefined {\n if (this.disposed) return undefined;\n const t = this.tasks.get(id);\n if (t) t.lastUsed = Date.now();\n return t;\n }\n\n /** Removes a session from the registry and finalizes it. */\n remove(id: string): void {\n const t = this.tasks.get(id);\n if (!t) return;\n this.tasks.delete(id);\n t.kill();\n }\n\n /** Kills and clears all sessions (called when the Session/Environment ends). */\n killAll(): void {\n for (const t of this.tasks.values()) t.kill();\n this.tasks.clear();\n }\n\n /** Synchronously hard-kills all sessions (process 'exit' fallback path). */\n killAllHard(): void {\n for (const t of this.tasks.values()) t.killHard();\n this.tasks.clear();\n }\n\n /** Disposes: removes the fallback registration and kills all sessions. Idempotent. */\n dispose(): void {\n if (this.disposed) return;\n this.disposed = true;\n clearInterval(this.reapTimer);\n LIVE_REGISTRIES.delete(this as unknown as BackgroundRegistry<BackgroundTask>);\n this.killAll();\n }\n\n /** Whether the registry has been disposed (the host Session has ended). */\n get isDisposed(): boolean {\n return this.disposed;\n }\n\n /** Reclaims sessions idle for longer than `IDLE_TTL_MS` (leak fallback, triggered by the periodic sweep). */\n private reapIdle(): void {\n const cutoff = Date.now() - IDLE_TTL_MS;\n for (const [id, t] of this.tasks) {\n if (t.lastUsed <= cutoff) this.remove(id);\n }\n }\n\n private ensureActive(): void {\n if (this.disposed) {\n throw new Error(\"background session registry disposed\");\n }\n }\n}\n","/**\n * Yield-time clamping shared by background-session tools.\n *\n * `yield_time_ms` is a soft budget for a single tool call: \"wait at most until the session ends\n * or this duration expires\" (expiry yields, it's not a failure). Only a lower bound is set: a\n * wait that's too short isn't meaningful and just adds round trips. The upper bound is no longer\n * an independent constant — it's derived from the tool's own `timeoutMs` (with a reserved\n * margin, so the yield happens before the Environment's timeout fallback fires); no upper bound\n * is set when `timeoutMs <= 0` (disabled).\n */\n\n/** Lower bound for yield time (ms). */\nexport const MIN_YIELD_MS = 250;\n/** Margin (ms) reserved between the yield upper bound and the tool's `timeoutMs`: the yield must happen before the timeout fallback. */\nconst TIMEOUT_MARGIN_MS = 1_000;\n\n/** Clamps the raw argument to `[MIN_YIELD_MS, timeoutMs - margin]`; falls back to `fallback` if not a number, no upper bound when `timeoutMs <= 0`. */\nexport function clampYield(raw: unknown, fallback: number, timeoutMs?: number): number {\n const n = typeof raw === \"number\" && Number.isFinite(raw) ? raw : fallback;\n const lower = Math.max(n, MIN_YIELD_MS);\n if (timeoutMs === undefined || timeoutMs <= 0) return lower;\n return Math.min(lower, Math.max(timeoutMs - TIMEOUT_MARGIN_MS, MIN_YIELD_MS));\n}\n","/**\n * WakeSignal —— a single wakeup point shared by background sessions.\n *\n * Producer events (data arrival / run finished / new approval request) call `notify()`;\n * waiters use `wait(ms)` to wait for \"woken up\" or expiry, whichever comes first. `notify`\n * swaps in a new promise before resolving the old one, so a waiter that wakes up just\n * re-checks state — it never misses an event that immediately follows.\n */\nexport class WakeSignal {\n private promise!: Promise<void>;\n private resolve!: () => void;\n\n constructor() {\n this.arm();\n }\n\n private arm(): void {\n this.promise = new Promise<void>((resolve) => {\n this.resolve = resolve;\n });\n }\n\n /** Wakes up all waiters: swaps in a new promise before resolving the old one (avoids missing an event that immediately follows). */\n notify(): void {\n const r = this.resolve;\n this.arm();\n r();\n }\n\n /** Waits for \"woken up\" or `ms` to elapse, whichever comes first. */\n async wait(ms: number): Promise<void> {\n let timer: ReturnType<typeof setTimeout> | null = null;\n const timeout = new Promise<void>((resolve) => {\n // wait is part of an active operation: the timer needs to keep the process alive, and is cleaned up immediately below after notify.\n timer = setTimeout(resolve, ms);\n });\n try {\n await Promise.race([this.promise, timeout]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n }\n}\n","/**\n * CappedTextBuffer — capacity-capped unread-text buffer shared by background sessions.\n *\n * When over capacity, drops the oldest content (keeping the tail) and tallies the dropped count;\n * `drain()` prefixes a marker noting the drop count when taking all unread content (guards\n * against a chatty background process / sub-agent blowing up memory, see each session class's\n * capacity constant).\n */\nexport class CappedTextBuffer {\n private text = \"\";\n private omitted = 0; // Characters dropped due to the capacity cap, not yet read\n\n /** `dropLabel` is used in the drop-marker text, e.g. \"earlier output\" / \"earlier subagent output\". */\n constructor(\n private readonly cap: number,\n private readonly dropLabel: string,\n ) {}\n\n get isEmpty(): boolean {\n return this.text.length === 0 && this.omitted === 0;\n }\n\n append(chunk: string): void {\n this.text += chunk;\n if (this.text.length > this.cap) {\n const drop = this.text.length - this.cap;\n this.text = this.text.slice(drop); // Keep the newest (tail), drop the oldest\n this.omitted += drop;\n }\n }\n\n /** Takes the current unread content (including the drop marker); clears the buffer. */\n drain(): string {\n if (this.isEmpty) return \"\";\n const b = this.text;\n this.text = \"\";\n if (this.omitted > 0) {\n const n = this.omitted;\n this.omitted = 0;\n return `[... ${n} chars of ${this.dropLabel} dropped ...]\\n${b}`;\n }\n return b;\n }\n}\n","/**\n * CommandSessionManager — registry and lifecycle management for long-running command sessions.\n *\n * Constructed by Environment (one per Session), injected via services and shared by the\n * `exec_command` and `input_command` tools. Registry responsibilities (id allocation, concurrency\n * cap, dispose, process 'exit' fallback) are handled by the generic `BackgroundRegistry` (shared\n * with subagent sessions, see `../background/registry.ts`); this class only retains\n * command-domain logic: spawning processes and assembling the child process environment (vault\n * injection + hardening).\n * Docs: /docs/tools § \"Background session caps\".\n */\nimport { ManagedSession } from \"./session.js\";\nimport { BackgroundRegistry } from \"../background/index.js\";\n\n/** Concurrent managed-session cap: evicts once exceeded (exited sessions first, otherwise LRU — killing a background process has bounded cost). */\nconst MAX_SESSIONS = 64;\n\n/**\n * Hardening overrides applied to the child process environment: suppresses editor/credential\n * prompts/pagers/color etc. that could interact, avoiding a command hanging while waiting for\n * input. `GIT_EDITOR=true` prevents `git commit`/`rebase -i` from popping an editor;\n * `GIT_TERMINAL_PROMPT=0` prevents git from interactively asking for credentials; in pipe mode,\n * git and similar tools already auto-disable the pager, so the `PAGER` entries are just an extra\n * safeguard.\n */\nconst HARDENED_ENV: NodeJS.ProcessEnv = {\n GIT_EDITOR: \"true\",\n GIT_TERMINAL_PROMPT: \"0\",\n TERM: \"dumb\",\n NO_COLOR: \"1\",\n PAGER: \"cat\",\n GIT_PAGER: \"cat\",\n};\n\nexport class CommandSessionManager {\n private readonly registry = new BackgroundRegistry<ManagedSession>({\n idPrefix: \"proc\",\n maxTasks: MAX_SESSIONS,\n });\n\n /** Agent vault environment variables: injected into the child process on every spawn (values never enter the model context, only the environment). */\n private readonly vault: Record<string, string>;\n\n constructor(opts?: { vault?: Record<string, string> }) {\n this.vault = opts?.vault ?? {};\n }\n\n /** Starts a command, returning an **unregistered** session (no process_id yet). */\n spawn(opts: { cmd: string; cwd: string }): ManagedSession {\n if (this.registry.isDisposed) {\n throw new Error(\"command session manager disposed\");\n }\n return new ManagedSession({\n cmd: opts.cmd,\n cwd: opts.cwd,\n // Spread order is priority: vault overrides host variables of the same name, but must\n // come before HARDENED_ENV — the hardening entries (GIT_EDITOR/PAGER etc. that prevent\n // interactive hangs) must never be overridable by vault.\n env: { ...process.env, ...this.vault, ...HARDENED_ENV },\n });\n }\n\n /** Registers a still-running session as a background process, allocating and returning a unique `process_id`. */\n register(session: ManagedSession): string {\n this.registry.makeRoom(true);\n return this.registry.register(session);\n }\n\n /** Looks up a session by process_id and refreshes its access time; returns undefined if it doesn't exist. */\n get(processId: string): ManagedSession | undefined {\n return this.registry.get(processId);\n }\n\n /** Removes from the registry and cleans up the process group (called after the session exits). */\n remove(processId: string): void {\n this.registry.remove(processId);\n }\n\n /** Disposes: removes the fallback registration and kills all sessions (the process 'exit' fallback is hooked up by the registry itself). Idempotent. */\n dispose(): void {\n this.registry.dispose();\n }\n}\n","/**\n * Default yield durations for long-running command sessions.\n *\n * `yield_time_ms` is the soft budget for a tool call to \"wait at most until the command ends or\n * this duration elapses\" (yielding on expiry is not a failure); see `../background/limits.ts`\n * for the clamping logic: it only sets a floor, the ceiling is derived from the tool's own\n * `timeoutMs`.\n */\n\n/** Default wait duration (milliseconds) for `exec_command` starting a command. */\nexport const DEFAULT_EXEC_YIELD_MS = 60_000;\n/** Default wait duration (milliseconds) for `input_command` when there's a write. */\nexport const DEFAULT_WRITE_YIELD_MS = 250;\n/** Default wait duration (milliseconds) for `input_command` on an empty poll. */\nexport const DEFAULT_EMPTY_POLL_YIELD_MS = 5_000;\n","/**\n * input_command — accesses a long-running command session started by `exec_command` (BuiltinTool).\n *\n * Finds the session by `process_id`: if `chars` is non-empty, writes it to stdin first (when it is exactly `\\u0003`, special-cased as sending SIGINT to the\n * process group, i.e. Ctrl-C — it must be sent alone; mixing it with other content errors out,\n * since a pipe has no terminal line discipline and a mixed-in ETX byte would just be written\n * into stdin silently with no effect), and if empty, nothing is written and it only polls.\n * It then collects new output within `yield_time_ms` or waits for exit. If the command is still\n * running, returns the same `process_id`; once exited, returns the trailing output and exit\n * status and cleans up the session.\n *\n * Shares the same `CommandSessionManager` injected by Environment with exec_command. An\n * interruption only cancels this poll — **it does not kill the background process** (the process\n * was started independently earlier; interrupting one poll shouldn't kill it as a side effect).\n * Docs: /docs/tools § \"Command sessions\".\n */\nimport { partialToolCallOutput } from \"../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../omnimessage/index.js\";\nimport type { EnvironmentServices, ToolDefinitionConfig } from \"../../interfaces.js\";\nimport type { BuiltinTool, ToolExecutionContext, ToolResult } from \"./types.js\";\nimport {\n DEFAULT_EMPTY_POLL_YIELD_MS,\n DEFAULT_WRITE_YIELD_MS,\n resultForExit,\n} from \"./command/index.js\";\nimport { clampYield } from \"./background/index.js\";\n\n/** Tool name constant. */\nexport const INPUT_COMMAND_NAME = \"input_command\";\n\n/** Ctrl-C: the ETX control character (U+0003). Received alone, it sends SIGINT to the process group instead of writing the byte into stdin. */\nconst INTERRUPT = String.fromCharCode(3); // U+0003 (ETX)\n\nexport function createInputCommandTool(\n definition: ToolDefinitionConfig,\n services?: EnvironmentServices,\n): BuiltinTool {\n const manager = services?.commandSessions;\n return {\n name: INPUT_COMMAND_NAME,\n definition,\n async *execute(\n args: Record<string, unknown>,\n ctx: ToolExecutionContext,\n ): AsyncGenerator<OmniMessage, ToolResult | void> {\n const { toolCallId, signal } = ctx;\n const delta = (output: string): OmniMessage =>\n partialToolCallOutput({ eventType: \"delta\", output, toolCallId });\n\n if (!manager) {\n yield delta(\"[input_command unavailable: no command session manager configured]\");\n return { stopReason: \"failed\" };\n }\n\n const processId = args[\"process_id\"];\n if (typeof processId !== \"string\" || processId.length === 0) {\n yield delta('Missing required argument \"process_id\" for input_command.');\n return { stopReason: \"failed\" };\n }\n const session = manager.get(processId);\n if (!session) {\n yield delta(\n `[input_command error: unknown process_id ${processId} (the session may have exited and been cleared)]`,\n );\n return { stopReason: \"failed\" };\n }\n\n const chars = typeof args[\"chars\"] === \"string\" ? (args[\"chars\"] as string) : \"\";\n const empty = chars.length === 0;\n const yieldMs = clampYield(\n args[\"yield_time_ms\"],\n empty ? DEFAULT_EMPTY_POLL_YIELD_MS : DEFAULT_WRITE_YIELD_MS,\n definition.timeoutMs,\n );\n\n if (signal?.aborted) return { stopReason: \"aborted\" };\n\n // Write / interrupt (empty chars just polls). U+0003 mixed with other content errors out\n // rather than being written silently (same as codex): a pipe has no terminal line\n // discipline, so an ETX byte in stdin produces no interruption — the model would just\n // see the command still running.\n if (!empty) {\n if (chars === INTERRUPT) session.interrupt();\n else if (chars.includes(INTERRUPT)) {\n yield delta(\n '[input_command error: chars mixes U+0003 (Ctrl-C) with other content; send \"\\\\u0003\" alone to interrupt]',\n );\n return { stopReason: \"failed\" };\n } else session.write(chars);\n }\n\n for await (const chunk of session.collect(yieldMs, signal)) yield delta(chunk);\n\n if (signal?.aborted) return { stopReason: \"aborted\" };\n if (session.running) {\n return {\n stopReason: \"completed\",\n note: `[process still running with process_id ${processId}]`,\n };\n }\n // Already exited: clean up the registry and report the exit status.\n manager.remove(processId);\n if (session.error) {\n return { stopReason: \"failed\", note: `[spawn error: ${session.error.message}]` };\n }\n return resultForExit(session.exit);\n },\n };\n}\n","/**\n * SubagentSessionManager —— registry and lifecycle management for background subagent sessions.\n *\n * Constructed by Environment (one per Session), injected via services to be shared by the\n * `run_subagent` and `input_subagent` tools. Registry duties are handled by the generic\n * `BackgroundRegistry` (shared with command sessions, see `../background/registry.ts`).\n * Difference from command sessions: when at capacity, **running sessions are never evicted**\n * (discarding in-progress subagent work is unacceptable) — only completed, idle ones are\n * evicted; if there's still no room, the tool rejects spawning a new one.\n * Docs: /docs/tools § \"Background session caps\".\n */\nimport { BackgroundRegistry } from \"../background/index.js\";\nimport type { ManagedSubagentSession } from \"./session.js\";\n\n/**\n * Cap on concurrently managed background subagent sessions. This is a **spawn admission cap**,\n * not a hard limit: there's an await between the `makeRoom` check (before spawn) and `register`\n * (after the yield window ends), so parallel run_subagent calls can briefly push the registered\n * count over the cap — an already-running child session is never discarded just to hold the line.\n */\nconst MAX_SESSIONS = 8;\n\nexport class SubagentSessionManager {\n private readonly registry = new BackgroundRegistry<ManagedSubagentSession>({\n idPrefix: \"subagent\",\n maxTasks: MAX_SESSIONS,\n });\n\n /** Whether the manager has been disposed (the host Session has ended). */\n get isDisposed(): boolean {\n return this.registry.isDisposed;\n }\n\n /** Whether there's still room for a new background session (evicting a completed, idle one if needed; never evicts a running one). */\n makeRoom(): boolean {\n return this.registry.makeRoom(false);\n }\n\n /**\n * Registers a still-running session as a background session, allocating and returning a\n * unique `subagent_id`: `subagent-<last 8 hex of child Session id>` (falls back to random on\n * collision), whose suffix aligns with the message origin/frontend nesting label\n * (`agent-<last 3 chars>`) for correlation.\n */\n register(session: ManagedSubagentSession): string {\n // A full yield window has elapsed since the pre-spawn makeRoom check, so the registry may\n // have been filled by parallel calls in the meantime: free up room once more (only evicting\n // completed, idle ones); if still no room, register anyway, tolerating a brief overshoot\n // (see MAX_SESSIONS).\n this.registry.makeRoom(false);\n return this.registry.register(session, session.sessionId.slice(-8));\n }\n\n /** Looks up a session by subagent_id and refreshes its access time; returns undefined if not found. */\n get(subagentId: string): ManagedSubagentSession | undefined {\n return this.registry.get(subagentId);\n }\n\n /** Disposes: removes the fallback registration and finalizes all sessions (the process 'exit' fallback is hooked by the registry itself). Idempotent. */\n dispose(): void {\n this.registry.dispose();\n }\n}\n","/**\n * ManagedSubagentSession — a subagent session capable of running in the background.\n *\n * Holds a `SubagentHandle` and drives its `run` (pump): the same child Session may run across\n * multiple rounds (the first round is initiated by `run_subagent`, later rounds append a Prompt\n * via `input_subagent`). Structurally mirrors a command session (ManagedSession): the parent tool\n * call collects output live within the yield window; output produced outside the window (while\n * running in the background) goes into a buffer, delivered all at once on the next access.\n *\n * Three kinds of output, each with its own destination:\n * - **Message buffer**: all of the child session's OmniMessage (already tagged with origin), for\n * the parent tool call to forward to the frontend for rendering; capped in count, overflow\n * drops the oldest (only affects frontend replay — the child Session's own Trace loses no\n * data);\n * - **Text buffer**: assistant text deltas from the direct child layer (origin one hop), fed back\n * as the parent tool's own output to the LLM; capped in capacity (prevents memory bloat),\n * overflow drops the oldest with a marker;\n * - **Approval queue**: the child session's tool approval requests. While running in the\n * background, the parent session may have no active tool call to forward approval through, so\n * the request is queued and the child session blocks waiting; the parent tool call\n * (run_subagent / input_subagent) attaches an approval sink (`attachApprovalSink`) within its\n * window to consult Human one request at a time — if detached mid-consultation (window ends),\n * the request stays queued, and a late-arriving decision still takes effect (settled guard,\n * first to arrive wins).\n *\n * Cleanup: `kill()` aborts the current run via AbortSignal, denies all pending approvals, and\n * releases child Session resources; idempotent. The child Session runs in-process, so there's no\n * need for a separate synchronous hard-kill path (its command sessions are reaped by their own\n * exit fallback); `killHard` is equivalent to `kill`.\n */\nimport type { OmniMessage } from \"../../../omnimessage/index.js\";\nimport type { ApprovalDecision, ToolCallPayload } from \"../../../omnimessage/index.js\";\nimport type { ApproveFn, SubagentHandle } from \"../../../interfaces.js\";\nimport type { ToolResult } from \"../types.js\";\nimport { CappedTextBuffer, WakeSignal } from \"../background/index.js\";\n\n/** Message buffer count cap: overflow drops the oldest (only frontend replay is affected — the child Session has its own Trace). */\nconst MESSAGE_BUFFER_CAP = 4096;\n/** Text buffer capacity cap (characters): prevents a chatty child Agent from blowing up memory. */\nconst OUTPUT_BUFFER_CAP = 1024 * 1024; // 1 MiB\n\n/** Terminal state of one run. */\nexport interface SubagentExit {\n status: \"completed\" | \"failed\";\n note?: string;\n}\n\n/** A pending approval request: the settled guard makes the decision first-to-arrive-wins (late/duplicate decisions are ignored). */\ninterface PendingApproval {\n toolCall: OmniMessage<ToolCallPayload>;\n settled: boolean;\n resolve: (decision: ApprovalDecision) => void;\n}\n\nexport class ManagedSubagentSession {\n /** Timestamp of the last access (used for the eviction policy). */\n lastUsed: number = Date.now();\n\n private readonly handle: SubagentHandle;\n private readonly abortCtrl = new AbortController();\n\n private messages: OmniMessage[] = [];\n private readonly textBuffer = new CappedTextBuffer(OUTPUT_BUFFER_CAP, \"earlier subagent output\");\n\n private isRunning = false;\n private exitInfo: SubagentExit | null = null;\n private killed = false;\n\n private readonly approvals: PendingApproval[] = [];\n private sink: { approve: ApproveFn; detached: Promise<void> } | null = null;\n private sinkEpoch = 0;\n private pumpingApprovals = false;\n\n // Single wake point: new message / run finished / new approval request all wake a waiting waitWake through it.\n private readonly wakeSignal = new WakeSignal();\n\n constructor(handle: SubagentHandle) {\n this.handle = handle;\n }\n\n /** Child Session id (one hop of a message's origin); `subagent_id` is derived from its tail so the frontend can correlate it. */\n get sessionId(): string {\n return this.handle.sessionId;\n }\n\n /** Whether a round of the task is currently running. */\n get running(): boolean {\n return this.isRunning;\n }\n\n /** Terminal state of the most recent run; null if no round has ever completed. */\n get exit(): SubagentExit | null {\n return this.exitInfo;\n }\n\n /** Number of pending approval requests (the parent tool uses this to hint the model to poll again). */\n get pendingApprovals(): number {\n return this.approvals.length;\n }\n\n /** Whether there's unread output (buffered messages or text); used to re-check the predicate before waiting (see collect.ts). */\n get hasPending(): boolean {\n return this.messages.length > 0 || !this.textBuffer.isEmpty;\n }\n\n /**\n * Starts a new round of the task on the child Session (async pump, doesn't block the caller).\n * Throws if already disposed or still running (converted to an explanatory output by the\n * caller).\n */\n startRun(prompt: string): void {\n if (this.killed) throw new Error(\"subagent session disposed\");\n if (this.isRunning) throw new Error(\"subagent is still running\");\n this.isRunning = true;\n this.exitInfo = null;\n void this.pump(prompt);\n }\n\n /** Takes the buffered child-session messages (already tagged with origin, for the parent tool to forward). */\n drainMessages(): OmniMessage[] {\n if (this.messages.length === 0) return [];\n const out = this.messages;\n this.messages = [];\n return out;\n }\n\n /** Takes the currently unread child Agent text (including the drop marker); clears the buffer. */\n drainText(): string {\n return this.textBuffer.drain();\n }\n\n /** External wakeup (e.g. the parent tool call was aborted): makes a waiting `waitWake` return immediately. */\n wakeup(): void {\n this.wakeSignal.notify();\n }\n\n /** Waits for \"woken up\" or `ms` to expire, whichever comes first. */\n async waitWake(ms: number): Promise<void> {\n await this.wakeSignal.wait(ms);\n }\n\n /**\n * Attaches an approval sink: the parent tool call active within the window hands in its own\n * `ctx.approve`, and queued approval requests are consulted with Human through it one at a\n * time. Returns a detach function (called when the window ends); a later attach replaces the\n * former one.\n */\n attachApprovalSink(approve: ApproveFn): () => void {\n const epoch = ++this.sinkEpoch;\n let onDetach!: () => void;\n const detached = new Promise<void>((resolve) => {\n onDetach = resolve;\n });\n this.sink = { approve, detached };\n void this.pumpApprovals();\n return () => {\n if (this.sinkEpoch === epoch) this.sink = null;\n onDetach();\n };\n }\n\n /** Cleanup: aborts the current run, denies pending approvals, releases child Session resources; idempotent. */\n kill(): void {\n if (this.killed) return;\n this.killed = true;\n this.abortCtrl.abort();\n for (const req of [...this.approvals]) this.settle(req, \"deny\");\n // If running, released by pump's finally after it finishes; otherwise released immediately.\n if (!this.isRunning) this.handle.dispose();\n this.wakeSignal.notify();\n }\n\n /** Synchronous hard-kill path: the child Session runs in-process with no separate OS resources, so this is equivalent to `kill`. */\n killHard(): void {\n this.kill();\n }\n\n // -------------------------------------------------------------------------\n // Internal: pump and buffering\n // -------------------------------------------------------------------------\n\n /** Drives one round of `handle.run`: buffers messages and text, settling the terminal state when it ends. */\n private async pump(prompt: string): Promise<void> {\n let wroteAny = false;\n let childAbort: string | null = null;\n try {\n for await (const msg of this.handle.run({\n prompt,\n signal: this.abortCtrl.signal,\n approve: this.childApprove,\n })) {\n this.bufferMessage(msg);\n if ((msg.origin?.length ?? 0) === 1) {\n const p = msg.payload as {\n type?: string;\n event_type?: string;\n text?: string;\n reason?: string;\n };\n // A direct child layer's abort event: the child session was interrupted/failed (LLM\n // request error, user interruption, etc). A child session failure doesn't throw, it\n // only emits an event, based on which this round is reported as failed rather than\n // marked completed.\n if (p.type === \"abort\") {\n childAbort = p.reason ?? \"aborted\";\n } else if (\n p.type === \"partial_text\" &&\n p.event_type === \"delta\" &&\n typeof p.text === \"string\" &&\n p.text\n ) {\n wroteAny = true;\n this.appendText(p.text);\n }\n }\n this.wakeSignal.notify();\n }\n if (childAbort !== null) {\n this.exitInfo = { status: \"failed\", note: `[subagent aborted: ${childAbort}]` };\n } else if (!wroteAny) {\n this.exitInfo = {\n status: \"completed\",\n note: \"[subagent finished without a text answer]\",\n };\n } else {\n this.exitInfo = { status: \"completed\" };\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n this.exitInfo = { status: \"failed\", note: `[subagent error: ${message}]` };\n } finally {\n this.isRunning = false;\n if (this.killed) this.handle.dispose();\n this.wakeSignal.notify();\n }\n }\n\n private bufferMessage(msg: OmniMessage): void {\n this.messages.push(msg);\n // Overflow drops the oldest: only affects frontend replay — the child Session's Trace and text buffer are unaffected.\n if (this.messages.length > MESSAGE_BUFFER_CAP) this.messages.shift();\n }\n\n private appendText(text: string): void {\n this.textBuffer.append(text);\n }\n\n // -------------------------------------------------------------------------\n // Internal: approval queue\n // -------------------------------------------------------------------------\n\n /** Approval callback handed to the child Session: the request is queued and waits for some parent tool call to consult Human and give a decision. */\n private readonly childApprove: ApproveFn = (toolCall) => {\n if (this.killed) return Promise.resolve(\"deny\");\n return new Promise<ApprovalDecision>((resolve) => {\n this.approvals.push({ toolCall, settled: false, resolve });\n this.wakeSignal.notify(); // Wake the parent tool call waiting within the window, so it can consult as soon as possible\n void this.pumpApprovals();\n });\n };\n\n /** Settles an approval decision: first to arrive wins, late/duplicate decisions are ignored. */\n private settle(req: PendingApproval, decision: ApprovalDecision): void {\n if (req.settled) return;\n req.settled = true;\n const idx = this.approvals.indexOf(req);\n if (idx >= 0) this.approvals.splice(idx, 1);\n req.resolve(decision);\n this.wakeSignal.notify();\n }\n\n /**\n * Hands the request at the head of the queue to the currently attached approval sink, one at a\n * time. Stops when the window ends (the sink is detached); unresolved requests stay queued for\n * the next sink; if a consultation already in flight resolves late, the decision still takes\n * effect via settle.\n */\n private async pumpApprovals(): Promise<void> {\n if (this.pumpingApprovals) return;\n this.pumpingApprovals = true;\n try {\n while (this.sink && this.approvals.length > 0) {\n const sink = this.sink;\n const req = this.approvals[0]!;\n const answer = sink.approve(req.toolCall).then(\n (d) => this.settle(req, d),\n () => this.settle(req, \"deny\"), // An approval sink error is treated as a denial (avoids leaving the child session stuck forever)\n );\n await Promise.race([answer, sink.detached]);\n if (req.settled) continue;\n if (this.sink && this.sink !== sink) continue; // The sink was replaced by a new call: retry with the new sink\n break; // The sink was detached and still unresolved: stay queued for the next sink\n }\n } finally {\n this.pumpingApprovals = false;\n }\n }\n}\n\n/** Converts a run's terminal state into a tool result (note is appended outside the truncation, so it isn't lost with long output). */\nexport function resultForSubagentExit(exit: SubagentExit | null): ToolResult {\n if (!exit) return { stopReason: \"completed\" };\n return { stopReason: exit.status, ...(exit.note !== undefined ? { note: exit.note } : {}) };\n}\n","/**\n * Default yield duration for background subagent sessions.\n *\n * See `../background/limits.ts` for the clamping logic: only a lower bound is set, and the\n * upper bound is derived from the tool's own `timeoutMs`.\n */\n\n/** Default wait duration (ms) for `run_subagent` launching a task and `input_subagent` appending a Prompt to continue. */\nexport const DEFAULT_SUBAGENT_YIELD_MS = 300_000;\n/** Default wait duration (ms) for `input_subagent` empty polling. */\nexport const DEFAULT_SUBAGENT_POLL_YIELD_MS = 10_000;\n","/**\n * collectWindow —— yield-window collector shared by run_subagent / input_subagent.\n *\n * Within the `yieldMs` window, emits child-session output in real time: buffered child-session\n * messages (already origin-tagged, passed through to the frontend) and subagent text deltas\n * (fed back to the LLM as this parent tool's own output delta). The window also hooks up an\n * approval outlet, forwarding the child session's queued approval requests one by one to the\n * Human via `approve`. The window ends on \"run finished / signal abort / deadline reached\", and\n * does a final drain right before ending (to catch the tail buffer at the moment the run\n * finishes). Deciding the end state and finalizing are the caller's responsibility.\n */\nimport { partialToolCallOutput } from \"../../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../../omnimessage/index.js\";\nimport type { ApproveFn } from \"../../../interfaces.js\";\nimport type { ManagedSubagentSession } from \"./session.js\";\n\nexport async function* collectWindow(\n session: ManagedSubagentSession,\n opts: { yieldMs: number; toolCallId: string; signal?: AbortSignal; approve?: ApproveFn },\n): AsyncGenerator<OmniMessage> {\n const { yieldMs, toolCallId, signal, approve } = opts;\n const delta = (output: string): OmniMessage =>\n partialToolCallOutput({ eventType: \"delta\", output, toolCallId });\n const detach = approve ? session.attachApprovalSink(approve) : null;\n // abort only ends this window (whether to kill the child session is up to the caller);\n // wakes up a pending waitWake so it returns immediately.\n const onAbort = (): void => session.wakeup();\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n const start = Date.now();\n for (;;) {\n for (const m of session.drainMessages()) yield m;\n const text = session.drainText();\n if (text) yield delta(text);\n if (!session.running) break;\n if (signal?.aborted) break;\n const remaining = yieldMs - (Date.now() - start);\n if (remaining <= 0) break;\n // Re-check the predicate before sleeping: output arriving while `yield` is suspended\n // would fire its wakeup before this wait even starts, and get missed otherwise.\n if (session.hasPending) continue;\n await session.waitWake(remaining);\n }\n // Final drain: there may still be a tail buffer right when the run finishes/yields.\n for (const m of session.drainMessages()) yield m;\n const tail = session.drainText();\n if (tail) yield delta(tail);\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n detach?.();\n }\n}\n","/**\n * run_subagent — delegates a subtask to a child Agent, supporting a switch to long-running\n * background execution.\n *\n * The tool itself doesn't depend on Agent/Session, only holding an injected `SubagentRunner`\n * (breaking the circular dependency). The model may freely choose the child Agent (`agent_id`)\n * and model (`model_id`) via arguments; if omitted, it falls back to reusing the current Agent\n * and the Project's default Model respectively. The spawned child session is managed by\n * `ManagedSubagentSession` (sharing the `SubagentSessionManager` injected by Environment with\n * `input_subagent`).\n *\n * The two-phase semantics mirror `exec_command`: within the `yield_time_ms` window, child-session\n * messages are forwarded live (tagged with origin, so the frontend can see the child Agent's tool\n * calls and token usage) and the child Agent's text deltas are copied as this tool's output; if\n * the child Agent finishes within the window, its terminal state is returned and the child\n * session is released; if it's still running once the window expires, it's registered as a\n * background session, returning `subagent_id` for subsequent access the same way as\n * `input_command` (polling / appending a Prompt, see input-subagent.ts).\n *\n * Approval: `run_subagent` itself is a read-write tool (`rw`), so its invocation requires Human\n * approval; the child session's tool approval requests are forwarded to the same Human within\n * the window via the session's approval queue (tagged with origin), and queued for the next\n * access while running in the background. An interruption within the startup window kills the\n * child session per exec_command semantics; precheck errors such as exceeding the depth limit or\n * a nonexistent agent are expressed by the runner as a throw, and collapsed to failed.\n * Docs: /docs/tools § \"Subagents\".\n */\nimport { partialToolCallOutput } from \"../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../omnimessage/index.js\";\nimport type { EnvironmentServices, ToolDefinitionConfig } from \"../../interfaces.js\";\nimport type { BuiltinTool, ToolExecutionContext, ToolResult } from \"./types.js\";\nimport {\n DEFAULT_SUBAGENT_YIELD_MS,\n ManagedSubagentSession,\n resultForSubagentExit,\n} from \"./subagent/index.js\";\nimport { collectWindow } from \"./subagent/collect.js\";\nimport { clampYield } from \"./background/index.js\";\n\n/** Tool name constant (used only within this tool module, never exposed to Environment). */\nexport const SUBAGENT_NAME = \"run_subagent\";\n\n/** Pending-approval hint: lets the model know it should poll again to move the child Agent forward. */\nexport function approvalHint(session: ManagedSubagentSession): string {\n const n = session.pendingApprovals;\n return n > 0 ? ` [subagent is waiting for approval of ${n} tool call(s); poll to review]` : \"\";\n}\n\n/** Builds run_subagent's BuiltinTool from tool config + injected services. */\nexport function createSubagentTool(\n definition: ToolDefinitionConfig,\n services?: EnvironmentServices,\n): BuiltinTool {\n const runner = services?.subagentRunner;\n const manager = services?.subagentSessions;\n return {\n name: SUBAGENT_NAME,\n definition,\n async *execute(\n args: Record<string, unknown>,\n ctx: ToolExecutionContext,\n ): AsyncGenerator<OmniMessage, ToolResult | void> {\n const { toolCallId, signal, approve } = ctx;\n const fail = function* (msg: string): Generator<OmniMessage> {\n yield partialToolCallOutput({ eventType: \"delta\", output: msg, toolCallId });\n };\n\n // Missing arguments / unconfigured services both collapse to an explanatory output rather\n // than throwing (consistent with other tools).\n if (!runner) {\n yield* fail(\"[run_subagent unavailable: no subagent runner configured]\");\n return { stopReason: \"failed\" };\n }\n if (!manager || manager.isDisposed) {\n yield* fail(\"[run_subagent unavailable: no subagent session manager available]\");\n return { stopReason: \"failed\" };\n }\n const prompt = typeof args.prompt === \"string\" ? args.prompt : \"\";\n if (prompt.trim().length === 0) {\n yield* fail(\"[run_subagent error: missing required string argument `prompt`]\");\n return { stopReason: \"failed\" };\n }\n const agentId = typeof args.agent_id === \"string\" ? args.agent_id : undefined;\n const modelId = typeof args.model_id === \"string\" ? args.model_id : undefined;\n const yieldMs = clampYield(\n args.yield_time_ms,\n DEFAULT_SUBAGENT_YIELD_MS,\n definition.timeoutMs,\n );\n\n if (signal?.aborted) return { stopReason: \"aborted\" };\n\n // Concurrency cap (running child Agents are never evicted): reject spawning if there's no\n // room.\n if (!manager.makeRoom()) {\n yield* fail(\n \"[run_subagent error: too many background subagents; poll or finish existing ones first]\",\n );\n return { stopReason: \"failed\" };\n }\n\n // Spawn the child Session (precheck errors such as exceeding the depth limit or a\n // nonexistent agent are expressed as a throw).\n let session: ManagedSubagentSession;\n try {\n const handle = await runner.spawn({\n ...(agentId !== undefined ? { agentId } : {}),\n ...(modelId !== undefined ? { modelId } : {}),\n });\n session = new ManagedSubagentSession(handle);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n yield* fail(`[run_subagent error: ${message}]`);\n return { stopReason: \"failed\" };\n }\n\n // An interruption within the startup window kills the child session (consistent with\n // exec_command); once switched to background, this listener is removed in `finally`.\n const onAbort = (): void => session.kill();\n let registered = false;\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n try {\n session.startRun(prompt);\n yield* collectWindow(session, {\n yieldMs,\n toolCallId,\n ...(signal ? { signal } : {}),\n ...(approve ? { approve } : {}),\n });\n\n if (signal?.aborted) return { stopReason: \"aborted\" };\n if (session.running) {\n // Still running once the window expires: register as a background session, returning\n // subagent_id for input_subagent to continue accessing it.\n const id = manager.register(session);\n registered = true;\n return {\n stopReason: \"completed\",\n note:\n `[subagent running with subagent_id ${id}; use input_subagent to poll for progress ` +\n `or send a follow-up prompt]` +\n approvalHint(session),\n };\n }\n // Finished within the window: report the terminal state; releasing the child session is\n // handled uniformly in `finally` (never registered, so no subagent_id).\n return resultForSubagentExit(session.exit);\n } finally {\n signal?.removeEventListener(\"abort\", onAbort);\n if (!registered) session.kill();\n }\n },\n };\n}\n","/**\n * input_subagent —— accesses a subagent session that `run_subagent` moved to the background\n * (BuiltinTool).\n *\n * Finds the session by `subagent_id`: when `prompt` is empty, nothing is written — it just\n * polls (collecting subagent messages and text deltas buffered during the background period,\n * or waiting for the run to end); when non-empty and the subagent is idle, it's fed in as a new\n * user message to continue on the same child Session (long-running subagent, multi-turn\n * conversation); when non-empty but the subagent is still running, it errors, suggesting to\n * poll first. Within the window, queued approval requests from the child session are also\n * passed through (see subagent/session.ts).\n *\n * Difference from `input_command`: once a round of work finishes, the session is **not\n * removed** (kept to receive a follow-up prompt) — it's only released when the parent Session\n * ends, or evicted as an idle session once concurrency is full. Interruption only aborts this\n * particular access, **it never kills the child session** (the subagent was launched\n * independently earlier; the user interrupting one poll shouldn't kill it along the way).\n * Docs: /docs/tools § \"Subagents\".\n */\nimport { partialToolCallOutput } from \"../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../omnimessage/index.js\";\nimport type { EnvironmentServices, ToolDefinitionConfig } from \"../../interfaces.js\";\nimport type { BuiltinTool, ToolExecutionContext, ToolResult } from \"./types.js\";\nimport {\n DEFAULT_SUBAGENT_POLL_YIELD_MS,\n DEFAULT_SUBAGENT_YIELD_MS,\n resultForSubagentExit,\n} from \"./subagent/index.js\";\nimport { approvalHint } from \"./run-subagent.js\";\nimport { collectWindow } from \"./subagent/collect.js\";\nimport { clampYield } from \"./background/index.js\";\n\n/** Tool name constant. */\nexport const INPUT_SUBAGENT_NAME = \"input_subagent\";\n\nexport function createInputSubagentTool(\n definition: ToolDefinitionConfig,\n services?: EnvironmentServices,\n): BuiltinTool {\n const manager = services?.subagentSessions;\n return {\n name: INPUT_SUBAGENT_NAME,\n definition,\n async *execute(\n args: Record<string, unknown>,\n ctx: ToolExecutionContext,\n ): AsyncGenerator<OmniMessage, ToolResult | void> {\n const { toolCallId, signal, approve } = ctx;\n const delta = (output: string): OmniMessage =>\n partialToolCallOutput({ eventType: \"delta\", output, toolCallId });\n\n if (!manager) {\n yield delta(\"[input_subagent unavailable: no subagent session manager configured]\");\n return { stopReason: \"failed\" };\n }\n\n const subagentId = args[\"subagent_id\"];\n if (typeof subagentId !== \"string\" || subagentId.length === 0) {\n yield delta('Missing required argument \"subagent_id\" for input_subagent.');\n return { stopReason: \"failed\" };\n }\n const session = manager.get(subagentId);\n if (!session) {\n yield delta(\n `[input_subagent error: unknown subagent_id ${subagentId} ` +\n `(the session may have finished and been cleared)]`,\n );\n return { stopReason: \"failed\" };\n }\n\n const prompt = typeof args[\"prompt\"] === \"string\" ? (args[\"prompt\"] as string) : \"\";\n const empty = prompt.trim().length === 0;\n const yieldMs = clampYield(\n args[\"yield_time_ms\"],\n empty ? DEFAULT_SUBAGENT_POLL_YIELD_MS : DEFAULT_SUBAGENT_YIELD_MS,\n definition.timeoutMs,\n );\n\n if (signal?.aborted) return { stopReason: \"aborted\" };\n\n // Continue with a follow-up prompt (empty prompt just polls). New input is not accepted\n // while running: poll first to collect progress.\n if (!empty) {\n if (session.running) {\n yield delta(\n `[input_subagent error: subagent ${subagentId} is still running; ` +\n `poll with an empty prompt to collect progress first]`,\n );\n return { stopReason: \"failed\" };\n }\n // startRun expresses edge cases like already-disposed via throw, collapsed here into failed (the tool never throws outward).\n try {\n session.startRun(prompt);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n yield delta(`[input_subagent error: ${message}]`);\n return { stopReason: \"failed\" };\n }\n }\n\n yield* collectWindow(session, {\n yieldMs,\n toolCallId,\n ...(signal ? { signal } : {}),\n ...(approve ? { approve } : {}),\n });\n\n // Interruption only aborts this access, it doesn't kill the child session.\n if (signal?.aborted) return { stopReason: \"aborted\" };\n if (session.running) {\n return {\n stopReason: \"completed\",\n note: `[subagent still running with subagent_id ${subagentId}]` + approvalHint(session),\n };\n }\n // This round of work has ended: report the end state; the session is kept (can be resumed), not removed from the registry.\n const result = resultForSubagentExit(session.exit);\n const idleHint = `[subagent idle with subagent_id ${subagentId}; send a follow-up prompt to continue]`;\n return {\n ...result,\n note: result.note !== undefined ? `${result.note} ${idleHint}` : idleHint,\n };\n },\n };\n}\n","/**\n * read_image — image-reading tool, a builtin tool implementation (BuiltinTool).\n *\n * Reads an image and feeds it back to the model as **image content**: if `source` is an http(s)\n * URL, downloads it with the global fetch (respecting the abort signal); otherwise reads it as a\n * local file path (relative paths are resolved against the Workspace). Only png/jpeg/gif/webp\n * are allowed (determined in order by response header / magic number / extension); errors out\n * above 5MB.\n *\n * Division of responsibility with Environment (see environment.ts): on success, yields a brief\n * descriptive delta (e.g. `image/png, 123.4 kB`), while the image itself is carried via the\n * return value `ToolResult.images` (a data URL) for Environment to attach when closing out (a\n * single streaming delta carries it all at once before stop, plus the final complete\n * `tool_call_output`); on failure, yields explanatory text and closes with `failed`, **never\n * throwing**; if interrupted, only reports `aborted` — the interruption note is appended by\n * Environment.\n *\n * This tool is only used by sessions with a model that supports images (config entry\n * `forModel: \"vision\"`); text-only models use describe_image instead (the image is handed to a\n * configured vision model to describe, returning text — see describe-image.ts), and the image\n * loading/validation logic is shared via `loadImage`.\n * Docs: /docs/tools § \"Image tools\".\n */\nimport path from \"node:path\";\nimport { readFile, stat } from \"node:fs/promises\";\nimport { partialToolCallOutput } from \"../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../omnimessage/index.js\";\nimport type { ToolDefinitionConfig } from \"../../interfaces.js\";\nimport type { BuiltinTool, ToolExecutionContext, ToolResult } from \"./types.js\";\n\n/** Tool name constant (used only within this tool module, never exposed to Environment). */\nexport const READ_IMAGE_NAME = \"read_image\";\n\n/**\n * Image size upper bound (bytes): errors out above this. Taken as the common denominator of\n * per-provider single-image hard limits (Claude API is around 5MB, some compatible endpoints are\n * lower) — since local validation passing but the next request getting a blanket 400 from the\n * provider is a non-retryable path, the limit must not exceed the strictest downstream; this also\n * avoids oversized images blowing up the context and Trace.\n */\nexport const MAX_IMAGE_BYTES = 5 * 1024 * 1024;\n\n/** Supported image mime types (the four generally accepted across providers). */\nconst SUPPORTED_MIMES = new Set([\"image/png\", \"image/jpeg\", \"image/gif\", \"image/webp\"]);\n\n/** Extension -> mime (fallback when magic-number sniffing fails). */\nconst EXT_TO_MIME: Record<string, string> = {\n \".png\": \"image/png\",\n \".jpg\": \"image/jpeg\",\n \".jpeg\": \"image/jpeg\",\n \".gif\": \"image/gif\",\n \".webp\": \"image/webp\",\n};\n\n/** Sniffs the mime type from the file header's magic number; returns null if unrecognized. */\nfunction sniffMime(buf: Buffer): string | null {\n if (buf.length >= 8 && buf.readUInt32BE(0) === 0x89504e47) return \"image/png\";\n if (buf.length >= 3 && buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff) {\n return \"image/jpeg\";\n }\n if (buf.length >= 6) {\n const head = buf.subarray(0, 6).toString(\"latin1\");\n if (head === \"GIF87a\" || head === \"GIF89a\") return \"image/gif\";\n }\n if (\n buf.length >= 12 &&\n buf.subarray(0, 4).toString(\"latin1\") === \"RIFF\" &&\n buf.subarray(8, 12).toString(\"latin1\") === \"WEBP\"\n ) {\n return \"image/webp\";\n }\n return null;\n}\n\n/** Infers the mime type from a path / URL pathname's extension; returns null if it can't be inferred. */\nfunction mimeFromExt(p: string): string | null {\n return EXT_TO_MIME[path.extname(p).toLowerCase()] ?? null;\n}\n\n/** Byte count -> human-readable size (B / kB / MB, one decimal place). */\nexport function formatSize(bytes: number): string {\n if (bytes < 1024) return `${bytes} B`;\n const kb = bytes / 1024;\n if (kb < 1024) return `${kb.toFixed(1)} kB`;\n return `${(kb / 1024).toFixed(1)} MB`;\n}\n\nconst OVERSIZE_MESSAGE = (size: number): string =>\n `Image too large: ${formatSize(size)} exceeds the ${formatSize(MAX_IMAGE_BYTES)} limit.`;\n\nconst UNSUPPORTED_MESSAGE = (detected: string | null): string =>\n `Unsupported image type${detected ? ` \"${detected}\"` : \"\"}: only png, jpeg, gif and webp are supported.`;\n\n/** Result of `loadImage`: success (bytes + mime) / interrupted / failed (explanatory message). */\nexport type LoadImageResult =\n | { ok: true; bytes: Buffer; mime: string }\n | { ok: false; reason: \"aborted\" }\n | { ok: false; reason: \"failed\"; message: string };\n\n/**\n * Reads and validates an image (shared by read_image and describe_image):\n * an http(s) URL is downloaded with the global fetch, otherwise read as a local path (resolved\n * against Workspace); validates the size upper bound and mime type (determined in order by\n * response header / magic number / extension). Never throws.\n */\nexport async function loadImage(\n source: string,\n workspaceDir: string,\n signal?: AbortSignal,\n): Promise<LoadImageResult> {\n if (signal?.aborted) return { ok: false, reason: \"aborted\" };\n\n let bytes: Buffer;\n let mime: string | null;\n if (/^https?:\\/\\//i.test(source)) {\n // URL branch: downloads via the global fetch (abort signal passed through to the request);\n // mime is preferentially taken from the response header, falling back to magic number / URL\n // extension.\n let res: Response;\n try {\n res = await fetch(source, signal ? { signal } : {});\n } catch (err) {\n if (signal?.aborted) return { ok: false, reason: \"aborted\" };\n const message = err instanceof Error ? err.message : String(err);\n return {\n ok: false,\n reason: \"failed\",\n message: `Failed to download image \"${source}\": ${message}`,\n };\n }\n if (!res.ok) {\n return {\n ok: false,\n reason: \"failed\",\n message: `Failed to download image \"${source}\": HTTP ${res.status}`,\n };\n }\n // When content-length is trustworthy, reject an oversized response early to avoid reading it\n // into memory for nothing.\n const declared = Number(res.headers.get(\"content-length\") ?? \"\");\n if (Number.isFinite(declared) && declared > MAX_IMAGE_BYTES) {\n return { ok: false, reason: \"failed\", message: OVERSIZE_MESSAGE(declared) };\n }\n try {\n bytes = Buffer.from(await res.arrayBuffer());\n } catch (err) {\n if (signal?.aborted) return { ok: false, reason: \"aborted\" };\n const message = err instanceof Error ? err.message : String(err);\n return {\n ok: false,\n reason: \"failed\",\n message: `Failed to download image \"${source}\": ${message}`,\n };\n }\n const headerMime = (res.headers.get(\"content-type\") ?? \"\").split(\";\")[0]!.trim().toLowerCase();\n let urlExtMime: string | null = null;\n try {\n urlExtMime = mimeFromExt(new URL(source).pathname);\n } catch {\n urlExtMime = null; // A URL parse failure only affects the extension fallback\n }\n mime = SUPPORTED_MIMES.has(headerMime) ? headerMime : (sniffMime(bytes) ?? urlExtMime);\n if (mime === null && headerMime) mime = headerMime; // Include the real response type in the error\n } else {\n // Local-path branch: relative paths are resolved against Workspace; stat first to check the\n // size before reading, to avoid reading an oversized file into memory in one go.\n const filePath = path.resolve(workspaceDir, source);\n try {\n const st = await stat(filePath);\n // Explicitly reject non-file paths such as directories: readFile's EISDIR error isn't\n // model-friendly.\n if (!st.isFile()) {\n return {\n ok: false,\n reason: \"failed\",\n message: `Failed to read image \"${source}\": path is not a file.`,\n };\n }\n if (st.size > MAX_IMAGE_BYTES) {\n return { ok: false, reason: \"failed\", message: OVERSIZE_MESSAGE(st.size) };\n }\n bytes = await readFile(filePath);\n } catch (err) {\n if (signal?.aborted) return { ok: false, reason: \"aborted\" };\n const message = err instanceof Error ? err.message : String(err);\n return {\n ok: false,\n reason: \"failed\",\n message: `Failed to read image \"${source}\": ${message}`,\n };\n }\n mime = sniffMime(bytes) ?? mimeFromExt(filePath);\n }\n\n if (signal?.aborted) return { ok: false, reason: \"aborted\" };\n // Empty file/response: magic-number sniffing fails to identify it, but the extension fallback\n // may still let it through — an empty base64 sent to the provider is guaranteed to error, so\n // reject it here.\n if (bytes.length === 0) {\n return { ok: false, reason: \"failed\", message: `Image \"${source}\" is empty.` };\n }\n if (bytes.length > MAX_IMAGE_BYTES) {\n return { ok: false, reason: \"failed\", message: OVERSIZE_MESSAGE(bytes.length) };\n }\n if (mime === null || !SUPPORTED_MIMES.has(mime)) {\n return { ok: false, reason: \"failed\", message: UNSUPPORTED_MESSAGE(mime) };\n }\n return { ok: true, bytes, mime };\n}\n\n/**\n * read_image builtin tool: reads a local file or downloads a URL, validates its type and size,\n * then outputs a data URL image. `definition` is overridden by Environment at construction time\n * with the same-named entry from ToolConfig (description/arguments/permissions/limits).\n */\nexport function createReadImageTool(definition: ToolDefinitionConfig): BuiltinTool {\n return {\n name: READ_IMAGE_NAME,\n definition,\n async *execute(\n args: Record<string, unknown>,\n ctx: ToolExecutionContext,\n ): AsyncGenerator<OmniMessage, ToolResult | void> {\n const { toolCallId, signal } = ctx;\n const delta = (output: string): OmniMessage =>\n partialToolCallOutput({ eventType: \"delta\", output, toolCallId });\n\n const source = args[\"source\"];\n if (typeof source !== \"string\" || source.length === 0) {\n yield delta('Missing required argument \"source\" for read_image.');\n return { stopReason: \"failed\" };\n }\n\n const res = await loadImage(source, ctx.workspaceDir, signal);\n if (!res.ok) {\n if (res.reason === \"aborted\") return { stopReason: \"aborted\" };\n yield delta(res.message);\n return { stopReason: \"failed\" };\n }\n\n // Success: yield a brief one-line description as a text delta (both in the streaming and\n // complete message), while the image itself is carried via the return value for\n // Environment to attach.\n yield delta(`${res.mime}, ${formatSize(res.bytes.length)}`);\n return { images: [`data:${res.mime};base64,${res.bytes.toString(\"base64\")}`] };\n },\n };\n}\n","/**\n * describe_image —— image-proxy-read tool, the text-only-model variant of read_image\n * (`forModel: \"text-only\"`, see default-config.ts): the image itself is never fed back into\n * the session model (some providers flatly 400 on a tool_result carrying an image); instead it\n * is sent, together with the caller-supplied `prompt`, in a single one-off request to the\n * Project-configured vision model (`vision_model`), and the vision model's text answer is\n * returned as the tool's output.\n *\n * The tool definition (description/parameters, including `prompt`) comes entirely from the\n * config entry; this implementation does no runtime rewriting — which tool is used for which\n * model class is declared by the entry's `forModel` annotation, so the config file is what you get.\n *\n * Behavioral contract (shared with read_image): `source` supports http(s) URLs and local paths;\n * validation/size limits are reused from `loadImage`; on failure, outputs explanatory text and\n * finishes with `failed`, never throws; on interruption, only reports `aborted`. Messages from\n * the internal one-off request never enter the parent session stream (no origin, not leaked out).\n * Docs: /docs/tools § \"Image tools\".\n */\nimport { imageUrlMessage, partialToolCallOutput, userText } from \"../../omnimessage/index.js\";\nimport type { OmniMessage } from \"../../omnimessage/index.js\";\nimport type { LLMOutcome, ToolDefinitionConfig, VisionDescriberService } from \"../../interfaces.js\";\nimport type { BuiltinTool, ToolExecutionContext, ToolResult } from \"./types.js\";\nimport { formatSize, loadImage } from \"./read-image.js\";\n\n/** Tool name constant (used only inside this tool module, not exposed to Environment). */\nexport const DESCRIBE_IMAGE_NAME = \"describe_image\";\n\n/** Default question used when the caller doesn't supply a prompt. */\nconst DEFAULT_PROMPT =\n \"Describe this image in detail, including any visible text, numbers, UI elements and layout.\";\n\n/** Constructs the describe_image tool: definition (description/parameters) is taken as-is from the config entry. */\nexport function createDescribeImageTool(\n definition: ToolDefinitionConfig,\n describer: VisionDescriberService,\n): BuiltinTool {\n return {\n name: DESCRIBE_IMAGE_NAME,\n definition,\n async *execute(\n args: Record<string, unknown>,\n ctx: ToolExecutionContext,\n ): AsyncGenerator<OmniMessage, ToolResult | void> {\n const { toolCallId, signal } = ctx;\n const delta = (output: string): OmniMessage =>\n partialToolCallOutput({ eventType: \"delta\", output, toolCallId });\n\n const source = args[\"source\"];\n if (typeof source !== \"string\" || source.length === 0) {\n yield delta('Missing required argument \"source\" for describe_image.');\n return { stopReason: \"failed\" };\n }\n if (describer.modelId === null || describer.createLLM === undefined) {\n yield delta(\n \"No vision model is configured for this project. The current model does not accept \" +\n \"images; ask the user to pick a vision model in the model settings (vision_model) \" +\n \"to enable image reading.\",\n );\n return { stopReason: \"failed\" };\n }\n\n const res = await loadImage(source, ctx.workspaceDir, signal);\n if (!res.ok) {\n if (res.reason === \"aborted\") return { stopReason: \"aborted\" };\n yield delta(res.message);\n return { stopReason: \"failed\" };\n }\n\n const prompt =\n typeof args[\"prompt\"] === \"string\" && args[\"prompt\"].trim().length > 0\n ? args[\"prompt\"]\n : DEFAULT_PROMPT;\n const dataUrl = `data:${res.mime};base64,${res.bytes.toString(\"base64\")}`;\n\n // One-off vision model request: prompt + image are merged into a single user message;\n // its text deltas (partial_text delta) are forwarded in real time as this tool's own\n // output delta — the description streams out piece by piece, not buffered as a whole.\n // Partial concatenation == the full message (see generative-model.ts), so the full text\n // is not forwarded again; other messages like thinking/token_usage are ignored, never\n // leaked into the parent session.\n const llm = describer.createLLM();\n const gen = llm.streamGenerate({\n newMessages: [userText(prompt), imageUrlMessage(dataUrl)],\n ...(signal ? { signal } : {}),\n });\n yield delta(\n `${res.mime}, ${formatSize(res.bytes.length)} — described by ${describer.modelId}:\\n`,\n );\n let streamedAny = false;\n let outcome: LLMOutcome | undefined;\n for (;;) {\n const step = await gen.next();\n if (step.done) {\n outcome = step.value;\n break;\n }\n const p = step.value.payload as { type?: string; event_type?: string; text?: string };\n if (p.type === \"partial_text\" && p.event_type === \"delta\" && p.text) {\n streamedAny = true;\n yield delta(p.text);\n }\n }\n if (signal?.aborted) return { stopReason: \"aborted\" };\n if (!outcome || outcome.status !== \"completed\") {\n const detail =\n outcome && \"message\" in outcome && outcome.message ? `: ${outcome.message}` : \"\";\n yield delta(\n `${streamedAny ? \"\\n\" : \"\"}Vision model (${describer.modelId}) request ${outcome?.status ?? \"failed\"}${detail}`,\n );\n return { stopReason: \"failed\" };\n }\n if (!streamedAny) yield delta(\"[vision model returned no text]\");\n },\n };\n}\n","/**\n * Built-in tool registry —— maps tool names to BuiltinTool factories.\n *\n * Environment uses this table to assemble entries from ToolConfig into BuiltinTool instances:\n * a tool is only assembled if its name is in the table (i.e. a supported built-in tool); the\n * description/parameters/permission/maxOutputLength from config are injected into the tool's\n * `definition` by each factory.\n * When adding a new built-in tool, just register one factory entry here — no changes to\n * Environment needed.\n *\n * Docs: packages/docs/content/tools.{zh,en}.md (site path /docs/tools) documents every\n * built-in tool and the approval flow — keep the page in sync when this table changes.\n */\nimport type { EnvironmentServices, ToolDefinitionConfig } from \"../../interfaces.js\";\nimport type { BuiltinTool } from \"./types.js\";\nimport { EXEC_COMMAND_NAME, createExecCommandTool } from \"./exec-command.js\";\nimport { INPUT_COMMAND_NAME, createInputCommandTool } from \"./input-command.js\";\nimport { SUBAGENT_NAME, createSubagentTool } from \"./run-subagent.js\";\nimport { INPUT_SUBAGENT_NAME, createInputSubagentTool } from \"./input-subagent.js\";\nimport { READ_IMAGE_NAME, createReadImageTool } from \"./read-image.js\";\nimport { DESCRIBE_IMAGE_NAME, createDescribeImageTool } from \"./describe-image.js\";\n\n/**\n * A factory that constructs a BuiltinTool instance from a tool config entry; optionally\n * receives runtime services injected by Environment.\n * Most tools ignore `services`; only a few (e.g. `run_subagent`) use it.\n */\nexport type BuiltinToolFactory = (\n definition: ToolDefinitionConfig,\n services?: EnvironmentServices,\n) => BuiltinTool;\n\n/** Tool name -> factory. */\nexport const BUILTIN_TOOL_FACTORIES: Record<string, BuiltinToolFactory> = {\n [EXEC_COMMAND_NAME]: createExecCommandTool,\n [INPUT_COMMAND_NAME]: createInputCommandTool,\n [SUBAGENT_NAME]: createSubagentTool,\n [INPUT_SUBAGENT_NAME]: createInputSubagentTool,\n [READ_IMAGE_NAME]: createReadImageTool,\n // describe_image: the text-only-model variant of read_image (hands the image to the\n // configured vision model for description, returns text).\n // Which tool is used for which model class is declared by the config entry's forModel\n // annotation; before assembly, selectBuiltinToolsForModel has already filtered out entries\n // that don't apply to the session's model.\n [DESCRIBE_IMAGE_NAME]: (definition, services) =>\n createDescribeImageTool(definition, services?.visionDescriber ?? { modelId: null }),\n};\n","/**\n * Environment —— executes approved tool calls inside the Workspace.\n *\n * Environment has no knowledge of any specific tool: it only assembles the tool names supported\n * by ToolConfig into BuiltinTool instances (see `environment/tools/`), and dispatches execution\n * by looking up the tool name. Adding a new built-in tool only requires implementing BuiltinTool\n * and registering it — no changes to this file needed. Tool call **rendering** is not core's\n * concern; it's handled by the CLI / Web frontend.\n *\n * The **framing and finalization** of the tool stream is handled uniformly by Environment:\n * - Entering execution immediately emits `start`; the tool only needs to yield output deltas\n * (its own start/stop are ignored);\n * - Output is truncated online **front-to-back** by maxOutputLength (head is kept, forwarding\n * stops once exceeded); the truncation marker, the tool's self-reported end marker\n * (`ToolResult.note`, e.g. exit code — appended outside the truncation, never lost even when\n * long output is truncated), and timeout/interruption/error markers are all emitted as part\n * of the stream — **the content produced by concatenating streamed chunks matches the full\n * message exactly**;\n * - Nested session messages carrying an origin marker (e.g. forwarded from run_subagent) pass\n * through unchanged, taking no part in this tool's output or finalization;\n * - Argument parsing failures, unknown tool names, tool throws, and other exceptions all\n * collapse into an explanatory, complete `tool_call_output` — never throws — and **output is\n * never empty under any circumstance**.\n * Docs: /docs/tools § \"Execution contract\".\n */\nimport { partialToolCallOutput, toolCallOutput } from \"../omnimessage/index.js\";\nimport type { OmniMessage, StopReason } from \"../omnimessage/index.js\";\nimport type {\n EnvironmentConfig,\n EnvironmentInterface,\n ToolConfig,\n ToolDefinition,\n ToolExecutionRequest,\n ToolPermission,\n} from \"../interfaces.js\";\nimport type { BuiltinTool, ToolResult } from \"./tools/types.js\";\nimport { BUILTIN_TOOL_FACTORIES } from \"./tools/registry.js\";\nimport { CommandSessionManager } from \"./tools/command/index.js\";\nimport { SubagentSessionManager } from \"./tools/subagent/index.js\";\n\n/** Default cap on tool output truncation (characters). */\nconst DEFAULT_MAX_OUTPUT_LENGTH = 16000;\n\n/** Default timeout cap for a single tool call (milliseconds); <=0 disables it (every tool must be bound by timeoutMs). */\nconst DEFAULT_TOOL_TIMEOUT_MS = 120000;\n\n/** Marker appended to the result when a tool is interrupted by the user. */\nconst TOOL_ABORTED_NOTE = \"[interrupted: tool aborted by user]\";\n\n/** Placeholder marker used when a tool produces no output at all (tool_call_output content is never empty). */\nconst TOOL_EMPTY_NOTE = \"[no output]\";\n\n/**\n * Explanation for a failed argument JSON parse. The normal pipeline never reaches this: bad\n * JSON already throws during AgentHub's parsing stage, and the LLM layer finalizes it as\n * malformed for the engine to reconnect (see generative-model.ts) — it's never dispatched into\n * Environment as a completed tool_call. This function is only a defensive fallback for the\n * public interface.\n */\nfunction describeArgumentsError(name: string, raw: string, err: unknown): string {\n const detail = err instanceof Error ? err.message : String(err);\n if (raw.trim() === \"\") {\n return `Tool call \"${name}\" failed: the arguments field is empty. Re-issue the call with a complete JSON object.`;\n }\n return `Tool call \"${name}\" failed: the arguments are not valid JSON (${detail}). Re-issue the call with one complete, valid JSON object.`;\n}\n\n/** Appends a marker after existing content: newline-joins if content is non-empty, otherwise just returns the marker. */\nfunction appendNote(base: string, note: string): string {\n return base ? `${base}\\n${note}` : note;\n}\n\n/** The delta needed to stream out `note` on top of existing content `base` (includes separator, same basis as appendNote). */\nfunction noteSuffix(base: string, note: string): string {\n return base ? `\\n${note}` : note;\n}\n\nexport class Environment implements EnvironmentInterface {\n private readonly workspaceDir: string;\n private readonly toolConfig: ToolConfig;\n /** Assembled built-in tools: tool name -> BuiltinTool. Only tools supported by the registry and present in config. */\n private readonly tools: Map<string, BuiltinTool>;\n /** Long-running command session registry: constructed within this Environment and shared between exec_command / input_command. */\n private readonly commandSessions: CommandSessionManager;\n /** Background subagent session registry: constructed within this Environment and shared between run_subagent / input_subagent. */\n private readonly subagentSessions: SubagentSessionManager;\n\n constructor(config: EnvironmentConfig) {\n this.workspaceDir = config.workspaceDir;\n this.toolConfig = config.toolConfig;\n this.tools = new Map();\n // The background session registry is created alongside Environment (one per Session) and\n // injected into whichever tools need it; all sessions are finalized together on dispose.\n // The vault environment variables are injected into child processes by the command session\n // registry at spawn time.\n this.commandSessions = new CommandSessionManager(\n config.vault !== undefined ? { vault: config.vault } : {},\n );\n this.subagentSessions = new SubagentSessionManager();\n const services = {\n ...config.services,\n commandSessions: this.commandSessions,\n subagentSessions: this.subagentSessions,\n };\n // Assemble the tools supported by config into BuiltinTool instances; unrecognized tool\n // names are skipped (neither exposed to the LLM nor executable).\n for (const def of config.toolConfig.customTools) {\n const factory = BUILTIN_TOOL_FACTORIES[def.name];\n if (factory) this.tools.set(def.name, factory(def, services));\n }\n }\n\n /** Releases runtime resources held by Environment: finalizes all managed background sessions (command and subagent). Idempotent. */\n dispose(): void {\n this.commandSessions.dispose();\n this.subagentSessions.dispose();\n }\n\n /**\n * Lists tools available to the current Session, for context_engine to initialize GenerativeModel.\n * Only lists tools that have been assembled (i.e. supported by the registry) — tool names\n * unrecognized in config are not exposed to the LLM (consistent with the constructor);\n * the definition (description/parameters) treats **the config entry as the single source of\n * truth** — factories must not rewrite the definition at runtime; where a differentiated\n * implementation is needed, use a separate explicit tool-name entry with a `forModel`\n * annotation (e.g. read_image / describe_image).\n * Only exposes `{name, description, parameters}`, dropping permission/maxOutputLength.\n * MCP Server config flows into Environment via toolConfig; enumerating concrete MCP tools\n * is left to a later adapter layer.\n */\n async listTools(): Promise<ToolDefinition[]> {\n return this.toolConfig.customTools\n .filter((tool) => this.tools.has(tool.name))\n .map((tool) => ({\n name: tool.name,\n description: tool.description,\n ...(tool.parameters !== undefined ? { parameters: tool.parameters } : {}),\n }));\n }\n\n /** Looks up a tool's permission level (for the frontend's permission-mode decisions); returns undefined for an unknown tool. */\n toolPermission(name: string): ToolPermission | undefined {\n return this.toolConfig.customTools.find((t) => t.name === name)?.permission;\n }\n\n /**\n * Executes an approved tool call, streaming `partial_tool_call_output` and a final\n * `tool_call_output`; nested messages carrying origin pass through unchanged. Dispatches by\n * looking up the tool name; any exception collapses into an explanatory output — never throws.\n *\n * The priority for deciding stop_reason is: user interruption > timeout > tool throw > tool\n * self-report. Interruption is determined by the `signal` held by Environment, and is\n * compatible with both a tool self-reporting aborted and an AbortError raised by the\n * interruption. An internal abort raised by a timeout does not count as a user interruption —\n * it's finalized as failed, with the timeout reason written into the output.\n * Docs: /docs/tools § \"Execution contract\".\n */\n async *executeTool(request: ToolExecutionRequest): AsyncGenerator<OmniMessage> {\n const payload = request.toolCall.payload;\n // tool_call_id is passed through unchanged, so context_engine and the LLM can associate the\n // request with its result.\n const toolCallId = payload.tool_call_id;\n const name = payload.name;\n\n // Every path is framed uniformly by Environment: entering execution emits start; the end\n // uniformly emits stop + the full message.\n yield partialToolCallOutput({ eventType: \"start\", toolCallId });\n\n const tool = this.tools.get(name);\n if (!tool) {\n yield* emitFailure(toolCallId, `Unknown tool: ${name}`);\n return;\n }\n\n // Parse the tool's argument JSON; a parse failure collapses into an explanatory output\n // (also streamed, so the frontend can render it).\n let parsed: unknown;\n try {\n parsed = JSON.parse(payload.arguments);\n } catch (err) {\n yield* emitFailure(toolCallId, describeArgumentsError(name, payload.arguments, err));\n return;\n }\n\n const args =\n parsed !== null && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : {};\n\n const maxOutputLength = tool.definition.maxOutputLength ?? DEFAULT_MAX_OUTPUT_LENGTH;\n const timeoutMs = tool.definition.timeoutMs ?? DEFAULT_TOOL_TIMEOUT_MS;\n const signal = request.signal;\n\n // User interruption and tool timeout are merged into a single internal signal handed to the\n // tool: either one triggers abortion of execution.\n // The timeout constraint is enforced uniformly by Environment for all tools; the\n // tool only needs to respond to signal.\n const ac = new AbortController();\n if (signal?.aborted) ac.abort();\n const onAbort = (): void => ac.abort();\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n let timedOut = false;\n const timer =\n timeoutMs > 0\n ? setTimeout(() => {\n timedOut = true;\n ac.abort();\n }, timeoutMs)\n : null;\n timer?.unref?.();\n\n // Consume the tool stream: content deltas are forwarded after online front-truncation;\n // nested messages pass through; manual iteration to capture the generator's return value.\n let streamed = \"\"; // Content forwarded so far (<= maxOutputLength)\n let contentLen = 0; // Total length of content produced by the tool (including truncated/discarded parts)\n let toolOutput: string | null = null; // Fallback: content basis when the tool produces a full message itself\n let selfReported: StopReason | undefined; // Tool's self-reported stop reason (return value takes priority over the full message)\n let selfNote: string | null = null; // Tool's self-reported end marker (e.g. exit code), appended outside truncation\n let selfImages: string[] | undefined; // Tool's self-reported images (data URL), carried via a single streamed delta and the full message\n let thrown: unknown = null;\n const gen = tool.execute(args, {\n workspaceDir: this.workspaceDir,\n toolCallId,\n signal: ac.signal,\n // Pass through the parent's approve callback (run_subagent uses it so the child Session\n // inherits the parent's approval mode; other tools ignore it).\n ...(request.approve ? { approve: request.approve } : {}),\n });\n try {\n for (;;) {\n const res = await gen.next();\n if (res.done) {\n const result: ToolResult | void = res.value;\n if (result?.stopReason) selfReported = result.stopReason;\n if (result?.note) selfNote = result.note;\n if (result?.images && result.images.length > 0) selfImages = result.images;\n break;\n }\n const out = res.value;\n if (out.origin && out.origin.length > 0) {\n yield out; // Nested session message: pass through unchanged, not part of this tool's output/finalization\n continue;\n }\n const p = out.payload as {\n type?: string;\n event_type?: string;\n stop_reason?: string;\n output?: string;\n };\n if (p.type === \"partial_tool_call_output\") {\n // Only takes delta content; start/stop are ignored (framing is uniformly handled by Environment).\n if (p.event_type !== \"delta\" || !p.output) continue;\n contentLen += p.output.length;\n // maxOutputLength <= 0 means truncation is disabled (same semantics as timeoutMs).\n const room =\n maxOutputLength > 0 ? maxOutputLength - streamed.length : Number.POSITIVE_INFINITY;\n if (room > 0) {\n const chunk = p.output.length > room ? p.output.slice(0, room) : p.output;\n streamed += chunk;\n // Rebuild the delta: tool_call_id is uniformly enforced by Environment, never trusting the tool's own value.\n yield partialToolCallOutput({\n eventType: \"delta\",\n output: chunk,\n toolCallId,\n });\n }\n } else if (p.type === \"tool_call_output\") {\n // Fallback: if the tool still produces a full message, use it as the basis for content and stop reason (not needed under the new contract).\n toolOutput = p.output ?? \"\";\n if (selfReported === undefined && p.stop_reason) {\n selfReported = p.stop_reason as StopReason;\n }\n } else {\n // Other message types without origin: protocol misuse, ignore and warn (keep the parent stream clean).\n process.stderr.write(\n `[penguin] tool \"${name}\" yielded unexpected message type \"${p.type}\"; ignored.\\n`,\n );\n }\n }\n } catch (err) {\n // A tool throw also collapses into the uniform finalization: keep already-streamed content, don't discard produced output.\n thrown = err;\n } finally {\n if (timer) clearTimeout(timer);\n signal?.removeEventListener(\"abort\", onAbort);\n }\n\n // Uniform finalization. Content basis = the tool's self-produced full message (fallback\n // path) or the already-forwarded delta; after front-truncating to the cap, the truncation\n // marker and interruption/timeout/error markers are appended in turn, all made up via\n // streamed deltas — streamed concatenation == the full message.\n const contentBase = toolOutput ?? streamed;\n const capped =\n maxOutputLength > 0 && contentBase.length > maxOutputLength\n ? contentBase.slice(0, maxOutputLength)\n : contentBase;\n const truncated = capped.length < contentBase.length || contentLen > streamed.length;\n\n const aborted =\n signal?.aborted === true ||\n (!timedOut &&\n (selfReported === \"aborted\" ||\n (thrown as { name?: string } | null)?.name === \"AbortError\"));\n let stopReason: StopReason;\n const notes: string[] = [];\n if (truncated) {\n notes.push(`[output truncated: exceeded ${maxOutputLength} chars]`);\n }\n // The tool's self-reported end marker (e.g. exit code): appended outside the truncation —\n // if treated as a content delta it would get cut off once long output hits the cap, and the\n // model would misread a command that failed after printing lots of output as successful.\n if (selfNote) {\n notes.push(selfNote);\n }\n if (aborted) {\n stopReason = \"aborted\";\n notes.push(TOOL_ABORTED_NOTE);\n } else if (timedOut) {\n stopReason = \"failed\";\n notes.push(`[tool timeout: exceeded ${timeoutMs}ms]`);\n } else if (thrown != null) {\n stopReason = \"failed\";\n notes.push(`[tool error] ${thrown instanceof Error ? thrown.message : String(thrown)}`);\n } else {\n stopReason = selfReported ?? \"completed\";\n }\n // The tool's reply must never be empty: an empty tool_result leaves the model unable to\n // tell \"silent success\" apart from \"call failed\", and some Providers outright reject empty\n // content blocks.\n if (capped === \"\" && notes.length === 0) {\n notes.push(TOOL_EMPTY_NOTE);\n }\n const noteText = notes.join(\"\\n\");\n const fullOutput = noteText ? appendNote(capped, noteText) : capped;\n\n // Compensating content delta: if nothing was streamed, emit the whole thing at once; on the\n // fallback path, emit the portion of the full message beyond the already-streamed prefix\n // (if the tool is internally inconsistent, the full message wins — no further reconciliation).\n let compensation = \"\";\n if (streamed === \"\") compensation = capped;\n else if (toolOutput !== null && capped.startsWith(streamed)) {\n compensation = capped.slice(streamed.length);\n }\n if (compensation) {\n yield partialToolCallOutput({\n eventType: \"delta\",\n output: compensation,\n toolCallId,\n });\n }\n if (noteText) {\n yield partialToolCallOutput({\n eventType: \"delta\",\n output: noteSuffix(capped, noteText),\n toolCallId,\n });\n }\n // Images are made up via streaming: images are not delta'd — a single delta carries them\n // all at once right before stop, and the full message carries them again — satisfying\n // \"streamed concatenation == full message\" the same way text does (truncation only applies\n // to text, never touches images).\n // Only carried on normal completion; interruption/timeout/error paths carry no images, to keep finalization simple.\n const images = stopReason === \"completed\" ? selfImages : undefined;\n if (images) {\n yield partialToolCallOutput({ eventType: \"delta\", toolCallId, images });\n }\n yield partialToolCallOutput({ eventType: \"stop\", toolCallId, stopReason });\n yield toolCallOutput({\n output: fullOutput,\n toolCallId,\n stopReason,\n ...(images ? { images } : {}),\n });\n }\n}\n\n/** Upfront failure (unknown tool/argument parse failure): delta(explanation) -> stop -> full failed output (start already emitted by the caller). */\nfunction* emitFailure(toolCallId: string, message: string): Generator<OmniMessage> {\n yield partialToolCallOutput({ eventType: \"delta\", output: message, toolCallId });\n yield partialToolCallOutput({ eventType: \"stop\", toolCallId, stopReason: \"failed\" });\n yield toolCallOutput({ output: message, toolCallId, stopReason: \"failed\" });\n}\n","/**\n * Trace writer — append-only JSON Lines.\n *\n * Docs: packages/docs/content/sessions-and-traces.{zh,en}.md (site path\n * /docs/sessions-and-traces) documents the file layout and recording rules.\n *\n * Design points:\n * - Every observable action is appended to Trace; historical events are never modified in place\n * (append-only).\n * - One Trace file corresponds to one complete model context; when the context is compacted\n * and a new segment is produced, `rotate()` starts a new, separately numbered file.\n * - Only \"recordable\" messages are written: `session_meta`, complete `model_msg`, and all\n * `event_msg`; streaming `partial_*` messages are skipped (the producer appends the\n * corresponding complete message once the segment ends); nested child-session messages are\n * never written (their spawn location is recorded via the `subagent` pointer event written by\n * context_engine).\n * - Path convention: `<tracesDir>/<yyyy-mm-dd>/<sessionId>_<index3>.jsonl`.\n */\nimport { appendFile, mkdir, readFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport {\n PartialAggregator,\n isCompleteModelMessage,\n isEventMessage,\n isSessionMeta,\n} from \"../omnimessage/index.js\";\nimport type { OmniMessage } from \"../omnimessage/index.js\";\nimport { formatLocalDate } from \"../internal/dates.js\";\n\nexport interface WriterOptions {\n /** Trace root directory, typically `<agent>/traces`. */\n tracesDir: string;\n /** Current Session id, written into the file name. */\n sessionId: string;\n /** The time used to derive the date subdirectory; defaults to `new Date()`. */\n date?: Date;\n /**\n * Directly specifies the date subdirectory name (used when Session resumption continues\n * writing to the original file: the Trace file follows the context, not the date); takes\n * priority over `date`.\n */\n dateDir?: string;\n /** Starting Trace index (used when Session resumption continues the original index); defaults to 1. */\n startIndex?: number;\n}\n\n/** Zero-pads a Trace index to 3 digits, e.g. 1 -> \"001\". */\nfunction formatIndex(index: number): string {\n return index.toString().padStart(3, \"0\");\n}\n\n/**\n * Determines whether an OmniMessage should be written to Trace (skips streaming partial_* and nested child-session messages).\n *\n * Child-session messages are never written to this Trace: the child Session has its own complete\n * Trace, and recording it again would distort this Trace's statistics. The spawn location is\n * recorded via the `subagent` pointer event (recording only the child Session id) that\n * context_engine writes at the spawn site; when the session is reopened, the server uses this to\n * re-attach the child session to its corresponding run_subagent tool card.\n * Docs: /docs/sessions-and-traces § \"Trace design\".\n */\nfunction isRecordable(msg: OmniMessage): boolean {\n if (msg.origin && msg.origin.length > 0) return false;\n return isCompleteModelMessage(msg) || isEventMessage(msg) || isSessionMeta(msg);\n}\n\n/**\n * append-only JSONL Trace writer.\n *\n * Single-writer scenario (MVP): concurrency safety isn't required, but every write uses\n * `appendFile` (O_APPEND) rather than caching a file handle and seeking to write, avoiding\n * overwriting existing content; this also removes the need for an explicit close.\n */\nexport class Writer {\n private readonly tracesDir: string;\n private readonly sessionId: string;\n private readonly dateDir: string;\n /** Current Trace index, starting at 1; incremented by `rotate()`. */\n private index = 1;\n /** Set true once the date directory has been created for the current file, to avoid a redundant mkdir. */\n private ensuredDirForIndex = -1;\n\n constructor(opts: WriterOptions) {\n this.tracesDir = opts.tracesDir;\n this.sessionId = opts.sessionId;\n this.dateDir = opts.dateDir ?? formatLocalDate(opts.date ?? new Date());\n this.index = opts.startIndex ?? 1;\n }\n\n /** Absolute path of the current Trace file. */\n currentPath(): string {\n const fileName = `${this.sessionId}_${formatIndex(this.index)}.jsonl`;\n return join(this.tracesDir, this.dateDir, fileName);\n }\n\n /**\n * Appends one message. Only written if it's a recordable message; streaming `partial_*` is\n * skipped. `mkdir -p`s the date directory on the first write to the current file.\n */\n async write(msg: OmniMessage): Promise<void> {\n if (!isRecordable(msg)) return;\n const path = this.currentPath();\n if (this.ensuredDirForIndex !== this.index) {\n await mkdir(dirname(path), { recursive: true });\n this.ensuredDirForIndex = this.index;\n }\n await appendFile(path, `${JSON.stringify(msg)}\\n`, \"utf8\");\n }\n\n /** Writes multiple messages in sequence. */\n async writeAll(msgs: OmniMessage[]): Promise<void> {\n for (const msg of msgs) {\n await this.write(msg);\n }\n }\n\n /**\n * Aggregates a message stream mixed with streaming `partial_*` into complete messages first,\n * then writes them per the `write` convention. A convenience helper: `write` skips partial_*\n * by default (the producer will already append the complete message), so this method is only\n * needed when reconstructing a complete context from raw streaming fragments.\n */\n async aggregateAndWrite(msgs: OmniMessage[]): Promise<void> {\n const agg = new PartialAggregator();\n for (const msg of msgs) {\n await this.writeAll(agg.push(msg));\n }\n await this.writeAll(agg.flush());\n }\n\n /**\n * Starts a new Trace file: increments the index, so the next `write` goes to the new file.\n * Used to split into a separate file when the context is compacted and a new context segment is produced.\n * Docs: /docs/sessions-and-traces § \"Trace design\".\n */\n async rotate(): Promise<void> {\n this.index += 1;\n }\n}\n\n/** Parses a Trace file line by line (ignoring blank lines), for testing and later reads. */\nexport async function readTrace(path: string): Promise<OmniMessage[]> {\n const content = await readFile(path, \"utf8\");\n return content\n .split(\"\\n\")\n .filter((line) => line.trim().length > 0)\n .map((line) => JSON.parse(line) as OmniMessage);\n}\n","/** Local-timezone date formatting (internal shared helper, not exported via the barrel). */\n\n/** Format a date as local `yyyy-mm-dd` (local timezone, 4-digit year, zero-padded 2-digit month/day). */\nexport function formatLocalDate(date: Date): string {\n const year = date.getFullYear().toString().padStart(4, \"0\");\n const month = (date.getMonth() + 1).toString().padStart(2, \"0\");\n const day = date.getDate().toString().padStart(2, \"0\");\n return `${year}-${month}-${day}`;\n}\n","/**\n * Trace replay — the core of Session resume.\n *\n * Replay produces two results: the **history** injected via setHistory (committed turns only),\n * and the **carry-over** input resent with the first `run` after resume. Resume is **best-effort**:\n * Trace only records real messages, so synthesized carry-over (`<turn_aborted>` flattening, pairing\n * placeholders) is never written to Trace — replay reconstructs from the original messages\n * (unanswered input is resent as-is, pairing placeholders are resynthesized as needed). History is\n * guaranteed to be **structurally valid** (turns complete, tool_call pairs matched), not a\n * byte-for-byte match of what AgentHub actually received; incomplete model output (thinking/text)\n * is allowed to be lost.\n *\n * Messages are attributed to a Request by **position**, not by content inspection:\n * - Input = user-side messages accumulated after the previous `request` `stop` (the first\n * Request is `session_meta`) and before this `start` (messages are written to Trace before\n * being sent with the request); user-side messages that land between `start` and `stop`\n * (output from parallel tools completing during the request) count toward the **next** turn's\n * input.\n * - Output = assistant messages between this `start` and `stop`.\n *\n * Determination order: first check file-level compaction closure; then evaluate turn by turn\n * (completed turns go to history, others are dropped wholesale while keeping outputs paired with\n * already-committed tool_calls); finally, the remaining input is the carry-over, with pairing\n * backfill applied.\n * Docs: /docs/sessions-and-traces § \"Session recovery\".\n */\nimport { readdir, readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport {\n emptyTokenCounts,\n isCompleteModelMessage,\n isEventMessage,\n isSessionMeta,\n toolCallOutput,\n userText,\n} from \"../omnimessage/index.js\";\nimport type {\n CompactionEndPayload,\n CompleteModelMessage,\n OmniMessage,\n RequestBeginPayload,\n RequestEndPayload,\n SessionMetaMessage,\n TokenCounts,\n TokenUsagePayload,\n ToolCallPayload,\n} from \"../omnimessage/index.js\";\nimport { extractSummary } from \"../engine/context-engine.js\";\n\n/** Replay result: all the state needed to resume a Session. */\nexport interface ResumeResult {\n /** Committed history (complete model_msg, in order), injected in one shot via setHistory; empty on compaction closure. */\n history: CompleteModelMessage[];\n /**\n * Pending input (carry-over): resent alongside new input with the first `run` after resume.\n * Already includes pairing-backfill placeholders — placeholders exist only in memory\n * (synthesized carry-over is never written to Trace) and are resynthesized on each resume.\n */\n carryOver: OmniMessage[];\n /** Compaction closure (file-level): this file's context is fully closed; resume starts a new, empty context. */\n contextClosed: boolean;\n /** Compaction closure in summarize mode: the reconstructed `<context_summary>` summary, prepended to the next run's input. */\n pendingSummary?: OmniMessage;\n /** Session-level cumulative Token carry-over (the session value from the last token_usage). */\n sessionTokens: TokenCounts;\n /** The request.total from the last token_usage (context usage figure). */\n lastRequestTotal: number;\n /** Session cumulative turn count carry-over (count of completed requests). */\n sessionTurns: number;\n /** Rendering view: this context's complete model_msg plus key event_msg entries (including interrupted turns and their markers); empty on compaction closure. */\n renderMessages: OmniMessage[];\n /** The file's first session_meta; null if missing (unresumable — the caller reports the error). */\n meta: SessionMetaMessage | null;\n}\n\n/** Content of the pairing-backfill placeholder output (the tool hadn't finished and no output was persisted before the process exited). */\nconst PROCESS_EXIT_PLACEHOLDER = \"[interrupted: process exited before the tool finished]\";\n\n/**\n * Parse Trace JSONL content. Tolerates a **truncated last line** left behind by an abnormal\n * process exit (that line is ignored); corruption in the middle is outside the crash window\n * (append-only, single writer), so it throws loudly.\n */\nexport function parseTraceLines(content: string): OmniMessage[] {\n const lines = content.split(\"\\n\");\n const out: OmniMessage[] = [];\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i]!.trim();\n if (!line) continue;\n try {\n out.push(JSON.parse(line) as OmniMessage);\n } catch (err) {\n const isLastNonEmpty = lines.slice(i + 1).every((l) => l.trim().length === 0);\n if (isLastNonEmpty) break;\n throw err;\n }\n }\n return out;\n}\n\n/** Read and parse a Trace file (tolerates a truncated last line). */\nexport async function readTraceTolerant(path: string): Promise<OmniMessage[]> {\n return parseTraceLines(await readFile(path, \"utf8\"));\n}\n\n/** A located Trace file: its path, containing date-directory name, and index. */\nexport interface LocatedTraceFile {\n path: string;\n dateDir: string;\n index: number;\n}\n\nconst TRACE_FILE_RE = /^(.+)_(\\d{3})\\.jsonl$/;\n\n/**\n * Locate the **highest-index** Trace file for a Session (one Trace file corresponds to one\n * complete model context). Scans `<tracesDir>/<yyyy-mm-dd>/<sessionId>_<index3>.jsonl`; returns\n * null if no match is found.\n */\nexport async function findLatestTraceFile(\n tracesDir: string,\n sessionId: string,\n): Promise<LocatedTraceFile | null> {\n let best: LocatedTraceFile | null = null;\n for (const dateDir of await listDirs(tracesDir)) {\n for (const file of await listFiles(join(tracesDir, dateDir))) {\n const match = TRACE_FILE_RE.exec(file);\n if (!match || match[1] !== sessionId) continue;\n const index = Number(match[2]);\n if (!best || index > best.index) {\n best = { path: join(tracesDir, dateDir, file), dateDir, index };\n }\n }\n }\n return best;\n}\n\n/**\n * The id of the most recent Session under this Agent, determined by the timestamp embedded in\n * session_id (ids are zero-padded, so lexical order equals chronological order). Returns null if\n * there are no Sessions.\n */\nexport async function latestSessionId(tracesDir: string): Promise<string | null> {\n const dateDirs = (await listDirs(tracesDir)).sort((a, b) => b.localeCompare(a));\n for (const dateDir of dateDirs) {\n const files = (await listFiles(join(tracesDir, dateDir))).sort((a, b) => b.localeCompare(a));\n for (const file of files) {\n const match = TRACE_FILE_RE.exec(file);\n if (!match) continue;\n if (await hasResumableTraceContent(join(tracesDir, dateDir, file))) {\n return match[1]!;\n }\n }\n }\n return null;\n}\n\nasync function hasResumableTraceContent(file: string): Promise<boolean> {\n try {\n const messages = await readTraceTolerant(file);\n return messages.some((msg) => !isSessionMeta(msg));\n } catch {\n return false;\n }\n}\n\nasync function listDirs(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isDirectory()).map((e) => e.name);\n } catch {\n return []; // traces directory doesn't exist yet: no Sessions\n }\n}\n\nasync function listFiles(dir: string): Promise<string[]> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n return entries.filter((e) => e.isFile()).map((e) => e.name);\n } catch {\n return [];\n }\n}\n\nfunction isRequestBegin(msg: OmniMessage): msg is OmniMessage<RequestBeginPayload> {\n return isEventMessage(msg) && (msg.payload as { type?: string }).type === \"request_begin\";\n}\n\nfunction isRequestEnd(msg: OmniMessage): msg is OmniMessage<RequestEndPayload> {\n return isEventMessage(msg) && (msg.payload as { type?: string }).type === \"request_end\";\n}\n\nfunction isCompactionEnd(msg: OmniMessage): msg is OmniMessage<CompactionEndPayload> {\n return isEventMessage(msg) && (msg.payload as { type?: string }).type === \"compaction_end\";\n}\n\nfunction toolCallOutputId(msg: OmniMessage): string | null {\n const p = msg.payload as { type?: string; tool_call_id?: string };\n return p.type === \"tool_call_output\" ? (p.tool_call_id ?? null) : null;\n}\n\n/**\n * Replay a Trace file (the current context), reconstructing history and carry-over input.\n * Input is the message sequence parsed by `readTraceTolerant`.\n */\nexport function resumeTrace(messages: OmniMessage[]): ResumeResult {\n const meta = (messages.find(isSessionMeta) as SessionMetaMessage | undefined) ?? null;\n const sessionTokens = lastSessionTokens(messages);\n const lastRequestTotal = lastRequestTotalOf(messages);\n\n // —— First check the file-level case: compaction closure (the file ends with a completed\n // compaction stop and no new file was opened, i.e. it's still the latest index at resume time)\n // — this file's context is fully closed, so the whole file is not replayed.\n const last = messages[messages.length - 1];\n if (last && isCompactionEnd(last)) {\n const p = last.payload;\n if (p.status === \"completed\") {\n const result: ResumeResult = {\n history: [],\n carryOver: [],\n contextClosed: true,\n sessionTokens,\n lastRequestTotal: 0, // new context has no usage yet\n sessionTurns: 0, // turn count resets after compaction completes\n renderMessages: [],\n meta,\n };\n if (p.mode === \"summarize\") {\n // Reconstruct the summary from the compaction request's output (the assistant text of\n // the last completed Request).\n const summaryText = lastCompletedRequestText(messages);\n result.pendingSummary = userText(\n `<context_summary>\\n${extractSummary(summaryText)}\\n</context_summary>`,\n );\n }\n return result;\n }\n }\n\n // —— Turn-by-turn determination + pending-input convergence.\n const history: CompleteModelMessage[] = [];\n /** Pending-input buffer: user-side messages not yet sent with any committed Request. */\n let pending: OmniMessage[] = [];\n /** The current Request's input snapshot (frozen at begin) and its outputs. */\n let snapshot: OmniMessage[] = [];\n let outputs: CompleteModelMessage[] = [];\n let inRequest = false;\n /** Whether we're between a matched pair of compaction events: the compaction prompt in this\n * span is not conversational input and must not be resent as-is if uncommitted. */\n let inCompaction = false;\n /** Ids of tool_calls that are committed (in history) and ids of outputs that are paired (in\n * history input). */\n const committedCallIds = new Set<string>();\n const answeredIds = new Set<string>();\n let sessionTurns = 0;\n const renderMessages: OmniMessage[] = [];\n\n const placeholderFor = (id: string): CompleteModelMessage =>\n toolCallOutput({\n output: PROCESS_EXIT_PLACEHOLDER,\n toolCallId: id,\n stopReason: \"aborted\",\n }) as CompleteModelMessage;\n\n const dropUncommittedRound = (): void => {\n // Uncommitted turn: the whole turn is excluded from history. Its **original input** (user\n // text/images and structured tool output) goes back into the pending buffer as-is — best\n // effort to resend \"the last input that got no response\"; incomplete model output\n // (thinking/text) is allowed to be lost.\n // Exception: a failed compaction turn's compaction prompt is not conversational input and is\n // not reclaimed (structured output is still reclaimed, subject to eligibility filtering).\n const keep = inCompaction ? snapshot.filter((m) => toolCallOutputId(m) !== null) : snapshot;\n pending = [...keep, ...pending];\n snapshot = [];\n outputs = [];\n inRequest = false;\n };\n\n for (const msg of messages) {\n if (isSessionMeta(msg)) continue;\n\n if (isRequestBegin(msg)) {\n // Defensive: the previous turn had begin but no end (shouldn't happen mid-file — a process\n // exit only affects the tail) — treat it as uncommitted.\n if (inRequest) dropUncommittedRound();\n inRequest = true;\n snapshot = pending;\n pending = [];\n continue;\n }\n if (isRequestEnd(msg)) {\n if (msg.payload.status === \"completed\") {\n // Structural eligibility filter (same rule as the final carry-over): a tool_call_output\n // in the snapshot is kept only if it pairs with a tool_call that is **committed and not\n // yet answered** — tool output dispatched by a dropped turn gets persisted in the next\n // turn's input span, but its tool_call isn't in history, so keeping it as-is would create\n // an orphan tool_result with no preceding tool_use, which every request would be rejected\n // for by the provider after resume (\"Replay Rules\"' strict pairing guarantee).\n const eligible = snapshot.filter((m) => {\n const id = toolCallOutputId(m);\n return id === null || (committedCallIds.has(id) && !answeredIds.has(id));\n });\n // Structural repair (best-effort): a tool_call committed earlier but still unpaired — its\n // matching output was once sent as synthesized carry-over but **never written to Trace**\n // — resynthesize a placeholder before this turn's input, to keep the injected history\n // structurally valid (every assistant tool_use is followed by a user tool_result).\n const snapshotOutputIds = new Set(\n eligible.map(toolCallOutputId).filter((id): id is string => id !== null),\n );\n for (const id of committedCallIds) {\n if (answeredIds.has(id) || snapshotOutputIds.has(id)) continue;\n history.push(placeholderFor(id));\n answeredIds.add(id);\n }\n history.push(...(eligible as CompleteModelMessage[]), ...outputs);\n for (const id of snapshotOutputIds) answeredIds.add(id);\n for (const m of outputs) {\n const p = m.payload as Partial<ToolCallPayload>;\n if (p.type === \"tool_call\" && p.stop_reason === \"completed\" && p.tool_call_id) {\n committedCallIds.add(p.tool_call_id);\n }\n }\n sessionTurns += 1;\n snapshot = [];\n outputs = [];\n inRequest = false;\n } else {\n dropUncommittedRound();\n }\n continue;\n }\n\n if (isCompleteModelMessage(msg)) {\n renderMessages.push(msg);\n const role = (msg.payload as { role?: string }).role;\n if (role === \"user\") {\n // All user-side messages go into the pending buffer: ones between begin/end (output from\n // parallel tools completing during the request) count toward the next turn's input.\n pending.push(msg);\n } else if (inRequest) {\n outputs.push(msg);\n }\n // Defensive: assistant messages outside a span (shouldn't happen) are excluded from\n // history, kept only for rendering.\n continue;\n }\n if (isEventMessage(msg)) {\n const t = (msg.payload as { type?: string }).type;\n if (t === \"compaction_begin\") inCompaction = true;\n else if (t === \"compaction_end\") inCompaction = false;\n else if (t === \"abort\" && (msg.origin?.length ?? 0) === 0) renderMessages.push(msg);\n // Other events (token_usage / approval_decision, etc.) don't participate in turn\n // determination.\n }\n }\n\n // File ends mid-request (begin but no end — the process exited during a request): treat as an\n // uncommitted turn.\n if (inRequest) dropUncommittedRound();\n\n // —— Structured resend eligibility filter: a tool_call_output in the pending input is kept\n // only if it pairs with a tool_call that is **committed and not yet answered**. Tool output\n // dispatched by an uncommitted turn itself (which may be persisted before or after that turn's\n // end — the tool and LLM streams run concurrently, so finishing order is unpredictable) is\n // dropped entirely: its tool_call isn't in history, so a structured resend would form an orphan\n // tool_result with no preceding tool_use, which the provider would reject.\n pending = pending.filter((m) => {\n const id = toolCallOutputId(m);\n return id === null || (committedCallIds.has(id) && !answeredIds.has(id));\n });\n\n // —— Pairing backfill: for any tool_call committed in history with no matching output in\n // either history or pending input, add an interrupted-state placeholder to pending input.\n // Placeholders exist only in memory (synthesized carry-over is never written to Trace) and are\n // resynthesized on each resume as needed.\n const pairedIds = new Set<string>(answeredIds);\n for (const m of pending) {\n const id = toolCallOutputId(m);\n if (id !== null) pairedIds.add(id);\n }\n const pairingBackfill: OmniMessage[] = [];\n for (const id of committedCallIds) {\n if (pairedIds.has(id)) continue;\n pairingBackfill.push(placeholderFor(id));\n }\n\n return {\n history,\n carryOver: [...pending, ...pairingBackfill],\n contextClosed: false,\n sessionTokens,\n lastRequestTotal,\n sessionTurns,\n renderMessages,\n meta,\n };\n}\n\n/** The session cumulative total from the last token_usage (zero if none). */\nfunction lastSessionTokens(messages: OmniMessage[]): TokenCounts {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (!isEventMessage(msg)) continue;\n const p = msg.payload as Partial<TokenUsagePayload>;\n if (p.type === \"token_usage\" && p.session) return p.session;\n }\n return emptyTokenCounts();\n}\n\n/** The request.total from the last token_usage (context usage figure; 0 if none). */\nfunction lastRequestTotalOf(messages: OmniMessage[]): number {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i]!;\n if (!isEventMessage(msg)) continue;\n const p = msg.payload as Partial<TokenUsagePayload>;\n if (p.type === \"token_usage\" && p.request) return p.request.total;\n }\n return 0;\n}\n\n/** Concatenated assistant text of the last completed Request (on compaction closure, this is the compaction request's output). */\nfunction lastCompletedRequestText(messages: OmniMessage[]): string {\n let text = \"\";\n let current = \"\";\n let inRequest = false;\n for (const msg of messages) {\n if (isRequestBegin(msg)) {\n inRequest = true;\n current = \"\";\n continue;\n }\n if (isRequestEnd(msg)) {\n {\n // A completed request with empty text still overwrites (we take the text of “the last\n // completed request”, even if empty) — otherwise a textless compaction output would fall\n // back to an earlier turn's normal reply and get mistakenly injected as the summary; the\n // in-process path yields an empty summary here (extractSummary(“”)).\n if (msg.payload.status === \"completed\") text = current;\n inRequest = false;\n }\n continue;\n }\n if (!inRequest || !isCompleteModelMessage(msg)) continue;\n const p = msg.payload as { type?: string; role?: string; text?: string };\n if (p.type === \"text\" && p.role === \"assistant\" && p.text) current += p.text;\n }\n return text;\n}\n","/**\n * context_engine — orchestrates the ReAct loop.\n *\n * context_engine only handles OmniMessage, orchestrating the flow of events between the\n * Human, LLM, and Environment interfaces, and writes every observable action to Trace.\n * The initial version keeps a linear message history.\n *\n * Human is the SDK's input/output boundary: there is no \"Human implementation/interface\".\n * Input is the Prompt list passed to `run`, plus the abort signal `signal` and the\n * per-tool approval callback `approve` in `RunOptions`; output is the OmniMessage stream\n * produced by `run`.\n *\n * Docs: packages/docs/content/agent-loop.{zh,en}.md (site path /docs/agent-loop) documents\n * the turn lifecycle, carry-over, reconnect and compaction implemented here.\n *\n * Approval is an **in-turn interaction** and tool calls are **async/incremental** (see\n * comment #24):\n * - A single `run` call automatically runs the entire ReAct loop (no more resuming in batches);\n * - Each tool_call is emitted as soon as its stream completes → `await approve` → if\n * allowed, it runs via Environment;\n * - Execution **does not block** continued consumption of the LLM stream or approval of\n * the next tool (executions can overlap), but approvals still happen one at a time;\n * - partial/complete `tool_call_output` is yielded in **completion order**;\n * - once all tool outputs for the turn are ready, they become the next turn's LLM input;\n * the Task ends once a turn produces no more tool_call.\n *\n * Implementation note: an internal queue merges \"the LLM event stream + N concurrent tool\n * output streams\" into a single yield sequence. GenerativeModel is a stateful object\n * (AgentHub maintains the history); each turn the engine only hands it the \"new\" messages:\n * the user Prompt on the first turn, and the previous turn's tool_call_output afterward.\n */\nimport {\n abortEvent,\n approvalDecision,\n assistantText,\n compactionBegin,\n compactionEnd,\n emptyTokenCounts,\n isCompleteModelMessage,\n isSessionMeta,\n partialText,\n requestBegin,\n requestEnd,\n subagentEvent,\n toolCallOutput,\n userText,\n} from \"../omnimessage/index.js\";\nimport type {\n ApprovalDecision,\n CompactionMode,\n CompactionReason,\n OmniMessage,\n StopReason,\n TextPayload,\n ThinkingPayload,\n TokenCounts,\n TokenUsagePayload,\n ToolCallOutputPayload,\n ToolCallPayload,\n} from \"../omnimessage/index.js\";\nimport type { ApproveFn, EnvironmentInterface, LLMInterface, LLMOutcome } from \"../interfaces.js\";\n\n/** Trace sink: `write` a complete/event/meta message; `rotate` starts a new file (compaction splits files). */\nexport interface TraceSink {\n write(msg: OmniMessage): Promise<void>;\n /** Optional: start a new Trace file (index+1), used to record the new model context after compaction. */\n rotate?(): Promise<void>;\n}\n\n/**\n * Resolved context compaction settings (defaults filled in by the composition layer).\n * Docs: /docs/agent-loop § \"Compaction\".\n */\nexport interface CompactionSettings {\n /** Context token threshold (uses the most recent token_usage's request.total); <=0 disables it. */\n maxContextLength: number;\n /** Session cumulative turn threshold (counted per LLM Request, across Tasks); <=0 means no limit. */\n maxSessionTurns: number;\n mode: CompactionMode;\n /** Prompt used for summarize compaction. */\n prompt: string;\n}\n\n/** Result of one compaction run: status is a terminal state (completed / failed / aborted); carries the summary message when summarize succeeds. */\ninterface CompactionResult {\n status: StopReason;\n summary?: OmniMessage;\n}\n\n/**\n * Options for `run`.\n * Docs: /docs/agent-loop § \"Inputs and outputs\".\n */\nexport interface RunOptions {\n /** Abort signal (e.g. Ctrl-C). */\n signal?: AbortSignal;\n /** Per-tool approval callback; defaults to denying everything (conservative, to avoid accidental approval when unattended). */\n approve?: ApproveFn;\n}\n\n/**\n * Engine initial state (used for Session resumption): derived by replaying Trace, so the\n * resumed engine behaves the same as before the process\n * exited. Not passed when creating a normal new Session.\n */\nexport interface EngineInitialState {\n /** Pending input (carry-over): resent alongside new input on the first `run` after resumption (synthetic placeholders exist only in memory, never written to Trace). */\n carryOver?: OmniMessage[];\n /** Summary recovered from a completed summarize compaction: used as the prefix of the next `run` input (merged with the user Prompt). */\n pendingSummary?: OmniMessage;\n /** Carried-over Session cumulative turn count. */\n sessionTurns?: number;\n /** Carried-over Session cumulative token counts (handed to the new object when compaction swaps it in). */\n sessionTokens?: TokenCounts;\n /** Most recent token_usage's request.total (the context usage figure, keeps compaction threshold checks continuous). */\n lastRequestTotal?: number;\n /** Recovered from a completed compaction: the context is already closed, so rotate the Trace file (index+1, writing session_meta) before the first write. */\n pendingTraceRotation?: boolean;\n}\n\nexport interface ContextEngineDeps {\n llm: LLMInterface;\n environment: EnvironmentInterface;\n /** Optional Trace writer; the writer is responsible for filtering out streaming partial_* messages. */\n trace?: TraceSink;\n /** Engine initial state (derived by replaying Trace on Session resumption). */\n initialState?: EngineInitialState;\n /** Maximum LLM turns for a single Task. Defaults to 100. */\n maxTurns?: number;\n /** Maximum automatic retries for LLM timeout/reconnect within a single run. Defaults to 2. */\n maxReconnects?: number;\n /** Linear backoff base (ms) before each reconnect retry; actual backoff = base × retry number. Defaults to 250. */\n reconnectBackoffMs?: number;\n /**\n * Creates a new LLM object after compaction (a fresh model context); the argument is the\n * current Session cumulative token counts, for the new object to carry forward\n * (token_usage.session stays continuous across compaction). Context compaction is\n * unavailable if this is not provided.\n */\n createLLM?: (sessionTokens: TokenCounts) => LLMInterface;\n /** Context compaction settings; only takes effect if provided together with `createLLM`. */\n compaction?: CompactionSettings;\n /** This Session's session_meta message; written at the start of the new Trace file after compaction splits it. */\n sessionMeta?: OmniMessage;\n}\n\n/** Whether compaction is possible; when not `ok`, `compact()` is a no-op and yields no messages (see ContextEngine.compactability). */\nexport type CompactAvailability = \"ok\" | \"unsupported\" | \"empty\" | \"just_compacted\";\n\n/** Result of executing one LLM turn (the return value of runTurn). */\ninterface TurnResult {\n /** All tool outputs for this turn, reordered to match the original tool_call order (for the next turn's LLM input). */\n toolOutputs: OmniMessage[];\n /** tool_calls issued by the model this turn (in original order, real requests only). */\n toolCalls: OmniMessage<ToolCallPayload>[];\n /** Complete thinking/text segments produced by the model this turn (including partial segments finalized on interruption), for carry-over flattening. */\n assistantSegments: OmniMessage[];\n /** Terminal state of this turn's LLM request (completed / failed / aborted / timeout / malformed). */\n outcome: LLMOutcome;\n}\n\n/**\n * Merge queue: lets multiple concurrent producers (the LLM stream consumer + several tool\n * executions) push OmniMessage entries; a single consumer (run's generator) pulls and\n * yields them in push order. Finishes once all producers are done and the queue is drained.\n * Docs: /docs/message-flow § \"The merge point: MergeQueue\".\n */\nclass MergeQueue {\n private items: OmniMessage[] = [];\n private producers = 0;\n private wake: (() => void) | null = null;\n\n /** Registers a producer. */\n addProducer(): void {\n this.producers += 1;\n }\n\n /** Deregisters a producer (its stream has finished). */\n removeProducer(): void {\n this.producers -= 1;\n this.signal();\n }\n\n /** Pushes a message and wakes the consumer. */\n push(msg: OmniMessage): void {\n this.items.push(msg);\n this.signal();\n }\n\n private signal(): void {\n if (this.wake) {\n const w = this.wake;\n this.wake = null;\n w();\n }\n }\n\n /** Takes the next message; waits if empty but producers remain; returns null if empty and no producers remain. */\n async next(): Promise<OmniMessage | null> {\n for (;;) {\n if (this.items.length > 0) return this.items.shift()!;\n if (this.producers === 0) return null;\n await new Promise<void>((resolve) => {\n this.wake = resolve;\n });\n }\n }\n}\n\nexport class ContextEngine {\n private readonly maxTurns: number;\n private readonly maxReconnects: number;\n private readonly reconnectBackoffMs: number;\n /** Interruption cleanup: content to resend generated when the previous run was aborted, held on the engine across runs. */\n private pendingCarryOver: OmniMessage[] = [];\n /** Current LLM object; swapped for a new one created by `createLLM` after a successful compaction (a fresh model context). */\n private llm: LLMInterface;\n /** Session cumulative turn count: counted per LLM Request that produces token_usage, across Tasks; reset to zero after compaction completes. */\n private sessionTurns = 0;\n /** Whether the current context was produced by a compaction (`startNewContext`); this flag becomes meaningless once a new completed turn occurs. */\n private fromCompaction = false;\n /** Most recent token_usage's request.total, i.e. the current context usage figure. */\n private lastRequestTotal = 0;\n /** Most recent token_usage's session cumulative counts, handed to the new LLM object when compaction swaps it in. */\n private lastSessionTokens: TokenCounts = emptyTokenCounts();\n /** Summary produced by a Task-boundary compaction: used as the prefix of the next `run` input (merged with the next user Prompt). */\n private pendingSummary: OmniMessage | null = null;\n /**\n * Set to true once compaction completes: Trace rotation is deferred until the next\n * message that needs writing (see `write`) — so that if no further messages follow the\n * compaction, we don't create an empty file containing only session_meta.\n */\n private pendingTraceRotation = false;\n\n constructor(private readonly deps: ContextEngineDeps) {\n this.maxTurns = deps.maxTurns ?? 100;\n this.maxReconnects = deps.maxReconnects ?? 2;\n this.reconnectBackoffMs = deps.reconnectBackoffMs ?? 250;\n this.llm = deps.llm;\n // Session resumption: apply the initial state derived from replay.\n const init = deps.initialState;\n if (init) {\n this.pendingCarryOver = init.carryOver ?? [];\n this.pendingSummary = init.pendingSummary ?? null;\n this.sessionTurns = init.sessionTurns ?? 0;\n this.lastSessionTokens = init.sessionTokens ?? emptyTokenCounts();\n this.lastRequestTotal = init.lastRequestTotal ?? 0;\n this.pendingTraceRotation = init.pendingTraceRotation ?? false;\n }\n }\n\n /**\n * Runs a Task to completion, streaming out OmniMessage. `newMessages` is this call's\n * Prompt (only the newly added input, not the full history — history is maintained by the\n * stateful GenerativeModel); `opts.signal` is the abort signal, `opts.approve` is the\n * per-tool approval callback.\n * Docs: /docs/agent-loop § \"The loop at a glance\".\n */\n async *run(newMessages: OmniMessage[], opts?: RunOptions): AsyncGenerator<OmniMessage> {\n const signal = opts?.signal;\n // Default approval policy: deny (conservative). CLI/Web will inject a real callback (interactive or permission-mode based).\n const approve: ApproveFn = opts?.approve ?? (async () => \"deny\");\n\n // Merge the Task-boundary compaction summary (the new context's first input, merged with\n // this Prompt), the carry-over left over from the last interruption, and this call's new\n // input, to form this Request's input.\n const summary = this.pendingSummary;\n this.pendingSummary = null;\n const carryOver = this.pendingCarryOver;\n this.pendingCarryOver = [];\n const prefix = summary ? [summary, ...carryOver] : carryOver;\n const input = prefix.length ? [...prefix, ...newMessages] : newMessages;\n\n // Input is written to Trace (Prompt record, incl. audit trail) but not replayed to\n // the render layer. carry-over is not written to Trace: real messages (tool outputs etc.)\n // are already written when produced; synthetic content (flatten text, backfilled\n // placeholders) is **sent to the model only, never persisted** — Trace records only real\n // messages, and resumption replay best-effort reconstructs from original messages.\n // Exception: the compaction summary, which is the new\n // context's first input record, is written as usual.\n if (summary) await this.write(summary);\n for (const msg of newMessages) await this.write(msg);\n\n if (signal?.aborted) {\n // Aborted before the Request was issued: the input is held **as-is** as carry-over\n // (trailing-input semantics: input the Request never got to send is kept unchanged)\n // — not flattened, so replay matches in-process behavior and\n // multimodal input isn't lost. The message is already written to Trace, so it won't be\n // rewritten on the next send.\n this.pendingCarryOver = input;\n yield* this.emitAbort(\"aborted by user\");\n return;\n }\n\n let turnCount = 0;\n // Each turn's LLM input: the first turn is the Prompt, later turns are the previous turn's\n // tool outputs.\n let nextInput: OmniMessage[] = input;\n\n for (;;) {\n // max_turns guard: emit a length notice and stop once exceeded.\n if (turnCount >= this.maxTurns) {\n // This turn's pending input (usually the previous turn's tool outputs) was never\n // submitted to the LLM: hold it as carry-over, to be resent merged with new input on\n // the next `run` (same as interruption-cleanup case A) — the previous turn's assistant\n // tool_call has already been committed by AgentHub, so discarding its paired output and\n // sending a fresh message would be rejected by the provider as an unanswered tool_use\n // (400, see issue #33).\n this.pendingCarryOver = nextInput;\n yield* this.emitMaxTurns();\n return;\n }\n turnCount += 1;\n\n // This turn's input. A timeout/malformed attempt is never committed to history by\n // AgentHub (an abnormally interrupted stream doesn't land in history), so reconnect\n // resends this turn's input unchanged, appending a `<turn_retried>` block carrying what\n // the failed attempt already produced — the model continues from there instead of\n // re-running tools; the tag is distinct from the user-interruption `<turn_aborted>`.\n const failedTurns: TurnResult[] = [];\n let attemptInput = nextInput;\n let reconnects = 0;\n let turn: TurnResult;\n\n for (;;) {\n // Both LLM and Environment handle errors internally and guarantee a complete, closed\n // output with no thrown exceptions; the engine doesn't handle exceptions —\n // it decides retry/resend purely from `outcome`.\n turn = yield* this.runTurn(attemptInput, approve, signal);\n\n // User interruption (the LLM stream was aborted, outcome=aborted, or `signal` fired\n // during tool execution): stop and hand control back to the user.\n if (signal?.aborted || turn.outcome.status === \"aborted\") {\n this.pendingCarryOver = this.buildCarryOver(attemptInput, turn);\n yield* this.emitAbort(\"aborted by user\");\n return;\n }\n // Non-retryable error (auth/parameter etc.): stop and hand control back to the user;\n // the failure reason is written to the abort event / Trace.\n if (turn.outcome.status === \"failed\") {\n this.pendingCarryOver = this.buildCarryOver(attemptInput, turn);\n yield* this.emitAbort(`llm request error: ${turn.outcome.message ?? \"unknown\"}`);\n return;\n }\n // Completed normally.\n if (turn.outcome.status === \"completed\") break;\n\n // Only timeout / malformed remain: reconnect automatically within the same run. When\n // retries are exhausted or the backoff is interrupted, the retry input is held as-is as\n // carry-over (the original input is already written to Trace, so it isn't rewritten).\n // The frontend surfaces the retry process and count via request_end(timeout|malformed)\n // followed by the next request_begin.\n failedTurns.push(turn);\n attemptInput = this.withRetriedTurns(nextInput, failedTurns);\n if (reconnects >= this.maxReconnects) {\n this.pendingCarryOver = attemptInput;\n const reason = turn.outcome.status === \"malformed\" ? \"malformed response\" : \"reconnect\";\n yield* this.emitAbort(`${reason} failed after ${this.maxReconnects} retries`);\n return;\n }\n reconnects += 1;\n if (!(await this.backoff(reconnects, signal))) {\n this.pendingCarryOver = attemptInput;\n yield* this.emitAbort(\"aborted during reconnect backoff\");\n return;\n }\n }\n\n // Compaction checkpoint: after every LLM Request produces token_usage. This also\n // applies mid-Task — when runTurn returns, all of this turn's\n // tool results are ready and paired with their tool_call.\n const midTask = turn.toolOutputs.length > 0;\n const compactionReason = this.compactionTrigger();\n if (compactionReason) {\n const mode = this.deps.compaction!.mode;\n if (mode === \"discard\") {\n // Once discarded, the current Task can't continue: if mid-Task, defer until this\n // Task ends.\n if (!midTask) {\n yield* this.discardContext(compactionReason);\n return;\n }\n } else {\n const result = yield* this.summarizeContext(\n compactionReason,\n midTask ? turn.toolOutputs : [],\n signal,\n );\n if (result.status === \"aborted\") {\n // User interrupted compaction: keep the original context; if mid-Task, hold the\n // tool outputs as carry-over per case A.\n if (midTask) {\n this.pendingCarryOver = this.buildCarryOver(attemptInput, turn);\n yield* this.emitAbort(\"aborted during compaction\");\n }\n return;\n }\n if (result.status === \"completed\") {\n if (!midTask) {\n // Task boundary: the summary is merged with the next user Prompt as the new\n // context's first input.\n this.pendingSummary = result.summary!;\n return;\n }\n // Mid-Task: the summary itself becomes the new LLM object's first input (this\n // turn's tool results were already folded into the compaction request and absorbed\n // into the summary); continuation relies on the model's own next-step plan written\n // into the summary, with no hardcoded continuation instruction appended.\n await this.write(result.summary!);\n nextInput = [result.summary!];\n continue;\n }\n // failed: keep the original context and Trace index; the current Task continues and\n // retries on the next trigger (no fallback to discard).\n }\n }\n\n // No tool_call this turn -> the Task ends (the final reply has already been streamed out).\n if (!midTask) return;\n // Otherwise continue, using the tool outputs as the next turn's LLM input.\n nextInput = turn.toolOutputs;\n }\n }\n\n /**\n * User-initiated compaction request (e.g. a CLI command): reuses the automatic compaction\n * flow without checking thresholds (reason=manual). Only callable at a Task boundary (between\n * runs); streams out paired compaction events. No-op when compaction is not configured.\n *\n * Carry-over left over from an interruption is cleaned up here too: summarize folds it into\n * the compaction request (structured tool outputs keep their pairing with the already\n * committed tool_call, otherwise the compaction request itself would be rejected by the\n * provider as an unanswered tool_use, see issue #33; flatten text is absorbed into the\n * summary); discard drops the structured outputs paired with the old context, keeping only the\n * self-contained flatten text.\n */\n /**\n * Whether compaction is possible, and the **reason** when it isn't.\n *\n * `compact()` is a no-op and **yields no messages** in these cases; if the UI treats invoking\n * it as a successful start, it ends up waiting forever for a compaction banner that never\n * arrives — that's exactly how \"/compact does nothing after an interruption\" happens. Callers\n * (Web / CLI) should give feedback upfront based on this.\n *\n * - `unsupported`: compaction capability is not configured;\n * - `empty`: the current context hasn't completed a single turn (`sessionTurns` only\n * increments when `token_usage` arrives — a turn only counts once the request finishes\n * normally, so it's still 0 right after the first request is interrupted);\n * - `just_compacted`: no new conversation since the last compaction. Both cases have\n * `sessionTurns` === 0, but they mean two completely different things to the user and must\n * not be conflated.\n */\n compactability(): CompactAvailability {\n if (!this.deps.compaction || !this.deps.createLLM) return \"unsupported\";\n if (this.sessionTurns > 0) return \"ok\";\n return this.fromCompaction ? \"just_compacted\" : \"empty\";\n }\n\n async *compact(opts?: { signal?: AbortSignal }): AsyncGenerator<OmniMessage> {\n if (!this.deps.compaction || !this.deps.createLLM) return;\n // The current context has no completed LLM turns: nothing to compact, return immediately.\n // This also guards against two /compact calls in a row — the new context is empty right\n // after the previous compaction, so running again would overwrite the not-yet-consumed\n // pendingSummary with an \"empty summary,\" permanently losing the only record of the prior\n // conversation.\n if (this.sessionTurns === 0) return;\n if (this.deps.compaction.mode === \"discard\") {\n this.pendingCarryOver = this.pendingCarryOver.filter(\n (m) => (m.payload as { type?: string }).type !== \"tool_call_output\",\n );\n yield* this.discardContext(\"manual\");\n return;\n }\n const result = yield* this.summarizeContext(\"manual\", this.pendingCarryOver, opts?.signal);\n if (result.status === \"completed\") {\n this.pendingCarryOver = [];\n this.pendingSummary = result.summary!;\n }\n }\n\n /**\n * Linear backoff before a reconnect retry (base × retry number, numbering starts at 1);\n * returns false if the user interrupts during the backoff, so the caller can proceed to\n * interruption cleanup.\n */\n private backoff(attempt: number, signal?: AbortSignal): Promise<boolean> {\n const ms = this.reconnectBackoffMs * attempt;\n return new Promise<boolean>((resolve) => {\n if (signal?.aborted) {\n resolve(false);\n return;\n }\n const onAbort = (): void => {\n clearTimeout(timer);\n resolve(false);\n };\n const timer = setTimeout(() => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(true);\n }, ms);\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n });\n }\n\n /**\n * Runs one LLM turn: consumes the LLM stream, approving each complete tool_call immediately;\n * \"allow\" runs it concurrently (without blocking further stream consumption/approval), \"deny\"\n * feeds back an aborted output. partial/complete tool_call_output is yielded in completion\n * order. Returns all of this turn's tool outputs (for the next turn) and whether it was\n * interrupted midway.\n * Docs: /docs/agent-loop § \"Lifecycle of a turn\".\n */\n private async *runTurn(\n input: OmniMessage[],\n approve: ApproveFn,\n signal?: AbortSignal,\n ): AsyncGenerator<OmniMessage, TurnResult> {\n const queue = new MergeQueue();\n // Tool outputs are collected in **completion order** (for streaming yield to the frontend);\n // the tool_calls' **original order** is recorded separately, and reordered back to original\n // order when fed into the next LLM turn (async tool calls: feedback order is preserved).\n const toolOutputs: OmniMessage[] = [];\n const toolCalls: OmniMessage<ToolCallPayload>[] = [];\n const callOrder: string[] = [];\n // This turn's complete thinking/text segments produced by the model (including partial\n // segments finalized on interruption), for carry-over flatten.\n const assistantSegments: OmniMessage[] = [];\n // This turn's LLM terminal state: taken from streamGenerate's generator return value.\n let outcome: LLMOutcome = { status: \"completed\" };\n\n // Driver task: consumes the LLM stream + approves one at a time + dispatches tool\n // execution. It is itself a producer.\n queue.addProducer();\n const drive = (async () => {\n try {\n // Request boundary events (replayability): start is\n // emitted when the request is issued, stop carries the terminal state at completion —\n // replay mechanically determines from these whether the turn was committed by AgentHub.\n const startEvt = requestBegin();\n queue.push(startEvt);\n await this.write(startEvt);\n // Iterate manually to capture the generator's **return value** (LLMOutcome); LLM\n // guarantees it never throws.\n const gen = this.llm.streamGenerate({\n newMessages: input,\n ...(signal ? { signal } : {}),\n });\n for (;;) {\n const res = await gen.next();\n if (res.done) {\n outcome = res.value;\n const stopEvt = requestEnd(outcome.status);\n queue.push(stopEvt);\n await this.write(stopEvt);\n break;\n }\n const msg = res.value;\n queue.push(msg);\n await this.write(msg);\n // token_usage means \"this Request completed normally\": record the context usage /\n // Session cumulative counts, and increment the Session turn count (counted per LLM\n // Request, across Tasks; used for compaction threshold checks).\n if (this.observeTokenUsage(msg)) this.sessionTurns += 1;\n // Collect complete thinking/text segments (including partial segments finalized on\n // interruption), for carry-over flatten.\n if (\n isCompleteModelMessage(msg) &&\n (msg.payload.type === \"thinking\" || msg.payload.type === \"text\")\n ) {\n assistantSegments.push(msg);\n }\n // Approve as soon as each real, complete tool_call finishes streaming. A tool_call\n // synthesized to close out an interruption carries a non-\"completed\" stop_reason (see\n // finishInterrupted): its arguments weren't fully emitted, and it exists only\n // for structural closure and observability — it isn't dispatched for execution, isn't\n // added to this turn's ledger, and gets no paired output backfilled: such a tool_call\n // was never committed to history by AgentHub, so there's nothing to pair. This turn\n // must then end with a non-completed outcome (only interruption closure produces such\n // a tool_call): timeout/malformed is cleaned up by reconnect resending the flatten\n // carry-over, failed/aborted exits directly.\n if (isCompleteModelMessage(msg) && msg.payload.type === \"tool_call\") {\n const tc = msg as OmniMessage<ToolCallPayload>;\n if (tc.payload.stop_reason !== \"completed\") continue;\n const toolCallId = tc.payload.tool_call_id;\n callOrder.push(toolCallId);\n toolCalls.push(tc);\n // Already interrupted: stop dispatching new tools, but keep consuming until the LLM\n // returns its outcome (the LLM will close out quickly and return aborted).\n if (signal?.aborted) continue;\n // The approval callback is injected externally (RunOptions.approve): any throw\n // collapses to deny (conservative), so the exception never escapes the engine —\n // otherwise it would propagate through session.run without building carry-over,\n // leaving the already-committed tool_use unanswered.\n let decision: ApprovalDecision;\n try {\n decision = await approve(tc);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[penguin] approve callback threw: ${message}; denying.\\n`);\n decision = \"deny\";\n }\n if (signal?.aborted) continue;\n // approve is a callback; context_engine emits its decision as an approval_decision\n // OmniMessage: pushed to the stream for frontend rendering, and written to Trace.\n const decisionMsg = approvalDecision(decision, toolCallId);\n queue.push(decisionMsg);\n await this.write(decisionMsg);\n if (decision !== \"allow\") {\n // User denied: feed back an aborted output, indicating the tool call was\n // manually canceled.\n const denied = toolCallOutput({\n output: \"Tool call denied by user.\",\n toolCallId,\n stopReason: \"aborted\",\n });\n queue.push(denied);\n await this.write(denied);\n toolOutputs.push(denied);\n continue;\n }\n // Approved: run concurrently, without blocking further consumption of the LLM\n // stream or approval of the next tool.\n queue.addProducer();\n void this.executeOne(tc, queue, toolOutputs, signal, approve).finally(() => {\n queue.removeProducer();\n });\n }\n }\n } finally {\n queue.removeProducer();\n }\n })();\n\n // Single consumer: yield merged messages one at a time until all producers are done and\n // the queue is drained.\n for (;;) {\n const msg = await queue.next();\n if (msg === null) break;\n yield msg;\n }\n // Wait for the driver task to fully finish (state settles).\n await drive;\n\n // Feed into the next turn: reordered to the original tool_call order (each tool_call has\n // exactly one output, see the executeOne invariant).\n const byId = new Map<string, OmniMessage>();\n for (const out of toolOutputs) {\n const id = (out.payload as { tool_call_id?: string }).tool_call_id;\n if (id !== undefined) byId.set(id, out);\n }\n const orderedOutputs: OmniMessage[] = [];\n const seen = new Set<string>();\n for (const id of callOrder) {\n if (seen.has(id)) continue; // Dedupe: feed back exactly one output per tool_call_id, to preserve pairing\n seen.add(id);\n const out = byId.get(id);\n if (out) orderedOutputs.push(out);\n }\n return { toolOutputs: orderedOutputs, toolCalls, assistantSegments, outcome };\n }\n\n /**\n * Executes a single approved tool: streams its partial/complete tool_call_output (through the\n * queue), and collects the complete tool_call_output into toolOutputs.\n *\n * Environment is contracted to handle all errors internally: it guarantees exactly one\n * complete `tool_call_output` to close out and never throws. But since\n * EnvironmentInterface can be injected by consumers via a public API, if a contract-violating\n * exception escapes, this fire-and-forget promise would take down the process with an\n * unhandled rejection, and the missing output would leave the already-committed tool_use\n * unanswered (the next request gets rejected by the provider) — so a boundary safety net is\n * kept here, collapsing a contract-violating exception into a failed output. This guarantees\n * exactly one complete output per tool enters toolOutputs, keeping tool_use and tool_result\n * paired.\n */\n private async executeOne(\n toolCall: OmniMessage<ToolCallPayload>,\n queue: MergeQueue,\n toolOutputs: OmniMessage[],\n signal?: AbortSignal,\n approve?: ApproveFn,\n ): Promise<void> {\n let completed = false;\n try {\n for await (const out of this.deps.environment.executeTool({\n toolCall,\n ...(signal ? { signal } : {}),\n // Pass through the parent approval callback: run_subagent uses this so the child\n // Session inherits the parent Agent's approval mode.\n ...(approve ? { approve } : {}),\n })) {\n queue.push(out);\n // Nested-session messages carrying an origin: forwarded to the frontend as a stream;\n // their content is not written to the parent Trace (the child Session has its own\n // Trace). When a direct child session's (origin length 1) session_meta arrives, write a\n // subagent pointer event to the parent Trace (recording only the child Session id), so\n // reopening the session can recursively expand child Traces — pointers for grandchild\n // sessions are recorded by the child Trace itself, so only depth 1 is recognized here.\n // Never fed back — a child session's tool_call_output has no pairing with the parent's\n // tool_call, and feeding it back by mistake would be rejected by the Provider.\n if (out.origin && out.origin.length > 0) {\n if (isSessionMeta(out) && out.origin.length === 1) {\n await this.write(subagentEvent(out.origin[0]!));\n }\n continue;\n }\n await this.write(out);\n if (isCompleteModelMessage(out) && out.payload.type === \"tool_call_output\") {\n toolOutputs.push(out);\n completed = true;\n }\n }\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n if (completed) {\n // Thrown only after the complete output was ready: pairing is intact, so just warn.\n process.stderr.write(`[penguin] environment threw after tool output: ${message}\\n`);\n return;\n }\n const failed = toolCallOutput({\n output: `[tool error] ${message}`,\n toolCallId: toolCall.payload.tool_call_id,\n stopReason: \"failed\",\n });\n queue.push(failed);\n await this.write(failed);\n toolOutputs.push(failed);\n }\n }\n\n /** Max turns reached: emits a failed notice (streaming fragments + complete text) for CLI/frontend rendering. */\n private async *emitMaxTurns(): AsyncGenerator<OmniMessage> {\n // Reduce leading newlines: avoid stacking extra newlines before the text (comment #15).\n const text = `[reached max turns (${this.maxTurns}); stopping]`;\n const partials = [\n partialText(\"start\"),\n partialText(\"delta\", text),\n partialText(\"stop\", \"\", \"failed\"),\n ];\n for (const partial of partials) {\n yield partial;\n await this.write(partial);\n }\n const note = assistantText(text, \"failed\");\n yield note;\n await this.write(note);\n }\n\n // -------------------------------------------------------------------------\n // Context compaction\n // -------------------------------------------------------------------------\n\n /**\n * Checks the compaction threshold: triggers once context usage (the most recent\n * token_usage's request.total) or the Session cumulative turn count **reaches** the threshold\n * (>=) — e.g. maxSessionTurns=1 compacts as soon as turn 1 completes, without waiting for the\n * next Task; when both are configured, either reaching its threshold triggers compaction.\n * Never triggers when compaction is not configured.\n * Docs: /docs/agent-loop § \"Compaction\".\n */\n private compactionTrigger(): CompactionReason | null {\n const settings = this.deps.compaction;\n if (!settings || !this.deps.createLLM) return null;\n if (settings.maxContextLength > 0 && this.lastRequestTotal >= settings.maxContextLength) {\n return \"context\";\n }\n if (settings.maxSessionTurns > 0 && this.sessionTurns >= settings.maxSessionTurns) {\n return \"turns\";\n }\n return null;\n }\n\n /** Records context usage and Session cumulative counts from a token_usage event; returns whether the message is a token_usage. */\n private observeTokenUsage(msg: OmniMessage): boolean {\n if (msg.type !== \"event_msg\") return false;\n const payload = msg.payload as Partial<TokenUsagePayload>;\n if (payload.type !== \"token_usage\") return false;\n if (payload.request) this.lastRequestTotal = payload.request.total;\n if (payload.session) this.lastSessionTokens = payload.session;\n return true;\n }\n\n /**\n * `discard` compaction: sends no compaction request, simply discards the old context —\n * swaps in a new LLM object and splits a new Trace file, with the next turn's input used\n * unchanged as the new object's first input. Only runs at a Task boundary (deferred by the\n * caller while mid-Task).\n */\n private async *discardContext(reason: CompactionReason): AsyncGenerator<OmniMessage> {\n yield* this.emitCompactionBegin(reason, \"discard\");\n yield* this.emitCompactionEnd(reason, \"discard\", \"completed\");\n await this.startNewContext();\n }\n\n /**\n * `summarize` compaction: appends the compaction Prompt to the **old** LLM object (first\n * folding in all of this turn's tool results when mid-Task, to keep tool_use/tool_result\n * pairing), then extracts the `<summary>` and wraps it as `<context_summary>` user text. The\n * compaction request's streamed output is not pushed to the Human output stream (it emits\n * paired compaction events, plus the compaction request's `token_usage` — positioned between\n * the two events, so the frontend can count compaction cost into its stats), but it is written\n * to the old Trace. timeout/malformed reconnect via the existing retry mechanism, collapsing\n * to failed once retries are exhausted; on failure/abort, the original context and Trace index\n * are kept — it does not fall back to discard.\n * Docs: /docs/agent-loop § \"Compaction\".\n */\n private async *summarizeContext(\n reason: CompactionReason,\n pendingToolOutputs: OmniMessage[],\n signal?: AbortSignal,\n ): AsyncGenerator<OmniMessage, CompactionResult> {\n const settings = this.deps.compaction!;\n yield* this.emitCompactionBegin(reason, \"summarize\");\n\n // Compaction request input: this turn's tool results (mid-Task) or leftover interruption\n // carry-over, plus the compaction Prompt. The compaction exchange is written to the old\n // Trace (traceable but not pushed to the user); tool results were already written when\n // executed and aren't recorded again, while carry-over's not-yet-written synthetic content\n // (flatten text, backfilled placeholders) and the compaction Prompt are written now.\n const prompt = userText(settings.prompt);\n const input = [...pendingToolOutputs, prompt];\n await this.write(prompt);\n\n let reconnects = 0;\n for (;;) {\n if (signal?.aborted) {\n yield* this.emitCompactionEnd(reason, \"summarize\", \"aborted\");\n return { status: \"aborted\" };\n }\n const attempt = await this.runCompactionRequest(input, signal);\n if (attempt.status === \"completed\") {\n // The compaction request's token_usage is pushed to the Human output stream (already\n // written to Trace in runCompactionRequest, so here it's only yielded, not rewritten);\n // the frontend uses this to count compaction cost into stats and display it on the\n // compaction-complete line.\n if (attempt.usage) yield attempt.usage;\n // Lenient extraction: if the output lacks a <summary> tag, use the entire compaction\n // output as-is rather than treating it as a failure.\n const summary = userText(\n `<context_summary>\\n${extractSummary(attempt.text)}\\n</context_summary>`,\n );\n yield* this.emitCompactionEnd(reason, \"summarize\", \"completed\");\n await this.startNewContext();\n return { status: \"completed\", summary };\n }\n if (attempt.status === \"aborted\" || attempt.status === \"failed\") {\n yield* this.emitCompactionEnd(reason, \"summarize\", attempt.status);\n return { status: attempt.status };\n }\n // timeout / malformed: retried via reconnect. The compaction request was never committed\n // by AgentHub (case B), so the original input is resent unchanged.\n if (reconnects >= this.maxReconnects) {\n yield* this.emitCompactionEnd(reason, \"summarize\", \"failed\");\n return { status: \"failed\" };\n }\n reconnects += 1;\n const ok = await this.backoff(reconnects, signal);\n if (!ok) {\n yield* this.emitCompactionEnd(reason, \"summarize\", \"aborted\");\n return { status: \"aborted\" };\n }\n }\n }\n\n /**\n * Issues one compaction request (an ordinary LLM Request): consumes the old LLM object's\n * streamed output but **does not push it to the Human output stream** (except `token_usage`\n * — captured and handed back via the return value for summarizeContext to yield); complete\n * messages and events are written to the old Trace; complete text segments are collected as\n * the compaction output. Token usage is counted into the Session cumulative totals (recorded\n * via observeTokenUsage, for the new object to carry forward).\n */\n private async runCompactionRequest(\n input: OmniMessage[],\n signal?: AbortSignal,\n ): Promise<{ status: StopReason; text: string; usage: OmniMessage | null }> {\n // The compaction request is itself an ordinary Request, emitting paired request events —\n // written to the (old) Trace only, not pushed to the stream, keeping the compaction process\n // invisible to Human.\n await this.write(requestBegin());\n const gen = this.llm.streamGenerate({\n newMessages: input,\n ...(signal ? { signal } : {}),\n });\n let text = \"\";\n let usage: OmniMessage | null = null;\n for (;;) {\n const res = await gen.next();\n if (res.done) {\n await this.write(requestEnd(res.value.status));\n return { status: res.value.status, text, usage };\n }\n const msg = res.value;\n await this.write(msg);\n if (this.observeTokenUsage(msg)) usage = msg;\n if (isCompleteModelMessage(msg) && msg.payload.type === \"text\") {\n text += (msg.payload as TextPayload).text;\n }\n }\n }\n\n /**\n * Opens a new model context after successful compaction: swaps in a new LLM object (carrying\n * forward the Session cumulative token counts), resets the Session turn count and context\n * usage counter. Trace **does not** split files immediately — that's deferred until the next\n * message that needs writing, when it rotates and opens with a session_meta (see `write`),\n * avoiding an empty file if no further messages follow the compaction.\n */\n private async startNewContext(): Promise<void> {\n this.pendingTraceRotation = true;\n this.llm = this.deps.createLLM!(this.lastSessionTokens);\n this.sessionTurns = 0;\n this.lastRequestTotal = 0;\n // Lets compactability() distinguish \"just compacted\" from \"hasn't chatted yet\" — both have\n // sessionTurns === 0, but they mean two completely different things to the user (being told\n // \"no completed conversation turns yet\" right after compacting is as good as saying nothing).\n this.fromCompaction = true;\n }\n\n /** Yields and records a compaction start event (carrying reason/mode/current context usage/Session cumulative turns). */\n private async *emitCompactionBegin(\n reason: CompactionReason,\n mode: CompactionMode,\n ): AsyncGenerator<OmniMessage> {\n const msg = compactionBegin({\n reason,\n mode,\n context: this.lastRequestTotal,\n turns: this.sessionTurns,\n });\n yield msg;\n await this.write(msg);\n }\n\n /** Yields and records a compaction stop event (carrying the result status; non-completed means compaction was abandoned). */\n private async *emitCompactionEnd(\n reason: CompactionReason,\n mode: CompactionMode,\n status: StopReason,\n ): AsyncGenerator<OmniMessage> {\n const msg = compactionEnd({ reason, mode, status });\n yield msg;\n await this.write(msg);\n }\n\n /** Interruption: emits an abort event. Cleanup/resending is managed centrally by `run` via carry-over; the LLM history is never touched again. */\n private async *emitAbort(reason: string): AsyncGenerator<OmniMessage> {\n const msg = abortEvent(reason);\n yield msg;\n await this.write(msg);\n }\n\n /**\n * Builds the interruption resend content (carry-over, interruption cleanup)\n * based on the LLM's terminal state. Used only for the **exit** cleanup of\n * aborted / failed (reconnect retry doesn't go through here — retry input is assembled by\n * withRetriedTurns, appending `<turn_retried>` with the failed attempt's output, distinct\n * from the user-interruption `<turn_aborted>`):\n * - Model output completed (case A, outcome=completed): AgentHub already committed an\n * assistant turn containing `tool_call`, so it can only be resent as a structured\n * `tool_call_output` to pair with it (cannot flatten, or the already-committed tool_call\n * would be left unanswered and rejected).\n * - Model output incomplete (case B): the `tool_call_output` in this turn's input (paired\n * with the previous completed turn) is kept as-is; the text input and this turn's\n * thinking/text/tool call/result are flattened into a single `<turn_aborted>` plain-text\n * user message.\n * Docs: /docs/agent-loop § \"Interruption and carry-over\".\n */\n private buildCarryOver(attemptInput: OmniMessage[], turn: TurnResult): OmniMessage[] {\n if (turn.outcome.status === \"completed\") {\n // Case A: every **committed** tool_call must have a paired output. If execution was\n // interrupted and some tool_calls were committed but never dispatched/completed, backfill\n // an interrupted-state placeholder for each, avoiding an unanswered tool_use in the next\n // turn that the provider would reject.\n const haveIds = new Set(\n turn.toolOutputs.map((o) => (o.payload as { tool_call_id?: string }).tool_call_id),\n );\n const backfill = turn.toolCalls\n .filter((tc) => !haveIds.has(tc.payload.tool_call_id))\n .map((tc) =>\n toolCallOutput({\n output: \"[interrupted: tool aborted by user]\",\n toolCallId: tc.payload.tool_call_id,\n stopReason: \"aborted\",\n }),\n );\n // Placeholders are sent to the model only and not written to Trace (synthetic carry-over\n // isn't persisted); resumption replay re-synthesizes placeholders as needed to guarantee\n // pairing (pairing fallback). Real outputs were already\n // written when produced.\n return backfill.length ? [...turn.toolOutputs, ...backfill] : turn.toolOutputs;\n }\n return this.flattenCarryOver(\n attemptInput,\n turn.assistantSegments,\n turn.toolCalls,\n turn.toolOutputs,\n );\n }\n\n /**\n * Case B: flattens this attempt's input and its produced content into carry-over. Structured\n * `tool_call_output` in the input (paired with the previous completed turn) is kept as-is;\n * everything else (text input, model thinking/text, this attempt's tool calls/results) is\n * transcribed into a single `<turn_aborted>` plain-text user message (includes all\n * completed and incomplete messages, including partial thinking/text). If the input text is\n * itself already a `<turn_aborted>` block (from a previous attempt or a previous run's\n * carry-over), its content is unwrapped and merged in, keeping a single-level structure.\n *\n * TODO(multimodal): only text input is currently kept — `image_url` / `inline_data` input is\n * lost during flatten (the `<turn_aborted>` structure has no corresponding transcription yet);\n * multimodal carry-over support to be added later.\n */\n private flattenCarryOver(\n attemptInput: OmniMessage[],\n assistantSegments: OmniMessage[],\n toolCalls: OmniMessage<ToolCallPayload>[],\n toolOutputs: OmniMessage[],\n ): OmniMessage[] {\n const structured = attemptInput.filter(\n (m) => (m.payload as { type?: string }).type === \"tool_call_output\",\n );\n const textInputs = attemptInput.filter((m) => (m.payload as { type?: string }).type === \"text\");\n const flattened = userText(\n this.buildTurnAbortedText(textInputs, assistantSegments, toolCalls, toolOutputs),\n );\n // flatten is sent to the model only and not written to Trace (synthetic carry-over isn't\n // persisted): resumption replay resends the discarded turn's **original input** as-is\n // (best-effort), with no dependency on this synthetic message.\n return [...structured, flattened];\n }\n\n /** Transcribes the interrupted turn's input, model thinking/text, and tool calls/results into a single `<turn_aborted>` plain-text block. */\n private buildTurnAbortedText(\n textInputs: OmniMessage[],\n assistantSegments: OmniMessage[],\n toolCalls: OmniMessage<ToolCallPayload>[],\n toolOutputs: OmniMessage[],\n ): string {\n const lines: string[] = [\"<turn_aborted>\"];\n for (const m of textInputs) {\n const t = (m.payload as TextPayload).text;\n // If this text is itself already a synthetic block — a previous run's `<turn_aborted>`,\n // or this turn's reconnect-appended `<turn_retried>` — extract its inner lines and merge\n // them in directly, avoiding layered nesting / unbounded growth (keeping a single-level\n // structure).\n const inner = unwrapSyntheticBlock(t);\n if (inner !== null) {\n if (inner) lines.push(inner);\n } else {\n lines.push(` <user_input>${t}</user_input>`);\n }\n }\n lines.push(...transcribeTurnLines(assistantSegments, toolCalls, toolOutputs));\n lines.push(\"</turn_aborted>\");\n return lines.join(\"\\n\");\n }\n\n /**\n * Assembles the reconnect retry input: the original input is kept as-is (structure and\n * multimodal content preserved), with a `<turn_retried>` text appended at the end carrying\n * each failed attempt's thinking/text and tool calls/results produced so far; if nothing has\n * been produced yet, it's just the original input. The synthetic message is sent to the model\n * only and not written to Trace (same rule as flatten carry-over).\n * Docs: /docs/agent-loop § \"Automatic reconnect\".\n */\n private withRetriedTurns(input: OmniMessage[], failedTurns: TurnResult[]): OmniMessage[] {\n const lines = failedTurns.flatMap((t) =>\n transcribeTurnLines(t.assistantSegments, t.toolCalls, t.toolOutputs),\n );\n if (lines.length === 0) return input;\n return [...input, userText([\"<turn_retried>\", ...lines, \"</turn_retried>\"].join(\"\\n\"))];\n }\n\n /**\n * Trace writes are **best-effort**: observability should never interrupt the ReAct\n * loop, so write failures only warn rather than throw. The first write after compaction first\n * performs the deferred Trace rotation: splitting the file and opening it with session_meta.\n */\n private async write(msg: OmniMessage): Promise<void> {\n if (!this.deps.trace) return;\n if (this.pendingTraceRotation) {\n this.pendingTraceRotation = false;\n try {\n if (this.deps.trace.rotate) await this.deps.trace.rotate();\n if (this.deps.sessionMeta) await this.deps.trace.write(this.deps.sessionMeta);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[trace] rotate failed: ${message}\\n`);\n }\n }\n try {\n await this.deps.trace.write(msg);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[trace] write failed: ${message}\\n`);\n }\n }\n}\n\n/**\n * If the text is an engine-synthesized whole block (`<turn_aborted>` or `<turn_retried>`),\n * strips the outer tags and returns the inner lines (may be an empty string); otherwise returns\n * null. Both kinds of synthetic blocks use identical inner markup (thinking/text/tool_call/\n * tool_call_output), so it can be merged directly into a new block while keeping a single-level\n * structure.\n */\nfunction unwrapSyntheticBlock(text: string): string | null {\n const m = /^<(turn_aborted|turn_retried)>\\n?([\\s\\S]*?)\\n?<\\/\\1>\\s*$/.exec(text);\n return m ? m[2]! : null;\n}\n\n/** Transcribes the model's produced thinking/text and tool calls/results into tagged lines (shared by `<turn_aborted>`/`<turn_retried>`). */\nfunction transcribeTurnLines(\n assistantSegments: OmniMessage[],\n toolCalls: OmniMessage<ToolCallPayload>[],\n toolOutputs: OmniMessage[],\n): string[] {\n const lines: string[] = [];\n // The model's produced thinking/text (including partial segments finalized on interruption),\n // written in production order.\n for (const seg of assistantSegments) {\n const p = seg.payload as { type?: string };\n if (p.type === \"thinking\") {\n lines.push(` <thinking>${(seg.payload as ThinkingPayload).thinking}</thinking>`);\n } else if (p.type === \"text\") {\n lines.push(` <text>${(seg.payload as TextPayload).text}</text>`);\n }\n }\n for (const tc of toolCalls) {\n const p = tc.payload;\n lines.push(` <tool_call name=\"${p.name}\" id=\"${p.tool_call_id}\">${p.arguments}</tool_call>`);\n }\n for (const out of toolOutputs) {\n const p = out.payload as ToolCallOutputPayload;\n lines.push(\n ` <tool_call_output id=\"${p.tool_call_id}\" status=\"${p.stop_reason ?? \"completed\"}\">${p.output}</tool_call_output>`,\n );\n }\n return lines;\n}\n\n/**\n * Extracts the summary within `<summary></summary>` from compaction output; when the tag is\n * missing, leniently uses the entire output as-is (not treated as a failure). Also used by\n * Session resumption's \"compaction closure\" replay to\n * reconstruct the `<context_summary>` pending input from the old Trace's compaction output.\n */\nexport function extractSummary(raw: string): string {\n const match = /<summary>([\\s\\S]*?)<\\/summary>/.exec(raw);\n return (match ? match[1]! : raw).trim();\n}\n","/**\n * Session creation helpers (used by `agent.createSession` for assembly, not exported\n * via the barrel): Session id generation, runtime environment fields, and temp\n * Workspace creation.\n */\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { randomBytes, randomUUID } from \"node:crypto\";\n\nimport { formatLocalDate } from \"./dates.js\";\nimport type { SessionEnvironmentValues } from \"../state/agent-state.js\";\nimport { workspacesDir } from \"../state/index.js\";\nimport { userText } from \"../omnimessage/index.js\";\nimport type { OmniMessage } from \"../omnimessage/index.js\";\n\n/** Session runtime environment fields: the placeholder substitution values for `assembleSystemPrompt`; producer and consumer share the same type. */\nexport type SessionEnvironment = SessionEnvironmentValues;\n\n/** Generate a Session id of the form `session-YYYY-MM-DD-HH-mm-ss-<8-hex>` (local timezone, zero-padded: 4-digit year, 2 digits for the rest; hex from randomUUID). */\nexport function formatSessionId(date: Date = new Date()): string {\n const pad = (n: number) => n.toString().padStart(2, \"0\");\n const ts =\n `${formatLocalDate(date)}` +\n `-${pad(date.getHours())}-${pad(date.getMinutes())}-${pad(date.getSeconds())}`;\n const hex = randomUUID().replace(/-/g, \"\").slice(0, 8);\n return `session-${ts}-${hex}`;\n}\n\n/**\n * Generate this Session's runtime environment fields (injected via specific\n * placeholders in the system prompt).\n * This is system-generated runtime context, not sourced from Agent State / Workspace files.\n */\nexport function sessionEnvironment(\n workspaceDir: string,\n sessionId: string,\n ids: { agentId: string; projectDir: string },\n date = new Date(),\n): SessionEnvironment {\n return {\n sessionId,\n cwd: workspaceDir,\n agentId: ids.agentId,\n projectDir: ids.projectDir,\n platform: process.platform,\n osVersion: getOsVersion(),\n date: formatLocalDate(date),\n };\n}\n\nfunction getOsVersion(): string {\n // os.* is a stable built-in API that normally doesn't throw; but this function only\n // builds a single line of environment info for the system prompt, so it's not worth\n // letting an exception take down createSession — fall back to \"unknown\" instead.\n try {\n if (process.platform === \"win32\") {\n return `${os.version()} ${os.release()}`;\n }\n return `${os.type()} ${os.release()}`;\n } catch {\n return \"unknown\";\n }\n}\n\n/** The 8-hex space is 2^32, so the odds of consecutive collisions are negligible; the cap only guards against an infinite loop caused by an abnormal filesystem. */\nconst MAX_TMP_ID_ATTEMPTS = 16;\n\n/**\n * Create a temporary Workspace under `<agent>/workspaces/<workspace_id>`, where the\n * directory name is the workspace_id, shaped like `tmp-<8hex>`; if it collides with\n * an existing directory, regenerate the id. No symlinks are created inside the Workspace:\n * the model composes absolute paths (to Agent State, scratchpad, etc.) directly from the\n * Environment placeholders (Project Dir / Agent ID) in the system prompt.\n */\nexport async function createTempWorkspace(\n root: string,\n projectId: string,\n agentId: string,\n): Promise<string> {\n const base = workspacesDir(root, projectId, agentId);\n await fs.mkdir(base, { recursive: true });\n // The final directory must use a non-recursive mkdir: recursive mkdir succeeds\n // silently when the directory already exists, which would put a new Session into\n // an existing temp Workspace; EEXIST means an id collision, so retry with a new id.\n for (let attempt = 0; attempt < MAX_TMP_ID_ATTEMPTS; attempt++) {\n const dir = path.join(base, `tmp-${randomUUID().slice(0, 8)}`);\n try {\n await fs.mkdir(dir);\n return dir;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n throw new Error(\n `failed to allocate a unique temp workspace id under ${base} after ${MAX_TMP_ID_ATTEMPTS} attempts`,\n );\n}\n\n/** Maps a data URL's mime type to a file extension on disk; unknown mimes use bin (the image-reading tool sniffs the magic bytes and doesn't rely on the extension). */\nconst MIME_TO_EXT: Record<string, string> = {\n \"image/png\": \"png\",\n \"image/jpeg\": \"jpg\",\n \"image/gif\": \"gif\",\n \"image/webp\": \"webp\",\n};\n\n/**\n * Input conversion for when the session model doesn't support images: image messages\n * in the `run` input are written to disk as files (base64 data URLs are saved to the\n * session scratchpad; http(s) URLs are referenced\n * as-is), and the path/URL is appended to the user text (an `[attached image: …]`\n * line); the image message itself is removed from the input — the model views it by\n * path via describe_image (read on its behalf by a vision model), and images never\n * enter that session's history directly.\n * Returns the input unchanged when there are no images; an image that can't be\n * parsed is replaced with an explanatory line rather than silently dropped.\n */\nexport async function imagesToScratchpadPaths(\n input: OmniMessage[],\n dir: string,\n): Promise<OmniMessage[]> {\n const isImage = (m: OmniMessage): boolean =>\n (m.payload as { type?: string }).type === \"image_url\";\n if (!input.some(isImage)) return input;\n\n const lines: string[] = [];\n for (const msg of input) {\n if (!isImage(msg)) continue;\n const url = (msg.payload as { image_url?: string }).image_url ?? \"\";\n if (/^https?:\\/\\//i.test(url)) {\n lines.push(`[attached image: ${url}]`);\n continue;\n }\n const match = /^data:([^;,]+);base64,(.+)$/s.exec(url);\n if (!match) {\n lines.push(\"[an attached image could not be saved and was dropped]\");\n continue;\n }\n await fs.mkdir(dir, { recursive: true });\n const ext = MIME_TO_EXT[match[1]!.toLowerCase()] ?? \"bin\";\n // Filename = upload-<8 random hex chars> (same convention as project-<8hex>; the\n // prefix distinguishes model-generated temp files).\n // \"wx\" flag does exclusive creation to avoid name collisions: on the rare chance of a collision, retry with a new random value.\n let file: string;\n for (;;) {\n file = path.join(dir, `upload-${randomBytes(4).toString(\"hex\")}.${ext}`);\n try {\n await fs.writeFile(file, Buffer.from(match[2]!, \"base64\"), { flag: \"wx\" });\n break;\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code !== \"EEXIST\") throw err;\n }\n }\n lines.push(`[attached image: ${file}]`);\n }\n\n // Concatenation: the path lines are appended after the last user text message; if the input is images only, add a plain path-only text message.\n const rest = input.filter((m) => !isImage(m));\n const suffix = lines.join(\"\\n\");\n const lastTextIdx = rest.findLastIndex((m) => {\n const p = m.payload as { type?: string; role?: string };\n return p.type === \"text\" && p.role === \"user\";\n });\n if (lastTextIdx === -1) return [...rest, userText(suffix)];\n return rest.map((m, i) => {\n if (i !== lastTextIdx) return m;\n const p = m.payload as { type: string; role: string; text: string };\n return { ...m, payload: { ...p, text: `${p.text}\\n\\n${suffix}` } } as OmniMessage;\n });\n}\n","/**\n * Session title generation: an **out-of-band, one-off request** that generates\n * a short title from the first-turn conversation text.\n *\n * Called by `session.generateTitle()`: sends one request using the bare LLM for the session's\n * Model (no tools, no system prompt, thinking off), without writing history or Trace. Material\n * defaults to what the Session self-captures during run (see session.ts); this module is only\n * responsible for the prompt format, driving the one-off request, and sanitizing the result —\n * when to generate a title and where to store it is decided by the host (Web server / CLI).\n */\nimport { userText } from \"./omnimessage/index.js\";\nimport type {\n OmniMessage,\n TextPayload,\n TokenCounts,\n TokenUsagePayload,\n} from \"./omnimessage/index.js\";\nimport type { LLMInterface } from \"./interfaces.js\";\n\n/** Cap on conversation text spliced into the title request (user/model each truncated separately, to control cost). */\nconst EXCERPT_MAX_CHARS = 2000;\n/** Cap on title length (fallback truncation for when the model occasionally ignores the constraint). */\nconst TITLE_MAX_CHARS = 30;\n\nexport interface SessionTitleResult {\n /** The sanitized title; null when material is insufficient, the request fails, or the output is empty. */\n title: string | null;\n /** Token consumption for this request (accumulated token_usage.request); null if no request occurred or there's no usage. */\n usage: TokenCounts | null;\n}\n\n/**\n * Assembles the title-generation Prompt (exported for host/test assertion use). Uses English\n * instructions to avoid polluting the title's language, and requires the **title to be in the\n * same language as the conversation** (English conversation gets an English title, Chinese\n * conversation gets a Chinese title); when assistant material is empty, it relies on the user\n * request alone.\n */\nexport function buildTitlePrompt(userExcerpt: string, assistantExcerpt: string): string {\n const clip = (s: string) => (s.length > EXCERPT_MAX_CHARS ? s.slice(0, EXCERPT_MAX_CHARS) : s);\n const lines = [\n \"Generate a concise title for the conversation below.\",\n \"Rules:\",\n \"- Write the title in the SAME language the user is using.\",\n \"- Keep it short: at most 6 words, or ~16 characters for CJK.\",\n \"- Output ONLY the title text — no quotes, no trailing punctuation, no explanation.\",\n \"\",\n \"[User]\",\n clip(userExcerpt),\n ];\n if (assistantExcerpt.trim()) {\n lines.push(\"\", \"[Assistant]\", clip(assistantExcerpt));\n }\n return lines.join(\"\\n\");\n}\n\n/** Sanitizes model output into a title: strips leading/trailing quotes/brackets and trailing punctuation (until stable), collapses whitespace, and truncates if too long; returns null for an empty result. */\nexport function sanitizeTitle(raw: string): string | null {\n let t = raw.replace(/\\s+/g, \" \").trim();\n // Stripping quotes can expose more punctuation underneath (or vice versa), so strip repeatedly until stable.\n for (let prev = \"\"; prev !== t;) {\n prev = t;\n t = t\n .replace(/^[\"'“”‘’「」『』《》〈〉【】()()\\s]+/, \"\")\n .replace(/[\"'“”‘’「」『』《》〈〉【】()()\\s]+$/, \"\")\n .replace(/[。..!!??;;,,、::]+$/, \"\")\n .trim();\n }\n if (!t) return null;\n return t.length > TITLE_MAX_CHARS ? t.slice(0, TITLE_MAX_CHARS) : t;\n}\n\n/**\n * Drives a single title-generation request: collects model text and token_usage, and resolves\n * based on the outcome. Generation only requires user material (assistant material may be\n * empty — a pure tool-only turn can still get a title); no request is sent if user material is\n * empty; `title` is null if the request doesn't complete (any usage already produced is still\n * returned).\n */\nexport async function generateTitleWithLLM(\n llm: LLMInterface,\n args: { userText: string; assistantText: string; signal?: AbortSignal },\n): Promise<SessionTitleResult> {\n if (!args.userText.trim()) {\n return { title: null, usage: null };\n }\n const prompt = buildTitlePrompt(args.userText, args.assistantText);\n const gen = llm.streamGenerate({\n newMessages: [userText(prompt)],\n ...(args.signal ? { signal: args.signal } : {}),\n });\n let collected = \"\";\n let usage: TokenCounts | null = null;\n for (;;) {\n const step = await gen.next();\n if (step.done) {\n if (step.value.status !== \"completed\") return { title: null, usage };\n break;\n }\n const msg = step.value;\n if (isAssistantText(msg)) collected += msg.payload.text;\n if (isTokenUsage(msg)) {\n const r = msg.payload.request;\n usage = usage\n ? {\n cache_read: usage.cache_read + r.cache_read,\n cache_write: usage.cache_write + r.cache_write,\n output: usage.output + r.output,\n total: usage.total + r.total,\n }\n : { ...r };\n }\n }\n return { title: sanitizeTitle(collected), usage };\n}\n\nfunction isAssistantText(msg: OmniMessage): msg is OmniMessage<TextPayload> {\n const payload = msg.payload as { type?: string; role?: string };\n return msg.type === \"model_msg\" && payload.type === \"text\" && payload.role === \"assistant\";\n}\n\nfunction isTokenUsage(msg: OmniMessage): msg is OmniMessage<TokenUsagePayload> {\n return msg.type === \"event_msg\" && (msg.payload as { type?: string }).type === \"token_usage\";\n}\n","/**\n * Session — a continuous conversation context under the same Agent and Workspace.\n *\n * Human is the SDK's input/output boundary: there is no \"Human\n * implementation/interface\".\n * - Input: the OmniMessage list (Prompt) passed to `run(newMessages, opts?)`, plus the abort\n * signal `signal` and the per-call approval callback `approve` in `opts`;\n * - Output: `run` streams OmniMessage via an async generator.\n *\n * Approval is a **within-turn interaction**: as soon as a tool_call finishes streaming, `approve`\n * is requested immediately, and it executes if allowed. Approvals for multiple tools happen one\n * at a time, but execution doesn't block the generation/approval of subsequent tools (execution\n * can overlap). GenerativeModel maintains history across turns/Tasks. A Task ends when a turn no\n * longer produces a tool_call (final reply).\n *\n * Rendering tool calls is not Session/core's responsibility: the CLI / Web frontend renders it\n * from the streamed OmniMessage on its own.\n * Docs: /docs/agent-loop; /docs/interfaces § \"The Human boundary\".\n */\nimport { sessionMeta } from \"./omnimessage/index.js\";\nimport type { OmniMessage, SessionMetaPayload, TokenCounts } from \"./omnimessage/index.js\";\nimport { imagesToScratchpadPaths } from \"./internal/session-support.js\";\nimport type { EnvironmentInterface, LLMInterface, ToolPermission } from \"./interfaces.js\";\nimport { generateTitleWithLLM } from \"./session-title.js\";\nimport type { SessionTitleResult } from \"./session-title.js\";\nimport { ContextEngine } from \"./engine/context-engine.js\";\nimport type {\n CompactAvailability,\n CompactionSettings,\n EngineInitialState,\n RunOptions,\n TraceSink,\n} from \"./engine/context-engine.js\";\n\nexport interface SessionConfig {\n /** Session metadata (session_id / provider / model_id / model_context_window / system_prompt / tools / thinking_level / agent_state / workspace). */\n meta: SessionMetaPayload;\n llm: LLMInterface;\n environment: EnvironmentInterface;\n trace?: TraceSink;\n maxTurns?: number;\n /** Creates a new LLM object after compaction (carries over the Session's accumulated Token count); context compaction is unavailable if not provided. */\n createLLM?: (sessionTokens: TokenCounts) => LLMInterface;\n /**\n * Factory for the bare LLM used by out-of-band, one-off requests (same Model/credential as\n * the session; no tools, no system prompt, thinking off): used for meta-requests such as\n * `generateTitle`; if not provided, `generateTitle` returns null.\n */\n createBareLLM?: () => LLMInterface;\n /** Context compaction settings (defaults are filled in by the composition layer); only takes effect when provided together with `createLLM`. */\n compaction?: CompactionSettings;\n /** Session resume: `session_meta` is already in the original Trace file, so it isn't written again on the first run (avoids duplication). */\n metaAlreadyWritten?: boolean;\n /** Session resume: the engine's initial state derived from Trace replay (carry-over / accumulated stats, etc.). */\n initialEngineState?: EngineInitialState;\n /** Session resume: the full historical messages of the current context (for rendering, including interrupted turns and their markers), for frontend display. */\n resumedHistory?: OmniMessage[];\n /**\n * Set when the session's model doesn't support images (the composition layer decides this via\n * ModelEntry.vision): images in `run` input are saved to this directory (the session's\n * scratchpad), and the path is appended to the user text instead — the model views the image\n * via describe_image, and images never enter the session history directly (some providers\n * return a 400 outright on image input).\n */\n inputImagesDir?: string;\n}\n\n/** Cap on captured title material (chars per side, matching buildTitlePrompt's truncation); stops accumulating once exceeded. */\nconst TITLE_MATERIAL_LIMIT = 2000;\n\n/**\n * Accumulates title material: the body text of complete text messages from the main session\n * (no origin) — thinking and tool calls naturally don't count — and stops once the cap is hit.\n */\nfunction appendTitleText(base: string, msg: OmniMessage, role: \"user\" | \"assistant\"): string {\n if (base.length >= TITLE_MATERIAL_LIMIT) return base;\n if (msg.origin && msg.origin.length > 0) return base;\n const p = msg.payload as { type?: string; role?: string; text?: string };\n if (msg.type !== \"model_msg\" || p.type !== \"text\" || p.role !== role || !p.text) return base;\n return base ? `${base}\\n${p.text}` : p.text;\n}\n\nexport class Session {\n readonly sessionId: string;\n /** The session model's provider group (paired with `modelId` to form the model reference). */\n readonly provider: string;\n /** The session model's upstream model_id (the request id sent to AgentHub). */\n readonly modelId: string;\n readonly workspaceDir: string;\n /** Session resume: the full historical messages of the current context (for rendering); undefined for a non-resumed Session. */\n readonly resumedHistory?: OmniMessage[];\n\n private readonly engine: ContextEngine;\n private readonly environment: EnvironmentInterface;\n private readonly trace?: TraceSink;\n private readonly meta: OmniMessage;\n private readonly createBareLLM?: () => LLMInterface;\n private readonly inputImagesDir?: string;\n private metaWritten = false;\n /** Title material (used by `generateTitle` as the default): the user input and model body text of the first Task that contains user text. */\n private titleUserText = \"\";\n private titleAssistantText = \"\";\n /** Material-frozen flag: becomes true once the first Task containing user text finishes; subsequent runs stop accumulating. */\n private titleMaterialFrozen = false;\n\n constructor(config: SessionConfig) {\n this.sessionId = config.meta.session_id;\n this.provider = config.meta.provider;\n this.modelId = config.meta.model_id;\n this.workspaceDir = config.meta.workspace;\n this.environment = config.environment;\n this.trace = config.trace;\n this.meta = sessionMeta(config.meta);\n this.metaWritten = config.metaAlreadyWritten ?? false;\n if (config.resumedHistory) this.resumedHistory = config.resumedHistory;\n if (config.createBareLLM) this.createBareLLM = config.createBareLLM;\n if (config.inputImagesDir) this.inputImagesDir = config.inputImagesDir;\n this.engine = new ContextEngine({\n llm: config.llm,\n environment: config.environment,\n ...(config.trace ? { trace: config.trace } : {}),\n ...(config.maxTurns !== undefined ? { maxTurns: config.maxTurns } : {}),\n // Context compaction: new LLM factory + resolved settings + writes session_meta at the start of the new Trace file after splitting.\n ...(config.createLLM ? { createLLM: config.createLLM } : {}),\n ...(config.compaction ? { compaction: config.compaction } : {}),\n ...(config.initialEngineState ? { initialState: config.initialEngineState } : {}),\n sessionMeta: this.meta,\n });\n }\n\n /**\n * Runs a Task to completion and streams out OmniMessage. `newMessages` is this call's Prompt\n * (only the newly added input); `opts` carries the abort signal `signal` and the per-call\n * approval callback `approve` (the engine calls it once per tool_call within a turn).\n * On the first run, `session_meta` is written to the Trace first.\n *\n * A single `run` automatically drives the whole ReAct loop: consuming the LLM stream,\n * approving and executing tools one at a time, feeding results back for the next turn,\n * until a turn no longer produces a tool_call (Task ends) or it's aborted.\n * Docs: /docs/agent-loop § \"The loop at a glance\".\n */\n async *run(newMessages: OmniMessage[], opts?: RunOptions): AsyncGenerator<OmniMessage> {\n // Model doesn't support images: input images are saved to disk first (session scratchpad),\n // then the path is appended to the text before it reaches the engine/Trace.\n if (this.inputImagesDir) {\n newMessages = await imagesToScratchpadPaths(newMessages, this.inputImagesDir);\n }\n await this.ensureMetaWritten();\n // Self-captures title material (the title is derived from the first-turn\n // conversation text): while material isn't frozen yet, collect this call's user text and\n // the produced model text; freezes once the first Task containing user text finishes, so\n // the title reflects the start of the conversation.\n const capture = !this.titleMaterialFrozen;\n if (capture) {\n for (const m of newMessages) {\n this.titleUserText = appendTitleText(this.titleUserText, m, \"user\");\n }\n }\n for await (const msg of this.engine.run(newMessages, opts)) {\n if (capture) {\n this.titleAssistantText = appendTitleText(this.titleAssistantText, msg, \"assistant\");\n }\n yield msg;\n }\n if (capture && this.titleUserText.trim()) this.titleMaterialFrozen = true;\n }\n\n /**\n * User-initiated request to compact context (e.g. a CLI command): reuses the automatic\n * compaction flow but skips the threshold check (reason=manual). Only callable at Task\n * boundaries (between runs); streams out paired `compaction` events. The summarize digest\n * becomes the prefix of the next `run`'s input (merged with the next user Prompt). A no-op\n * if compaction isn't configured.\n * Docs: /docs/agent-loop § \"Compaction\".\n */\n async *compact(opts?: { signal?: AbortSignal }): AsyncGenerator<OmniMessage> {\n yield* this.engine.compact(opts);\n }\n\n /**\n * Whether compaction is possible, and why not if not (see ContextEngine.compactability).\n * When the result isn't `ok`, `compact()` is a no-op and yields no messages — callers should\n * give feedback based on this rather than triggering a silent, fruitless compaction.\n */\n compactability(): CompactAvailability {\n return this.engine.compactability();\n }\n\n /** Writes `session_meta` to the Trace before the first run/compaction; best-effort — failure doesn't interrupt the run. */\n private async ensureMetaWritten(): Promise<void> {\n if (this.metaWritten) return;\n if (this.trace) {\n try {\n await this.trace.write(this.meta);\n } catch (err) {\n const message = err instanceof Error ? err.message : String(err);\n process.stderr.write(`[trace] session_meta write failed: ${message}\\n`);\n }\n }\n this.metaWritten = true;\n }\n\n /**\n * Out-of-band, one-off request that generates a short title from the first-turn conversation\n * text: sends one request using the bare LLM for the session's Model (no\n * tools, no system prompt, thinking off), **without writing history or Trace**. Material\n * defaults to the first Task text self-captured by the Session (user input and model body\n * text collected during run; thinking and tool calls don't count), so callers don't need to\n * supply it; `material` can override this (e.g. when a host generates a title for a\n * sub-session — the material is that sub-session's own conversation). `title` is null if the\n * material is empty, the request fails, or the composition layer didn't supply a bare LLM\n * factory. Token consumption is returned via `usage` for the host to account for.\n * Docs: /docs/agent-loop § \"Side channels\".\n */\n async generateTitle(args?: {\n /** Material override; defaults to the Session's self-captured material. */\n material?: { userText: string; assistantText: string };\n signal?: AbortSignal;\n }): Promise<SessionTitleResult> {\n if (!this.createBareLLM) return { title: null, usage: null };\n const material = args?.material ?? {\n userText: this.titleUserText,\n assistantText: this.titleAssistantText,\n };\n return generateTitleWithLLM(this.createBareLLM(), {\n ...material,\n ...(args?.signal ? { signal: args.signal } : {}),\n });\n }\n\n /** Queries a tool's permission level (for the frontend to determine permission mode); returns undefined for unknown tools. */\n toolPermission(name: string): ToolPermission | undefined {\n return this.environment.toolPermission(name);\n }\n\n /** This Session's session_meta message (used e.g. by host tools to forward nested-session metadata to a parent session). */\n get metaMessage(): OmniMessage {\n return this.meta;\n }\n\n /**\n * Releases runtime resources held by the Session: kills long-running command sessions\n * managed by the Environment. The host calls this when the Session ends (CLI exit, Web\n * session close) to avoid leaking background processes into the host process's lifetime.\n * Optional, idempotent.\n */\n dispose(): void {\n this.environment.dispose?.();\n }\n}\n","/**\n * Agent and the `createAgent` entry point.\n *\n * `createAgent` is the unified way to create/load an Agent: it initializes Agent State\n * if the directory is empty, otherwise loads by agentId.\n * An Agent has exactly one Agent State and can run multiple times; the\n * Workspace is determined when a Session is created.\n */\nimport fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport {\n assertValidId,\n assembleSystemPrompt,\n buildToolConfig,\n selectBuiltinToolsForModel,\n DEFAULT_COMPACTION_PROMPT,\n formatModelRef,\n getModel,\n listInstalledSkills,\n loadAgentVault,\n loadOrInitAgentState,\n loadProjectConfig,\n projectDir,\n resolveModelRef,\n scratchpadDir,\n systemConfigPath,\n tracesDir,\n type AgentState,\n type ModelRef,\n type ProjectConfig,\n} from \"./state/index.js\";\nimport { GenerativeModel, ToolCallIdAllocator } from \"./llm/index.js\";\nimport { Environment } from \"./environment/index.js\";\nimport {\n Writer,\n findLatestTraceFile,\n latestSessionId as latestTraceSessionId,\n readTraceTolerant,\n resumeTrace,\n} from \"./trace/index.js\";\nimport { Session } from \"./session.js\";\nimport {\n createTempWorkspace,\n formatSessionId,\n sessionEnvironment,\n} from \"./internal/session-support.js\";\nimport { userText, withOrigin } from \"./omnimessage/index.js\";\nimport type {\n MessageOrigin,\n OmniMessage,\n TokenCounts,\n ToolCallPayload,\n} from \"./omnimessage/index.js\";\nimport { SUBAGENT_NAME } from \"./environment/tools/run-subagent.js\";\nimport { INPUT_SUBAGENT_NAME } from \"./environment/tools/input-subagent.js\";\nimport type { CompactionSettings } from \"./engine/context-engine.js\";\nimport type {\n GenerativeModelConfig,\n SubagentRunner,\n ToolDefinition,\n VisionDescriberService,\n} from \"./interfaces.js\";\nimport type { ModelEntry } from \"./state/index.js\";\n\n/**\n * Maximum subagent spawn depth. Currently capped at 1 level (a subagent cannot spawn\n * another subagent); the depth mechanism is designed to support multiple levels —\n * raise this constant to allow deeper nesting.\n */\nconst MAX_SUBAGENT_DEPTH = 1;\n\nexport interface CreateAgentOptions {\n agentId?: string;\n projectId?: string;\n /** Local data root directory; defaults to `resolveRoot()` (PENGUIN_HOME or ~/.penguin/data). */\n root?: string;\n}\n\nexport interface CreateSessionOptions {\n /** Workspace for this run; if unspecified, a temporary Workspace is created under the Agent directory. */\n workspaceDir?: string;\n /** Model used for this Session (upstream model_id); if unspecified, uses the Project's default Model. */\n modelId?: string;\n /**\n * Provider grouping for `modelId` (a paired reference); if omitted, resolved via\n * `resolveModelRef` semantics — `model_id` only resolves if it is a globally unique\n * exact match in the config; zero or multiple matches produce a clear error.\n */\n provider?: string;\n /** Explicit credentials; if unspecified, falls back to credentials in the Project config, then to AgentHub reading environment variables. */\n apiKey?: string;\n baseUrl?: string;\n /** Internal use: this Session's depth in the subagent spawn chain (0 at the top level), used to cap spawn depth. */\n subagentDepth?: number;\n}\n\nexport interface ResumeSessionOptions {\n /** Id of the Session to resume. */\n sessionId: string;\n /** Explicit credentials; if unspecified, falls back to credentials in the Project config, then to AgentHub reading environment variables. */\n apiKey?: string;\n baseUrl?: string;\n}\n\n/**\n * Effective compaction threshold: capped at 75% of the model's `context_window` —\n * the threshold must stay well below the hard window limit, otherwise small-window\n * models get rejected by the provider (a non-retryable 400) before compaction even\n * triggers, and the compaction request itself (old context + prompt + summary output)\n * also needs headroom. Not clamped when `<=0` (disabled) or the window is unknown.\n */\nexport function effectiveMaxContextLength(configured: number, contextWindow: unknown): number {\n if (configured <= 0) return configured;\n if (typeof contextWindow !== \"number\") return configured;\n return Math.min(configured, Math.floor(contextWindow * 0.75));\n}\n\n/** Create or load an Agent. */\nexport async function createAgent(opts: CreateAgentOptions = {}): Promise<Agent> {\n const state = await loadOrInitAgentState(opts);\n const projectConfig = await loadProjectConfig(state.root, state.projectId);\n return new Agent(state, projectConfig);\n}\n\nexport class Agent {\n constructor(\n readonly state: AgentState,\n readonly projectConfig: ProjectConfig,\n ) {}\n\n /**\n * Create a Session in the specified (or a temporary) Workspace.\n * Docs: /docs/sessions-and-traces § \"Run model\".\n */\n async createSession(opts: CreateSessionOptions = {}): Promise<Session> {\n // Model is validated first (before creating the Workspace, so failure leaves no\n // temp directory behind): the reference must resolve to an entry in the Project\n // config (the (provider, model_id) pair is the unique key); a reference\n // outside the config throws immediately rather than passing silently — otherwise\n // credentials, pricing, and the context window would all be unavailable.\n if (opts.modelId === undefined && opts.provider !== undefined) {\n throw new Error(\n \"指定了 provider 却未指定 modelId:模型引用须成对给出(provider 不能单独使用)。\",\n );\n }\n let ref: ModelRef;\n if (opts.modelId !== undefined) {\n // The only entry point for resolving an \"omitted provider\" reference (resolveModelRef): three branches — unique match / zero matches / ambiguous.\n ref = resolveModelRef(this.projectConfig, opts.modelId, opts.provider);\n } else if (this.projectConfig.default_model) {\n ref = this.projectConfig.default_model;\n } else {\n throw new Error(\n \"未指定 modelId,且 Project 配置中没有 default_model。请用 `penguin config model add/default` 设置默认模型。\",\n );\n }\n const modelEntry = getModel(this.projectConfig, ref);\n if (!modelEntry) {\n throw new Error(\n `Model 不在 Project 配置中:${formatModelRef(ref)}。请用 \\`penguin config model list\\` 查看已配置的模型,或用 \\`penguin config model add\\` 添加。`,\n );\n }\n // Credentials are inlined on the model entry (single config file); an\n // explicit argument takes priority, falling back to AgentHub reading env vars\n // when both are absent.\n const apiKey = opts.apiKey ?? modelEntry.api_key;\n const baseUrl = opts.baseUrl ?? modelEntry.base_url;\n\n // An explicit Workspace must already exist as a directory: if it\n // doesn't, throw rather than auto-create (to avoid a typo silently working in\n // the wrong location); a temp Workspace is only created when unspecified.\n let workspaceDir: string;\n if (opts.workspaceDir) {\n workspaceDir = path.resolve(opts.workspaceDir);\n let stat;\n try {\n stat = await fs.stat(workspaceDir);\n } catch {\n throw new Error(\n `Workspace 不存在:${workspaceDir}。请指定一个已存在的目录,或不指定 Workspace 以使用临时目录。`,\n );\n }\n if (!stat.isDirectory()) {\n throw new Error(`Workspace 不是目录:${workspaceDir}。`);\n }\n } else {\n workspaceDir = await createTempWorkspace(\n this.state.root,\n this.state.projectId,\n this.state.agentId,\n );\n }\n const sessionId = formatSessionId();\n const subagentDepth = opts.subagentDepth ?? 0;\n\n // Agent-level vault (agent_state/.vault.toml) and installed Skills: read the current values each time a Session is created.\n const vault = await loadAgentVault(this.state.root, this.state.projectId, this.state.agentId);\n const installedSkills = await listInstalledSkills(\n this.state.root,\n this.state.projectId,\n this.state.agentId,\n );\n\n // The assembled system prompt goes both to the LLM and into session_meta (so the\n // Trace can audit the actual effective value). The vault only injects **key names**\n // into the prompt (so the model knows which API keys are available); values only\n // go into the subprocess environment. Skills only inject metadata (name and\n // description); the model reads the body on demand via shell.\n const systemPrompt = assembleSystemPrompt(\n this.state,\n sessionEnvironment(workspaceDir, sessionId, {\n agentId: this.state.agentId,\n projectDir: projectDir(this.state.root, this.state.projectId),\n }),\n Object.keys(vault),\n installedSkills,\n );\n\n const rt = await this.buildRuntime({\n workspaceDir,\n modelEntry,\n apiKey,\n baseUrl,\n systemPrompt,\n subagentDepth,\n vault,\n });\n\n const trace = new Writer({\n tracesDir: tracesDir(this.state.root, this.state.projectId, this.state.agentId),\n sessionId,\n });\n\n return new Session({\n meta: {\n session_id: sessionId,\n provider: modelEntry.provider,\n model_id: modelEntry.model_id,\n model_context_window: modelEntry.context_window ?? \"unknown\",\n system_prompt: systemPrompt,\n tools: rt.tools,\n thinking_level: this.state.systemConfig.model?.thinking_level ?? \"default\",\n agent_state: this.state.stateDir,\n workspace: workspaceDir,\n },\n llm: rt.llm,\n environment: rt.environment,\n trace,\n createLLM: rt.createLLM,\n createBareLLM: rt.createBareLLM,\n compaction: rt.compaction,\n // Model doesn't support images: input images are written to the session scratchpad and their paths appended to the text (viewed via describe_image).\n ...(modelEntry.vision === false\n ? {\n inputImagesDir: path.join(\n scratchpadDir(this.state.root, this.state.projectId, this.state.agentId),\n sessionId,\n ),\n }\n : {}),\n // Max turns comes from the Agent's system_config (runtime parameters belong to the Agent config).\n ...(this.state.systemConfig.max_turns !== undefined\n ? { maxTurns: this.state.systemConfig.max_turns }\n : {}),\n });\n }\n\n /**\n * Resume an existing Session and continue the conversation.\n *\n * The resume source is the Session's **latest-index** Trace file: runtime config is\n * read from its `session_meta` (Model, the original system prompt text, and the\n * Workspace all carry over from the original Session and cannot be changed), while\n * tools and Environment are reassembled from the current Agent State. The replayed,\n * already-committed history is injected once via AgentHub's setHistory (used only on\n * resume); any leftover input is rebuilt as carry-over (paired fallback placeholders\n * are synthesized in memory only, never written to the Trace). Messages after resume\n * continue in the original Trace file (the file follows the context, not the date),\n * and Token / turn-count stats carry over from their original values.\n * Docs: /docs/sessions-and-traces § \"Session recovery\".\n */\n async resumeSession(opts: ResumeSessionOptions): Promise<Session> {\n const { sessionId } = opts;\n const dir = tracesDir(this.state.root, this.state.projectId, this.state.agentId);\n const located = await findLatestTraceFile(dir, sessionId);\n if (!located) {\n throw new Error(`Session 不存在:${sessionId}(在 ${dir} 下未找到对应 Trace 文件)。`);\n }\n const resumed = resumeTrace(await readTraceTolerant(located.path));\n if (!resumed.meta) {\n throw new Error(`Trace 缺少 session_meta,无法恢复:${located.path}`);\n }\n const meta = resumed.meta.payload;\n // Model reference is stored as a pair in session_meta; a missing provider means legacy data (no migration since the product hasn't shipped yet).\n if (typeof meta.provider !== \"string\") {\n throw new Error(\n `Trace 来自旧版本数据(session_meta 缺少 provider,模型引用未分列):${located.path}。请删除数据目录后重建会话。`,\n );\n }\n\n // The Workspace carries over from the original Session and must still exist (throw if missing, never auto-create).\n const workspaceDir = meta.workspace;\n let stat;\n try {\n stat = await fs.stat(workspaceDir);\n } catch {\n throw new Error(`原 Session 的 Workspace 已不存在:${workspaceDir},无法恢复。`);\n }\n if (!stat.isDirectory()) {\n throw new Error(`原 Session 的 Workspace 不是目录:${workspaceDir},无法恢复。`);\n }\n\n // The Model carries over from the original Session (paired reference) and must still be present in the Project config.\n const ref: ModelRef = { provider: meta.provider, model_id: meta.model_id };\n const modelEntry = getModel(this.projectConfig, ref);\n if (!modelEntry) {\n throw new Error(\n `原 Session 的 Model 不在 Project 配置中:${formatModelRef(ref)}。请用 \\`penguin config model add\\` 重新配置后再恢复。`,\n );\n }\n const apiKey = opts.apiKey ?? modelEntry.api_key;\n const baseUrl = opts.baseUrl ?? modelEntry.base_url;\n\n // Tools and Environment are reassembled from the current Agent State (tool\n // definitions are passed with every Request and aren't part of the history); the\n // system prompt uses the original text recorded in the Trace (identical to the\n // original history); the vault uses current values (it's injected into the\n // subprocess environment, not the history, so a resumed Session should get the\n // latest keys too).\n const rt = await this.buildRuntime({\n workspaceDir,\n modelEntry,\n apiKey,\n baseUrl,\n systemPrompt: meta.system_prompt,\n subagentDepth: 0,\n vault: await loadAgentVault(this.state.root, this.state.projectId, this.state.agentId),\n });\n\n // History is injected once into a fresh context object (setHistory is only used\n // on resume); Session cumulative Token counts carry over. Wrap the error\n // descriptively: bad tool arguments in the history (e.g. truncated JSON written by\n // a third-party OpenAI-compatible endpoint) throw a raw SyntaxError during\n // conversion, so the error must indicate Trace history corruption rather than a\n // regular runtime error.\n if (resumed.history.length > 0) {\n try {\n rt.llm.setHistory(resumed.history);\n } catch (err) {\n const detail = err instanceof Error ? err.message : String(err);\n throw new Error(\n `恢复失败:Trace 历史无法注入(记录可能损坏,如非法的工具参数 JSON):${detail}`,\n );\n }\n }\n rt.llm.sessionTokens = resumed.sessionTokens;\n\n // Continue writing to the original Trace file (the Trace only records real messages; synthesized paired placeholders are re-emitted in memory alongside carry-over).\n const trace = new Writer({\n tracesDir: dir,\n sessionId,\n dateDir: located.dateDir,\n startIndex: located.index,\n });\n\n return new Session({\n meta: {\n session_id: sessionId,\n provider: modelEntry.provider,\n model_id: modelEntry.model_id,\n model_context_window: modelEntry.context_window ?? \"unknown\",\n system_prompt: meta.system_prompt,\n tools: rt.tools,\n thinking_level: this.state.systemConfig.model?.thinking_level ?? \"default\",\n agent_state: this.state.stateDir,\n workspace: workspaceDir,\n },\n llm: rt.llm,\n environment: rt.environment,\n trace,\n createLLM: rt.createLLM,\n createBareLLM: rt.createBareLLM,\n compaction: rt.compaction,\n // Model doesn't support images: input images are written to the session scratchpad and their paths appended to the text (viewed via describe_image).\n ...(modelEntry.vision === false\n ? {\n inputImagesDir: path.join(\n scratchpadDir(this.state.root, this.state.projectId, this.state.agentId),\n sessionId,\n ),\n }\n : {}),\n ...(this.state.systemConfig.max_turns !== undefined\n ? { maxTurns: this.state.systemConfig.max_turns }\n : {}),\n // session_meta is already in the original Trace file, so it isn't rewritten; on the first write after a compaction-triggered rotation, the file is split first.\n metaAlreadyWritten: true,\n initialEngineState: {\n carryOver: resumed.carryOver,\n ...(resumed.pendingSummary ? { pendingSummary: resumed.pendingSummary } : {}),\n sessionTurns: resumed.sessionTurns,\n sessionTokens: resumed.sessionTokens,\n lastRequestTotal: resumed.lastRequestTotal,\n pendingTraceRotation: resumed.contextClosed,\n },\n resumedHistory: resumed.renderMessages,\n });\n }\n\n /** Id of the most recent Session under the current Agent (determined by the timestamp in session_id); returns null if there is no Session. */\n async latestSessionId(): Promise<string | null> {\n return latestTraceSessionId(\n tracesDir(this.state.root, this.state.projectId, this.state.agentId),\n );\n }\n\n /**\n * Assemble a Session's runtime components (shared by createSession and\n * resumeSession): the child-Agent runner, Environment and tools, the LLM object\n * and its post-compaction rebuild factory, and the compaction config.\n */\n private async buildRuntime(args: {\n workspaceDir: string;\n /** This Session's Model entry: the caller (createSession / resumeSession) has already validated it exists in the config. */\n modelEntry: ModelEntry;\n apiKey: string | undefined;\n baseUrl: string | undefined;\n systemPrompt: string;\n subagentDepth: number;\n vault: Record<string, string>;\n }): Promise<{\n environment: Environment;\n tools: ToolDefinition[];\n llm: GenerativeModel;\n createLLM: (sessionTokens: TokenCounts) => GenerativeModel;\n createBareLLM: () => GenerativeModel;\n compaction: CompactionSettings;\n }> {\n const { workspaceDir, modelEntry, apiKey, baseUrl, systemPrompt, subagentDepth, vault } = args;\n // Child-Agent runner: injected into the run_subagent tool so it doesn't need to\n // depend on Agent/Session (breaking a circular dependency). The model can\n // optionally choose agentId (omitted = call the current Agent) and modelId\n // (omitted = Project default). Precheck errors (depth limit exceeded / agent\n // doesn't exist) are expressed as throws, which the Environment collapses to failed.\n // Docs: /docs/interfaces § \"Subagent interfaces\"\n const parentAgent = this;\n const { root, projectId, agentId: parentAgentId } = this.state;\n const subagentRunner: SubagentRunner = {\n // Spawn and run are separate: the same child Session can run for multiple turns\n // (continuing via input_subagent appending a prompt); resource cleanup is\n // consolidated in handle.dispose (called by the managing ManagedSubagentSession).\n async spawn({ agentId, modelId }) {\n if (subagentDepth >= MAX_SUBAGENT_DEPTH) {\n throw new Error(\n `subagent depth limit ${MAX_SUBAGENT_DEPTH} reached; not spawning another subagent`,\n );\n }\n if (agentId !== undefined && agentId !== parentAgentId) {\n try {\n assertValidId(\"agent_id\", agentId);\n await fs.access(systemConfigPath(root, projectId, agentId));\n } catch {\n throw new Error(\n `subagent error: agent \"${agentId}\" does not exist or is not accessible`,\n );\n }\n }\n const childAgent =\n agentId !== undefined && agentId !== parentAgentId\n ? await createAgent({ root, projectId, agentId })\n : parentAgent;\n const childSession = await childAgent.createSession({\n workspaceDir,\n ...(modelId !== undefined ? { modelId } : {}),\n subagentDepth: subagentDepth + 1,\n });\n // All child-session messages are tagged with an origin (the child Session id,\n // prepended as one hop from outer to inner); the first turn forwards the\n // child's session_meta first (including agent_state and other metadata) so the\n // parent frontend can recognize the nested session (for rendering, stats,\n // approval visibility); the parent Trace skips these accordingly (the child\n // Session has its own Trace, linked by session id).\n const hop: MessageOrigin = childSession.sessionId;\n let metaSent = false;\n return {\n sessionId: hop,\n async *run({ prompt, signal, approve }) {\n if (!metaSent) {\n metaSent = true;\n yield withOrigin(childSession.metaMessage, hop);\n }\n // Pass through the parent's approval callback: the child Session inherits\n // the parent Agent's approval mode (with no callback, the child engine\n // defaults to deny). The tool_call received for approval also carries the\n // origin, so the approval UI can identify which tool a subagent is calling.\n const childApprove = approve\n ? (tc: OmniMessage<ToolCallPayload>) => approve(withOrigin(tc, hop))\n : undefined;\n for await (const msg of childSession.run([userText(prompt)], {\n ...(signal ? { signal } : {}),\n ...(childApprove ? { approve: childApprove } : {}),\n })) {\n yield withOrigin(msg, hop);\n }\n },\n dispose() {\n childSession.dispose();\n },\n };\n },\n };\n\n // Tool exposure is capped by depth: a (leaf) child Agent that has reached the\n // max spawn depth no longer gets run_subagent or input_subagent (the latter\n // depends on the subagent_id produced by the former, so exposing it alone is\n // meaningless).\n const canSpawn = subagentDepth < MAX_SUBAGENT_DEPTH;\n const baseToolConfig = buildToolConfig(this.state);\n // Select tool entries by the session model's type (marked via forModel: vision\n // models use read_image, text-only models use describe_image; entries without\n // this marker are unaffected).\n const modelVision = modelEntry.vision !== false;\n let customTools = selectBuiltinToolsForModel(baseToolConfig.customTools, modelVision);\n if (!canSpawn) {\n customTools = customTools.filter(\n (d) => d.name !== SUBAGENT_NAME && d.name !== INPUT_SUBAGENT_NAME,\n );\n }\n const toolConfig = { ...baseToolConfig, customTools };\n\n // When the session model doesn't support images (vision=false): inject a vision\n // model service for describe_image (forModel: \"text-only\", selected by the filter\n // above) — images are described by the Project config's vision_model (a paired\n // reference), and the tool returns text. Even when unconfigured or invalid, it is\n // still injected (modelId=null); the tool then finishes with a failed explanation,\n // and images are never allowed into that session's history.\n let visionDescriber: VisionDescriberService | undefined;\n if (modelEntry.vision === false) {\n const visionRef = this.projectConfig.vision_model;\n const visionEntry = visionRef ? getModel(this.projectConfig, visionRef) : undefined;\n if (visionEntry && visionEntry.vision !== false) {\n visionDescriber = {\n // The model attribution in the tool output matches the request's source: both are the entry's upstream model_id.\n modelId: visionEntry.model_id,\n createLLM: () =>\n new GenerativeModel({\n modelId: visionEntry.model_id,\n ...(visionEntry.api_key !== undefined ? { apiKey: visionEntry.api_key } : {}),\n ...(visionEntry.base_url !== undefined ? { baseUrl: visionEntry.base_url } : {}),\n ...(visionEntry.client_type !== undefined\n ? { clientType: visionEntry.client_type }\n : {}),\n tools: [],\n thinkingLevel: \"none\",\n maxTokens: 2048,\n requestTimeoutMs: 60_000,\n }),\n };\n } else {\n visionDescriber = { modelId: null };\n }\n }\n\n // Environment binds the Workspace and tool config; tools are listed first so\n // GenerativeModel can be initialized. Vault environment variables are injected\n // into command subprocesses (shared by createSession and resumeSession; the\n // caller reads the current agent_state/.vault.toml); a child Agent loads **its\n // own** vault via createAgent rather than inheriting the parent's.\n const environment = new Environment({\n workspaceDir,\n toolConfig,\n services: { subagentRunner, ...(visionDescriber ? { visionDescriber } : {}) },\n ...(Object.keys(vault).length > 0 ? { vault } : {}),\n });\n const tools = await environment.listTools();\n\n // LLM constructor args are extracted into a constant so they can be reused as-is when\n // rebuilding a new LLM object after compaction (with a fresh model context) — the system\n // prompt and tool definitions aren't part of the compacted history, so the new object keeps\n // them unchanged. The model id sent to AgentHub is always the entry's upstream `model_id`\n // (client_type inference/passing follows it); session_meta, Trace, usage, pricing, and catalog\n // matching all use the (provider, model_id) pair as the primary key.\n // The tool_call_id uniqueness registry is shared with the new LLM rebuilt from llmConfig after\n // compaction: its uniqueness scope is the Session's whole render span, so same-named tool calls\n // after compaction don't collide with earlier tool cards' ids.\n const llmConfig: GenerativeModelConfig = {\n modelId: modelEntry.model_id,\n toolCallIds: new ToolCallIdAllocator(),\n ...(apiKey !== undefined ? { apiKey } : {}),\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n ...(modelEntry.client_type !== undefined ? { clientType: modelEntry.client_type } : {}),\n tools,\n systemPrompt,\n ...(modelEntry.context_window !== undefined\n ? { contextWindow: modelEntry.context_window }\n : {}),\n ...(this.state.systemConfig.model?.max_tokens !== undefined\n ? { maxTokens: this.state.systemConfig.model.max_tokens }\n : {}),\n ...(this.state.systemConfig.model?.thinking_level !== undefined\n ? { thinkingLevel: this.state.systemConfig.model.thinking_level }\n : {}),\n ...(this.state.systemConfig.model?.timeoutMs !== undefined\n ? { requestTimeoutMs: this.state.systemConfig.model.timeoutMs }\n : {}),\n };\n const llm = new GenerativeModel(llmConfig);\n const createLLM = (sessionTokens: TokenCounts): GenerativeModel => {\n const next = new GenerativeModel(llmConfig);\n // Carries over the Session's cumulative Token counts, so token_usage.session stays continuous across compaction.\n next.sessionTokens = sessionTokens;\n return next;\n };\n // Bare LLM for one-off out-of-band requests (meta requests like generateTitle):\n // same Model/credentials, no tools, no system prompt, thinking disabled, a small\n // output cap, and an independent timeout.\n const createBareLLM = (): GenerativeModel =>\n new GenerativeModel({\n modelId: modelEntry.model_id,\n ...(apiKey !== undefined ? { apiKey } : {}),\n ...(baseUrl !== undefined ? { baseUrl } : {}),\n ...(modelEntry.client_type !== undefined ? { clientType: modelEntry.client_type } : {}),\n tools: [],\n thinkingLevel: \"none\",\n maxTokens: 300,\n requestTimeoutMs: 30_000,\n });\n\n // Compaction config: defaults are filled in here; an unknown mode falls back to summarize (the default).\n const compactionConfig = this.state.systemConfig.compaction;\n const compaction: CompactionSettings = {\n maxContextLength: effectiveMaxContextLength(\n compactionConfig?.max_context_length ?? 128000,\n modelEntry.context_window,\n ),\n maxSessionTurns: compactionConfig?.max_session_turns ?? -1,\n mode: compactionConfig?.mode === \"discard\" ? \"discard\" : \"summarize\",\n prompt: compactionConfig?.prompt ?? DEFAULT_COMPACTION_PROMPT,\n };\n\n return { environment, tools, llm, createLLM, createBareLLM, compaction };\n }\n}\n","/**\n * @prismshadow/penguin-core — public entry point for the PenguinHarness core SDK.\n *\n * Exports the OmniMessage protocol, the three interface contracts (Human/LLM/Environment),\n * and the runtime entry points for Agent / Session / context_engine along with their\n * submodules (state / llm / environment / trace).\n *\n * Typical usage:\n *\n * ```ts\n * const agent = await createAgent({ agentId: \"default_agent\" });\n * const session = await agent.createSession({ workspaceDir, modelId });\n * for await (const output of session.run([userText(\"...\")])) { ... }\n * ```\n */\n\n// Protocol and interface contracts (foundation)\nexport * from \"./omnimessage/index.js\";\nexport * from \"./interfaces.js\";\n\n// Submodules\nexport * from \"./state/index.js\";\nexport * from \"./llm/index.js\";\nexport * from \"./environment/index.js\";\nexport * from \"./trace/index.js\";\n\n// Runtime entry points\nexport { ContextEngine } from \"./engine/context-engine.js\";\nexport type {\n CompactAvailability,\n CompactionSettings,\n ContextEngineDeps,\n EngineInitialState,\n RunOptions,\n TraceSink,\n} from \"./engine/context-engine.js\";\nexport { Session } from \"./session.js\";\nexport type { SessionConfig } from \"./session.js\";\nexport { buildTitlePrompt, generateTitleWithLLM, sanitizeTitle } from \"./session-title.js\";\nexport type { SessionTitleResult } from \"./session-title.js\";\nexport { Agent, createAgent } from \"./agent.js\";\nexport type { CreateAgentOptions, CreateSessionOptions, ResumeSessionOptions } from \"./agent.js\";\n\n/** SDK version number. */\nexport const VERSION = \"0.0.1\";\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,OAAO,QAAQ;AACf,OAAO,UAAU;AAGV,IAAM,qBAAqB;AAG3B,IAAM,mBAAmB;AAQzB,SAAS,cAAsB;AACpC,SAAO,QAAQ,IAAI,gBAAgB,KAAK,KAAK,GAAG,QAAQ,GAAG,YAAY,MAAM;AAC/E;AAGO,SAAS,WAAW,MAAc,WAA2B;AAClE,SAAO,KAAK,KAAK,MAAM,SAAS;AAClC;AAGO,SAAS,UAAU,MAAc,WAA2B;AACjE,SAAO,KAAK,KAAK,WAAW,MAAM,SAAS,GAAG,QAAQ;AACxD;AAGO,SAAS,SAAS,MAAc,WAAmB,SAAyB;AACjF,SAAO,KAAK,KAAK,UAAU,MAAM,SAAS,GAAG,OAAO;AACtD;AAGO,SAAS,cAAc,MAAc,WAAmB,SAAyB;AACtF,SAAO,KAAK,KAAK,SAAS,MAAM,WAAW,OAAO,GAAG,aAAa;AACpE;AAGO,SAAS,UAAU,MAAc,WAAmB,SAAyB;AAClF,SAAO,KAAK,KAAK,SAAS,MAAM,WAAW,OAAO,GAAG,QAAQ;AAC/D;AAGO,SAAS,cAAc,MAAc,WAAmB,SAAyB;AACtF,SAAO,KAAK,KAAK,SAAS,MAAM,WAAW,OAAO,GAAG,YAAY;AACnE;AAGO,SAAS,cAAc,MAAc,WAAmB,SAAyB;AACtF,SAAO,KAAK,KAAK,SAAS,MAAM,WAAW,OAAO,GAAG,YAAY;AACnE;AAOO,SAAS,kBAAkB,MAAc,WAA2B;AACzE,SAAO,KAAK,KAAK,WAAW,MAAM,SAAS,GAAG,sBAAsB;AACtE;AAGO,SAAS,iBAAiB,MAAc,WAAmB,SAAyB;AACzF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,oBAAoB;AAChF;AAGO,SAAS,aAAa,MAAc,WAAmB,SAAyB;AACrF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,WAAW;AACvE;AAGO,SAAS,eAAe,MAAc,WAAmB,SAAyB;AACvF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,aAAa;AACzE;AAGO,SAAS,SAAS,MAAc,WAAmB,SAAyB;AACjF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,OAAO;AACnE;AAGO,SAAS,UAAU,MAAc,WAAmB,SAAyB;AAClF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,QAAQ;AACpE;AAGO,SAAS,UAAU,MAAc,WAAmB,SAAyB;AAClF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,QAAQ;AACpE;AAGO,SAAS,YAAY,MAAc,WAAmB,SAAyB;AACpF,SAAO,KAAK,KAAK,cAAc,MAAM,WAAW,OAAO,GAAG,UAAU;AACtE;AAGO,SAAS,cAAc,MAAc,WAAmB,SAAyB;AACtF,SAAO,KAAK,KAAK,SAAS,MAAM,WAAW,OAAO,GAAG,YAAY;AACnE;AAGO,SAAS,aAAa,MAAc,WAAmB,SAAyB;AACrF,SAAO,KAAK,KAAK,SAAS,MAAM,WAAW,OAAO,GAAG,WAAW;AAClE;;;AC1FO,IAAM,wBAAwB;AAC9B,IAAM,yBAAyB;AAC/B,IAAM,6BAA6B;AACnC,IAAM,yBAAyB;AAC/B,IAAM,kBAAkB;AACxB,IAAM,uBAAuB;AAC7B,IAAM,0BAA0B;AAChC,IAAM,uBAAuB;AAC7B,IAAM,yBAAyB;AAC/B,IAAM,mBAAmB;AA+ChC,IAAM,wBAAwB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2EvB,IAAM,4BACX;AAWF,SAAS,sBAA8C;AACrD,SAAO;AAAA,IACL;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MAIF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,KAAK;AAAA,YACH,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA,SAAS;AAAA,YACP,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,eAAe;AAAA,YACb,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,UAAU,CAAC,KAAK;AAAA,MAClB;AAAA,MACA,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,YAAY;AAAA,YACV,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA,OAAO;AAAA,YACL,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,eAAe;AAAA,YACb,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,UAAU,CAAC,YAAY;AAAA,MACzB;AAAA,MACA,YAAY;AAAA;AAAA,MAEZ,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MAEF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,UAAU;AAAA,YACR,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,UAAU;AAAA,YACR,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,eAAe;AAAA,YACb,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,UAAU,CAAC,QAAQ;AAAA,MACrB;AAAA,MACA,YAAY;AAAA;AAAA,MAEZ,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,aACE;AAAA,MACF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,aAAa;AAAA,YACX,MAAM;AAAA,YACN,aAAa;AAAA,UACf;AAAA,UACA,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,eAAe;AAAA,YACb,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,UAAU,CAAC,aAAa;AAAA,MAC1B;AAAA,MACA,YAAY;AAAA;AAAA,MAEZ,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MAGF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,UAAU,CAAC,QAAQ;AAAA,MACrB;AAAA,MACA,YAAY;AAAA,MACZ,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA,IACA;AAAA,MACE,MAAM;AAAA,MACN,UAAU;AAAA,MACV,aACE;AAAA,MAMF,YAAY;AAAA,QACV,MAAM;AAAA,QACN,YAAY;AAAA,UACV,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,UACA,QAAQ;AAAA,YACN,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,UAAU,CAAC,QAAQ;AAAA,MACrB;AAAA,MACA,YAAY;AAAA;AAAA,MAEZ,WAAW;AAAA,MACX,iBAAiB;AAAA,IACnB;AAAA,EACF;AACF;AAGO,SAAS,kBAAkB,QAA+C;AAC/E,QAAM,IAAI,OAAO;AACjB,SAAO,OAAO,MAAM,YAAY,OAAO,UAAU,CAAC,KAAK,KAAK,IAAI,IAAI;AACtE;AAGO,SAAS,sBAAoC;AAClD,SAAO;AAAA,IACL,SAAS;AAAA,IACT,eAAe;AAAA,IACf,WAAW;AAAA,IACX,OAAO;AAAA,MACL,YAAY;AAAA,MACZ,gBAAgB;AAAA,MAChB,WAAW;AAAA,IACb;AAAA,IACA,YAAY;AAAA,MACV,oBAAoB;AAAA,MACpB,mBAAmB;AAAA,MACnB,MAAM;AAAA,MACN,QAAQ;AAAA,IACV;AAAA,IACA,OAAO;AAAA,MACL,SAAS,oBAAoB;AAAA,MAC7B,YAAY,CAAC;AAAA,IACf;AAAA,EACF;AACF;AAQO,SAAS,kBAA0B;AACxC,SAAO;AACT;;;AC3XA,SAAS,yBAA4C;AAI9C,IAAM,oBAAuC,CAAC,gBAAgB;AAmB9D,SAAS,6BAA8E;AAC5F,SAAO;AAAA,IACL;AAAA,MACE,SAAS;AAAA,MACT,QAAQ;AAAA,QACN,MAAM;AAAA,QACN,aAAa;AAAA,QACb,QAAQ,kBAAkB;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AACF;;;AC3BA,OAAO,QAAQ;AACf,OAAOA,WAAU;AACjB,SAAS,SAAS,WAAW,aAAa,qBAAqB;AAexD,SAAS,eAAe,KAAuB;AACpD,SAAO,aAAa,IAAI,QAAQ,cAAc,IAAI,QAAQ;AAC5D;AAgFO,SAAS,uBAAsC;AACpD,SAAO;AAAA,IACL,eAAe,EAAE,UAAU,YAAY,UAAU,kBAAkB;AAAA,IACnE,QAAQ,mBAAmB;AAAA,EAC7B;AACF;AAGA,IAAM,kBACJ;AAGF,SAAS,cAAc,MAAc,MAAc,OAAsC;AACvF,MAAI,UAAU,OAAW,QAAO;AAChC,QAAM,MAAM;AACZ,MACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAO,IAAI,aAAa,YACxB,OAAO,IAAI,aAAa,UACxB;AACA,UAAM,IAAI;AAAA,MACR,+BAA0B,IAAI,mJAA8D,IAAI,SAAI,eAAe;AAAA,IACrH;AAAA,EACF;AACA,SAAO,EAAE,UAAU,IAAI,UAAU,UAAU,IAAI,SAAS;AAC1D;AAGA,SAAS,iBAAiB,MAAc,OAA4B;AAClE,QAAM,IAAI;AACV,MACE,OAAO,UAAU,YACjB,UAAU,QACV,OAAO,EAAE,aAAa,YACtB,OAAO,EAAE,aAAa,UACtB;AACA,UAAM,IAAI;AAAA,MACR,8LAA2E,IAAI,SAAI,eAAe;AAAA,IACpG;AAAA,EACF;AACA,SAAO;AACT;AAQA,eAAsB,kBAAkB,MAAc,WAA2C;AAC/F,QAAM,OAAO,kBAAkB,MAAM,SAAS;AAC9C,MAAI;AACJ,MAAI;AACF,UAAM,MAAM,GAAG,SAAS,MAAM,MAAM;AAAA,EACtC,SAAS,KAAK;AACZ,QAAK,IAA8B,SAAS,SAAU,QAAO,qBAAqB;AAClF,UAAM;AAAA,EACR;AAEA,QAAM,SAAU,UAAU,GAAG,KAAK,CAAC;AACnC,QAAM,eAAe,cAAc,MAAM,iBAAiB,OAAO,aAAa;AAC9E,QAAM,cAAc,cAAc,MAAM,gBAAgB,OAAO,YAAY;AAC3E,SAAO;AAAA,IACL,GAAI,OAAO,SAAS,SAAY,EAAE,MAAM,OAAO,KAAe,IAAI,CAAC;AAAA,IACnE,GAAI,iBAAiB,SAAY,EAAE,eAAe,aAAa,IAAI,CAAC;AAAA,IACpE,GAAI,gBAAgB,SAAY,EAAE,cAAc,YAAY,IAAI,CAAC;AAAA,IACjE,SAAU,OAAO,UAAoC,CAAC,GAAG,IAAI,CAAC,MAAM,iBAAiB,MAAM,CAAC,CAAC;AAAA,EAC/F;AACF;AAGA,SAAS,cAAc,KAAuB;AAC5C,QAAM,KAAK,CAAC,QAAwC,cAAc,GAAG,EAAE,KAAK;AAC5E,SAAO,KAAK,GAAG,EAAE,UAAU,IAAI,SAAS,CAAC,CAAC,KAAK,GAAG,EAAE,UAAU,IAAI,SAAS,CAAC,CAAC;AAC/E;AAGA,SAAS,gBAAgB,GAA2B;AAClD,MAAI,MAAM,QAAQ,OAAO,MAAM,YAAY,MAAM,QAAQ,CAAC,EAAG,QAAO;AACpE,QAAM,IAAI;AACV,SAAO,OAAO,EAAE,aAAa,YAAY,OAAO,EAAE,aAAa;AACjE;AAYO,SAAS,wBAAwB,MAAuC;AAC7E,QAAM,OAAiB,CAAC;AACxB,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,IAAI,GAAG;AAC/C,QAAI,UAAU,UAAa,QAAQ,SAAU;AAC7C,SAAK;AAAA,MACH,gBAAgB,KAAK,IACjB,GAAG,GAAG,MAAM,cAAc,KAAK,CAAC,KAChC,cAAc,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,EAAE,KAAK;AAAA,IAC3C;AAAA,EACF;AACA,QAAM,SAAS,MAAM,QAAQ,KAAK,MAAM,IAAI,KAAK,SAAS,CAAC;AAC3D,SAAO,CAAC,GAAG,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,EAAE,KAAK,IAAI;AACvD;AAQA,eAAsB,kBACpB,MACA,WACA,KACe;AACf,QAAM,OAAO,kBAAkB,MAAM,SAAS;AAC9C,QAAM,GAAG,MAAMC,MAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AACtD,QAAM,GAAG,UAAU,MAAM,wBAAwB,EAAE,GAAG,IAAI,CAAC,GAAG;AAAA,IAC5D,UAAU;AAAA,IACV,MAAM;AAAA,EACR,CAAC;AACD,QAAM,GAAG,MAAM,MAAM,GAAK;AAC5B;AAYA,eAAsB,SACpB,MACA,WACA,OAcA,MACwB;AACxB,QAAM,MAAM,MAAM,kBAAkB,MAAM,SAAS;AACnD,QAAM,WAAW,MAAM,YAAY,yBAAyB,MAAM,QAAQ;AAK1E,QAAM,MAAM,IAAI,OAAO,UAAU,CAAC,MAAM,EAAE,aAAa,YAAY,EAAE,aAAa,MAAM,QAAQ;AAChG,QAAM,WAAW,OAAO,IAAI,IAAI,OAAO,GAAG,IAAI;AAC9C,QAAM,aAAyB;AAAA,IAC7B;AAAA,IACA,UAAU,MAAM;AAAA,EAClB;AACA,QAAM,gBAAgB,MAAM,kBAAkB,UAAU;AACxD,MAAI,kBAAkB,QAAW;AAC/B,eAAW,iBAAiB;AAAA,EAC9B;AACA,QAAM,aAAa,MAAM,eAAe,UAAU;AAClD,MAAI,eAAe,QAAW;AAC5B,eAAW,cAAc;AAAA,EAC3B;AAEA,MAAI,UAAU,iBAAiB,QAAW;AACxC,eAAW,eAAe,SAAS;AAAA,EACrC;AACA,QAAM,SAAS,MAAM,UAAU,UAAU;AACzC,MAAI,WAAW,QAAW;AACxB,eAAW,SAAS;AAAA,EACtB;AAIA,QAAM,gBAAuC;AAAA,IAC3C,GAAG,UAAU;AAAA,IACb,GAAG,MAAM;AAAA,EACX;AACA,MACE,cAAc,eAAe,UAC7B,cAAc,gBAAgB,UAC9B,cAAc,WAAW,QACzB;AACA,eAAW,UAAU;AAAA,MACnB,MAAM;AAAA,MACN,YAAY,cAAc,cAAc;AAAA,MACxC,aAAa,cAAc,eAAe;AAAA,MAC1C,QAAQ,cAAc,UAAU;AAAA,IAClC;AAAA,EACF;AAEA,QAAM,SAAS,MAAM,WAAW,UAAU;AAC1C,MAAI,WAAW,QAAW;AACxB,eAAW,UAAU;AAAA,EACvB;AACA,QAAM,UAAU,MAAM,YAAY,UAAU;AAC5C,MAAI,YAAY,QAAW;AACzB,eAAW,WAAW;AAAA,EACxB;AACA,MAAI,UAAU,eAAe,QAAW;AACtC,eAAW,aAAa,SAAS;AAAA,EACnC;AACA,MAAI,OAAO,GAAG;AACZ,QAAI,OAAO,GAAG,IAAI;AAAA,EACpB,OAAO;AACL,QAAI,OAAO,KAAK,UAAU;AAAA,EAC5B;AAEA,MAAI,MAAM,YAAY;AACpB,QAAI,gBAAgB,EAAE,UAAU,UAAU,MAAM,SAAS;AAAA,EAC3D;AAEA,QAAM,kBAAkB,MAAM,WAAW,GAAG;AAC5C,SAAO;AACT;AAMA,eAAsB,gBACpB,MACA,WACA,KACwB;AACxB,QAAM,MAAM,MAAM,kBAAkB,MAAM,SAAS;AACnD,MAAI,CAAC,SAAS,KAAK,GAAG,GAAG;AACvB,UAAM,IAAI;AAAA,MACR,mFAA4B,eAAe,GAAG,CAAC;AAAA,IACjD;AAAA,EACF;AACA,MAAI,gBAAgB,EAAE,UAAU,IAAI,UAAU,UAAU,IAAI,SAAS;AACrE,QAAM,kBAAkB,MAAM,WAAW,GAAG;AAC5C,SAAO;AACT;AAOA,eAAsB,eACpB,MACA,WACA,KACwB;AACxB,QAAM,MAAM,MAAM,kBAAkB,MAAM,SAAS;AACnD,QAAM,QAAQ,SAAS,KAAK,GAAG;AAC/B,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,kFAA2B,eAAe,GAAG,CAAC;AAAA,IAChD;AAAA,EACF;AACA,MAAI,MAAM,WAAW,OAAO;AAC1B,UAAM,IAAI,MAAM,gHAAgC,eAAe,GAAG,CAAC,QAAG;AAAA,EACxE;AACA,MAAI,eAAe,EAAE,UAAU,IAAI,UAAU,UAAU,IAAI,SAAS;AACpE,QAAM,kBAAkB,MAAM,WAAW,GAAG;AAC5C,SAAO;AACT;AAGO,SAAS,SAAS,KAAoB,KAAuC;AAClF,SAAO,IAAI,OAAO,KAAK,CAAC,MAAM,EAAE,aAAa,IAAI,YAAY,EAAE,aAAa,IAAI,QAAQ;AAC1F;AAUO,SAAS,gBAAgB,KAAoB,SAAiB,UAA6B;AAChG,MAAI,aAAa,QAAW;AAC1B,UAAM,MAAgB,EAAE,UAAU,UAAU,QAAQ;AACpD,QAAI,CAAC,SAAS,KAAK,GAAG,GAAG;AACvB,YAAM,IAAI;AAAA,QACR,sDAAwB,eAAe,GAAG,CAAC;AAAA,MAC7C;AAAA,IACF;AACA,WAAO;AAAA,EACT;AACA,QAAM,aAAa,IAAI,OAAO,OAAO,CAAC,MAAM,EAAE,aAAa,OAAO;AAClE,MAAI,WAAW,WAAW,GAAG;AAC3B,WAAO,EAAE,UAAU,WAAW,CAAC,EAAG,UAAU,UAAU,QAAQ;AAAA,EAChE;AACA,MAAI,WAAW,WAAW,GAAG;AAC3B,UAAM,IAAI;AAAA,MACR,mFAAsC,OAAO;AAAA,IAC/C;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IACR,4DAAoB,OAAO,oDAAY,WACpC,IAAI,CAAC,MAAM,eAAe,EAAE,UAAU,EAAE,UAAU,UAAU,EAAE,SAAS,CAAC,CAAC,EACzE,KAAK,QAAG,CAAC;AAAA,EACd;AACF;;;AClaA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,SAAS,WAAW,aAAaC,sBAAqB;AAC/D;AAAA,EACE,qBAAAC;AAAA,EACA;AAAA,OAEK;;;ACLP,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,aAAaC,sBAAqB;AAC3C,SAAS,aAAa,qBAAqB;AAIpC,IAAM,uBAAuB;AAGpC,IAAM,2BAA2B;AAAA,EAC/B,OAAO;AAAA,EACP,aACE;AAAA,EAEF,MAAM;AACR;AAGA,IAAM,gBAA0E;AAAA,EAC9E;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOX,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,WAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQX,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOV;AACF;AAgBA,IAAM,sBAQD;AAAA,EACH;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,UAAU;AAAA,IACV,eAAe;AAAA,IACf,SACE;AAAA,IAGF,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,UAAU;AAAA,IACV,eAAe;AAAA,IACf,SACE;AAAA,IAGF,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA,EACA;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,IACT,UAAU;AAAA,IACV,UAAU;AAAA,IACV,eAAe;AAAA,IACf,SACE;AAAA,IAIF,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,MAAM;AAAA,UACJ;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,UACA;AAAA,YACE,OAAO;AAAA,YACP,MAAM;AAAA,YACN,aAAa;AAAA,YACb,YAAY;AAAA,UACd;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAGA,SAAS,MAAM,GAAmB;AAChC,SAAO,KAAK,MAAM,IAAI,GAAG,IAAI;AAC/B;AAEA,SAAS,QAAQ,QAA0B;AACzC,SAAO,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI,OAAO,MAAM;AAChE;AAEA,SAAS,IAAI,QAA0B;AACrC,SAAO,MAAM,OAAO,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,CAAC;AAChD;AAQO,SAAS,yBAmBd;AACA,SAAO;AAAA,IACL,aAAa,oBAAoB,IAAI,CAAC,MAAM;AAC1C,YAAM,QAAQ,EAAE,MAAM,IAAI,CAAC,OAAO;AAAA,QAChC,MAAM,EAAE;AAAA,QACR,OAAO,QAAQ,EAAE,KAAK,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,QACzC,MAAM,QAAQ,EAAE,KAAK,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAAA,QACvC,aAAa,QAAQ,EAAE,KAAK,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;AAAA,QACrD,MAAM,EAAE;AAAA,MACV,EAAE;AACF,aAAO;AAAA,QACL,MAAM,EAAE;AAAA,QACR,SAAS,EAAE;AAAA,QACX,UAAU,EAAE;AAAA,QACZ,UAAU,EAAE;AAAA,QACZ,eAAe,EAAE;AAAA,QACjB,SAAS,EAAE;AAAA,QACX,OAAO,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC;AAAA,QACpC,MAAM,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAAA,QAClC,aAAa,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,WAAW,CAAC;AAAA,QAChD;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAQA,eAAsB,0BACpB,MACA,WACA,SACe;AACf,QAAM,MAAM,cAAc,MAAM,WAAW,OAAO;AAClD,MAAI;AACF,UAAMC,IAAG,OAAO,GAAG;AACnB;AAAA,EACF,QAAQ;AAAA,EAER;AACA,QAAM,WAAWC,MAAK,KAAK,KAAK,oBAAoB;AACpD,QAAM,QAAQ;AAAA,IACZ,cAAc,QAAQ,CAAC,MAAM;AAAA,MAC3BD,IAAG,MAAMC,MAAK,KAAK,UAAU,EAAE,IAAI,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,MACpED,IAAG,MAAMC,MAAK,KAAK,UAAU,EAAE,IAAI,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,IACnE,CAAC;AAAA,EACH;AACA,QAAM,QAAQ,IAAI;AAAA,IAChBD,IAAG;AAAA,MACDC,MAAK,KAAK,UAAU,uBAAuB;AAAA,MAC3C,GAAGC,eAAc,wBAAwB,CAAC;AAAA;AAAA,MAC1C;AAAA,IACF;AAAA,IACAF,IAAG;AAAA,MACDC,MAAK,KAAK,UAAU,iBAAiB;AAAA,MACrC,cAAc,uBAAuB,CAAC;AAAA,MACtC;AAAA,IACF;AAAA,IACA,GAAG,cAAc,QAAQ,CAAC,MAAM;AAAA,MAC9BD,IAAG,UAAUC,MAAK,KAAK,UAAU,EAAE,IAAI,aAAa,WAAW,GAAG,EAAE,WAAW,MAAM;AAAA,MACrFD,IAAG,UAAUC,MAAK,KAAK,UAAU,EAAE,IAAI,UAAU,WAAW,GAAG,EAAE,QAAQ,MAAM;AAAA,IACjF,CAAC;AAAA,EACH,CAAC;AACH;;;ADnSA,IAAM,aAAa;AAGZ,SAAS,UAAU,IAAqB;AAC7C,SAAO,WAAW,KAAK,EAAE;AAC3B;AAEO,SAAS,cAAc,MAAc,IAAkB;AAC5D,MAAI,CAAC,WAAW,KAAK,EAAE,GAAG;AACxB,UAAM,IAAI;AAAA,MACR,WAAW,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;AAAA,IACvC;AAAA,EACF;AACF;AAiCA,eAAsB,qBAAqB,MAKnB;AACtB,QAAM,OAAO,MAAM,QAAQ,YAAY;AACvC,QAAM,YAAY,MAAM,aAAa;AACrC,QAAM,UAAU,MAAM,WAAW;AAGjC,gBAAc,cAAc,SAAS;AACrC,gBAAc,YAAY,OAAO;AAEjC,QAAM,WAAW,cAAc,MAAM,WAAW,OAAO;AACvD,QAAM,aAAa,iBAAiB,MAAM,WAAW,OAAO;AAC5D,QAAM,SAAS,aAAa,MAAM,WAAW,OAAO;AAEpD,MAAI;AACJ,MAAI;AAEJ,MAAI,MAAM,WAAW,UAAU,GAAG;AAEhC,UAAM,YAAY,MAAME,IAAG,SAAS,YAAY,MAAM;AACtD,UAAM,SAAS,UAAU,SAAS;AAIlC,QACE,WAAW,QACX,OAAO,WAAW,YAClB,OAAQ,OAAwB,kBAAkB,UAClD;AACA,YAAM,IAAI,MAAM,6CAAoB,UAAU,oFAA6B;AAAA,IAC7E;AACA,mBAAe;AACf,eAAY,MAAM,WAAW,MAAM,IAAK,MAAMA,IAAG,SAAS,QAAQ,MAAM,IAAI,gBAAgB;AAAA,EAC9F,OAAO;AAEL,UAAM,QAAQ,IAAI;AAAA,MAChBA,IAAG,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAAA,MACtCA,IAAG,MAAM,SAAS,MAAM,WAAW,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,MAChEA,IAAG,MAAM,UAAU,MAAM,WAAW,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,MACjEA,IAAG,MAAM,UAAU,MAAM,WAAW,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,MACjEA,IAAG,MAAM,cAAc,MAAM,WAAW,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,IACvE,CAAC;AACD,UAAM,SAAS,MAAM;AACrB,mBAAe;AAAA,MACb,GAAG,oBAAoB;AAAA,MACvB,GAAI,QAAQ,SAAS,SAAY,EAAE,MAAM,OAAO,KAAK,IAAI,CAAC;AAAA,MAC1D,GAAI,QAAQ,gBAAgB,SAAY,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,IACjF;AACA,eAAW,QAAQ,YAAY,gBAAgB;AAQ/C,UAAM,SACJ,MAAM,WAAW,UAAa,YAAY,mBACtCC,mBAAkB,IACjB,MAAM,QAAQ,UAAU,CAAC;AAChC,UAAM,QAAQ,IAAI;AAAA,MAChBD,IAAG,UAAU,QAAQ,UAAU,MAAM;AAAA,MACrC,GAAG,OAAO,IAAI,CAAC,UAAU,aAAa,MAAM,WAAW,SAAS,KAAK,CAAC;AAAA;AAAA;AAAA;AAAA,MAItE,GAAI,YAAY,mBACZ,CAAC,0BAA0B,MAAM,WAAW,OAAO,CAAC,IACpD,CAAC;AAAA,IACP,CAAC;AAID,UAAMA,IAAG,UAAU,YAAYE,eAAc,YAAY,GAAG,MAAM;AAAA,EACpE;AAEA,SAAO,EAAE,MAAM,WAAW,SAAS,UAAU,cAAc,SAAS;AACtE;AASA,eAAsB,uBAAuB,MAGvB;AACpB,QAAM,WAAqB,CAAC;AAC5B,aAAW,EAAE,SAAS,OAAO,KAAK,2BAA2B,GAAG;AAC9D,UAAM,qBAAqB;AAAA,MACzB,GAAI,MAAM,SAAS,SAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC;AAAA,MACtD,GAAI,MAAM,cAAc,SAAY,EAAE,WAAW,KAAK,UAAU,IAAI,CAAC;AAAA,MACrE;AAAA,MACA;AAAA,IACF,CAAC;AACD,aAAS,KAAK,OAAO;AAAA,EACvB;AACA,SAAO;AACT;AASA,SAAS,cAAc,MAAwB;AAC7C,SAAO,KAAK,IAAI,CAAC,QAAQ,KAAK,GAAG,EAAE,EAAE,KAAK,IAAI;AAChD;AAWA,eAAsB,aACpB,MACA,WACA,SACA,OACe;AACf,gBAAc,cAAc,SAAS;AACrC,gBAAc,YAAY,OAAO;AACjC,gBAAc,cAAc,MAAM,IAAI;AACtC,QAAM,MAAMC,MAAK,KAAK,UAAU,MAAM,WAAW,OAAO,GAAG,MAAM,IAAI;AACrE,QAAMH,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,QAAM,UAAU,MAAM,QAAQ,SAAS,IAAI,IAAI,MAAM,UAAU,GAAG,MAAM,OAAO;AAAA;AAC/E,QAAM,WAAWG,MAAK,KAAK,KAAK,UAAU;AAC1C,QAAM,QAAQ,IAAI;AAAA,IAChBH,IAAG,UAAUG,MAAK,KAAK,KAAK,UAAU,GAAG,SAAS,MAAM;AAAA,IACxD,MAAM,SAAS,SACXH,IAAG,UAAU,UAAU,MAAM,MAAM,MAAM,IACzCA,IAAG,GAAG,UAAU,EAAE,OAAO,KAAK,CAAC;AAAA,EACrC,CAAC;AACH;AAGA,eAAsB,YACpB,MACA,WACA,SACA,MACe;AACf,gBAAc,cAAc,SAAS;AACrC,gBAAc,YAAY,OAAO;AACjC,gBAAc,cAAc,IAAI;AAChC,QAAMA,IAAG,GAAGG,MAAK,KAAK,UAAU,MAAM,WAAW,OAAO,GAAG,IAAI,GAAG;AAAA,IAChE,WAAW;AAAA,IACX,OAAO;AAAA,EACT,CAAC;AACH;AAkBA,eAAsB,oBACpB,MACA,WACA,SAC2B;AAC3B,gBAAc,cAAc,SAAS;AACrC,gBAAc,YAAY,OAAO;AACjC,QAAM,MAAM,UAAU,MAAM,WAAW,OAAO;AAC9C,MAAI;AACJ,MAAI;AACF,cAAU,MAAMH,IAAG,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACzD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAA2B,CAAC;AAClC,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,YAAY,EAAG;AAC1B,QAAI;AACJ,QAAI;AACF,YAAM,MAAMA,IAAG,SAASG,MAAK,KAAK,KAAK,MAAM,MAAM,UAAU,GAAG,MAAM;AAAA,IACxE,QAAQ;AACN;AAAA,IACF;AACA,QAAI;AACJ,QAAI;AACF,aAAO,MAAMH,IAAG,SAASG,MAAK,KAAK,KAAK,MAAM,MAAM,UAAU,GAAG,MAAM;AAAA,IACzE,QAAQ;AAAA,IAER;AAMA,UAAM,SAAS,sBAAsB,GAAG;AACxC,WAAO,KAAK;AAAA,MACV,GAAI,UAAU,EAAE,aAAa,IAAI,SAAS,GAAG,SAAS,GAAG;AAAA,MACzD,MAAM,MAAM;AAAA,MACZ,GAAI,SAAS,SAAY,EAAE,KAAK,IAAI,CAAC;AAAA,IACvC,CAAC;AAAA,EACH;AACA,SAAO,OAAO,KAAK,CAAC,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,CAAC;AAC3D;AAOO,SAAS,qBAAqB,QAAiC;AACpE,SAAO,OACJ,IAAI,CAAC,MAAO,EAAE,cAAc,OAAO,EAAE,IAAI,aAAQ,EAAE,WAAW,KAAK,OAAO,EAAE,IAAI,IAAK,EACrF,KAAK,IAAI;AACd;AAkBO,SAAS,qBACd,OACAC,qBACA,WACA,eACQ;AACR,SAAO,MAAM,aAAa,cACvB,MAAM,qBAAqB,EAC3B,KAAK,MAAM,SAAS,KAAK,CAAC,EAC1B,MAAM,sBAAsB,EAC5B,KAAK,cAAc,aAAa,CAAC,CAAC,CAAC,EACnC,MAAM,0BAA0B,EAChC,KAAK,qBAAqB,iBAAiB,CAAC,CAAC,CAAC,EAC9C,MAAM,oBAAoB,EAC1B,KAAKA,qBAAoB,WAAW,MAAM,OAAO,EACjD,MAAM,uBAAuB,EAC7B,KAAKA,qBAAoB,cAAc,EAAE,EACzC,MAAM,sBAAsB,EAC5B,KAAKA,qBAAoB,aAAa,EAAE,EACxC,MAAM,eAAe,EACrB,KAAKA,qBAAoB,OAAO,EAAE,EAClC,MAAM,oBAAoB,EAC1B,KAAKA,qBAAoB,YAAY,EAAE,EACvC,MAAM,sBAAsB,EAC5B,KAAKA,qBAAoB,aAAa,EAAE,EACxC,MAAM,gBAAgB,EACtB,KAAKA,qBAAoB,QAAQ,EAAE,EACnC,KAAK;AACV;AAeO,SAAS,2BACd,OACA,aACwB;AACxB,QAAM,OAAO,cAAc,WAAW;AACtC,SAAO,MAAM,OAAO,CAAC,MAAM,EAAE,aAAa,UAAa,EAAE,aAAa,IAAI;AAC5E;AAEO,SAAS,gBAAgB,OAA+B;AAC7D,QAAM,cAAc,MAAM,aAAa;AACvC,QAAM,UAAU,aAAa,WAAW,oBAAoB,EAAE,OAAO,WAAW,CAAC;AACjF,SAAO;AAAA,IACL,aAAa;AAAA,IACb,YAAY,aAAa,cAAc,CAAC;AAAA,EAC1C;AACF;AAEA,eAAe,WAAW,UAAoC;AAC5D,MAAI;AACF,UAAMJ,IAAG,OAAO,QAAQ;AACxB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;;;AEtZA,OAAOK,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,SAASC,YAAW,aAAaC,sBAAqB;AAK/D,IAAM,oBAAoB;AAQnB,IAAM,yBAAyB;AAG/B,SAAS,gBAAgB,KAAsB;AACpD,SAAO,kBAAkB,KAAK,GAAG;AACnC;AAGO,SAAS,oBAAoB,KAAmB;AACrD,MAAI,CAAC,gBAAgB,GAAG,GAAG;AACzB,UAAM,IAAI;AAAA,MACR,qBAAqB,KAAK,UAAU,GAAG,CAAC;AAAA,IAC1C;AAAA,EACF;AACF;AAGO,SAAS,sBAAsB,KAAa,OAAqB;AACtE,MAAI,MAAM,SAAS,wBAAwB;AACzC,UAAM,IAAI;AAAA,MACR,mBAAmB,GAAG,iBAAiB,MAAM,MAAM,MAAM,sBAAsB;AAAA,IACjF;AAAA,EACF;AACF;AAUA,eAAsB,eACpB,MACA,WACA,SACiC;AACjC,gBAAc,cAAc,SAAS;AACrC,gBAAc,YAAY,OAAO;AACjC,MAAI;AACJ,MAAI;AACF,UAAM,MAAMC,IAAG,SAAS,eAAe,MAAM,WAAW,OAAO,GAAG,MAAM;AAAA,EAC1E,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACA,QAAM,SAAkBC,WAAU,GAAG,KAAK,CAAC;AAC3C,QAAM,QAAgC,CAAC;AACvC,MAAI,WAAW,QAAQ,OAAO,WAAW,YAAY,CAAC,MAAM,QAAQ,MAAM,GAAG;AAC3E,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,GAAG;AAC3C,UAAI,OAAO,MAAM,YAAY,gBAAgB,CAAC,EAAG,OAAM,CAAC,IAAI;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;AAQA,eAAsB,eACpB,MACA,WACA,SACA,OACe;AACf,gBAAc,cAAc,SAAS;AACrC,gBAAc,YAAY,OAAO;AACjC,aAAW,OAAO,OAAO,KAAK,KAAK,EAAG,qBAAoB,GAAG;AAC7D,QAAM,OAAO,eAAe,MAAM,WAAW,OAAO;AACpD,MAAI,OAAO,KAAK,KAAK,EAAE,WAAW,GAAG;AACnC,UAAMD,IAAG,GAAG,MAAM,EAAE,OAAO,KAAK,CAAC;AACjC;AAAA,EACF;AACA,QAAMA,IAAG,MAAME,MAAK,QAAQ,IAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAGtD,QAAMF,IAAG,UAAU,MAAM,GAAGG,eAAc,KAAK,CAAC;AAAA,GAAM,EAAE,UAAU,QAAQ,MAAM,IAAM,CAAC;AACvF,QAAMH,IAAG,MAAM,MAAM,GAAK;AAC5B;AAQA,eAAsB,cACpB,MACA,WACA,SACA,KACA,OACiC;AACjC,sBAAoB,GAAG;AACvB,wBAAsB,KAAK,KAAK;AAChC,QAAM,QAAQ,MAAM,eAAe,MAAM,WAAW,OAAO;AAC3D,QAAM,GAAG,IAAI;AACb,QAAM,eAAe,MAAM,WAAW,SAAS,KAAK;AACpD,SAAO;AACT;AAMA,eAAsB,iBACpB,MACA,WACA,SACA,KACiC;AACjC,QAAM,QAAQ,MAAM,eAAe,MAAM,WAAW,OAAO;AAC3D,MAAI,EAAE,OAAO,OAAQ,QAAO;AAC5B,SAAO,MAAM,GAAG;AAChB,QAAM,eAAe,MAAM,WAAW,SAAS,KAAK;AACpD,SAAO;AACT;;;AClIA,SAAS,yBAAAI,8BAAiD;;;ACQ1D,SAAS,eAAe,qBAAqB;;;ACLtC,IAAM,sBAAN,MAA0B;AAAA;AAAA,EAEvB,OAAO,oBAAI,IAAY;AAAA;AAAA,EAG/B,SAAS,IAAkB;AACzB,SAAK,KAAK,IAAI,EAAE;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,YAA4B;AACnC,QAAI,KAAK;AACT,aAAS,IAAI,GAAG,KAAK,KAAK,IAAI,EAAE,GAAG,KAAK,GAAG;AACzC,WAAK,GAAG,UAAU,IAAI,CAAC;AAAA,IACzB;AACA,SAAK,KAAK,IAAI,EAAE;AAChB,WAAO;AAAA,EACT;AACF;AAQO,SAAS,sBAAsB,IAAoB;AACxD,SAAO,GAAG,QAAQ,SAAS,EAAE;AAC/B;;;ADqBA,SAAS,mBAAmB,KAAsC;AAChE,MAAI,CAAC,IAAK,QAAO,CAAC;AAClB,QAAM,SAAkB,KAAK,MAAM,GAAG;AACtC,SAAO,WAAW,QAAQ,OAAO,WAAW,WAAY,SAAqC,CAAC;AAChG;AAMA,SAAS,qBAAqB,SAA4C;AAIxE,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA,QACd,GAAI,QAAQ,SAAS,OAAO,EAAE,OAAO,QAAQ,MAAM,IAAI,CAAC;AAAA,QACxD,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MAC5E;AAAA,IACF,KAAK;AACH,aAAO,EAAE,MAAM,aAAa,WAAW,QAAQ,UAAU;AAAA,IAC3D,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,OAAO,KAAK,QAAQ,MAAM,QAAQ;AAAA,QACxC,WAAW,QAAQ;AAAA,QACnB,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MAC5E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,OAAO,KAAK,QAAQ,MAAM,QAAQ;AAAA,QACxC,WAAW,QAAQ;AAAA,QACnB,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MAC5E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,UAAU,QAAQ;AAAA,QAClB,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MAC5E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA;AAAA,QAEd,WAAW,mBAAmB,QAAQ,SAAS;AAAA;AAAA,QAE/C,cAAc,sBAAsB,QAAQ,YAAY;AAAA,QACxD,GAAI,QAAQ,cAAc,SAAY,EAAE,WAAW,QAAQ,UAAU,IAAI,CAAC;AAAA,MAC5E;AAAA,IACF,KAAK;AACH,aAAO;AAAA,QACL,MAAM;AAAA,QACN,MAAM,QAAQ;AAAA;AAAA;AAAA,QAGd,GAAI,QAAQ,UAAU,QAAQ,OAAO,SAAS,IAAI,EAAE,QAAQ,QAAQ,OAAO,IAAI,CAAC;AAAA;AAAA,QAEhF,cAAc,sBAAsB,QAAQ,YAAY;AAAA,MAC1D;AAAA,IACF,SAAS;AAEP,YAAM,cAAqB;AAC3B,YAAM,IAAI;AAAA,QACR,6CAA8C,YAAkC,IAAI;AAAA,MACtF;AAAA,IACF;AAAA,EACF;AACF;AAQO,SAAS,0BAA0B,SAAsC;AAC9E,QAAM,SAA0B,CAAC;AACjC,MAAI,cAA6B;AACjC,aAAW,OAAO,SAAS;AACzB,UAAM,OAAQ,IAAI,QAA8B;AAChD,QAAI,SAAS,UAAU,SAAS,aAAa;AAC3C,YAAM,IAAI,MAAM,iDAAiD,KAAK,UAAU,IAAI,IAAI,CAAC,EAAE;AAAA,IAC7F;AACA,QAAI,SAAS,aAAa;AACxB,aAAO,KAAK,CAAC,CAAC;AACd,oBAAc;AAAA,IAChB;AACA,WAAO,OAAO,SAAS,CAAC,EAAG,KAAK,GAAG;AAAA,EACrC;AACA,SAAO,OAAO,IAAI,qBAAqB;AACzC;AASO,SAAS,sBAAsB,UAAqC;AACzE,MAAI,SAAS,WAAW,GAAG;AACzB,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AAEA,QAAM,WAAW,SAAS,IAAI,CAAC,MAAM,EAAE,OAA+B;AAEtE,QAAM,OAAO,SAAS,CAAC,EAAG;AAE1B,QAAM,eAA8B,CAAC;AACrC,aAAW,WAAW,UAAU;AAC9B,QAAI,QAAQ,SAAS,MAAM;AACzB,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,iBAAa,KAAK,qBAAqB,OAAO,CAAC;AAAA,EACjD;AAEA,SAAO,EAAE,MAAM,eAAe,aAAa;AAC7C;AAiBO,SAAS,mBAAmB,OAAmC;AACpE,QAAM,SAAS,MAAM,iBAAiB;AACtC,QAAM,SAAS,MAAM,iBAAiB;AACtC,QAAM,WAAW,MAAM,mBAAmB;AAC1C,QAAM,WAAW,MAAM,mBAAmB;AAC1C,QAAM,YAAY;AAClB,QAAM,aAAa;AACnB,QAAM,SAAS,WAAW;AAC1B,SAAO;AAAA,IACL,YAAY;AAAA,IACZ,aAAa;AAAA,IACb;AAAA,IACA,OAAO,YAAY,aAAa;AAAA,EAClC;AACF;AA+BO,IAAM,kBAAN,MAAsB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAM3B,YAA6B,cAAmC,IAAI,oBAAoB,GAAG;AAA9D;AAAA,EAA+D;AAAA,EAA/D;AAAA;AAAA,EAGrB,cAAc;AAAA,EACd,kBAAkB;AAAA;AAAA,EAElB,cAAc,oBAAI,IAAY;AAAA;AAAA,EAE9B,mBAAkC;AAAA;AAAA,EAGlC,aAAa;AAAA,EACb,iBAAiB;AAAA;AAAA;AAAA;AAAA,EAIjB;AAAA,EACA,YAA2B;AAAA,EAC3B;AAAA;AAAA,EAEA,YAAsB,CAAC;AAAA;AAAA,EAEvB,QAAQ,oBAAI,IAAiC;AAAA,EAE7C,eAAoC;AAAA;AAAA,EAEpC,gBAA6B,iBAAiB;AAAA;AAAA,EAGtD,CAAC,UAAU,OAAyC;AAClD,QAAI,MAAM,iBAAiB,MAAM;AAC/B,WAAK,eAAe,MAAM;AAAA,IAC5B;AACA,QAAI,MAAM,gBAAgB;AAMxB,WAAK,gBAAgB,KAAK,UAAU,MAAM,cAAc;AAAA,IAC1D;AAEA,eAAW,QAAQ,MAAM,eAAe;AACtC,cAAQ,KAAK,MAAM;AAAA,QACjB,KAAK,QAAQ;AAMX,cAAI,KAAK,SAAS,QAAQ,KAAK,UAAU,KAAK,WAAW;AACvD,mBAAO,KAAK,cAAc,WAAW;AACrC,mBAAO,KAAK,UAAU,WAAW;AACjC,iBAAK,YAAY,KAAK;AAAA,UACxB;AACA,cAAI,KAAK,UAAW,MAAK,gBAAgB,KAAK;AAC9C,cAAI,CAAC,KAAK,KAAM;AAKhB,iBAAO,KAAK,cAAc,WAAW;AACrC,cAAI,CAAC,KAAK,aAAa;AACrB,iBAAK,cAAc;AACnB,kBAAM,YAAY,OAAO;AAAA,UAC3B;AACA,eAAK,cAAc,KAAK;AACxB,gBAAM,YAAY,SAAS,KAAK,IAAI;AACpC;AAAA,QACF;AAAA,QACA,KAAK,YAAY;AAOf,cAAI,KAAK,sBAAsB,WAAc,KAAK,YAAY,KAAK,YAAY;AAC7E,mBAAO,KAAK,cAAc,WAAW;AAAA,UACvC;AACA,cAAI,KAAK,UAAW,MAAK,oBAAoB,KAAK;AAClD,cAAI,CAAC,KAAK,SAAU;AAKpB,iBAAO,KAAK,UAAU,WAAW;AACjC,cAAI,CAAC,KAAK,iBAAiB;AACzB,iBAAK,kBAAkB;AACvB,kBAAM,gBAAgB,OAAO;AAAA,UAC/B;AACA,eAAK,kBAAkB,KAAK;AAC5B,gBAAM,gBAAgB,SAAS,KAAK,QAAQ;AAC5C;AAAA,QACF;AAAA,QACA,KAAK,qBAAqB;AAIxB,gBAAM,cAAc,KAAK,gBAAgB,KAAK;AAC9C,cAAI,CAAC,YAAa;AAClB,cAAI,KAAK,aAAc,MAAK,mBAAmB,KAAK;AACpD,gBAAM,MAAM,KAAK,WAAW,aAAa,KAAK,IAAI;AAClD,cAAI,KAAK,KAAM,KAAI,OAAO,KAAK;AAC/B,cAAI,KAAK,UAAW,KAAI,YAAY,KAAK;AAEzC,cAAI,CAAC,KAAK,YAAY,IAAI,IAAI,UAAU,GAAG;AAIzC,mBAAO,KAAK,cAAc,WAAW;AACrC,mBAAO,KAAK,UAAU,WAAW;AACjC,iBAAK,YAAY,IAAI,IAAI,UAAU;AACnC,kBAAM,gBAAgB;AAAA,cACpB,WAAW;AAAA,cACX,MAAM,IAAI;AAAA,cACV,YAAY,IAAI;AAAA,YAClB,CAAC;AAAA,UACH;AAKA,cAAI,KAAK,WAAW;AAClB,gBAAI,cAAc,KAAK;AACvB,kBAAM,gBAAgB;AAAA,cACpB,WAAW;AAAA,cACX,MAAM;AAAA,cACN,WAAW,KAAK;AAAA,cAChB,YAAY,IAAI;AAAA,YAClB,CAAC;AAAA,UACH;AACA;AAAA,QACF;AAAA,QACA,KAAK,aAAa;AAMhB,cAAI,CAAC,KAAK,aAAc;AAMxB,iBAAO,KAAK,cAAc,WAAW;AACrC,iBAAO,KAAK,UAAU,WAAW;AAMjC,cAAI,MAAM,KAAK,MAAM,IAAI,KAAK,YAAY;AAC1C,cAAI,CAAC,OAAO,IAAI,QAAS,OAAM,KAAK,WAAW,KAAK,cAAc,KAAK,IAAI;AAC3E,cAAI,OAAO,KAAK;AAChB,cAAI,eAAe,KAAK,UAAU,KAAK,aAAa,CAAC,CAAC;AACtD,cAAI,KAAK,UAAW,KAAI,YAAY,KAAK;AACzC,iBAAO,KAAK,iBAAiB,GAAG;AAChC;AAAA,QACF;AAAA;AAAA;AAAA,QAGA;AACE;AAAA,MACJ;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,CAAC,SAAiC;AAChC,UAAM,aAAa,KAAK,eAAe;AAOvC,WAAO,KAAK,cAAc,UAAU;AACpC,WAAO,KAAK,UAAU,UAAU;AAIhC,eAAW,MAAM,KAAK,WAAW;AAC/B,UAAI,CAAC,GAAI;AACT,YAAM,MAAM,KAAK,MAAM,IAAI,EAAE;AAC7B,UAAI,IAAI,QAAS;AACjB,aAAO,KAAK,iBAAiB,GAAG;AAAA,IAClC;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,CAAC,kBAAkB,YAAgD;AACjE,WAAO,KAAK,cAAc,UAAU;AACpC,WAAO,KAAK,UAAU,UAAU;AAChC,eAAW,MAAM,KAAK,WAAW;AAC/B,UAAI,CAAC,GAAI;AACT,YAAM,MAAM,KAAK,MAAM,IAAI,EAAE;AAC7B,UAAI,IAAI,QAAS;AACjB,aAAO,KAAK,iBAAiB,KAAK,UAAU;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,CAAS,iBACP,KACA,aAAyB,aACD;AACxB,QAAI,UAAU;AACd,QAAI,KAAK,YAAY,IAAI,IAAI,UAAU,GAAG;AAExC,YAAM,gBAAgB;AAAA,QACpB,WAAW;AAAA,QACX,MAAM;AAAA,QACN,YAAY,IAAI;AAAA,QAChB;AAAA,MACF,CAAC;AAAA,IACH;AACA,UAAM,SAAS;AAAA,MACb,MAAM,IAAI;AAAA,MACV,WAAW,IAAI,gBAAgB,IAAI;AAAA,MACnC,YAAY,IAAI;AAAA,MAChB;AAAA,MACA,GAAI,IAAI,cAAc,SAAY,EAAE,WAAW,IAAI,UAAU,IAAI,CAAC;AAAA,IACpE,CAAC;AAED,QAAI,KAAK,qBAAqB,IAAI,aAAa;AAC7C,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBA,CAAS,cAAc,YAAgD;AACrE,QAAI,KAAK,iBAAiB;AACxB,YAAM,gBAAgB,QAAQ,IAAI,UAAU;AAC5C,WAAK,kBAAkB;AAAA,IACzB;AAGA,QAAI,KAAK,kBAAkB,KAAK,sBAAsB,QAAW;AAC/D,YAAM,gBAAgB,KAAK,gBAAgB,YAAY;AAAA,QACrD,GAAI,KAAK,sBAAsB,SAAY,EAAE,WAAW,KAAK,kBAAkB,IAAI,CAAC;AAAA,MACtF,CAAC;AACD,WAAK,iBAAiB;AACtB,WAAK,oBAAoB;AAAA,IAC3B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,CAAS,UAAU,YAAgD;AACjE,QAAI,KAAK,aAAa;AACpB,YAAM,YAAY,QAAQ,IAAI,UAAU;AACxC,WAAK,cAAc;AAAA,IACrB;AAIA,QAAI,KAAK,cAAc,KAAK,kBAAkB,QAAW;AACvD,YAAM,cAAc,KAAK,YAAY,YAAY;AAAA,QAC/C,GAAI,KAAK,aAAa,OAAO,EAAE,OAAO,KAAK,UAAU,IAAI,CAAC;AAAA,QAC1D,GAAI,KAAK,kBAAkB,SAAY,EAAE,WAAW,KAAK,cAAc,IAAI,CAAC;AAAA,MAC9E,CAAC;AACD,WAAK,aAAa;AAClB,WAAK,gBAAgB;AAAA,IAGvB;AAAA,EACF;AAAA;AAAA,EAGA,kBAA2B;AACzB,WAAO,KAAK,iBAAiB;AAAA,EAC/B;AAAA;AAAA,EAGA,mBAAgC;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA,EAEQ,WAAW,aAAqB,MAAmC;AACzE,WAAO,KAAK,MAAM,IAAI,WAAW,KAAK,KAAK,WAAW,aAAa,IAAI;AAAA,EACzE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,WAAW,aAAqB,MAAmC;AACzE,UAAM,MAA2B;AAAA,MAC/B,MAAM,QAAQ;AAAA,MACd,YAAY;AAAA,MACZ,cAAc;AAAA,MACd,YAAY,KAAK,YAAY,SAAS,WAAW;AAAA,MACjD;AAAA,MACA,WAAW;AAAA,MACX,SAAS;AAAA,IACX;AACA,SAAK,MAAM,IAAI,aAAa,GAAG;AAC/B,SAAK,UAAU,KAAK,WAAW;AAC/B,WAAO;AAAA,EACT;AAAA,EAEQ,UAAU,OAAmC;AACnD,WAAO,mBAAmB,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,iBAA6B;AACnC,QACE,KAAK,gBAAgB,QACrB,KAAK,iBAAiB,UACtB,KAAK,iBAAiB,aACtB;AACA,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AACF;AAWO,SAAS,gBACd,QACA,sBAAmC,iBAAiB,GAKpD;AACA,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,MAAqB,CAAC;AAC5B,aAAW,SAAS,QAAQ;AAC1B,eAAW,OAAO,WAAW,UAAU,KAAK,EAAG,KAAI,KAAK,GAAG;AAAA,EAC7D;AACA,aAAW,OAAO,WAAW,OAAO,EAAG,KAAI,KAAK,GAAG;AAEnD,QAAM,gBAAgB,WAAW,iBAAiB;AAClD,QAAM,gBAAgB,eAAe,qBAAqB,aAAa;AACvE,MAAI,KAAK,WAAW,eAAe,aAAa,CAAC;AAEjD,SAAO,EAAE,UAAU,KAAK,eAAe,cAAc;AACvD;AAeO,SAAS,0BAA0B,OAAyB;AACjE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,iBAAiB,YAAa,QAAO;AACzC,QAAM,MAAM;AACZ,MAAI,IAAI,SAAS,cAAe,QAAO;AACvC,MAAI,IAAI,SAAS,IAAI,UAAU,OAAO;AACpC,WAAO,0BAA0B,IAAI,KAAK;AAAA,EAC5C;AACA,SAAO;AACT;AAYO,SAAS,wBAAwB,OAAyB;AAC/D,MAAI,SAAS,KAAM,QAAO;AAC1B,QAAM,MAAM;AACZ,QAAM,MAAM,IAAI,WAAW;AAC3B,MACE,IAAI,WAAW,sCAAsC,KACrD,IAAI,WAAW,uBAAuB,GACtC;AACA,WAAO;AAAA,EACT;AACA,MAAI,IAAI,SAAS,IAAI,UAAU,OAAO;AACpC,WAAO,wBAAwB,IAAI,KAAK;AAAA,EAC1C;AACA,SAAO;AACT;AAaO,SAAS,iBAAiB,OAAyB;AACxD,MAAI,SAAS,KAAM,QAAO;AAE1B,QAAM,MAAM;AASZ,QAAM,SAAS,IAAI,UAAU,IAAI;AACjC,MAAI,OAAO,WAAW,UAAU;AAC9B,QAAI,WAAW,OAAO,WAAW,IAAK,QAAO;AAC7C,QAAI,UAAU,OAAO,UAAU,IAAK,QAAO;AAC3C,QAAI,UAAU,OAAO,UAAU,IAAK,QAAO;AAAA,EAC7C;AAGA,QAAM,iBAAiB,oBAAI,IAAI;AAAA,IAC7B;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AACD,MAAI,IAAI,QAAQ,eAAe,IAAI,IAAI,IAAI,EAAG,QAAO;AAGrD,MAAI,IAAI,SAAS,aAAc,QAAO;AACtC,QAAM,OAAO,GAAG,IAAI,QAAQ,EAAE,IAAI,IAAI,WAAW,EAAE,GAAG,YAAY;AAClE,MACE,wIAAwI;AAAA,IACtI;AAAA,EACF,GACA;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AACT;AAYO,IAAM,kBAAN,MAA8C;AAAA,EAClC;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA;AAAA;AAAA,EAGjB,gBAA6B,iBAAiB;AAAA,EAE9C,YAAY,QAA+B;AAKzC,SAAK,SAAS,IAAI,cAAc;AAAA,MAC9B,OAAO,OAAO;AAAA,MACd,GAAI,OAAO,WAAW,SAAY,EAAE,QAAQ,OAAO,OAAO,IAAI,CAAC;AAAA,MAC/D,GAAI,OAAO,YAAY,SAAY,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;AAAA,MAClE,GAAI,OAAO,eAAe,SAAY,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,IAC7E,CAAC;AAED,SAAK,YAAY,eAAe,MAAM;AACtC,SAAK,mBAAmB,OAAO,oBAAoB;AACnD,SAAK,cAAc,OAAO,eAAe,IAAI,oBAAoB;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA2BA,OAAO,eACL,QACyC;AACzC,UAAM,aAAa,OAAO;AAG1B,QAAI,YAAY,QAAS,QAAO,EAAE,QAAQ,UAAU;AAKpD,QAAI;AACJ,QAAI;AACF,mBAAa,sBAAsB,OAAO,WAAW;AAAA,IACvD,SAAS,KAAK;AACZ,aAAO;AAAA,QACL,QAAQ;AAAA,QACR,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,MAC1D;AAAA,IACF;AAEA,UAAM,aAAa,IAAI,gBAAgB,KAAK,WAAW;AAGvD,UAAM,KAAK,IAAI,gBAAgB;AAC/B,UAAM,cAAc,MAAY,GAAG,MAAM;AACzC,gBAAY,iBAAiB,SAAS,aAAa,EAAE,MAAM,KAAK,CAAC;AAEjE,QAAI,WAAW;AACf,QAAI,QAA8C;AAClD,UAAM,aAAa,MAAY;AAC7B,UAAI,OAAO;AACT,qBAAa,KAAK;AAClB,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,UAAM,WAAW,MAAY;AAC3B,UAAI,KAAK,oBAAoB,EAAG;AAChC,iBAAW;AACX,cAAQ,WAAW,MAAM;AACvB,mBAAW;AACX,WAAG,MAAM;AAAA,MACX,GAAG,KAAK,gBAAgB;AAAA,IAC1B;AAIA,QAAI,UAA6B;AACjC,QAAI;AACF,YAAM,KAAK,KAAK,WAAW,YAAY,GAAG,MAAM,EAAE,OAAO,aAAa,EAAE;AACxE,iBAAS;AAWP,YAAI,YAAY,SAAS;AACvB,oBAAU,EAAE,QAAQ,UAAU;AAC9B;AAAA,QACF;AAIA,iBAAS;AACT,YAAI;AACJ,YAAI;AACF,gBAAM,MAAM,GAAG,KAAK;AAAA,QACtB,UAAE;AACA,qBAAW;AAAA,QACb;AACA,YAAI,IAAI,KAAM;AACd,YAAI,YAAY,SAAS;AACvB,oBAAU,EAAE,QAAQ,UAAU;AAC9B;AAAA,QACF;AACA,mBAAW,OAAO,WAAW,UAAU,IAAI,KAAK,EAAG,OAAM;AAAA,MAC3D;AAAA,IACF,SAAS,OAAO;AAGd,UAAI,YAAY,SAAS;AACvB,kBAAU,EAAE,QAAQ,UAAU;AAAA,MAChC,WAAW,UAAU;AACnB,kBAAU,EAAE,QAAQ,UAAU;AAAA,MAChC,WAAW,0BAA0B,KAAK,KAAK,wBAAwB,KAAK,GAAG;AAI7E,kBAAU;AAAA,UACR,QAAQ;AAAA,UACR,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAChE;AAAA,MACF,WAAW,iBAAiB,KAAK,GAAG;AAClC,kBAAU,EAAE,QAAQ,UAAU;AAAA,MAChC,WAAY,OAA6B,SAAS,cAAc;AAC9D,kBAAU,EAAE,QAAQ,UAAU;AAAA,MAChC,OAAO;AACL,kBAAU;AAAA,UACR,QAAQ;AAAA,UACR,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAChE;AAAA,MACF;AAAA,IACF,UAAE;AACA,iBAAW;AACX,kBAAY,oBAAoB,SAAS,WAAW;AAAA,IACtD;AAYA,QAAI,CAAC,WAAW,CAAC,WAAW,gBAAgB,GAAG;AAC7C,UAAI,YAAY,QAAS,WAAU,EAAE,QAAQ,UAAU;AAAA,eAC9C,SAAU,WAAU,EAAE,QAAQ,UAAU;AAAA,IACnD;AAEA,QAAI,SAAS;AAEX,YAAM,SAAqB,QAAQ,WAAW,cAAc,WAAW,QAAQ;AAC/E,iBAAW,OAAO,WAAW,kBAAkB,MAAM,EAAG,OAAM;AAC9D,aAAO;AAAA,IACT;AAGA,eAAW,OAAO,WAAW,OAAO,EAAG,OAAM;AAC7C,UAAM,gBAAgB,WAAW,iBAAiB;AAClD,SAAK,gBAAgB,eAAe,KAAK,eAAe,aAAa;AACrE,UAAM,WAAW,KAAK,eAAe,aAAa;AAClD,WAAO,EAAE,QAAQ,YAAY;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWA,WAAW,SAA8B;AACvC,QAAI,QAAQ,WAAW,EAAG;AAI1B,eAAW,OAAO,SAAS;AACzB,YAAM,IAAI,IAAI;AACd,UAAI,EAAE,SAAS,eAAe,EAAE,cAAc;AAC5C,aAAK,YAAY,SAAS,EAAE,YAAY;AAAA,MAC1C;AAAA,IACF;AACA,SAAK,OAAO,WAAW,0BAA0B,OAAO,CAAC;AAAA,EAC3D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQU,WAAW,YAAwB,QAA8C;AACzF,WAAO,KAAK,OAAO,0BAA0B;AAAA,MAC3C,SAAS;AAAA,MACT,QAAQ,KAAK;AAAA,MACb;AAAA,IACF,CAAC;AAAA,EACH;AACF;AAOO,SAAS,iBAAiB,MAAgE;AAC/F,MAAI,SAAS,OAAW,QAAO;AAC/B,QAAM,QAAkD;AAAA,IACtD,MAAM,cAAc;AAAA,IACpB,KAAK,cAAc;AAAA,IACnB,QAAQ,cAAc;AAAA,IACtB,MAAM,cAAc;AAAA,IACpB,OAAO,cAAc;AAAA,EACvB;AACA,SAAO,MAAM,IAAI;AACnB;AAGO,SAAS,yBAAyB,OAAuC;AAC9E,SAAO,MAAM,IAAI,CAAC,UAAU;AAAA,IAC1B,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,GAAI,KAAK,eAAe,SAAY,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,EACzE,EAAE;AACJ;AAGO,SAAS,eAAe,QAA0C;AACvE,QAAM,YAAuB;AAAA,IAC3B,OAAO,yBAAyB,OAAO,KAAK;AAAA,EAC9C;AACA,MAAI,OAAO,iBAAiB,QAAW;AACrC,cAAU,gBAAgB,OAAO;AAAA,EACnC;AACA,MAAI,OAAO,cAAc,QAAW;AAClC,cAAU,aAAa,OAAO;AAAA,EAChC;AACA,QAAM,WAAW,iBAAiB,OAAO,aAAa;AACtD,MAAI,aAAa,QAAW;AAC1B,cAAU,iBAAiB;AAAA,EAC7B;AACA,SAAO;AACT;;;AE9gCA,OAAOC,WAAU;;;ACIjB,SAAS,aAAgC;;;ACFzC,SAAS,kBAAkB;AAG3B,IAAM,cAAc,KAAK,KAAK,KAAK;AAEnC,IAAM,gBAAgB,KAAK;AAgB3B,IAAM,kBAAkB,oBAAI,IAAwC;AACpE,IAAI,oBAAoB;AACxB,SAAS,iBAAuB;AAC9B,MAAI,kBAAmB;AACvB,sBAAoB;AACpB,UAAQ,GAAG,QAAQ,MAAM;AACvB,eAAW,KAAK,gBAAiB,GAAE,YAAY;AAAA,EACjD,CAAC;AACH;AAEO,IAAM,qBAAN,MAAmD;AAAA,EACvC,QAAQ,oBAAI,IAAe;AAAA,EAC3B;AAAA,EACA;AAAA,EACA;AAAA,EACT,WAAW;AAAA,EAEnB,YAAY,MAA8C;AACxD,SAAK,WAAW,KAAK;AACrB,SAAK,WAAW,KAAK;AACrB,oBAAgB,IAAI,IAAqD;AACzE,mBAAe;AACf,SAAK,YAAY,YAAY,MAAM,KAAK,SAAS,GAAG,aAAa;AACjE,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA,EAEA,IAAI,OAAe;AACjB,WAAO,KAAK,MAAM;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,SAAS,cAAgC;AACvC,QAAI,KAAK,MAAM,OAAO,KAAK,SAAU,QAAO;AAC5C,QAAI,QAAuB;AAC3B,QAAI,UAAU,OAAO;AACrB,eAAW,CAAC,IAAI,CAAC,KAAK,KAAK,OAAO;AAChC,UAAI,CAAC,EAAE,SAAS;AACd,aAAK,OAAO,EAAE;AACd,eAAO;AAAA,MACT;AACA,UAAI,EAAE,WAAW,SAAS;AACxB,kBAAU,EAAE;AACZ,gBAAQ;AAAA,MACV;AAAA,IACF;AACA,QAAI,CAAC,aAAc,QAAO;AAC1B,QAAI,MAAO,MAAK,OAAO,KAAK;AAC5B,WAAO,KAAK,MAAM,OAAO,KAAK;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS,MAAS,iBAAkC;AAClD,SAAK,aAAa;AAClB,QAAI,KAAK,kBAAkB,GAAG,KAAK,QAAQ,IAAI,eAAe,KAAK,KAAK,SAAS;AACjF,WAAO,KAAK,MAAM,IAAI,EAAE,EAAG,MAAK,KAAK,SAAS;AAC9C,SAAK,WAAW,KAAK,IAAI;AACzB,SAAK,MAAM,IAAI,IAAI,IAAI;AACvB,WAAO;AAAA,EACT;AAAA,EAEQ,WAAmB;AACzB,WAAO,GAAG,KAAK,QAAQ,IAAI,WAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,CAAC,CAAC;AAAA,EACvE;AAAA;AAAA,EAGA,IAAI,IAA2B;AAC7B,QAAI,KAAK,SAAU,QAAO;AAC1B,UAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,QAAI,EAAG,GAAE,WAAW,KAAK,IAAI;AAC7B,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,OAAO,IAAkB;AACvB,UAAM,IAAI,KAAK,MAAM,IAAI,EAAE;AAC3B,QAAI,CAAC,EAAG;AACR,SAAK,MAAM,OAAO,EAAE;AACpB,MAAE,KAAK;AAAA,EACT;AAAA;AAAA,EAGA,UAAgB;AACd,eAAW,KAAK,KAAK,MAAM,OAAO,EAAG,GAAE,KAAK;AAC5C,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA;AAAA,EAGA,cAAoB;AAClB,eAAW,KAAK,KAAK,MAAM,OAAO,EAAG,GAAE,SAAS;AAChD,SAAK,MAAM,MAAM;AAAA,EACnB;AAAA;AAAA,EAGA,UAAgB;AACd,QAAI,KAAK,SAAU;AACnB,SAAK,WAAW;AAChB,kBAAc,KAAK,SAAS;AAC5B,oBAAgB,OAAO,IAAqD;AAC5E,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA,EAGA,IAAI,aAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGQ,WAAiB;AACvB,UAAM,SAAS,KAAK,IAAI,IAAI;AAC5B,eAAW,CAAC,IAAI,CAAC,KAAK,KAAK,OAAO;AAChC,UAAI,EAAE,YAAY,OAAQ,MAAK,OAAO,EAAE;AAAA,IAC1C;AAAA,EACF;AAAA,EAEQ,eAAqB;AAC3B,QAAI,KAAK,UAAU;AACjB,YAAM,IAAI,MAAM,sCAAsC;AAAA,IACxD;AAAA,EACF;AACF;;;ACjKO,IAAM,eAAe;AAE5B,IAAM,oBAAoB;AAGnB,SAAS,WAAW,KAAc,UAAkB,WAA4B;AACrF,QAAM,IAAI,OAAO,QAAQ,YAAY,OAAO,SAAS,GAAG,IAAI,MAAM;AAClE,QAAM,QAAQ,KAAK,IAAI,GAAG,YAAY;AACtC,MAAI,cAAc,UAAa,aAAa,EAAG,QAAO;AACtD,SAAO,KAAK,IAAI,OAAO,KAAK,IAAI,YAAY,mBAAmB,YAAY,CAAC;AAC9E;;;ACdO,IAAM,aAAN,MAAiB;AAAA,EACd;AAAA,EACA;AAAA,EAER,cAAc;AACZ,SAAK,IAAI;AAAA,EACX;AAAA,EAEQ,MAAY;AAClB,SAAK,UAAU,IAAI,QAAc,CAAC,YAAY;AAC5C,WAAK,UAAU;AAAA,IACjB,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SAAe;AACb,UAAM,IAAI,KAAK;AACf,SAAK,IAAI;AACT,MAAE;AAAA,EACJ;AAAA;AAAA,EAGA,MAAM,KAAK,IAA2B;AACpC,QAAI,QAA8C;AAClD,UAAM,UAAU,IAAI,QAAc,CAAC,YAAY;AAE7C,cAAQ,WAAW,SAAS,EAAE;AAAA,IAChC,CAAC;AACD,QAAI;AACF,YAAM,QAAQ,KAAK,CAAC,KAAK,SAAS,OAAO,CAAC;AAAA,IAC5C,UAAE;AACA,UAAI,MAAO,cAAa,KAAK;AAAA,IAC/B;AAAA,EACF;AACF;;;AClCO,IAAM,mBAAN,MAAuB;AAAA;AAAA;AAAA,EAK5B,YACmB,KACA,WACjB;AAFiB;AACA;AAAA,EAChB;AAAA,EAFgB;AAAA,EACA;AAAA,EANX,OAAO;AAAA,EACP,UAAU;AAAA,EAQlB,IAAI,UAAmB;AACrB,WAAO,KAAK,KAAK,WAAW,KAAK,KAAK,YAAY;AAAA,EACpD;AAAA,EAEA,OAAO,OAAqB;AAC1B,SAAK,QAAQ;AACb,QAAI,KAAK,KAAK,SAAS,KAAK,KAAK;AAC/B,YAAM,OAAO,KAAK,KAAK,SAAS,KAAK;AACrC,WAAK,OAAO,KAAK,KAAK,MAAM,IAAI;AAChC,WAAK,WAAW;AAAA,IAClB;AAAA,EACF;AAAA;AAAA,EAGA,QAAgB;AACd,QAAI,KAAK,QAAS,QAAO;AACzB,UAAM,IAAI,KAAK;AACf,SAAK,OAAO;AACZ,QAAI,KAAK,UAAU,GAAG;AACpB,YAAM,IAAI,KAAK;AACf,WAAK,UAAU;AACf,aAAO,QAAQ,CAAC,aAAa,KAAK,SAAS;AAAA,EAAkB,CAAC;AAAA,IAChE;AACA,WAAO;AAAA,EACT;AACF;;;AJdA,IAAM,yBAAyB,QAAQ,aAAa;AAGpD,IAAM,qBAAqB;AAE3B,IAAM,oBAAoB,OAAO;AAMjC,IAAM,mBAAmB;AAkBlB,IAAM,iBAAN,MAAqB;AAAA;AAAA,EAE1B,WAAmB,KAAK,IAAI;AAAA,EAEX;AAAA,EACA,SAAS,IAAI,iBAAiB,mBAAmB,gBAAgB;AAAA,EAC1E,SAAS;AAAA,EACT,WAA+B;AAAA,EAC/B,aAA2B;AAAA,EAC3B,SAAS;AAAA,EACT,YAAkD;AAAA;AAAA,EAEzC,aAAa,IAAI,WAAW;AAAA,EAE7C,YAAY,MAAoB;AAC9B,SAAK,QAAQ,MAAM,QAAQ,CAAC,OAAO,KAAK,GAAG,GAAG;AAAA,MAC5C,KAAK,KAAK;AAAA,MACV,KAAK,KAAK;AAAA,MACV,UAAU;AAAA;AAAA,MACV,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AACD,SAAK,MAAM,QAAQ,YAAY,MAAM;AACrC,SAAK,MAAM,QAAQ,YAAY,MAAM;AAIrC,SAAK,MAAM,OAAO,GAAG,SAAS,MAAM;AAAA,IAAC,CAAC;AACtC,SAAK,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc,KAAK,WAAW,CAAC,CAAC;AAC/D,SAAK,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAc,KAAK,WAAW,CAAC,CAAC;AAI/D,SAAK,MAAM,GAAG,QAAQ,CAAC,MAAM,WAAW,KAAK,WAAW,EAAE,MAAM,OAAO,CAAC,CAAC;AACzE,SAAK,MAAM,GAAG,SAAS,CAAC,QAAQ,KAAK,YAAY,GAAG,CAAC;AAAA,EACvD;AAAA;AAAA,EAGQ,YAAY,KAA2B;AAC7C,QAAI;AACF,UAAI,0BAA0B,OAAO,KAAK,MAAM,QAAQ,YAAY,KAAK,MAAM,MAAM,GAAG;AACtF,gBAAQ,KAAK,CAAC,KAAK,MAAM,KAAK,GAAG;AAAA,MACnC,OAAO;AACL,aAAK,MAAM,KAAK,GAAG;AAAA,MACrB;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EAEQ,WAAW,OAAqB;AACtC,SAAK,OAAO,OAAO,KAAK;AACxB,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA,EACQ,WAAW,MAAyB;AAC1C,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,WAAW;AAChB,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA,EACQ,YAAY,KAAkB;AACpC,QAAI,KAAK,OAAQ;AACjB,SAAK,aAAa;AAClB,SAAK,SAAS;AACd,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAGA,IAAI,UAAmB;AACrB,WAAO,CAAC,KAAK;AAAA,EACf;AAAA,EACA,IAAI,OAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA,EACA,IAAI,QAAsB;AACxB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAO,QAAQ,SAAiB,QAA8C;AAC5E,UAAM,QAAQ,KAAK,IAAI;AACvB,UAAM,UAAU,MAAY,KAAK,WAAW,OAAO;AACnD,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,QAAI;AAEF,aAAO,CAAC,KAAK,QAAQ;AACnB,cAAM,QAAQ,KAAK,OAAO,MAAM;AAChC,YAAI,MAAO,OAAM;AACjB,YAAI,QAAQ,QAAS;AACrB,cAAM,YAAY,WAAW,KAAK,IAAI,IAAI;AAC1C,YAAI,aAAa,GAAG;AAClB,gBAAMC,QAAO,KAAK,OAAO,MAAM;AAC/B,cAAIA,MAAM,OAAMA;AAChB;AAAA,QACF;AAGA,YAAI,CAAC,KAAK,OAAO,QAAS;AAC1B,cAAM,KAAK,WAAW,KAAK,SAAS;AAAA,MACtC;AAEA,YAAM,OAAO,KAAK,OAAO,MAAM;AAC/B,UAAI,KAAM,OAAM;AAChB,YAAM,aAAa,KAAK,IAAI;AAC5B,iBAAS;AACP,YAAI,CAAC,KAAK,OAAO,SAAS;AACxB,gBAAM,KAAK,OAAO,MAAM;AACxB;AAAA,QACF;AACA,cAAM,OAAO,sBAAsB,KAAK,IAAI,IAAI;AAChD,YAAI,QAAQ,EAAG;AACf,cAAM,KAAK,WAAW,KAAK,IAAI;AAC/B,YAAI,KAAK,OAAO,QAAS;AAAA,MAC3B;AACA,YAAM,OAAO,KAAK,OAAO,MAAM;AAC/B,UAAI,KAAM,OAAM;AAAA,IAClB,UAAE;AACA,cAAQ,oBAAoB,SAAS,OAAO;AAAA,IAC9C;AAAA,EACF;AAAA,EAEA,MAAM,OAAqB;AACzB,SAAK,WAAW,KAAK,IAAI;AACzB,QAAI;AACF,UAAI,CAAC,KAAK,MAAM,SAAS,KAAK,MAAM,MAAM,UAAW;AACrD,WAAK,MAAM,MAAM,MAAM,OAAO,MAAM;AAAA,MAAC,CAAC;AAAA,IACxC,QAAQ;AAAA,IAER;AAAA,EACF;AAAA,EACA,YAAkB;AAChB,SAAK,WAAW,KAAK,IAAI;AACzB,SAAK,YAAY,QAAQ;AAAA,EAC3B;AAAA;AAAA,EAGA,OAAa;AACX,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,YAAY,SAAS;AAG1B,SAAK,YAAY,WAAW,MAAM,KAAK,YAAY,SAAS,GAAG,gBAAgB;AAC/E,SAAK,UAAU,QAAQ;AAAA,EACzB;AAAA;AAAA,EAGA,WAAiB;AACf,SAAK,SAAS;AACd,QAAI,KAAK,WAAW;AAClB,mBAAa,KAAK,SAAS;AAC3B,WAAK,YAAY;AAAA,IACnB;AACA,SAAK,YAAY,SAAS;AAAA,EAC5B;AACF;AAGO,SAAS,cAAc,MAAsC;AAClE,MAAI,CAAC,KAAM,QAAO,EAAE,YAAY,YAAY;AAC5C,MAAI,KAAK,OAAQ,QAAO,EAAE,YAAY,UAAU,MAAM,yBAAyB,KAAK,MAAM,IAAI;AAC9F,MAAI,KAAK,SAAS;AAChB,WAAO,EAAE,YAAY,UAAU,MAAM,eAAe,KAAK,QAAQ,SAAS,IAAI;AAChF,SAAO,EAAE,YAAY,YAAY;AACnC;;;AKpNA,IAAM,eAAe;AAUrB,IAAM,eAAkC;AAAA,EACtC,YAAY;AAAA,EACZ,qBAAqB;AAAA,EACrB,MAAM;AAAA,EACN,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEO,IAAM,wBAAN,MAA4B;AAAA,EAChB,WAAW,IAAI,mBAAmC;AAAA,IACjE,UAAU;AAAA,IACV,UAAU;AAAA,EACZ,CAAC;AAAA;AAAA,EAGgB;AAAA,EAEjB,YAAY,MAA2C;AACrD,SAAK,QAAQ,MAAM,SAAS,CAAC;AAAA,EAC/B;AAAA;AAAA,EAGA,MAAM,MAAoD;AACxD,QAAI,KAAK,SAAS,YAAY;AAC5B,YAAM,IAAI,MAAM,kCAAkC;AAAA,IACpD;AACA,WAAO,IAAI,eAAe;AAAA,MACxB,KAAK,KAAK;AAAA,MACV,KAAK,KAAK;AAAA;AAAA;AAAA;AAAA,MAIV,KAAK,EAAE,GAAG,QAAQ,KAAK,GAAG,KAAK,OAAO,GAAG,aAAa;AAAA,IACxD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,SAAS,SAAiC;AACxC,SAAK,SAAS,SAAS,IAAI;AAC3B,WAAO,KAAK,SAAS,SAAS,OAAO;AAAA,EACvC;AAAA;AAAA,EAGA,IAAI,WAA+C;AACjD,WAAO,KAAK,SAAS,IAAI,SAAS;AAAA,EACpC;AAAA;AAAA,EAGA,OAAO,WAAyB;AAC9B,SAAK,SAAS,OAAO,SAAS;AAAA,EAChC;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,SAAS,QAAQ;AAAA,EACxB;AACF;;;ACxEO,IAAM,wBAAwB;AAE9B,IAAM,yBAAyB;AAE/B,IAAM,8BAA8B;;;APepC,IAAM,oBAAoB;AAU1B,SAAS,sBACd,YACA,UACa;AACb,QAAM,UAAU,UAAU;AAC1B,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,QACL,MACA,KACgD;AAChD,YAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,YAAM,QAAQ,CAAC,WACb,sBAAsB,EAAE,WAAW,SAAS,QAAQ,WAAW,CAAC;AAElE,UAAI,CAAC,SAAS;AACZ,cAAM,MAAM,mEAAmE;AAC/E,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,MAAM,KAAK,KAAK;AACtB,UAAI,OAAO,QAAQ,YAAY,IAAI,WAAW,GAAG;AAC/C,cAAM,MAAM,mDAAmD;AAC/D,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,aAAa,KAAK,SAAS;AACjC,YAAM,UACJ,OAAO,eAAe,YAAY,WAAW,SAAS,IAClDC,MAAK,QAAQ,IAAI,cAAc,UAAU,IACzC,IAAI;AACV,YAAM,UAAU;AAAA,QACd,KAAK,eAAe;AAAA,QACpB;AAAA,QACA,WAAW;AAAA,MACb;AAGA,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AAEpD,UAAI;AACJ,UAAI;AACF,kBAAU,QAAQ,MAAM,EAAE,KAAK,KAAK,QAAQ,CAAC;AAAA,MAC/C,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAM,MAAM,iBAAiB,OAAO,GAAG;AACvC,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAIA,YAAM,UAAU,MAAY,QAAQ,KAAK;AACzC,UAAI,aAAa;AACjB,cAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,UAAI;AACF,yBAAiB,SAAS,QAAQ,QAAQ,SAAS,MAAM,EAAG,OAAM,MAAM,KAAK;AAE7E,YAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AACpD,YAAI,QAAQ,SAAS;AAGnB,gBAAM,KAAK,QAAQ,SAAS,OAAO;AACnC,uBAAa;AACb,iBAAO;AAAA,YACL,YAAY;AAAA,YACZ,MAAM,oCAAoC,EAAE;AAAA,UAC9C;AAAA,QACF;AAGA,YAAI,QAAQ,OAAO;AACjB,iBAAO,EAAE,YAAY,UAAU,MAAM,iBAAiB,QAAQ,MAAM,OAAO,IAAI;AAAA,QACjF;AACA,eAAO,cAAc,QAAQ,IAAI;AAAA,MACnC,UAAE;AACA,gBAAQ,oBAAoB,SAAS,OAAO;AAC5C,YAAI,CAAC,WAAY,SAAQ,KAAK;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AACF;;;AQ5FO,IAAM,qBAAqB;AAGlC,IAAM,YAAY,OAAO,aAAa,CAAC;AAEhC,SAAS,uBACd,YACA,UACa;AACb,QAAM,UAAU,UAAU;AAC1B,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,QACL,MACA,KACgD;AAChD,YAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,YAAM,QAAQ,CAAC,WACb,sBAAsB,EAAE,WAAW,SAAS,QAAQ,WAAW,CAAC;AAElE,UAAI,CAAC,SAAS;AACZ,cAAM,MAAM,oEAAoE;AAChF,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,YAAY,KAAK,YAAY;AACnC,UAAI,OAAO,cAAc,YAAY,UAAU,WAAW,GAAG;AAC3D,cAAM,MAAM,2DAA2D;AACvE,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,YAAM,UAAU,QAAQ,IAAI,SAAS;AACrC,UAAI,CAAC,SAAS;AACZ,cAAM;AAAA,UACJ,4CAA4C,SAAS;AAAA,QACvD;AACA,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,QAAQ,OAAO,KAAK,OAAO,MAAM,WAAY,KAAK,OAAO,IAAe;AAC9E,YAAM,QAAQ,MAAM,WAAW;AAC/B,YAAM,UAAU;AAAA,QACd,KAAK,eAAe;AAAA,QACpB,QAAQ,8BAA8B;AAAA,QACtC,WAAW;AAAA,MACb;AAEA,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AAMpD,UAAI,CAAC,OAAO;AACV,YAAI,UAAU,UAAW,SAAQ,UAAU;AAAA,iBAClC,MAAM,SAAS,SAAS,GAAG;AAClC,gBAAM;AAAA,YACJ;AAAA,UACF;AACA,iBAAO,EAAE,YAAY,SAAS;AAAA,QAChC,MAAO,SAAQ,MAAM,KAAK;AAAA,MAC5B;AAEA,uBAAiB,SAAS,QAAQ,QAAQ,SAAS,MAAM,EAAG,OAAM,MAAM,KAAK;AAE7E,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AACpD,UAAI,QAAQ,SAAS;AACnB,eAAO;AAAA,UACL,YAAY;AAAA,UACZ,MAAM,0CAA0C,SAAS;AAAA,QAC3D;AAAA,MACF;AAEA,cAAQ,OAAO,SAAS;AACxB,UAAI,QAAQ,OAAO;AACjB,eAAO,EAAE,YAAY,UAAU,MAAM,iBAAiB,QAAQ,MAAM,OAAO,IAAI;AAAA,MACjF;AACA,aAAO,cAAc,QAAQ,IAAI;AAAA,IACnC;AAAA,EACF;AACF;;;ACxFA,IAAMC,gBAAe;AAEd,IAAM,yBAAN,MAA6B;AAAA,EACjB,WAAW,IAAI,mBAA2C;AAAA,IACzE,UAAU;AAAA,IACV,UAAUA;AAAA,EACZ,CAAC;AAAA;AAAA,EAGD,IAAI,aAAsB;AACxB,WAAO,KAAK,SAAS;AAAA,EACvB;AAAA;AAAA,EAGA,WAAoB;AAClB,WAAO,KAAK,SAAS,SAAS,KAAK;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,SAAS,SAAyC;AAKhD,SAAK,SAAS,SAAS,KAAK;AAC5B,WAAO,KAAK,SAAS,SAAS,SAAS,QAAQ,UAAU,MAAM,EAAE,CAAC;AAAA,EACpE;AAAA;AAAA,EAGA,IAAI,YAAwD;AAC1D,WAAO,KAAK,SAAS,IAAI,UAAU;AAAA,EACrC;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,SAAS,QAAQ;AAAA,EACxB;AACF;;;ACzBA,IAAM,qBAAqB;AAE3B,IAAMC,qBAAoB,OAAO;AAe1B,IAAM,yBAAN,MAA6B;AAAA;AAAA,EAElC,WAAmB,KAAK,IAAI;AAAA,EAEX;AAAA,EACA,YAAY,IAAI,gBAAgB;AAAA,EAEzC,WAA0B,CAAC;AAAA,EAClB,aAAa,IAAI,iBAAiBA,oBAAmB,yBAAyB;AAAA,EAEvF,YAAY;AAAA,EACZ,WAAgC;AAAA,EAChC,SAAS;AAAA,EAEA,YAA+B,CAAC;AAAA,EACzC,OAA+D;AAAA,EAC/D,YAAY;AAAA,EACZ,mBAAmB;AAAA;AAAA,EAGV,aAAa,IAAI,WAAW;AAAA,EAE7C,YAAY,QAAwB;AAClC,SAAK,SAAS;AAAA,EAChB;AAAA;AAAA,EAGA,IAAI,YAAoB;AACtB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA;AAAA,EAGA,IAAI,UAAmB;AACrB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,OAA4B;AAC9B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,IAAI,mBAA2B;AAC7B,WAAO,KAAK,UAAU;AAAA,EACxB;AAAA;AAAA,EAGA,IAAI,aAAsB;AACxB,WAAO,KAAK,SAAS,SAAS,KAAK,CAAC,KAAK,WAAW;AAAA,EACtD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,SAAS,QAAsB;AAC7B,QAAI,KAAK,OAAQ,OAAM,IAAI,MAAM,2BAA2B;AAC5D,QAAI,KAAK,UAAW,OAAM,IAAI,MAAM,2BAA2B;AAC/D,SAAK,YAAY;AACjB,SAAK,WAAW;AAChB,SAAK,KAAK,KAAK,MAAM;AAAA,EACvB;AAAA;AAAA,EAGA,gBAA+B;AAC7B,QAAI,KAAK,SAAS,WAAW,EAAG,QAAO,CAAC;AACxC,UAAM,MAAM,KAAK;AACjB,SAAK,WAAW,CAAC;AACjB,WAAO;AAAA,EACT;AAAA;AAAA,EAGA,YAAoB;AAClB,WAAO,KAAK,WAAW,MAAM;AAAA,EAC/B;AAAA;AAAA,EAGA,SAAe;AACb,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAGA,MAAM,SAAS,IAA2B;AACxC,UAAM,KAAK,WAAW,KAAK,EAAE;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,mBAAmB,SAAgC;AACjD,UAAM,QAAQ,EAAE,KAAK;AACrB,QAAI;AACJ,UAAM,WAAW,IAAI,QAAc,CAAC,YAAY;AAC9C,iBAAW;AAAA,IACb,CAAC;AACD,SAAK,OAAO,EAAE,SAAS,SAAS;AAChC,SAAK,KAAK,cAAc;AACxB,WAAO,MAAM;AACX,UAAI,KAAK,cAAc,MAAO,MAAK,OAAO;AAC1C,eAAS;AAAA,IACX;AAAA,EACF;AAAA;AAAA,EAGA,OAAa;AACX,QAAI,KAAK,OAAQ;AACjB,SAAK,SAAS;AACd,SAAK,UAAU,MAAM;AACrB,eAAW,OAAO,CAAC,GAAG,KAAK,SAAS,EAAG,MAAK,OAAO,KAAK,MAAM;AAE9D,QAAI,CAAC,KAAK,UAAW,MAAK,OAAO,QAAQ;AACzC,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA,EAGA,WAAiB;AACf,SAAK,KAAK;AAAA,EACZ;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,KAAK,QAA+B;AAChD,QAAI,WAAW;AACf,QAAI,aAA4B;AAChC,QAAI;AACF,uBAAiB,OAAO,KAAK,OAAO,IAAI;AAAA,QACtC;AAAA,QACA,QAAQ,KAAK,UAAU;AAAA,QACvB,SAAS,KAAK;AAAA,MAChB,CAAC,GAAG;AACF,aAAK,cAAc,GAAG;AACtB,aAAK,IAAI,QAAQ,UAAU,OAAO,GAAG;AACnC,gBAAM,IAAI,IAAI;AAUd,cAAI,EAAE,SAAS,SAAS;AACtB,yBAAa,EAAE,UAAU;AAAA,UAC3B,WACE,EAAE,SAAS,kBACX,EAAE,eAAe,WACjB,OAAO,EAAE,SAAS,YAClB,EAAE,MACF;AACA,uBAAW;AACX,iBAAK,WAAW,EAAE,IAAI;AAAA,UACxB;AAAA,QACF;AACA,aAAK,WAAW,OAAO;AAAA,MACzB;AACA,UAAI,eAAe,MAAM;AACvB,aAAK,WAAW,EAAE,QAAQ,UAAU,MAAM,sBAAsB,UAAU,IAAI;AAAA,MAChF,WAAW,CAAC,UAAU;AACpB,aAAK,WAAW;AAAA,UACd,QAAQ;AAAA,UACR,MAAM;AAAA,QACR;AAAA,MACF,OAAO;AACL,aAAK,WAAW,EAAE,QAAQ,YAAY;AAAA,MACxC;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,WAAK,WAAW,EAAE,QAAQ,UAAU,MAAM,oBAAoB,OAAO,IAAI;AAAA,IAC3E,UAAE;AACA,WAAK,YAAY;AACjB,UAAI,KAAK,OAAQ,MAAK,OAAO,QAAQ;AACrC,WAAK,WAAW,OAAO;AAAA,IACzB;AAAA,EACF;AAAA,EAEQ,cAAc,KAAwB;AAC5C,SAAK,SAAS,KAAK,GAAG;AAEtB,QAAI,KAAK,SAAS,SAAS,mBAAoB,MAAK,SAAS,MAAM;AAAA,EACrE;AAAA,EAEQ,WAAW,MAAoB;AACrC,SAAK,WAAW,OAAO,IAAI;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA,EAOiB,eAA0B,CAACC,cAAa;AACvD,QAAI,KAAK,OAAQ,QAAO,QAAQ,QAAQ,MAAM;AAC9C,WAAO,IAAI,QAA0B,CAAC,YAAY;AAChD,WAAK,UAAU,KAAK,EAAE,UAAAA,WAAU,SAAS,OAAO,QAAQ,CAAC;AACzD,WAAK,WAAW,OAAO;AACvB,WAAK,KAAK,cAAc;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA;AAAA,EAGQ,OAAO,KAAsB,UAAkC;AACrE,QAAI,IAAI,QAAS;AACjB,QAAI,UAAU;AACd,UAAM,MAAM,KAAK,UAAU,QAAQ,GAAG;AACtC,QAAI,OAAO,EAAG,MAAK,UAAU,OAAO,KAAK,CAAC;AAC1C,QAAI,QAAQ,QAAQ;AACpB,SAAK,WAAW,OAAO;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAc,gBAA+B;AAC3C,QAAI,KAAK,iBAAkB;AAC3B,SAAK,mBAAmB;AACxB,QAAI;AACF,aAAO,KAAK,QAAQ,KAAK,UAAU,SAAS,GAAG;AAC7C,cAAM,OAAO,KAAK;AAClB,cAAM,MAAM,KAAK,UAAU,CAAC;AAC5B,cAAM,SAAS,KAAK,QAAQ,IAAI,QAAQ,EAAE;AAAA,UACxC,CAAC,MAAM,KAAK,OAAO,KAAK,CAAC;AAAA,UACzB,MAAM,KAAK,OAAO,KAAK,MAAM;AAAA;AAAA,QAC/B;AACA,cAAM,QAAQ,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAC1C,YAAI,IAAI,QAAS;AACjB,YAAI,KAAK,QAAQ,KAAK,SAAS,KAAM;AACrC;AAAA,MACF;AAAA,IACF,UAAE;AACA,WAAK,mBAAmB;AAAA,IAC1B;AAAA,EACF;AACF;AAGO,SAAS,sBAAsB,MAAuC;AAC3E,MAAI,CAAC,KAAM,QAAO,EAAE,YAAY,YAAY;AAC5C,SAAO,EAAE,YAAY,KAAK,QAAQ,GAAI,KAAK,SAAS,SAAY,EAAE,MAAM,KAAK,KAAK,IAAI,CAAC,EAAG;AAC5F;;;ACvSO,IAAM,4BAA4B;AAElC,IAAM,iCAAiC;;;ACM9C,gBAAuB,cACrB,SACA,MAC6B;AAC7B,QAAM,EAAE,SAAS,YAAY,QAAQ,QAAQ,IAAI;AACjD,QAAM,QAAQ,CAAC,WACb,sBAAsB,EAAE,WAAW,SAAS,QAAQ,WAAW,CAAC;AAClE,QAAM,SAAS,UAAU,QAAQ,mBAAmB,OAAO,IAAI;AAG/D,QAAM,UAAU,MAAY,QAAQ,OAAO;AAC3C,UAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,MAAI;AACF,UAAM,QAAQ,KAAK,IAAI;AACvB,eAAS;AACP,iBAAW,KAAK,QAAQ,cAAc,EAAG,OAAM;AAC/C,YAAM,OAAO,QAAQ,UAAU;AAC/B,UAAI,KAAM,OAAM,MAAM,IAAI;AAC1B,UAAI,CAAC,QAAQ,QAAS;AACtB,UAAI,QAAQ,QAAS;AACrB,YAAM,YAAY,WAAW,KAAK,IAAI,IAAI;AAC1C,UAAI,aAAa,EAAG;AAGpB,UAAI,QAAQ,WAAY;AACxB,YAAM,QAAQ,SAAS,SAAS;AAAA,IAClC;AAEA,eAAW,KAAK,QAAQ,cAAc,EAAG,OAAM;AAC/C,UAAM,OAAO,QAAQ,UAAU;AAC/B,QAAI,KAAM,OAAM,MAAM,IAAI;AAAA,EAC5B,UAAE;AACA,YAAQ,oBAAoB,SAAS,OAAO;AAC5C,aAAS;AAAA,EACX;AACF;;;ACXO,IAAM,gBAAgB;AAGtB,SAAS,aAAa,SAAyC;AACpE,QAAM,IAAI,QAAQ;AAClB,SAAO,IAAI,IAAI,yCAAyC,CAAC,mCAAmC;AAC9F;AAGO,SAAS,mBACd,YACA,UACa;AACb,QAAM,SAAS,UAAU;AACzB,QAAM,UAAU,UAAU;AAC1B,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,QACL,MACA,KACgD;AAChD,YAAM,EAAE,YAAY,QAAQ,QAAQ,IAAI;AACxC,YAAM,OAAO,WAAW,KAAqC;AAC3D,cAAM,sBAAsB,EAAE,WAAW,SAAS,QAAQ,KAAK,WAAW,CAAC;AAAA,MAC7E;AAIA,UAAI,CAAC,QAAQ;AACX,eAAO,KAAK,2DAA2D;AACvE,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,UAAI,CAAC,WAAW,QAAQ,YAAY;AAClC,eAAO,KAAK,mEAAmE;AAC/E,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,YAAM,SAAS,OAAO,KAAK,WAAW,WAAW,KAAK,SAAS;AAC/D,UAAI,OAAO,KAAK,EAAE,WAAW,GAAG;AAC9B,eAAO,KAAK,iEAAiE;AAC7E,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,YAAM,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AACpE,YAAM,UAAU,OAAO,KAAK,aAAa,WAAW,KAAK,WAAW;AACpE,YAAM,UAAU;AAAA,QACd,KAAK;AAAA,QACL;AAAA,QACA,WAAW;AAAA,MACb;AAEA,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AAIpD,UAAI,CAAC,QAAQ,SAAS,GAAG;AACvB,eAAO;AAAA,UACL;AAAA,QACF;AACA,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAIA,UAAI;AACJ,UAAI;AACF,cAAM,SAAS,MAAM,OAAO,MAAM;AAAA,UAChC,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,UAC3C,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC7C,CAAC;AACD,kBAAU,IAAI,uBAAuB,MAAM;AAAA,MAC7C,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,eAAO,KAAK,wBAAwB,OAAO,GAAG;AAC9C,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAIA,YAAM,UAAU,MAAY,QAAQ,KAAK;AACzC,UAAI,aAAa;AACjB,cAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,UAAI;AACF,gBAAQ,SAAS,MAAM;AACvB,eAAO,cAAc,SAAS;AAAA,UAC5B;AAAA,UACA;AAAA,UACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,UAC3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,QAC/B,CAAC;AAED,YAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AACpD,YAAI,QAAQ,SAAS;AAGnB,gBAAM,KAAK,QAAQ,SAAS,OAAO;AACnC,uBAAa;AACb,iBAAO;AAAA,YACL,YAAY;AAAA,YACZ,MACE,sCAAsC,EAAE,0EAExC,aAAa,OAAO;AAAA,UACxB;AAAA,QACF;AAGA,eAAO,sBAAsB,QAAQ,IAAI;AAAA,MAC3C,UAAE;AACA,gBAAQ,oBAAoB,SAAS,OAAO;AAC5C,YAAI,CAAC,WAAY,SAAQ,KAAK;AAAA,MAChC;AAAA,IACF;AAAA,EACF;AACF;;;ACxHO,IAAM,sBAAsB;AAE5B,SAAS,wBACd,YACA,UACa;AACb,QAAM,UAAU,UAAU;AAC1B,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,QACL,MACA,KACgD;AAChD,YAAM,EAAE,YAAY,QAAQ,QAAQ,IAAI;AACxC,YAAM,QAAQ,CAAC,WACb,sBAAsB,EAAE,WAAW,SAAS,QAAQ,WAAW,CAAC;AAElE,UAAI,CAAC,SAAS;AACZ,cAAM,MAAM,sEAAsE;AAClF,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,aAAa,KAAK,aAAa;AACrC,UAAI,OAAO,eAAe,YAAY,WAAW,WAAW,GAAG;AAC7D,cAAM,MAAM,6DAA6D;AACzE,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,YAAM,UAAU,QAAQ,IAAI,UAAU;AACtC,UAAI,CAAC,SAAS;AACZ,cAAM;AAAA,UACJ,8CAA8C,UAAU;AAAA,QAE1D;AACA,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,SAAS,OAAO,KAAK,QAAQ,MAAM,WAAY,KAAK,QAAQ,IAAe;AACjF,YAAM,QAAQ,OAAO,KAAK,EAAE,WAAW;AACvC,YAAM,UAAU;AAAA,QACd,KAAK,eAAe;AAAA,QACpB,QAAQ,iCAAiC;AAAA,QACzC,WAAW;AAAA,MACb;AAEA,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AAIpD,UAAI,CAAC,OAAO;AACV,YAAI,QAAQ,SAAS;AACnB,gBAAM;AAAA,YACJ,mCAAmC,UAAU;AAAA,UAE/C;AACA,iBAAO,EAAE,YAAY,SAAS;AAAA,QAChC;AAEA,YAAI;AACF,kBAAQ,SAAS,MAAM;AAAA,QACzB,SAAS,KAAK;AACZ,gBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,gBAAM,MAAM,0BAA0B,OAAO,GAAG;AAChD,iBAAO,EAAE,YAAY,SAAS;AAAA,QAChC;AAAA,MACF;AAEA,aAAO,cAAc,SAAS;AAAA,QAC5B;AAAA,QACA;AAAA,QACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,QAC3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC/B,CAAC;AAGD,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AACpD,UAAI,QAAQ,SAAS;AACnB,eAAO;AAAA,UACL,YAAY;AAAA,UACZ,MAAM,4CAA4C,UAAU,MAAM,aAAa,OAAO;AAAA,QACxF;AAAA,MACF;AAEA,YAAM,SAAS,sBAAsB,QAAQ,IAAI;AACjD,YAAM,WAAW,mCAAmC,UAAU;AAC9D,aAAO;AAAA,QACL,GAAG;AAAA,QACH,MAAM,OAAO,SAAS,SAAY,GAAG,OAAO,IAAI,IAAI,QAAQ,KAAK;AAAA,MACnE;AAAA,IACF;AAAA,EACF;AACF;;;ACrGA,OAAOC,WAAU;AACjB,SAAS,UAAU,YAAY;AAOxB,IAAM,kBAAkB;AASxB,IAAM,kBAAkB,IAAI,OAAO;AAG1C,IAAM,kBAAkB,oBAAI,IAAI,CAAC,aAAa,cAAc,aAAa,YAAY,CAAC;AAGtF,IAAM,cAAsC;AAAA,EAC1C,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,SAAS;AACX;AAGA,SAAS,UAAU,KAA4B;AAC7C,MAAI,IAAI,UAAU,KAAK,IAAI,aAAa,CAAC,MAAM,WAAY,QAAO;AAClE,MAAI,IAAI,UAAU,KAAK,IAAI,CAAC,MAAM,OAAQ,IAAI,CAAC,MAAM,OAAQ,IAAI,CAAC,MAAM,KAAM;AAC5E,WAAO;AAAA,EACT;AACA,MAAI,IAAI,UAAU,GAAG;AACnB,UAAM,OAAO,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,QAAQ;AACjD,QAAI,SAAS,YAAY,SAAS,SAAU,QAAO;AAAA,EACrD;AACA,MACE,IAAI,UAAU,MACd,IAAI,SAAS,GAAG,CAAC,EAAE,SAAS,QAAQ,MAAM,UAC1C,IAAI,SAAS,GAAG,EAAE,EAAE,SAAS,QAAQ,MAAM,QAC3C;AACA,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAGA,SAAS,YAAY,GAA0B;AAC7C,SAAO,YAAYC,MAAK,QAAQ,CAAC,EAAE,YAAY,CAAC,KAAK;AACvD;AAGO,SAAS,WAAW,OAAuB;AAChD,MAAI,QAAQ,KAAM,QAAO,GAAG,KAAK;AACjC,QAAM,KAAK,QAAQ;AACnB,MAAI,KAAK,KAAM,QAAO,GAAG,GAAG,QAAQ,CAAC,CAAC;AACtC,SAAO,IAAI,KAAK,MAAM,QAAQ,CAAC,CAAC;AAClC;AAEA,IAAM,mBAAmB,CAAC,SACxB,oBAAoB,WAAW,IAAI,CAAC,gBAAgB,WAAW,eAAe,CAAC;AAEjF,IAAM,sBAAsB,CAAC,aAC3B,yBAAyB,WAAW,KAAK,QAAQ,MAAM,EAAE;AAc3D,eAAsB,UACpB,QACA,cACA,QAC0B;AAC1B,MAAI,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAE3D,MAAI;AACJ,MAAI;AACJ,MAAI,gBAAgB,KAAK,MAAM,GAAG;AAIhC,QAAI;AACJ,QAAI;AACF,YAAM,MAAM,MAAM,QAAQ,SAAS,EAAE,OAAO,IAAI,CAAC,CAAC;AAAA,IACpD,SAAS,KAAK;AACZ,UAAI,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAC3D,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,SAAS,6BAA6B,MAAM,MAAM,OAAO;AAAA,MAC3D;AAAA,IACF;AACA,QAAI,CAAC,IAAI,IAAI;AACX,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,SAAS,6BAA6B,MAAM,WAAW,IAAI,MAAM;AAAA,MACnE;AAAA,IACF;AAGA,UAAM,WAAW,OAAO,IAAI,QAAQ,IAAI,gBAAgB,KAAK,EAAE;AAC/D,QAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,iBAAiB;AAC3D,aAAO,EAAE,IAAI,OAAO,QAAQ,UAAU,SAAS,iBAAiB,QAAQ,EAAE;AAAA,IAC5E;AACA,QAAI;AACF,cAAQ,OAAO,KAAK,MAAM,IAAI,YAAY,CAAC;AAAA,IAC7C,SAAS,KAAK;AACZ,UAAI,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAC3D,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,SAAS,6BAA6B,MAAM,MAAM,OAAO;AAAA,MAC3D;AAAA,IACF;AACA,UAAM,cAAc,IAAI,QAAQ,IAAI,cAAc,KAAK,IAAI,MAAM,GAAG,EAAE,CAAC,EAAG,KAAK,EAAE,YAAY;AAC7F,QAAI,aAA4B;AAChC,QAAI;AACF,mBAAa,YAAY,IAAI,IAAI,MAAM,EAAE,QAAQ;AAAA,IACnD,QAAQ;AACN,mBAAa;AAAA,IACf;AACA,WAAO,gBAAgB,IAAI,UAAU,IAAI,aAAc,UAAU,KAAK,KAAK;AAC3E,QAAI,SAAS,QAAQ,WAAY,QAAO;AAAA,EAC1C,OAAO;AAGL,UAAM,WAAWA,MAAK,QAAQ,cAAc,MAAM;AAClD,QAAI;AACF,YAAM,KAAK,MAAM,KAAK,QAAQ;AAG9B,UAAI,CAAC,GAAG,OAAO,GAAG;AAChB,eAAO;AAAA,UACL,IAAI;AAAA,UACJ,QAAQ;AAAA,UACR,SAAS,yBAAyB,MAAM;AAAA,QAC1C;AAAA,MACF;AACA,UAAI,GAAG,OAAO,iBAAiB;AAC7B,eAAO,EAAE,IAAI,OAAO,QAAQ,UAAU,SAAS,iBAAiB,GAAG,IAAI,EAAE;AAAA,MAC3E;AACA,cAAQ,MAAM,SAAS,QAAQ;AAAA,IACjC,SAAS,KAAK;AACZ,UAAI,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAC3D,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,aAAO;AAAA,QACL,IAAI;AAAA,QACJ,QAAQ;AAAA,QACR,SAAS,yBAAyB,MAAM,MAAM,OAAO;AAAA,MACvD;AAAA,IACF;AACA,WAAO,UAAU,KAAK,KAAK,YAAY,QAAQ;AAAA,EACjD;AAEA,MAAI,QAAQ,QAAS,QAAO,EAAE,IAAI,OAAO,QAAQ,UAAU;AAI3D,MAAI,MAAM,WAAW,GAAG;AACtB,WAAO,EAAE,IAAI,OAAO,QAAQ,UAAU,SAAS,UAAU,MAAM,cAAc;AAAA,EAC/E;AACA,MAAI,MAAM,SAAS,iBAAiB;AAClC,WAAO,EAAE,IAAI,OAAO,QAAQ,UAAU,SAAS,iBAAiB,MAAM,MAAM,EAAE;AAAA,EAChF;AACA,MAAI,SAAS,QAAQ,CAAC,gBAAgB,IAAI,IAAI,GAAG;AAC/C,WAAO,EAAE,IAAI,OAAO,QAAQ,UAAU,SAAS,oBAAoB,IAAI,EAAE;AAAA,EAC3E;AACA,SAAO,EAAE,IAAI,MAAM,OAAO,KAAK;AACjC;AAOO,SAAS,oBAAoB,YAA+C;AACjF,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,QACL,MACA,KACgD;AAChD,YAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,YAAM,QAAQ,CAAC,WACb,sBAAsB,EAAE,WAAW,SAAS,QAAQ,WAAW,CAAC;AAElE,YAAM,SAAS,KAAK,QAAQ;AAC5B,UAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,cAAM,MAAM,oDAAoD;AAChE,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,MAAM,MAAM,UAAU,QAAQ,IAAI,cAAc,MAAM;AAC5D,UAAI,CAAC,IAAI,IAAI;AACX,YAAI,IAAI,WAAW,UAAW,QAAO,EAAE,YAAY,UAAU;AAC7D,cAAM,MAAM,IAAI,OAAO;AACvB,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAKA,YAAM,MAAM,GAAG,IAAI,IAAI,KAAK,WAAW,IAAI,MAAM,MAAM,CAAC,EAAE;AAC1D,aAAO,EAAE,QAAQ,CAAC,QAAQ,IAAI,IAAI,WAAW,IAAI,MAAM,SAAS,QAAQ,CAAC,EAAE,EAAE;AAAA,IAC/E;AAAA,EACF;AACF;;;AC9NO,IAAM,sBAAsB;AAGnC,IAAM,iBACJ;AAGK,SAAS,wBACd,YACA,WACa;AACb,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,QACL,MACA,KACgD;AAChD,YAAM,EAAE,YAAY,OAAO,IAAI;AAC/B,YAAM,QAAQ,CAAC,WACb,sBAAsB,EAAE,WAAW,SAAS,QAAQ,WAAW,CAAC;AAElE,YAAM,SAAS,KAAK,QAAQ;AAC5B,UAAI,OAAO,WAAW,YAAY,OAAO,WAAW,GAAG;AACrD,cAAM,MAAM,wDAAwD;AACpE,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,UAAI,UAAU,YAAY,QAAQ,UAAU,cAAc,QAAW;AACnE,cAAM;AAAA,UACJ;AAAA,QAGF;AACA,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,MAAM,MAAM,UAAU,QAAQ,IAAI,cAAc,MAAM;AAC5D,UAAI,CAAC,IAAI,IAAI;AACX,YAAI,IAAI,WAAW,UAAW,QAAO,EAAE,YAAY,UAAU;AAC7D,cAAM,MAAM,IAAI,OAAO;AACvB,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AAEA,YAAM,SACJ,OAAO,KAAK,QAAQ,MAAM,YAAY,KAAK,QAAQ,EAAE,KAAK,EAAE,SAAS,IACjE,KAAK,QAAQ,IACb;AACN,YAAM,UAAU,QAAQ,IAAI,IAAI,WAAW,IAAI,MAAM,SAAS,QAAQ,CAAC;AAQvE,YAAM,MAAM,UAAU,UAAU;AAChC,YAAM,MAAM,IAAI,eAAe;AAAA,QAC7B,aAAa,CAAC,SAAS,MAAM,GAAG,gBAAgB,OAAO,CAAC;AAAA,QACxD,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,MAC7B,CAAC;AACD,YAAM;AAAA,QACJ,GAAG,IAAI,IAAI,KAAK,WAAW,IAAI,MAAM,MAAM,CAAC,wBAAmB,UAAU,OAAO;AAAA;AAAA,MAClF;AACA,UAAI,cAAc;AAClB,UAAI;AACJ,iBAAS;AACP,cAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,YAAI,KAAK,MAAM;AACb,oBAAU,KAAK;AACf;AAAA,QACF;AACA,cAAM,IAAI,KAAK,MAAM;AACrB,YAAI,EAAE,SAAS,kBAAkB,EAAE,eAAe,WAAW,EAAE,MAAM;AACnE,wBAAc;AACd,gBAAM,MAAM,EAAE,IAAI;AAAA,QACpB;AAAA,MACF;AACA,UAAI,QAAQ,QAAS,QAAO,EAAE,YAAY,UAAU;AACpD,UAAI,CAAC,WAAW,QAAQ,WAAW,aAAa;AAC9C,cAAM,SACJ,WAAW,aAAa,WAAW,QAAQ,UAAU,KAAK,QAAQ,OAAO,KAAK;AAChF,cAAM;AAAA,UACJ,GAAG,cAAc,OAAO,EAAE,iBAAiB,UAAU,OAAO,aAAa,SAAS,UAAU,QAAQ,GAAG,MAAM;AAAA,QAC/G;AACA,eAAO,EAAE,YAAY,SAAS;AAAA,MAChC;AACA,UAAI,CAAC,YAAa,OAAM,MAAM,iCAAiC;AAAA,IACjE;AAAA,EACF;AACF;;;ACjFO,IAAM,yBAA6D;AAAA,EACxE,CAAC,iBAAiB,GAAG;AAAA,EACrB,CAAC,kBAAkB,GAAG;AAAA,EACtB,CAAC,aAAa,GAAG;AAAA,EACjB,CAAC,mBAAmB,GAAG;AAAA,EACvB,CAAC,eAAe,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMnB,CAAC,mBAAmB,GAAG,CAAC,YAAY,aAClC,wBAAwB,YAAY,UAAU,mBAAmB,EAAE,SAAS,KAAK,CAAC;AACtF;;;ACLA,IAAM,4BAA4B;AAGlC,IAAM,0BAA0B;AAGhC,IAAM,oBAAoB;AAG1B,IAAM,kBAAkB;AASxB,SAAS,uBAAuB,MAAc,KAAa,KAAsB;AAC/E,QAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,MAAI,IAAI,KAAK,MAAM,IAAI;AACrB,WAAO,cAAc,IAAI;AAAA,EAC3B;AACA,SAAO,cAAc,IAAI,+CAA+C,MAAM;AAChF;AAGA,SAAS,WAAW,MAAc,MAAsB;AACtD,SAAO,OAAO,GAAG,IAAI;AAAA,EAAK,IAAI,KAAK;AACrC;AAGA,SAAS,WAAW,MAAc,MAAsB;AACtD,SAAO,OAAO;AAAA,EAAK,IAAI,KAAK;AAC9B;AAEO,IAAM,cAAN,MAAkD;AAAA,EACtC;AAAA,EACA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EAEjB,YAAY,QAA2B;AACrC,SAAK,eAAe,OAAO;AAC3B,SAAK,aAAa,OAAO;AACzB,SAAK,QAAQ,oBAAI,IAAI;AAKrB,SAAK,kBAAkB,IAAI;AAAA,MACzB,OAAO,UAAU,SAAY,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,IAC1D;AACA,SAAK,mBAAmB,IAAI,uBAAuB;AACnD,UAAM,WAAW;AAAA,MACf,GAAG,OAAO;AAAA,MACV,iBAAiB,KAAK;AAAA,MACtB,kBAAkB,KAAK;AAAA,IACzB;AAGA,eAAW,OAAO,OAAO,WAAW,aAAa;AAC/C,YAAM,UAAU,uBAAuB,IAAI,IAAI;AAC/C,UAAI,QAAS,MAAK,MAAM,IAAI,IAAI,MAAM,QAAQ,KAAK,QAAQ,CAAC;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA,EAGA,UAAgB;AACd,SAAK,gBAAgB,QAAQ;AAC7B,SAAK,iBAAiB,QAAQ;AAAA,EAChC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,YAAuC;AAC3C,WAAO,KAAK,WAAW,YACpB,OAAO,CAAC,SAAS,KAAK,MAAM,IAAI,KAAK,IAAI,CAAC,EAC1C,IAAI,CAAC,UAAU;AAAA,MACd,MAAM,KAAK;AAAA,MACX,aAAa,KAAK;AAAA,MAClB,GAAI,KAAK,eAAe,SAAY,EAAE,YAAY,KAAK,WAAW,IAAI,CAAC;AAAA,IACzE,EAAE;AAAA,EACN;AAAA;AAAA,EAGA,eAAe,MAA0C;AACvD,WAAO,KAAK,WAAW,YAAY,KAAK,CAAC,MAAM,EAAE,SAAS,IAAI,GAAG;AAAA,EACnE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAO,YAAY,SAA4D;AAC7E,UAAM,UAAU,QAAQ,SAAS;AAGjC,UAAM,aAAa,QAAQ;AAC3B,UAAM,OAAO,QAAQ;AAIrB,UAAM,sBAAsB,EAAE,WAAW,SAAS,WAAW,CAAC;AAE9D,UAAM,OAAO,KAAK,MAAM,IAAI,IAAI;AAChC,QAAI,CAAC,MAAM;AACT,aAAO,YAAY,YAAY,iBAAiB,IAAI,EAAE;AACtD;AAAA,IACF;AAIA,QAAI;AACJ,QAAI;AACF,eAAS,KAAK,MAAM,QAAQ,SAAS;AAAA,IACvC,SAAS,KAAK;AACZ,aAAO,YAAY,YAAY,uBAAuB,MAAM,QAAQ,WAAW,GAAG,CAAC;AACnF;AAAA,IACF;AAEA,UAAM,OACJ,WAAW,QAAQ,OAAO,WAAW,WAAY,SAAqC,CAAC;AAEzF,UAAM,kBAAkB,KAAK,WAAW,mBAAmB;AAC3D,UAAM,YAAY,KAAK,WAAW,aAAa;AAC/C,UAAM,SAAS,QAAQ;AAMvB,UAAM,KAAK,IAAI,gBAAgB;AAC/B,QAAI,QAAQ,QAAS,IAAG,MAAM;AAC9B,UAAM,UAAU,MAAY,GAAG,MAAM;AACrC,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,QAAI,WAAW;AACf,UAAM,QACJ,YAAY,IACR,WAAW,MAAM;AACf,iBAAW;AACX,SAAG,MAAM;AAAA,IACX,GAAG,SAAS,IACZ;AACN,WAAO,QAAQ;AAIf,QAAI,WAAW;AACf,QAAI,aAAa;AACjB,QAAI,aAA4B;AAChC,QAAI;AACJ,QAAI,WAA0B;AAC9B,QAAI;AACJ,QAAI,SAAkB;AACtB,UAAM,MAAM,KAAK,QAAQ,MAAM;AAAA,MAC7B,cAAc,KAAK;AAAA,MACnB;AAAA,MACA,QAAQ,GAAG;AAAA;AAAA;AAAA,MAGX,GAAI,QAAQ,UAAU,EAAE,SAAS,QAAQ,QAAQ,IAAI,CAAC;AAAA,IACxD,CAAC;AACD,QAAI;AACF,iBAAS;AACP,cAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,YAAI,IAAI,MAAM;AACZ,gBAAM,SAA4B,IAAI;AACtC,cAAI,QAAQ,WAAY,gBAAe,OAAO;AAC9C,cAAI,QAAQ,KAAM,YAAW,OAAO;AACpC,cAAI,QAAQ,UAAU,OAAO,OAAO,SAAS,EAAG,cAAa,OAAO;AACpE;AAAA,QACF;AACA,cAAM,MAAM,IAAI;AAChB,YAAI,IAAI,UAAU,IAAI,OAAO,SAAS,GAAG;AACvC,gBAAM;AACN;AAAA,QACF;AACA,cAAM,IAAI,IAAI;AAMd,YAAI,EAAE,SAAS,4BAA4B;AAEzC,cAAI,EAAE,eAAe,WAAW,CAAC,EAAE,OAAQ;AAC3C,wBAAc,EAAE,OAAO;AAEvB,gBAAM,OACJ,kBAAkB,IAAI,kBAAkB,SAAS,SAAS,OAAO;AACnE,cAAI,OAAO,GAAG;AACZ,kBAAM,QAAQ,EAAE,OAAO,SAAS,OAAO,EAAE,OAAO,MAAM,GAAG,IAAI,IAAI,EAAE;AACnE,wBAAY;AAEZ,kBAAM,sBAAsB;AAAA,cAC1B,WAAW;AAAA,cACX,QAAQ;AAAA,cACR;AAAA,YACF,CAAC;AAAA,UACH;AAAA,QACF,WAAW,EAAE,SAAS,oBAAoB;AAExC,uBAAa,EAAE,UAAU;AACzB,cAAI,iBAAiB,UAAa,EAAE,aAAa;AAC/C,2BAAe,EAAE;AAAA,UACnB;AAAA,QACF,OAAO;AAEL,kBAAQ,OAAO;AAAA,YACb,mBAAmB,IAAI,sCAAsC,EAAE,IAAI;AAAA;AAAA,UACrE;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AAEZ,eAAS;AAAA,IACX,UAAE;AACA,UAAI,MAAO,cAAa,KAAK;AAC7B,cAAQ,oBAAoB,SAAS,OAAO;AAAA,IAC9C;AAMA,UAAM,cAAc,cAAc;AAClC,UAAM,SACJ,kBAAkB,KAAK,YAAY,SAAS,kBACxC,YAAY,MAAM,GAAG,eAAe,IACpC;AACN,UAAM,YAAY,OAAO,SAAS,YAAY,UAAU,aAAa,SAAS;AAE9E,UAAM,UACJ,QAAQ,YAAY,QACnB,CAAC,aACC,iBAAiB,aACf,QAAqC,SAAS;AACrD,QAAI;AACJ,UAAM,QAAkB,CAAC;AACzB,QAAI,WAAW;AACb,YAAM,KAAK,+BAA+B,eAAe,SAAS;AAAA,IACpE;AAIA,QAAI,UAAU;AACZ,YAAM,KAAK,QAAQ;AAAA,IACrB;AACA,QAAI,SAAS;AACX,mBAAa;AACb,YAAM,KAAK,iBAAiB;AAAA,IAC9B,WAAW,UAAU;AACnB,mBAAa;AACb,YAAM,KAAK,2BAA2B,SAAS,KAAK;AAAA,IACtD,WAAW,UAAU,MAAM;AACzB,mBAAa;AACb,YAAM,KAAK,gBAAgB,kBAAkB,QAAQ,OAAO,UAAU,OAAO,MAAM,CAAC,EAAE;AAAA,IACxF,OAAO;AACL,mBAAa,gBAAgB;AAAA,IAC/B;AAIA,QAAI,WAAW,MAAM,MAAM,WAAW,GAAG;AACvC,YAAM,KAAK,eAAe;AAAA,IAC5B;AACA,UAAM,WAAW,MAAM,KAAK,IAAI;AAChC,UAAM,aAAa,WAAW,WAAW,QAAQ,QAAQ,IAAI;AAK7D,QAAI,eAAe;AACnB,QAAI,aAAa,GAAI,gBAAe;AAAA,aAC3B,eAAe,QAAQ,OAAO,WAAW,QAAQ,GAAG;AAC3D,qBAAe,OAAO,MAAM,SAAS,MAAM;AAAA,IAC7C;AACA,QAAI,cAAc;AAChB,YAAM,sBAAsB;AAAA,QAC1B,WAAW;AAAA,QACX,QAAQ;AAAA,QACR;AAAA,MACF,CAAC;AAAA,IACH;AACA,QAAI,UAAU;AACZ,YAAM,sBAAsB;AAAA,QAC1B,WAAW;AAAA,QACX,QAAQ,WAAW,QAAQ,QAAQ;AAAA,QACnC;AAAA,MACF,CAAC;AAAA,IACH;AAMA,UAAM,SAAS,eAAe,cAAc,aAAa;AACzD,QAAI,QAAQ;AACV,YAAM,sBAAsB,EAAE,WAAW,SAAS,YAAY,OAAO,CAAC;AAAA,IACxE;AACA,UAAM,sBAAsB,EAAE,WAAW,QAAQ,YAAY,WAAW,CAAC;AACzE,UAAM,eAAe;AAAA,MACnB,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC7B,CAAC;AAAA,EACH;AACF;AAGA,UAAU,YAAY,YAAoB,SAAyC;AACjF,QAAM,sBAAsB,EAAE,WAAW,SAAS,QAAQ,SAAS,WAAW,CAAC;AAC/E,QAAM,sBAAsB,EAAE,WAAW,QAAQ,YAAY,YAAY,SAAS,CAAC;AACnF,QAAM,eAAe,EAAE,QAAQ,SAAS,YAAY,YAAY,SAAS,CAAC;AAC5E;;;ACzWA,SAAS,YAAY,OAAO,YAAAC,iBAAgB;AAC5C,SAAS,SAAS,YAAY;;;AChBvB,SAAS,gBAAgB,MAAoB;AAClD,QAAM,OAAO,KAAK,YAAY,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AAC1D,QAAM,SAAS,KAAK,SAAS,IAAI,GAAG,SAAS,EAAE,SAAS,GAAG,GAAG;AAC9D,QAAM,MAAM,KAAK,QAAQ,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AACrD,SAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG;AAChC;;;ADwCA,SAAS,YAAY,OAAuB;AAC1C,SAAO,MAAM,SAAS,EAAE,SAAS,GAAG,GAAG;AACzC;AAYA,SAAS,aAAa,KAA2B;AAC/C,MAAI,IAAI,UAAU,IAAI,OAAO,SAAS,EAAG,QAAO;AAChD,SAAO,uBAAuB,GAAG,KAAK,eAAe,GAAG,KAAK,cAAc,GAAG;AAChF;AASO,IAAM,SAAN,MAAa;AAAA,EACD;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAET,QAAQ;AAAA;AAAA,EAER,qBAAqB;AAAA,EAE7B,YAAY,MAAqB;AAC/B,SAAK,YAAY,KAAK;AACtB,SAAK,YAAY,KAAK;AACtB,SAAK,UAAU,KAAK,WAAW,gBAAgB,KAAK,QAAQ,oBAAI,KAAK,CAAC;AACtE,SAAK,QAAQ,KAAK,cAAc;AAAA,EAClC;AAAA;AAAA,EAGA,cAAsB;AACpB,UAAM,WAAW,GAAG,KAAK,SAAS,IAAI,YAAY,KAAK,KAAK,CAAC;AAC7D,WAAO,KAAK,KAAK,WAAW,KAAK,SAAS,QAAQ;AAAA,EACpD;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,KAAiC;AAC3C,QAAI,CAAC,aAAa,GAAG,EAAG;AACxB,UAAMC,SAAO,KAAK,YAAY;AAC9B,QAAI,KAAK,uBAAuB,KAAK,OAAO;AAC1C,YAAM,MAAM,QAAQA,MAAI,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9C,WAAK,qBAAqB,KAAK;AAAA,IACjC;AACA,UAAM,WAAWA,QAAM,GAAG,KAAK,UAAU,GAAG,CAAC;AAAA,GAAM,MAAM;AAAA,EAC3D;AAAA;AAAA,EAGA,MAAM,SAAS,MAAoC;AACjD,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,MAAM,GAAG;AAAA,IACtB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,kBAAkB,MAAoC;AAC1D,UAAM,MAAM,IAAI,kBAAkB;AAClC,eAAW,OAAO,MAAM;AACtB,YAAM,KAAK,SAAS,IAAI,KAAK,GAAG,CAAC;AAAA,IACnC;AACA,UAAM,KAAK,SAAS,IAAI,MAAM,CAAC;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,SAAwB;AAC5B,SAAK,SAAS;AAAA,EAChB;AACF;AAGA,eAAsB,UAAUA,QAAsC;AACpE,QAAM,UAAU,MAAMC,UAASD,QAAM,MAAM;AAC3C,SAAO,QACJ,MAAM,IAAI,EACV,OAAO,CAAC,SAAS,KAAK,KAAK,EAAE,SAAS,CAAC,EACvC,IAAI,CAAC,SAAS,KAAK,MAAM,IAAI,CAAgB;AAClD;;;AE1HA,SAAS,SAAS,YAAAE,iBAAgB;AAClC,SAAS,QAAAC,aAAY;;;AC4IrB,IAAM,aAAN,MAAiB;AAAA,EACP,QAAuB,CAAC;AAAA,EACxB,YAAY;AAAA,EACZ,OAA4B;AAAA;AAAA,EAGpC,cAAoB;AAClB,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA,EAGA,iBAAuB;AACrB,SAAK,aAAa;AAClB,SAAK,OAAO;AAAA,EACd;AAAA;AAAA,EAGA,KAAK,KAAwB;AAC3B,SAAK,MAAM,KAAK,GAAG;AACnB,SAAK,OAAO;AAAA,EACd;AAAA,EAEQ,SAAe;AACrB,QAAI,KAAK,MAAM;AACb,YAAM,IAAI,KAAK;AACf,WAAK,OAAO;AACZ,QAAE;AAAA,IACJ;AAAA,EACF;AAAA;AAAA,EAGA,MAAM,OAAoC;AACxC,eAAS;AACP,UAAI,KAAK,MAAM,SAAS,EAAG,QAAO,KAAK,MAAM,MAAM;AACnD,UAAI,KAAK,cAAc,EAAG,QAAO;AACjC,YAAM,IAAI,QAAc,CAAC,YAAY;AACnC,aAAK,OAAO;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAEO,IAAM,gBAAN,MAAoB;AAAA,EAyBzB,YAA6B,MAAyB;AAAzB;AAC3B,SAAK,WAAW,KAAK,YAAY;AACjC,SAAK,gBAAgB,KAAK,iBAAiB;AAC3C,SAAK,qBAAqB,KAAK,sBAAsB;AACrD,SAAK,MAAM,KAAK;AAEhB,UAAM,OAAO,KAAK;AAClB,QAAI,MAAM;AACR,WAAK,mBAAmB,KAAK,aAAa,CAAC;AAC3C,WAAK,iBAAiB,KAAK,kBAAkB;AAC7C,WAAK,eAAe,KAAK,gBAAgB;AACzC,WAAK,oBAAoB,KAAK,iBAAiB,iBAAiB;AAChE,WAAK,mBAAmB,KAAK,oBAAoB;AACjD,WAAK,uBAAuB,KAAK,wBAAwB;AAAA,IAC3D;AAAA,EACF;AAAA,EAf6B;AAAA,EAxBZ;AAAA,EACA;AAAA,EACA;AAAA;AAAA,EAET,mBAAkC,CAAC;AAAA;AAAA,EAEnC;AAAA;AAAA,EAEA,eAAe;AAAA;AAAA,EAEf,iBAAiB;AAAA;AAAA,EAEjB,mBAAmB;AAAA;AAAA,EAEnB,oBAAiC,iBAAiB;AAAA;AAAA,EAElD,iBAAqC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMrC,uBAAuB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0B/B,OAAO,IAAI,aAA4B,MAAgD;AACrF,UAAM,SAAS,MAAM;AAErB,UAAM,UAAqB,MAAM,YAAY,YAAY;AAKzD,UAAM,UAAU,KAAK;AACrB,SAAK,iBAAiB;AACtB,UAAM,YAAY,KAAK;AACvB,SAAK,mBAAmB,CAAC;AACzB,UAAM,SAAS,UAAU,CAAC,SAAS,GAAG,SAAS,IAAI;AACnD,UAAM,QAAQ,OAAO,SAAS,CAAC,GAAG,QAAQ,GAAG,WAAW,IAAI;AAS5D,QAAI,QAAS,OAAM,KAAK,MAAM,OAAO;AACrC,eAAW,OAAO,YAAa,OAAM,KAAK,MAAM,GAAG;AAEnD,QAAI,QAAQ,SAAS;AAMnB,WAAK,mBAAmB;AACxB,aAAO,KAAK,UAAU,iBAAiB;AACvC;AAAA,IACF;AAEA,QAAI,YAAY;AAGhB,QAAI,YAA2B;AAE/B,eAAS;AAEP,UAAI,aAAa,KAAK,UAAU;AAO9B,aAAK,mBAAmB;AACxB,eAAO,KAAK,aAAa;AACzB;AAAA,MACF;AACA,mBAAa;AAOb,YAAM,cAA4B,CAAC;AACnC,UAAI,eAAe;AACnB,UAAI,aAAa;AACjB,UAAI;AAEJ,iBAAS;AAIP,eAAO,OAAO,KAAK,QAAQ,cAAc,SAAS,MAAM;AAIxD,YAAI,QAAQ,WAAW,KAAK,QAAQ,WAAW,WAAW;AACxD,eAAK,mBAAmB,KAAK,eAAe,cAAc,IAAI;AAC9D,iBAAO,KAAK,UAAU,iBAAiB;AACvC;AAAA,QACF;AAGA,YAAI,KAAK,QAAQ,WAAW,UAAU;AACpC,eAAK,mBAAmB,KAAK,eAAe,cAAc,IAAI;AAC9D,iBAAO,KAAK,UAAU,sBAAsB,KAAK,QAAQ,WAAW,SAAS,EAAE;AAC/E;AAAA,QACF;AAEA,YAAI,KAAK,QAAQ,WAAW,YAAa;AAOzC,oBAAY,KAAK,IAAI;AACrB,uBAAe,KAAK,iBAAiB,WAAW,WAAW;AAC3D,YAAI,cAAc,KAAK,eAAe;AACpC,eAAK,mBAAmB;AACxB,gBAAM,SAAS,KAAK,QAAQ,WAAW,cAAc,uBAAuB;AAC5E,iBAAO,KAAK,UAAU,GAAG,MAAM,iBAAiB,KAAK,aAAa,UAAU;AAC5E;AAAA,QACF;AACA,sBAAc;AACd,YAAI,CAAE,MAAM,KAAK,QAAQ,YAAY,MAAM,GAAI;AAC7C,eAAK,mBAAmB;AACxB,iBAAO,KAAK,UAAU,kCAAkC;AACxD;AAAA,QACF;AAAA,MACF;AAKA,YAAM,UAAU,KAAK,YAAY,SAAS;AAC1C,YAAM,mBAAmB,KAAK,kBAAkB;AAChD,UAAI,kBAAkB;AACpB,cAAM,OAAO,KAAK,KAAK,WAAY;AACnC,YAAI,SAAS,WAAW;AAGtB,cAAI,CAAC,SAAS;AACZ,mBAAO,KAAK,eAAe,gBAAgB;AAC3C;AAAA,UACF;AAAA,QACF,OAAO;AACL,gBAAM,SAAS,OAAO,KAAK;AAAA,YACzB;AAAA,YACA,UAAU,KAAK,cAAc,CAAC;AAAA,YAC9B;AAAA,UACF;AACA,cAAI,OAAO,WAAW,WAAW;AAG/B,gBAAI,SAAS;AACX,mBAAK,mBAAmB,KAAK,eAAe,cAAc,IAAI;AAC9D,qBAAO,KAAK,UAAU,2BAA2B;AAAA,YACnD;AACA;AAAA,UACF;AACA,cAAI,OAAO,WAAW,aAAa;AACjC,gBAAI,CAAC,SAAS;AAGZ,mBAAK,iBAAiB,OAAO;AAC7B;AAAA,YACF;AAKA,kBAAM,KAAK,MAAM,OAAO,OAAQ;AAChC,wBAAY,CAAC,OAAO,OAAQ;AAC5B;AAAA,UACF;AAAA,QAGF;AAAA,MACF;AAGA,UAAI,CAAC,QAAS;AAEd,kBAAY,KAAK;AAAA,IACnB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA8BA,iBAAsC;AACpC,QAAI,CAAC,KAAK,KAAK,cAAc,CAAC,KAAK,KAAK,UAAW,QAAO;AAC1D,QAAI,KAAK,eAAe,EAAG,QAAO;AAClC,WAAO,KAAK,iBAAiB,mBAAmB;AAAA,EAClD;AAAA,EAEA,OAAO,QAAQ,MAA8D;AAC3E,QAAI,CAAC,KAAK,KAAK,cAAc,CAAC,KAAK,KAAK,UAAW;AAMnD,QAAI,KAAK,iBAAiB,EAAG;AAC7B,QAAI,KAAK,KAAK,WAAW,SAAS,WAAW;AAC3C,WAAK,mBAAmB,KAAK,iBAAiB;AAAA,QAC5C,CAAC,MAAO,EAAE,QAA8B,SAAS;AAAA,MACnD;AACA,aAAO,KAAK,eAAe,QAAQ;AACnC;AAAA,IACF;AACA,UAAM,SAAS,OAAO,KAAK,iBAAiB,UAAU,KAAK,kBAAkB,MAAM,MAAM;AACzF,QAAI,OAAO,WAAW,aAAa;AACjC,WAAK,mBAAmB,CAAC;AACzB,WAAK,iBAAiB,OAAO;AAAA,IAC/B;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOQ,QAAQ,SAAiB,QAAwC;AACvE,UAAM,KAAK,KAAK,qBAAqB;AACrC,WAAO,IAAI,QAAiB,CAAC,YAAY;AACvC,UAAI,QAAQ,SAAS;AACnB,gBAAQ,KAAK;AACb;AAAA,MACF;AACA,YAAM,UAAU,MAAY;AAC1B,qBAAa,KAAK;AAClB,gBAAQ,KAAK;AAAA,MACf;AACA,YAAM,QAAQ,WAAW,MAAM;AAC7B,gBAAQ,oBAAoB,SAAS,OAAO;AAC5C,gBAAQ,IAAI;AAAA,MACd,GAAG,EAAE;AACL,cAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,IAC3D,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAe,QACb,OACA,SACA,QACyC;AACzC,UAAM,QAAQ,IAAI,WAAW;AAI7B,UAAM,cAA6B,CAAC;AACpC,UAAM,YAA4C,CAAC;AACnD,UAAM,YAAsB,CAAC;AAG7B,UAAM,oBAAmC,CAAC;AAE1C,QAAI,UAAsB,EAAE,QAAQ,YAAY;AAIhD,UAAM,YAAY;AAClB,UAAM,SAAS,YAAY;AACzB,UAAI;AAIF,cAAM,WAAW,aAAa;AAC9B,cAAM,KAAK,QAAQ;AACnB,cAAM,KAAK,MAAM,QAAQ;AAGzB,cAAM,MAAM,KAAK,IAAI,eAAe;AAAA,UAClC,aAAa;AAAA,UACb,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,QAC7B,CAAC;AACD,mBAAS;AACP,gBAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,cAAI,IAAI,MAAM;AACZ,sBAAU,IAAI;AACd,kBAAM,UAAU,WAAW,QAAQ,MAAM;AACzC,kBAAM,KAAK,OAAO;AAClB,kBAAM,KAAK,MAAM,OAAO;AACxB;AAAA,UACF;AACA,gBAAM,MAAM,IAAI;AAChB,gBAAM,KAAK,GAAG;AACd,gBAAM,KAAK,MAAM,GAAG;AAIpB,cAAI,KAAK,kBAAkB,GAAG,EAAG,MAAK,gBAAgB;AAGtD,cACE,uBAAuB,GAAG,MACzB,IAAI,QAAQ,SAAS,cAAc,IAAI,QAAQ,SAAS,SACzD;AACA,8BAAkB,KAAK,GAAG;AAAA,UAC5B;AAUA,cAAI,uBAAuB,GAAG,KAAK,IAAI,QAAQ,SAAS,aAAa;AACnE,kBAAM,KAAK;AACX,gBAAI,GAAG,QAAQ,gBAAgB,YAAa;AAC5C,kBAAM,aAAa,GAAG,QAAQ;AAC9B,sBAAU,KAAK,UAAU;AACzB,sBAAU,KAAK,EAAE;AAGjB,gBAAI,QAAQ,QAAS;AAKrB,gBAAI;AACJ,gBAAI;AACF,yBAAW,MAAM,QAAQ,EAAE;AAAA,YAC7B,SAAS,KAAK;AACZ,oBAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,sBAAQ,OAAO,MAAM,qCAAqC,OAAO;AAAA,CAAc;AAC/E,yBAAW;AAAA,YACb;AACA,gBAAI,QAAQ,QAAS;AAGrB,kBAAM,cAAc,iBAAiB,UAAU,UAAU;AACzD,kBAAM,KAAK,WAAW;AACtB,kBAAM,KAAK,MAAM,WAAW;AAC5B,gBAAI,aAAa,SAAS;AAGxB,oBAAM,SAAS,eAAe;AAAA,gBAC5B,QAAQ;AAAA,gBACR;AAAA,gBACA,YAAY;AAAA,cACd,CAAC;AACD,oBAAM,KAAK,MAAM;AACjB,oBAAM,KAAK,MAAM,MAAM;AACvB,0BAAY,KAAK,MAAM;AACvB;AAAA,YACF;AAGA,kBAAM,YAAY;AAClB,iBAAK,KAAK,WAAW,IAAI,OAAO,aAAa,QAAQ,OAAO,EAAE,QAAQ,MAAM;AAC1E,oBAAM,eAAe;AAAA,YACvB,CAAC;AAAA,UACH;AAAA,QACF;AAAA,MACF,UAAE;AACA,cAAM,eAAe;AAAA,MACvB;AAAA,IACF,GAAG;AAIH,eAAS;AACP,YAAM,MAAM,MAAM,MAAM,KAAK;AAC7B,UAAI,QAAQ,KAAM;AAClB,YAAM;AAAA,IACR;AAEA,UAAM;AAIN,UAAM,OAAO,oBAAI,IAAyB;AAC1C,eAAW,OAAO,aAAa;AAC7B,YAAM,KAAM,IAAI,QAAsC;AACtD,UAAI,OAAO,OAAW,MAAK,IAAI,IAAI,GAAG;AAAA,IACxC;AACA,UAAM,iBAAgC,CAAC;AACvC,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,MAAM,WAAW;AAC1B,UAAI,KAAK,IAAI,EAAE,EAAG;AAClB,WAAK,IAAI,EAAE;AACX,YAAM,MAAM,KAAK,IAAI,EAAE;AACvB,UAAI,IAAK,gBAAe,KAAK,GAAG;AAAA,IAClC;AACA,WAAO,EAAE,aAAa,gBAAgB,WAAW,mBAAmB,QAAQ;AAAA,EAC9E;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAc,WACZC,WACA,OACA,aACA,QACA,SACe;AACf,QAAI,YAAY;AAChB,QAAI;AACF,uBAAiB,OAAO,KAAK,KAAK,YAAY,YAAY;AAAA,QACxD,UAAAA;AAAA,QACA,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA;AAAA;AAAA,QAG3B,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC/B,CAAC,GAAG;AACF,cAAM,KAAK,GAAG;AASd,YAAI,IAAI,UAAU,IAAI,OAAO,SAAS,GAAG;AACvC,cAAI,cAAc,GAAG,KAAK,IAAI,OAAO,WAAW,GAAG;AACjD,kBAAM,KAAK,MAAM,cAAc,IAAI,OAAO,CAAC,CAAE,CAAC;AAAA,UAChD;AACA;AAAA,QACF;AACA,cAAM,KAAK,MAAM,GAAG;AACpB,YAAI,uBAAuB,GAAG,KAAK,IAAI,QAAQ,SAAS,oBAAoB;AAC1E,sBAAY,KAAK,GAAG;AACpB,sBAAY;AAAA,QACd;AAAA,MACF;AAAA,IACF,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,UAAI,WAAW;AAEb,gBAAQ,OAAO,MAAM,kDAAkD,OAAO;AAAA,CAAI;AAClF;AAAA,MACF;AACA,YAAM,SAAS,eAAe;AAAA,QAC5B,QAAQ,gBAAgB,OAAO;AAAA,QAC/B,YAAYA,UAAS,QAAQ;AAAA,QAC7B,YAAY;AAAA,MACd,CAAC;AACD,YAAM,KAAK,MAAM;AACjB,YAAM,KAAK,MAAM,MAAM;AACvB,kBAAY,KAAK,MAAM;AAAA,IACzB;AAAA,EACF;AAAA;AAAA,EAGA,OAAe,eAA4C;AAEzD,UAAM,OAAO,uBAAuB,KAAK,QAAQ;AACjD,UAAM,WAAW;AAAA,MACf,YAAY,OAAO;AAAA,MACnB,YAAY,SAAS,IAAI;AAAA,MACzB,YAAY,QAAQ,IAAI,QAAQ;AAAA,IAClC;AACA,eAAW,WAAW,UAAU;AAC9B,YAAM;AACN,YAAM,KAAK,MAAM,OAAO;AAAA,IAC1B;AACA,UAAM,OAAO,cAAc,MAAM,QAAQ;AACzC,UAAM;AACN,UAAM,KAAK,MAAM,IAAI;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcQ,oBAA6C;AACnD,UAAM,WAAW,KAAK,KAAK;AAC3B,QAAI,CAAC,YAAY,CAAC,KAAK,KAAK,UAAW,QAAO;AAC9C,QAAI,SAAS,mBAAmB,KAAK,KAAK,oBAAoB,SAAS,kBAAkB;AACvF,aAAO;AAAA,IACT;AACA,QAAI,SAAS,kBAAkB,KAAK,KAAK,gBAAgB,SAAS,iBAAiB;AACjF,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAGQ,kBAAkB,KAA2B;AACnD,QAAI,IAAI,SAAS,YAAa,QAAO;AACrC,UAAM,UAAU,IAAI;AACpB,QAAI,QAAQ,SAAS,cAAe,QAAO;AAC3C,QAAI,QAAQ,QAAS,MAAK,mBAAmB,QAAQ,QAAQ;AAC7D,QAAI,QAAQ,QAAS,MAAK,oBAAoB,QAAQ;AACtD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAe,eAAe,QAAuD;AACnF,WAAO,KAAK,oBAAoB,QAAQ,SAAS;AACjD,WAAO,KAAK,kBAAkB,QAAQ,WAAW,WAAW;AAC5D,UAAM,KAAK,gBAAgB;AAAA,EAC7B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,OAAe,iBACb,QACA,oBACA,QAC+C;AAC/C,UAAM,WAAW,KAAK,KAAK;AAC3B,WAAO,KAAK,oBAAoB,QAAQ,WAAW;AAOnD,UAAM,SAAS,SAAS,SAAS,MAAM;AACvC,UAAM,QAAQ,CAAC,GAAG,oBAAoB,MAAM;AAC5C,UAAM,KAAK,MAAM,MAAM;AAEvB,QAAI,aAAa;AACjB,eAAS;AACP,UAAI,QAAQ,SAAS;AACnB,eAAO,KAAK,kBAAkB,QAAQ,aAAa,SAAS;AAC5D,eAAO,EAAE,QAAQ,UAAU;AAAA,MAC7B;AACA,YAAM,UAAU,MAAM,KAAK,qBAAqB,OAAO,MAAM;AAC7D,UAAI,QAAQ,WAAW,aAAa;AAKlC,YAAI,QAAQ,MAAO,OAAM,QAAQ;AAGjC,cAAM,UAAU;AAAA,UACd;AAAA,EAAsB,eAAe,QAAQ,IAAI,CAAC;AAAA;AAAA,QACpD;AACA,eAAO,KAAK,kBAAkB,QAAQ,aAAa,WAAW;AAC9D,cAAM,KAAK,gBAAgB;AAC3B,eAAO,EAAE,QAAQ,aAAa,QAAQ;AAAA,MACxC;AACA,UAAI,QAAQ,WAAW,aAAa,QAAQ,WAAW,UAAU;AAC/D,eAAO,KAAK,kBAAkB,QAAQ,aAAa,QAAQ,MAAM;AACjE,eAAO,EAAE,QAAQ,QAAQ,OAAO;AAAA,MAClC;AAGA,UAAI,cAAc,KAAK,eAAe;AACpC,eAAO,KAAK,kBAAkB,QAAQ,aAAa,QAAQ;AAC3D,eAAO,EAAE,QAAQ,SAAS;AAAA,MAC5B;AACA,oBAAc;AACd,YAAM,KAAK,MAAM,KAAK,QAAQ,YAAY,MAAM;AAChD,UAAI,CAAC,IAAI;AACP,eAAO,KAAK,kBAAkB,QAAQ,aAAa,SAAS;AAC5D,eAAO,EAAE,QAAQ,UAAU;AAAA,MAC7B;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,MAAc,qBACZ,OACA,QAC0E;AAI1E,UAAM,KAAK,MAAM,aAAa,CAAC;AAC/B,UAAM,MAAM,KAAK,IAAI,eAAe;AAAA,MAClC,aAAa;AAAA,MACb,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,IAC7B,CAAC;AACD,QAAI,OAAO;AACX,QAAI,QAA4B;AAChC,eAAS;AACP,YAAM,MAAM,MAAM,IAAI,KAAK;AAC3B,UAAI,IAAI,MAAM;AACZ,cAAM,KAAK,MAAM,WAAW,IAAI,MAAM,MAAM,CAAC;AAC7C,eAAO,EAAE,QAAQ,IAAI,MAAM,QAAQ,MAAM,MAAM;AAAA,MACjD;AACA,YAAM,MAAM,IAAI;AAChB,YAAM,KAAK,MAAM,GAAG;AACpB,UAAI,KAAK,kBAAkB,GAAG,EAAG,SAAQ;AACzC,UAAI,uBAAuB,GAAG,KAAK,IAAI,QAAQ,SAAS,QAAQ;AAC9D,gBAAS,IAAI,QAAwB;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,kBAAiC;AAC7C,SAAK,uBAAuB;AAC5B,SAAK,MAAM,KAAK,KAAK,UAAW,KAAK,iBAAiB;AACtD,SAAK,eAAe;AACpB,SAAK,mBAAmB;AAIxB,SAAK,iBAAiB;AAAA,EACxB;AAAA;AAAA,EAGA,OAAe,oBACb,QACA,MAC6B;AAC7B,UAAM,MAAM,gBAAgB;AAAA,MAC1B;AAAA,MACA;AAAA,MACA,SAAS,KAAK;AAAA,MACd,OAAO,KAAK;AAAA,IACd,CAAC;AACD,UAAM;AACN,UAAM,KAAK,MAAM,GAAG;AAAA,EACtB;AAAA;AAAA,EAGA,OAAe,kBACb,QACA,MACA,QAC6B;AAC7B,UAAM,MAAM,cAAc,EAAE,QAAQ,MAAM,OAAO,CAAC;AAClD,UAAM;AACN,UAAM,KAAK,MAAM,GAAG;AAAA,EACtB;AAAA;AAAA,EAGA,OAAe,UAAU,QAA6C;AACpE,UAAM,MAAM,WAAW,MAAM;AAC7B,UAAM;AACN,UAAM,KAAK,MAAM,GAAG;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAkBQ,eAAe,cAA6B,MAAiC;AACnF,QAAI,KAAK,QAAQ,WAAW,aAAa;AAKvC,YAAM,UAAU,IAAI;AAAA,QAClB,KAAK,YAAY,IAAI,CAAC,MAAO,EAAE,QAAsC,YAAY;AAAA,MACnF;AACA,YAAM,WAAW,KAAK,UACnB,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,GAAG,QAAQ,YAAY,CAAC,EACpD;AAAA,QAAI,CAAC,OACJ,eAAe;AAAA,UACb,QAAQ;AAAA,UACR,YAAY,GAAG,QAAQ;AAAA,UACvB,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAKF,aAAO,SAAS,SAAS,CAAC,GAAG,KAAK,aAAa,GAAG,QAAQ,IAAI,KAAK;AAAA,IACrE;AACA,WAAO,KAAK;AAAA,MACV;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACP;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAeQ,iBACN,cACA,mBACA,WACA,aACe;AACf,UAAM,aAAa,aAAa;AAAA,MAC9B,CAAC,MAAO,EAAE,QAA8B,SAAS;AAAA,IACnD;AACA,UAAM,aAAa,aAAa,OAAO,CAAC,MAAO,EAAE,QAA8B,SAAS,MAAM;AAC9F,UAAM,YAAY;AAAA,MAChB,KAAK,qBAAqB,YAAY,mBAAmB,WAAW,WAAW;AAAA,IACjF;AAIA,WAAO,CAAC,GAAG,YAAY,SAAS;AAAA,EAClC;AAAA;AAAA,EAGQ,qBACN,YACA,mBACA,WACA,aACQ;AACR,UAAM,QAAkB,CAAC,gBAAgB;AACzC,eAAW,KAAK,YAAY;AAC1B,YAAM,IAAK,EAAE,QAAwB;AAKrC,YAAM,QAAQ,qBAAqB,CAAC;AACpC,UAAI,UAAU,MAAM;AAClB,YAAI,MAAO,OAAM,KAAK,KAAK;AAAA,MAC7B,OAAO;AACL,cAAM,KAAK,iBAAiB,CAAC,eAAe;AAAA,MAC9C;AAAA,IACF;AACA,UAAM,KAAK,GAAG,oBAAoB,mBAAmB,WAAW,WAAW,CAAC;AAC5E,UAAM,KAAK,iBAAiB;AAC5B,WAAO,MAAM,KAAK,IAAI;AAAA,EACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUQ,iBAAiB,OAAsB,aAA0C;AACvF,UAAM,QAAQ,YAAY;AAAA,MAAQ,CAAC,MACjC,oBAAoB,EAAE,mBAAmB,EAAE,WAAW,EAAE,WAAW;AAAA,IACrE;AACA,QAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,WAAO,CAAC,GAAG,OAAO,SAAS,CAAC,kBAAkB,GAAG,OAAO,iBAAiB,EAAE,KAAK,IAAI,CAAC,CAAC;AAAA,EACxF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,MAAM,KAAiC;AACnD,QAAI,CAAC,KAAK,KAAK,MAAO;AACtB,QAAI,KAAK,sBAAsB;AAC7B,WAAK,uBAAuB;AAC5B,UAAI;AACF,YAAI,KAAK,KAAK,MAAM,OAAQ,OAAM,KAAK,KAAK,MAAM,OAAO;AACzD,YAAI,KAAK,KAAK,YAAa,OAAM,KAAK,KAAK,MAAM,MAAM,KAAK,KAAK,WAAW;AAAA,MAC9E,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,gBAAQ,OAAO,MAAM,0BAA0B,OAAO;AAAA,CAAI;AAAA,MAC5D;AAAA,IACF;AACA,QAAI;AACF,YAAM,KAAK,KAAK,MAAM,MAAM,GAAG;AAAA,IACjC,SAAS,KAAK;AACZ,YAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,cAAQ,OAAO,MAAM,yBAAyB,OAAO;AAAA,CAAI;AAAA,IAC3D;AAAA,EACF;AACF;AASA,SAAS,qBAAqB,MAA6B;AACzD,QAAM,IAAI,2DAA2D,KAAK,IAAI;AAC9E,SAAO,IAAI,EAAE,CAAC,IAAK;AACrB;AAGA,SAAS,oBACP,mBACA,WACA,aACU;AACV,QAAM,QAAkB,CAAC;AAGzB,aAAW,OAAO,mBAAmB;AACnC,UAAM,IAAI,IAAI;AACd,QAAI,EAAE,SAAS,YAAY;AACzB,YAAM,KAAK,eAAgB,IAAI,QAA4B,QAAQ,aAAa;AAAA,IAClF,WAAW,EAAE,SAAS,QAAQ;AAC5B,YAAM,KAAK,WAAY,IAAI,QAAwB,IAAI,SAAS;AAAA,IAClE;AAAA,EACF;AACA,aAAW,MAAM,WAAW;AAC1B,UAAM,IAAI,GAAG;AACb,UAAM,KAAK,sBAAsB,EAAE,IAAI,SAAS,EAAE,YAAY,KAAK,EAAE,SAAS,cAAc;AAAA,EAC9F;AACA,aAAW,OAAO,aAAa;AAC7B,UAAM,IAAI,IAAI;AACd,UAAM;AAAA,MACJ,2BAA2B,EAAE,YAAY,aAAa,EAAE,eAAe,WAAW,KAAK,EAAE,MAAM;AAAA,IACjG;AAAA,EACF;AACA,SAAO;AACT;AAQO,SAAS,eAAe,KAAqB;AAClD,QAAM,QAAQ,iCAAiC,KAAK,GAAG;AACvD,UAAQ,QAAQ,MAAM,CAAC,IAAK,KAAK,KAAK;AACxC;;;ADljCA,IAAM,2BAA2B;AAO1B,SAAS,gBAAgB,SAAgC;AAC9D,QAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAM,MAAqB,CAAC;AAC5B,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;AACrC,UAAM,OAAO,MAAM,CAAC,EAAG,KAAK;AAC5B,QAAI,CAAC,KAAM;AACX,QAAI;AACF,UAAI,KAAK,KAAK,MAAM,IAAI,CAAgB;AAAA,IAC1C,SAAS,KAAK;AACZ,YAAM,iBAAiB,MAAM,MAAM,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,CAAC;AAC5E,UAAI,eAAgB;AACpB,YAAM;AAAA,IACR;AAAA,EACF;AACA,SAAO;AACT;AAGA,eAAsB,kBAAkBC,QAAsC;AAC5E,SAAO,gBAAgB,MAAMC,UAASD,QAAM,MAAM,CAAC;AACrD;AASA,IAAM,gBAAgB;AAOtB,eAAsB,oBACpBE,YACA,WACkC;AAClC,MAAI,OAAgC;AACpC,aAAW,WAAW,MAAM,SAASA,UAAS,GAAG;AAC/C,eAAW,QAAQ,MAAM,UAAUC,MAAKD,YAAW,OAAO,CAAC,GAAG;AAC5D,YAAM,QAAQ,cAAc,KAAK,IAAI;AACrC,UAAI,CAAC,SAAS,MAAM,CAAC,MAAM,UAAW;AACtC,YAAM,QAAQ,OAAO,MAAM,CAAC,CAAC;AAC7B,UAAI,CAAC,QAAQ,QAAQ,KAAK,OAAO;AAC/B,eAAO,EAAE,MAAMC,MAAKD,YAAW,SAAS,IAAI,GAAG,SAAS,MAAM;AAAA,MAChE;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAOA,eAAsB,gBAAgBA,YAA2C;AAC/E,QAAM,YAAY,MAAM,SAASA,UAAS,GAAG,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAC9E,aAAW,WAAW,UAAU;AAC9B,UAAM,SAAS,MAAM,UAAUC,MAAKD,YAAW,OAAO,CAAC,GAAG,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAC3F,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,cAAc,KAAK,IAAI;AACrC,UAAI,CAAC,MAAO;AACZ,UAAI,MAAM,yBAAyBC,MAAKD,YAAW,SAAS,IAAI,CAAC,GAAG;AAClE,eAAO,MAAM,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,yBAAyB,MAAgC;AACtE,MAAI;AACF,UAAM,WAAW,MAAM,kBAAkB,IAAI;AAC7C,WAAO,SAAS,KAAK,CAAC,QAAQ,CAAC,cAAc,GAAG,CAAC;AAAA,EACnD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,SAAS,KAAgC;AACtD,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,WAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EACjE,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAe,UAAU,KAAgC;AACvD,MAAI;AACF,UAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,WAAO,QAAQ,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI;AAAA,EAC5D,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,eAAe,KAA2D;AACjF,SAAO,eAAe,GAAG,KAAM,IAAI,QAA8B,SAAS;AAC5E;AAEA,SAAS,aAAa,KAAyD;AAC7E,SAAO,eAAe,GAAG,KAAM,IAAI,QAA8B,SAAS;AAC5E;AAEA,SAAS,gBAAgB,KAA4D;AACnF,SAAO,eAAe,GAAG,KAAM,IAAI,QAA8B,SAAS;AAC5E;AAEA,SAAS,iBAAiB,KAAiC;AACzD,QAAM,IAAI,IAAI;AACd,SAAO,EAAE,SAAS,qBAAsB,EAAE,gBAAgB,OAAQ;AACpE;AAMO,SAAS,YAAY,UAAuC;AACjE,QAAM,OAAQ,SAAS,KAAK,aAAa,KAAwC;AACjF,QAAM,gBAAgB,kBAAkB,QAAQ;AAChD,QAAM,mBAAmB,mBAAmB,QAAQ;AAKpD,QAAM,OAAO,SAAS,SAAS,SAAS,CAAC;AACzC,MAAI,QAAQ,gBAAgB,IAAI,GAAG;AACjC,UAAM,IAAI,KAAK;AACf,QAAI,EAAE,WAAW,aAAa;AAC5B,YAAM,SAAuB;AAAA,QAC3B,SAAS,CAAC;AAAA,QACV,WAAW,CAAC;AAAA,QACZ,eAAe;AAAA,QACf;AAAA,QACA,kBAAkB;AAAA;AAAA,QAClB,cAAc;AAAA;AAAA,QACd,gBAAgB,CAAC;AAAA,QACjB;AAAA,MACF;AACA,UAAI,EAAE,SAAS,aAAa;AAG1B,cAAM,cAAc,yBAAyB,QAAQ;AACrD,eAAO,iBAAiB;AAAA,UACtB;AAAA,EAAsB,eAAe,WAAW,CAAC;AAAA;AAAA,QACnD;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,EACF;AAGA,QAAM,UAAkC,CAAC;AAEzC,MAAI,UAAyB,CAAC;AAE9B,MAAI,WAA0B,CAAC;AAC/B,MAAI,UAAkC,CAAC;AACvC,MAAI,YAAY;AAGhB,MAAI,eAAe;AAGnB,QAAM,mBAAmB,oBAAI,IAAY;AACzC,QAAM,cAAc,oBAAI,IAAY;AACpC,MAAI,eAAe;AACnB,QAAM,iBAAgC,CAAC;AAEvC,QAAM,iBAAiB,CAAC,OACtB,eAAe;AAAA,IACb,QAAQ;AAAA,IACR,YAAY;AAAA,IACZ,YAAY;AAAA,EACd,CAAC;AAEH,QAAM,uBAAuB,MAAY;AAOvC,UAAM,OAAO,eAAe,SAAS,OAAO,CAAC,MAAM,iBAAiB,CAAC,MAAM,IAAI,IAAI;AACnF,cAAU,CAAC,GAAG,MAAM,GAAG,OAAO;AAC9B,eAAW,CAAC;AACZ,cAAU,CAAC;AACX,gBAAY;AAAA,EACd;AAEA,aAAW,OAAO,UAAU;AAC1B,QAAI,cAAc,GAAG,EAAG;AAExB,QAAI,eAAe,GAAG,GAAG;AAGvB,UAAI,UAAW,sBAAqB;AACpC,kBAAY;AACZ,iBAAW;AACX,gBAAU,CAAC;AACX;AAAA,IACF;AACA,QAAI,aAAa,GAAG,GAAG;AACrB,UAAI,IAAI,QAAQ,WAAW,aAAa;AAOtC,cAAM,WAAW,SAAS,OAAO,CAAC,MAAM;AACtC,gBAAM,KAAK,iBAAiB,CAAC;AAC7B,iBAAO,OAAO,QAAS,iBAAiB,IAAI,EAAE,KAAK,CAAC,YAAY,IAAI,EAAE;AAAA,QACxE,CAAC;AAKD,cAAM,oBAAoB,IAAI;AAAA,UAC5B,SAAS,IAAI,gBAAgB,EAAE,OAAO,CAAC,OAAqB,OAAO,IAAI;AAAA,QACzE;AACA,mBAAW,MAAM,kBAAkB;AACjC,cAAI,YAAY,IAAI,EAAE,KAAK,kBAAkB,IAAI,EAAE,EAAG;AACtD,kBAAQ,KAAK,eAAe,EAAE,CAAC;AAC/B,sBAAY,IAAI,EAAE;AAAA,QACpB;AACA,gBAAQ,KAAK,GAAI,UAAqC,GAAG,OAAO;AAChE,mBAAW,MAAM,kBAAmB,aAAY,IAAI,EAAE;AACtD,mBAAW,KAAK,SAAS;AACvB,gBAAM,IAAI,EAAE;AACZ,cAAI,EAAE,SAAS,eAAe,EAAE,gBAAgB,eAAe,EAAE,cAAc;AAC7E,6BAAiB,IAAI,EAAE,YAAY;AAAA,UACrC;AAAA,QACF;AACA,wBAAgB;AAChB,mBAAW,CAAC;AACZ,kBAAU,CAAC;AACX,oBAAY;AAAA,MACd,OAAO;AACL,6BAAqB;AAAA,MACvB;AACA;AAAA,IACF;AAEA,QAAI,uBAAuB,GAAG,GAAG;AAC/B,qBAAe,KAAK,GAAG;AACvB,YAAM,OAAQ,IAAI,QAA8B;AAChD,UAAI,SAAS,QAAQ;AAGnB,gBAAQ,KAAK,GAAG;AAAA,MAClB,WAAW,WAAW;AACpB,gBAAQ,KAAK,GAAG;AAAA,MAClB;AAGA;AAAA,IACF;AACA,QAAI,eAAe,GAAG,GAAG;AACvB,YAAM,IAAK,IAAI,QAA8B;AAC7C,UAAI,MAAM,mBAAoB,gBAAe;AAAA,eACpC,MAAM,iBAAkB,gBAAe;AAAA,eACvC,MAAM,YAAY,IAAI,QAAQ,UAAU,OAAO,EAAG,gBAAe,KAAK,GAAG;AAAA,IAGpF;AAAA,EACF;AAIA,MAAI,UAAW,sBAAqB;AAQpC,YAAU,QAAQ,OAAO,CAAC,MAAM;AAC9B,UAAM,KAAK,iBAAiB,CAAC;AAC7B,WAAO,OAAO,QAAS,iBAAiB,IAAI,EAAE,KAAK,CAAC,YAAY,IAAI,EAAE;AAAA,EACxE,CAAC;AAMD,QAAM,YAAY,IAAI,IAAY,WAAW;AAC7C,aAAW,KAAK,SAAS;AACvB,UAAM,KAAK,iBAAiB,CAAC;AAC7B,QAAI,OAAO,KAAM,WAAU,IAAI,EAAE;AAAA,EACnC;AACA,QAAM,kBAAiC,CAAC;AACxC,aAAW,MAAM,kBAAkB;AACjC,QAAI,UAAU,IAAI,EAAE,EAAG;AACvB,oBAAgB,KAAK,eAAe,EAAE,CAAC;AAAA,EACzC;AAEA,SAAO;AAAA,IACL;AAAA,IACA,WAAW,CAAC,GAAG,SAAS,GAAG,eAAe;AAAA,IAC1C,eAAe;AAAA,IACf;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAGA,SAAS,kBAAkB,UAAsC;AAC/D,WAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,CAAC,eAAe,GAAG,EAAG;AAC1B,UAAM,IAAI,IAAI;AACd,QAAI,EAAE,SAAS,iBAAiB,EAAE,QAAS,QAAO,EAAE;AAAA,EACtD;AACA,SAAO,iBAAiB;AAC1B;AAGA,SAAS,mBAAmB,UAAiC;AAC3D,WAAS,IAAI,SAAS,SAAS,GAAG,KAAK,GAAG,KAAK;AAC7C,UAAM,MAAM,SAAS,CAAC;AACtB,QAAI,CAAC,eAAe,GAAG,EAAG;AAC1B,UAAM,IAAI,IAAI;AACd,QAAI,EAAE,SAAS,iBAAiB,EAAE,QAAS,QAAO,EAAE,QAAQ;AAAA,EAC9D;AACA,SAAO;AACT;AAGA,SAAS,yBAAyB,UAAiC;AACjE,MAAI,OAAO;AACX,MAAI,UAAU;AACd,MAAI,YAAY;AAChB,aAAW,OAAO,UAAU;AAC1B,QAAI,eAAe,GAAG,GAAG;AACvB,kBAAY;AACZ,gBAAU;AACV;AAAA,IACF;AACA,QAAI,aAAa,GAAG,GAAG;AACrB;AAKE,YAAI,IAAI,QAAQ,WAAW,YAAa,QAAO;AAC/C,oBAAY;AAAA,MACd;AACA;AAAA,IACF;AACA,QAAI,CAAC,aAAa,CAAC,uBAAuB,GAAG,EAAG;AAChD,UAAM,IAAI,IAAI;AACd,QAAI,EAAE,SAAS,UAAU,EAAE,SAAS,eAAe,EAAE,KAAM,YAAW,EAAE;AAAA,EAC1E;AACA,SAAO;AACT;;;AE3bA,OAAOE,SAAQ;AACf,OAAOC,SAAQ;AACf,OAAOC,WAAU;AACjB,SAAS,aAAa,cAAAC,mBAAkB;AAYjC,SAAS,gBAAgB,OAAa,oBAAI,KAAK,GAAW;AAC/D,QAAM,MAAM,CAAC,MAAc,EAAE,SAAS,EAAE,SAAS,GAAG,GAAG;AACvD,QAAM,KACJ,GAAG,gBAAgB,IAAI,CAAC,IACpB,IAAI,KAAK,SAAS,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC,IAAI,IAAI,KAAK,WAAW,CAAC,CAAC;AAC9E,QAAM,MAAMC,YAAW,EAAE,QAAQ,MAAM,EAAE,EAAE,MAAM,GAAG,CAAC;AACrD,SAAO,WAAW,EAAE,IAAI,GAAG;AAC7B;AAOO,SAAS,mBACd,cACA,WACA,KACA,OAAO,oBAAI,KAAK,GACI;AACpB,SAAO;AAAA,IACL;AAAA,IACA,KAAK;AAAA,IACL,SAAS,IAAI;AAAA,IACb,YAAY,IAAI;AAAA,IAChB,UAAU,QAAQ;AAAA,IAClB,WAAW,aAAa;AAAA,IACxB,MAAM,gBAAgB,IAAI;AAAA,EAC5B;AACF;AAEA,SAAS,eAAuB;AAI9B,MAAI;AACF,QAAI,QAAQ,aAAa,SAAS;AAChC,aAAO,GAAGC,IAAG,QAAQ,CAAC,IAAIA,IAAG,QAAQ,CAAC;AAAA,IACxC;AACA,WAAO,GAAGA,IAAG,KAAK,CAAC,IAAIA,IAAG,QAAQ,CAAC;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,IAAM,sBAAsB;AAS5B,eAAsB,oBACpB,MACA,WACA,SACiB;AACjB,QAAM,OAAO,cAAc,MAAM,WAAW,OAAO;AACnD,QAAMC,IAAG,MAAM,MAAM,EAAE,WAAW,KAAK,CAAC;AAIxC,WAAS,UAAU,GAAG,UAAU,qBAAqB,WAAW;AAC9D,UAAM,MAAMC,MAAK,KAAK,MAAM,OAAOH,YAAW,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE;AAC7D,QAAI;AACF,YAAME,IAAG,MAAM,GAAG;AAClB,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,UAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,IAC9D;AAAA,EACF;AACA,QAAM,IAAI;AAAA,IACR,uDAAuD,IAAI,UAAU,mBAAmB;AAAA,EAC1F;AACF;AAGA,IAAM,cAAsC;AAAA,EAC1C,aAAa;AAAA,EACb,cAAc;AAAA,EACd,aAAa;AAAA,EACb,cAAc;AAChB;AAaA,eAAsB,wBACpB,OACA,KACwB;AACxB,QAAM,UAAU,CAAC,MACd,EAAE,QAA8B,SAAS;AAC5C,MAAI,CAAC,MAAM,KAAK,OAAO,EAAG,QAAO;AAEjC,QAAM,QAAkB,CAAC;AACzB,aAAW,OAAO,OAAO;AACvB,QAAI,CAAC,QAAQ,GAAG,EAAG;AACnB,UAAM,MAAO,IAAI,QAAmC,aAAa;AACjE,QAAI,gBAAgB,KAAK,GAAG,GAAG;AAC7B,YAAM,KAAK,oBAAoB,GAAG,GAAG;AACrC;AAAA,IACF;AACA,UAAM,QAAQ,+BAA+B,KAAK,GAAG;AACrD,QAAI,CAAC,OAAO;AACV,YAAM,KAAK,wDAAwD;AACnE;AAAA,IACF;AACA,UAAMA,IAAG,MAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AACvC,UAAM,MAAM,YAAY,MAAM,CAAC,EAAG,YAAY,CAAC,KAAK;AAIpD,QAAI;AACJ,eAAS;AACP,aAAOC,MAAK,KAAK,KAAK,UAAU,YAAY,CAAC,EAAE,SAAS,KAAK,CAAC,IAAI,GAAG,EAAE;AACvE,UAAI;AACF,cAAMD,IAAG,UAAU,MAAM,OAAO,KAAK,MAAM,CAAC,GAAI,QAAQ,GAAG,EAAE,MAAM,KAAK,CAAC;AACzE;AAAA,MACF,SAAS,KAAK;AACZ,YAAK,IAA8B,SAAS,SAAU,OAAM;AAAA,MAC9D;AAAA,IACF;AACA,UAAM,KAAK,oBAAoB,IAAI,GAAG;AAAA,EACxC;AAGA,QAAM,OAAO,MAAM,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC5C,QAAM,SAAS,MAAM,KAAK,IAAI;AAC9B,QAAM,cAAc,KAAK,cAAc,CAAC,MAAM;AAC5C,UAAM,IAAI,EAAE;AACZ,WAAO,EAAE,SAAS,UAAU,EAAE,SAAS;AAAA,EACzC,CAAC;AACD,MAAI,gBAAgB,GAAI,QAAO,CAAC,GAAG,MAAM,SAAS,MAAM,CAAC;AACzD,SAAO,KAAK,IAAI,CAAC,GAAG,MAAM;AACxB,QAAI,MAAM,YAAa,QAAO;AAC9B,UAAM,IAAI,EAAE;AACZ,WAAO,EAAE,GAAG,GAAG,SAAS,EAAE,GAAG,GAAG,MAAM,GAAG,EAAE,IAAI;AAAA;AAAA,EAAO,MAAM,GAAG,EAAE;AAAA,EACnE,CAAC;AACH;;;ACtJA,IAAM,oBAAoB;AAE1B,IAAM,kBAAkB;AAgBjB,SAAS,iBAAiB,aAAqB,kBAAkC;AACtF,QAAM,OAAO,CAAC,MAAe,EAAE,SAAS,oBAAoB,EAAE,MAAM,GAAG,iBAAiB,IAAI;AAC5F,QAAM,QAAQ;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,KAAK,WAAW;AAAA,EAClB;AACA,MAAI,iBAAiB,KAAK,GAAG;AAC3B,UAAM,KAAK,IAAI,eAAe,KAAK,gBAAgB,CAAC;AAAA,EACtD;AACA,SAAO,MAAM,KAAK,IAAI;AACxB;AAGO,SAAS,cAAc,KAA4B;AACxD,MAAI,IAAI,IAAI,QAAQ,QAAQ,GAAG,EAAE,KAAK;AAEtC,WAAS,OAAO,IAAI,SAAS,KAAI;AAC/B,WAAO;AACP,QAAI,EACD,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,8BAA8B,EAAE,EACxC,QAAQ,sBAAsB,EAAE,EAChC,KAAK;AAAA,EACV;AACA,MAAI,CAAC,EAAG,QAAO;AACf,SAAO,EAAE,SAAS,kBAAkB,EAAE,MAAM,GAAG,eAAe,IAAI;AACpE;AASA,eAAsB,qBACpB,KACA,MAC6B;AAC7B,MAAI,CAAC,KAAK,SAAS,KAAK,GAAG;AACzB,WAAO,EAAE,OAAO,MAAM,OAAO,KAAK;AAAA,EACpC;AACA,QAAM,SAAS,iBAAiB,KAAK,UAAU,KAAK,aAAa;AACjE,QAAM,MAAM,IAAI,eAAe;AAAA,IAC7B,aAAa,CAAC,SAAS,MAAM,CAAC;AAAA,IAC9B,GAAI,KAAK,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,EAC/C,CAAC;AACD,MAAI,YAAY;AAChB,MAAI,QAA4B;AAChC,aAAS;AACP,UAAM,OAAO,MAAM,IAAI,KAAK;AAC5B,QAAI,KAAK,MAAM;AACb,UAAI,KAAK,MAAM,WAAW,YAAa,QAAO,EAAE,OAAO,MAAM,MAAM;AACnE;AAAA,IACF;AACA,UAAM,MAAM,KAAK;AACjB,QAAI,gBAAgB,GAAG,EAAG,cAAa,IAAI,QAAQ;AACnD,QAAI,aAAa,GAAG,GAAG;AACrB,YAAM,IAAI,IAAI,QAAQ;AACtB,cAAQ,QACJ;AAAA,QACE,YAAY,MAAM,aAAa,EAAE;AAAA,QACjC,aAAa,MAAM,cAAc,EAAE;AAAA,QACnC,QAAQ,MAAM,SAAS,EAAE;AAAA,QACzB,OAAO,MAAM,QAAQ,EAAE;AAAA,MACzB,IACA,EAAE,GAAG,EAAE;AAAA,IACb;AAAA,EACF;AACA,SAAO,EAAE,OAAO,cAAc,SAAS,GAAG,MAAM;AAClD;AAEA,SAAS,gBAAgB,KAAmD;AAC1E,QAAM,UAAU,IAAI;AACpB,SAAO,IAAI,SAAS,eAAe,QAAQ,SAAS,UAAU,QAAQ,SAAS;AACjF;AAEA,SAAS,aAAa,KAAyD;AAC7E,SAAO,IAAI,SAAS,eAAgB,IAAI,QAA8B,SAAS;AACjF;;;ACvDA,IAAM,uBAAuB;AAM7B,SAAS,gBAAgB,MAAc,KAAkB,MAAoC;AAC3F,MAAI,KAAK,UAAU,qBAAsB,QAAO;AAChD,MAAI,IAAI,UAAU,IAAI,OAAO,SAAS,EAAG,QAAO;AAChD,QAAM,IAAI,IAAI;AACd,MAAI,IAAI,SAAS,eAAe,EAAE,SAAS,UAAU,EAAE,SAAS,QAAQ,CAAC,EAAE,KAAM,QAAO;AACxF,SAAO,OAAO,GAAG,IAAI;AAAA,EAAK,EAAE,IAAI,KAAK,EAAE;AACzC;AAEO,IAAM,UAAN,MAAc;AAAA,EACV;AAAA;AAAA,EAEA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA;AAAA,EAEA;AAAA,EAEQ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACT,cAAc;AAAA;AAAA,EAEd,gBAAgB;AAAA,EAChB,qBAAqB;AAAA;AAAA,EAErB,sBAAsB;AAAA,EAE9B,YAAY,QAAuB;AACjC,SAAK,YAAY,OAAO,KAAK;AAC7B,SAAK,WAAW,OAAO,KAAK;AAC5B,SAAK,UAAU,OAAO,KAAK;AAC3B,SAAK,eAAe,OAAO,KAAK;AAChC,SAAK,cAAc,OAAO;AAC1B,SAAK,QAAQ,OAAO;AACpB,SAAK,OAAO,YAAY,OAAO,IAAI;AACnC,SAAK,cAAc,OAAO,sBAAsB;AAChD,QAAI,OAAO,eAAgB,MAAK,iBAAiB,OAAO;AACxD,QAAI,OAAO,cAAe,MAAK,gBAAgB,OAAO;AACtD,QAAI,OAAO,eAAgB,MAAK,iBAAiB,OAAO;AACxD,SAAK,SAAS,IAAI,cAAc;AAAA,MAC9B,KAAK,OAAO;AAAA,MACZ,aAAa,OAAO;AAAA,MACpB,GAAI,OAAO,QAAQ,EAAE,OAAO,OAAO,MAAM,IAAI,CAAC;AAAA,MAC9C,GAAI,OAAO,aAAa,SAAY,EAAE,UAAU,OAAO,SAAS,IAAI,CAAC;AAAA;AAAA,MAErE,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,UAAU,IAAI,CAAC;AAAA,MAC1D,GAAI,OAAO,aAAa,EAAE,YAAY,OAAO,WAAW,IAAI,CAAC;AAAA,MAC7D,GAAI,OAAO,qBAAqB,EAAE,cAAc,OAAO,mBAAmB,IAAI,CAAC;AAAA,MAC/E,aAAa,KAAK;AAAA,IACpB,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,OAAO,IAAI,aAA4B,MAAgD;AAGrF,QAAI,KAAK,gBAAgB;AACvB,oBAAc,MAAM,wBAAwB,aAAa,KAAK,cAAc;AAAA,IAC9E;AACA,UAAM,KAAK,kBAAkB;AAK7B,UAAM,UAAU,CAAC,KAAK;AACtB,QAAI,SAAS;AACX,iBAAW,KAAK,aAAa;AAC3B,aAAK,gBAAgB,gBAAgB,KAAK,eAAe,GAAG,MAAM;AAAA,MACpE;AAAA,IACF;AACA,qBAAiB,OAAO,KAAK,OAAO,IAAI,aAAa,IAAI,GAAG;AAC1D,UAAI,SAAS;AACX,aAAK,qBAAqB,gBAAgB,KAAK,oBAAoB,KAAK,WAAW;AAAA,MACrF;AACA,YAAM;AAAA,IACR;AACA,QAAI,WAAW,KAAK,cAAc,KAAK,EAAG,MAAK,sBAAsB;AAAA,EACvE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUA,OAAO,QAAQ,MAA8D;AAC3E,WAAO,KAAK,OAAO,QAAQ,IAAI;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,iBAAsC;AACpC,WAAO,KAAK,OAAO,eAAe;AAAA,EACpC;AAAA;AAAA,EAGA,MAAc,oBAAmC;AAC/C,QAAI,KAAK,YAAa;AACtB,QAAI,KAAK,OAAO;AACd,UAAI;AACF,cAAM,KAAK,MAAM,MAAM,KAAK,IAAI;AAAA,MAClC,SAAS,KAAK;AACZ,cAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC/D,gBAAQ,OAAO,MAAM,sCAAsC,OAAO;AAAA,CAAI;AAAA,MACxE;AAAA,IACF;AACA,SAAK,cAAc;AAAA,EACrB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,cAAc,MAIY;AAC9B,QAAI,CAAC,KAAK,cAAe,QAAO,EAAE,OAAO,MAAM,OAAO,KAAK;AAC3D,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,UAAU,KAAK;AAAA,MACf,eAAe,KAAK;AAAA,IACtB;AACA,WAAO,qBAAqB,KAAK,cAAc,GAAG;AAAA,MAChD,GAAG;AAAA,MACH,GAAI,MAAM,SAAS,EAAE,QAAQ,KAAK,OAAO,IAAI,CAAC;AAAA,IAChD,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,eAAe,MAA0C;AACvD,WAAO,KAAK,YAAY,eAAe,IAAI;AAAA,EAC7C;AAAA;AAAA,EAGA,IAAI,cAA2B;AAC7B,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,UAAgB;AACd,SAAK,YAAY,UAAU;AAAA,EAC7B;AACF;;;ACjPA,OAAOE,SAAQ;AACf,OAAOC,WAAU;AA4DjB,IAAM,qBAAqB;AA0CpB,SAAS,0BAA0B,YAAoB,eAAgC;AAC5F,MAAI,cAAc,EAAG,QAAO;AAC5B,MAAI,OAAO,kBAAkB,SAAU,QAAO;AAC9C,SAAO,KAAK,IAAI,YAAY,KAAK,MAAM,gBAAgB,IAAI,CAAC;AAC9D;AAGA,eAAsB,YAAY,OAA2B,CAAC,GAAmB;AAC/E,QAAM,QAAQ,MAAM,qBAAqB,IAAI;AAC7C,QAAM,gBAAgB,MAAM,kBAAkB,MAAM,MAAM,MAAM,SAAS;AACzE,SAAO,IAAI,MAAM,OAAO,aAAa;AACvC;AAEO,IAAM,QAAN,MAAY;AAAA,EACjB,YACW,OACA,eACT;AAFS;AACA;AAAA,EACR;AAAA,EAFQ;AAAA,EACA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOX,MAAM,cAAc,OAA6B,CAAC,GAAqB;AAMrE,QAAI,KAAK,YAAY,UAAa,KAAK,aAAa,QAAW;AAC7D,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,QAAI;AACJ,QAAI,KAAK,YAAY,QAAW;AAE9B,YAAM,gBAAgB,KAAK,eAAe,KAAK,SAAS,KAAK,QAAQ;AAAA,IACvE,WAAW,KAAK,cAAc,eAAe;AAC3C,YAAM,KAAK,cAAc;AAAA,IAC3B,OAAO;AACL,YAAM,IAAI;AAAA,QACR;AAAA,MACF;AAAA,IACF;AACA,UAAM,aAAa,SAAS,KAAK,eAAe,GAAG;AACnD,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR,sDAAwB,eAAe,GAAG,CAAC;AAAA,MAC7C;AAAA,IACF;AAIA,UAAM,SAAS,KAAK,UAAU,WAAW;AACzC,UAAM,UAAU,KAAK,WAAW,WAAW;AAK3C,QAAI;AACJ,QAAI,KAAK,cAAc;AACrB,qBAAeC,MAAK,QAAQ,KAAK,YAAY;AAC7C,UAAIC;AACJ,UAAI;AACF,QAAAA,QAAO,MAAMC,IAAG,KAAK,YAAY;AAAA,MACnC,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,qCAAiB,YAAY;AAAA,QAC/B;AAAA,MACF;AACA,UAAI,CAACD,MAAK,YAAY,GAAG;AACvB,cAAM,IAAI,MAAM,2CAAkB,YAAY,QAAG;AAAA,MACnD;AAAA,IACF,OAAO;AACL,qBAAe,MAAM;AAAA,QACnB,KAAK,MAAM;AAAA,QACX,KAAK,MAAM;AAAA,QACX,KAAK,MAAM;AAAA,MACb;AAAA,IACF;AACA,UAAM,YAAY,gBAAgB;AAClC,UAAM,gBAAgB,KAAK,iBAAiB;AAG5C,UAAM,QAAQ,MAAM,eAAe,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAC5F,UAAM,kBAAkB,MAAM;AAAA,MAC5B,KAAK,MAAM;AAAA,MACX,KAAK,MAAM;AAAA,MACX,KAAK,MAAM;AAAA,IACb;AAOA,UAAM,eAAe;AAAA,MACnB,KAAK;AAAA,MACL,mBAAmB,cAAc,WAAW;AAAA,QAC1C,SAAS,KAAK,MAAM;AAAA,QACpB,YAAY,WAAW,KAAK,MAAM,MAAM,KAAK,MAAM,SAAS;AAAA,MAC9D,CAAC;AAAA,MACD,OAAO,KAAK,KAAK;AAAA,MACjB;AAAA,IACF;AAEA,UAAM,KAAK,MAAM,KAAK,aAAa;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,CAAC;AAED,UAAM,QAAQ,IAAI,OAAO;AAAA,MACvB,WAAW,UAAU,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAAA,MAC9E;AAAA,IACF,CAAC;AAED,WAAO,IAAI,QAAQ;AAAA,MACjB,MAAM;AAAA,QACJ,YAAY;AAAA,QACZ,UAAU,WAAW;AAAA,QACrB,UAAU,WAAW;AAAA,QACrB,sBAAsB,WAAW,kBAAkB;AAAA,QACnD,eAAe;AAAA,QACf,OAAO,GAAG;AAAA,QACV,gBAAgB,KAAK,MAAM,aAAa,OAAO,kBAAkB;AAAA,QACjE,aAAa,KAAK,MAAM;AAAA,QACxB,WAAW;AAAA,MACb;AAAA,MACA,KAAK,GAAG;AAAA,MACR,aAAa,GAAG;AAAA,MAChB;AAAA,MACA,WAAW,GAAG;AAAA,MACd,eAAe,GAAG;AAAA,MAClB,YAAY,GAAG;AAAA;AAAA,MAEf,GAAI,WAAW,WAAW,QACtB;AAAA,QACE,gBAAgBD,MAAK;AAAA,UACnB,cAAc,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAAA,UACvE;AAAA,QACF;AAAA,MACF,IACA,CAAC;AAAA;AAAA,MAEL,GAAI,KAAK,MAAM,aAAa,cAAc,SACtC,EAAE,UAAU,KAAK,MAAM,aAAa,UAAU,IAC9C,CAAC;AAAA,IACP,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAgBA,MAAM,cAAc,MAA8C;AAChE,UAAM,EAAE,UAAU,IAAI;AACtB,UAAM,MAAM,UAAU,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAC/E,UAAM,UAAU,MAAM,oBAAoB,KAAK,SAAS;AACxD,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI,MAAM,mCAAe,SAAS,gBAAM,GAAG,sEAAoB;AAAA,IACvE;AACA,UAAM,UAAU,YAAY,MAAM,kBAAkB,QAAQ,IAAI,CAAC;AACjE,QAAI,CAAC,QAAQ,MAAM;AACjB,YAAM,IAAI,MAAM,sEAA8B,QAAQ,IAAI,EAAE;AAAA,IAC9D;AACA,UAAM,OAAO,QAAQ,KAAK;AAE1B,QAAI,OAAO,KAAK,aAAa,UAAU;AACrC,YAAM,IAAI;AAAA,QACR,uJAAmD,QAAQ,IAAI;AAAA,MACjE;AAAA,IACF;AAGA,UAAM,eAAe,KAAK;AAC1B,QAAIC;AACJ,QAAI;AACF,MAAAA,QAAO,MAAMC,IAAG,KAAK,YAAY;AAAA,IACnC,QAAQ;AACN,YAAM,IAAI,MAAM,iEAA8B,YAAY,sCAAQ;AAAA,IACpE;AACA,QAAI,CAACD,MAAK,YAAY,GAAG;AACvB,YAAM,IAAI,MAAM,iEAA8B,YAAY,sCAAQ;AAAA,IACpE;AAGA,UAAM,MAAgB,EAAE,UAAU,KAAK,UAAU,UAAU,KAAK,SAAS;AACzE,UAAM,aAAa,SAAS,KAAK,eAAe,GAAG;AACnD,QAAI,CAAC,YAAY;AACf,YAAM,IAAI;AAAA,QACR,4EAAoC,eAAe,GAAG,CAAC;AAAA,MACzD;AAAA,IACF;AACA,UAAM,SAAS,KAAK,UAAU,WAAW;AACzC,UAAM,UAAU,KAAK,WAAW,WAAW;AAQ3C,UAAM,KAAK,MAAM,KAAK,aAAa;AAAA,MACjC;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA,cAAc,KAAK;AAAA,MACnB,eAAe;AAAA,MACf,OAAO,MAAM,eAAe,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAAA,IACvF,CAAC;AAQD,QAAI,QAAQ,QAAQ,SAAS,GAAG;AAC9B,UAAI;AACF,WAAG,IAAI,WAAW,QAAQ,OAAO;AAAA,MACnC,SAAS,KAAK;AACZ,cAAM,SAAS,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAC9D,cAAM,IAAI;AAAA,UACR,4LAA2C,MAAM;AAAA,QACnD;AAAA,MACF;AAAA,IACF;AACA,OAAG,IAAI,gBAAgB,QAAQ;AAG/B,UAAM,QAAQ,IAAI,OAAO;AAAA,MACvB,WAAW;AAAA,MACX;AAAA,MACA,SAAS,QAAQ;AAAA,MACjB,YAAY,QAAQ;AAAA,IACtB,CAAC;AAED,WAAO,IAAI,QAAQ;AAAA,MACjB,MAAM;AAAA,QACJ,YAAY;AAAA,QACZ,UAAU,WAAW;AAAA,QACrB,UAAU,WAAW;AAAA,QACrB,sBAAsB,WAAW,kBAAkB;AAAA,QACnD,eAAe,KAAK;AAAA,QACpB,OAAO,GAAG;AAAA,QACV,gBAAgB,KAAK,MAAM,aAAa,OAAO,kBAAkB;AAAA,QACjE,aAAa,KAAK,MAAM;AAAA,QACxB,WAAW;AAAA,MACb;AAAA,MACA,KAAK,GAAG;AAAA,MACR,aAAa,GAAG;AAAA,MAChB;AAAA,MACA,WAAW,GAAG;AAAA,MACd,eAAe,GAAG;AAAA,MAClB,YAAY,GAAG;AAAA;AAAA,MAEf,GAAI,WAAW,WAAW,QACtB;AAAA,QACE,gBAAgBD,MAAK;AAAA,UACnB,cAAc,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAAA,UACvE;AAAA,QACF;AAAA,MACF,IACA,CAAC;AAAA,MACL,GAAI,KAAK,MAAM,aAAa,cAAc,SACtC,EAAE,UAAU,KAAK,MAAM,aAAa,UAAU,IAC9C,CAAC;AAAA;AAAA,MAEL,oBAAoB;AAAA,MACpB,oBAAoB;AAAA,QAClB,WAAW,QAAQ;AAAA,QACnB,GAAI,QAAQ,iBAAiB,EAAE,gBAAgB,QAAQ,eAAe,IAAI,CAAC;AAAA,QAC3E,cAAc,QAAQ;AAAA,QACtB,eAAe,QAAQ;AAAA,QACvB,kBAAkB,QAAQ;AAAA,QAC1B,sBAAsB,QAAQ;AAAA,MAChC;AAAA,MACA,gBAAgB,QAAQ;AAAA,IAC1B,CAAC;AAAA,EACH;AAAA;AAAA,EAGA,MAAM,kBAA0C;AAC9C,WAAO;AAAA,MACL,UAAU,KAAK,MAAM,MAAM,KAAK,MAAM,WAAW,KAAK,MAAM,OAAO;AAAA,IACrE;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAc,aAAa,MAgBxB;AACD,UAAM,EAAE,cAAc,YAAY,QAAQ,SAAS,cAAc,eAAe,MAAM,IAAI;AAO1F,UAAM,cAAc;AACpB,UAAM,EAAE,MAAM,WAAW,SAAS,cAAc,IAAI,KAAK;AACzD,UAAM,iBAAiC;AAAA;AAAA;AAAA;AAAA,MAIrC,MAAM,MAAM,EAAE,SAAS,QAAQ,GAAG;AAChC,YAAI,iBAAiB,oBAAoB;AACvC,gBAAM,IAAI;AAAA,YACR,wBAAwB,kBAAkB;AAAA,UAC5C;AAAA,QACF;AACA,YAAI,YAAY,UAAa,YAAY,eAAe;AACtD,cAAI;AACF,0BAAc,YAAY,OAAO;AACjC,kBAAME,IAAG,OAAO,iBAAiB,MAAM,WAAW,OAAO,CAAC;AAAA,UAC5D,QAAQ;AACN,kBAAM,IAAI;AAAA,cACR,0BAA0B,OAAO;AAAA,YACnC;AAAA,UACF;AAAA,QACF;AACA,cAAM,aACJ,YAAY,UAAa,YAAY,gBACjC,MAAM,YAAY,EAAE,MAAM,WAAW,QAAQ,CAAC,IAC9C;AACN,cAAM,eAAe,MAAM,WAAW,cAAc;AAAA,UAClD;AAAA,UACA,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,UAC3C,eAAe,gBAAgB;AAAA,QACjC,CAAC;AAOD,cAAM,MAAqB,aAAa;AACxC,YAAI,WAAW;AACf,eAAO;AAAA,UACL,WAAW;AAAA,UACX,OAAO,IAAI,EAAE,QAAQ,QAAQ,QAAQ,GAAG;AACtC,gBAAI,CAAC,UAAU;AACb,yBAAW;AACX,oBAAM,WAAW,aAAa,aAAa,GAAG;AAAA,YAChD;AAKA,kBAAM,eAAe,UACjB,CAAC,OAAqC,QAAQ,WAAW,IAAI,GAAG,CAAC,IACjE;AACJ,6BAAiB,OAAO,aAAa,IAAI,CAAC,SAAS,MAAM,CAAC,GAAG;AAAA,cAC3D,GAAI,SAAS,EAAE,OAAO,IAAI,CAAC;AAAA,cAC3B,GAAI,eAAe,EAAE,SAAS,aAAa,IAAI,CAAC;AAAA,YAClD,CAAC,GAAG;AACF,oBAAM,WAAW,KAAK,GAAG;AAAA,YAC3B;AAAA,UACF;AAAA,UACA,UAAU;AACR,yBAAa,QAAQ;AAAA,UACvB;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAMA,UAAM,WAAW,gBAAgB;AACjC,UAAM,iBAAiB,gBAAgB,KAAK,KAAK;AAIjD,UAAM,cAAc,WAAW,WAAW;AAC1C,QAAI,cAAc,2BAA2B,eAAe,aAAa,WAAW;AACpF,QAAI,CAAC,UAAU;AACb,oBAAc,YAAY;AAAA,QACxB,CAAC,MAAM,EAAE,SAAS,iBAAiB,EAAE,SAAS;AAAA,MAChD;AAAA,IACF;AACA,UAAM,aAAa,EAAE,GAAG,gBAAgB,YAAY;AAQpD,QAAI;AACJ,QAAI,WAAW,WAAW,OAAO;AAC/B,YAAM,YAAY,KAAK,cAAc;AACrC,YAAM,cAAc,YAAY,SAAS,KAAK,eAAe,SAAS,IAAI;AAC1E,UAAI,eAAe,YAAY,WAAW,OAAO;AAC/C,0BAAkB;AAAA;AAAA,UAEhB,SAAS,YAAY;AAAA,UACrB,WAAW,MACT,IAAI,gBAAgB;AAAA,YAClB,SAAS,YAAY;AAAA,YACrB,GAAI,YAAY,YAAY,SAAY,EAAE,QAAQ,YAAY,QAAQ,IAAI,CAAC;AAAA,YAC3E,GAAI,YAAY,aAAa,SAAY,EAAE,SAAS,YAAY,SAAS,IAAI,CAAC;AAAA,YAC9E,GAAI,YAAY,gBAAgB,SAC5B,EAAE,YAAY,YAAY,YAAY,IACtC,CAAC;AAAA,YACL,OAAO,CAAC;AAAA,YACR,eAAe;AAAA,YACf,WAAW;AAAA,YACX,kBAAkB;AAAA,UACpB,CAAC;AAAA,QACL;AAAA,MACF,OAAO;AACL,0BAAkB,EAAE,SAAS,KAAK;AAAA,MACpC;AAAA,IACF;AAOA,UAAM,cAAc,IAAI,YAAY;AAAA,MAClC;AAAA,MACA;AAAA,MACA,UAAU,EAAE,gBAAgB,GAAI,kBAAkB,EAAE,gBAAgB,IAAI,CAAC,EAAG;AAAA,MAC5E,GAAI,OAAO,KAAK,KAAK,EAAE,SAAS,IAAI,EAAE,MAAM,IAAI,CAAC;AAAA,IACnD,CAAC;AACD,UAAM,QAAQ,MAAM,YAAY,UAAU;AAW1C,UAAM,YAAmC;AAAA,MACvC,SAAS,WAAW;AAAA,MACpB,aAAa,IAAI,oBAAoB;AAAA,MACrC,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,MACzC,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,WAAW,gBAAgB,SAAY,EAAE,YAAY,WAAW,YAAY,IAAI,CAAC;AAAA,MACrF;AAAA,MACA;AAAA,MACA,GAAI,WAAW,mBAAmB,SAC9B,EAAE,eAAe,WAAW,eAAe,IAC3C,CAAC;AAAA,MACL,GAAI,KAAK,MAAM,aAAa,OAAO,eAAe,SAC9C,EAAE,WAAW,KAAK,MAAM,aAAa,MAAM,WAAW,IACtD,CAAC;AAAA,MACL,GAAI,KAAK,MAAM,aAAa,OAAO,mBAAmB,SAClD,EAAE,eAAe,KAAK,MAAM,aAAa,MAAM,eAAe,IAC9D,CAAC;AAAA,MACL,GAAI,KAAK,MAAM,aAAa,OAAO,cAAc,SAC7C,EAAE,kBAAkB,KAAK,MAAM,aAAa,MAAM,UAAU,IAC5D,CAAC;AAAA,IACP;AACA,UAAM,MAAM,IAAI,gBAAgB,SAAS;AACzC,UAAM,YAAY,CAAC,kBAAgD;AACjE,YAAM,OAAO,IAAI,gBAAgB,SAAS;AAE1C,WAAK,gBAAgB;AACrB,aAAO;AAAA,IACT;AAIA,UAAM,gBAAgB,MACpB,IAAI,gBAAgB;AAAA,MAClB,SAAS,WAAW;AAAA,MACpB,GAAI,WAAW,SAAY,EAAE,OAAO,IAAI,CAAC;AAAA,MACzC,GAAI,YAAY,SAAY,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC3C,GAAI,WAAW,gBAAgB,SAAY,EAAE,YAAY,WAAW,YAAY,IAAI,CAAC;AAAA,MACrF,OAAO,CAAC;AAAA,MACR,eAAe;AAAA,MACf,WAAW;AAAA,MACX,kBAAkB;AAAA,IACpB,CAAC;AAGH,UAAM,mBAAmB,KAAK,MAAM,aAAa;AACjD,UAAM,aAAiC;AAAA,MACrC,kBAAkB;AAAA,QAChB,kBAAkB,sBAAsB;AAAA,QACxC,WAAW;AAAA,MACb;AAAA,MACA,iBAAiB,kBAAkB,qBAAqB;AAAA,MACxD,MAAM,kBAAkB,SAAS,YAAY,YAAY;AAAA,MACzD,QAAQ,kBAAkB,UAAU;AAAA,IACtC;AAEA,WAAO,EAAE,aAAa,OAAO,KAAK,WAAW,eAAe,WAAW;AAAA,EACzE;AACF;;;ACtlBO,IAAM,UAAU;","names":["path","path","fs","path","stringifyYaml","loadLibrarySkills","fs","path","stringifyToml","fs","path","stringifyToml","fs","loadLibrarySkills","stringifyYaml","path","sessionEnvironment","fs","path","parseToml","stringifyToml","fs","parseToml","path","stringifyToml","parseSkillFrontmatter","path","tail","path","MAX_SESSIONS","OUTPUT_BUFFER_CAP","toolCall","path","path","readFile","path","readFile","readFile","join","toolCall","path","readFile","tracesDir","join","fs","os","path","randomUUID","randomUUID","os","fs","path","fs","path","path","stat","fs"]}
|