@leviyuan/lodestar 0.11.17 → 0.11.18
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/README.md +3 -3
- package/dist/lodestar.js +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -176,10 +176,10 @@ keep_lodestar_instructions = "true"
|
|
|
176
176
|
| 字段 | 作用 | 默认 |
|
|
177
177
|
| --- | --- | --- |
|
|
178
178
|
| `cwd` | agent 工作目录(绝对路径) | `projects_root/<群名>` |
|
|
179
|
-
| `setting_sources` | `project`
|
|
179
|
+
| `setting_sources` | 显式列表如 `"project"`(只项目级,会丢全局)或 `"user,project"`;不配则**对齐裸 `claude` CLI**,读项目 `CLAUDE.md`/skills/agents/`settings.json` | `user,project,local` |
|
|
180
180
|
| `strict_mcp` | 只挂下方项目 MCP,忽略全局 MCP | 关 |
|
|
181
181
|
| `tools` | 允许的内置工具(逗号分隔);MCP 工具由 `load_project_mcp` 自动可用,不用列在这里 | claude_code 全套 |
|
|
182
|
-
| `load_project_mcp` | 读取 `<cwd>/.mcp.json` 并挂载其 MCP 服务 |
|
|
182
|
+
| `load_project_mcp` | 读取 `<cwd>/.mcp.json` 并挂载其 MCP 服务(对齐裸 `claude` 自动发现;无则无效果) | 开 |
|
|
183
183
|
| `keep_lodestar_instructions` | 保留夜航星卡片/输出约定系统提示 | 开 |
|
|
184
184
|
|
|
185
185
|
`strict_mcp = "true"` 时,项目 `.mcp.json` 是 agent 能挂上 MCP 的唯一通路 —— 全局插件/技能被全部挡掉,agent 干净专注。典型用法:外部自动化引擎在自己的目录里维护规则文件,夜航星负责飞书通道和卡片渲染,两者通过群消息驱动协作。
|
|
@@ -187,7 +187,7 @@ keep_lodestar_instructions = "true"
|
|
|
187
187
|
> ⚠️ **这组配置必须完整,配一半会把对话卡死。** `[projects.*]` 的字段是联动的,任一项开启后,它依赖的链路都要一起配齐:
|
|
188
188
|
>
|
|
189
189
|
> - **`setting_sources = "project"`** 会排除用户级 `~/.claude/settings.json`。如果你的 GLM 路由 / `ANTHROPIC_BASE_URL` 是写在 `~/.claude/settings.json`(而不是 lodestar 的 `[claude.env]`),会被丢掉、请求发不出去 → 卡死。**走 `project` 模式时,GLM 路由必须落在 `config.toml` 的 `[claude.env]`**(`env` 不受 `setting_sources` 影响)。
|
|
190
|
-
> -
|
|
190
|
+
> - 项目 `<cwd>/.mcp.json` 现在**默认读取**(对齐裸 `claude`)。若它存在,其声明的 MCP server 必须能秒级启动并完成 stdio 握手 —— server 卡住(路径错、二进制不存在、stdio 不响应)会让对话卡在真正调用模型之前,表现是卡片底部一直 `Thinking...` 且 model 显示成 `<synthetic>`。要跳过:显式 `load_project_mcp = "false"`。
|
|
191
191
|
> - **排查卡死**:看到 `model=<synthetic>` + 长时间 `Thinking`,先把 `[projects.*]` 整节注释掉重启 daemon;不卡了就是 profile 没配齐,按上面两条逐项检查。
|
|
192
192
|
|
|
193
193
|
---
|
package/dist/lodestar.js
CHANGED
|
@@ -221,7 +221,7 @@ Please migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resour
|
|
|
221
221
|
`)){let J=Q.match(/^(ID|VERSION_ID)=(.*)$/);if(J&&J[1]&&J[2]){let X=J[2].replace(/^"|"$/g,"");if(J[1]==="ID")x.linuxDistroId=X;else x.linuxDistroVersion=X}}}catch{}return x}),hM0=Q1(()=>{if(process.platform!=="darwin")return;let x=ag().match(/^(\d+)\./);if(!x||!x[1])return;return parseInt(x[1],10)-9}),xW0=Q1(function(){switch(xP()){case"macos":return"/Library/Application Support/ClaudeCode";case"windows":return"C:\\Program Files\\ClaudeCode";default:return"/etc/claude-code"}}),mM0=Q1(function(){return eK0(xW0(),"managed-settings.d")});lH=YW0;QW0=ZW0,JW0=QW0(),XW0=JW0;KW0=zW0,VW0=Function.prototype,HW0=Object.prototype,YP=VW0.toString,GW0=HW0.hasOwnProperty,FW0=YP.call(Object);UW0=$W0;dH=NW0;DW0=LW0;BW0=OW0;vW0=ZP;RW0=MW0,Oc=Math.max;QP=SW0;jW0=CW0,TW0=!WJ?aq:function(x,Z){return WJ(x,"toString",{configurable:!0,enumerable:!1,value:jW0(Z),writable:!0})},IW0=TW0,AW0=Date.now;bW0=EW0,kW0=bW0(IW0),JP=kW0;PW0=gW0;uW0=wW0;yW0=fW0,_M0=yW0(function(x,Z,Q,J){vW0(x,Z,Q,J)});mW0=hW0;nW0=_W0;dW0=lW0,Bc=G6?G6.isConcatSpreadable:void 0;pW0=iW0;sW0=XP;rW0=oW0;eW0=tW0,nM0=eW0(function(x,Z){return x==null?{}:dW0(x,Z)}),aW0=K0(()=>R.object({allowedDomains:R.array(R.string()).optional(),deniedDomains:R.array(R.string()).optional().describe("Domains that are always blocked, even if matched by allowedDomains. Supports the same wildcard syntax as allowedDomains. Merged from all settings sources regardless of allowManagedDomainsOnly."),allowManagedDomainsOnly:R.boolean().optional().describe("When true (and set in managed settings), only allowedDomains and WebFetch(domain:...) allow rules from managed settings are respected. User, project, local, and flag settings domains are ignored. Denied domains are still respected from all sources."),allowUnixSockets:R.array(R.string()).optional().describe("macOS only: Unix socket paths to allow. Ignored on Linux (seccomp cannot filter by path)."),allowAllUnixSockets:R.boolean().optional().describe("If true, allow all Unix sockets (disables blocking on both platforms)."),allowLocalBinding:R.boolean().optional(),allowMachLookup:R.array(R.string().refine((x)=>!(x.endsWith("*")?x.slice(0,-1):x).includes("*"),{message:'Wildcards are only allowed as a single trailing "*" (e.g., "com.example.*" or "*" for all services).'})).optional().describe('macOS only: Additional XPC/Mach service names to allow looking up. Supports trailing-wildcard prefix matching (e.g., "com.apple.coresimulator.*"). Needed for tools that communicate via XPC such as the iOS Simulator or Playwright.'),httpProxyPort:R.number().optional(),socksProxyPort:R.number().optional(),tlsTerminate:R.object({caCertPath:R.string().min(1).optional(),caKeyPath:R.string().min(1).optional()}).optional().describe("[EXPERIMENTAL] Enable in-process TLS termination so the per-request filter can see HTTPS request bodies. Provide a CA cert+key, or omit both to have sandbox-runtime generate an ephemeral one for the session.")}).optional()),xV0=K0(()=>R.object({allowWrite:R.array(R.string()).optional().describe("Additional paths to allow writing within the sandbox. Merged with paths from Edit(...) allow permission rules."),denyWrite:R.array(R.string()).optional().describe("Additional paths to deny writing within the sandbox. Merged with paths from Edit(...) deny permission rules."),denyRead:R.array(R.string()).optional().describe("Additional paths to deny reading within the sandbox. Merged with paths from Read(...) deny permission rules."),allowRead:R.array(R.string()).optional().describe("Paths to re-allow reading within denyRead regions. Takes precedence over denyRead for matching paths."),allowManagedReadPathsOnly:R.boolean().optional().describe("When true (set in managed settings), only allowRead paths from policySettings are used.")}).optional()),YV0=K0(()=>R.object({enabled:R.boolean().optional(),failIfUnavailable:R.boolean().optional().describe("Exit with an error at startup if sandbox.enabled is true but the sandbox cannot start (missing dependencies or unsupported platform). When false (default), a warning is shown and commands run unsandboxed. Intended for managed-settings deployments that require sandboxing as a hard gate."),autoAllowBashIfSandboxed:R.boolean().optional(),allowUnsandboxedCommands:R.boolean().optional().describe("Allow commands to run outside the sandbox via the dangerouslyDisableSandbox parameter. When false, the dangerouslyDisableSandbox parameter is completely ignored and all commands must run sandboxed. Default: true."),network:aW0(),filesystem:xV0(),ignoreViolations:R.record(R.string(),R.array(R.string())).optional(),enableWeakerNestedSandbox:R.boolean().optional(),enableWeakerNetworkIsolation:R.boolean().optional().describe("macOS only: Allow access to com.apple.trustd.agent in the sandbox. Needed for Go-based CLI tools (gh, gcloud, terraform, etc.) to verify TLS certificates when using httpProxyPort with a MITM proxy and custom CA. "+"**Reduces security** — opens a potential data exfiltration vector through the trustd service. Default: false"),allowAppleEvents:R.boolean().optional().describe("macOS only: Allow sandboxed commands to send Apple Events (and look up the appleeventsd Mach service). Needed for `open`, `osascript`, and browser-based auth flows that open URLs. "+"**Removes code-execution isolation** — sandboxed commands can launch other applications "+"unsandboxed with no user prompt, and can script running apps (e.g. Terminal) subject to the user's per-app TCC automation consent. "+"Only honored from user, managed/policy, or CLI (--settings) settings — "+"project settings (.claude/settings.json and .claude/settings.local.json) are ignored. Default: false"),excludedCommands:R.array(R.string()).optional(),ripgrep:R.object({command:R.string(),args:R.array(R.string()).optional()}).optional().describe("Custom ripgrep configuration for bundled ripgrep support"),bwrapPath:R.preprocess((x)=>typeof x==="string"&&vc(x)?x:void 0,R.string()).optional().catch(void 0).describe("Linux/WSL only: Absolute path to the bwrap (bubblewrap) binary. Overrides auto-detection via PATH. Only honored from admin-controlled managed settings."),socatPath:R.preprocess((x)=>typeof x==="string"&&vc(x)?x:void 0,R.string()).optional().catch(void 0).describe("Linux/WSL only: Absolute path to the socat binary used for the sandbox network proxy. Overrides auto-detection via PATH. Only honored from admin-controlled managed settings.")}).passthrough()),ZV0=["auto","iterm2","iterm2_with_bell","terminal_bell","kitty","ghostty","notifications_disabled"],QV0=["normal","vim"],JV0=["auto","tmux","in-process"],XV0=["dark","light","light-daltonized","dark-daltonized","light-ansi","dark-ansi"],zV0=["auto",...XV0],dM0=xP()==="macos"?"⏺":"●",e$=["acceptEdits","auto","bypassPermissions","default","dontAsk","plan"],KV0=[...e$,"bubble"],WV0=KV0,iM0=K0(()=>Rg.enum(WV0)),pM0=K0(()=>Rg.enum(e$)),VV0=["bash","powershell"],Z2=K0(()=>R.string().optional().describe('Permission rule syntax to filter when this hook runs (e.g., "Bash(git *)"). Only runs if the tool call matches the pattern. Avoids spawning hooks for non-matching commands.'));GV0=K0(()=>{let{BashCommandHookSchema:x,PromptHookSchema:Z,AgentHookSchema:Q,HttpHookSchema:J,McpToolHookSchema:X}=HV0();return R.discriminatedUnion("type",[x,Z,Q,J,X])}),FV0=K0(()=>R.object({matcher:R.string().optional().describe('String pattern to match (e.g. tool names like "Write")'),hooks:R.array(GV0()).describe("List of hooks to execute when the matcher matches")})),EJ=K0(()=>R.partialRecord(R.enum(Yq),R.array(FV0()))),sM0=K0(()=>R.enum(["local","user","project","dynamic","enterprise","claudeai","managed","agent"])),oM0=K0(()=>R.enum(["stdio","sse","sse-ide","http","ws","sdk"])),K5=K0(()=>R.literal("comms").optional().catch(void 0)),N6=K0(()=>R.number().int().positive()),$V0=K0(()=>R.object({type:R.literal("stdio").optional(),command:R.string().min(1,"Command cannot be empty"),args:R.array(R.string()).default([]),env:R.record(R.string(),R.string()).optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),role:K5()})),UV0=K0(()=>R.boolean()),zP=K0(()=>R.object({clientId:R.string().optional(),callbackPort:R.number().int().positive().optional(),authServerMetadataUrl:R.string().url().startsWith("https://",{message:"authServerMetadataUrl must use https://"}).optional(),scopes:R.string().min(1).optional(),xaa:UV0().optional()})),NV0=K0(()=>R.object({type:R.literal("sse"),url:R.string(),headers:R.record(R.string(),R.string()).optional(),headersHelper:R.string().optional(),oauth:zP().optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),role:K5(),toolPermissions:R.record(R.string(),a$()).optional()})),LV0=K0(()=>R.object({type:R.literal("sse-ide"),url:R.string(),ideName:R.string(),ideRunningInWindows:R.boolean().optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),role:K5()})),DV0=K0(()=>R.object({type:R.literal("ws-ide"),url:R.string(),ideName:R.string(),authToken:R.string().optional(),ideRunningInWindows:R.boolean().optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),role:K5()})),OV0=K0(()=>R.object({type:R.enum(["http","streamable-http"]).transform(()=>"http"),url:R.string(),headers:R.record(R.string(),R.string()).optional(),headersHelper:R.string().optional(),oauth:zP().optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),role:K5(),toolPermissions:R.record(R.string(),a$()).optional()})),BV0=K0(()=>R.object({type:R.literal("ws"),url:R.string(),headers:R.record(R.string(),R.string()).optional(),headersHelper:R.string().optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),role:K5()})),vV0=K0(()=>R.object({type:R.literal("sdk"),name:R.string(),timeout:N6().optional(),alwaysLoad:R.boolean().optional()})),a$=K0(()=>R.enum(["allow","ask","blocked"])),MV0=K0(()=>R.object({type:R.literal("claudeai-proxy"),url:R.string(),id:R.string(),displayName:R.string().optional(),iconUrl:R.string().optional(),timeout:N6().optional(),alwaysLoad:R.boolean().optional(),toolPermissions:R.record(R.string(),a$()).optional(),stateless:R.boolean().optional(),cachedInitResponse:R.record(R.string(),R.unknown()).nullish()})),iH=K0(()=>R.union([$V0(),NV0(),LV0(),DV0(),OV0(),BV0(),vV0(),MV0()])),rM0=K0(()=>R.object({mcpServers:R.record(R.string(),iH())})),RV0=new Set(["claude-community","claude-plugins-community"]),SV0=new Set(["claude-code-marketplace","claude-code-plugins","claude-plugins-official","anthropic-marketplace","anthropic-plugins","agent-skills","anthropic-agent-skills","life-sciences","knowledge-work-plugins","claude-for-legal","claude-for-financial-services","financial-services-plugins"]),KP=new Set([...SV0,...RV0]),CV0=/(?:official[^a-z0-9]*(anthropic|claude)|(?:anthropic|claude)[^a-z0-9]*official|^(?:anthropic|claude)[^a-z0-9]*(marketplace|plugins|official))/i,jV0=/[^\u0020-\u007E]/;c8=K0(()=>R.string().startsWith("./")),U3=K0(()=>c8().endsWith(".json")),Mc=K0(()=>R.union([c8().refine((x)=>x.endsWith(".mcpb")||x.endsWith(".dxt"),{message:"MCPB file path must end with .mcpb or .dxt"}).describe("Path to MCPB file relative to plugin root"),R.string().url().refine((x)=>x.endsWith(".mcpb")||x.endsWith(".dxt"),{message:"MCPB URL must end with .mcpb or .dxt"}).describe("URL to MCPB file")])),pH=K0(()=>c8().endsWith(".md")),sH=K0(()=>R.union([pH(),c8()])),WP=K0(()=>R.string().min(1,"Marketplace must have a name").refine((x)=>!x.includes(" "),{message:'Marketplace name cannot contain spaces. Use kebab-case (e.g., "my-marketplace")'}).refine((x)=>!x.includes("/")&&!x.includes("\\")&&!x.includes("..")&&x!==".",{message:'Marketplace name cannot contain path separators (/ or \\), ".." sequences, or be "."'}).refine((x)=>!TV0(x),{message:"Marketplace name impersonates an official Anthropic/Claude marketplace"}).refine((x)=>x.toLowerCase()!=="inline",{message:'Marketplace name "inline" is reserved for --plugin-dir session plugins'}).refine((x)=>x.toLowerCase()!=="builtin",{message:'Marketplace name "builtin" is reserved for built-in plugins'}).refine((x)=>x.toLowerCase()!=="skills-dir",{message:'Marketplace name "skills-dir" is reserved for plugins auto-loaded from .claude/skills/'})),xU=K0(()=>R.object({name:R.string().min(1,"Author name cannot be empty").describe("Display name of the plugin author or organization"),email:R.string().optional().describe("Contact email for support or feedback"),url:R.string().optional().describe("Website, GitHub profile, or organization URL")})),IV0=K0(()=>R.object({$schema:R.string().optional().describe("JSON Schema reference for editor autocomplete/validation; ignored at load time"),name:R.string().min(1,"Plugin name cannot be empty").refine((x)=>!x.includes(" "),{message:'Plugin name cannot contain spaces. Use kebab-case (e.g., "my-plugin")'}).describe("Unique identifier for the plugin, used for namespacing (prefer kebab-case)"),displayName:R.string().optional().describe('Human-readable name shown in UI (e.g., "GitHub Utils"). Falls back to `name` when omitted. Unlike `name`, may contain spaces and any casing; not used for namespacing or lookup.'),version:R.string().optional().describe("Semantic version (e.g., 1.2.3) following semver.org specification"),description:R.string().optional().describe("Brief, user-facing explanation of what the plugin provides"),author:xU().optional().describe("Information about the plugin creator or maintainer"),homepage:R.string().url().optional().describe("Plugin homepage or documentation URL"),repository:R.string().optional().describe("Source code repository URL"),license:R.string().optional().describe("SPDX license identifier (e.g., MIT, Apache-2.0)"),keywords:R.array(R.string()).optional().describe("Tags for plugin discovery and categorization"),defaultEnabled:R.boolean().optional().describe("Whether the plugin starts enabled when the user has no explicit enabled/disabled setting for it (default: true). Explicit enabledPlugins values always win, and a plugin required by an enabled dependent is enabled regardless of this value."),dependencies:R.array(tV0()).optional().describe(`Plugins that must be enabled for this plugin to function. Bare names (no "@marketplace") are resolved against the declaring plugin's own marketplace.`)})),tM0=K0(()=>R.object({description:R.string().optional().describe("Brief, user-facing explanation of what these hooks provide"),hooks:R.lazy(()=>EJ()).describe("The hooks provided by the plugin, in the same format as the one used for settings")})),cV0=K0(()=>R.object({hooks:R.union([U3().describe("Path to file with additional hooks (in addition to those in hooks/hooks.json, if it exists), relative to the plugin root"),R.lazy(()=>EJ()).describe("Additional hooks (in addition to those in hooks/hooks.json, if it exists)"),R.array(R.union([U3().describe("Path to file with additional hooks (in addition to those in hooks/hooks.json, if it exists), relative to the plugin root"),R.lazy(()=>EJ()).describe("Additional hooks (in addition to those in hooks/hooks.json, if it exists)")]))])})),qV0=K0(()=>R.object({source:sH().optional().describe("Path to command markdown file, relative to plugin root"),content:R.string().optional().describe("Inline markdown content for the command"),description:R.string().optional().describe("Command description override"),argumentHint:R.string().optional().describe('Hint for command arguments (e.g., "[file]")'),model:R.string().optional().describe("Default model for this command"),allowedTools:R.array(R.string()).optional().describe("Tools allowed when command runs")}).refine((x)=>x.source&&!x.content||!x.source&&x.content,{message:'Command must have either "source" (file path) or "content" (inline markdown), but not both'})),AV0=K0(()=>R.object({commands:R.union([sH().describe("Path to a command file or skill directory, relative to the plugin root. When set, the commands/ directory is not auto-loaded — list its files here if you want both."),R.array(sH().describe("Path to a command file or skill directory, relative to the plugin root. When set, the commands/ directory is not auto-loaded — list its files here if you want both.")).describe("List of command file or skill directory paths. When set, the commands/ directory is not auto-loaded."),R.record(R.string(),qV0()).describe('Object mapping of command names to their metadata and source files. Command name becomes the slash command name (e.g., "about" → "/plugin:about")')])})),EV0=K0(()=>R.object({agents:R.union([pH().describe("Path to an agent file, relative to the plugin root. When set, the agents/ directory is not auto-loaded — list its files here if you want both."),R.array(pH().describe("Path to an agent file, relative to the plugin root. When set, the agents/ directory is not auto-loaded — list its files here if you want both.")).describe("List of agent file paths. When set, the agents/ directory is not auto-loaded.")])})),bV0=K0(()=>R.object({skills:R.union([c8().describe("Path to a skill directory, relative to the plugin root. Loaded in addition to the skills/ directory (except: for a marketplace entry whose source resolves to the marketplace root, declaring a specific subdirectory replaces the skills/ scan)."),R.array(c8().describe("Path to a skill directory, relative to the plugin root.")).describe("List of skill directory paths, loaded in addition to the skills/ directory (except: for a marketplace entry whose source resolves to the marketplace root, declaring specific subdirectories replaces the skills/ scan).")])})),VP=K0(()=>R.object({outputStyles:R.union([c8().describe("Path to an output-styles directory or file, relative to the plugin root. When set, the output-styles/ directory is not auto-loaded — list its files here if you want both."),R.array(c8().describe("Path to an output-styles directory or file, relative to the plugin root. When set, the output-styles/ directory is not auto-loaded — list its files here if you want both.")).describe("List of output-style directory or file paths. When set, the output-styles/ directory is not auto-loaded.")])})),HP=K0(()=>R.object({themes:R.union([c8().describe("Path to a themes directory or file, relative to the plugin root. When set, the themes/ directory is not auto-loaded — list its files here if you want both."),R.array(c8().describe("Path to a themes directory or file, relative to the plugin root. When set, the themes/ directory is not auto-loaded — list its files here if you want both.")).describe("List of theme directory or file paths. When set, the themes/ directory is not auto-loaded.")])})),kV0=K0(()=>R.object({})),Rc=K0(()=>R.string().min(1)),gV0=K0(()=>R.string().min(2).refine((x)=>x.startsWith("."),{message:'File extensions must start with dot (e.g., ".ts", not "ts")'})),PV0=K0(()=>R.object({mcpServers:R.union([U3().describe("MCP servers to include in the plugin (in addition to those in the .mcp.json file, if it exists)"),Mc().describe("Path or URL to MCPB file containing MCP server configuration"),R.record(R.string(),iH()).describe("MCP server configurations keyed by server name"),R.array(R.union([U3().describe("Path to MCP servers configuration file"),Mc().describe("Path or URL to MCPB file"),R.record(R.string(),iH()).describe("Inline MCP server configurations")])).describe("Array of MCP server configurations (paths, MCPB files, or inline definitions)")])})),GP=K0(()=>R.object({type:R.enum(["string","number","boolean","directory","file"]).describe("Type of the configuration value"),title:R.string().describe("Human-readable label shown in the config dialog"),description:R.string().describe("Help text shown beneath the field in the config dialog"),required:R.boolean().optional().describe("If true, validation fails when this field is empty"),default:R.union([R.string(),R.number(),R.boolean(),R.array(R.string())]).optional().describe("Default value used when the user provides nothing"),multiple:R.boolean().optional().describe("For string type: allow an array of strings"),sensitive:R.boolean().optional().describe("If true, masks dialog input and stores value in secure storage (keychain/credentials file) instead of settings.json"),min:R.number().optional().describe("Minimum value (number type only)"),max:R.number().optional().describe("Maximum value (number type only)")}).strict()),wV0=K0(()=>R.object({userConfig:R.record(R.string().regex(/^[A-Za-z_]\w*$/,"Option keys must be valid identifiers (letters, digits, underscore; no leading digit) — they become CLAUDE_PLUGIN_OPTION_<KEY> env vars in hooks"),GP()).optional().describe("User-configurable values this plugin needs. Prompted at enable time. Non-sensitive values saved to settings.json; sensitive values to secure storage. Available as ${user_config.KEY} in MCP/LSP server config, hook commands, and (non-sensitive only) skill/agent content. Keep sensitive value counts small.")})),uV0=K0(()=>R.object({channels:R.array(R.object({server:R.string().min(1).describe("Name of the MCP server this channel binds to. Must match a key in this plugin's mcpServers."),displayName:R.string().optional().describe('Human-readable name shown in the config dialog title (e.g., "Telegram"). Defaults to the server name.'),userConfig:R.record(R.string(),GP()).optional().describe("Fields to prompt the user for when enabling this plugin in assistant mode. Saved values are substituted into ${user_config.KEY} references in the mcpServers env.")}).strict()).describe("Channels this plugin provides. Each entry declares an MCP server as a message channel and optionally specifies user configuration to prompt for at enable time.")})),Sc=K0(()=>R.strictObject({command:R.string().min(1).refine((x)=>{if(x.includes(" ")&&!x.startsWith("/"))return!1;return!0},{message:"Command should not contain spaces. Use args array for arguments."}).describe('Command to execute the LSP server (e.g., "typescript-language-server")'),args:R.array(Rc()).optional().describe("Command-line arguments to pass to the server"),extensionToLanguage:R.record(gV0(),Rc()).refine((x)=>Object.keys(x).length>0,{message:"extensionToLanguage must have at least one mapping"}).describe("Mapping from file extension to LSP language ID. File extensions and languages are derived from this mapping."),transport:R.enum(["stdio","socket"]).default("stdio").describe("Communication transport mechanism"),env:R.record(R.string(),R.string()).optional().describe("Environment variables to set when starting the server"),initializationOptions:R.unknown().optional().describe("Initialization options passed to the server during initialization"),settings:R.unknown().optional().describe("Settings passed to the server via workspace/didChangeConfiguration"),workspaceFolder:R.string().optional().describe("Workspace folder path to use for the server"),startupTimeout:R.number().int().positive().optional().describe("Maximum time to wait for server startup (milliseconds)"),shutdownTimeout:R.number().int().positive().optional().describe("Maximum time to wait for graceful shutdown (milliseconds)"),restartOnCrash:R.boolean().optional().describe("Whether to restart the server if it crashes"),maxRestarts:R.number().int().nonnegative().optional().describe("Maximum number of restart attempts before giving up"),diagnostics:R.boolean().optional().describe("Whether to push publishDiagnostics into the agent context after edits. Set to false to keep LSP navigation (goToDefinition, hover, etc.) but suppress automatic diagnostic injection. Defaults to true.")})),fV0=K0(()=>R.strictObject({name:R.string().min(1).describe("Identifier for this monitor, unique within the plugin. Used to dedupe so re-arming (plugin reload, repeat skill invoke) does not spawn duplicates."),command:R.string().min(1).describe('Shell command to run as a persistent background monitor. Each stdout line is delivered to the model as a <task_notification> event; the process runs for the session lifetime. ${CLAUDE_PLUGIN_ROOT}, ${CLAUDE_PLUGIN_DATA}, ${CLAUDE_PROJECT_DIR}, ${user_config.*}, and ${ENV_VAR} are substituted. Runs in the session cwd — prefix with `cd "${CLAUDE_PLUGIN_ROOT}" && ` if the script needs its own directory.'),description:R.string().min(1).describe("Short human-readable description of what is being monitored (shown in task panel and notification summary)."),when:R.union([R.literal("always"),R.string().startsWith("on-skill-invoke:").refine((x)=>x.length>16,{message:"on-skill-invoke: must specify a skill name"})]).default("always").describe('Arm trigger. "always" arms at session start and on plugin reload. "on-skill-invoke:<skill>" arms the first time that skill is dispatched (via Skill tool or slash command).')})),yV0=K0(()=>R.array(fV0()).refine((x)=>new Set(x.map((Z)=>Z.name)).size===x.length,{message:"Monitor names must be unique within a plugin"})),FP=K0(()=>R.object({monitors:R.union([U3().describe("Path to a JSON file containing the monitors array, relative to the plugin root"),yV0()]).describe("Background watch scripts the host arms as persistent Monitor tasks (unsandboxed, same trust tier as hooks) so plugins need not instruct the model to arm them. When omitted, monitors/monitors.json at the plugin root is loaded if present.")})),hV0=K0(()=>R.object({lspServers:R.union([U3().describe("Path to .lsp.json configuration file relative to plugin root"),R.record(R.string(),Sc()).describe("LSP server configurations keyed by server name"),R.array(R.union([U3().describe("Path to LSP configuration file"),R.record(R.string(),Sc()).describe("Inline LSP server configurations")])).describe("Array of LSP server configurations (paths or inline definitions)")])})),$P=K0(()=>R.string().refine((x)=>!x.includes("..")&&!x.includes("//"),"Package name cannot contain path traversal patterns").refine((x)=>{let Z=/^@[a-z0-9][a-z0-9-._]*\/[a-z0-9][a-z0-9-._]*$/,Q=/^[a-z0-9][a-z0-9-._]*$/;return Z.test(x)||Q.test(x)},"Invalid npm package name format")),mV0=K0(()=>R.object({settings:R.record(R.string(),R.unknown()).optional().describe("Settings to merge into the user settings while this plugin is enabled. Only the documented allowlisted keys are applied.")})),_V0=K0(()=>R.object({experimental:R.preprocess((x)=>typeof x==="object"&&x!==null&&!Array.isArray(x)?x:void 0,R.object({...HP().partial().shape,...FP().partial().shape,...VP().partial().shape,evals:R.union([R.string(),R.array(R.string())]).optional().describe("Path(s) to evaluation query files for `claude plugin eval`. Defaults to `evals/`.")}).optional().describe("Components whose manifest shape may change without a deprecation cycle. Move a key out of here once it is promoted to stable."))})),nV0=K0(()=>R.object({...IV0().shape,...cV0().partial().shape,...AV0().partial().shape,...EV0().partial().shape,...bV0().partial().shape,...VP().partial().shape,...HP().partial().shape,...kV0().shape,...uV0().partial().shape,...PV0().partial().shape,...hV0().partial().shape,...FP().partial().shape,...mV0().partial().shape,...wV0().partial().shape,..._V0().partial().shape})),bJ=K0(()=>R.discriminatedUnion("source",[R.object({source:R.literal("url"),url:R.string().url().describe("Direct URL to marketplace.json file"),headers:R.record(R.string(),R.string()).optional().describe("Custom HTTP headers (e.g., for authentication)")}),R.object({source:R.literal("github"),repo:R.string().describe("GitHub repository in owner/repo format"),ref:R.string().optional().describe('Git branch or tag to use (e.g., "main", "v1.0.0"). Defaults to repository default branch.'),path:R.string().optional().describe("Path to marketplace.json within repo (defaults to .claude-plugin/marketplace.json)"),sparsePaths:R.array(R.string()).optional().describe('Directories to include via git sparse-checkout (cone mode). Use for monorepos where the marketplace lives in a subdirectory. Example: [".claude-plugin", "plugins"]. If omitted, the full repository is cloned.'),skipLfs:R.boolean().optional().describe("Skip Git LFS smudge during clone and update (sets GIT_LFS_SKIP_SMUDGE=1) so LFS pointer files stay as pointers instead of downloading their content. Use for marketplaces hosted in repos with large LFS objects.")}),R.object({source:R.literal("git"),url:R.string().describe("Full git repository URL"),ref:R.string().optional().describe('Git branch or tag to use (e.g., "main", "v1.0.0"). Defaults to repository default branch.'),path:R.string().optional().describe("Path to marketplace.json within repo (defaults to .claude-plugin/marketplace.json)"),sparsePaths:R.array(R.string()).optional().describe('Directories to include via git sparse-checkout (cone mode). Use for monorepos where the marketplace lives in a subdirectory. Example: [".claude-plugin", "plugins"]. If omitted, the full repository is cloned.'),skipLfs:R.boolean().optional().describe("Skip Git LFS smudge during clone and update (sets GIT_LFS_SKIP_SMUDGE=1) so LFS pointer files stay as pointers instead of downloading their content. Use for marketplaces hosted in repos with large LFS objects.")}),R.object({source:R.literal("npm"),package:$P().describe("NPM package containing marketplace.json")}),R.object({source:R.literal("file"),path:R.string().describe("Local file path to marketplace.json")}),R.object({source:R.literal("directory"),path:R.string().describe("Local directory containing .claude-plugin/marketplace.json")}),R.object({source:R.literal("skills-dir")}).describe("Policy-list sentinel for the ~/.claude/skills/ auto-load (@skills-dir plugins). In strictKnownMarketplaces: opt the scan back IN (by default any allowlist blocks it). In blockedMarketplaces: turn the scan OFF without otherwise restricting marketplaces. Only meaningful in those two managed-settings lists (areLocalPluginDirsAllowedByPolicy); known_marketplaces.json / marketplace add etc. ignore it."),R.object({source:R.literal("hostPattern"),hostPattern:R.string().describe('Regex pattern to match the host/domain extracted from any marketplace source type. For github sources, matches against "github.com". For git sources (SSH or HTTPS), extracts the hostname from the URL. Use in strictKnownMarketplaces to allow all marketplaces from a specific host (e.g., "^github\\.mycompany\\.com$").')}),R.object({source:R.literal("pathPattern"),pathPattern:R.string().describe('Regex pattern matched against the .path field of file and directory sources. Use in strictKnownMarketplaces to allow filesystem-based marketplaces alongside hostPattern restrictions for network sources. Use ".*" to allow all filesystem paths, or a narrower pattern (e.g., "^/opt/approved/") to restrict to specific directories.')}),R.object({source:R.literal("settings"),name:WP().refine((x)=>!KP.has(x.toLowerCase()),{message:"Reserved marketplace names cannot be used with settings sources. validateOfficialNameSource only accepts github/git sources from anthropics/* for these names; a settings source would be rejected after loadAndCacheMarketplace has already written to disk with cleanupNeeded=false."}).describe("Marketplace name. Must match the extraKnownMarketplaces key (enforced); the synthetic manifest is written under this name. Same validation "+"as PluginMarketplaceSchema plus reserved-name rejection — "+"validateOfficialNameSource runs after the disk write, too late to clean up."),plugins:R.array(lV0()).describe("Plugin entries declared inline in settings.json"),owner:xU().optional()}).describe("Inline marketplace manifest defined directly in settings.json. The reconciler writes a synthetic marketplace.json to the cache; diffMarketplaces detects edits via isEqual on the stored source (the plugins array is inside this object, so edits surface as sourceChanged).")])),OH=K0(()=>R.string().length(40).regex(/^[a-f0-9]{40}$/,"Must be a full 40-character lowercase git commit SHA")),UP=K0(()=>R.union([R.preprocess((x)=>x==="."?"./":x,c8()).describe("Path to the plugin root, relative to the marketplace root (the directory containing .claude-plugin/, not .claude-plugin/ itself)"),R.object({source:R.literal("npm"),package:$P().or(R.string().refine((x)=>/^(?:file|https?|git(?:\+https?|\+ssh)?|ssh|github|gitlab|bitbucket):/i.test(x)||!x.includes(".."),'Package reference cannot contain ".." path segments')).describe("Package name (or url, or local path, or anything else that can be passed to `npm` as a package)"),version:R.string().optional().describe("Specific version or version range (e.g., ^1.0.0, ~2.1.0)"),registry:R.string().url().optional().describe("Custom NPM registry URL (defaults to using system default, likely npmjs.org)")}).describe("NPM package as plugin source"),R.object({source:R.literal("url"),url:R.string().describe("Full git repository URL (https:// or git@)"),ref:R.string().optional().describe('Git branch or tag to use (e.g., "main", "v1.0.0"). Defaults to repository default branch.'),sha:OH().optional().describe("Specific commit SHA to use")}),R.object({source:R.literal("github"),repo:R.string().describe("GitHub repository in owner/repo format"),ref:R.string().optional().describe('Git branch or tag to use (e.g., "main", "v1.0.0"). Defaults to repository default branch.'),sha:OH().optional().describe("Specific commit SHA to use")}),R.object({source:R.literal("git-subdir"),url:R.string().describe("Git repository: GitHub owner/repo shorthand, https://, or git@ URL"),path:R.string().min(1).describe('Subdirectory within the repo containing the plugin (e.g., "tools/claude-plugin"). Cloned sparsely using partial clone (--filter=tree:0) to minimize bandwidth for monorepos.'),ref:R.string().optional().describe('Git branch or tag to use (e.g., "main", "v1.0.0"). Defaults to repository default branch.'),sha:OH().optional().describe("Specific commit SHA to use")}).describe("Plugin located in a subdirectory of a larger repository (monorepo). Only the specified subdirectory is materialized; the rest of the repo is not downloaded."),R.object({source:R.literal("unsupported")}).describe("Placeholder for source types this Claude Code version does not "+"recognize. Never authored by hand — PluginMarketplaceSchema rewrites "+'unparseable sources to this so the entry remains in marketplace.plugins (detectDelistedPlugins must not see it as removed). Install attempts fail at cachePlugin with a clear "update Claude Code" message.')])),lV0=K0(()=>R.object({name:R.string().min(1,"Plugin name cannot be empty").refine((x)=>!x.includes(" "),{message:'Plugin name cannot contain spaces. Use kebab-case (e.g., "my-plugin")'}).describe("Plugin name as it appears in the target repository"),source:UP().describe("Where to fetch the plugin from. Must be a remote source — relative "+"paths have no marketplace repository to resolve against."),description:R.string().optional(),version:R.string().optional(),strict:R.boolean().optional()}).refine((x)=>typeof x.source!=="string",{message:'Plugins in a settings-sourced marketplace must use remote sources (github, git-subdir, npm, url). Relative-path sources like "./foo" have no marketplace repository to resolve against.'}).refine((x)=>typeof x.source==="string"||x.source.source!=="unsupported",{message:"source.source: 'unsupported' is a parse-time placeholder and cannot be authored. Use a remote source (github, git-subdir, npm, url)."})),dV0=K0(()=>R.object({cli:R.array(R.string().max(64)).max(10).optional().describe('First command tokens (e.g. ["stripe"]) — exact match against commands run this session.'),hosts:R.array(R.string().max(128)).max(20).optional().describe('Hostnames (e.g. ["api.stripe.com"]) — exact, case-insensitive match against '+"hostnames seen in https?:// URLs in bash commands run this session. Bare hostname only: lowercase, no scheme, no port, no path."),filesRead:R.array(R.string().max(256)).max(10).optional().describe('Glob patterns (e.g. ["**/*.tf"]) — the plugin is relevant when a file Claude has read '+"this session matches any pattern. Matched against read-file paths, forward-slash normalized, case-insensitive."),manifestDeps:R.array(R.object({file:R.string().max(256),pattern:R.string().max(256)})).max(10).optional().describe("Dependency declared in a package manifest. Each {file, pattern} is a pair of RegExp sources: "+"`file` matches the manifest filename (package.json, go.mod, requirements.txt, …); "+"`pattern` matches the dependency declaration inside that file. Evaluated against files read this session."),cwd:R.array(R.string().max(256)).max(10).optional().describe('Glob patterns (e.g. ["Engine/Source/Runtime/Renderer/**"]) — the plugin is relevant when the '+`session's working directory is at or under a directory matching the pattern. Matched against the cwd both relative to the enclosing git repo root and as an absolute path, forward-slash normalized, case-insensitive. A bare directory (no glob characters) means "cwd is at or under this directory". Known at session start, so this signal can surface a suggestion before the first turn.`)})),iV0=K0(()=>R.object({topic:R.string().max(64).optional().describe('What the user is working with when this plugin is relevant — fills "Working with {topic}?". '+'Often the product name (e.g. "Stripe"); use a domain (e.g. "design") when the plugin name does not read naturally as a topic. Defaults to the plugin name with each hyphen-segment capitalized.'),signals:dV0().optional().describe("Matchers that determine when the plugin is relevant.")})),pV0=K0(()=>nV0().partial().extend({name:R.string().min(1,"Plugin name cannot be empty").refine((x)=>!x.includes(" "),{message:'Plugin name cannot contain spaces. Use kebab-case (e.g., "my-plugin")'}).describe("Unique identifier matching the plugin name"),source:UP().describe("Where to fetch the plugin from"),category:R.string().optional().describe('Category for organizing plugins (e.g., "productivity", "development")'),tags:R.array(R.string()).optional().describe("Tags for searchability and discovery"),strict:R.boolean().optional().default(!0).describe("Require the plugin manifest to be present in the plugin folder. If false, the marketplace entry provides the manifest."),relevance:R.preprocess((x)=>typeof x==="object"&&x!==null&&!Array.isArray(x)?x:void 0,iV0().optional()).describe(`Declares when this plugin is relevant to the user's work. Consumed by the spinner tip ("Working with {topic}?"), session-start auto-suggest, and marketplace browse ranking.`)})),sV0=K0(()=>R.object({name:R.string().min(1).refine((x)=>!x.includes(" "))}));eM0=K0(()=>R.object({$schema:R.string().optional().describe("JSON Schema reference for editor autocomplete/validation; ignored at load time"),name:WP(),version:R.string().optional().describe("Marketplace manifest version"),description:R.string().optional().describe("Human-readable description of this marketplace"),owner:xU().describe("Marketplace maintainer or curator information"),plugins:R.array(R.unknown()).transform(oV0).describe("Collection of available plugins in this marketplace"),forceRemoveDeletedPlugins:R.boolean().optional().describe("When true, plugins removed from this marketplace will be automatically uninstalled and flagged for users"),metadata:R.object({pluginRoot:R.string().optional().describe("Base path for relative plugin sources"),version:R.string().optional().describe("Marketplace version"),description:R.string().optional().describe("Marketplace description")}).optional().describe("Optional marketplace metadata"),allowCrossMarketplaceDependenciesOn:R.array(R.string()).optional().describe("Marketplace names whose plugins may be auto-installed as dependencies. Only the root marketplace's allowlist applies — no transitive trust.")})),NP=K0(()=>R.string().regex(/^[A-Za-z0-9][-A-Za-z0-9._]*@[A-Za-z0-9][-A-Za-z0-9._]*$/,"Plugin ID must be in format: plugin@marketplace")),rV0=/^[A-Za-z0-9][-A-Za-z0-9._]*(@[A-Za-z0-9][-A-Za-z0-9._]*)?(@\^[^@]*)?$/,tV0=K0(()=>R.union([R.string().regex(rV0,"Dependency must be a plugin name, optionally qualified with @marketplace").transform((x)=>x.replace(/@\^[^@]*$/,"")),R.object({name:R.string().min(1).regex(/^[A-Za-z0-9][-A-Za-z0-9._]*$/),marketplace:R.string().min(1).regex(/^[A-Za-z0-9][-A-Za-z0-9._]*$/).optional()}).loose().transform((x)=>x.marketplace?`${x.name}@${x.marketplace}`:x.name)])),eV0=K0(()=>R.object({version:R.string().describe("Currently installed version"),installedAt:R.string().describe("ISO 8601 timestamp of installation"),lastUpdated:R.string().optional().describe("ISO 8601 timestamp of last update"),installPath:R.string().describe("Absolute path to the installed plugin directory"),gitCommitSha:R.string().optional().describe("Git commit SHA for git-based plugins (for version tracking)"),resolvedVersion:R.string().optional().describe("Tag-derived semver this install resolved to (when fetched via a version constraint). Used by verifyAndDemote in preference to manifest.version, since the upstream may have forgotten to bump plugin.json."),auto:R.boolean().optional().describe("True when this plugin was pulled in as a dependency rather than installed explicitly. Auto-installed plugins are eligible for removal by the orphan sweep when nothing depends on them. Absent = manual (preserves pre-flag installs).")})),aV0=K0(()=>R.object({version:R.literal(1).describe("Schema version 1"),plugins:R.record(NP(),eV0()).describe("Map of plugin IDs to their installation metadata")})),xH0=K0(()=>R.enum(["managed","user","project","local"])),YH0=K0(()=>R.object({scope:xH0().describe("Installation scope"),projectPath:R.string().optional().describe("Project path (required for project/local scopes)"),installPath:R.string().describe("Absolute path to the versioned plugin directory"),version:R.string().optional().describe("Currently installed version"),installedAt:R.string().optional().describe("ISO 8601 timestamp of installation"),lastUpdated:R.string().optional().describe("ISO 8601 timestamp of last update"),gitCommitSha:R.string().optional().describe("Git commit SHA for git-based plugins"),resolvedVersion:R.string().optional().describe("Tag-derived semver this install resolved to"),auto:R.boolean().optional().describe("True when pulled in as a dependency. Eligible for orphan sweep.")})),ZH0=K0(()=>R.object({version:R.literal(2).describe("Schema version 2"),plugins:R.record(NP(),R.array(YH0())).describe("Map of plugin IDs to arrays of installation entries")})),aM0=K0(()=>R.union([aV0(),ZH0()])),QH0=K0(()=>R.object({source:bJ().describe("Where to fetch the marketplace from"),installLocation:R.string().describe("Local cache path where marketplace manifest is stored"),lastUpdated:R.string().describe("ISO 8601 timestamp of last marketplace refresh"),autoUpdate:R.boolean().optional().describe("Whether to automatically update this marketplace and its installed plugins on startup")})),xR0=K0(()=>R.record(R.string(),QH0())),JH0=["autoMode","deepLink","voice","briefView","screenReader"],rQ={},SX={autoMode:{buildGate:()=>!1,shape:()=>rQ,permissionsShape:()=>rQ,permissionModes:()=>[]},deepLink:{buildGate:()=>!0,shape:()=>({disableDeepLinkRegistration:R.enum(["disable"]).optional().describe("Prevent claude-cli:// protocol handler registration with the OS")})},voice:{buildGate:()=>!1,shape:()=>rQ},briefView:{buildGate:()=>!0,shape:()=>({defaultView:R.enum(["chat","transcript"]).optional().describe("Default transcript view: chat (SendUserMessage checkpoints only) or transcript (full)")})},screenReader:{buildGate:()=>!1,shape:()=>rQ}};Cc={Task:"Agent",KillShell:"TaskStop",KillBash:"TaskStop",AgentOutputTool:"TaskOutput",BashOutputTool:"TaskOutput",AgentOutput:"TaskOutput",BashOutput:"TaskOutput",ListPeers:"ListAgents",Brief:"SendUserMessage",ListMcpResources:"ListMcpResourcesTool",ReadMcpResource:"ReadMcpResourceTool"};YR0=`mcp__${OP}__bash`,ZR0=`mcp__${OP}__web_fetch`;kJ={filePatternTools:["Read","Write","Edit","Glob","NotebookRead","NotebookEdit","Cd"],bashPrefixTools:["Bash"],customValidation:{WebSearch:(x)=>{if(x.includes("*")||x.includes("?"))return{valid:!1,error:"WebSearch does not support wildcards",suggestion:"Use exact search terms without * or ?",examples:["WebSearch(claude ai)","WebSearch(typescript tutorial)"]};return{valid:!0}},WebFetch:(x)=>{if(x.includes("://")||x.startsWith("http"))return{valid:!1,error:"WebFetch permissions use domain format, not URLs",suggestion:'Use "domain:hostname" format',examples:["WebFetch(domain:example.com)","WebFetch(domain:github.com)"]};if(!x.startsWith("domain:"))return{valid:!1,error:'WebFetch permissions must use "domain:" prefix',suggestion:'Use "domain:hostname" format',examples:["WebFetch(domain:example.com)","WebFetch(domain:*.google.com)"]};return{valid:!0}}}};Ic=K0(()=>vP()),DH0=K0(()=>vP("allow"));OH0=K0(()=>R.record(R.string(),R.coerce.string()));QR0=K0(()=>MP(LP())),BH0=K0(()=>R.object({source:bJ().describe("Where to fetch the marketplace from"),installLocation:R.string().optional().describe("Local cache path where marketplace manifest is stored (auto-generated if not provided)"),autoUpdate:R.boolean().optional().describe("Whether to automatically update this marketplace and its installed plugins on startup")})),vH0=K0(()=>R.object({serverName:R.string().regex(/^[a-zA-Z0-9_-]+$/,"Server name can only contain letters, numbers, hyphens, and underscores").optional().describe("Name of the MCP server that users are allowed to configure"),serverCommand:R.array(R.string()).min(1,"Server command must have at least one element (the command)").optional().describe("Command array [command, ...args] to match exactly for allowed stdio servers"),serverUrl:R.string().optional().describe('URL pattern with wildcard support (e.g., "https://*.example.com/*") for allowed remote MCP servers')}).refine((x)=>BA([x.serverName!==void 0,x.serverCommand!==void 0,x.serverUrl!==void 0],Boolean)===1,{message:'Entry must have exactly one of "serverName", "serverCommand", or "serverUrl"'})),MH0=K0(()=>R.object({serverName:R.string().regex(/^[a-zA-Z0-9_-]+$/,"Server name can only contain letters, numbers, hyphens, and underscores").optional().describe("Name of the MCP server that is explicitly blocked"),serverCommand:R.array(R.string()).min(1,"Server command must have at least one element (the command)").optional().describe("Command array [command, ...args] to match exactly for blocked stdio servers"),serverUrl:R.string().optional().describe('URL pattern with wildcard support (e.g., "https://*.example.com/*") for blocked remote MCP servers')}).refine((x)=>BA([x.serverName!==void 0,x.serverCommand!==void 0,x.serverUrl!==void 0],Boolean)===1,{message:'Entry must have exactly one of "serverName", "serverCommand", or "serverUrl"'})),RH0=K0(()=>R.object({path:R.string().describe("Absolute path to the helper executable"),timeoutMs:R.number().int().min(1000).optional(),refreshIntervalMs:R.union([R.literal(0),R.number().int().min(60000)]).optional()})),cc=["skills","agents","hooks","mcp"],qc=Object.freeze({type:"invalid-entry-stripped"}),SH0=K0(()=>R.union([R.object({type:R.literal("regex").describe('Config variant. This client understands "regex": matches turn output and builds a URL from named capture groups. Entries with other variants are preserved but skipped at runtime.'),pattern:R.string().describe("Regex matched against turn output (tool results and assistant text)"),url:R.string().describe("Link target. {name} placeholders are filled from named regex capture groups, e.g. (?<id>...) -> {id}. Values are URL-encoded; the origin must be literal in the template. The scheme must be https, http, or a recognized editor or workspace deep-link scheme: vscode, vscode-insiders, cursor, windsurf, zed, jetbrains, idea, slack, linear, notion, figma."),label:R.string().optional().describe("Badge text. {name} placeholders filled from named capture groups; defaults to the full match.")}).passthrough(),R.object({type:R.string().describe("Config variant discriminator for entries this client does not understand; the entry is preserved as-is and skipped at runtime.")}).passthrough()]));jH0=K0(()=>CH0(LP())),JR0=Object.freeze({serverName:"invalid-entry-stripped"}),XR0=[{matches:(x)=>x.path==="permissions.defaultMode"&&x.code==="invalid_value",tip:{suggestion:'Valid modes: "acceptEdits" (ask before file changes), "plan" (analysis only), "bypassPermissions" (auto-accept all), or "default" (standard behavior)',docLink:`${T4}/iam#permission-modes`}},{matches:(x)=>x.path==="apiKeyHelper"&&x.code==="invalid_type",tip:{suggestion:'Provide a shell command that outputs your API key to stdout. The script should output only the API key. Example: "/bin/generate_temp_api_key.sh"'}},{matches:(x)=>x.path==="cleanupPeriodDays"&&x.code==="too_small",tip:{suggestion:'cleanupPeriodDays must be at least 1. To keep transcripts for a long time, set a large number (e.g. 3650 for ~10 years). To disable transcript writes entirely, remove this setting and use the --no-session-persistence CLI flag or the SDK persistSession:false option instead. (0 is rejected because it previously silently disabled all transcript writes, which users setting it to mean "never clean up" did not expect.)'}},{matches:(x)=>x.path.startsWith("env.")&&x.code==="invalid_type",tip:{suggestion:'Environment variables must be strings. Wrap numbers and booleans in quotes. Example: "DEBUG": "true", "PORT": "3000"',docLink:`${T4}/settings#environment-variables`}},{matches:(x)=>(x.path==="permissions.allow"||x.path==="permissions.deny")&&x.code==="invalid_type"&&x.expected==="array",tip:{suggestion:'Permission rules must be in an array. Format: ["Tool(specifier)"]. Examples: ["Bash(npm run build)", "Edit(docs/**)", "Read(~/.zshrc)"]. Use * for wildcards.'}},{matches:(x)=>x.path.startsWith("hooks.")&&x.code==="invalid_key",tip:{suggestion:"Not a recognized hook event. Common events: PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, SessionEnd, Stop. Check spelling and capitalization.",docLink:`${T4}/hooks`}},{matches:(x)=>/\.hooks\.\d+\.command$/.test(x.path)&&x.code==="invalid_type"&&x.received==="undefined",tip:{suggestion:'Command hooks require `command`. For exec form (no shell), set `command` to the executable and `args` to its arguments: {"type": "command", "command": "echo", "args": ["hi"]}. For shell form, set `command` to the full shell string: {"type": "command", "command": "echo hi"}.',docLink:`${T4}/hooks#exec-form-and-shell-form`}},{matches:(x)=>x.path.includes("hooks")&&x.code==="invalid_type",tip:{suggestion:'Hooks use a matcher + hooks array. The matcher is a string: a tool name ("Bash"), pipe-separated list ("Edit|Write"), or empty to match all. Example: {"PostToolUse": [{"matcher": "Edit|Write", "hooks": [{"type": "command", "command": "echo Done"}]}]}'}},{matches:(x)=>x.code==="invalid_type"&&x.expected==="boolean",tip:{suggestion:'Use true or false without quotes. Example: "includeCoAuthoredBy": true'}},{matches:(x)=>x.code==="unrecognized_keys",tip:{suggestion:"Check for typos or refer to the documentation for valid fields",docLink:`${T4}/settings`}},{matches:(x)=>x.code==="invalid_value"&&x.enumValues!==void 0,tip:{suggestion:void 0}},{matches:(x)=>x.code==="invalid_type"&&x.expected==="object"&&x.received===null&&x.path==="",tip:{suggestion:"Check for missing commas, unmatched brackets, or trailing commas. Use a JSON validator to identify the exact syntax error."}},{matches:(x)=>x.path==="permissions.additionalDirectories"&&x.code==="invalid_type",tip:{suggestion:'Must be an array of directory paths. Example: ["~/projects", "/tmp/workspace"]. You can also use --add-dir flag or /add-dir command',docLink:`${T4}/iam#working-directories`}}],zR0={permissions:`${T4}/iam#configuring-permissions`,env:`${T4}/settings#environment-variables`,hooks:`${T4}/hooks`},KR0=K0(()=>jH0().strict()),WR0=new Set(Yq),VR0=Object.freeze({settings:{},errors:[]});process.env.NoDefaultCurrentDirectoryInExePath="1";AH0=new Set(["EBUSY","EMFILE","ENFILE","ENOTEMPTY","EPERM"])});function gH0(x){return{...TP[x]??{},...hx.claude.models[x]??{}}}function PH0(x){let Z=gH0(x);return{key:`claude:${x}`,name:x,displayName:Z.display_name?.trim()||`Claude Code · ${x}`,description:Z.description?.trim()||`使用 ${x} 路由运行 Claude Code 后端。`,sdkModel:Z.model?.trim()||"opus"}}function wH0(){return[...new Set([...Object.keys(TP),...Object.keys(hx.claude.models)])].map(PH0).filter((Z)=>Z!==null)}function uH0(x){if(!x?.startsWith("claude:"))return null;let Z=x.slice(7);if(!Z||Z==="default")return null;return wH0().find((Q)=>Q.name===Z||Q.key===x)??null}function L6(x){return x.startsWith("claude:")?x:`claude:${x}`}function YU(x){if(!x)return"opus";let Z=uH0(x);if(Z)return Z.sdkModel;let Q=x.startsWith("claude:")?x.slice(7):x;return Q==="default"?"opus":Q}var TP;var IP=E0(()=>{a6();TP={glm:{display_name:"Claude Code · GLM",description:"使用 GLM 路由,适合中文交流和通用编码任务。"}}});import{existsSync as XU,readFileSync as kP}from"node:fs";import{homedir as a2}from"node:os";import{spawn as fH0}from"node:child_process";import{delimiter as cP,join as V5,posix as yH0,win32 as hH0}from"node:path";import{EventEmitter as mH0}from"node:events";function _H0(x){return x==="win32"?";":":"}function QU(x,...Z){return x==="win32"?hH0.join(...Z):yH0.join(...Z)}function qP(x){let Z=x.toLowerCase();return Z.endsWith(".cmd")||Z.endsWith(".bat")}function AP(x){let Z=fH0(x.command,x.args,{cwd:x.cwd,env:x.env,shell:!0,signal:x.signal,stdio:["pipe","pipe","ignore"],windowsHide:!0});if(!Z.stdin||!Z.stdout)throw Z.kill(),Error("failed to open stdio for Claude Code Windows shell shim");return Z}function PP(x={}){let Z=x.platform??process.platform,Q=x.exists??XU,J=x.homeDir??a2();if(Z!=="win32"){let z=[QU(Z,J,".local","npm-global","bin","claude"),QU(Z,J,".local","bin","claude")];for(let Y of z)if(Q(Y))return Y}let X=lH0(x);if(X)return X;return null}function wP(){PP()}function nH0(x={}){let Z=x.platform??process.platform,Q=x.configuredBin===void 0?hx.claude.bin:x.configuredBin;if(Q){if(!(x.exists??XU)(Q))throw Error(`lodestar: [claude].bin not found: ${Q} (config.toml)`);if(Z==="win32"&&qP(Q))return{pathToClaudeCodeExecutable:Q,spawnClaudeCodeProcess:AP,description:`windows-shell-shim:${Q}`};return{pathToClaudeCodeExecutable:Q,description:`config:${Q}`}}let J=PP(x);if(!J)return{description:"sdk-default"};if(Z==="win32"&&qP(J))return{pathToClaudeCodeExecutable:J,spawnClaudeCodeProcess:AP,description:`windows-shell-shim:${J}`};return{pathToClaudeCodeExecutable:J,description:J}}function lH0(x={}){let Z=x.platform??process.platform,Q=x.pathEnv??process.env.PATH??"";if(!Q)return null;let J=x.exists??XU,X=Z==="win32"?["claude.exe","claude.cmd","claude.bat","claude"]:["claude"];for(let z of Q.split(_H0(Z))){if(!z)continue;for(let Y of X){let G=QU(Z,z,Y);if(J(G))return G}}return null}function dH0(){if(process.platform==="win32")return process.env.PATH??"";let x=[V5(a2(),".local","npm-global","bin"),V5(a2(),".local","bin"),V5(a2(),".bun","bin"),...(process.env.PATH??"").split(cP),"/usr/local/bin","/usr/bin","/bin"];return[...new Set(x.filter(Boolean))].join(cP)}function iH0(x){let Z=S7(x);if(!Z)return null;let Q=(Z.input_tokens??0)+(Z.output_tokens??0)+(Z.cache_creation_input_tokens??0)+(Z.cache_read_input_tokens??0);if((Z.total_tokens??Q)<=0)return null;if(Z.total_tokens==null)Z.total_tokens=Q;return Z}function pH0(x){if(!x)return null;let Z=(x.input_tokens??0)+(x.cache_read_input_tokens??0)+(x.cache_creation_input_tokens??0);return Z>0?Z:null}function sH0(x,Z){let Q=x.replace(/\//g,"-"),J=process.env.CLAUDE_CONFIG_DIR||V5(a2(),".claude");return V5(J,"projects",Q,`${Z}.jsonl`)}function oH0(x){let Z;try{Z=kP(x,"utf8")}catch{return null}let Q=Z.split(`
|
|
222
222
|
`);for(let J=Q.length-1;J>=0;J--){let X=Q[J].trim();if(!X)continue;try{let z=JSON.parse(X);if(z?.type==="assistant"&&z?.message?.usage)return z.message.usage}catch{}}return null}function EP(x){return x&&x.trim()?x:"claude:default"}function rH0(x){if(!x||typeof x!=="object")return{usage:null,contextWindow:null};let Z={},Q=null;for(let X of Object.values(x)){if(!X||typeof X!=="object")continue;let z=X;Z.input_tokens=(Z.input_tokens??0)+W5(z.inputTokens??z.input_tokens),Z.output_tokens=(Z.output_tokens??0)+W5(z.outputTokens??z.output_tokens),Z.reasoning_output_tokens=(Z.reasoning_output_tokens??0)+W5(z.reasoningOutputTokens??z.reasoning_output_tokens),Z.cache_read_input_tokens=(Z.cache_read_input_tokens??0)+W5(z.cacheReadInputTokens??z.cache_read_input_tokens),Z.cache_creation_input_tokens=(Z.cache_creation_input_tokens??0)+W5(z.cacheCreationInputTokens??z.cache_creation_input_tokens);let Y=W5(z.contextWindow??z.context_window);if(Y>0)Q=Math.max(Q??0,Y)}let J=(Z.input_tokens??0)+(Z.output_tokens??0)+(Z.cache_creation_input_tokens??0)+(Z.cache_read_input_tokens??0);if(J<=0)return{usage:null,contextWindow:Q};return Z.total_tokens=J,{usage:Z,contextWindow:Q}}function CX(x){return{...x}}function ZU(x){if(!x||typeof x!=="object")return[];return Object.keys(x).slice(0,20)}function tH0(x,Z){let Q=x?{...x}:{},J=(X)=>{let z=Z[X];if(typeof z!=="number"||!Number.isFinite(z))return;Q[X]=(Q[X]??0)+z};return J("total_tokens"),J("input_tokens"),J("output_tokens"),J("reasoning_output_tokens"),J("cache_creation_input_tokens"),J("cache_read_input_tokens"),Q}function W5(x){return typeof x==="number"&&Number.isFinite(x)?x:0}function uP(x){if(typeof x==="string")return x;if(Array.isArray(x))return x.map((Z)=>{if(typeof Z==="string")return Z;if(Z&&typeof Z==="object"&&typeof Z.text==="string")return Z.text;return JSON.stringify(Z)}).join(`
|
|
223
223
|
`);return x==null?"":JSON.stringify(x,null,2)}function eH0(x){let Z=uP(x);try{let Q=JSON.parse(Z);if(Array.isArray(Q)){let J=Q.map((X)=>{if(typeof X==="string")return X;if(X&&typeof X==="object"&&typeof X.text==="string")return X.text;return JSON.stringify(X)}).filter(Boolean);if(J.length>0)return J.join(`
|
|
224
|
-
`)}}catch{}return Z}function aH0(x){return`server_tool:${x}`}function xY(x){if(typeof x==="string")return x.replace(/https?:\/\/[^\s"'`<>]+/g,"<url-redacted>");if(Array.isArray(x))return x.map((Z)=>xY(Z));if(x&&typeof x==="object"){let Z={};for(let[Q,J]of Object.entries(x))Z[Q]=xY(J);return Z}return x}function xG0(x){let Z=x.match(/Built-in Tool:\s*([A-Za-z0-9_.:-]+)/)?.[1];if(!Z)return null;let Q=x.match(/\*\*Input:\*\*\s*```(?:json)?\s*([\s\S]*?)```/)?.[1]?.trim();if(!Q)return{name:Z,input:{}};try{return{name:Z,input:xY(JSON.parse(Q))}}catch{return{name:Z,input:{raw:xY(Q)}}}}function YG0(x){let Z=x.trim();return Z.includes("Built-in Tool:")||Z.startsWith("**Output:**")&&Z.includes("_result")}function ZG0(x){let Z=x.supportedEffortLevels&&x.supportedEffortLevels.length>0?x.supportedEffortLevels:[Y3];return{id:L6(x.value),model:L6(x.value),displayName:x.displayName||L6(x.value),description:x.description||"Claude Agent SDK backend",hidden:!1,isDefault:!1,supportedReasoningEfforts:Z.map((Q)=>({reasoningEffort:Q,description:""})),defaultReasoningEffort:Z.includes(Y3)?Y3:Z[0]}}function XG0(x){if(!x?.settingSources)return[...bP];let Z=x.settingSources.split(",").map((Q)=>Q.trim()).filter(Boolean);return Z.length?Z:[...bP]}function zG0(x){if(!x?.tools)return{type:"preset",preset:"claude_code"};let Z=x.tools.split(",").map((Q)=>Q.trim()).filter(Boolean);return Z.length?Z:{type:"preset",preset:"claude_code"}}function KG0(x){let Z=V5(x,".mcp.json"),Q;try{Q=kP(Z,"utf8")}catch(J){g(`claude-agent-process: project .mcp.json not readable at ${Z}: ${J}`);return}try{let J=JSON.parse(Q);if(J&&typeof J==="object"&&J.mcpServers&&typeof J.mcpServers==="object")return J.mcpServers;g(`claude-agent-process: project .mcp.json has no mcpServers object at ${Z}`);return}catch(J){g(`claude-agent-process: project .mcp.json parse failed at ${Z}: ${J}`);return}}function WG0(x){if(!fP.includes(x.dialogKind))return null;let Z=x.payload&&typeof x.payload==="object"?x.payload:{},Q=VG0(Z);if(Q.length===0)return null;return{...Z,questions:Q}}function VG0(x){let Z=x.questions;return(Array.isArray(Z)?Z:Z&&typeof Z==="object"?[Z]:[x]).map((J)=>HG0(J)).filter((J)=>J!==null)}function HG0(x){let Z=x&&typeof x==="object"?x:{question:x},Q=JU(Z.question,Z.prompt,Z.title,Z.message,Z.text,Z.label);if(!Q)return null;if(!Q.trim())return null;let J=GG0(Z.options??Z.choices??Z.suggestions);return{...Z,question:Q,options:J,...typeof Z.header==="string"?{header:Z.header}:{},...typeof Z.multiSelect==="boolean"?{multiSelect:Z.multiSelect}:{}}}function GG0(x){if(!Array.isArray(x))return[];return x.map((Z)=>{if(typeof Z==="string")return{label:Z};if(!Z||typeof Z!=="object")return null;let Q=Z,J=JU(Q.label,Q.value,Q.text,Q.title);if(!J?.trim())return null;let X=JU(Q.description,Q.detail,Q.preview);return X?{label:J,description:X}:{label:J}}).filter((Z)=>Z!==null)}function JU(...x){for(let Z of x)if(typeof Z==="string")return Z;return}var gP,e2,fP,QG0="bypassPermissions",JG0=!0,bP,zU;var yP=E0(()=>{jP();a6();Lx();f9();IP();gP=class gP{items=[];waiters=[];closed=!1;push(x){if(this.closed)throw Error("input stream is closed");let Z=this.waiters.shift();if(Z)Z({value:x,done:!1});else this.items.push(x)}close(){if(this.closed)return;this.closed=!0;while(this.waiters.length)this.waiters.shift()?.({value:void 0,done:!0})}[Symbol.asyncIterator](){return this}next(){if(this.items.length>0)return Promise.resolve({value:this.items.shift(),done:!1});if(this.closed)return Promise.resolve({value:void 0,done:!0});return new Promise((x)=>this.waiters.push(x))}};e2=new Map;fP=["ask_user_question","askUserQuestion","AskUserQuestion"],bP=["user"];zU=class zU extends mH0{provider="claude";opts;input=new gP;query=null;alive=!0;expectedExit=!1;started=!1;pendingPermissions=new Map;pendingInjectedContext=[];requestCounter=0;cumulativeUsageFromResults=null;turnActive=!1;emittedToolUseIds=new Set;emittedToolResultIds=new Set;pendingServerToolInputs=[];sessionId=null;lastAssistantUuid=null;lastModel=null;lastEffort=null;lastUsage=null;lastTotalUsage=null;lastResult={cost_usd:null,cost_delta_usd:null,duration_ms:null,num_turns:null,usage:null,subtype:null,is_error:!1};lastContextWindow=null;lastContextTokens=null;constructor(x){super();this.on("error",()=>{}),this.opts=x,this.lastEffort=x.effort,this.lastModel=x.model?L6(x.model):null}sendInitialize(){if(this.started)return;this.started=!0;let x=YU(this.opts.model),Z=this.opts.profile;if(Z)g(`claude-agent-process: project profile active — settingSources=${Z.settingSources??"-"} strictMcp=${Z.strictMcp??!1} tools=${Z.tools??"-"} loadProjectMcp=${Z.loadProjectMcp??!
|
|
224
|
+
`)}}catch{}return Z}function aH0(x){return`server_tool:${x}`}function xY(x){if(typeof x==="string")return x.replace(/https?:\/\/[^\s"'`<>]+/g,"<url-redacted>");if(Array.isArray(x))return x.map((Z)=>xY(Z));if(x&&typeof x==="object"){let Z={};for(let[Q,J]of Object.entries(x))Z[Q]=xY(J);return Z}return x}function xG0(x){let Z=x.match(/Built-in Tool:\s*([A-Za-z0-9_.:-]+)/)?.[1];if(!Z)return null;let Q=x.match(/\*\*Input:\*\*\s*```(?:json)?\s*([\s\S]*?)```/)?.[1]?.trim();if(!Q)return{name:Z,input:{}};try{return{name:Z,input:xY(JSON.parse(Q))}}catch{return{name:Z,input:{raw:xY(Q)}}}}function YG0(x){let Z=x.trim();return Z.includes("Built-in Tool:")||Z.startsWith("**Output:**")&&Z.includes("_result")}function ZG0(x){let Z=x.supportedEffortLevels&&x.supportedEffortLevels.length>0?x.supportedEffortLevels:[Y3];return{id:L6(x.value),model:L6(x.value),displayName:x.displayName||L6(x.value),description:x.description||"Claude Agent SDK backend",hidden:!1,isDefault:!1,supportedReasoningEfforts:Z.map((Q)=>({reasoningEffort:Q,description:""})),defaultReasoningEffort:Z.includes(Y3)?Y3:Z[0]}}function XG0(x){if(!x?.settingSources)return[...bP];let Z=x.settingSources.split(",").map((Q)=>Q.trim()).filter(Boolean);return Z.length?Z:[...bP]}function zG0(x){if(!x?.tools)return{type:"preset",preset:"claude_code"};let Z=x.tools.split(",").map((Q)=>Q.trim()).filter(Boolean);return Z.length?Z:{type:"preset",preset:"claude_code"}}function KG0(x){let Z=V5(x,".mcp.json"),Q;try{Q=kP(Z,"utf8")}catch(J){g(`claude-agent-process: project .mcp.json not readable at ${Z}: ${J}`);return}try{let J=JSON.parse(Q);if(J&&typeof J==="object"&&J.mcpServers&&typeof J.mcpServers==="object")return J.mcpServers;g(`claude-agent-process: project .mcp.json has no mcpServers object at ${Z}`);return}catch(J){g(`claude-agent-process: project .mcp.json parse failed at ${Z}: ${J}`);return}}function WG0(x){if(!fP.includes(x.dialogKind))return null;let Z=x.payload&&typeof x.payload==="object"?x.payload:{},Q=VG0(Z);if(Q.length===0)return null;return{...Z,questions:Q}}function VG0(x){let Z=x.questions;return(Array.isArray(Z)?Z:Z&&typeof Z==="object"?[Z]:[x]).map((J)=>HG0(J)).filter((J)=>J!==null)}function HG0(x){let Z=x&&typeof x==="object"?x:{question:x},Q=JU(Z.question,Z.prompt,Z.title,Z.message,Z.text,Z.label);if(!Q)return null;if(!Q.trim())return null;let J=GG0(Z.options??Z.choices??Z.suggestions);return{...Z,question:Q,options:J,...typeof Z.header==="string"?{header:Z.header}:{},...typeof Z.multiSelect==="boolean"?{multiSelect:Z.multiSelect}:{}}}function GG0(x){if(!Array.isArray(x))return[];return x.map((Z)=>{if(typeof Z==="string")return{label:Z};if(!Z||typeof Z!=="object")return null;let Q=Z,J=JU(Q.label,Q.value,Q.text,Q.title);if(!J?.trim())return null;let X=JU(Q.description,Q.detail,Q.preview);return X?{label:J,description:X}:{label:J}}).filter((Z)=>Z!==null)}function JU(...x){for(let Z of x)if(typeof Z==="string")return Z;return}var gP,e2,fP,QG0="bypassPermissions",JG0=!0,bP,zU;var yP=E0(()=>{jP();a6();Lx();f9();IP();gP=class gP{items=[];waiters=[];closed=!1;push(x){if(this.closed)throw Error("input stream is closed");let Z=this.waiters.shift();if(Z)Z({value:x,done:!1});else this.items.push(x)}close(){if(this.closed)return;this.closed=!0;while(this.waiters.length)this.waiters.shift()?.({value:void 0,done:!0})}[Symbol.asyncIterator](){return this}next(){if(this.items.length>0)return Promise.resolve({value:this.items.shift(),done:!1});if(this.closed)return Promise.resolve({value:void 0,done:!0});return new Promise((x)=>this.waiters.push(x))}};e2=new Map;fP=["ask_user_question","askUserQuestion","AskUserQuestion"],bP=["user","project","local"];zU=class zU extends mH0{provider="claude";opts;input=new gP;query=null;alive=!0;expectedExit=!1;started=!1;pendingPermissions=new Map;pendingInjectedContext=[];requestCounter=0;cumulativeUsageFromResults=null;turnActive=!1;emittedToolUseIds=new Set;emittedToolResultIds=new Set;pendingServerToolInputs=[];sessionId=null;lastAssistantUuid=null;lastModel=null;lastEffort=null;lastUsage=null;lastTotalUsage=null;lastResult={cost_usd:null,cost_delta_usd:null,duration_ms:null,num_turns:null,usage:null,subtype:null,is_error:!1};lastContextWindow=null;lastContextTokens=null;constructor(x){super();this.on("error",()=>{}),this.opts=x,this.lastEffort=x.effort,this.lastModel=x.model?L6(x.model):null}sendInitialize(){if(this.started)return;this.started=!0;let x=YU(this.opts.model),Z=this.opts.profile;if(Z)g(`claude-agent-process: project profile active — settingSources=${Z.settingSources??"-"} strictMcp=${Z.strictMcp??!1} tools=${Z.tools??"-"} loadProjectMcp=${Z.loadProjectMcp??!0} keepInstructions=${Z.keepLodestarInstructions??!0}`);let Q=XG0(Z),J=zG0(Z),X=Z?.strictMcp===!0,z=Z?.loadProjectMcp!==!1?KG0(this.opts.workDir):void 0,Y=Z?.keepLodestarInstructions===!1?void 0:this.opts.appendSystemPrompt;try{let G=nH0();g(`claude-agent-process: spawn SDK query model=${x??"default"} effort=${this.opts.effort} cwd=${this.opts.workDir} executable=${G.description}`),this.query=SP({prompt:this.input,options:{cwd:this.opts.workDir,...x?{model:x}:{},effort:this.opts.effort,resume:this.opts.resumeSessionId,...G.pathToClaudeCodeExecutable?{pathToClaudeCodeExecutable:G.pathToClaudeCodeExecutable}:{},...G.spawnClaudeCodeProcess?{spawnClaudeCodeProcess:G.spawnClaudeCodeProcess}:{},permissionMode:QG0,allowDangerouslySkipPermissions:JG0,env:{...process.env,PATH:dH0(),...hx.claude.env},settingSources:Q,tools:J,...X?{strictMcpConfig:!0}:{},...z?{mcpServers:z}:{},toolConfig:{askUserQuestion:{previewFormat:"markdown"}},supportedDialogKinds:[...fP],onUserDialog:(W,H)=>this.onUserDialog(W,H),includePartialMessages:!1,systemPrompt:{type:"preset",preset:"claude_code",...Y?{append:Y}:{}},stderr:(W)=>{let H=W.trim();if(H)g(`claude-agent-process[stderr]: ${H}`)}}})}catch(G){this.alive=!1;let W=G instanceof Error?G:Error(String(G));this.emit("error",W),this.emit("exit",{code:1,signal:null,expected:this.expectedExit});return}this.readLoop(this.query)}sendUserText(x,Z=[]){if(!this.alive){g("claude-agent-process: sendUserText ignored on dead process");return}if(!this.started)this.sendInitialize();let Q=Z.length?Z.map((X)=>`[file: ${X}]`).join(" ")+`
|
|
225
225
|
|
|
226
226
|
`:"",J=this.pendingInjectedContext.length?this.pendingInjectedContext.splice(0).join(`
|
|
227
227
|
|
|
@@ -327,7 +327,7 @@ ${Q}`:Z}function Tz(x){let Z=g3(x),Q=vy(x.workDir,j5(x),Z);if(!Q)return null;ret
|
|
|
327
327
|
${z}`,card:LY({slug:"wt",branch:"work/*",status:Q,body:z,template:"red"})}}}async function Ey(x,Z){let Q=k3(Z);if(!Q)return cY(x,!1,"❌ 名称无效","error");if(IY(Q))return cY(x,!1,`❌ ${Q} 是 AI 自动化系统保留 worktree,不能解散。`,"error");let J=g3(x),X=j5(x);try{let z=C5(J,Q);if(x.hasRunningPeerSession(z)){let H=`❌ 解散 ${Q} 失败: Codex 正在运行,请先在 ${z} 群里 stop 或 kill。`;return cY(x,!1,H,"error")}CN(X,J,Q);let Y=await Nw(z),G=Sy(X,J,Q),W=[`✅ ${Q} 已解散`,G.removedWorktree?"dir removed":"dir missing",Y.disbanded?"group removed":"group missing",G.branch].join(`
|
|
328
328
|
`);return cY(x,!0,W,"success")}catch(z){let Y=`❌ 解散 ${Q} 失败: ${Hx(z)}`;return cY(x,!1,Y,"error")}}var by=E0(()=>{Uy();Q4();rx();Lx();Ty()});import{existsSync as lU0}from"node:fs";import{isAbsolute as dU0,join as iU0}from"node:path";function pU0(x){return x.itemId||x.turnId||"__latest__"}function sU0(x){let Z=null;for(let Q of x.contextCompactionPending.keys())Z=Q;return Z}function oU0(x,Z){if(!x)return Z;return{...x,...Z,phase:"end"}}function uy(x,Z){let Q=Math.max(0,Math.floor((Date.now()-Z)/1000));return`${x} (${Q}s)`}var ky=1000,rU0=2000,tU0="Thinking...",eU0="Writing...",aU0="Working...",gy=1e4,Py=120000,xN0=50,AN=5,wy=250,T5;var fy=E0(()=>{C7();f9();yP();C3();Q4();rx();Lx();PU();du();ru();Yf();Kf();Tf();Af();MN();df();of();tf();af();Jy();Vy();by();T5=class T5{sessionName;chatId;opts;static all=new Set;proc=null;currentTurn=null;backgroundTasks=[];pendingBgTasks=[];backgroundCard=null;backgroundRefreshTimer=null;backgroundRefreshTick=null;openingBackground=!1;backgroundDetailAdded=new Set;lastMainTaskToolUseId=null;pendingRebuildBackgroundCard=!1;bgResumePending=!1;orphanAssistantSegments=[];orphanAssistantCurrent="";bgResumeCardless=!1;sawResultWhileOpening=!1;pendingPermissions=new Map;pendingAsks=new Map;pendingHostAsks=new Map;currentGoal=null;status="stopped";pendingUserMessageCount=0;pendingMidTurnMsgs=[];pendingTurnInputs=[];multiMsgBuffer=null;multiMsgReactions=new Map;lastUserOpenId="";pendingReactionIds=new Map;currentBatchReactionIds=new Map;initCount=0;openingTurn=!1;turnCounter=0;userInterrupted=!1;lastSessionId=null;selectedProvider="claude";selectedModel=null;selectedEffort=null;modelPanels=new Map;startedAt=0;cumStats={tokens:0,costUsd:0,turns:0};lastTurnDelta=null;currentTurnUsageBaseline=null;currentTurnUsageBaselineKnown=!1;lastTurnUsage=null;usageTotalsSeedUnknown=!1;manualContextCompactionPending=!1;runningAgy=null;startingAgy=!1;agyForwardPrompts=new Map;taskBoard=[];constructor(x,Z,Q={}){this.sessionName=x;this.chatId=Z;this.opts=Q;T5.all.add(this);let J=Gw(x);if(this.selectedProvider=J?.provider??"claude",this.selectedModel=J?.model??null,this.selectedEffort=J?.effort??null,J){if(this.selectedProvider==="claude"&&this.selectedModel!=="claude:glm")this.selectedModel="claude:glm",this.selectedEffort="max";else if(this.selectedProvider==="codex"&&this.selectedModel!=="gpt-5.5")this.selectedModel="gpt-5.5",this.selectedEffort="xhigh"}if(this.selectedModel)g(`session "${x}": restored selected provider=${this.selectedProvider} model=${this.selectedModel} effort=${this.selectedEffort??"unset"}`);if(this.lastSessionId=GU(x,this.selectedProvider),this.lastSessionId)g(`session "${x}": restored ${this.selectedProvider} lastSessionId=${this.lastSessionId.slice(0,8)}…`)}peerSnapshot(){return{name:this.sessionName,status:this.status,uptimeMs:this.startedAt?Date.now()-this.startedAt:void 0}}get workDir(){let x=HU(this.sessionName)?.cwd;return x&&x.trim()?x:iU0(M3,this.sessionName)}isRunning(){return!!this.proc&&this.proc.isAlive()}currentProvider(){return this.selectedProvider}hasRunningPeerSession(x){return[...T5.all].some((Z)=>Z.sessionName===x&&Z.isRunning())}modelForSpawn(){return this.selectedModel??void 0}effortForSpawn(){return this.selectedProvider==="codex"&&R8(this.selectedEffort)?this.selectedEffort:rV}claudeEffortForSpawn(){return this.selectedProvider==="claude"&&Z6(this.selectedEffort)?this.selectedEffort:Y3}currentModelLabel(){return this.selectedModel??this.proc?.lastModel??null}currentEffortLabel(){return this.selectedEffort??this.proc?.lastEffort??(this.selectedProvider==="claude"?Y3:rV)}modelEffortLabel(){let x=this.currentModelLabel(),Z=this.currentEffortLabel();return x?`${x}/${Z}`:Z}withModel(x){let Z=this.modelEffortLabel();return x.includes(Z)?x:`${x} · ${Z}`}replaceFooterContent(x,Z){return Zx(x,$0.footer,{tag:"markdown",element_id:$0.footer,content:Z.trim()||" "})}modelLine(){let x=this.currentModelLabel(),Z=this.currentEffortLabel(),Q=this.selectedProvider==="claude"?x?.replace(/^claude:/i,""):x,J=Q?`${Q}/${Z}`:Z;return this.selectedProvider==="claude"?`${Q6(this.selectedProvider)} · ${J}`:J}backendLabel(x=this.selectedProvider){return Q6(x)}spawnAgent(x){if(this.selectedProvider==="claude")return wP(),new zU({workDir:this.workDir,model:this.modelForSpawn(),effort:this.claudeEffortForSpawn(),resumeSessionId:x,appendSystemPrompt:this.spawnDeveloperInstructions(),profile:HU(this.sessionName)});return new tV({workDir:this.workDir,model:this.modelForSpawn(),effort:this.effortForSpawn(),resumeSessionId:x,appendSystemPrompt:this.spawnDeveloperInstructions()})}async applyModelSelection(x,Z,Q){this.selectedProvider=x,this.selectedModel=Z,this.selectedEffort=Q,this.lastSessionId=GU(this.sessionName,x),Hw(this.sessionName,x,Z,Q),await this.stopIdleMismatchedProcess()}async stopIdleMismatchedProcess(){if(!this.proc?.isAlive())return;if(this.proc.provider===this.selectedProvider)return;if(this.currentTurn||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0)return;let x=this.proc;g(`session "${this.sessionName}": stop idle ${x.provider} process after switching to ${this.selectedProvider}`),this.proc=null,this.initCount=0,this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await x.kill(1000)}async stopIdleCurrentProcess(x){if(!this.proc?.isAlive())return!1;if(this.currentTurn||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0)return!1;let Z=this.proc;return g(`session "${this.sessionName}": stop idle ${Z.provider} process: ${x}`),this.proc=null,this.initCount=0,this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await Z.kill(1000),!0}startFooterTimer(x,Z,Q=(J)=>J){let J=Date.now(),X=Z,z=!1,Y=()=>{if(z)return;this.replaceFooterContent(x,Q(uy(X,J)))},G=setInterval(Y,ky);return Y(),{setStatus(W){X=W,Y()},stop(){if(z)return;z=!0,clearInterval(G)},elapsedSec(){return((Date.now()-J)/1000).toFixed(1)}}}async openStatusCard(x,Z,Q="blue"){let J=Date.now(),X=uU({sessionName:this.sessionName,title:x,status:uy(Z,J),template:Q}),z=await mx(this.chatId,X);if(!z)return g(`session "${this.sessionName}": status card send failed title=${x}`),await ux(this.chatId,`❌ 创建状态卡片失败: ${x}`),null;let Y;try{Y=await a8(z)}catch(G){return g(`session "${this.sessionName}": status card id_convert failed title=${x}: ${G}`),await ux(this.chatId,`❌ 状态卡片初始化失败: ${x}`),null}return g4(Y,1),{cardId:Y,title:x,timer:this.startFooterTimer(Y,Z,(G)=>UY(x,G))}}setStatusCard(x,Z){x?.timer.setStatus(Z)}async closeStatusCard(x,Z){if(!x)return;x.timer.stop();let Q=x.timer.elapsedSec(),J=UY(x.title,`${Z} (${Q}s)`);await b8(x.cardId),await this.replaceFooterContent(x.cardId,J),v1(x.cardId),await b1(x.cardId,x4({durationSec:Q,suffix:Z})),await d1(x.cardId)}beginAgyForwardToCodex(x,Z=""){return Rf(this,x,Z)}runAgyCommand(x){return Sf(this,x)}stopAgyTask(x="\uD83D\uDED1 agy 已打断"){return jf(this,x)}resetFreshConversationState(){this.turnCounter=0,this.currentGoal=null,this.cumStats={tokens:0,costUsd:0,turns:0},this.lastTurnDelta=null,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.lastTurnUsage=null,this.usageTotalsSeedUnknown=!1}async start(x={}){let Z=x.announce??!0,Q=x.onStatus;if(this.isRunning()){if(this.proc?.provider===this.selectedProvider)return Q?.(this.withModel(`✅ ${this.backendLabel()} 已运行`)),!0;if(await this.stopIdleMismatchedProcess(),this.proc?.isAlive())return Q?.(`⚠️ 当前 ${this.backendLabel(this.proc.provider)} turn 尚未结束,模型切换将在后续新 turn 生效`),!0}if(this.selectedProvider==="codex")Q?.("\uD83D\uDD0E 检查 Codex 登录");else Q?.("\uD83D\uDD0E 检查 Claude Code");if(this.selectedProvider==="codex"&&!Sw()){if(this.status="stopped",this.opts.onLifecycleChange?.(),Q?.("❌ Codex 未登录 ChatGPT 账号"),Z)await C0(this.chatId,"❌ Codex 未登录 ChatGPT 账号。\n请在服务器上运行 `codex login` 后再试。");return!1}if(!lU0(this.workDir)){if(Q?.(`\uD83C\uDD95 创建项目目录 ~/${this.sessionName}`),Z)await C0(this.chatId,`\uD83C\uDD95 目录 ~/${this.sessionName} 不存在,正在创建…`);try{Rw(this.workDir)}catch(G){if(this.status="stopped",this.opts.onLifecycleChange?.(),Q?.(`❌ 创建项目失败: ${G}`),Z)await C0(this.chatId,`❌ 创建项目失败: ${G}`);return!1}}if(!x.freshConversationStateAlreadyReset)this.resetFreshConversationState();this.status="starting",Q?.(this.withModel(`\uD83D\uDE80 启动 ${this.backendLabel()}`));let J;try{J=this.spawnAgent()}catch(G){let W=`${this.backendLabel()} 启动失败: ${Hx(G)}`;if(g(`session "${this.sessionName}": ${W}`),Q?.(`❌ ${W}`),Z)await C0(this.chatId,`❌ ${W}`);return this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),!1}this.proc=J,this.wireProc(this.proc);let X=this.backendLabel(),z=this.selectedProvider==="claude"?this.waitForProcEarlyFailure(this.proc,wy):this.waitForProcInit(this.proc,5000);Q?.(this.selectedProvider==="claude"?`⏳ 检查 ${X} 启动`:`⏳ 等待 ${X} init`),this.proc.sendInitialize();let Y=await z;if(Y.state==="error"||Y.state==="exit"){let G=Y.error?Hx(Y.error):Y.state;if(g(`session "${this.sessionName}": ${this.selectedProvider} init failed: ${G}`),Q?.(`❌ ${X} 启动失败: ${G}`),Z)await C0(this.chatId,`❌ ${X} 启动失败: ${G}`);return await this.proc?.kill(1000).catch(()=>{}),this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),!1}if(Y.state==="timeout")g(`session "${this.sessionName}": ${this.selectedProvider} init wait timeout (5s)`),Q?.(this.withModel(this.withWorktreeInstructionNotice(`⏳ ${X} 已启动,init 确认超时`)));if(Z){let G=this.modelLine();await C0(this.chatId,[this.withWorktreeInstructionNotice(`✅ Lodestar session "${this.sessionName}" 已就绪,发消息开始对话。`),G].filter(Boolean).join(`
|
|
329
329
|
`))}return this.status="idle",this.startedAt=Date.now(),this.opts.onLifecycleChange?.(),Q?.(this.withModel(this.withWorktreeInstructionNotice(`✅ ${this.backendLabel()} 已就绪`))),!0}async waitForProcInit(x,Z){return await new Promise((Q)=>{let J=!1,X=(H,K)=>{if(J)return;J=!0,clearTimeout(z),x.off("init",Y),x.off("error",G),x.off("exit",W),Q({state:H,error:K})},z=setTimeout(()=>X("timeout"),Z),Y=()=>X("init"),G=(H)=>X("error",H),W=(H)=>X("exit",H);x.once("init",Y),x.once("error",G),x.once("exit",W)})}async waitForProcEarlyFailure(x,Z){return await new Promise((Q)=>{let J=!1,X=(H,K)=>{if(J)return;J=!0,clearTimeout(z),x.off("init",Y),x.off("error",G),x.off("exit",W),Q({state:H,error:K})},z=setTimeout(()=>X("ready"),Z),Y=()=>X("init"),G=(H)=>X("error",H),W=(H)=>X("exit",H);x.once("init",Y),x.once("error",G),x.once("exit",W)})}async waitForProcResumeInit(x,Z){return await new Promise((Q)=>{let J=!1,X=(K,N)=>{if(J)return;J=!0,clearTimeout(z),clearTimeout(Y),x.off("init",G),x.off("error",W),x.off("exit",H),Q({state:K,error:N})},z=setTimeout(()=>{if(!J)Z()},gy),Y=setTimeout(()=>{X("timeout",Error(`resume init timed out after ${Py/1000}s`))},Py),G=()=>X("init"),W=(K)=>X("error",K),H=(K)=>X("exit",K);x.once("init",G),x.once("error",W),x.once("exit",H)})}releaseAllReactions(){for(let[x,Z]of[...this.pendingReactionIds.entries(),...this.currentBatchReactionIds.entries()])if(Z)E8(x,Z);this.pendingReactionIds=new Map,this.currentBatchReactionIds=new Map}clearStaleIdleQueueState(x){if(this.initCount<1||this.currentTurn||this.openingTurn||this.pendingUserMessageCount===0)return;g(`session "${this.sessionName}": clear stale pending queue before ${x} pendingCount=${this.pendingUserMessageCount} reactions=${this.pendingReactionIds.size}`),this.pendingUserMessageCount=0;for(let[Z,Q]of this.pendingReactionIds)if(Q)E8(Z,Q);this.pendingReactionIds=new Map}async stop(x="已终止",Z={}){let Q=Z.announce??!0,J=Z.onStatus,X=await this.stopAgyTask(`\uD83D\uDED1 ${x}`);if(!this.proc){if(this.status="stopped",this.opts.onLifecycleChange?.(),J?.(X?`✅ ${x}`:"⚪ session 当前未运行"),Q&&!X)await C0(this.chatId,`⚪ session "${this.sessionName}" 当前未运行`);return}J?.(`\uD83D\uDED1 停止 ${this.backendLabel(this.proc.provider)}`),g(`session "${this.sessionName}": stop (${x})`);let z=this.proc;if(this.proc=null,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("stop"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),await z.kill(),J?.(`✅ ${x}`),Q)await C0(this.chatId,`\uD83D\uDD34 ${x} (session: ${this.sessionName})`)}async restart(x=!1,Z={}){let Q=Z.announce??!0,J=Z.onStatus,X=this.lastSessionId,z=X?X.slice(0,8):"",Y=null;if(!J&&Q&&x&&X){let H=this.proc?this.withModel(`\uD83D\uDD01 重启 ${this.backendLabel(this.proc.provider)}`):this.withModel(`\uD83D\uDD01 恢复上一会话 thread=${z}…`);if(Y=await this.openStatusCard("restart",H),Y)J=(K)=>this.setStatusCard(Y,K)}let G=Q&&!Y,W=async(H)=>{if(Y)await this.closeStatusCard(Y,H)};if(this.bgResumePending=!1,this.sawResultWhileOpening=!1,this.discardOrphanAssistant(),this.proc){J?.(`\uD83D\uDED1 停止当前 ${this.backendLabel(this.proc.provider)}`);let H=this.proc;this.proc=null,await H.kill()}if(this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("restart"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,x&&X){this.status="starting",this.usageTotalsSeedUnknown=!0,J?.(this.withModel(`\uD83D\uDD01 恢复上一会话 thread=${z}…`));let H;try{H=this.spawnAgent(X)}catch(O){let v=`❌ ${this.backendLabel()} 恢复失败: ${Hx(O)}`;if(g(`session "${this.sessionName}": ${this.selectedProvider} resume failed before spawn: ${Hx(O)}`),J?.(v),G)await C0(this.chatId,v);return this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),await W(v),!1}this.proc=H,this.wireProc(this.proc);let K=this.backendLabel(),N=this.selectedProvider==="claude"?this.waitForProcEarlyFailure(this.proc,wy):this.waitForProcResumeInit(this.proc,()=>{g(`session "${this.sessionName}": ${this.selectedProvider} resume init still pending after ${gy/1000}s`),J?.(this.withModel(`⏳ 仍在等待 ${K} init 确认 thread=${z}…`))});J?.(this.selectedProvider==="claude"?`⏳ 检查 ${K} 恢复启动`:`⏳ 等待 ${K} init 确认`),this.proc.sendInitialize();let L=await N;if(L.state==="error"||L.state==="exit"||L.state==="timeout"){let O=L.error?Hx(L.error):L.state;g(`session "${this.sessionName}": ${this.selectedProvider} resume failed: ${O}`);let v=L.state==="timeout"?`❌ ${K} 恢复超时`:`❌ ${K} 恢复失败: ${O}`;if(J?.(v),G)await C0(this.chatId,v);return await this.proc?.kill(1000).catch(()=>{}),this.proc=null,this.status="stopped",this.opts.onLifecycleChange?.(),await W(v),!1}let D=this.withModel(this.withWorktreeInstructionNotice(this.selectedProvider==="claude"&&L.state==="ready"?`✅ 已准备恢复上一会话 thread=${z}…`:`✅ 已恢复上一会话 thread=${z}…`));if(J?.(D),G)await C0(this.chatId,D);return this.status="idle",this.startedAt=Date.now(),this.opts.onLifecycleChange?.(),await W(D),!0}else{if(x){if(J?.("⚠️ 没有可恢复的上一会话,将以新会话启动"),G)await C0(this.chatId,"⚠️ 没有可恢复的上一会话,将以新会话启动")}return this.resetFreshConversationState(),await this.start({...Z,freshConversationStateAlreadyReset:!0})}}worktreeProjectName(){return g3(this)}worktreeProjectDir(){return j5(this)}spawnDeveloperInstructions(){return Iy(this)}worktreeInstructionLoadedNotice(){return cN(this)}withWorktreeInstructionNotice(x){return cy(this,x)}worktreeExtraInstruction(){return Tz(this)}runWorktreeCommand(x,Z){return qy(this,x,Z)}showWorktrees(){return qN(this)}showTasklistPanel(){return Xy(this)}onTasklistEnable(){return zy(this)}onTasklistDeletePrompt(x){return Ky(this,x)}onTasklistDeleteConfirm(x){return Wy(this,x)}runCompactCommand(){return ef(this)}showModelPanel(){return Zy(this)}onModelSelect(x,Z="",Q="",J=null){return Qy(this,x,Z,Q,J)}onModelEffortSelect(x,Z,Q="",J="",X=""){return SN(this,x,Z,Q,J,X)}onWorktreeDisband(x){return Ey(this,x)}runCommand(x,Z=""){return rf(this,x,Z)}async buildConsoleOpts(x,Z){let Q=await Hu();return{sessionName:this.sessionName,status:this.status,provider:this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),worktreeInstructionNotice:this.worktreeInstructionLoadedNotice(),peers:[...T5.all].filter((J)=>J.isRunning()).map((J)=>({...J.peerSnapshot(),isCurrent:J===this})),usage:x,glmUsage:Z,sysinfo:Q}}async buildConsoleCard(x){return fU(await this.buildConsoleOpts(x))}async patchConsoleUsage(x){let Z=await this.buildConsoleOpts(void 0);if(this.currentProvider()==="claude")Z.glmUsage=await FN();else Z.usage=await lu();await Zx(x,$0.consoleUsage,NY(Z))}patchConsoleUsageLater(x){(async()=>{try{await this.patchConsoleUsage(x)}catch(Z){g(`session "${this.sessionName}": consoleUsage patch failed: ${Z}`)}finally{try{await d1(x)}catch(Z){g(`session "${this.sessionName}": console card dispose failed: ${Z}`)}}})()}async replaceStatusCardWithConsole(x,Z){x.timer.stop();let Q=x.timer.elapsedSec(),J=await this.buildConsoleOpts(void 0);await b8(x.cardId),await Zx(x.cardId,$0.footer,zz(J,$0.footer)),await kx(x.cardId,Xz(J),{type:"insert_after",targetElementId:$0.footer}),await kx(x.cardId,Kz(J.sysinfo),{type:"insert_after",targetElementId:$0.consoleProjects}),await kx(x.cardId,NY(J),{type:"insert_after",targetElementId:$0.consoleHost}),v1(x.cardId),await b1(x.cardId,x4({durationSec:Q,suffix:Z})),this.patchConsoleUsageLater(x.cardId)}async showConsole(){let x=await this.buildConsoleCard(void 0),Z=await mx(this.chatId,x);if(!Z)return;(async()=>{let Q="";try{Q=await a8(Z),await this.patchConsoleUsage(Q)}catch(J){g(`session "${this.sessionName}": consoleUsage patch failed: ${J}`)}finally{if(Q)try{await d1(Q)}catch(J){g(`session "${this.sessionName}": console card dispose failed: ${J}`)}}})()}interrupt(){if(!this.proc)return;g(`session "${this.sessionName}": interrupt`),this.proc.sendInterrupt()}async startColdUserTurn(x,Z,Q){this.openingTurn=!0,this.resetFreshConversationState(),this.pendingTurnInputs.push(x);try{await this.openTurnCard(Q,"user_message",{initialFooter:"Waiting...(0s)",startThinking:!1,directStart:!0});let J=this.currentTurn;if(!J)return;let X=this.startFooterTimer(J.cardId,`\uD83D\uDE80 启动 ${this.backendLabel()}`,(G)=>this.withModel(G)),z=`\uD83D\uDE80 启动 ${this.backendLabel()}`,Y=await this.start({announce:!1,freshConversationStateAlreadyReset:!0,onStatus:(G)=>{z=G,X.setStatus(G)}});if(X.stop(),await b8(J.cardId),!Y){this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),await this.closeTurnCard(z.startsWith("❌")?z:"❌ 启动失败",{forcePush:!0});return}this.startThinkingFooter(J),this.proc.sendUserText(Z,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}async onUserMessage(x,Z=[],Q="",J=""){if(this.clearStaleIdleQueueState("user_message"),this.startingAgy||this.runningAgy){await C0(this.chatId,"⏳ agy 任务正在执行;请等待完成,或发送 stop 打断后再继续。");return}if(this.proc?.isAlive()&&this.proc.provider!==this.selectedProvider&&!this.currentTurn&&!this.openingTurn&&this.pendingUserMessageCount===0&&this.pendingMidTurnMsgs.length===0)await this.stopIdleMismatchedProcess();let X=this.currentTurn!==null||this.openingTurn||this.pendingUserMessageCount>0||this.pendingMidTurnMsgs.length>0;this.lastUserOpenId=Q;let Y=(Z.length?Z.map((W)=>`[file: ${W}]`).join(" ")+`
|
|
330
|
-
`:"")+x,G=(W)=>{this.pendingReactionIds.set(W,""),(async()=>{let H=await E1(W,"OneSecond");if(!H)return;if(this.pendingReactionIds.has(W))this.pendingReactionIds.set(W,H);else if(this.currentBatchReactionIds.has(W))this.currentBatchReactionIds.set(W,H);else E8(W,H)})()};if(!this.isRunning()){if(this.openingTurn||this.currentTurn){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}await this.startColdUserTurn(x,Y,Q);return}if(this.currentTurn!==null){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(!this.openingTurn&&this.initCount>=1){this.openingTurn=!0;try{if(this.pendingTurnInputs.push(x),await this.openTurnCard(Q,"user_message"),!this.currentTurn)return;this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}return}if(this.openingTurn||this.pendingUserMessageCount>0){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(this.pendingTurnInputs.push(x),this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,X&&J)G(J)}hasPendingAsk(){return Ef(this)}hasPendingHostAsk(){return mf(this)}onMultiMessageInbound(x,Z,Q,J){return zf(this,x,Z,Q,J)}clearMultiMsgBuffer(x){UN(this,x)}onAskMessageAnswer(x,Z,Q){return bf(this,x,Z,Q)}onHostAskMessageAnswer(x,Z,Q){return nf(this,x,Z,Q)}onAskAnswer(x,Z,Q,J){return kf(this,x,Z,Q,J)}onHostAskAnswer(x,Z,Q,J){return lf(this,x,Z,Q,J)}onAskCustomAnswer(x,Z,Q,J){return BN(this,x,Z,Q,J)}onHostAskCustomAnswer(x,Z,Q,J){return RN(this,x,Z,Q,J)}onPermissionDecision(x,Z,Q){return pf(this,x,Z,Q)}async startHostAskContinuation(x){if(!this.isRunning())throw Error(`${this.backendLabel()} is not running`);if(this.proc?.provider!=="codex")throw Error("askusr host continuation is only supported by Codex");if(this.currentTurn||this.openingTurn)throw Error(`${this.backendLabel()} turn still active`);this.openingTurn=!0;try{if(await this.openTurnCard("","user_message"),!this.currentTurn)throw Error("failed to open continuation turn card");this.proc.sendUserText(x,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}persistResumableSessionId(){let x=this.proc,Z=x?.sessionId;if(!x||!Z)return;if(Ww(this.sessionName,Z,x.provider),x.provider!==this.selectedProvider)return;if(Z===this.lastSessionId)return;this.lastSessionId=Z}bgStore(){return{active:this.backgroundTasks,pending:this.pendingBgTasks}}applyBgStore(x){this.backgroundTasks=x.active,this.pendingBgTasks=x.pending}onMainThreadAdvance(){if(this.pendingBgTasks.length===0)return;this.applyBgStore(QN(this.bgStore())),this.onBackgroundTaskChanged()}bgTaskOwns(x){return this.backgroundTasks.some((Z)=>Z.toolUseId===x)||this.pendingBgTasks.some((Z)=>Z.toolUseId===x)}onBackgroundTaskChanged(){if(!O5(this.backgroundTasks)){if(this.backgroundCard)this.settleBackgroundCard();return}if(!this.backgroundCard&&!this.openingBackground){this.openingBackground=!0,this.openBackgroundCard().finally(()=>{this.openingBackground=!1});return}this.scheduleBackgroundRefresh()}async openBackgroundCard(){if(this.backgroundCard)return;let x=KN(this.backgroundTasks),Z=await mx(this.chatId,x);if(!Z){g(`session "${this.sessionName}": background card send failed`);return}let Q;try{Q=await a8(Z)}catch(J){g(`session "${this.sessionName}": background card id_convert failed: ${J}`);return}g4(Q,this.backgroundTasks.length),this.backgroundCard={messageId:Z,cardId:Q},this.backgroundDetailAdded=new Set(this.backgroundTasks.map((J)=>J.id)),g(`session "${this.sessionName}": background card opened cardId=${Q.slice(0,12)} tasks=${this.backgroundTasks.length}`),this.startBackgroundRefreshTick()}startBackgroundRefreshTick(){if(this.backgroundRefreshTick)return;this.backgroundRefreshTick=setInterval(()=>{if(!this.backgroundCard)return;if(!O5(this.backgroundTasks))return;this.refreshBackgroundCardFull()},rU0)}stopBackgroundRefreshTick(){if(this.backgroundRefreshTick)clearInterval(this.backgroundRefreshTick),this.backgroundRefreshTick=null}scheduleBackgroundRefresh(){if(!this.backgroundCard)return;if(this.backgroundRefreshTimer)return;this.backgroundRefreshTimer=setTimeout(()=>{this.backgroundRefreshTimer=null,this.refreshBackgroundCardFull()},1500)}refreshBackgroundCardFull(){let x=this.backgroundCard;if(!x)return;let Z=Date.now();for(let Q of this.backgroundTasks)if(!this.backgroundDetailAdded.has(Q.id))this.backgroundDetailAdded.add(Q.id),kx(x.cardId,B5(Q,Z));else Zx(x.cardId,RY.panel(Q.id),B5(Q,Z))}async settleBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundCard=null,this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;this.stopBackgroundRefreshTick(),await b8(x.cardId),await R3(x.messageId,Lz(this.backgroundTasks)),v1(x.cardId),await b1(x.cardId,x4({suffix:"\uD83E\uDDED 后台任务已结束"})),await d1(x.cardId),this.backgroundTasks=[],this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card settled cardId=${x.cardId.slice(0,12)}`)}async migrateBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;this.stopBackgroundRefreshTick();let Z=this.backgroundTasks.filter(E3).length;if(await b8(x.cardId),Z>0)await R3(x.messageId,Lz(this.backgroundTasks));else await R3(x.messageId,WN());v1(x.cardId),await b1(x.cardId,x4({suffix:"\uD83E\uDDED 已迁移至新卡"})),await d1(x.cardId),this.backgroundCard=null,this.backgroundTasks=this.backgroundTasks.filter((Q)=>!E3(Q)),this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card migrated cardId=${x.cardId.slice(0,12)} terminal=${Z} active=${this.backgroundTasks.length}`)}wireProc(x){x.on("error",(Z)=>{g(`session "${this.sessionName}": ${x.provider} process error: ${Z}`)}),x.on("init",()=>{if(this.persistResumableSessionId(),this.initCount++,g(`session "${this.sessionName}": SDK init#${this.initCount} pendingCount=${this.pendingUserMessageCount} midBuffer=${this.pendingMidTurnMsgs.length} currentTurn=${this.currentTurn?"yes":"no"} openingTurn=${this.openingTurn}`),this.currentTurn||this.openingTurn)return;let Z=this.pendingUserMessageCount>0,Q=!Z&&this.bgResumePending;if(!Z&&!Q)return;this.bgResumePending=!1;let J=this.lastUserOpenId;if(Z)this.pendingUserMessageCount=0,this.currentBatchReactionIds=this.pendingReactionIds,this.pendingReactionIds=new Map;this.openingTurn=!0,this.sawResultWhileOpening=!1,(async()=>{try{if(await this.openTurnCard(J,Z?"user_message":"bg_task_resume"),!this.currentTurn)if(Z)g(`session "${this.sessionName}": init-path openTurnCard failed — sendInterrupt + release reactions`),this.proc?.sendInterrupt(),this.releaseAllReactions();else if(this.bgResumeCardless=!0,this.sawResultWhileOpening)this.sawResultWhileOpening=!1,g(`session "${this.sessionName}": bg-resume openTurnCard failed, result already arrived — flushing orphan now`),this.flushOrphanAssistantToChat("bg-resume open failed, result already arrived");else g(`session "${this.sessionName}": bg-resume openTurnCard failed — orphan text flush will cover the output`);else if(this.sawResultWhileOpening)this.sawResultWhileOpening=!1,g(`session "${this.sessionName}": result raced card-open — closing freshly-opened turn card now`),await this.closeTurnCard(void 0,{hasFreshResult:!0}),this.status="idle";else this.status="working"}finally{this.openingTurn=!1}})()}),x.on("turn_started",()=>{this.persistResumableSessionId();let Z=this.proc?.lastTotalUsage;if(this.usageTotalsSeedUnknown&&!Z){this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;return}this.currentTurnUsageBaseline=Z?{...Z}:null,this.currentTurnUsageBaselineKnown=!0}),x.on("token_usage",({totalUsage:Z})=>{if(this.persistResumableSessionId(),Z)this.usageTotalsSeedUnknown=!1}),x.on("turn_plan_updated",(Z)=>{this.handleTurnPlanUpdated(Z)}),x.on("plan_delta",(Z)=>{this.handlePlanDelta(Z)}),x.on("context_compacted",(Z)=>{this.handleContextCompacted(Z)}),x.on("rate_limits_updated",(Z)=>{_u(Z)}),x.on("thread_goal_updated",(Z)=>{this.handleThreadGoalUpdated(Z)}),x.on("thread_goal_cleared",()=>{this.handleThreadGoalCleared()}),x.on("assistant_text",({text:Z})=>{this.appendAssistant(Z)}),x.on("assistant_block_stop",()=>{this.finalizeCurrentAssistantSegment(),this.onMainThreadAdvance()}),x.on("tool_use",({id:Z,name:Q,input:J,parentToolUseId:X})=>{if(If(this,Z,Q,J),!X)this.onMainThreadAdvance();if(!X&&(Q==="Task"||Q==="Agent"))this.lastMainTaskToolUseId=Z;if(X&&this.bgTaskOwns(X))this.applyBgStore(XN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("tool_result",({tool_use_id:Z,content:Q,is_error:J,parentToolUseId:X})=>{if(cf(this,Z,Q,J),X&&this.bgTaskOwns(X))this.applyBgStore(zN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("can_use_tool",(Z)=>{sf(this,Z)}),x.on("hook_callback",(Z)=>{this.proc?.sendHookResponse(Z.request_id,{})}),x.on("result",()=>{if(this.persistResumableSessionId(),this.accumulateResultStats(),this.openingTurn)this.sawResultWhileOpening=!0;if(this.userInterrupted){this.userInterrupted=!1,this.discardOrphanAssistant(),this.bgResumePending=!1;let W=this.proc?.lastResult.subtype??"unknown",H=this.proc?.lastResult.is_error===!0;g(`session "${this.sessionName}": SDK result after user stop subtype=${W} isError=${H} — ignored`),this.status="idle";return}if(!this.currentTurn&&!this.openingTurn)this.flushOrphanAssistantToChat("result with no turn card");let Z=this.pendingMidTurnMsgs.length>0,Q=this.proc?.lastResult.is_error===!0,J=this.proc?.lastResult.subtype??"success",X=this.pendingHostAsks.size>0,z,Y=!1,G=this.proc?this.backendLabel(this.proc.provider):this.backendLabel();if(Z&&!X)z=Q?`⚠️ ${G} ${J},用户已介入`:"\uD83D\uDCE8 转交新卡";else if(Q)z=`⚠️ ${G} ${J}`,Y=!0;if(g(`session "${this.sessionName}": SDK result subtype=${J} isError=${Q} midBuffer=${this.pendingMidTurnMsgs.length} forcePush=${Y}`),this.closeTurnCard(z,{forcePush:Y,hasFreshResult:!0}),this.status="idle",yf(this),Z&&!X)this.drainMidTurnAndOpen()}),x.on("bg_task_started",(Z)=>{let Q=Z.tool_use_id??this.lastMainTaskToolUseId??void 0;g(`session "${this.sessionName}": bg_task_started task=${Z.task_id} type=${Z.task_type??"-"} subagent=${Z.subagent_type??"-"} toolUseId=${Q?.slice(0,8)??"-"} desc=${(Z.description??"").slice(0,40)}`),this.applyBgStore(xN(this.bgStore(),{...Z,tool_use_id:Q})),this.onBackgroundTaskChanged()}),x.on("bg_task_progress",(Z)=>{this.applyBgStore(YN(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_updated",(Z)=>{this.applyBgStore(ZN(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_settled",(Z)=>{if(g(`session "${this.sessionName}": bg_task_settled task=${Z.task_id} status=${Z.status}`),this.applyBgStore(JN(this.bgStore(),Z)),this.onBackgroundTaskChanged(),!this.currentTurn&&!this.openingTurn&&this.initCount>=1)this.bgResumePending=!0}),x.on("exit",({code:Z,signal:Q,expected:J})=>{if(g(`session "${this.sessionName}": ${x.provider} exited code=${Z} signal=${Q} expected=${J}`),this.proc!==x){g(`session "${this.sessionName}": ignore stale ${x.provider} exit; current=${this.proc?.provider??"none"}`);return}if(this.proc=null,this.flushOrphanAssistantToChat("process exit"),this.bgResumePending=!1,this.bgResumeCardless=!1,this.sawResultWhileOpening=!1,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("process exit"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.userInterrupted=!1,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),!J&&Z!==0&&Q!=="SIGTERM")C0(this.chatId,`⚠️ ${this.backendLabel(x.provider)} 异常退出 (code=${Z}, signal=${Q})。回复任意消息将重新启动。`)})}accumulateResultStats(){let x=this.proc?.lastResult;if(!x)return;let Z=this.currentTurnUsageBaselineKnown?pV(this.proc?.lastTotalUsage,this.currentTurnUsageBaseline):this.proc?.provider==="claude"&&x.usage?{...x.usage}:null;this.lastTurnUsage=Z,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;let Q=sV(Z),J=this.proc?.provider==="claude"?0:x.cost_delta_usd??0,X=x.duration_ms??0;if(Q!=null)this.cumStats.tokens+=Q;this.cumStats.costUsd+=J,this.cumStats.turns+=x.num_turns??1,this.lastTurnDelta={tokens:Q,costUsd:J,durationMs:X}}currentContextTokens(){if(this.proc?.provider==="claude")return this.proc?.lastContextTokens??null;let x=this.proc?.lastUsage;return wX(x)}contextLimitForDisplay(){return uX(this.proc?.lastContextWindow)}async drainMidTurnAndOpen(){if(this.pendingMidTurnMsgs.length===0)return;let x=this.pendingMidTurnMsgs;this.pendingMidTurnMsgs=[],this.openingTurn=!0;try{for(let X of x)if(this.pendingTurnInputs.push(X.text),X.msgId){let z=this.pendingReactionIds.get(X.msgId)??"";this.currentBatchReactionIds.set(X.msgId,z),this.pendingReactionIds.delete(X.msgId)}let Z=x.map((X)=>X.wireText).join(`
|
|
330
|
+
`:"")+x,G=(W)=>{this.pendingReactionIds.set(W,""),(async()=>{let H=await E1(W,"OneSecond");if(!H)return;if(this.pendingReactionIds.has(W))this.pendingReactionIds.set(W,H);else if(this.currentBatchReactionIds.has(W))this.currentBatchReactionIds.set(W,H);else E8(W,H)})()};if(!this.isRunning()){if(this.openingTurn||this.currentTurn){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}await this.startColdUserTurn(x,Y,Q);return}if(this.currentTurn!==null){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(!this.openingTurn&&this.initCount>=1){this.openingTurn=!0;try{if(this.pendingTurnInputs.push(x),await this.openTurnCard(Q,"user_message"),!this.currentTurn)return;this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}return}if(this.openingTurn||this.pendingUserMessageCount>0){if(this.pendingMidTurnMsgs.push({text:x,wireText:Y,userOpenId:Q,msgId:J}),J)G(J);return}if(this.pendingTurnInputs.push(x),this.proc.sendUserText(Y,[]),this.pendingUserMessageCount++,X&&J)G(J)}hasPendingAsk(){return Ef(this)}hasPendingHostAsk(){return mf(this)}onMultiMessageInbound(x,Z,Q,J){return zf(this,x,Z,Q,J)}clearMultiMsgBuffer(x){UN(this,x)}onAskMessageAnswer(x,Z,Q){return bf(this,x,Z,Q)}onHostAskMessageAnswer(x,Z,Q){return nf(this,x,Z,Q)}onAskAnswer(x,Z,Q,J){return kf(this,x,Z,Q,J)}onHostAskAnswer(x,Z,Q,J){return lf(this,x,Z,Q,J)}onAskCustomAnswer(x,Z,Q,J){return BN(this,x,Z,Q,J)}onHostAskCustomAnswer(x,Z,Q,J){return RN(this,x,Z,Q,J)}onPermissionDecision(x,Z,Q){return pf(this,x,Z,Q)}async startHostAskContinuation(x){if(!this.isRunning())throw Error(`${this.backendLabel()} is not running`);if(this.proc?.provider!=="codex")throw Error("askusr host continuation is only supported by Codex");if(this.currentTurn||this.openingTurn)throw Error(`${this.backendLabel()} turn still active`);this.openingTurn=!0;try{if(await this.openTurnCard("","user_message"),!this.currentTurn)throw Error("failed to open continuation turn card");this.proc.sendUserText(x,[]),this.pendingUserMessageCount++,this.status="working"}finally{this.openingTurn=!1}}persistResumableSessionId(){let x=this.proc,Z=x?.sessionId;if(!x||!Z)return;if(Ww(this.sessionName,Z,x.provider),x.provider!==this.selectedProvider)return;if(Z===this.lastSessionId)return;this.lastSessionId=Z}bgStore(){return{active:this.backgroundTasks,pending:this.pendingBgTasks}}applyBgStore(x){this.backgroundTasks=x.active,this.pendingBgTasks=x.pending}onMainThreadAdvance(){if(this.pendingBgTasks.length===0)return;this.applyBgStore(QN(this.bgStore())),this.onBackgroundTaskChanged()}bgTaskOwns(x){return this.backgroundTasks.some((Z)=>Z.toolUseId===x)||this.pendingBgTasks.some((Z)=>Z.toolUseId===x)}onBackgroundTaskChanged(){if(!O5(this.backgroundTasks)){if(this.backgroundCard)this.settleBackgroundCard();return}if(!this.backgroundCard&&!this.openingBackground){this.openingBackground=!0,this.openBackgroundCard().finally(()=>{this.openingBackground=!1});return}this.scheduleBackgroundRefresh()}async openBackgroundCard(){if(this.backgroundCard)return;let x=KN(this.backgroundTasks),Z=await mx(this.chatId,x);if(!Z){g(`session "${this.sessionName}": background card send failed`);return}let Q;try{Q=await a8(Z)}catch(J){g(`session "${this.sessionName}": background card id_convert failed: ${J}`);return}g4(Q,this.backgroundTasks.length),this.backgroundCard={messageId:Z,cardId:Q},this.backgroundDetailAdded=new Set(this.backgroundTasks.map((J)=>J.id)),g(`session "${this.sessionName}": background card opened cardId=${Q.slice(0,12)} tasks=${this.backgroundTasks.length}`),this.startBackgroundRefreshTick()}startBackgroundRefreshTick(){if(this.backgroundRefreshTick)return;this.backgroundRefreshTick=setInterval(()=>{if(!this.backgroundCard)return;if(!O5(this.backgroundTasks))return;this.refreshBackgroundCardFull()},rU0)}stopBackgroundRefreshTick(){if(this.backgroundRefreshTick)clearInterval(this.backgroundRefreshTick),this.backgroundRefreshTick=null}scheduleBackgroundRefresh(){if(!this.backgroundCard)return;if(this.backgroundRefreshTimer)return;this.backgroundRefreshTimer=setTimeout(()=>{this.backgroundRefreshTimer=null,this.refreshBackgroundCardFull()},1500)}refreshBackgroundCardFull(){let x=this.backgroundCard;if(!x)return;let Z=Date.now();for(let Q of this.backgroundTasks)if(!this.backgroundDetailAdded.has(Q.id))this.backgroundDetailAdded.add(Q.id),kx(x.cardId,B5(Q,Z));else Zx(x.cardId,RY.panel(Q.id),B5(Q,Z))}async settleBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundCard=null,this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;this.stopBackgroundRefreshTick(),await b8(x.cardId),await R3(x.messageId,Lz(this.backgroundTasks)),v1(x.cardId),await b1(x.cardId,x4({suffix:"\uD83E\uDDED 后台任务已结束"})),await d1(x.cardId),this.backgroundTasks=[],this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card settled cardId=${x.cardId.slice(0,12)}`)}async migrateBackgroundCard(){let x=this.backgroundCard;if(!x)return;if(this.backgroundRefreshTimer)clearTimeout(this.backgroundRefreshTimer),this.backgroundRefreshTimer=null;this.stopBackgroundRefreshTick();let Z=this.backgroundTasks.filter(E3).length;if(await b8(x.cardId),Z>0)await R3(x.messageId,Lz(this.backgroundTasks));else await R3(x.messageId,WN());v1(x.cardId),await b1(x.cardId,x4({suffix:"\uD83E\uDDED 已迁移至新卡"})),await d1(x.cardId),this.backgroundCard=null,this.backgroundTasks=this.backgroundTasks.filter((Q)=>!E3(Q)),this.backgroundDetailAdded.clear(),g(`session "${this.sessionName}": background card migrated cardId=${x.cardId.slice(0,12)} terminal=${Z} active=${this.backgroundTasks.length}`)}wireProc(x){x.on("error",(Z)=>{g(`session "${this.sessionName}": ${x.provider} process error: ${Z}`)}),x.on("init",()=>{if(this.persistResumableSessionId(),this.initCount++,g(`session "${this.sessionName}": SDK init#${this.initCount} pendingCount=${this.pendingUserMessageCount} midBuffer=${this.pendingMidTurnMsgs.length} currentTurn=${this.currentTurn?"yes":"no"} openingTurn=${this.openingTurn}`),this.currentTurn||this.openingTurn)return;let Z=this.pendingUserMessageCount>0,Q=!Z&&this.bgResumePending;if(!Z&&!Q)return;this.bgResumePending=!1;let J=this.lastUserOpenId;if(Z)this.pendingUserMessageCount=0,this.currentBatchReactionIds=this.pendingReactionIds,this.pendingReactionIds=new Map;this.openingTurn=!0,this.sawResultWhileOpening=!1,(async()=>{try{if(await this.openTurnCard(J,Z?"user_message":"bg_task_resume"),!this.currentTurn)if(Z)g(`session "${this.sessionName}": init-path openTurnCard failed — sendInterrupt + release reactions`),this.proc?.sendInterrupt(),this.releaseAllReactions();else if(this.bgResumeCardless=!0,this.sawResultWhileOpening)this.sawResultWhileOpening=!1,g(`session "${this.sessionName}": bg-resume openTurnCard failed, result already arrived — flushing orphan now`),this.flushOrphanAssistantToChat("bg-resume open failed, result already arrived");else g(`session "${this.sessionName}": bg-resume openTurnCard failed — orphan text flush will cover the output`);else if(this.sawResultWhileOpening)this.sawResultWhileOpening=!1,g(`session "${this.sessionName}": result raced card-open — closing freshly-opened turn card now`),await this.closeTurnCard(void 0,{hasFreshResult:!0}),this.status="idle";else this.status="working"}finally{this.openingTurn=!1}})()}),x.on("turn_started",()=>{this.persistResumableSessionId();let Z=this.proc?.lastTotalUsage;if(this.usageTotalsSeedUnknown&&!Z){this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;return}this.currentTurnUsageBaseline=Z?{...Z}:null,this.currentTurnUsageBaselineKnown=!0}),x.on("token_usage",({totalUsage:Z})=>{if(this.persistResumableSessionId(),Z)this.usageTotalsSeedUnknown=!1}),x.on("turn_plan_updated",(Z)=>{this.handleTurnPlanUpdated(Z)}),x.on("plan_delta",(Z)=>{this.handlePlanDelta(Z)}),x.on("context_compacted",(Z)=>{this.handleContextCompacted(Z)}),x.on("rate_limits_updated",(Z)=>{if(x.provider==="codex")_u(Z)}),x.on("thread_goal_updated",(Z)=>{this.handleThreadGoalUpdated(Z)}),x.on("thread_goal_cleared",()=>{this.handleThreadGoalCleared()}),x.on("assistant_text",({text:Z})=>{this.appendAssistant(Z)}),x.on("assistant_block_stop",()=>{this.finalizeCurrentAssistantSegment(),this.onMainThreadAdvance()}),x.on("tool_use",({id:Z,name:Q,input:J,parentToolUseId:X})=>{if(If(this,Z,Q,J),!X)this.onMainThreadAdvance();if(!X&&(Q==="Task"||Q==="Agent"))this.lastMainTaskToolUseId=Z;if(X&&this.bgTaskOwns(X))this.applyBgStore(XN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("tool_result",({tool_use_id:Z,content:Q,is_error:J,parentToolUseId:X})=>{if(cf(this,Z,Q,J),X&&this.bgTaskOwns(X))this.applyBgStore(zN({active:this.backgroundTasks,pending:this.pendingBgTasks},X,Z,Q,J)),this.onBackgroundTaskChanged()}),x.on("can_use_tool",(Z)=>{sf(this,Z)}),x.on("hook_callback",(Z)=>{this.proc?.sendHookResponse(Z.request_id,{})}),x.on("result",()=>{if(this.persistResumableSessionId(),this.accumulateResultStats(),this.openingTurn)this.sawResultWhileOpening=!0;if(this.userInterrupted){this.userInterrupted=!1,this.discardOrphanAssistant(),this.bgResumePending=!1;let W=this.proc?.lastResult.subtype??"unknown",H=this.proc?.lastResult.is_error===!0;g(`session "${this.sessionName}": SDK result after user stop subtype=${W} isError=${H} — ignored`),this.status="idle";return}if(!this.currentTurn&&!this.openingTurn)this.flushOrphanAssistantToChat("result with no turn card");let Z=this.pendingMidTurnMsgs.length>0,Q=this.proc?.lastResult.is_error===!0,J=this.proc?.lastResult.subtype??"success",X=this.pendingHostAsks.size>0,z,Y=!1,G=this.proc?this.backendLabel(this.proc.provider):this.backendLabel();if(Z&&!X)z=Q?`⚠️ ${G} ${J},用户已介入`:"\uD83D\uDCE8 转交新卡";else if(Q)z=`⚠️ ${G} ${J}`,Y=!0;if(g(`session "${this.sessionName}": SDK result subtype=${J} isError=${Q} midBuffer=${this.pendingMidTurnMsgs.length} forcePush=${Y}`),this.closeTurnCard(z,{forcePush:Y,hasFreshResult:!0}),this.status="idle",yf(this),Z&&!X)this.drainMidTurnAndOpen()}),x.on("bg_task_started",(Z)=>{let Q=Z.tool_use_id??this.lastMainTaskToolUseId??void 0;g(`session "${this.sessionName}": bg_task_started task=${Z.task_id} type=${Z.task_type??"-"} subagent=${Z.subagent_type??"-"} toolUseId=${Q?.slice(0,8)??"-"} desc=${(Z.description??"").slice(0,40)}`),this.applyBgStore(xN(this.bgStore(),{...Z,tool_use_id:Q})),this.onBackgroundTaskChanged()}),x.on("bg_task_progress",(Z)=>{this.applyBgStore(YN(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_updated",(Z)=>{this.applyBgStore(ZN(this.bgStore(),Z)),this.onBackgroundTaskChanged()}),x.on("bg_task_settled",(Z)=>{if(g(`session "${this.sessionName}": bg_task_settled task=${Z.task_id} status=${Z.status}`),this.applyBgStore(JN(this.bgStore(),Z)),this.onBackgroundTaskChanged(),!this.currentTurn&&!this.openingTurn&&this.initCount>=1)this.bgResumePending=!0}),x.on("exit",({code:Z,signal:Q,expected:J})=>{if(g(`session "${this.sessionName}": ${x.provider} exited code=${Z} signal=${Q} expected=${J}`),this.proc!==x){g(`session "${this.sessionName}": ignore stale ${x.provider} exit; current=${this.proc?.provider??"none"}`);return}if(this.proc=null,this.flushOrphanAssistantToChat("process exit"),this.bgResumePending=!1,this.bgResumeCardless=!1,this.sawResultWhileOpening=!1,this.stopFooterStatus(this.currentTurn),this.currentTurn=null,this.clearMultiMsgBuffer("process exit"),this.pendingUserMessageCount=0,this.pendingMidTurnMsgs=[],this.pendingTurnInputs=[],this.lastUserOpenId="",this.releaseAllReactions(),this.initCount=0,this.openingTurn=!1,this.pendingAsks.clear(),this.pendingHostAsks.clear(),this.pendingPermissions.clear(),this.userInterrupted=!1,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1,this.usageTotalsSeedUnknown=!1,this.status="stopped",this.opts.onLifecycleChange?.(),!J&&Z!==0&&Q!=="SIGTERM")C0(this.chatId,`⚠️ ${this.backendLabel(x.provider)} 异常退出 (code=${Z}, signal=${Q})。回复任意消息将重新启动。`)})}accumulateResultStats(){let x=this.proc?.lastResult;if(!x)return;let Z=this.currentTurnUsageBaselineKnown?pV(this.proc?.lastTotalUsage,this.currentTurnUsageBaseline):this.proc?.provider==="claude"&&x.usage?{...x.usage}:null;this.lastTurnUsage=Z,this.currentTurnUsageBaseline=null,this.currentTurnUsageBaselineKnown=!1;let Q=sV(Z),J=this.proc?.provider==="claude"?0:x.cost_delta_usd??0,X=x.duration_ms??0;if(Q!=null)this.cumStats.tokens+=Q;this.cumStats.costUsd+=J,this.cumStats.turns+=x.num_turns??1,this.lastTurnDelta={tokens:Q,costUsd:J,durationMs:X}}currentContextTokens(){if(this.proc?.provider==="claude")return this.proc?.lastContextTokens??null;let x=this.proc?.lastUsage;return wX(x)}contextLimitForDisplay(){return uX(this.proc?.lastContextWindow)}async drainMidTurnAndOpen(){if(this.pendingMidTurnMsgs.length===0)return;let x=this.pendingMidTurnMsgs;this.pendingMidTurnMsgs=[],this.openingTurn=!0;try{for(let X of x)if(this.pendingTurnInputs.push(X.text),X.msgId){let z=this.pendingReactionIds.get(X.msgId)??"";this.currentBatchReactionIds.set(X.msgId,z),this.pendingReactionIds.delete(X.msgId)}let Z=x.map((X)=>X.wireText).join(`
|
|
331
331
|
|
|
332
332
|
`);this.proc.sendUserText(Z,[]),this.pendingUserMessageCount++;let J=x[x.length-1]?.userOpenId??this.lastUserOpenId;await this.openTurnCard(J,"user_message"),this.status="working"}finally{this.openingTurn=!1}}async openTurnCard(x,Z,Q={}){if(this.bgResumePending=!1,this.backgroundCard&&O5(this.backgroundTasks))try{await this.migrateBackgroundCard(),this.pendingRebuildBackgroundCard=!0}catch(L){g(`session "${this.sessionName}": background migrate failed (non-blocking): ${L}`)}let J=++this.turnCounter,X=this.pendingTurnInputs;this.pendingTurnInputs=[],g(`session "${this.sessionName}": openTurnCard turn=${J} trigger=${Z} inputs=${X.length}`);let z=this.withModel(Q.initialFooter??"Waiting...(0s)"),Y=Yz({sessionName:this.sessionName,turn:J,provider:this.proc?.provider??this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),kind:Z,userInputs:X,initialFooter:z,directStart:Q.directStart}),G=await mx(this.chatId,Y);if(!G){if(g(`session "${this.sessionName}": openTurnCard sendCard EXHAUSTED retries — surfacing via raw text`),Z==="user_message")await ux(this.chatId,"❌ 创建对话卡片失败 (Feishu SDK 重试 3 次后仍连不上)。你这条消息没能送到 Codex,请稍后重发。");return}let W;try{W=await a8(G)}catch(L){g(`session "${this.sessionName}": id_convert failed: ${L}`);return}let H=(Z==="bg_task_resume"?1:0)+(X.length>0?1:0)+1;g4(W,H,(L)=>this.onCardWriteFailure(L));let K={cardId:W,messageId:G,userOpenId:x,trigger:Z,toolCount:0,toolByUseId:new Map,planSteps:[],planExplanation:null,planUpdateCount:0,goalUpdateCount:0,contextCompactCount:0,contextCompactionPending:new Map,readBatches:new Map,openReadBatchI:null,taskCreateI:null,taskUpdateI:null,taskBoardResetThisTurn:!1,taskLiveInserted:!1,assistantSegmentCount:0,currentAssistantSegmentId:null,currentAssistantText:"",segmentTexts:new Map,startedAt:Date.now(),footerStatusHandle:null,footerStatusStartedAt:0,footerStatusLabel:null,rotating:null,rotateCount:0,failureRotateCount:0,rotateGivenUp:!1,outboundSeenPaths:new Set,outboundSentPaths:new Set,hostAskMarkersSeen:new Set};if(this.currentTurn=K,Q.startThinking!==!1)this.startThinkingFooter(K);let N=this.takeOrphanAssistantText();if(N)this.appendAssistant(N),this.finalizeCurrentAssistantSegment();if(this.pendingRebuildBackgroundCard){if(this.pendingRebuildBackgroundCard=!1,O5(this.backgroundTasks))this.openBackgroundCard()}}maybeMidTurnRotate(){let x=this.currentTurn;if(!x)return;if(x.rotating)return;if(UU(x.cardId)<xN0)return;this.startMidTurnRotate(x)}onCardWriteFailure(x){let Z=this.currentTurn;if(!Z)return;if(Z.rotating)return;if(Z.rotateGivenUp)return;if(Z.failureRotateCount>=AN){Z.rotateGivenUp=!0,this.stopFooterStatus(Z),jw(Z.cardId),g(`session "${this.sessionName}": failure-rotate cap (${AN}) hit — giving up, rest of turn is log-only`),ux(this.chatId,`⚠️ 卡片写入失败已触发 ${AN} 次换卡仍未恢复(疑似飞书故障或元素超限),本轮后续输出仅日志可见。`);return}let Q=Cw(x)?`element limit (${x})`:`write failure (code=${x??"n/a"})`;g(`session "${this.sessionName}": ${Q} on card=${Z.cardId.slice(0,8)}… — rotating to fresh card`),Z.failureRotateCount++,this.startMidTurnRotate(Z)}startMidTurnRotate(x){if(x.rotating)return;x.rotateCount++;let{cardId:Z,toolByUseId:Q,readBatches:J}=x;x.rotating=(async()=>{try{g(`session "${this.sessionName}": mid-turn rotate triggered card=${Z.slice(0,8)}… elementCount=${UU(Z)}`);let X=Yz({sessionName:this.sessionName,turn:this.turnCounter,provider:this.proc?.provider??this.selectedProvider,model:this.currentModelLabel()??void 0,effort:this.currentEffortLabel(),kind:"card_full",userInputs:[]}),z=await mx(this.chatId,X);if(!z){g(`session "${this.sessionName}": mid-turn rotate sendCard EXHAUSTED — staying on old card,subsequent adds will drop`),await ux(this.chatId,"⚠️ 卡片元素超出飞书上限,本轮后续输出仅日志可见(开新卡失败)。");return}let Y;try{Y=await a8(z)}catch(K){g(`session "${this.sessionName}": mid-turn rotate id_convert failed: ${K}`);return}g4(Y,2,(K)=>this.onCardWriteFailure(K)),this.stopFooterStatus(x),x.cardId=Y,x.messageId=z,x.toolCount=0,x.toolByUseId=new Map,x.readBatches=new Map,x.openReadBatchI=null,x.planUpdateCount=0,x.goalUpdateCount=0;let G=x.currentAssistantSegmentId,W=(x.currentAssistantText??"").trim(),H=x.segmentTexts;if(x.assistantSegmentCount=0,x.currentAssistantSegmentId=null,x.currentAssistantText="",x.segmentTexts=new Map,W)this.startWritingFooter(x);else this.startThinkingFooter(x);if(x.taskLiveInserted)kx(x.cardId,A3(this.taskBoard),{type:"insert_before",targetElementId:$0.footer});for(let[K,N]of H){if(G&&W&&K===G)continue;if(!XY(Z,K))continue;let L=x.assistantSegmentCount++,D=$0.assistant(L);x.segmentTexts.set(D,N),kx(Y,this.completedAssistantElement(D,N),{type:"insert_before",targetElementId:M1(x)})}if(qf(this,Z,Y,Q,J),G&&W){let K=x.assistantSegmentCount++,N=$0.assistant(K);x.currentAssistantSegmentId=N,x.currentAssistantText=W,x.segmentTexts.set(N,W)}try{await b8(Z);for(let[N,L]of H){if(G&&W&&N===G)continue;if(XY(Z,N))continue;await Zx(Z,N,{tag:"markdown",element_id:N,content:this.cleanAssistantTextForDisplay(L).trim()||" "})}let K=x.contextCompactCount>0?` · \uD83D\uDEA8 压缩×${x.contextCompactCount}`:"";await this.replaceFooterContent(Z,this.withModel(`\uD83D\uDCE8 已续至下一张卡 ↓${K}`)),v1(Z),await b1(Z,x4({suffix:"\uD83D\uDCE8 转下一张"})),await d1(Z)}catch(K){g(`session "${this.sessionName}": mid-turn rotate close-old failed: ${K}`)}g(`session "${this.sessionName}": mid-turn rotate done old=${Z.slice(0,8)}… new=${Y.slice(0,8)}…`)}finally{x.rotating=null}})()}addPlanSnapshotOnCurrentTurn(){let x=this.currentTurn;if(!x||x.planSteps.length===0)return;this.maybeMidTurnRotate();let Z=x.cardId,Q=$0.planUpdate(x.planUpdateCount++);kx(Z,bU(x.planSteps,x.planExplanation,"",Q),{type:"insert_before",targetElementId:M1(x)})}addGoalUpdateOnCurrentTurn(x){let Z=this.currentTurn;if(!Z)return;this.maybeMidTurnRotate();let Q=$0.goalUpdate(Z.goalUpdateCount++);kx(Z.cardId,kU(x,Q),{type:"insert_before",targetElementId:M1(Z)})}addGoalClearedOnCurrentTurn(){let x=this.currentTurn;if(!x)return;this.maybeMidTurnRotate();let Z=$0.goalUpdate(x.goalUpdateCount++);kx(x.cardId,{tag:"markdown",element_id:Z,content:`**\uD83C\uDFAF 当前目标**
|
|
333
333
|
|