@oxbit/cli 0.8.0 → 0.9.0

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.
@@ -933,11 +933,11 @@ const $id$1 = "https://oxbit.dev/schemas/settings.v1.schema.json";
933
933
  const title$2 = "Oxbit Settings";
934
934
  const description$2 = "User, private workspace and repository settings. Literal dotted IDs; objects merge recursively, arrays and scalar values replace. Unrecognized extension settings are preserved. Null is a value, not a reset, and must be valid for its setting.";
935
935
  const type$2 = "object";
936
- const properties$2 = /* @__PURE__ */ JSON.parse(`{"$schema":{"type":"string","description":"JSON Schema URI. Ignored when merging effective preferences."},"agentACP.provider":{"title":"Default agent","description":"A built-in agent ID, an ACP Registry agent ID, or custom.","type":"string","default":"codex"},"agentACP.useModifierToSend":{"title":"Send with Ctrl/Cmd+Enter only","description":"Enter inserts a new line and Ctrl/Cmd+Enter sends the message.","type":"boolean","default":false},"agentACP.notifications":{"title":"Agent desktop notifications","description":"Show a system notification when the agent needs input or finishes while Oxbit is in the background.","type":"string","default":"whenHidden","enum":["whenHidden","never"]},"agentACP.codex.command":{"title":"Codex ACP executable","type":"string","default":"npx"},"agentACP.codex.args":{"title":"Codex ACP arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@agentclientprotocol/codex-acp@2.2.2\\"]"},"agentACP.claude.command":{"title":"Claude Agent executable","type":"string","default":"npx"},"agentACP.claude.args":{"title":"Claude Agent arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@agentclientprotocol/claude-agent-acp@0.89.1\\"]"},"agentACP.gemini.command":{"title":"Gemini CLI executable","type":"string","default":"npx"},"agentACP.gemini.args":{"title":"Gemini CLI arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@google/gemini-cli@0.63.0\\",\\"--acp\\"]"},"agentACP.copilot.command":{"title":"GitHub Copilot executable","type":"string","default":"npx"},"agentACP.copilot.args":{"title":"GitHub Copilot arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@github/copilot@1.0.95\\",\\"--acp\\"]"},"agentACP.cursor.command":{"title":"Cursor ACP executable","type":"string","default":"agent"},"agentACP.cursor.args":{"title":"Cursor ACP arguments (JSON array)","type":"string","default":"[\\"acp\\"]"},"agentACP.amp.command":{"title":"Amp Agent ACP executable","type":"string","default":"npx"},"agentACP.amp.args":{"title":"Amp Agent ACP arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"amp-acp@0.10.0\\"]"},"agentACP.custom.name":{"title":"Custom agent name","type":"string","default":""},"agentACP.custom.command":{"title":"Custom agent executable","type":"string","default":""},"agentACP.custom.args":{"title":"Custom agent arguments (JSON array)","type":"string","default":"[]"},"workbench.keymap":{"title":"Keymap","description":"Keyboard layout applied on top of the Oxbit defaults. User keybindings always win.","type":"string","default":"default","enum":["default","vscode","jetbrains","macos","sublime","atom","visual-studio","emacs"]},"runtime.keepAlive":{"title":"Keep Runtime Alive","description":"How long a runtime Oxbit started keeps running after the last client disconnects or its connection heartbeats stop. Terminals and tasks continue while it runs.","type":"string","default":"75s","enum":["75s","15m","1h","8h","untilStopped"]},"runtime.autoReconnect":{"title":"Reconnect Automatically","description":"Reconnect to the runtime after the connection drops, including when the iOS app returns to the foreground.","type":"boolean","default":true},"workbench.tooltipDelay":{"title":"Tooltip Delay","description":"Delay in milliseconds before showing a tooltip on hover. Keyboard focus shows tooltips immediately.","type":"number","default":400,"minimum":0,"maximum":5000},"workbench.colorTheme":{"title":"Color Theme","description":"Specifies the color theme used in the workbench.","type":"string","default":"Graphite (dark)"},"workbench.density":{"title":"Layout Density","description":"Controls row heights, padding and label sizes across the workbench.","type":"string","default":"compact","enum":["compact","comfortable"]},"workbench.sidebarLocation":{"title":"Sidebar Location","description":"Where the primary sidebar is docked.","type":"string","default":"left","enum":["left","right"]},"editor.fontFamily":{"title":"Font Family","description":"Controls the font family of the editor.","type":"string","default":"'JetBrains Mono', ui-monospace, monospace"},"editor.fontSize":{"title":"Font Size","description":"Font size in pixels.","type":"number","default":13,"minimum":8,"maximum":32},"editor.lineHeight":{"title":"Line Height","description":"Line height in pixels. 0 uses 1.55 × font size.","type":"number","default":20,"minimum":0,"maximum":60},"editor.fontLigatures":{"title":"Font Ligatures","description":"Enables font ligatures when the font supports them.","type":"boolean","default":false},"editor.tabSize":{"title":"Tab Size","description":"The number of spaces a tab is equal to.","type":"number","default":2,"minimum":1,"maximum":8},"editor.insertSpaces":{"title":"Insert Spaces","description":"Insert spaces when pressing Tab.","type":"boolean","default":true},"editor.detectIndentation":{"title":"Detect Indentation","description":"Detect Tab Size and Insert Spaces from file contents on open.","type":"boolean","default":true},"editor.renderIndentGuides":{"title":"Indentation Guides","description":"Render vertical guides for each indent level.","type":"boolean","default":true},"editor.wordWrap":{"title":"Word Wrap","description":"Controls how lines should wrap.","type":"string","default":"off","enum":["off","on","bounded"]},"editor.renderWhitespace":{"title":"Render Whitespace","description":"Controls how the editor renders whitespace characters.","type":"string","default":"selection","enum":["none","boundary","selection","all"]},"editor.cursorBlinking":{"title":"Cursor Blinking","description":"Cursor animation style.","type":"string","default":"blink","enum":["blink","smooth","phase","solid"]},"editor.minimap":{"title":"Minimap","description":"Show a minimap of the document.","type":"boolean","default":false},"files.autoSave":{"title":"Auto Save","description":"Controls auto save of editors that have unsaved changes.","type":"string","default":"off","enum":["off","afterDelay","onFocusChange","onWindowChange"]},"files.autoSaveDelay":{"title":"Auto Save Delay","description":"Delay in milliseconds after which an editor is saved automatically. Applies when Auto Save is afterDelay.","type":"number","default":1000,"minimum":100,"maximum":60000},"files.trimTrailingWhitespace":{"title":"Trim Trailing Whitespace","description":"Remove trailing whitespace when saving.","type":"boolean","default":true},"editor.formatOnSave":{"title":"Format On Save","description":"Format a file on save. A formatter must be available.","type":"boolean","default":false},"editor.defaultFormatter":{"title":"Default Formatter","description":"Formatter used when several are available.","type":"string","default":"oxbit.prettier","anyOf":[{"enum":["oxbit.prettier","oxbit.builtin-ts"]},{"type":"string"}]},"terminal.fontSize":{"title":"Terminal Font Size","description":"Font size in pixels for the terminal.","type":"number","default":12,"minimum":8,"maximum":32},"terminal.scrollback":{"title":"Scrollback","description":"Maximum number of lines kept in the terminal buffer.","type":"number","default":5000,"minimum":100,"maximum":100000},"terminal.confirmOnKill":{"title":"Confirm On Kill","description":"Ask before killing a terminal with a running process.","type":"boolean","default":true},"scm.autoFetch":{"title":"Auto Fetch","description":"Periodically fetch from the default remote.","type":"boolean","default":true},"scm.diffLayout":{"title":"Diff Layout","description":"Default layout for diff editors.","type":"string","default":"side-by-side","enum":["side-by-side","inline"]},"files.associations":{"title":"File Associations","description":"Map filename or workspace-relative glob patterns to language IDs. Explicit associations take precedence over built-in detection.","type":"object","default":{},"propertyNames":{"minLength":1,"maxLength":1024},"additionalProperties":{"type":"string","pattern":"^[\\\\w+-]+$"}},"languageServers":{"title":"Language Servers","description":"Configure enabled state, selectors, rootMarkers, executable, args, env, initializationOptions, settings, and priority by server ID. Servers install on first use in a trusted runtime. License keys belong in the runtime-local credential file.","type":"object","default":{},"propertyNames":{"pattern":"^[a-zA-Z0-9._-]+$"},"additionalProperties":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Enable this language server."},"selectors":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"pattern":{"type":"string"},"scheme":{"const":"file"}},"additionalProperties":false,"minProperties":1}},"rootMarkers":{"type":"array","items":{"type":"string","pattern":"^(?!/)(?!.*\\\\\\\\)(?!.*(?:^|/)\\\\.\\\\.(?:/|$))[^\\\\u0000]+$","minLength":1}},"executable":{"type":"string","pattern":"^[^\\\\u0000]*$","minLength":1},"args":{"type":"array","items":{"type":"string","pattern":"^[^\\\\u0000]*$"}},"env":{"type":"object","propertyNames":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"type":"string","pattern":"^[^\\\\u0000]*$"}},"initializationOptions":{"type":"object","additionalProperties":true,"description":"Server-specific initialization options. Store license credentials in the runtime credential file."},"settings":{"type":"object","additionalProperties":true,"description":"Server-specific configuration; nested objects merge across files."},"priority":{"type":"number"}},"additionalProperties":false}},"ui.fontFamily":{"title":"UI Font Family","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":""},"ui.fontSize":{"title":"UI Font Size","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":12,"minimum":8,"maximum":72},"ui.fontWeight":{"title":"UI Font Weight","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":400,"minimum":100,"maximum":900},"ui.fontStyle":{"title":"UI Font Style","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":"normal","enum":["normal","italic","oblique"]},"ui.lineHeight":{"title":"UI Line Height","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":1.4,"minimum":1,"maximum":3},"ui.letterSpacing":{"title":"UI Letter Spacing","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":0,"minimum":-3,"maximum":10},"ui.fontLigatures":{"title":"UI Font Ligatures","description":"Overrides theme typography. Reset restores the theme value.","type":"boolean","default":false},"terminal.fontFamily":{"title":"Terminal Font Family","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":""},"terminal.fontWeight":{"title":"Terminal Font Weight","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":400,"minimum":100,"maximum":900},"terminal.fontStyle":{"title":"Terminal Font Style","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":"normal","enum":["normal","italic","oblique"]},"terminal.lineHeight":{"title":"Terminal Line Height","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":1,"minimum":1,"maximum":3},"terminal.letterSpacing":{"title":"Terminal Letter Spacing","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":0,"minimum":-3,"maximum":10},"terminal.fontLigatures":{"title":"Terminal Font Ligatures","description":"Overrides theme typography. Reset restores the theme value.","type":"boolean","default":false},"workbench.iconTheme":{"title":"File Icon Theme","description":"Select a locally installed icon theme. Unavailable selections use Oxbit defaults until restored.","type":"string","default":"oxbit.default"},"workbench.productIconTheme":{"title":"Product Icon Theme","description":"Select a locally installed icon theme. Unavailable selections use Oxbit defaults until restored.","type":"string","default":"oxbit.default"},"editor.semanticHighlighting":{"title":"Semantic Highlighting","description":"Color symbols using language server information.","type":"boolean","default":true},"editor.inlayHints.types":{"title":"Type Inlay Hints","description":"Show supported type hints in the visible editor.","type":"boolean","default":true},"editor.inlayHints.parameters":{"title":"Parameter Inlay Hints","description":"Show supported parameter hints in the visible editor.","type":"boolean","default":true},"editor.largeFileIntelligence":{"title":"Large File Intelligence","description":"Allow full semantic highlighting and inlay hints for files larger than 1 MiB.","type":"boolean","default":false},"workbench.locale":{"title":"Display Language","type":"string","default":"en","enum":["en","de","es","ja","zh"]},"workbench.reducedMotion":{"title":"Reduced Motion","type":"boolean","default":false},"desktop.projects.openBehavior":{"title":"Open projects","type":"string","default":"currentWindow","enum":["currentWindow","newWindow"]},"desktop.tools.gitPath":{"title":"git executable path","type":"string","default":""},"desktop.tools.ghPath":{"title":"gh executable path","type":"string","default":""},"project.intelligence":{"type":"object","properties":{"enabled":{"type":"boolean","default":true,"description":"Index project dependencies, imports and related files."},"exclude":{"type":"array","items":{"type":"string","maxLength":1024},"default":[]},"maxFiles":{"type":"integer","minimum":1,"maximum":100000,"default":20000},"maxFileBytes":{"type":"integer","minimum":1,"maximum":5242880,"default":1048576}},"additionalProperties":false,"title":"Project Intelligence","description":"Partial overrides of project.json intelligence. Objects merge across files."},"project.schemas":{"type":"object","properties":{"catalog":{"type":"boolean","default":true,"description":"Discover file schemas through SchemaStore. Bundled Oxbit settings support remains available."},"download":{"type":"boolean","default":true,"description":"Allow remote schema downloads; cached and bundled schemas work offline."},"associations":{"type":"array","maxItems":1000,"default":[],"items":{"type":"object","properties":{"url":{"type":"string","minLength":1,"description":"Schema URL or workspace file URI."},"fileMatch":{"type":"array","items":{"type":"string","maxLength":1024}},"schema":{"type":["object","boolean"],"description":"An inline JSON Schema."}},"additionalProperties":false,"anyOf":[{"required":["url"]},{"required":["schema"]}]}}},"additionalProperties":false,"title":"JSON Schemas","description":"Partial overrides of project.json schemas. Association arrays replace lower arrays."}}`);
936
+ const properties$2 = /* @__PURE__ */ JSON.parse(`{"$schema":{"type":"string","description":"JSON Schema URI. Ignored when merging effective preferences."},"agentACP.provider":{"title":"Default agent","description":"A built-in agent ID, an ACP Registry agent ID, or custom.","type":"string","default":"codex"},"agentACP.useModifierToSend":{"title":"Send with Ctrl/Cmd+Enter only","description":"Enter inserts a new line and Ctrl/Cmd+Enter sends the message.","type":"boolean","default":false},"agentACP.notifications":{"title":"Agent desktop notifications","description":"Show a system notification when the agent needs input or finishes while Oxbit is in the background.","type":"string","default":"whenHidden","enum":["whenHidden","never"]},"agentACP.codex.command":{"title":"Codex ACP executable","type":"string","default":"npx"},"agentACP.codex.args":{"title":"Codex ACP arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@agentclientprotocol/codex-acp@2.2.2\\"]"},"agentACP.claude.command":{"title":"Claude Agent executable","type":"string","default":"npx"},"agentACP.claude.args":{"title":"Claude Agent arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@agentclientprotocol/claude-agent-acp@0.89.1\\"]"},"agentACP.gemini.command":{"title":"Gemini CLI executable","type":"string","default":"npx"},"agentACP.gemini.args":{"title":"Gemini CLI arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@google/gemini-cli@0.63.0\\",\\"--acp\\"]"},"agentACP.copilot.command":{"title":"GitHub Copilot executable","type":"string","default":"npx"},"agentACP.copilot.args":{"title":"GitHub Copilot arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"@github/copilot@1.0.95\\",\\"--acp\\"]"},"agentACP.cursor.command":{"title":"Cursor ACP executable","type":"string","default":"agent"},"agentACP.cursor.args":{"title":"Cursor ACP arguments (JSON array)","type":"string","default":"[\\"acp\\"]"},"agentACP.amp.command":{"title":"Amp Agent ACP executable","type":"string","default":"npx"},"agentACP.amp.args":{"title":"Amp Agent ACP arguments (JSON array)","type":"string","default":"[\\"-y\\",\\"amp-acp@0.10.0\\"]"},"agentACP.custom.name":{"title":"Custom agent name","type":"string","default":""},"agentACP.custom.command":{"title":"Custom agent executable","type":"string","default":""},"agentACP.custom.args":{"title":"Custom agent arguments (JSON array)","type":"string","default":"[]"},"agentACP.oxbit.api":{"title":"Oxbit Agent API","description":"openai uses the Responses API, anthropic the Messages API, and openai-compatible Chat Completions at the base URL.","type":"string","default":"openai","enum":["openai","anthropic","openai-compatible"]},"agentACP.oxbit.baseUrl":{"title":"Oxbit Agent base URL","description":"Origin and path prefix for openai-compatible, for example https://openrouter.ai/api.","type":"string","default":""},"agentACP.oxbit.model":{"title":"Oxbit Agent model","description":"Model ID for new conversations.","type":"string","default":""},"workbench.keymap":{"title":"Keymap","description":"Keyboard layout applied on top of the Oxbit defaults. User keybindings always win.","type":"string","default":"default","enum":["default","vscode","jetbrains","macos","sublime","atom","visual-studio","emacs"]},"runtime.keepAlive":{"title":"Keep Runtime Alive","description":"How long a runtime Oxbit started keeps running after the last client disconnects or its connection heartbeats stop. Terminals and tasks continue while it runs.","type":"string","default":"75s","enum":["75s","15m","1h","8h","untilStopped"]},"runtime.autoReconnect":{"title":"Reconnect Automatically","description":"Reconnect to the runtime after the connection drops, including when the iOS app returns to the foreground.","type":"boolean","default":true},"workbench.tooltipDelay":{"title":"Tooltip Delay","description":"Delay in milliseconds before showing a tooltip on hover. Keyboard focus shows tooltips immediately.","type":"number","default":400,"minimum":0,"maximum":5000},"workbench.colorTheme":{"title":"Color Theme","description":"Specifies the color theme used in the workbench.","type":"string","default":"Graphite (dark)"},"workbench.density":{"title":"Layout Density","description":"Controls row heights, padding and label sizes across the workbench.","type":"string","default":"compact","enum":["compact","comfortable"]},"workbench.sidebarLocation":{"title":"Sidebar Location","description":"Where the primary sidebar is docked.","type":"string","default":"left","enum":["left","right"]},"editor.fontFamily":{"title":"Font Family","description":"Controls the font family of the editor.","type":"string","default":"'JetBrains Mono', ui-monospace, monospace"},"editor.fontSize":{"title":"Font Size","description":"Font size in pixels.","type":"number","default":13,"minimum":8,"maximum":32},"editor.lineHeight":{"title":"Line Height","description":"Line height in pixels. 0 uses 1.55 × font size.","type":"number","default":20,"minimum":0,"maximum":60},"editor.fontLigatures":{"title":"Font Ligatures","description":"Enables font ligatures when the font supports them.","type":"boolean","default":false},"editor.tabSize":{"title":"Tab Size","description":"The number of spaces a tab is equal to.","type":"number","default":2,"minimum":1,"maximum":8},"editor.insertSpaces":{"title":"Insert Spaces","description":"Insert spaces when pressing Tab.","type":"boolean","default":true},"editor.detectIndentation":{"title":"Detect Indentation","description":"Detect Tab Size and Insert Spaces from file contents on open.","type":"boolean","default":true},"editor.renderIndentGuides":{"title":"Indentation Guides","description":"Render vertical guides for each indent level.","type":"boolean","default":true},"editor.wordWrap":{"title":"Word Wrap","description":"Controls how lines should wrap.","type":"string","default":"off","enum":["off","on","bounded"]},"editor.renderWhitespace":{"title":"Render Whitespace","description":"Controls how the editor renders whitespace characters.","type":"string","default":"selection","enum":["none","boundary","selection","all"]},"editor.cursorBlinking":{"title":"Cursor Blinking","description":"Cursor animation style.","type":"string","default":"blink","enum":["blink","smooth","phase","solid"]},"editor.minimap":{"title":"Minimap","description":"Show a minimap of the document.","type":"boolean","default":false},"files.autoSave":{"title":"Auto Save","description":"Controls auto save of editors that have unsaved changes.","type":"string","default":"off","enum":["off","afterDelay","onFocusChange","onWindowChange"]},"files.autoSaveDelay":{"title":"Auto Save Delay","description":"Delay in milliseconds after which an editor is saved automatically. Applies when Auto Save is afterDelay.","type":"number","default":1000,"minimum":100,"maximum":60000},"files.trimTrailingWhitespace":{"title":"Trim Trailing Whitespace","description":"Remove trailing whitespace when saving.","type":"boolean","default":true},"editor.formatOnSave":{"title":"Format On Save","description":"Format a file on save. A formatter must be available.","type":"boolean","default":false},"editor.defaultFormatter":{"title":"Default Formatter","description":"Formatter used when several are available.","type":"string","default":"oxbit.prettier","anyOf":[{"enum":["oxbit.prettier","oxbit.builtin-ts"]},{"type":"string"}]},"terminal.fontSize":{"title":"Terminal Font Size","description":"Font size in pixels for the terminal.","type":"number","default":12,"minimum":8,"maximum":32},"terminal.scrollback":{"title":"Scrollback","description":"Maximum number of lines kept in the terminal buffer.","type":"number","default":5000,"minimum":100,"maximum":100000},"terminal.confirmOnKill":{"title":"Confirm On Kill","description":"Ask before killing a terminal with a running process.","type":"boolean","default":true},"scm.autoFetch":{"title":"Auto Fetch","description":"Periodically fetch from the default remote.","type":"boolean","default":true},"scm.diffLayout":{"title":"Diff Layout","description":"Default layout for diff editors.","type":"string","default":"side-by-side","enum":["side-by-side","inline"]},"files.associations":{"title":"File Associations","description":"Map filename or workspace-relative glob patterns to language IDs. Explicit associations take precedence over built-in detection.","type":"object","default":{},"propertyNames":{"minLength":1,"maxLength":1024},"additionalProperties":{"type":"string","pattern":"^[\\\\w+-]+$"}},"languageServers":{"title":"Language Servers","description":"Configure enabled state, selectors, rootMarkers, executable, args, env, initializationOptions, settings, and priority by server ID. Servers install on first use in a trusted runtime. License keys belong in the runtime-local credential file.","type":"object","default":{},"propertyNames":{"pattern":"^[a-zA-Z0-9._-]+$"},"additionalProperties":{"type":"object","properties":{"enabled":{"type":"boolean","description":"Enable this language server."},"selectors":{"type":"array","items":{"type":"object","properties":{"language":{"type":"string"},"pattern":{"type":"string"},"scheme":{"const":"file"}},"additionalProperties":false,"minProperties":1}},"rootMarkers":{"type":"array","items":{"type":"string","pattern":"^(?!/)(?!.*\\\\\\\\)(?!.*(?:^|/)\\\\.\\\\.(?:/|$))[^\\\\u0000]+$","minLength":1}},"executable":{"type":"string","pattern":"^[^\\\\u0000]*$","minLength":1},"args":{"type":"array","items":{"type":"string","pattern":"^[^\\\\u0000]*$"}},"env":{"type":"object","propertyNames":{"pattern":"^[A-Za-z_][A-Za-z0-9_]*$"},"additionalProperties":{"type":"string","pattern":"^[^\\\\u0000]*$"}},"initializationOptions":{"type":"object","additionalProperties":true,"description":"Server-specific initialization options. Store license credentials in the runtime credential file."},"settings":{"type":"object","additionalProperties":true,"description":"Server-specific configuration; nested objects merge across files."},"priority":{"type":"number"}},"additionalProperties":false}},"ui.fontFamily":{"title":"UI Font Family","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":""},"ui.fontSize":{"title":"UI Font Size","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":12,"minimum":8,"maximum":72},"ui.fontWeight":{"title":"UI Font Weight","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":400,"minimum":100,"maximum":900},"ui.fontStyle":{"title":"UI Font Style","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":"normal","enum":["normal","italic","oblique"]},"ui.lineHeight":{"title":"UI Line Height","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":1.4,"minimum":1,"maximum":3},"ui.letterSpacing":{"title":"UI Letter Spacing","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":0,"minimum":-3,"maximum":10},"ui.fontLigatures":{"title":"UI Font Ligatures","description":"Overrides theme typography. Reset restores the theme value.","type":"boolean","default":false},"terminal.fontFamily":{"title":"Terminal Font Family","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":""},"terminal.fontWeight":{"title":"Terminal Font Weight","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":400,"minimum":100,"maximum":900},"terminal.fontStyle":{"title":"Terminal Font Style","description":"Overrides theme typography. Reset restores the theme value.","type":"string","default":"normal","enum":["normal","italic","oblique"]},"terminal.lineHeight":{"title":"Terminal Line Height","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":1,"minimum":1,"maximum":3},"terminal.letterSpacing":{"title":"Terminal Letter Spacing","description":"Overrides theme typography. Reset restores the theme value.","type":"number","default":0,"minimum":-3,"maximum":10},"terminal.fontLigatures":{"title":"Terminal Font Ligatures","description":"Overrides theme typography. Reset restores the theme value.","type":"boolean","default":false},"workbench.iconTheme":{"title":"File Icon Theme","description":"Select a locally installed icon theme. Unavailable selections use Oxbit defaults until restored.","type":"string","default":"oxbit.default"},"workbench.productIconTheme":{"title":"Product Icon Theme","description":"Select a locally installed icon theme. Unavailable selections use Oxbit defaults until restored.","type":"string","default":"oxbit.default"},"editor.semanticHighlighting":{"title":"Semantic Highlighting","description":"Color symbols using language server information.","type":"boolean","default":true},"editor.inlayHints.types":{"title":"Type Inlay Hints","description":"Show supported type hints in the visible editor.","type":"boolean","default":true},"editor.inlayHints.parameters":{"title":"Parameter Inlay Hints","description":"Show supported parameter hints in the visible editor.","type":"boolean","default":true},"editor.largeFileIntelligence":{"title":"Large File Intelligence","description":"Allow full semantic highlighting and inlay hints for files larger than 1 MiB.","type":"boolean","default":false},"workbench.locale":{"title":"Display Language","type":"string","default":"en","enum":["en","de","es","ja","zh"]},"workbench.reducedMotion":{"title":"Reduced Motion","type":"boolean","default":false},"desktop.projects.openBehavior":{"title":"Open projects","type":"string","default":"currentWindow","enum":["currentWindow","newWindow"]},"desktop.tools.gitPath":{"title":"git executable path","type":"string","default":""},"desktop.tools.ghPath":{"title":"gh executable path","type":"string","default":""},"project.intelligence":{"type":"object","properties":{"enabled":{"type":"boolean","default":true,"description":"Index project dependencies, imports and related files."},"exclude":{"type":"array","items":{"type":"string","maxLength":1024},"default":[]},"maxFiles":{"type":"integer","minimum":1,"maximum":100000,"default":20000},"maxFileBytes":{"type":"integer","minimum":1,"maximum":5242880,"default":1048576}},"additionalProperties":false,"title":"Project Intelligence","description":"Partial overrides of project.json intelligence. Objects merge across files."},"project.schemas":{"type":"object","properties":{"catalog":{"type":"boolean","default":true,"description":"Discover file schemas through SchemaStore. Bundled Oxbit settings support remains available."},"download":{"type":"boolean","default":true,"description":"Allow remote schema downloads; cached and bundled schemas work offline."},"associations":{"type":"array","maxItems":1000,"default":[],"items":{"type":"object","properties":{"url":{"type":"string","minLength":1,"description":"Schema URL or workspace file URI."},"fileMatch":{"type":"array","items":{"type":"string","maxLength":1024}},"schema":{"type":["object","boolean"],"description":"An inline JSON Schema."}},"additionalProperties":false,"anyOf":[{"required":["url"]},{"required":["schema"]}]}}},"additionalProperties":false,"title":"JSON Schemas","description":"Partial overrides of project.json schemas. Association arrays replace lower arrays."}}`);
937
937
  const propertyNames = { "anyOf": [{ "not": { "pattern": "^\\[.*\\]$" } }, { "pattern": "^\\[[^\\[\\]]+\\]$" }] };
938
938
  const patternProperties = { "^\\[[^\\[\\]]+\\]$": { "$ref": "#/definitions/languageOverrides" } };
939
939
  const additionalProperties$1 = true;
940
- const definitions$1 = { "languageOverrides": { "type": "object", "properties": { "agentACP.provider": { "$ref": "#/properties/agentACP.provider" }, "agentACP.useModifierToSend": { "$ref": "#/properties/agentACP.useModifierToSend" }, "agentACP.notifications": { "$ref": "#/properties/agentACP.notifications" }, "agentACP.codex.command": { "$ref": "#/properties/agentACP.codex.command" }, "agentACP.codex.args": { "$ref": "#/properties/agentACP.codex.args" }, "agentACP.claude.command": { "$ref": "#/properties/agentACP.claude.command" }, "agentACP.claude.args": { "$ref": "#/properties/agentACP.claude.args" }, "agentACP.gemini.command": { "$ref": "#/properties/agentACP.gemini.command" }, "agentACP.gemini.args": { "$ref": "#/properties/agentACP.gemini.args" }, "agentACP.copilot.command": { "$ref": "#/properties/agentACP.copilot.command" }, "agentACP.copilot.args": { "$ref": "#/properties/agentACP.copilot.args" }, "agentACP.cursor.command": { "$ref": "#/properties/agentACP.cursor.command" }, "agentACP.cursor.args": { "$ref": "#/properties/agentACP.cursor.args" }, "agentACP.amp.command": { "$ref": "#/properties/agentACP.amp.command" }, "agentACP.amp.args": { "$ref": "#/properties/agentACP.amp.args" }, "agentACP.custom.name": { "$ref": "#/properties/agentACP.custom.name" }, "agentACP.custom.command": { "$ref": "#/properties/agentACP.custom.command" }, "agentACP.custom.args": { "$ref": "#/properties/agentACP.custom.args" }, "workbench.keymap": { "$ref": "#/properties/workbench.keymap" }, "runtime.keepAlive": { "$ref": "#/properties/runtime.keepAlive" }, "runtime.autoReconnect": { "$ref": "#/properties/runtime.autoReconnect" }, "workbench.tooltipDelay": { "$ref": "#/properties/workbench.tooltipDelay" }, "workbench.colorTheme": { "$ref": "#/properties/workbench.colorTheme" }, "workbench.density": { "$ref": "#/properties/workbench.density" }, "workbench.sidebarLocation": { "$ref": "#/properties/workbench.sidebarLocation" }, "editor.fontFamily": { "$ref": "#/properties/editor.fontFamily" }, "editor.fontSize": { "$ref": "#/properties/editor.fontSize" }, "editor.lineHeight": { "$ref": "#/properties/editor.lineHeight" }, "editor.fontLigatures": { "$ref": "#/properties/editor.fontLigatures" }, "editor.tabSize": { "$ref": "#/properties/editor.tabSize" }, "editor.insertSpaces": { "$ref": "#/properties/editor.insertSpaces" }, "editor.detectIndentation": { "$ref": "#/properties/editor.detectIndentation" }, "editor.renderIndentGuides": { "$ref": "#/properties/editor.renderIndentGuides" }, "editor.wordWrap": { "$ref": "#/properties/editor.wordWrap" }, "editor.renderWhitespace": { "$ref": "#/properties/editor.renderWhitespace" }, "editor.cursorBlinking": { "$ref": "#/properties/editor.cursorBlinking" }, "editor.minimap": { "$ref": "#/properties/editor.minimap" }, "files.autoSave": { "$ref": "#/properties/files.autoSave" }, "files.autoSaveDelay": { "$ref": "#/properties/files.autoSaveDelay" }, "files.trimTrailingWhitespace": { "$ref": "#/properties/files.trimTrailingWhitespace" }, "editor.formatOnSave": { "$ref": "#/properties/editor.formatOnSave" }, "editor.defaultFormatter": { "$ref": "#/properties/editor.defaultFormatter" }, "terminal.fontSize": { "$ref": "#/properties/terminal.fontSize" }, "terminal.scrollback": { "$ref": "#/properties/terminal.scrollback" }, "terminal.confirmOnKill": { "$ref": "#/properties/terminal.confirmOnKill" }, "scm.autoFetch": { "$ref": "#/properties/scm.autoFetch" }, "scm.diffLayout": { "$ref": "#/properties/scm.diffLayout" }, "files.associations": { "$ref": "#/properties/files.associations" }, "languageServers": { "$ref": "#/properties/languageServers" }, "ui.fontFamily": { "$ref": "#/properties/ui.fontFamily" }, "ui.fontSize": { "$ref": "#/properties/ui.fontSize" }, "ui.fontWeight": { "$ref": "#/properties/ui.fontWeight" }, "ui.fontStyle": { "$ref": "#/properties/ui.fontStyle" }, "ui.lineHeight": { "$ref": "#/properties/ui.lineHeight" }, "ui.letterSpacing": { "$ref": "#/properties/ui.letterSpacing" }, "ui.fontLigatures": { "$ref": "#/properties/ui.fontLigatures" }, "terminal.fontFamily": { "$ref": "#/properties/terminal.fontFamily" }, "terminal.fontWeight": { "$ref": "#/properties/terminal.fontWeight" }, "terminal.fontStyle": { "$ref": "#/properties/terminal.fontStyle" }, "terminal.lineHeight": { "$ref": "#/properties/terminal.lineHeight" }, "terminal.letterSpacing": { "$ref": "#/properties/terminal.letterSpacing" }, "terminal.fontLigatures": { "$ref": "#/properties/terminal.fontLigatures" }, "workbench.iconTheme": { "$ref": "#/properties/workbench.iconTheme" }, "workbench.productIconTheme": { "$ref": "#/properties/workbench.productIconTheme" }, "editor.semanticHighlighting": { "$ref": "#/properties/editor.semanticHighlighting" }, "editor.inlayHints.types": { "$ref": "#/properties/editor.inlayHints.types" }, "editor.inlayHints.parameters": { "$ref": "#/properties/editor.inlayHints.parameters" }, "editor.largeFileIntelligence": { "$ref": "#/properties/editor.largeFileIntelligence" }, "workbench.locale": { "$ref": "#/properties/workbench.locale" }, "workbench.reducedMotion": { "$ref": "#/properties/workbench.reducedMotion" }, "desktop.projects.openBehavior": { "$ref": "#/properties/desktop.projects.openBehavior" }, "desktop.tools.gitPath": { "$ref": "#/properties/desktop.tools.gitPath" }, "desktop.tools.ghPath": { "$ref": "#/properties/desktop.tools.ghPath" }, "project.intelligence": false, "project.schemas": false }, "additionalProperties": true, "description": "Settings for a language ID, such as [mdx] or [php]. Runtime project options belong at the top level.", "propertyNames": { "not": { "pattern": "^\\[.*\\]$" } } } };
940
+ const definitions$1 = { "languageOverrides": { "type": "object", "properties": { "agentACP.provider": { "$ref": "#/properties/agentACP.provider" }, "agentACP.useModifierToSend": { "$ref": "#/properties/agentACP.useModifierToSend" }, "agentACP.notifications": { "$ref": "#/properties/agentACP.notifications" }, "agentACP.codex.command": { "$ref": "#/properties/agentACP.codex.command" }, "agentACP.codex.args": { "$ref": "#/properties/agentACP.codex.args" }, "agentACP.claude.command": { "$ref": "#/properties/agentACP.claude.command" }, "agentACP.claude.args": { "$ref": "#/properties/agentACP.claude.args" }, "agentACP.gemini.command": { "$ref": "#/properties/agentACP.gemini.command" }, "agentACP.gemini.args": { "$ref": "#/properties/agentACP.gemini.args" }, "agentACP.copilot.command": { "$ref": "#/properties/agentACP.copilot.command" }, "agentACP.copilot.args": { "$ref": "#/properties/agentACP.copilot.args" }, "agentACP.cursor.command": { "$ref": "#/properties/agentACP.cursor.command" }, "agentACP.cursor.args": { "$ref": "#/properties/agentACP.cursor.args" }, "agentACP.amp.command": { "$ref": "#/properties/agentACP.amp.command" }, "agentACP.amp.args": { "$ref": "#/properties/agentACP.amp.args" }, "agentACP.custom.name": { "$ref": "#/properties/agentACP.custom.name" }, "agentACP.custom.command": { "$ref": "#/properties/agentACP.custom.command" }, "agentACP.custom.args": { "$ref": "#/properties/agentACP.custom.args" }, "agentACP.oxbit.api": { "$ref": "#/properties/agentACP.oxbit.api" }, "agentACP.oxbit.baseUrl": { "$ref": "#/properties/agentACP.oxbit.baseUrl" }, "agentACP.oxbit.model": { "$ref": "#/properties/agentACP.oxbit.model" }, "workbench.keymap": { "$ref": "#/properties/workbench.keymap" }, "runtime.keepAlive": { "$ref": "#/properties/runtime.keepAlive" }, "runtime.autoReconnect": { "$ref": "#/properties/runtime.autoReconnect" }, "workbench.tooltipDelay": { "$ref": "#/properties/workbench.tooltipDelay" }, "workbench.colorTheme": { "$ref": "#/properties/workbench.colorTheme" }, "workbench.density": { "$ref": "#/properties/workbench.density" }, "workbench.sidebarLocation": { "$ref": "#/properties/workbench.sidebarLocation" }, "editor.fontFamily": { "$ref": "#/properties/editor.fontFamily" }, "editor.fontSize": { "$ref": "#/properties/editor.fontSize" }, "editor.lineHeight": { "$ref": "#/properties/editor.lineHeight" }, "editor.fontLigatures": { "$ref": "#/properties/editor.fontLigatures" }, "editor.tabSize": { "$ref": "#/properties/editor.tabSize" }, "editor.insertSpaces": { "$ref": "#/properties/editor.insertSpaces" }, "editor.detectIndentation": { "$ref": "#/properties/editor.detectIndentation" }, "editor.renderIndentGuides": { "$ref": "#/properties/editor.renderIndentGuides" }, "editor.wordWrap": { "$ref": "#/properties/editor.wordWrap" }, "editor.renderWhitespace": { "$ref": "#/properties/editor.renderWhitespace" }, "editor.cursorBlinking": { "$ref": "#/properties/editor.cursorBlinking" }, "editor.minimap": { "$ref": "#/properties/editor.minimap" }, "files.autoSave": { "$ref": "#/properties/files.autoSave" }, "files.autoSaveDelay": { "$ref": "#/properties/files.autoSaveDelay" }, "files.trimTrailingWhitespace": { "$ref": "#/properties/files.trimTrailingWhitespace" }, "editor.formatOnSave": { "$ref": "#/properties/editor.formatOnSave" }, "editor.defaultFormatter": { "$ref": "#/properties/editor.defaultFormatter" }, "terminal.fontSize": { "$ref": "#/properties/terminal.fontSize" }, "terminal.scrollback": { "$ref": "#/properties/terminal.scrollback" }, "terminal.confirmOnKill": { "$ref": "#/properties/terminal.confirmOnKill" }, "scm.autoFetch": { "$ref": "#/properties/scm.autoFetch" }, "scm.diffLayout": { "$ref": "#/properties/scm.diffLayout" }, "files.associations": { "$ref": "#/properties/files.associations" }, "languageServers": { "$ref": "#/properties/languageServers" }, "ui.fontFamily": { "$ref": "#/properties/ui.fontFamily" }, "ui.fontSize": { "$ref": "#/properties/ui.fontSize" }, "ui.fontWeight": { "$ref": "#/properties/ui.fontWeight" }, "ui.fontStyle": { "$ref": "#/properties/ui.fontStyle" }, "ui.lineHeight": { "$ref": "#/properties/ui.lineHeight" }, "ui.letterSpacing": { "$ref": "#/properties/ui.letterSpacing" }, "ui.fontLigatures": { "$ref": "#/properties/ui.fontLigatures" }, "terminal.fontFamily": { "$ref": "#/properties/terminal.fontFamily" }, "terminal.fontWeight": { "$ref": "#/properties/terminal.fontWeight" }, "terminal.fontStyle": { "$ref": "#/properties/terminal.fontStyle" }, "terminal.lineHeight": { "$ref": "#/properties/terminal.lineHeight" }, "terminal.letterSpacing": { "$ref": "#/properties/terminal.letterSpacing" }, "terminal.fontLigatures": { "$ref": "#/properties/terminal.fontLigatures" }, "workbench.iconTheme": { "$ref": "#/properties/workbench.iconTheme" }, "workbench.productIconTheme": { "$ref": "#/properties/workbench.productIconTheme" }, "editor.semanticHighlighting": { "$ref": "#/properties/editor.semanticHighlighting" }, "editor.inlayHints.types": { "$ref": "#/properties/editor.inlayHints.types" }, "editor.inlayHints.parameters": { "$ref": "#/properties/editor.inlayHints.parameters" }, "editor.largeFileIntelligence": { "$ref": "#/properties/editor.largeFileIntelligence" }, "workbench.locale": { "$ref": "#/properties/workbench.locale" }, "workbench.reducedMotion": { "$ref": "#/properties/workbench.reducedMotion" }, "desktop.projects.openBehavior": { "$ref": "#/properties/desktop.projects.openBehavior" }, "desktop.tools.gitPath": { "$ref": "#/properties/desktop.tools.gitPath" }, "desktop.tools.ghPath": { "$ref": "#/properties/desktop.tools.ghPath" }, "project.intelligence": false, "project.schemas": false }, "additionalProperties": true, "description": "Settings for a language ID, such as [mdx] or [php]. Runtime project options belong at the top level.", "propertyNames": { "not": { "pattern": "^\\[.*\\]$" } } } };
941
941
  const schema = {
942
942
  $schema: $schema$2,
943
943
  $id: $id$1,
@@ -1166,6 +1166,7 @@ const ACP_CUSTOM_PROVIDER = "custom";
1166
1166
  const ACP_REGISTRY_URL = "https://cdn.agentclientprotocol.com/registry/v1/latest/registry.json";
1167
1167
  const acpPreset = (id) => ACP_PROVIDERS.find((provider) => provider.id === id);
1168
1168
  const acpBuiltinForRegistry = (registryId) => ACP_PROVIDERS.find((provider) => provider.registryId === registryId);
1169
+ const PUSH_RELAY_URL = "https://oxbit-push.ryanyannelli.workers.dev";
1169
1170
  const taskPhases = [
1170
1171
  "preinit",
1171
1172
  "init",
@@ -3456,6 +3457,8 @@ function bounded(value) {
3456
3457
  );
3457
3458
  return value;
3458
3459
  }
3460
+ const BRANCH_DIFF_FILES = 50;
3461
+ const BRANCH_DIFF_TEXT = 2e5;
3459
3462
  const conflicted = (index, working) => ["DD", "AU", "UD", "UA", "DU", "AA", "UU"].includes(index + working);
3460
3463
  const digest$1 = (value) => createHash("sha256").update(value).digest("hex");
3461
3464
  class Git {
@@ -3786,18 +3789,18 @@ class Git {
3786
3789
  if (typeof params.path !== "string")
3787
3790
  throw new RpcError("INVALID_PARAMS", "A file path is required");
3788
3791
  await this.files.resolve(params.path, true);
3789
- const detail = await this.show(params.ref, signal), file = detail.files.find((file2) => file2.path === params.path);
3792
+ const detail2 = await this.show(params.ref, signal), file = detail2.files.find((file2) => file2.path === params.path);
3790
3793
  if (!file)
3791
3794
  throw new RpcError("NOT_FOUND", "File is not part of this commit");
3792
- const before = detail.commit.parents[0] ? await this.optional(
3795
+ const before = detail2.commit.parents[0] ? await this.optional(
3793
3796
  [
3794
3797
  "show",
3795
- `${detail.commit.parents[0]}:${file.originalPath ?? file.path}`
3798
+ `${detail2.commit.parents[0]}:${file.originalPath ?? file.path}`
3796
3799
  ],
3797
3800
  signal
3798
3801
  ) : void 0;
3799
3802
  const after = await this.optional(
3800
- ["show", `${detail.commit.id}:${file.path}`],
3803
+ ["show", `${detail2.commit.id}:${file.path}`],
3801
3804
  signal
3802
3805
  );
3803
3806
  const diff = await this.run(
@@ -3808,7 +3811,7 @@ class Git {
3808
3811
  "--no-ext-diff",
3809
3812
  "--no-textconv",
3810
3813
  "--no-color",
3811
- detail.commit.id,
3814
+ detail2.commit.id,
3812
3815
  "--",
3813
3816
  file.path,
3814
3817
  ...file.originalPath ? [file.originalPath] : []
@@ -3866,6 +3869,88 @@ class Git {
3866
3869
  )).stdout
3867
3870
  });
3868
3871
  }
3872
+ /** The upstream remote's default branch, then `origin/HEAD`, then local `main` or `master`. */
3873
+ async baseBranch(branch, signal) {
3874
+ const remote = branch ? (await this.optional(["config", "--get", `branch.${branch}.remote`], signal)).stdout.trim() : "";
3875
+ const candidates = [
3876
+ ...remote && remote !== "." ? [`refs/remotes/${remote}/HEAD`] : [],
3877
+ "refs/remotes/origin/HEAD",
3878
+ "refs/heads/main",
3879
+ "refs/heads/master"
3880
+ ];
3881
+ for (const ref of candidates) {
3882
+ const commit = await this.optional(
3883
+ ["rev-parse", "--verify", "--quiet", "--end-of-options", `${ref}^{commit}`],
3884
+ signal
3885
+ );
3886
+ if (commit.exitCode) continue;
3887
+ const name = await this.optional(["rev-parse", "--abbrev-ref", ref], signal);
3888
+ return { name: name.stdout.trim() || ref, commit: commit.stdout.trim() };
3889
+ }
3890
+ return void 0;
3891
+ }
3892
+ async untrackedPatch(relative, signal) {
3893
+ const result = await this.run(
3894
+ ["diff", "--no-index", "--no-ext-diff", "--no-textconv", "--no-color", "--src-prefix=a/", "--dst-prefix=b/", "--", "/dev/null", relative],
3895
+ signal,
3896
+ this.files.root,
3897
+ void 0,
3898
+ true
3899
+ );
3900
+ if (result.exitCode > 1)
3901
+ throw new RpcError("GIT_FAILED", result.stderr.trim() || `Git exited ${result.exitCode}`);
3902
+ return result.stdout;
3903
+ }
3904
+ /** Committed, staged, unstaged and untracked changes since the merge base with the base branch. */
3905
+ async branchDiff(params, signal) {
3906
+ const limit2 = Number(params.limit ?? BRANCH_DIFF_FILES);
3907
+ if (!Number.isInteger(limit2) || limit2 < 0 || limit2 > BRANCH_DIFF_FILES)
3908
+ throw new RpcError("INVALID_PARAMS", "Invalid file limit");
3909
+ const inside = await this.optional(["rev-parse", "--is-inside-work-tree"], signal);
3910
+ if (inside.exitCode || inside.stdout.trim() !== "true") {
3911
+ if (inside.exitCode && !inside.stderr.includes("not a git repository"))
3912
+ throw new RpcError("GIT_FAILED", inside.stderr.trim());
3913
+ return { repository: false };
3914
+ }
3915
+ const head = await this.head(signal);
3916
+ if (!head) throw new RpcError("NOT_FOUND", "This branch has no commits yet");
3917
+ const branch = (await this.optional(["symbolic-ref", "-q", "--short", "HEAD"], signal)).stdout.trim();
3918
+ const base = await this.baseBranch(branch, signal);
3919
+ if (!base) throw new RpcError("NOT_FOUND", "No base branch: origin/HEAD, main and master are missing");
3920
+ const mergeBase = await this.optional(["merge-base", base.commit, head], signal);
3921
+ if (mergeBase.exitCode)
3922
+ throw new RpcError("NOT_FOUND", `${branch || "HEAD"} has no common history with ${base.name}`);
3923
+ const from = mergeBase.stdout.trim();
3924
+ const names = async (args) => (await this.run(args, signal)).stdout.split("\0").filter(Boolean);
3925
+ const tracked = await names(["diff", "--name-only", "--relative", "-z", from, "--"]);
3926
+ const untracked = await names(["ls-files", "--others", "--exclude-standard", "-z", "--"]);
3927
+ const files = [];
3928
+ let size = 0, truncated = false;
3929
+ for (const [index, relative] of [...tracked, ...untracked].entries()) {
3930
+ if (files.length >= limit2 || size >= BRANCH_DIFF_TEXT) {
3931
+ truncated = true;
3932
+ break;
3933
+ }
3934
+ let diff = "";
3935
+ try {
3936
+ diff = index < tracked.length ? (await this.run(
3937
+ ["diff", "--no-ext-diff", "--no-textconv", "--no-color", "--relative", "--src-prefix=a/", "--dst-prefix=b/", from, "--", relative],
3938
+ signal
3939
+ )).stdout : await this.untrackedPatch(relative, signal);
3940
+ } catch (error) {
3941
+ if (error.code !== "OUTPUT_LIMIT") throw error;
3942
+ truncated = true;
3943
+ }
3944
+ const binary2 = /^Binary files /m.test(diff);
3945
+ if (size + diff.length > BRANCH_DIFF_TEXT) {
3946
+ diff = diff.slice(0, BRANCH_DIFF_TEXT - size);
3947
+ truncated = true;
3948
+ }
3949
+ size += diff.length;
3950
+ files.push({ path: relative, diff, binary: binary2 });
3951
+ }
3952
+ return bounded({ base: base.name, head: branch || head.slice(0, 7), files, truncated });
3953
+ }
3869
3954
  async action(method, params, signal, progress) {
3870
3955
  const pending = this.queue.catch(() => {
3871
3956
  }).then(() => this.execute(method, params, signal, progress));
@@ -4617,7 +4702,10 @@ class LanguageServer {
4617
4702
  if (resume) this.paused = false;
4618
4703
  if (!resume && this.phase === "failed") throw new RpcError("LSP_FAILED", this.error || "Language server failed. Retry from Language Servers.");
4619
4704
  if (this.paused) throw new RpcError("LSP_STOPPED", "Language server is stopped. Start it from Language Servers.");
4620
- if (this.starting) return this.starting;
4705
+ if (this.starting) {
4706
+ this.emit("oxbit/serverState", { state: this.phase });
4707
+ return this.starting;
4708
+ }
4621
4709
  if (this.child && this.capabilities)
4622
4710
  return { capabilities: this.capabilities, registrations: [...this.registrations.values()], rootUri: this.uri(""), projectRootUri: pathToFileURL(this.options.root ?? this.files.root).href, version: this.launchSpec?.version };
4623
4711
  this.error = "";
@@ -5247,6 +5335,47 @@ function deferToRuff(spec) {
5247
5335
  diagnostics: (diagnostics) => diagnostics.filter((item) => item.code !== void 0)
5248
5336
  };
5249
5337
  }
5338
+ const phpProbes = [
5339
+ { command: ["herd", "which-php"], php: (output) => output.includes("No usable PHP version found") ? void 0 : [output.trim()] },
5340
+ { command: ["valet", "which-php"], php: (output) => [output.trim()] },
5341
+ { command: ["./vendor/bin/sail", "ps"], php: () => ["./vendor/bin/sail", "php"] },
5342
+ { command: ["lando", "php", "-r", "echo PHP_BINARY;"], php: () => ["lando", "php"] },
5343
+ { command: ["ddev", "php", "-r", "echo PHP_BINARY;"], php: () => ["ddev", "php"] },
5344
+ { command: ["php", "-r", "echo PHP_BINARY;"], php: (output) => [output.trim()] }
5345
+ ];
5346
+ function probe(command, cwd, env2, timeout, signal) {
5347
+ return new Promise((resolve) => {
5348
+ const child = spawn(command[0], command.slice(1), { cwd, env: env2, detached: process.platform !== "win32", stdio: ["ignore", "pipe", "ignore"] });
5349
+ trackChild(child);
5350
+ let output = "";
5351
+ const finish = (value) => {
5352
+ clearTimeout(timer);
5353
+ signal?.removeEventListener("abort", stop2);
5354
+ resolve(value);
5355
+ };
5356
+ const stop2 = () => {
5357
+ killProcess(child);
5358
+ finish();
5359
+ };
5360
+ const timer = setTimeout(stop2, timeout);
5361
+ signal?.addEventListener("abort", stop2, { once: true });
5362
+ child.stdout.on("data", (data) => {
5363
+ output = (output + data.toString()).slice(-4096);
5364
+ });
5365
+ child.on("error", () => finish());
5366
+ child.on("close", (code) => finish(code === 0 ? output : void 0));
5367
+ });
5368
+ }
5369
+ async function laravelPhpCommand(root, env2, signal, timeout = 5e3) {
5370
+ for (const { command, php } of phpProbes) {
5371
+ signal?.throwIfAborted();
5372
+ const output = await probe(command, root, env2, timeout, signal);
5373
+ const found = output === void 0 ? void 0 : php(output);
5374
+ if (found?.[0]) return found;
5375
+ }
5376
+ signal?.throwIfAborted();
5377
+ throw new Error("Laravel needs PHP on the runtime host. No PHP was found through Herd, Valet, Sail, Lando, DDEV or php on PATH. Install PHP or set languageServers.laravel.initializationOptions.phpCommand.");
5378
+ }
5250
5379
  const npmPresets = {
5251
5380
  typescript: ["typescript-language-server", "lib/cli.mjs", ["typescript"]],
5252
5381
  html: ["vscode-langservers-extracted", "bin/vscode-html-language-server"],
@@ -5487,19 +5616,22 @@ class ManagedInstaller {
5487
5616
  }
5488
5617
  }
5489
5618
  } catch {
5490
- try {
5491
- if (Date.now() - (await fs.stat(lock)).mtimeMs > 6e4) {
5492
- await fs.rm(lock, { recursive: true, force: true });
5493
- continue;
5494
- }
5495
- } catch {
5619
+ }
5620
+ try {
5621
+ if (Date.now() - (await fs.stat(lock)).mtimeMs > 6e4) {
5622
+ await fs.rm(lock, { recursive: true, force: true });
5623
+ continue;
5496
5624
  }
5625
+ } catch {
5497
5626
  }
5498
5627
  if (Date.now() > deadline) throw new Error("Timed out waiting for another language server installation");
5499
5628
  await setTimeout$1(100, void 0, { signal });
5500
5629
  }
5501
5630
  }
5502
5631
  const stage = path.join(base, `.staging-${randomUUID()}`);
5632
+ const heartbeat = setInterval(() => void fs.utimes(lock, /* @__PURE__ */ new Date(), /* @__PURE__ */ new Date()).catch(() => {
5633
+ }), 1e4);
5634
+ heartbeat.unref();
5503
5635
  try {
5504
5636
  if (await complete()) return target;
5505
5637
  for (const name of await fs.readdir(base)) if (name.startsWith(".staging-")) await fs.rm(path.join(base, name), { recursive: true, force: true });
@@ -5515,6 +5647,7 @@ class ManagedInstaller {
5515
5647
  await fs.rename(stage, target);
5516
5648
  return target;
5517
5649
  } finally {
5650
+ clearInterval(heartbeat);
5518
5651
  await fs.rm(stage, { recursive: true, force: true });
5519
5652
  await fs.rm(lock, { recursive: true, force: true });
5520
5653
  }
@@ -5813,6 +5946,8 @@ class LanguageServerManager {
5813
5946
  else spec = await resolveLaunch(definition2.id, root, this.installer, signal);
5814
5947
  spec = { ...spec, args: definition2.args ?? spec.args, env: { ...spec.env, ...definition2.env }, initializationOptions: merge(spec.initializationOptions, definition2.initializationOptions), settings: merge(spec.settings, definition2.settings) };
5815
5948
  if (ruff) spec = deferToRuff(spec);
5949
+ if (definition2.id === "laravel" && !spec.initializationOptions?.phpCommand && (spec.initializationOptions?.phpEnvironment ?? "auto") === "auto")
5950
+ spec.initializationOptions = { ...spec.initializationOptions, phpCommand: await laravelPhpCommand(root, { ...process.env, ...spec.env }, signal) };
5816
5951
  if (definition2.id === "json") {
5817
5952
  const options2 = this.project ? (await this.project.reload()).schemas : { catalog: true, download: true, associations: [] };
5818
5953
  spec.settings = await this.schemas.settings(spec.settings ?? {}, options2);
@@ -5853,7 +5988,7 @@ class LanguageServerManager {
5853
5988
  if (!instance.attachments.size && !instance.idle) {
5854
5989
  instance.idle = setTimeout(() => {
5855
5990
  instance.idle = void 0;
5856
- void this.stop(false, instance.id);
5991
+ if (this.instances.get(instance.id) === instance) void this.stop(false, instance.id);
5857
5992
  }, this.idleMs);
5858
5993
  instance.idle.unref();
5859
5994
  }
@@ -7390,7 +7525,7 @@ class SubagentRegistry {
7390
7525
  let cached;
7391
7526
  function runtimeVersion() {
7392
7527
  if (cached) return cached;
7393
- return cached = "0.8.0";
7528
+ return cached = "0.9.0";
7394
7529
  }
7395
7530
  const MAX_REQUEST_BYTES = 1024 * 1024;
7396
7531
  const MAX_CONCURRENT_REQUESTS = 16;
@@ -7991,16 +8126,19 @@ function strings(value) {
7991
8126
  }
7992
8127
  class AgentACP {
7993
8128
  constructor(files, emit, stateChanged = () => {
7994
- }, acpRegistry) {
8129
+ }, acpRegistry, statusChanged = () => {
8130
+ }) {
7995
8131
  this.files = files;
7996
8132
  this.emit = emit;
7997
8133
  this.stateChanged = stateChanged;
7998
8134
  this.acpRegistry = acpRegistry;
8135
+ this.statusChanged = statusChanged;
7999
8136
  }
8000
8137
  files;
8001
8138
  emit;
8002
8139
  stateChanged;
8003
8140
  acpRegistry;
8141
+ statusChanged;
8004
8142
  agents = /* @__PURE__ */ new Map();
8005
8143
  mcp = new AgentMCP((id, name, args) => this.tool(id, name, args));
8006
8144
  get(owner, id) {
@@ -8050,6 +8188,20 @@ class AgentACP {
8050
8188
  if (agent.client) this.emit(agent.client, event, payload);
8051
8189
  this.stateChanged();
8052
8190
  }
8191
+ report(agent, status2) {
8192
+ try {
8193
+ this.statusChanged({
8194
+ owner: agent.owner,
8195
+ agent: agent.connection.name ?? agent.connection.provider,
8196
+ status: status2,
8197
+ pending: agent.approvals.size,
8198
+ startedAt: agent.startedAt
8199
+ });
8200
+ } catch (error) {
8201
+ process.stderr.write(`Oxbit agent status listener failed: ${String(error)}
8202
+ `);
8203
+ }
8204
+ }
8053
8205
  list(owner) {
8054
8206
  return { sessions: [...this.agents.values()].filter((a) => a.owner === owner && !a.stopped).map((a) => ({
8055
8207
  connection: a.connection,
@@ -8305,11 +8457,16 @@ class AgentACP {
8305
8457
  agent.busy = false;
8306
8458
  }
8307
8459
  }
8308
- if (["session/new", "session/load", "session/resume"].includes(method)) {
8460
+ if (["session/new", "session/load", "session/resume", "session/fork"].includes(method)) {
8309
8461
  if (method === "session/load" && !agent.connection.capabilities?.loadSession)
8310
8462
  throw new Error("This agent does not support loading conversations");
8311
8463
  if (method === "session/resume" && !agent.connection.capabilities?.sessionCapabilities?.resume)
8312
8464
  throw new Error("This agent does not support resuming conversations");
8465
+ if (method === "session/fork" && !agent.connection.capabilities?.sessionCapabilities?.fork)
8466
+ throw new Error("This agent does not support forking conversations");
8467
+ const source2 = agent.connection.sessionId;
8468
+ if (method === "session/fork" && !source2)
8469
+ throw new Error("Start a conversation first");
8313
8470
  if (agent.subagents?.activeCount)
8314
8471
  throw new Error(
8315
8472
  "Wait for active subagents or disconnect before changing conversations"
@@ -8321,7 +8478,7 @@ class AgentACP {
8321
8478
  const previousBytes = agent.eventBytes;
8322
8479
  const previousTitle = agent.title;
8323
8480
  const previousTruncated = agent.truncated;
8324
- const target = method === "session/new" ? void 0 : requireString(params, "sessionId");
8481
+ const target = method === "session/new" || method === "session/fork" ? void 0 : requireString(params, "sessionId");
8325
8482
  if (agent.approvals.size)
8326
8483
  throw new Error(
8327
8484
  "Resolve pending requests before changing conversations"
@@ -8342,7 +8499,7 @@ class AgentACP {
8342
8499
  const result = await this.request(agent, method, {
8343
8500
  cwd: this.files.root,
8344
8501
  mcpServers: [await this.mcp.server(agent.connection.id, !!agent.connection.capabilities?.mcpCapabilities?.http)],
8345
- ...target ? { sessionId: target } : {}
8502
+ ...target ? { sessionId: target } : method === "session/fork" ? { sessionId: source2 } : {}
8346
8503
  });
8347
8504
  const sessionId2 = target ?? result?.sessionId;
8348
8505
  if (typeof sessionId2 !== "string" || !sessionId2)
@@ -8418,12 +8575,14 @@ class AgentACP {
8418
8575
  const messageId = params.messageId === void 0 ? randomUUID() : requireString(params, "messageId", 256);
8419
8576
  agent.busy = true;
8420
8577
  agent.turn++;
8578
+ agent.startedAt = Date.now();
8421
8579
  if (agent.title === "New conversation") agent.title = text2.slice(0, 100);
8422
8580
  this.publish(agent, "acp.turnStarted", {
8423
8581
  messageId,
8424
8582
  text: text2,
8425
8583
  context: JSON.parse(JSON.stringify(params.context ?? [], withoutImageData))
8426
8584
  });
8585
+ this.report(agent, "working");
8427
8586
  const abort = () => this.stopAgent(agent, "Agent request interrupted");
8428
8587
  signal?.addEventListener("abort", abort, { once: true });
8429
8588
  let stopReason;
@@ -8448,6 +8607,8 @@ class AgentACP {
8448
8607
  signal?.removeEventListener("abort", abort);
8449
8608
  if (!agent.stopped) {
8450
8609
  this.publish(agent, "acp.turnEnded", { stopReason, error: failure2 });
8610
+ if (failure2 !== void 0) this.report(agent, "failed");
8611
+ else if (stopReason !== "cancelled") this.report(agent, "finished");
8451
8612
  this.publishQueue(agent);
8452
8613
  queueMicrotask(() => this.runQueue(agent));
8453
8614
  }
@@ -8530,6 +8691,14 @@ ${content}`
8530
8691
  this.publishQueue(agent);
8531
8692
  return { prompt };
8532
8693
  }
8694
+ moveQueued(owner, id, promptId, index) {
8695
+ const agent = this.get(owner, id);
8696
+ const from = agent.queue.findIndex((p) => p.id === promptId);
8697
+ if (from < 0) throw new Error("The queued message has already started or was removed");
8698
+ const [prompt] = agent.queue.splice(from, 1);
8699
+ agent.queue.splice(Math.max(0, Math.min(index, agent.queue.length)), 0, prompt);
8700
+ return this.publishQueue(agent);
8701
+ }
8533
8702
  resumeQueue(owner, id) {
8534
8703
  const agent = this.get(owner, id);
8535
8704
  agent.queuePaused = false;
@@ -8691,6 +8860,7 @@ ${content}`
8691
8860
  if (method !== "fs/read_text_file" && !method.startsWith("oxbit/"))
8692
8861
  registry.waiting(sessionId, requestId, true, params.toolCallId ?? params.toolCall?.toolCallId);
8693
8862
  this.publish(agent, "acp.request", this.requestEvent(agent, requestId, request));
8863
+ if (method === "session/request_permission") this.report(agent, "waiting");
8694
8864
  });
8695
8865
  }
8696
8866
  async tool(id, name, args) {
@@ -12209,6 +12379,215 @@ function loginShellEnvironment(cwd, inherited = process.env, platform = process.
12209
12379
  child.on("close", (code) => finish(code === 0 ? parseShellEnvironment(Buffer.concat(chunks).toString(), mark) : void 0));
12210
12380
  });
12211
12381
  }
12382
+ const PUSH_DEVICES_FILE = "push-devices.json";
12383
+ const MAX_DEVICES = 16;
12384
+ const RETENTION_MS = 30 * 24 * 60 * 60 * 1e3;
12385
+ const WINDOW_SECONDS = 15 * 60;
12386
+ const RELAY_TIMEOUT_MS = 1e4;
12387
+ const HANDLE = /^[A-Za-z0-9_-]{1,2048}$/;
12388
+ const STATUSES = ["working", "waiting", "finished", "failed"];
12389
+ const TITLES = { waiting: "Agent needs your input", finished: "Agent finished", failed: "Agent turn failed" };
12390
+ const log = (message2) => process.stderr.write(`Oxbit push: ${message2}
12391
+ `);
12392
+ function deviceIdOf(params) {
12393
+ const value = requireString(params, "deviceId", 128);
12394
+ if (!value) throw new RpcError("INVALID_PARAMS", "Invalid deviceId");
12395
+ return value;
12396
+ }
12397
+ function handleOf(params, key) {
12398
+ const value = params[key];
12399
+ if (value === void 0 || value === null) return void 0;
12400
+ if (typeof value !== "string" || !HANDLE.test(value)) throw new RpcError("INVALID_PARAMS", `Invalid ${key}`);
12401
+ return value;
12402
+ }
12403
+ const text$1 = (value) => typeof value === "string" && value.length <= 1024;
12404
+ function activityOf(value) {
12405
+ if (value === null) return null;
12406
+ const v = value ?? {};
12407
+ const startedAt = v.startedAt;
12408
+ if (typeof value !== "object" || !text$1(v.agent) || !text$1(v.title) || !text$1(v.detail) || !STATUSES.includes(v.status) || !Number.isSafeInteger(v.pending) || v.pending < 0 || startedAt !== void 0 && (typeof startedAt !== "number" || !Number.isFinite(startedAt) || startedAt < 0))
12409
+ throw new RpcError("INVALID_PARAMS", "Invalid activity");
12410
+ return {
12411
+ agent: v.agent,
12412
+ title: v.title,
12413
+ status: v.status,
12414
+ detail: v.detail,
12415
+ pending: v.pending,
12416
+ ...startedAt === void 0 ? {} : { startedAt }
12417
+ };
12418
+ }
12419
+ function restore(entry2) {
12420
+ try {
12421
+ const activity = entry2.activity === void 0 ? null : activityOf(entry2.activity);
12422
+ const device = {
12423
+ deviceId: deviceIdOf(entry2),
12424
+ session: requireString(entry2, "session", 128),
12425
+ alert: handleOf(entry2, "alert"),
12426
+ liveActivity: handleOf(entry2, "liveActivity"),
12427
+ active: false,
12428
+ ...activity ? { activity } : {},
12429
+ updatedAt: entry2.updatedAt
12430
+ };
12431
+ return Number.isFinite(device.updatedAt) ? device : void 0;
12432
+ } catch {
12433
+ return void 0;
12434
+ }
12435
+ }
12436
+ function detail(status2, pending) {
12437
+ if (status2 === "working") return "Working";
12438
+ if (status2 === "waiting") return pending === 1 ? "1 request needs an answer" : `${pending} requests need an answer`;
12439
+ return TITLES[status2];
12440
+ }
12441
+ function activityState(snapshot, event) {
12442
+ return {
12443
+ status: event.status,
12444
+ title: snapshot.title,
12445
+ detail: detail(event.status, event.pending),
12446
+ pending: event.pending,
12447
+ ...event.status === "working" && event.startedAt !== void 0 ? { startedAt: Math.floor(event.startedAt / 1e3) } : {}
12448
+ };
12449
+ }
12450
+ class PushDevices {
12451
+ constructor(file) {
12452
+ this.file = file;
12453
+ }
12454
+ file;
12455
+ devices = /* @__PURE__ */ new Map();
12456
+ /** Last Live Activity state sent per device. */
12457
+ sent = /* @__PURE__ */ new Map();
12458
+ persistence = Promise.resolve();
12459
+ static async open(dataDir) {
12460
+ const store = new PushDevices(path.join(dataDir, PUSH_DEVICES_FILE));
12461
+ try {
12462
+ const state2 = JSON.parse(await fs.readFile(store.file, "utf8"));
12463
+ if (state2?.version === 1 && Array.isArray(state2.devices))
12464
+ for (const entry2 of state2.devices) {
12465
+ const device = entry2 && typeof entry2 === "object" ? restore(entry2) : void 0;
12466
+ if (device) store.devices.set(device.deviceId, device);
12467
+ }
12468
+ } catch (error) {
12469
+ if (error.code !== "ENOENT") log(`ignoring unreadable ${PUSH_DEVICES_FILE}: ${String(error)}`);
12470
+ }
12471
+ store.prune();
12472
+ return store;
12473
+ }
12474
+ async register(session, connection, params) {
12475
+ const deviceId = deviceIdOf(params), alert = handleOf(params, "alert"), liveActivity = handleOf(params, "liveActivity");
12476
+ const previous = this.devices.get(deviceId);
12477
+ this.sent.delete(deviceId);
12478
+ this.devices.set(deviceId, {
12479
+ ...previous,
12480
+ deviceId,
12481
+ session,
12482
+ alert,
12483
+ liveActivity,
12484
+ active: true,
12485
+ connection,
12486
+ updatedAt: Date.now()
12487
+ });
12488
+ await this.persist();
12489
+ return {};
12490
+ }
12491
+ async state(session, connection, params) {
12492
+ const deviceId = deviceIdOf(params);
12493
+ if (typeof params.active !== "boolean") throw new RpcError("INVALID_PARAMS", "active must be boolean");
12494
+ const activity = params.activity === void 0 ? void 0 : activityOf(params.activity);
12495
+ const device = this.devices.get(deviceId);
12496
+ if (!device || device.session !== session) throw new RpcError("NOT_FOUND", "Register the device first");
12497
+ device.active = params.active;
12498
+ if (params.active) device.connection = connection;
12499
+ if (activity) device.activity = activity;
12500
+ else if (activity === null) delete device.activity;
12501
+ device.updatedAt = Date.now();
12502
+ this.sent.delete(deviceId);
12503
+ await this.persist();
12504
+ return {};
12505
+ }
12506
+ async unregister(session, params) {
12507
+ const deviceId = deviceIdOf(params);
12508
+ if (this.devices.get(deviceId)?.session === session) {
12509
+ this.devices.delete(deviceId);
12510
+ this.sent.delete(deviceId);
12511
+ await this.persist();
12512
+ }
12513
+ return {};
12514
+ }
12515
+ /** Marks devices inactive when the connection that set them active closes. */
12516
+ disconnect(connection) {
12517
+ let changed = false;
12518
+ for (const device of this.devices.values())
12519
+ if (device.active && device.connection === connection) {
12520
+ device.active = false;
12521
+ changed = true;
12522
+ }
12523
+ if (changed) this.persist().catch((error) => log(`cannot save devices: ${String(error)}`));
12524
+ }
12525
+ /** Sends alert and Live Activity pushes to the owner's inactive devices; resolves after every send settles. */
12526
+ async notify(event) {
12527
+ const sends = [];
12528
+ for (const device of this.devices.values()) {
12529
+ if (device.active || device.session !== event.owner) continue;
12530
+ if (event.status !== "working" && device.alert)
12531
+ sends.push(this.send(device.deviceId, "alert", {
12532
+ handle: device.alert,
12533
+ alert: { title: TITLES[event.status], body: device.activity?.title || event.agent }
12534
+ }));
12535
+ if (!device.liveActivity || !device.activity) continue;
12536
+ const state2 = activityState(device.activity, event), end = event.status === "finished" || event.status === "failed";
12537
+ const key = JSON.stringify([end, state2]);
12538
+ if (this.sent.get(device.deviceId) === key) continue;
12539
+ this.sent.set(device.deviceId, key);
12540
+ const window = Math.floor(Date.now() / 1e3) + WINDOW_SECONDS;
12541
+ sends.push(this.send(device.deviceId, "liveActivity", {
12542
+ handle: device.liveActivity,
12543
+ activity: { event: end ? "end" : "update", state: state2, ...end ? { dismissalDate: window } : { staleDate: window } }
12544
+ }));
12545
+ }
12546
+ await Promise.all(sends);
12547
+ }
12548
+ flush() {
12549
+ return this.persistence.catch(() => {
12550
+ });
12551
+ }
12552
+ async send(deviceId, kind, request) {
12553
+ const relay = (process.env.OXBIT_PUSH_RELAY ?? PUSH_RELAY_URL).replace(/\/+$/, "");
12554
+ try {
12555
+ const response = await fetch(`${relay}/v1/push`, {
12556
+ method: "POST",
12557
+ headers: { "Content-Type": "application/json" },
12558
+ body: JSON.stringify(request),
12559
+ signal: AbortSignal.timeout(RELAY_TIMEOUT_MS)
12560
+ });
12561
+ const result = await response.json().catch(() => ({}));
12562
+ const device = this.devices.get(deviceId);
12563
+ if (result.gone && device?.[kind] === request.handle) {
12564
+ delete device[kind];
12565
+ if (kind === "liveActivity") this.sent.delete(deviceId);
12566
+ await this.persist();
12567
+ } else if (!response.ok || !result.sent)
12568
+ log(`relay did not send to ${deviceId}: ${result.reason ?? `HTTP ${response.status}`}`);
12569
+ } catch (error) {
12570
+ log(`push to ${deviceId} failed: ${error instanceof Error ? error.message : String(error)}`);
12571
+ }
12572
+ }
12573
+ prune(now = Date.now()) {
12574
+ for (const [id, device] of this.devices)
12575
+ if (now - device.updatedAt > RETENTION_MS) this.devices.delete(id);
12576
+ const oldest = [...this.devices.values()].sort((a, b) => a.updatedAt - b.updatedAt);
12577
+ for (const device of oldest.slice(0, Math.max(0, oldest.length - MAX_DEVICES))) this.devices.delete(device.deviceId);
12578
+ for (const id of this.sent.keys()) if (!this.devices.has(id)) this.sent.delete(id);
12579
+ }
12580
+ persist() {
12581
+ this.prune();
12582
+ const payload = JSON.stringify({ version: 1, devices: [...this.devices.values()] });
12583
+ this.persistence = this.persistence.catch(() => {
12584
+ }).then(async () => {
12585
+ await fs.writeFile(this.file + ".tmp", payload, { mode: 384 });
12586
+ await fs.rename(this.file + ".tmp", this.file);
12587
+ });
12588
+ return this.persistence;
12589
+ }
12590
+ }
12212
12591
  const MAX_SOCKET_BACKLOG_BYTES = MAX_FILE_WRITE_MESSAGE_BYTES;
12213
12592
  const allCapabilities = [
12214
12593
  "filesystem.read",
@@ -12351,10 +12730,11 @@ async function createRuntime(options2) {
12351
12730
  return session;
12352
12731
  };
12353
12732
  const acpRegistry = new ACPRegistry(dataDir, options2.acpRegistryUrl);
12733
+ const pushDevices = await PushDevices.open(dataDir);
12354
12734
  const agents = new AgentACP(files, (connectionId, name, params) => {
12355
12735
  const connection = connections.get(connectionId);
12356
12736
  if (connection?.session && !connection.session.revoked && trusted) event(connection, name, params);
12357
- }, () => updateIdle(), acpRegistry);
12737
+ }, () => updateIdle(), acpRegistry, (status2) => void pushDevices.notify(status2));
12358
12738
  const processEvent = ({ event: name, params, stream, seq }, ownerId) => {
12359
12739
  const cap = name.startsWith("terminal.") ? "terminal" : "tasks";
12360
12740
  for (const c of connections.values()) {
@@ -12701,7 +13081,7 @@ async function createRuntime(options2) {
12701
13081
  if (method.startsWith("terminal.")) return { cap: "terminal", trust: true };
12702
13082
  if (method.startsWith("tasks.")) return { cap: "tasks", trust: !["tasks.catalog", "tasks.list", "tasks.attach", "tasks.worktrees"].includes(method) };
12703
13083
  if (method.startsWith("git.")) return { cap: "git", trust: true };
12704
- if (method.startsWith("acp.")) return { cap: "extensions", trust: true };
13084
+ if (method.startsWith("acp.") || method.startsWith("push.")) return { cap: "extensions", trust: true };
12705
13085
  if (method.startsWith("lsp.")) return { cap: "lsp", trust: true };
12706
13086
  if (method.startsWith("collab.")) return { cap: "collaboration" };
12707
13087
  if (method.startsWith("extensions."))
@@ -12734,6 +13114,7 @@ async function createRuntime(options2) {
12734
13114
  if (!["acp.start", "acp.list", "acp.attach", "acp.detach", "acp.stop", "acp.disconnect", "acp.registry"].includes(method) && !method.startsWith("acp.checkpoint."))
12735
13115
  agents.control(session.id, requireString(params, "id"), connection.id);
12736
13116
  }
13117
+ if (method.startsWith("push.")) owner(connection);
12737
13118
  if (method.startsWith("project.")) owner(connection);
12738
13119
  if (method.startsWith("settings.")) owner(connection);
12739
13120
  switch (method) {
@@ -12768,6 +13149,9 @@ async function createRuntime(options2) {
12768
13149
  return agents.enqueue(session.id, requireString(params, "id"), params, method === "acp.interrupt");
12769
13150
  case "acp.dequeue":
12770
13151
  return agents.dequeue(session.id, requireString(params, "id"), requireString(params, "promptId"));
13152
+ case "acp.moveQueued":
13153
+ if (!Number.isInteger(params.index)) throw new RpcError("INVALID_PARAMS", "index must be an integer");
13154
+ return agents.moveQueued(session.id, requireString(params, "id"), requireString(params, "promptId"), params.index);
12771
13155
  case "acp.resumeQueue":
12772
13156
  return agents.resumeQueue(session.id, requireString(params, "id"));
12773
13157
  case "acp.call":
@@ -12784,6 +13168,12 @@ async function createRuntime(options2) {
12784
13168
  case "acp.disconnect":
12785
13169
  agents.disconnect(session.id, connection.id);
12786
13170
  return {};
13171
+ case "push.register":
13172
+ return pushDevices.register(session.id, connection.id, params);
13173
+ case "push.state":
13174
+ return pushDevices.state(session.id, connection.id, params);
13175
+ case "push.unregister":
13176
+ return pushDevices.unregister(session.id, params);
12787
13177
  case "acp.checkpoint.create":
12788
13178
  return checkpoints.create(signal);
12789
13179
  case "acp.checkpoint.diff":
@@ -12965,6 +13355,8 @@ async function createRuntime(options2) {
12965
13355
  return git.show(params.ref, signal);
12966
13356
  case "git.commitDiff":
12967
13357
  return git.commitDiff(params, signal);
13358
+ case "git.branchDiff":
13359
+ return git.branchDiff(params, signal);
12968
13360
  case "git.stashes":
12969
13361
  return git.stashes(signal);
12970
13362
  case "git.stashDiff":
@@ -13129,6 +13521,25 @@ async function createRuntime(options2) {
13129
13521
  params.encoding,
13130
13522
  params.eol
13131
13523
  );
13524
+ case "search.files": {
13525
+ const term = requireString(params, "query", 1024).trim().toLowerCase();
13526
+ if (!term) return [];
13527
+ const result = await runCommand(options2.desktop?.rgPath ?? "rg", ["--files", "--hidden", "--glob", "!.git", "--glob", "!node_modules", "--glob", "!.oxbit-tmp-*"], {
13528
+ cwd: root,
13529
+ signal,
13530
+ maxBytes: 64 * MAX_BUFFER_BYTES
13531
+ });
13532
+ if (result.exitCode > 1) throw new RpcError("SEARCH_FAILED", result.stderr.trim());
13533
+ const matches = (path2) => {
13534
+ let at = 0;
13535
+ for (const char of term) {
13536
+ at = path2.indexOf(char, at) + 1;
13537
+ if (!at) return false;
13538
+ }
13539
+ return true;
13540
+ };
13541
+ return result.stdout.split("\n").filter((path2) => path2 && matches(path2.toLowerCase())).sort().slice(0, 1e3);
13542
+ }
13132
13543
  case "search.query": {
13133
13544
  const query = requireString(params, "query", 8192);
13134
13545
  if (!query) return [];
@@ -13394,6 +13805,7 @@ async function createRuntime(options2) {
13394
13805
  clearTimeout(authDeadline);
13395
13806
  agents.detach(connection.id);
13396
13807
  lsp.detach(connection.id);
13808
+ pushDevices.disconnect(connection.id);
13397
13809
  connections.delete(connection.id);
13398
13810
  updateIdle();
13399
13811
  for (const pending of connection.edits.values()) {
@@ -13544,6 +13956,7 @@ async function createRuntime(options2) {
13544
13956
  await Promise.allSettled([...inflight.values()]);
13545
13957
  await collaboration.close();
13546
13958
  await persistence;
13959
+ await pushDevices.flush();
13547
13960
  await new Promise((resolve) => websocket.close(() => resolve()));
13548
13961
  await new Promise((resolve) => server.close(() => resolve()));
13549
13962
  }
@@ -13741,7 +14154,7 @@ function lanAddresses() {
13741
14154
  return { v4: of("IPv4"), v6: of("IPv6") };
13742
14155
  }
13743
14156
  const stderrLog = (line) => process.stderr.write(line + "\n");
13744
- function advertise(ad, log = stderrLog) {
14157
+ function advertise(ad, log2 = stderrLog) {
13745
14158
  const instance = instanceLabel(ad.name, ad.id);
13746
14159
  const current = (ttl) => records({ ...ad, ...(ad.addresses ?? lanAddresses)() }, ttl);
13747
14160
  const timers = [];
@@ -13760,11 +14173,11 @@ function advertise(ad, log = stderrLog) {
13760
14173
  };
13761
14174
  const fail = (error) => {
13762
14175
  if (closed) return;
13763
- log(`mDNS advertising disabled: ${error.message}`);
14176
+ log2(`mDNS advertising disabled: ${error.message}`);
13764
14177
  stop2();
13765
14178
  };
13766
14179
  const sendFailure = (error) => {
13767
- if (!sendFailed) log(`mDNS send failed: ${error.message}`);
14180
+ if (!sendFailed) log2(`mDNS send failed: ${error.message}`);
13768
14181
  sendFailed = true;
13769
14182
  };
13770
14183
  const send = (packet, port2 = PORT, address = GROUP, done) => {
@@ -14083,7 +14496,7 @@ Runtime id: ${daemon.id}
14083
14496
  }
14084
14497
  async function detach(invocation, target, dataDir, entry2) {
14085
14498
  await fs.mkdir(dataDir, { recursive: true, mode: 448 });
14086
- const log = await fs.open(logFile(dataDir), "a", 384);
14499
+ const log2 = await fs.open(logFile(dataDir), "a", 384);
14087
14500
  const args = [entry2, "--foreground", "--no-open", ...invocation.lan ? ["--lan"] : ["--host", invocation.host]];
14088
14501
  if (invocation.port) args.push("--port", String(invocation.port));
14089
14502
  args.push("--keep-alive", invocation.keepAlive ? String(invocation.keepAlive) : "forever");
@@ -14095,7 +14508,7 @@ async function detach(invocation, target, dataDir, entry2) {
14095
14508
  delete env2.OXBIT_KEEP_ALIVE;
14096
14509
  const child = spawn(process.execPath, args, {
14097
14510
  detached: true,
14098
- stdio: ["ignore", log.fd, log.fd],
14511
+ stdio: ["ignore", log2.fd, log2.fd],
14099
14512
  env: env2
14100
14513
  });
14101
14514
  let exit;
@@ -14103,7 +14516,7 @@ async function detach(invocation, target, dataDir, entry2) {
14103
14516
  exit = code ?? 1;
14104
14517
  });
14105
14518
  child.unref();
14106
- await log.close();
14519
+ await log2.close();
14107
14520
  const deadline = Date.now() + READY_TIMEOUT;
14108
14521
  while (Date.now() < deadline) {
14109
14522
  const daemon = await liveDaemon(dataDir);