aicodeman 1.42.0 → 1.44.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.
- package/README.md +13 -12
- package/dist/claude-session-title.d.ts +6 -4
- package/dist/claude-session-title.d.ts.map +1 -1
- package/dist/claude-session-title.js +6 -4
- package/dist/claude-session-title.js.map +1 -1
- package/dist/cli-agent.d.ts +3 -3
- package/dist/cli-agent.d.ts.map +1 -1
- package/dist/cli-agent.js +8 -6
- package/dist/cli-agent.js.map +1 -1
- package/dist/config/cli-registry/patterns.d.ts +1 -1
- package/dist/config/cli-registry/patterns.d.ts.map +1 -1
- package/dist/config/cli-registry/patterns.js +3 -0
- package/dist/config/cli-registry/patterns.js.map +1 -1
- package/dist/config/cli-registry/schema.d.ts +1 -0
- package/dist/config/cli-registry/schema.d.ts.map +1 -1
- package/dist/config/cli-registry/schema.js +5 -3
- package/dist/config/cli-registry/schema.js.map +1 -1
- package/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +263 -4
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/config/cli-registry/types.d.ts +13 -5
- package/dist/config/cli-registry/types.d.ts.map +1 -1
- package/dist/config/map-limits.d.ts +13 -1
- package/dist/config/map-limits.d.ts.map +1 -1
- package/dist/config/map-limits.js +28 -1
- package/dist/config/map-limits.js.map +1 -1
- package/dist/config-backup.d.ts +279 -0
- package/dist/config-backup.d.ts.map +1 -0
- package/dist/config-backup.js +794 -0
- package/dist/config-backup.js.map +1 -0
- package/dist/cron/cron-service.d.ts +5 -5
- package/dist/cron/cron-service.js +6 -6
- package/dist/cron/cron-service.js.map +1 -1
- package/dist/custom-model-hosts.d.ts +16 -0
- package/dist/custom-model-hosts.d.ts.map +1 -1
- package/dist/custom-model-hosts.js.map +1 -1
- package/dist/hooks-config.d.ts +24 -0
- package/dist/hooks-config.d.ts.map +1 -1
- package/dist/hooks-config.js +47 -0
- package/dist/hooks-config.js.map +1 -1
- package/dist/intent-store.d.ts +2 -0
- package/dist/intent-store.d.ts.map +1 -1
- package/dist/intent-store.js +9 -0
- package/dist/intent-store.js.map +1 -1
- package/dist/mcp-sync-targets.d.ts +6 -3
- package/dist/mcp-sync-targets.d.ts.map +1 -1
- package/dist/mcp-sync-targets.js +6 -3
- package/dist/mcp-sync-targets.js.map +1 -1
- package/dist/mux-interface.d.ts +28 -7
- package/dist/mux-interface.d.ts.map +1 -1
- package/dist/prompt-cache-keepwarm.d.ts +193 -0
- package/dist/prompt-cache-keepwarm.d.ts.map +1 -0
- package/dist/prompt-cache-keepwarm.js +359 -0
- package/dist/prompt-cache-keepwarm.js.map +1 -0
- package/dist/respawn-controller.d.ts.map +1 -1
- package/dist/respawn-controller.js +6 -5
- package/dist/respawn-controller.js.map +1 -1
- package/dist/session-auto-ops.d.ts.map +1 -1
- package/dist/session-auto-ops.js +6 -1
- package/dist/session-auto-ops.js.map +1 -1
- package/dist/session-cli-registry-bridge.d.ts +15 -1
- package/dist/session-cli-registry-bridge.d.ts.map +1 -1
- package/dist/session-cli-registry-bridge.js +20 -3
- package/dist/session-cli-registry-bridge.js.map +1 -1
- package/dist/session-submit-verifier.d.ts +19 -0
- package/dist/session-submit-verifier.d.ts.map +1 -1
- package/dist/session-submit-verifier.js +88 -0
- package/dist/session-submit-verifier.js.map +1 -1
- package/dist/session.d.ts +83 -12
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +150 -16
- package/dist/session.js.map +1 -1
- package/dist/state-store.d.ts +10 -0
- package/dist/state-store.d.ts.map +1 -1
- package/dist/state-store.js +35 -0
- package/dist/state-store.js.map +1 -1
- package/dist/tab-layout-service.d.ts +33 -0
- package/dist/tab-layout-service.d.ts.map +1 -1
- package/dist/tab-layout-service.js +75 -3
- package/dist/tab-layout-service.js.map +1 -1
- package/dist/tmux-manager.d.ts +52 -9
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +148 -30
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-prefix-table.d.ts +54 -0
- package/dist/tmux-prefix-table.d.ts.map +1 -0
- package/dist/tmux-prefix-table.js +137 -0
- package/dist/tmux-prefix-table.js.map +1 -0
- package/dist/tui/tui-app.d.ts +9 -1
- package/dist/tui/tui-app.d.ts.map +1 -1
- package/dist/tui/tui-app.js +21 -8
- package/dist/tui/tui-app.js.map +1 -1
- package/dist/tui/tui-client.d.ts +3 -2
- package/dist/tui/tui-client.d.ts.map +1 -1
- package/dist/tui/tui-client.js +3 -2
- package/dist/tui/tui-client.js.map +1 -1
- package/dist/types/app-state.d.ts +6 -0
- package/dist/types/app-state.d.ts.map +1 -1
- package/dist/types/app-state.js.map +1 -1
- package/dist/types/session.d.ts +72 -5
- package/dist/types/session.d.ts.map +1 -1
- package/dist/types/session.js +2 -1
- package/dist/types/session.js.map +1 -1
- package/dist/usage-telemetry.d.ts +15 -0
- package/dist/usage-telemetry.d.ts.map +1 -1
- package/dist/usage-telemetry.js +45 -2
- package/dist/usage-telemetry.js.map +1 -1
- package/dist/utils/cli-installed-probes.d.ts +1 -1
- package/dist/utils/cli-installed-probes.d.ts.map +1 -1
- package/dist/utils/cli-installed-probes.js +4 -2
- package/dist/utils/cli-installed-probes.js.map +1 -1
- package/dist/utils/copilot-cli-resolver.d.ts +48 -0
- package/dist/utils/copilot-cli-resolver.d.ts.map +1 -0
- package/dist/utils/copilot-cli-resolver.js +112 -0
- package/dist/utils/copilot-cli-resolver.js.map +1 -0
- package/dist/utils/copilot-plan-usage.d.ts +46 -0
- package/dist/utils/copilot-plan-usage.d.ts.map +1 -0
- package/dist/utils/copilot-plan-usage.js +95 -0
- package/dist/utils/copilot-plan-usage.js.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/single-line.d.ts +22 -0
- package/dist/utils/single-line.d.ts.map +1 -0
- package/dist/utils/single-line.js +30 -0
- package/dist/utils/single-line.js.map +1 -0
- package/dist/web/approval-inbox.d.ts +6 -0
- package/dist/web/approval-inbox.d.ts.map +1 -1
- package/dist/web/approval-inbox.js +2 -2
- package/dist/web/approval-inbox.js.map +1 -1
- package/dist/web/case-path.d.ts +4 -2
- package/dist/web/case-path.d.ts.map +1 -1
- package/dist/web/case-path.js +13 -4
- package/dist/web/case-path.js.map +1 -1
- package/dist/web/plan-usage-latest.d.ts +2 -1
- package/dist/web/plan-usage-latest.d.ts.map +1 -1
- package/dist/web/plan-usage-latest.js +12 -2
- package/dist/web/plan-usage-latest.js.map +1 -1
- package/dist/web/ports/index.d.ts +1 -0
- package/dist/web/ports/index.d.ts.map +1 -1
- package/dist/web/ports/keep-warm-port.d.ts +9 -0
- package/dist/web/ports/keep-warm-port.d.ts.map +1 -0
- package/dist/web/ports/keep-warm-port.js +6 -0
- package/dist/web/ports/keep-warm-port.js.map +1 -0
- package/dist/web/ports/session-port.d.ts +3 -1
- package/dist/web/ports/session-port.d.ts.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.9314ee60.js +43 -0
- package/dist/web/public/app.9314ee60.js.br +0 -0
- package/dist/web/public/app.9314ee60.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.12b0c247.js → constants.4866ecc3.js} +205 -3
- package/dist/web/public/constants.4866ecc3.js.br +0 -0
- package/dist/web/public/constants.4866ecc3.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/git-status-ui.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/host-wake-ui.js.gz +0 -0
- package/dist/web/public/i18n.22ca902b.js +2 -0
- package/dist/web/public/i18n.22ca902b.js.br +0 -0
- package/dist/web/public/i18n.22ca902b.js.gz +0 -0
- package/dist/web/public/{image-input.fcdcb46c.js → image-input.941386e7.js} +24 -14
- package/dist/web/public/image-input.941386e7.js.br +0 -0
- package/dist/web/public/image-input.941386e7.js.gz +0 -0
- package/dist/web/public/index.html +110 -16
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.8bc46081.js.gz +0 -0
- package/dist/web/public/{keyboard-accessory.b42f539b.js → keyboard-accessory.4e8b20ac.js} +51 -22
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.br +0 -0
- package/dist/web/public/keyboard-accessory.4e8b20ac.js.gz +0 -0
- package/dist/web/public/{mobile-handlers.0ef9c311.js → mobile-handlers.44adaa07.js} +48 -16
- package/dist/web/public/mobile-handlers.44adaa07.js.br +0 -0
- package/dist/web/public/mobile-handlers.44adaa07.js.gz +0 -0
- package/dist/web/public/mobile-ime-preview.2dbaf262.js.gz +0 -0
- package/dist/web/public/mobile-overview.js +1 -0
- package/dist/web/public/mobile-overview.js.br +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.12fb18de.css +1 -0
- package/dist/web/public/mobile.12fb18de.css.br +0 -0
- package/dist/web/public/mobile.12fb18de.css.gz +0 -0
- package/dist/web/public/notification-manager.feb82c04.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.73ec0137.js → panels-ui.cbb24ba1.js} +66 -66
- package/dist/web/public/panels-ui.cbb24ba1.js.br +0 -0
- package/dist/web/public/panels-ui.cbb24ba1.js.gz +0 -0
- package/dist/web/public/ralph-panel.b350e2ef.js.gz +0 -0
- package/dist/web/public/ralph-wizard.b6c0dd19.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/reboot-restore-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.d7adcf6a.js.gz +0 -0
- package/dist/web/public/sanitize-html.e7147e76.js.gz +0 -0
- package/dist/web/public/security-notices.json +21 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/{session-ui.d52ff6aa.js → session-ui.9c1f009b.js} +8 -4
- package/dist/web/public/session-ui.9c1f009b.js.br +0 -0
- package/dist/web/public/session-ui.9c1f009b.js.gz +0 -0
- package/dist/web/public/settings-ui.fea9ddae.js +106 -0
- package/dist/web/public/settings-ui.fea9ddae.js.br +0 -0
- package/dist/web/public/settings-ui.fea9ddae.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview-worker.js.gz +0 -0
- package/dist/web/public/spreadsheet-preview.75c751de.js.gz +0 -0
- package/dist/web/public/spreadsheet-xlsx-core.js.gz +0 -0
- package/dist/web/public/styles.e096fcd6.css +1 -0
- package/dist/web/public/styles.e096fcd6.css.br +0 -0
- package/dist/web/public/styles.e096fcd6.css.gz +0 -0
- package/dist/web/public/subagent-windows.07277a58.js.gz +0 -0
- package/dist/web/public/sw.js +2 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-layout-browser.2d6189f7.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.7157e0bd.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.af09f99a.js.gz +0 -0
- package/dist/web/public/terminal-split.js.gz +0 -0
- package/dist/web/public/terminal-tile.js +41 -22
- package/dist/web/public/terminal-tile.js.br +0 -0
- package/dist/web/public/terminal-tile.js.gz +0 -0
- package/dist/web/public/terminal-ui.68dbd352.js +2 -0
- package/dist/web/public/terminal-ui.68dbd352.js.br +0 -0
- package/dist/web/public/terminal-ui.68dbd352.js.gz +0 -0
- package/dist/web/public/tile-grid.js +3 -6
- package/dist/web/public/tile-grid.js.br +0 -0
- package/dist/web/public/tile-grid.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/exceljs.min.js.gz +0 -0
- package/dist/web/public/vendor/fflate.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.ae5e3d5d.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.0b69623c.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.2342c37b.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/public/whats-new.json +9 -9
- package/dist/web/response-viewer-transcript.d.ts +6 -0
- package/dist/web/response-viewer-transcript.d.ts.map +1 -1
- package/dist/web/response-viewer-transcript.js +134 -1
- package/dist/web/response-viewer-transcript.js.map +1 -1
- package/dist/web/routes/admin-routes.d.ts +2 -1
- package/dist/web/routes/admin-routes.d.ts.map +1 -1
- package/dist/web/routes/admin-routes.js +3 -0
- package/dist/web/routes/admin-routes.js.map +1 -1
- package/dist/web/routes/case-routes.d.ts +2 -2
- package/dist/web/routes/case-routes.d.ts.map +1 -1
- package/dist/web/routes/case-routes.js +81 -3
- package/dist/web/routes/case-routes.js.map +1 -1
- package/dist/web/routes/config-backup-routes.d.ts +54 -0
- package/dist/web/routes/config-backup-routes.d.ts.map +1 -0
- package/dist/web/routes/config-backup-routes.js +268 -0
- package/dist/web/routes/config-backup-routes.js.map +1 -0
- package/dist/web/routes/custom-model-routes.d.ts +17 -1
- package/dist/web/routes/custom-model-routes.d.ts.map +1 -1
- package/dist/web/routes/custom-model-routes.js +118 -13
- package/dist/web/routes/custom-model-routes.js.map +1 -1
- package/dist/web/routes/hook-event-routes.d.ts.map +1 -1
- package/dist/web/routes/hook-event-routes.js +26 -5
- package/dist/web/routes/hook-event-routes.js.map +1 -1
- package/dist/web/routes/index.d.ts +1 -0
- package/dist/web/routes/index.d.ts.map +1 -1
- package/dist/web/routes/index.js +1 -0
- package/dist/web/routes/index.js.map +1 -1
- package/dist/web/routes/mcp-sync-routes.d.ts +4 -4
- package/dist/web/routes/mcp-sync-routes.js +4 -4
- package/dist/web/routes/ralph-routes.js +2 -2
- package/dist/web/routes/ralph-routes.js.map +1 -1
- package/dist/web/routes/reboot-restore-routes.d.ts.map +1 -1
- package/dist/web/routes/reboot-restore-routes.js +2 -0
- package/dist/web/routes/reboot-restore-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts +14 -4
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +131 -30
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/routes/status-telemetry-routes.d.ts +2 -2
- package/dist/web/routes/status-telemetry-routes.d.ts.map +1 -1
- package/dist/web/routes/status-telemetry-routes.js +8 -4
- package/dist/web/routes/status-telemetry-routes.js.map +1 -1
- package/dist/web/routes/system-routes.d.ts +2 -2
- package/dist/web/routes/system-routes.d.ts.map +1 -1
- package/dist/web/routes/system-routes.js +18 -0
- package/dist/web/routes/system-routes.js.map +1 -1
- package/dist/web/routes/webview-routes.d.ts.map +1 -1
- package/dist/web/routes/webview-routes.js +1 -0
- package/dist/web/routes/webview-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +32 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +64 -4
- package/dist/web/schemas.js.map +1 -1
- package/dist/web/server.d.ts +12 -2
- package/dist/web/server.d.ts.map +1 -1
- package/dist/web/server.js +127 -6
- package/dist/web/server.js.map +1 -1
- package/dist/web/session-listener-wiring.d.ts +6 -0
- package/dist/web/session-listener-wiring.d.ts.map +1 -1
- package/dist/web/session-listener-wiring.js +10 -3
- package/dist/web/session-listener-wiring.js.map +1 -1
- package/dist/web/sse-events.d.ts +5 -2
- package/dist/web/sse-events.d.ts.map +1 -1
- package/dist/web/sse-events.js +5 -2
- package/dist/web/sse-events.js.map +1 -1
- package/dist/web/webview-proxy.d.ts +8 -3
- package/dist/web/webview-proxy.d.ts.map +1 -1
- package/dist/web/webview-proxy.js +23 -5
- package/dist/web/webview-proxy.js.map +1 -1
- package/package.json +1 -1
- package/skills/codeman/SKILL.md +23 -11
- package/skills/codeman/preamble.sh +12 -2
- package/skills/codeman/reference/endpoints.md +16 -15
- package/skills/codeman/reference/messaging.md +5 -5
- package/skills/codeman/reference/recipes.md +2 -2
- package/skills/codeman/reference/verbs.md +25 -17
- package/dist/web/public/app.f04bb2a5.js +0 -43
- package/dist/web/public/app.f04bb2a5.js.br +0 -0
- package/dist/web/public/app.f04bb2a5.js.gz +0 -0
- package/dist/web/public/constants.12b0c247.js.br +0 -0
- package/dist/web/public/constants.12b0c247.js.gz +0 -0
- package/dist/web/public/i18n.06659676.js +0 -2
- package/dist/web/public/i18n.06659676.js.br +0 -0
- package/dist/web/public/i18n.06659676.js.gz +0 -0
- package/dist/web/public/image-input.fcdcb46c.js.br +0 -0
- package/dist/web/public/image-input.fcdcb46c.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.b42f539b.js.br +0 -0
- package/dist/web/public/keyboard-accessory.b42f539b.js.gz +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.br +0 -0
- package/dist/web/public/mobile-handlers.0ef9c311.js.gz +0 -0
- package/dist/web/public/mobile.2dff1580.css +0 -1
- package/dist/web/public/mobile.2dff1580.css.br +0 -0
- package/dist/web/public/mobile.2dff1580.css.gz +0 -0
- package/dist/web/public/panels-ui.73ec0137.js.br +0 -0
- package/dist/web/public/panels-ui.73ec0137.js.gz +0 -0
- package/dist/web/public/session-ui.d52ff6aa.js.br +0 -0
- package/dist/web/public/session-ui.d52ff6aa.js.gz +0 -0
- package/dist/web/public/settings-ui.3a22bd2a.js +0 -97
- package/dist/web/public/settings-ui.3a22bd2a.js.br +0 -0
- package/dist/web/public/settings-ui.3a22bd2a.js.gz +0 -0
- package/dist/web/public/styles.ba6a9b84.css +0 -1
- package/dist/web/public/styles.ba6a9b84.css.br +0 -0
- package/dist/web/public/styles.ba6a9b84.css.gz +0 -0
- package/dist/web/public/terminal-ui.0a7ccc29.js +0 -2
- package/dist/web/public/terminal-ui.0a7ccc29.js.br +0 -0
- package/dist/web/public/terminal-ui.0a7ccc29.js.gz +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Covers session CRUD, input/output, terminal buffer, quick-start, quick-run,
|
|
4
4
|
* auto-clear, auto-compact, image watcher, flicker filter, and logout.
|
|
5
5
|
*/
|
|
6
|
-
import { join, dirname, extname, basename } from 'node:path';
|
|
6
|
+
import { join, dirname, extname, basename, resolve as resolvePath } from 'node:path';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
8
|
import { existsSync, statSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import { execFile } from 'node:child_process';
|
|
@@ -17,7 +17,7 @@ import { Session, cliTakesSessionModel, isAltScreenStripMode, isExternalCliMode,
|
|
|
17
17
|
import { SseEvent } from '../sse-events.js';
|
|
18
18
|
import { webviewCapabilities } from '../../webview-capabilities.js';
|
|
19
19
|
import { htmlViewCapabilities } from '../../html-view-capabilities.js';
|
|
20
|
-
import { CreateSessionSchema, SessionNameSchema, SessionColorSchema, RunPromptSchema, SessionInputWithLimitSchema, ResizeSchema, AutoClearSchema, AutoCompactSchema, AutoResumeSchema, PinSessionSchema, ImageWatcherSchema, FlickerFilterSchema, QuickRunSchema, QuickStartSchema, InteractiveStartSchema, SessionOrderUpdateSchema, SessionWaitQuerySchema, SessionWaitOutputQuerySchema, CustomModelSelectionSchema, } from '../schemas.js';
|
|
20
|
+
import { CreateSessionSchema, SessionNameSchema, SessionColorSchema, RunPromptSchema, SessionInputWithLimitSchema, ResizeSchema, AutoClearSchema, AutoCompactSchema, AutoResumeSchema, KeepWarmSchema, PinSessionSchema, ImageWatcherSchema, FlickerFilterSchema, QuickRunSchema, QuickStartSchema, InteractiveStartSchema, SessionOrderUpdateSchema, SessionWaitQuerySchema, SessionWaitOutputQuerySchema, CustomModelSelectionSchema, } from '../schemas.js';
|
|
21
21
|
import { readCustomModelHosts } from '../../custom-model-hosts.js';
|
|
22
22
|
import { applyCustomModelInjection, removeConfigDir } from '../../custom-model-injection-apply.js';
|
|
23
23
|
import { getLlamaSwapStatus, triggerLlamaSwapLoad, exceedsSafeContextFloor, CLAUDE_MIN_SAFE_CONTEXT_TOKENS, } from './custom-model-routes.js';
|
|
@@ -27,7 +27,7 @@ import { TabLayoutValidationError } from '../../tab-layout.js';
|
|
|
27
27
|
import { sessionWaits, resolveWaitSignals, sessionHookOptions, signalForStatus, WaitCapacityError, } from '../session-wait-registry.js';
|
|
28
28
|
import { RemoteWakeRegistry, REMOTE_WAKE_REQUEST_READY_TIMEOUT_MS, createDefaultRemoteWakeDeps, isProbeable, } from '../../remote-wake.js';
|
|
29
29
|
import { clampWaitMs, MAX_BUFFER_SCAN_BYTES } from '../../config/agent-wait.js';
|
|
30
|
-
import { autoConfigureRalph, canAccessOwned, CASES_DIR, findPersistedSessionOrFail, findSessionOrFail, getAuthUser, isAdmin, isPlainPromptInput, isWorkingDirAllowed, ownerFor, parseBody, persistAndBroadcastSession, resolveAgentCaseOrigin, resolveCasesDir, resolveParentSessionId, sessionCapacityMessage, SETTINGS_PATH, validatePathWithinBase, } from '../route-helpers.js';
|
|
30
|
+
import { autoConfigureRalph, canAccessOwned, CASES_DIR, findPersistedSessionOrFail, findSessionOrFail, getAuthUser, isAdmin, isPlainPromptInput, isWorkingDirAllowed, ownerFor, parseBody, persistAndBroadcastSession, readJsonConfig, resolveAgentCaseOrigin, resolveCasesDir, resolveParentSessionId, sessionCapacityMessage, SETTINGS_PATH, validatePathWithinBase, } from '../route-helpers.js';
|
|
31
31
|
import { buildAgentCaseMarker, writeAgentCaseMarker } from '../../agent-case-marker.js';
|
|
32
32
|
import { canUsernameRunPrivilegedCommands, resolveClaudeModeForUsername } from '../../user-store.js';
|
|
33
33
|
import { clampEnvOverridesForOwner } from '../../session-env-clamp.js';
|
|
@@ -58,13 +58,48 @@ import { describeUnknownPath, probePathKind } from '../../utils/index.js';
|
|
|
58
58
|
import { findLatestOmpSessionId } from '../../utils/omp-session-resolver.js';
|
|
59
59
|
import { scanOmpSessionsHistory } from '../../omp-transcript.js';
|
|
60
60
|
import { scanCodexSessionsHistory, codexThreadBySessionId } from '../../codex-transcript.js';
|
|
61
|
-
import { getLastTranscriptResponse, isExternalCliTranscriptMode, parseExternalCliTranscript, selectLastAnsweredTurn, } from '../response-viewer-transcript.js';
|
|
61
|
+
import { transcriptNeedsScreenCapture, getLastTranscriptResponse, isExternalCliTranscriptMode, parseExternalCliTranscript, selectLastAnsweredTurn, } from '../response-viewer-transcript.js';
|
|
62
62
|
import { readDeepSeekLastResponse } from '../../deepseek-transcript.js';
|
|
63
63
|
import { appendClaudeCustomTitle } from '../../claude-session-title.js';
|
|
64
64
|
import { UPLOADS_DIR } from '../paste-image-gc.js';
|
|
65
65
|
// Path to linked-cases registry (same file used by case-routes resolveCasePath)
|
|
66
66
|
const LINKED_CASES_FILE = dataPath('linked-cases.json');
|
|
67
67
|
const CODEMAN_CONFIG_DIR = getDataDir();
|
|
68
|
+
const SAFE_CASE_NAME = /^[a-zA-Z0-9_-]+$/;
|
|
69
|
+
/** `<repo>/.claude/worktrees/<name>`: the layout Claude Code's own worktree feature creates. */
|
|
70
|
+
const CLAUDE_WORKTREE_PATH = /^(.*)\/\.claude\/worktrees\/([^/]+)\/?$/;
|
|
71
|
+
/** Did the committed layout put this session in a named group (not Ungrouped)? */
|
|
72
|
+
function landedInGroup(layout, sessionId) {
|
|
73
|
+
return !!layout?.groups.some((group) => group.refs.some((ref) => ref.kind === 'session' && ref.id === sessionId));
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* The case a POST /api/sessions `workingDir` belongs to, for its default session
|
|
77
|
+
* group: a direct child of the caller's case space, or (admin / single-user, as
|
|
78
|
+
* GET /api/cases lists them) a linked case's exact path, or a Claude worktree
|
|
79
|
+
* (`<case>/.claude/worktrees/<name>`) of either. Lexical only: the browser sends
|
|
80
|
+
* the path the case list gave it, and this must never touch a possibly-dead
|
|
81
|
+
* mount. Anything else is no case, which keeps today's placement.
|
|
82
|
+
*/
|
|
83
|
+
async function inferLocalCaseName(req, workingDir) {
|
|
84
|
+
// The Git worktrees modal launches Claude through this route in the worktree, and every other CLI
|
|
85
|
+
// through quick-start with the case name: both are sessions of that case.
|
|
86
|
+
const resolved = resolvePath(workingDir);
|
|
87
|
+
const target = CLAUDE_WORKTREE_PATH.exec(resolved)?.[1] || resolved;
|
|
88
|
+
const user = getAuthUser(req);
|
|
89
|
+
if (dirname(target) === resolvePath(resolveCasesDir(user))) {
|
|
90
|
+
const name = basename(target);
|
|
91
|
+
if (SAFE_CASE_NAME.test(name))
|
|
92
|
+
return name;
|
|
93
|
+
}
|
|
94
|
+
if (isMultiUserMode() && !isAdmin(req))
|
|
95
|
+
return undefined;
|
|
96
|
+
const linked = await readJsonConfig(LINKED_CASES_FILE, 'linked cases', {});
|
|
97
|
+
for (const [name, path] of Object.entries(linked)) {
|
|
98
|
+
if (SAFE_CASE_NAME.test(name) && typeof path === 'string' && resolvePath(path) === target)
|
|
99
|
+
return name;
|
|
100
|
+
}
|
|
101
|
+
return undefined;
|
|
102
|
+
}
|
|
68
103
|
// Pre-compiled regex for terminal buffer cleaning (avoids per-request compilation)
|
|
69
104
|
// eslint-disable-next-line no-control-regex
|
|
70
105
|
const CLAUDE_BANNER_PATTERN = /\x1b\[1mClaud/;
|
|
@@ -189,6 +224,22 @@ export function stripInkRedrawBloat(buffer) {
|
|
|
189
224
|
* (valid UTF-8, no NUL byte) rather than on a signature, since plain text has none.
|
|
190
225
|
*/
|
|
191
226
|
const TEXT_UPLOAD_EXTS = new Set(['.txt', '.md', '.csv', '.tsv', '.json', '.log', '.yaml', '.yml', '.xml']);
|
|
227
|
+
/**
|
|
228
|
+
* Documents a user hands an agent to read: the office formats plus the text
|
|
229
|
+
* family. Curated on purpose, never SUPPORTED_ATTACHMENT_EXTENSIONS or
|
|
230
|
+
* EDITABLE_EXTENSIONS, which carry sh/py/js/html; a prompt upload is a thing
|
|
231
|
+
* to read, not a thing to run or serve. Each is judged on its bytes by
|
|
232
|
+
* imageMagicMatchesExt (signature, or UTF-8 without NUL for text). The browser
|
|
233
|
+
* keeps its own copies (test/upload-document-list-parity.test.ts pins them).
|
|
234
|
+
*/
|
|
235
|
+
export const ALLOWED_DOCUMENT_EXTS = new Set([
|
|
236
|
+
'.pdf',
|
|
237
|
+
'.docx',
|
|
238
|
+
'.pptx',
|
|
239
|
+
'.xlsx',
|
|
240
|
+
'.zip',
|
|
241
|
+
...TEXT_UPLOAD_EXTS,
|
|
242
|
+
]);
|
|
192
243
|
/**
|
|
193
244
|
* Validate upload bytes against a declared extension. Sniffs the first ~12 bytes
|
|
194
245
|
* for a known magic-number signature (a text file is instead required to be
|
|
@@ -456,7 +507,7 @@ async function clampExternalCliBypassForOwner(owner, configs) {
|
|
|
456
507
|
* named individually, so `privilegedParams` on anything outside that list was declared but
|
|
457
508
|
* unreachable.
|
|
458
509
|
*/
|
|
459
|
-
export async function _clampExternalCliBypassForOwner(owner, codexConfig, geminiConfig, antigravityConfig, piConfig, grokConfig, deepSeekConfig) {
|
|
510
|
+
export async function _clampExternalCliBypassForOwner(owner, codexConfig, geminiConfig, antigravityConfig, piConfig, grokConfig, deepSeekConfig, copilotConfig) {
|
|
460
511
|
const out = await clampExternalCliBypassForOwner(owner, {
|
|
461
512
|
codexConfig,
|
|
462
513
|
geminiConfig,
|
|
@@ -464,6 +515,7 @@ export async function _clampExternalCliBypassForOwner(owner, codexConfig, gemini
|
|
|
464
515
|
piConfig,
|
|
465
516
|
grokConfig,
|
|
466
517
|
deepSeekConfig,
|
|
518
|
+
copilotConfig,
|
|
467
519
|
});
|
|
468
520
|
return out;
|
|
469
521
|
}
|
|
@@ -1102,7 +1154,7 @@ options = {}) {
|
|
|
1102
1154
|
// Section 6.3: force non-granted users to a classifier-guarded mode.
|
|
1103
1155
|
const effectiveClaudeMode = await resolveClaudeModeForUsername(claudeModeConfig.claudeMode, owner);
|
|
1104
1156
|
// Section 6.3: clamp Codex/Gemini/Antigravity bypass switches for a non-granted owner (no-op single-user/granted).
|
|
1105
|
-
const { codexConfig: gatedCodexConfig, geminiConfig: gatedGeminiConfig, antigravityConfig: gatedAntigravityConfig, piConfig: gatedPiConfig, grokConfig: gatedGrokConfig, deepSeekConfig: gatedDeepSeekConfig, } = await _clampExternalCliBypassForOwner(owner, launchBody.codexConfig, launchBody.geminiConfig, launchBody.antigravityConfig, launchBody.piConfig, launchBody.grokConfig, launchBody.deepSeekConfig);
|
|
1157
|
+
const { codexConfig: gatedCodexConfig, geminiConfig: gatedGeminiConfig, antigravityConfig: gatedAntigravityConfig, piConfig: gatedPiConfig, grokConfig: gatedGrokConfig, deepSeekConfig: gatedDeepSeekConfig, copilotConfig: gatedCopilotConfig, } = await _clampExternalCliBypassForOwner(owner, launchBody.codexConfig, launchBody.geminiConfig, launchBody.antigravityConfig, launchBody.piConfig, launchBody.grokConfig, launchBody.deepSeekConfig, launchBody.copilotConfig);
|
|
1106
1158
|
const terminalHistoryConfig = await ctx.getTerminalHistoryConfig();
|
|
1107
1159
|
const session = new Session({
|
|
1108
1160
|
workingDir,
|
|
@@ -1120,6 +1172,7 @@ options = {}) {
|
|
|
1120
1172
|
antigravityConfig: mode === 'antigravity' ? gatedAntigravityConfig : undefined,
|
|
1121
1173
|
piConfig: mode === 'pi' ? gatedPiConfig : undefined,
|
|
1122
1174
|
grokConfig: mode === 'grok' ? gatedGrokConfig : undefined,
|
|
1175
|
+
copilotConfig: mode === 'copilot' ? gatedCopilotConfig : undefined,
|
|
1123
1176
|
deepSeekConfig: mode === 'deepseek' ? gatedDeepSeekConfig : undefined,
|
|
1124
1177
|
ompConfig: resolveOmpConfigForCreate(mode, workingDir, launchBody.ompConfig),
|
|
1125
1178
|
resumeSessionId: validatedResumeId,
|
|
@@ -1131,9 +1184,13 @@ options = {}) {
|
|
|
1131
1184
|
owner,
|
|
1132
1185
|
parentSessionId: resolveParentSessionId(ctx, req, body.parentSessionId, owner),
|
|
1133
1186
|
});
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1187
|
+
// Only a named workingDir: without one the session starts in the server's own cwd, which is no case.
|
|
1188
|
+
const placementCase = remote || !body.workingDir ? undefined : await inferLocalCaseName(req, workingDir);
|
|
1189
|
+
const placement = {
|
|
1190
|
+
...(body.tabGroupId ? { tabGroupId: body.tabGroupId } : {}),
|
|
1191
|
+
...(placementCase ? { caseName: placementCase } : {}),
|
|
1192
|
+
};
|
|
1193
|
+
const tabLayout = Object.keys(placement).length > 0 ? await ctx.addSession(session, placement) : await ctx.addSession(session);
|
|
1137
1194
|
ctx.store.incrementSessionsCreated();
|
|
1138
1195
|
ctx.persistSessionState(session);
|
|
1139
1196
|
await ctx.setupSessionListeners(session);
|
|
@@ -1148,9 +1205,13 @@ options = {}) {
|
|
|
1148
1205
|
// Avoids serializing 2-3MB of terminal+text buffers per session creation.
|
|
1149
1206
|
const lightState = ctx.getSessionStateWithRespawn(session);
|
|
1150
1207
|
ctx.broadcast(SseEvent.SessionCreated, lightState);
|
|
1151
|
-
// A grouped creation
|
|
1152
|
-
// draws the tab inside its group at once
|
|
1153
|
-
|
|
1208
|
+
// A grouped creation (asked for, or its case's default group) also returns the
|
|
1209
|
+
// layout that placed it, so the browser draws the tab inside its group at once
|
|
1210
|
+
// instead of after a re-read.
|
|
1211
|
+
return {
|
|
1212
|
+
session: lightState,
|
|
1213
|
+
...(body.tabGroupId || landedInGroup(tabLayout, session.id) ? { tabLayout } : {}),
|
|
1214
|
+
};
|
|
1154
1215
|
});
|
|
1155
1216
|
// ========== Rename Session ==========
|
|
1156
1217
|
app.put('/api/sessions/:id/name', async (req) => {
|
|
@@ -1228,6 +1289,7 @@ options = {}) {
|
|
|
1228
1289
|
return createErrorResponse(ApiErrorCode.NOT_FOUND, 'Model endpoint not found');
|
|
1229
1290
|
}
|
|
1230
1291
|
const contextLength = endpoint.modelContextLengths?.[body.modelId];
|
|
1292
|
+
const slotCount = endpoint.modelSlotCounts?.[body.modelId];
|
|
1231
1293
|
// Some CLIs (today: only claude) carry enough of their own fixed system-prompt/tool-
|
|
1232
1294
|
// schema overhead that a small enough real context guarantees a first-message failure
|
|
1233
1295
|
// no matter what CLAUDE_CODE_MAX_CONTEXT_TOKENS says — confirmed live at ~36.4K tokens
|
|
@@ -1237,12 +1299,13 @@ options = {}) {
|
|
|
1237
1299
|
// `confirmed`, which still means both) — NOT by `confirmedSwap`: this warning is
|
|
1238
1300
|
// about the caller's own session, and the swap warning below is about someone
|
|
1239
1301
|
// else's, so an answer to one is not consent to the other.
|
|
1240
|
-
if (!(body.confirmed || body.confirmedContext) && exceedsSafeContextFloor(entry, contextLength)) {
|
|
1302
|
+
if (!(body.confirmed || body.confirmedContext) && exceedsSafeContextFloor(entry, contextLength, slotCount)) {
|
|
1241
1303
|
return {
|
|
1242
1304
|
requiresContextWarning: true,
|
|
1243
1305
|
modelId: body.modelId,
|
|
1244
1306
|
contextLength,
|
|
1245
1307
|
minSafeContextTokens: CLAUDE_MIN_SAFE_CONTEXT_TOKENS,
|
|
1308
|
+
...(slotCount && slotCount > 1 ? { slotCount } : {}),
|
|
1246
1309
|
};
|
|
1247
1310
|
}
|
|
1248
1311
|
// llama.cpp runs exactly one model at a time; llama-swap unloads and reloads it on
|
|
@@ -2417,7 +2480,13 @@ options = {}) {
|
|
|
2417
2480
|
// where a real rollout file is the better source.
|
|
2418
2481
|
if (isExternalCliTranscriptMode(session.mode)) {
|
|
2419
2482
|
const externalQuery = req.query;
|
|
2420
|
-
|
|
2483
|
+
// A repainting full-screen TUI (copilot) streams absolute cursor moves, not lines, so the
|
|
2484
|
+
// buffer cannot be segmented; read the pane's visible screen instead, and fall back to the
|
|
2485
|
+
// buffer when there is no tmux pane to ask (direct PTY, tests).
|
|
2486
|
+
const screen = transcriptNeedsScreenCapture(session.mode) && session.muxName
|
|
2487
|
+
? (ctx.mux.capturePaneText?.(session.muxName) ?? null)
|
|
2488
|
+
: null;
|
|
2489
|
+
const blocks = parseExternalCliTranscript(screen ?? session.terminalBuffer, session.mode);
|
|
2421
2490
|
return {
|
|
2422
2491
|
text: getLastTranscriptResponse(blocks),
|
|
2423
2492
|
timestamp: '',
|
|
@@ -3065,6 +3134,32 @@ options = {}) {
|
|
|
3065
3134
|
},
|
|
3066
3135
|
};
|
|
3067
3136
|
});
|
|
3137
|
+
// ========== Prompt-cache keep-warm ==========
|
|
3138
|
+
// Per-session opt-in for the keep-alive nudge (prompt-cache-keepwarm.ts). `enabled: null`
|
|
3139
|
+
// clears the choice so the app-wide default applies again. Refused for a CLI that reports
|
|
3140
|
+
// no cache telemetry on its statusline: without `expires_at` there is nothing to schedule.
|
|
3141
|
+
app.post('/api/sessions/:id/keep-warm', async (req) => {
|
|
3142
|
+
const { id } = req.params;
|
|
3143
|
+
const body = parseBody(KeepWarmSchema, req.body, 'Invalid request body');
|
|
3144
|
+
const session = findSessionOrFail(ctx, id, req);
|
|
3145
|
+
if (!getCli(session.mode)?.capabilities.statusLineTelemetry) {
|
|
3146
|
+
return createErrorResponse(ApiErrorCode.INVALID_INPUT, 'Prompt-cache keep-warm needs the cache telemetry this CLI does not report');
|
|
3147
|
+
}
|
|
3148
|
+
session.setKeepWarmEnabled(body.enabled ?? undefined);
|
|
3149
|
+
// Re-evaluate BEFORE the state broadcast and the response: both carry `keepWarm`, and a
|
|
3150
|
+
// page that received the previous status would overwrite the one the toggle just produced.
|
|
3151
|
+
await ctx.keepWarm.schedule(id);
|
|
3152
|
+
persistAndBroadcastSession(ctx, session);
|
|
3153
|
+
return {
|
|
3154
|
+
success: true,
|
|
3155
|
+
data: {
|
|
3156
|
+
keepWarm: {
|
|
3157
|
+
enabled: session.keepWarmEnabled ?? null,
|
|
3158
|
+
status: session.keepWarm,
|
|
3159
|
+
},
|
|
3160
|
+
},
|
|
3161
|
+
};
|
|
3162
|
+
});
|
|
3068
3163
|
// ========== Auto-Resume (usage-limit pause) ==========
|
|
3069
3164
|
app.post('/api/sessions/:id/auto-resume', async (req) => {
|
|
3070
3165
|
const { id } = req.params;
|
|
@@ -3227,7 +3322,7 @@ options = {}) {
|
|
|
3227
3322
|
const capMsg = sessionCapacityMessage(ctx.sessions, owner);
|
|
3228
3323
|
if (capMsg)
|
|
3229
3324
|
return createErrorResponse(ApiErrorCode.SESSION_BUSY, capMsg);
|
|
3230
|
-
const { caseName = 'testcase', sessionName, mode = 'claude', modelOverride, openCodeConfig, codexConfig, geminiConfig, antigravityConfig, piConfig, grokConfig, deepSeekConfig, ompConfig, envOverrides, effort, advisorModel, parentSessionId, agentOrigin, customModel, tabGroupId, worktree, } = parseBody(QuickStartSchema, req.body);
|
|
3325
|
+
const { caseName = 'testcase', sessionName, mode = 'claude', modelOverride, openCodeConfig, codexConfig, geminiConfig, antigravityConfig, piConfig, grokConfig, copilotConfig, deepSeekConfig, ompConfig, envOverrides, effort, advisorModel, parentSessionId, agentOrigin, customModel, tabGroupId, worktree, } = parseBody(QuickStartSchema, req.body);
|
|
3231
3326
|
// Resolved ONCE here: the same value labels a case directory this request creates
|
|
3232
3327
|
// (agent-case-marker.ts) and draws the tab lineage line on the session below.
|
|
3233
3328
|
const qsParentSessionId = resolveParentSessionId(ctx, req, parentSessionId, owner);
|
|
@@ -3277,6 +3372,7 @@ options = {}) {
|
|
|
3277
3372
|
antigravityConfig ||
|
|
3278
3373
|
piConfig ||
|
|
3279
3374
|
grokConfig ||
|
|
3375
|
+
copilotConfig ||
|
|
3280
3376
|
deepSeekConfig ||
|
|
3281
3377
|
ompConfig ||
|
|
3282
3378
|
openCodeConfig ||
|
|
@@ -3335,6 +3431,7 @@ options = {}) {
|
|
|
3335
3431
|
antigravityConfig ||
|
|
3336
3432
|
piConfig ||
|
|
3337
3433
|
grokConfig ||
|
|
3434
|
+
copilotConfig ||
|
|
3338
3435
|
deepSeekConfig ||
|
|
3339
3436
|
ompConfig ||
|
|
3340
3437
|
openCodeConfig ||
|
|
@@ -3419,6 +3516,7 @@ options = {}) {
|
|
|
3419
3516
|
antigravityConfig,
|
|
3420
3517
|
piConfig,
|
|
3421
3518
|
grokConfig,
|
|
3519
|
+
copilotConfig,
|
|
3422
3520
|
deepSeekConfig,
|
|
3423
3521
|
}));
|
|
3424
3522
|
if (qsLaunchError) {
|
|
@@ -3605,6 +3703,7 @@ options = {}) {
|
|
|
3605
3703
|
antigravityConfig,
|
|
3606
3704
|
piConfig,
|
|
3607
3705
|
grokConfig,
|
|
3706
|
+
copilotConfig,
|
|
3608
3707
|
deepSeekConfig,
|
|
3609
3708
|
ompConfig,
|
|
3610
3709
|
};
|
|
@@ -3623,7 +3722,7 @@ options = {}) {
|
|
|
3623
3722
|
const qsClaudeModeConfig = await ctx.getClaudeModeConfig();
|
|
3624
3723
|
const qsEffectiveClaudeMode = await resolveClaudeModeForUsername(qsClaudeModeConfig.claudeMode, owner);
|
|
3625
3724
|
// Section 6.3: clamp Codex/Gemini/Antigravity bypass switches for a non-granted owner (no-op single-user/granted).
|
|
3626
|
-
const { codexConfig: qsGatedCodexConfig, geminiConfig: qsGatedGeminiConfig, antigravityConfig: qsGatedAntigravityConfig, piConfig: qsGatedPiConfig, grokConfig: qsGatedGrokConfig, deepSeekConfig: qsGatedDeepSeekConfig, } = await _clampExternalCliBypassForOwner(owner, qsLaunchConfigs.codexConfig, qsLaunchConfigs.geminiConfig, qsLaunchConfigs.antigravityConfig, qsLaunchConfigs.piConfig, qsLaunchConfigs.grokConfig, qsLaunchConfigs.deepSeekConfig);
|
|
3725
|
+
const { codexConfig: qsGatedCodexConfig, geminiConfig: qsGatedGeminiConfig, antigravityConfig: qsGatedAntigravityConfig, piConfig: qsGatedPiConfig, grokConfig: qsGatedGrokConfig, deepSeekConfig: qsGatedDeepSeekConfig, copilotConfig: qsGatedCopilotConfig, } = await _clampExternalCliBypassForOwner(owner, qsLaunchConfigs.codexConfig, qsLaunchConfigs.geminiConfig, qsLaunchConfigs.antigravityConfig, qsLaunchConfigs.piConfig, qsLaunchConfigs.grokConfig, qsLaunchConfigs.deepSeekConfig, qsLaunchConfigs.copilotConfig);
|
|
3627
3726
|
const qsTerminalHistoryConfig = await ctx.getTerminalHistoryConfig();
|
|
3628
3727
|
const qsGatedEnvOverrides = await clampEnvOverridesForOwner(owner, envOverrides);
|
|
3629
3728
|
const qsResolvedOmpConfig = resolveOmpConfigForCreate(mode, workspacePath, qsLaunchOmpConfig);
|
|
@@ -3660,6 +3759,7 @@ options = {}) {
|
|
|
3660
3759
|
if (!cmEndpoint)
|
|
3661
3760
|
return createErrorResponse(ApiErrorCode.NOT_FOUND, 'Model endpoint not found');
|
|
3662
3761
|
const cmContextLength = cmEndpoint.modelContextLengths?.[customModel.modelId];
|
|
3762
|
+
const cmSlotCount = cmEndpoint.modelSlotCounts?.[customModel.modelId];
|
|
3663
3763
|
// See the dedicated route's own comment for the full reasoning: some CLIs' own fixed
|
|
3664
3764
|
// overhead can exceed a small enough real context on the very first message,
|
|
3665
3765
|
// regardless of contextLengthVar. Warn before creating a session that's certain to
|
|
@@ -3667,12 +3767,13 @@ options = {}) {
|
|
|
3667
3767
|
// See the dedicated route above for why this reads `confirmedContext` and never
|
|
3668
3768
|
// `confirmedSwap`.
|
|
3669
3769
|
if (!(customModel.confirmed || customModel.confirmedContext) &&
|
|
3670
|
-
exceedsSafeContextFloor(cmEntry, cmContextLength)) {
|
|
3770
|
+
exceedsSafeContextFloor(cmEntry, cmContextLength, cmSlotCount)) {
|
|
3671
3771
|
return {
|
|
3672
3772
|
requiresContextWarning: true,
|
|
3673
3773
|
modelId: customModel.modelId,
|
|
3674
3774
|
contextLength: cmContextLength,
|
|
3675
3775
|
minSafeContextTokens: CLAUDE_MIN_SAFE_CONTEXT_TOKENS,
|
|
3776
|
+
...(cmSlotCount && cmSlotCount > 1 ? { slotCount: cmSlotCount } : {}),
|
|
3676
3777
|
};
|
|
3677
3778
|
}
|
|
3678
3779
|
// See the dedicated route's own comment for the full reasoning: llama.cpp runs one
|
|
@@ -3758,6 +3859,7 @@ options = {}) {
|
|
|
3758
3859
|
antigravityConfig: mode === 'antigravity' ? qsGatedAntigravityConfig : undefined,
|
|
3759
3860
|
piConfig: mode === 'pi' ? qsGatedPiConfig : undefined,
|
|
3760
3861
|
grokConfig: mode === 'grok' ? qsGatedGrokConfig : undefined,
|
|
3862
|
+
copilotConfig: mode === 'copilot' ? qsGatedCopilotConfig : undefined,
|
|
3761
3863
|
deepSeekConfig: mode === 'deepseek' ? qsGatedDeepSeekConfig : undefined,
|
|
3762
3864
|
ompConfig: qsResolvedOmpConfig,
|
|
3763
3865
|
envOverrides: qsCustomModelEnvOverrides,
|
|
@@ -3805,7 +3907,7 @@ options = {}) {
|
|
|
3805
3907
|
session.ralphTracker.enableAutoEnable(); // Allow re-enabling on restart
|
|
3806
3908
|
}
|
|
3807
3909
|
}
|
|
3808
|
-
const tabLayout =
|
|
3910
|
+
const tabLayout = await ctx.addSession(session, { caseName, ...(tabGroupId ? { tabGroupId } : {}) });
|
|
3809
3911
|
ctx.store.incrementSessionsCreated();
|
|
3810
3912
|
ctx.persistSessionState(session);
|
|
3811
3913
|
await ctx.setupSessionListeners(session);
|
|
@@ -3891,7 +3993,7 @@ options = {}) {
|
|
|
3891
3993
|
caseName,
|
|
3892
3994
|
...(worktree ? { worktree, workingDir: workspacePath } : {}),
|
|
3893
3995
|
...(customModel ? { modelSwapInProgress: qsCustomModelSwapInProgress } : {}),
|
|
3894
|
-
...(tabGroupId ? { tabLayout } : {}),
|
|
3996
|
+
...(tabGroupId || landedInGroup(tabLayout, session.id) ? { tabLayout } : {}),
|
|
3895
3997
|
};
|
|
3896
3998
|
}
|
|
3897
3999
|
catch (err) {
|
|
@@ -4016,8 +4118,6 @@ options = {}) {
|
|
|
4016
4118
|
}
|
|
4017
4119
|
return sawNonCli;
|
|
4018
4120
|
}
|
|
4019
|
-
/** `<repo>/.claude/worktrees/<name>` — the layout Claude Code's own worktree feature creates. */
|
|
4020
|
-
const CLAUDE_WORKTREE_PATH = /^(.*)\/\.claude\/worktrees\/([^/]+)\/?$/;
|
|
4021
4121
|
/**
|
|
4022
4122
|
* Recover cwd / branch / worktree from a transcript chunk.
|
|
4023
4123
|
*
|
|
@@ -4782,12 +4882,6 @@ options = {}) {
|
|
|
4782
4882
|
// so an uploaded video is a type that previews. 3gp/mkv are deliberately out: they
|
|
4783
4883
|
// would upload but not preview, and a phone's camera roll produces neither.
|
|
4784
4884
|
const ALLOWED_VIDEO_EXTS = new Set([...VIDEO_ATTACHMENT_EXTENSIONS].map((e) => `.${e}`));
|
|
4785
|
-
// Documents a user hands an agent to read: the office formats plus the text
|
|
4786
|
-
// family. Curated on purpose, never SUPPORTED_ATTACHMENT_EXTENSIONS or
|
|
4787
|
-
// EDITABLE_EXTENSIONS, which carry sh/py/js/html; a prompt upload is a thing
|
|
4788
|
-
// to read, not a thing to run or serve. Each is judged on its bytes by
|
|
4789
|
-
// imageMagicMatchesExt (signature, or UTF-8 without NUL for text).
|
|
4790
|
-
const ALLOWED_DOCUMENT_EXTS = new Set(['.pdf', '.docx', '.pptx', '.xlsx', '.zip', ...TEXT_UPLOAD_EXTS]);
|
|
4791
4885
|
const VIDEO_MIME_EXT = {
|
|
4792
4886
|
mp4: '.mp4',
|
|
4793
4887
|
quicktime: '.mov',
|
|
@@ -5006,12 +5100,19 @@ options = {}) {
|
|
|
5006
5100
|
if (!imageMagicMatchesExt(imageBytes, origExt)) {
|
|
5007
5101
|
// The first bytes of a text file are user content, so for text the log
|
|
5008
5102
|
// names the failure and never the bytes; a signature is safe to print.
|
|
5009
|
-
|
|
5010
|
-
|
|
5103
|
+
// A text refusal names the encoding, since an Excel or Notepad export
|
|
5104
|
+
// (UTF-16 with a BOM, or Windows-1252) is not a corrupt file.
|
|
5105
|
+
const isText = TEXT_UPLOAD_EXTS.has(origExt);
|
|
5106
|
+
const bom = imageBytes.length >= 2 ? imageBytes.readUInt16BE(0) : 0;
|
|
5107
|
+
const utf16 = bom === 0xfffe || bom === 0xfeff;
|
|
5108
|
+
const detail = isText
|
|
5109
|
+
? `reason=${utf16 ? 'utf16-bom' : imageBytes.includes(0) ? 'nul-byte' : 'not-utf8'}`
|
|
5011
5110
|
: `magic=${imageBytes.subarray(0, 12).toString('hex')}`;
|
|
5012
5111
|
console.warn(`[paste-image] document mismatch: filename=${JSON.stringify(part.filename)} mime=${JSON.stringify(part.mimetype)} declaredExt=${origExt} size=${imageBytes.length} ${detail}`);
|
|
5013
5112
|
reply.code(415);
|
|
5014
|
-
return createErrorResponse(ApiErrorCode.INVALID_INPUT,
|
|
5113
|
+
return createErrorResponse(ApiErrorCode.INVALID_INPUT, isText
|
|
5114
|
+
? `This ${origExt} file is ${utf16 ? 'UTF-16 text, not UTF-8' : 'not UTF-8 text'}; save it as UTF-8 and attach it again`
|
|
5115
|
+
: `File bytes do not match declared type ${origExt}`);
|
|
5015
5116
|
}
|
|
5016
5117
|
const saved = await writeBufferedUpload(session.workingDir, origExt, imageBytes, sanitizeUploadStem(part.filename));
|
|
5017
5118
|
if (!saved) {
|