@songsid/agend 2.3.4-beta.6 → 2.3.5-alpha.2
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 +2 -0
- package/README.zh-TW.md +2 -0
- package/THIRD_PARTY_NOTICES.md +53 -0
- package/dist/agent-endpoint.d.ts +5 -1
- package/dist/agent-endpoint.js +12 -2
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/backend/claude-code.d.ts +15 -12
- package/dist/backend/claude-code.js +137 -133
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-package.d.ts +5 -2
- package/dist/backend/codex-package.js +1 -5
- package/dist/backend/codex-package.js.map +1 -1
- package/dist/backend/codex.d.ts +136 -113
- package/dist/backend/codex.js +1271 -1219
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/grok-model-cache.d.ts +7 -0
- package/dist/backend/grok-model-cache.js +65 -0
- package/dist/backend/grok-model-cache.js.map +1 -0
- package/dist/backend/grok.d.ts +2 -0
- package/dist/backend/grok.js +22 -0
- package/dist/backend/grok.js.map +1 -1
- package/dist/backend/native-pane.d.ts +9 -0
- package/dist/backend/native-pane.js +59 -0
- package/dist/backend/native-pane.js.map +1 -0
- package/dist/backend/types.d.ts +7 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/channel/ipc-bridge.d.ts +2 -0
- package/dist/channel/mcp-tools.js +1 -1
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/cli.js +6 -2
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.js +59 -0
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +16 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +62 -5
- package/dist/daemon.js +749 -267
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-consumed-watch.d.ts +15 -3
- package/dist/delivery-consumed-watch.js +23 -1
- package/dist/delivery-consumed-watch.js.map +1 -1
- package/dist/delivery-outbox.d.ts +5 -0
- package/dist/delivery-outbox.js +32 -4
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-reconciliation.d.ts +2 -35
- package/dist/delivery-reconciliation.js +2 -156
- package/dist/delivery-reconciliation.js.map +1 -1
- package/dist/fleet-context.d.ts +2 -2
- package/dist/fleet-level-config.d.ts +1 -1
- package/dist/fleet-level-config.js +2 -0
- package/dist/fleet-level-config.js.map +1 -1
- package/dist/fleet-manager.d.ts +125 -2
- package/dist/fleet-manager.js +1157 -27
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-windows-runtime.d.ts +92 -0
- package/dist/fleet-windows-runtime.js +373 -0
- package/dist/fleet-windows-runtime.js.map +1 -0
- package/dist/instance-config-impact.js +4 -0
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +20 -3
- package/dist/instance-lifecycle.js +358 -156
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/local-pane-endpoint.d.ts +46 -0
- package/dist/local-pane-endpoint.js +189 -0
- package/dist/local-pane-endpoint.js.map +1 -0
- package/dist/local-pane-launcher.d.ts +2 -0
- package/dist/local-pane-launcher.js +38 -0
- package/dist/local-pane-launcher.js.map +1 -0
- package/dist/local-pane-stage.ps1 +101 -0
- package/dist/locale.js +34 -0
- package/dist/locale.js.map +1 -1
- package/dist/model-catalog-defaults.d.ts +14 -0
- package/dist/model-catalog-defaults.js +20 -0
- package/dist/model-catalog-defaults.js.map +1 -0
- package/dist/native-agent-admission.d.ts +7 -0
- package/dist/native-agent-admission.js +26 -0
- package/dist/native-agent-admission.js.map +1 -0
- package/dist/native-daemon-client.d.ts +13 -0
- package/dist/native-daemon-client.js +28 -0
- package/dist/native-daemon-client.js.map +1 -0
- package/dist/native-instance-api.d.ts +14 -0
- package/dist/native-instance-api.js +205 -0
- package/dist/native-instance-api.js.map +1 -0
- package/dist/native-instance-input.d.ts +84 -0
- package/dist/native-instance-input.js +54 -0
- package/dist/native-instance-input.js.map +1 -0
- package/dist/native-instance-registry.d.ts +32 -0
- package/dist/native-instance-registry.js +105 -0
- package/dist/native-instance-registry.js.map +1 -0
- package/dist/native-mcp.d.ts +40 -0
- package/dist/native-mcp.js +278 -0
- package/dist/native-mcp.js.map +1 -0
- package/dist/native-model-catalog.d.ts +5 -0
- package/dist/native-model-catalog.js +76 -0
- package/dist/native-model-catalog.js.map +1 -0
- package/dist/native-pane-crash-budget.d.ts +20 -0
- package/dist/native-pane-crash-budget.js +60 -0
- package/dist/native-pane-crash-budget.js.map +1 -0
- package/dist/native-pane-create.d.ts +26 -0
- package/dist/native-pane-create.js +53 -0
- package/dist/native-pane-create.js.map +1 -0
- package/dist/native-pane-lifecycle.d.ts +65 -0
- package/dist/native-pane-lifecycle.js +227 -0
- package/dist/native-pane-lifecycle.js.map +1 -0
- package/dist/native-pane-manager.d.ts +58 -0
- package/dist/native-pane-manager.js +563 -0
- package/dist/native-pane-manager.js.map +1 -0
- package/dist/native-pane-output-log.d.ts +15 -0
- package/dist/native-pane-output-log.js +108 -0
- package/dist/native-pane-output-log.js.map +1 -0
- package/dist/native-pane-reconciliation.d.ts +4 -0
- package/dist/native-pane-reconciliation.js +40 -0
- package/dist/native-pane-reconciliation.js.map +1 -0
- package/dist/native-pane-recovery.d.ts +12 -0
- package/dist/native-pane-recovery.js +10 -0
- package/dist/native-pane-recovery.js.map +1 -0
- package/dist/native-pane-tools.d.ts +2 -0
- package/dist/native-pane-tools.js +14 -0
- package/dist/native-pane-tools.js.map +1 -0
- package/dist/needs-you-hub.d.ts +2 -0
- package/dist/needs-you-hub.js +1 -0
- package/dist/needs-you-hub.js.map +1 -1
- package/dist/needs-you.d.ts +12 -1
- package/dist/needs-you.js +8 -1
- package/dist/needs-you.js.map +1 -1
- package/dist/outbound-handlers.d.ts +16 -2
- package/dist/outbound-handlers.js +67 -3
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +38 -0
- package/dist/outbound-schemas.js +12 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/pane-port.d.ts +107 -0
- package/dist/pane-port.js +2 -0
- package/dist/pane-port.js.map +1 -0
- package/dist/runtime-api.d.ts +8 -0
- package/dist/runtime-api.js +96 -0
- package/dist/runtime-api.js.map +1 -0
- package/dist/runtime-assets/@xterm/addon-serialize/LICENSE +21 -0
- package/dist/runtime-assets/@xterm/addon-serialize/lib/addon-serialize.js +2 -0
- package/dist/runtime-assets/@xterm/addon-serialize/lib/addon-serialize.mjs +19 -0
- package/dist/runtime-assets/@xterm/addon-serialize/package.json +29 -0
- package/dist/runtime-assets/@xterm/headless/LICENSE +21 -0
- package/dist/runtime-assets/@xterm/headless/lib-headless/xterm-headless.js +2 -0
- package/dist/runtime-assets/@xterm/headless/lib-headless/xterm-headless.mjs +46 -0
- package/dist/runtime-assets/@xterm/headless/package.json +29 -0
- package/dist/runtime-assets/node-pty/CONPTY-LICENSE +21 -0
- package/dist/runtime-assets/node-pty/CONPTY-NOTICE.md +503 -0
- package/dist/runtime-assets/node-pty/LICENSE +69 -0
- package/dist/runtime-assets/node-pty/lib/conpty_console_list_agent.js +16 -0
- package/dist/runtime-assets/node-pty/lib/eventEmitter2.js +47 -0
- package/dist/runtime-assets/node-pty/lib/index.js +52 -0
- package/dist/runtime-assets/node-pty/lib/interfaces.js +7 -0
- package/dist/runtime-assets/node-pty/lib/shared/conout.js +11 -0
- package/dist/runtime-assets/node-pty/lib/terminal.js +190 -0
- package/dist/runtime-assets/node-pty/lib/types.js +7 -0
- package/dist/runtime-assets/node-pty/lib/unixTerminal.js +346 -0
- package/dist/runtime-assets/node-pty/lib/utils.js +39 -0
- package/dist/runtime-assets/node-pty/lib/windowsConoutConnection.js +125 -0
- package/dist/runtime-assets/node-pty/lib/windowsPtyAgent.js +320 -0
- package/dist/runtime-assets/node-pty/lib/windowsTerminal.js +199 -0
- package/dist/runtime-assets/node-pty/lib/worker/conoutSocketWorker.js +22 -0
- package/dist/runtime-assets/node-pty/package.json +64 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty/OpenConsole.exe +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty/conpty.dll +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty.node +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-arm64/conpty_console_list.node +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty/OpenConsole.exe +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty/conpty.dll +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty.node +0 -0
- package/dist/runtime-assets/node-pty/prebuilds/win32-x64/conpty_console_list.node +0 -0
- package/dist/runtime-bundle.d.ts +17 -0
- package/dist/runtime-bundle.js +147 -0
- package/dist/runtime-bundle.js.map +1 -0
- package/dist/runtime-cli.d.ts +2 -0
- package/dist/runtime-cli.js +117 -0
- package/dist/runtime-cli.js.map +1 -0
- package/dist/runtime-client-store.d.ts +49 -0
- package/dist/runtime-client-store.js +292 -0
- package/dist/runtime-client-store.js.map +1 -0
- package/dist/runtime-client.d.ts +100 -0
- package/dist/runtime-client.js +700 -0
- package/dist/runtime-client.js.map +1 -0
- package/dist/runtime-coordinator.d.ts +82 -0
- package/dist/runtime-coordinator.js +864 -0
- package/dist/runtime-coordinator.js.map +1 -0
- package/dist/runtime-pane-assets.d.ts +17 -0
- package/dist/runtime-pane-assets.js +23 -0
- package/dist/runtime-pane-assets.js.map +1 -0
- package/dist/runtime-pane-boot.d.ts +2 -0
- package/dist/runtime-pane-boot.js +24 -0
- package/dist/runtime-pane-boot.js.map +1 -0
- package/dist/runtime-pane-codex-index.d.ts +1 -0
- package/dist/runtime-pane-codex-index.js +47 -0
- package/dist/runtime-pane-codex-index.js.map +1 -0
- package/dist/runtime-pane-config.d.ts +16 -0
- package/dist/runtime-pane-config.js +54 -0
- package/dist/runtime-pane-config.js.map +1 -0
- package/dist/runtime-pane-host.d.ts +43 -0
- package/dist/runtime-pane-host.js +403 -0
- package/dist/runtime-pane-host.js.map +1 -0
- package/dist/runtime-pane-job-owner.d.ts +1 -0
- package/dist/runtime-pane-job-owner.js +29 -0
- package/dist/runtime-pane-job-owner.js.map +1 -0
- package/dist/runtime-pane-job.ps1 +116 -0
- package/dist/runtime-pane-link.d.ts +40 -0
- package/dist/runtime-pane-link.js +246 -0
- package/dist/runtime-pane-link.js.map +1 -0
- package/dist/runtime-pane-mcp-protocol.d.ts +32 -0
- package/dist/runtime-pane-mcp-protocol.js +39 -0
- package/dist/runtime-pane-mcp-protocol.js.map +1 -0
- package/dist/runtime-pane-mcp-relay.d.ts +17 -0
- package/dist/runtime-pane-mcp-relay.js +70 -0
- package/dist/runtime-pane-mcp-relay.js.map +1 -0
- package/dist/runtime-pane-mcp.d.ts +43 -0
- package/dist/runtime-pane-mcp.js +137 -0
- package/dist/runtime-pane-mcp.js.map +1 -0
- package/dist/runtime-pane-native.d.ts +45 -0
- package/dist/runtime-pane-native.js +65 -0
- package/dist/runtime-pane-native.js.map +1 -0
- package/dist/runtime-pane-output.d.ts +19 -0
- package/dist/runtime-pane-output.js +53 -0
- package/dist/runtime-pane-output.js.map +1 -0
- package/dist/runtime-pane-port.d.ts +134 -0
- package/dist/runtime-pane-port.js +537 -0
- package/dist/runtime-pane-port.js.map +1 -0
- package/dist/runtime-pane-private.d.ts +14 -0
- package/dist/runtime-pane-private.js +51 -0
- package/dist/runtime-pane-private.js.map +1 -0
- package/dist/runtime-pane-private.ps1 +14 -0
- package/dist/runtime-pane-process.d.ts +4 -0
- package/dist/runtime-pane-process.js +108 -0
- package/dist/runtime-pane-process.js.map +1 -0
- package/dist/runtime-pane-protocol.d.ts +169 -0
- package/dist/runtime-pane-protocol.js +189 -0
- package/dist/runtime-pane-protocol.js.map +1 -0
- package/dist/runtime-pane-screen.d.ts +19 -0
- package/dist/runtime-pane-screen.js +76 -0
- package/dist/runtime-pane-screen.js.map +1 -0
- package/dist/runtime-pane-service.d.ts +11 -0
- package/dist/runtime-pane-service.js +221 -0
- package/dist/runtime-pane-service.js.map +1 -0
- package/dist/runtime-pane-stdio-protocol.d.ts +54 -0
- package/dist/runtime-pane-stdio-protocol.js +94 -0
- package/dist/runtime-pane-stdio-protocol.js.map +1 -0
- package/dist/runtime-pane-stdio.d.ts +3 -0
- package/dist/runtime-pane-stdio.js +123 -0
- package/dist/runtime-pane-stdio.js.map +1 -0
- package/dist/runtime-pane-store.d.ts +33 -0
- package/dist/runtime-pane-store.js +164 -0
- package/dist/runtime-pane-store.js.map +1 -0
- package/dist/runtime-pane-transcript-protocol.d.ts +43 -0
- package/dist/runtime-pane-transcript-protocol.js +58 -0
- package/dist/runtime-pane-transcript-protocol.js.map +1 -0
- package/dist/runtime-pane-transcript.d.ts +45 -0
- package/dist/runtime-pane-transcript.js +605 -0
- package/dist/runtime-pane-transcript.js.map +1 -0
- package/dist/runtime-pane-worker.d.ts +1 -0
- package/dist/runtime-pane-worker.js +154 -0
- package/dist/runtime-pane-worker.js.map +1 -0
- package/dist/runtime-protocol.d.ts +149 -0
- package/dist/runtime-protocol.js +19 -0
- package/dist/runtime-protocol.js.map +1 -0
- package/dist/runtime-switches.d.ts +79 -0
- package/dist/runtime-switches.js +90 -0
- package/dist/runtime-switches.js.map +1 -0
- package/dist/runtime-transcript-delta-reader.d.ts +23 -0
- package/dist/runtime-transcript-delta-reader.js +74 -0
- package/dist/runtime-transcript-delta-reader.js.map +1 -0
- package/dist/runtime-transcript-source.d.ts +36 -0
- package/dist/runtime-transcript-source.js +189 -0
- package/dist/runtime-transcript-source.js.map +1 -0
- package/dist/runtime-windows-bridge.d.ts +41 -0
- package/dist/runtime-windows-bridge.js +157 -0
- package/dist/runtime-windows-bridge.js.map +1 -0
- package/dist/settings-api.js +58 -0
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +28 -0
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-confirmation.d.ts +15 -1
- package/dist/settings-confirmation.js +46 -9
- package/dist/settings-confirmation.js.map +1 -1
- package/dist/settings-effect.js +23 -1
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-http-confirmation.js +11 -2
- package/dist/settings-http-confirmation.js.map +1 -1
- package/dist/tmux-control.d.ts +2 -1
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.d.ts +2 -1
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-pane-host.d.ts +26 -0
- package/dist/tmux-pane-host.js +25 -0
- package/dist/tmux-pane-host.js.map +1 -0
- package/dist/topic-commands.js +3 -0
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-delta-reader.d.ts +1 -1
- package/dist/transcript-delta-reader.js +1 -1
- package/dist/transcript-delta-reader.js.map +1 -1
- package/dist/transcript-monitor.d.ts +3 -0
- package/dist/transcript-monitor.js +22 -46
- package/dist/transcript-monitor.js.map +1 -1
- package/dist/transcript-needles.d.ts +35 -0
- package/dist/transcript-needles.js +158 -0
- package/dist/transcript-needles.js.map +1 -0
- package/dist/transcript-records.d.ts +21 -0
- package/dist/transcript-records.js +140 -0
- package/dist/transcript-records.js.map +1 -0
- package/dist/transcript-sources.d.ts +5 -0
- package/dist/transcript-sources.js +8 -79
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +21 -0
- package/dist/ui/agent-proposals.js +90 -0
- package/dist/ui/agent-runtime.js +106 -0
- package/dist/ui/chat-store.js +13 -2
- package/dist/ui/chat-strings.js +48 -0
- package/dist/ui/cli-picker.js +42 -0
- package/dist/ui/native-instance-settings.js +47 -0
- package/dist/ui/native-pane-recovery.js +45 -0
- package/dist/ui/panel-chat.js +61 -32
- package/dist/ui/panel-details.js +2 -1
- package/dist/ui/panel-fleet.js +99 -18
- package/dist/ui/panel-needs.js +152 -3
- package/dist/ui/panel-runtimes.js +231 -0
- package/dist/ui/panel-settings.js +14 -8
- package/dist/ui/panel-windows-agent.js +86 -0
- package/dist/ui/panel-windows-cli.js +143 -0
- package/dist/ui/provider-marks.js +83 -0
- package/dist/ui/runtime-catalog.js +55 -0
- package/dist/ui/settings-catalog.js +37 -10
- package/dist/ui/settings-confirm.js +3 -1
- package/dist/ui/settings-dialogs.js +14 -5
- package/dist/ui/settings-registry.js +5 -0
- package/dist/ui/settings-strings.js +14 -0
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-route.js +6 -2
- package/dist/ui/shared/app-shell.js +3 -2
- package/dist/ui/shared/app-store.js +1 -0
- package/dist/ui/shared/app-stream.js +3 -1
- package/dist/ui/shared/app.css +301 -0
- package/dist/ui/shared/app.js +4 -0
- package/dist/ui/shared/header-tools.js +6 -4
- package/dist/ui/shared/instance-nav.js +20 -4
- package/dist/ui/shared/instance-switch.js +4 -3
- package/dist/ui/shared/ui-menu.js +28 -8
- package/dist/ui/shared/view-strings.js +6 -0
- package/dist/ui/windows-agents-nav.js +2 -0
- package/dist/ui/windows-cli-catalog.js +79 -0
- package/dist/update-check.d.ts +8 -2
- package/dist/update-check.js +5 -2
- package/dist/update-check.js.map +1 -1
- package/dist/web-api.d.ts +25 -0
- package/dist/web-api.js +171 -6
- package/dist/web-api.js.map +1 -1
- package/dist/web-chat-history.d.ts +19 -2
- package/dist/web-chat-history.js +72 -3
- package/dist/web-chat-history.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -1
- package/dist/web-shell-routes.js +7 -2
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/windows-agents.d.ts +112 -0
- package/dist/windows-agents.js +463 -0
- package/dist/windows-agents.js.map +1 -0
- package/dist/windows-cli-api.d.ts +23 -0
- package/dist/windows-cli-api.js +158 -0
- package/dist/windows-cli-api.js.map +1 -0
- package/dist/windows-cli-providers.d.ts +115 -0
- package/dist/windows-cli-providers.js +429 -0
- package/dist/windows-cli-providers.js.map +1 -0
- package/dist/windows-cli-worker.d.ts +58 -0
- package/dist/windows-cli-worker.js +479 -0
- package/dist/windows-cli-worker.js.map +1 -0
- package/dist/windows-cli.d.ts +115 -0
- package/dist/windows-cli.js +402 -0
- package/dist/windows-cli.js.map +1 -0
- package/dist/windows-native-mcp.d.ts +19 -0
- package/dist/windows-native-mcp.js +143 -0
- package/dist/windows-native-mcp.js.map +1 -0
- package/dist/windows-pane-mcp.d.ts +26 -0
- package/dist/windows-pane-mcp.js +187 -0
- package/dist/windows-pane-mcp.js.map +1 -0
- package/package.json +9 -3
- package/templates/runtime-assets/conpty-LICENSE +21 -0
- package/templates/runtime-assets/conpty-NOTICE.md +503 -0
- package/templates/runtime-assets/local-pane-stage.ps1 +101 -0
- package/templates/runtime-assets/runtime-pane-job.ps1 +116 -0
- package/templates/runtime-assets/runtime-pane-private.ps1 +14 -0
- package/templates/runtime-assets/xterm-LICENSE +21 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Windows uses the same staged instance settings flow, with its runtime's supported fields.
|
|
2
|
+
import { html, useState } from "/assets/app-html.js";
|
|
3
|
+
import { t, register } from "/assets/app-i18n.js";
|
|
4
|
+
import { Dialog } from "/assets/ui-dialog.js";
|
|
5
|
+
import { ChipList, ModelSelect } from "./settings-dialogs.js";
|
|
6
|
+
import { CliProductMark, cliLabel } from "./cli-picker.js";
|
|
7
|
+
import { useWindowsCliCatalog } from "./windows-cli-catalog.js";
|
|
8
|
+
import { useRuntimeCatalog, instanceMachineId } from "./runtime-catalog.js";
|
|
9
|
+
import { NativeAgendToolsHint, tr } from "./agent-runtime.js";
|
|
10
|
+
register("nativeInstance", {
|
|
11
|
+
"zh-TW": { fixed:"Runtime 與 CLI 隨此對話保留。", workspace:"此 Agent 的對話會沿用這個工作區。" },
|
|
12
|
+
en: { fixed:"This conversation keeps its runtime and CLI.", workspace:"This agent's conversation keeps this workspace." },
|
|
13
|
+
});
|
|
14
|
+
const tn = key => t(`settings.${key}`);
|
|
15
|
+
export function NativeInstanceSettings({ name, inst, ctx, onClose }) {
|
|
16
|
+
const machineId = instanceMachineId(inst);
|
|
17
|
+
const catalog = useWindowsCliCatalog(!machineId), machines = useRuntimeCatalog(!!machineId);
|
|
18
|
+
const machine = machines.data?.machines?.find(item => item.id === machineId);
|
|
19
|
+
const native = (machineId ? machine?.inventory : catalog.data)?.clis?.find(cli => cli.id === inst.backend);
|
|
20
|
+
const [form,setForm] = useState(() => ({display_name:inst.display_name || name,description:inst.description || "",model:inst.model || "",systemPrompt:inst.systemPrompt || "",native_sandbox:inst.native_sandbox || "read-only",tags:Array.isArray(inst.tags) ? [...inst.tags] : []}));
|
|
21
|
+
const set = key => value => setForm(current => ({...current,[key]:value}));
|
|
22
|
+
const ready = !!form.display_name.trim() && !(form.native_sandbox === "read-only" && native?.readOnlySupport === false);
|
|
23
|
+
const stage = () => {
|
|
24
|
+
if (!ready) return;
|
|
25
|
+
const patch = {};
|
|
26
|
+
for (const [key,value] of Object.entries(form)) {
|
|
27
|
+
const before = key === "display_name" ? inst.display_name || name : key === "native_sandbox" ? inst.native_sandbox || "read-only" : key === "tags" ? inst.tags || [] : inst[key] || "";
|
|
28
|
+
const next = typeof value === "string" ? value.trim() : value;
|
|
29
|
+
if (JSON.stringify(next) !== JSON.stringify(before)) patch[key] = next;
|
|
30
|
+
}
|
|
31
|
+
if (Object.keys(patch).length) ctx.stageAgent(name,inst,patch);
|
|
32
|
+
onClose();
|
|
33
|
+
};
|
|
34
|
+
return html`<${Dialog} title=${ctx.shortName(name,inst)} onClose=${onClose} wide=${true} actions=${html`<button type="button" class="btn" onClick=${onClose}>${tn("cancel")}</button><button type="button" class="btn btn-primary" disabled=${!ready} onClick=${stage}>${tn("modalDone")}</button>`}>
|
|
35
|
+
<div class="form native-instance-settings"><p class="note"><${CliProductMark} id=${inst.backend} size=${20} /> Windows · ${cliLabel(inst.backend)} — ${t("nativeInstance.fixed")}</p>${machineId ? html`<p class="note runtime-instance-machine">${tr("machine")}: ${machine?.name || inst.machine_name || machineId}</p>` : null}
|
|
36
|
+
<label class="field"><span>${tn("displayName")}</span><input value=${form.display_name} maxLength=${64} onInput=${event => set("display_name")(event.target.value)} /></label>
|
|
37
|
+
<label class="field"><span>${tn("description")}</span><input value=${form.description} maxLength=${512} onInput=${event => set("description")(event.target.value)} /></label>
|
|
38
|
+
<div class="field"><label for="native-instance-model">${tn("model")}</label><${ModelSelect} id="native-instance-model" value=${form.model} onChange=${set("model")} backend=${inst.backend} instance=${name} runtime="windows" machineId=${machineId} inherit=${tr("model")} /></div>
|
|
39
|
+
<label class="field"><span>${tn("workingDir")}</span><input value=${inst.working_directory || ""} readOnly=${true} aria-readonly="true" /><small>${t("nativeInstance.workspace")}</small></label>
|
|
40
|
+
<label class="field"><span>${tr("access")}</span><select value=${form.native_sandbox} onChange=${event => set("native_sandbox")(event.target.value)}><option value="read-only" disabled=${native?.readOnlySupport === false}>${tr("read")}</option><option value="workspace-write">${tr("write")}</option></select></label>
|
|
41
|
+
<${NativeAgendToolsHint} cli=${native} sandbox=${form.native_sandbox} />
|
|
42
|
+
${!ready && form.native_sandbox === "read-only" && native?.readOnlySupport === false ? html`<p class="note">${tr("noReadOnly")}</p>` : null}
|
|
43
|
+
<label class="field"><span>${tn("systemPrompt")}</span><textarea rows="5" maxLength=${8000} value=${form.systemPrompt} onInput=${event => set("systemPrompt")(event.target.value)} /></label>
|
|
44
|
+
<div class="field"><span>${tn("tags")}</span><${ChipList} items=${form.tags} onChange=${set("tags")} placeholder=${tn("tagPlaceholder")} label=${tn("tags")} /></div>
|
|
45
|
+
</div>
|
|
46
|
+
</${Dialog}>`;
|
|
47
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** Retained-pane recovery uses an explicit admin attestation; it never resubmits a chat message. */
|
|
2
|
+
import { html, useEffect, useRef, useState } from "/assets/app-html.js";
|
|
3
|
+
import { t } from "/assets/app-i18n.js";
|
|
4
|
+
import { guestPage } from "/assets/app-store.js";
|
|
5
|
+
import { Dialog } from "/assets/ui-dialog.js";
|
|
6
|
+
import { toast } from "/assets/ui-toast.js";
|
|
7
|
+
import "./chat-strings.js";
|
|
8
|
+
export function NativePaneRecovery({ name, inst, lease }) {
|
|
9
|
+
const [dialog, setDialog] = useState(false), [checked, setChecked] = useState(false), [busy, setBusy] = useState(false);
|
|
10
|
+
const confirmation = useRef(null), inFlight = useRef(false), recovery = inst.native_recovery;
|
|
11
|
+
const latest = useRef(null); latest.current = {name,recovery};
|
|
12
|
+
useEffect(() => { confirmation.current=null; setDialog(false); setChecked(false); }, [recovery?.launchId, recovery?.sessionId, recovery?.kind]);
|
|
13
|
+
const act = async (path, body) => {
|
|
14
|
+
if (inFlight.current || !lease.current()) return;
|
|
15
|
+
inFlight.current = true; setBusy(true);
|
|
16
|
+
try {
|
|
17
|
+
const response = await lease.fetch(path, { method: "POST", headers: { "Content-Type": "application/json" }, ...(body ? { body: JSON.stringify(body) } : {}) });
|
|
18
|
+
const result = await response.json();
|
|
19
|
+
if (!lease.current()) return;
|
|
20
|
+
if (!response.ok || result.error) toast(result.error || t("chat.disconnected"), false);
|
|
21
|
+
else { setDialog(false); setChecked(false); }
|
|
22
|
+
} catch (error) { if (lease.current()) toast(error.message || t("chat.disconnected"), false); }
|
|
23
|
+
finally { inFlight.current = false; if (lease.current()) setBusy(false); }
|
|
24
|
+
};
|
|
25
|
+
if (guestPage() || inst.native_adapter !== "pane") return null;
|
|
26
|
+
if (!recovery) return ["stopped", "crashed"].includes(inst.status)
|
|
27
|
+
? html`<div class="windows-agent-blocked"><button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/start/${encodeURIComponent(name)}`)}>${t("chat.start")}</button></div>` : null;
|
|
28
|
+
if (recovery.kind === "session-unconfirmed") return html`<div class="windows-agent-blocked" role="alert">
|
|
29
|
+
<p>${t(recovery.provider === "codex" ? "chat.paneCodexSessionUnconfirmed" : "chat.paneSessionUnconfirmed")}</p>
|
|
30
|
+
<button type="button" class="btn" disabled=${busy || !recovery.launchId} onClick=${() => { confirmation.current = {name,launchId:recovery.launchId,sessionId:recovery.sessionId??null}; setDialog(true); }}>${t("chat.paneNewConversation")}</button>
|
|
31
|
+
${dialog ? html`<${Dialog} title=${t("chat.paneNewConversation")} onClose=${() => { if (!busy) setDialog(false); }}>
|
|
32
|
+
<p>${t("chat.paneNewConversationConfirm",inst.display_name || name)}</p>
|
|
33
|
+
<button type="button" class="btn" disabled=${busy || confirmation.current?.name !== name || confirmation.current?.launchId !== recovery.launchId || confirmation.current?.sessionId !== (recovery.sessionId??null)} onClick=${() => { const target=confirmation.current, now=latest.current; if(target?.name===now.name && target.launchId===now.recovery?.launchId && target.sessionId===(now.recovery?.sessionId??null)) act(`/ui/instances/${encodeURIComponent(name)}/new-conversation`,{confirmedNewConversation:true,launchId:target.launchId,sessionId:target.sessionId}); }}>${t("chat.paneNewConversation")}</button>
|
|
34
|
+
</${Dialog}>` : null}
|
|
35
|
+
</div>`;
|
|
36
|
+
return html`<div class="windows-agent-blocked" role="alert"><p>${t(recovery.kind === "host-upgrade" ? "chat.paneHostUpgrade" : "chat.paneRecoveryNeeded")}</p>
|
|
37
|
+
${recovery.kind === "host-upgrade" ? html`<button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/stop/${encodeURIComponent(name)}`)}>${t("chat.stopInstance")}</button>` : recovery.launchId ? html`<button type="button" class="btn" disabled=${busy} onClick=${() => { confirmation.current = {name,launchId:recovery.launchId}; setChecked(false); setDialog(true); }}>${t("chat.paneRecover")}</button>` : null}
|
|
38
|
+
<button type="button" class="btn" disabled=${busy} onClick=${() => act(`/ui/start/${encodeURIComponent(name)}`)}>${t("chat.start")}</button>
|
|
39
|
+
${dialog ? html`<${Dialog} title=${t("chat.paneRecover")} onClose=${() => { if (!busy) setDialog(false); }}>
|
|
40
|
+
<p>${t("chat.paneRecoverConfirm", inst.display_name || name)}</p>
|
|
41
|
+
<label><input type="checkbox" checked=${checked} disabled=${busy} onChange=${event => setChecked(event.target.checked)} />${t("chat.paneProcessesStopped")}</label>
|
|
42
|
+
<button type="button" class="btn" disabled=${!checked || busy || confirmation.current?.name !== name || confirmation.current?.launchId !== recovery.launchId} onClick=${() => { if (checked && confirmation.current?.name === name && confirmation.current?.launchId === recovery.launchId) act(`/ui/instances/${encodeURIComponent(name)}/recover-pane`, { confirmedStopped: true, launchId: confirmation.current.launchId }); }}>${t("chat.paneRecoverConfirmed")}</button>
|
|
43
|
+
</${Dialog}>` : null}
|
|
44
|
+
</div>`;
|
|
45
|
+
}
|
package/dist/ui/panel-chat.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativePaneRecovery } from "./native-pane-recovery.js";
|
|
1
2
|
// #1408 step 1: the chat panel. Behind the session gate (/ui/js/), loaded by the app with a dynamic import in "full"
|
|
2
3
|
// mode only. boot() runs once per page (the chat store must hear the stream from the first frame); <ChatPanel> mounts
|
|
3
4
|
// for /ui and /ui/chat/<name>.
|
|
@@ -11,9 +12,9 @@ import { lang, t } from "/assets/app-i18n.js";
|
|
|
11
12
|
import { appStore, createStore, guestPage, guestTrusted, useStore } from "/assets/app-store.js";
|
|
12
13
|
import { ComposerAuth } from "/assets/web-authorize.js";
|
|
13
14
|
import { useLease } from "/assets/app-ctx.js";
|
|
14
|
-
import { modelText, namesOf, namesStore, navStore as instanceNavStore } from "/assets/instance-nav.js";
|
|
15
|
+
import { modelText, namesOf, namesStore, navStore as instanceNavStore, supportsInstanceFeature } from "/assets/instance-nav.js";
|
|
15
16
|
import { PanelHeader, onPanelKey, setTitle, statusClass, statusLabel, openDrawer, requestNewInstance } from "/assets/app-shell.js";
|
|
16
|
-
import { detailsPath, textLinkSegments } from "/assets/app-route.js";
|
|
17
|
+
import { detailsPath, textLinkSegments, sectionPath } from "/assets/app-route.js";
|
|
17
18
|
import { InstanceSwitch } from "/assets/instance-switch.js";
|
|
18
19
|
import { HeaderTools } from "/assets/header-tools.js";
|
|
19
20
|
import { navigate, navStore } from "/assets/app-nav.js";
|
|
@@ -29,7 +30,6 @@ import { applyTheme, clearAmbient, resolvedMode, themeFor, themeImageUrl, themeM
|
|
|
29
30
|
import { colorMenuItem } from "/assets/identity-color.js";
|
|
30
31
|
import { brokenPictures, cardFoldable, IdentityCard, useCard } from "/assets/identity-card.js";
|
|
31
32
|
import { AgentWidget, instanceBinds, widgetFor } from "/assets/agent-widget.js";
|
|
32
|
-
|
|
33
33
|
// #1629 r4 §1: the identity card under the chat's header — hidden by default; shown or not is this device's choice.
|
|
34
34
|
// #2217 (the user, 2026-10-19): no row of its own; its picture and bio can be folded away (the banner stays) — this
|
|
35
35
|
// device's choice too, kept the same way. The user (2026-10-20): no control on the card — one card button in the header
|
|
@@ -214,7 +214,7 @@ export function ChatPanel({ route, navKey }) {
|
|
|
214
214
|
store.setCurrent(name && inst ? name : null);
|
|
215
215
|
return () => store.setCurrent(null);
|
|
216
216
|
}, [name, !!inst]);
|
|
217
|
-
useEffect(() => { setTitle(name || t("app.chat")); }, [name, navKey]);
|
|
217
|
+
useEffect(() => { setTitle(inst?.runtime === "windows" ? inst.display_name || name : name || t("app.chat")); }, [name, navKey, inst?.runtime, inst?.display_name]);
|
|
218
218
|
if (!name) {
|
|
219
219
|
// #1854 (rank 7): one instruction — pick an agent, or with none at all, the one way to make one. Connecting a chat
|
|
220
220
|
// app (the first-run card, #1519 P7) is offered in Settings, not here: it competed with the first agent.
|
|
@@ -259,6 +259,7 @@ function useResolvedMode(lease) {
|
|
|
259
259
|
}
|
|
260
260
|
|
|
261
261
|
function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
262
|
+
const attachmentsSupported = supportsInstanceFeature(inst,"attachments");
|
|
262
263
|
const names = useStore(namesStore); // #1722: which name leads (the title)
|
|
263
264
|
// #1629: this agent's theme, on this surface only; redrawn when the themes, the viewer's switch or the mode move.
|
|
264
265
|
useStore(themesStore);
|
|
@@ -267,6 +268,17 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
267
268
|
const themeKey = JSON.stringify(theme);
|
|
268
269
|
const card = useCard(name, inst.card_v);
|
|
269
270
|
const [dialog, setDialog] = useState(null); // "delete" | null
|
|
271
|
+
const [recovering,setRecovering] = useState(false);
|
|
272
|
+
const recoverNative = async () => {
|
|
273
|
+
if (recovering) return;
|
|
274
|
+
setRecovering(true);
|
|
275
|
+
try {
|
|
276
|
+
const response = await lease.fetch(`/ui/instances/${encodeURIComponent(name)}/recover`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({confirmedStopped:true})});
|
|
277
|
+
const result = await response.json();
|
|
278
|
+
if (lease.current() && (!response.ok || result.error)) toast(result.error || t("chat.disconnected"),false);
|
|
279
|
+
} catch(error) { if (lease.current()) toast(error.message || t("chat.disconnected"),false); }
|
|
280
|
+
finally { if (lease.current()) setRecovering(false); }
|
|
281
|
+
};
|
|
270
282
|
const [wrap, setWrap] = useState(codeWrap);
|
|
271
283
|
const wide = useStore(chatWide).on;
|
|
272
284
|
const view = useRef(null), split = useRef(null), thread = useRef(null), bg = useRef(null), effectSurface = useRef(null);
|
|
@@ -289,12 +301,14 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
289
301
|
const el = view.current; if (!el) return undefined;
|
|
290
302
|
let depth = 0;
|
|
291
303
|
const hasFiles = (e) => !!(e.dataTransfer && [...e.dataTransfer.types].includes("Files"));
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
304
|
+
const offs = [
|
|
305
|
+
lease.on(el, "dragenter", (e) => { if (attachmentsSupported && hasFiles(e)) { depth++; el.classList.add("dragging"); } }),
|
|
306
|
+
lease.on(el, "dragover", (e) => { if (hasFiles(e)) e.preventDefault(); }),
|
|
307
|
+
lease.on(el, "dragleave", (e) => { if (hasFiles(e) && --depth <= 0) { depth = 0; el.classList.remove("dragging"); } }),
|
|
308
|
+
lease.on(el, "drop", (e) => { depth = 0; el.classList.remove("dragging"); if (e.dataTransfer && e.dataTransfer.files.length) { e.preventDefault(); if (attachmentsSupported) store.addFiles(name, e.dataTransfer.files); else toast(t("chat.runtimeNoAttachments"),false); } }),
|
|
309
|
+
];
|
|
310
|
+
return () => offs.forEach(off => off());
|
|
311
|
+
}, [lease,attachmentsSupported]);
|
|
298
312
|
// Esc stops the agent's reply (as Esc in its terminal would): from the chat, while it works, and only when nothing
|
|
299
313
|
// else that Esc would close is open (a dialog, a menu, the Session menu).
|
|
300
314
|
useEffect(() => lease.hold(onPanelKey((e) => {
|
|
@@ -317,10 +331,11 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
317
331
|
};
|
|
318
332
|
const items = [
|
|
319
333
|
// #1523 N2: Details is a page now (the Fleet side of this instance), not a dialog.
|
|
320
|
-
{ key: "details", label: t("chat.details"), icon: "info", onSelect: () => navigate(detailsPath(name)) },
|
|
321
|
-
|
|
322
|
-
running ? { key: "
|
|
323
|
-
running ? { key: "
|
|
334
|
+
supportsInstanceFeature(inst,"details") ? { key: "details", label: t("chat.details"), icon: "info", onSelect: () => navigate(detailsPath(name)) } : null,
|
|
335
|
+
inst.runtime === "windows" ? {key:"settings",label:t("app.settings"),icon:"settings",onSelect:() => navigate(sectionPath("agents"))} : null,
|
|
336
|
+
running || !supportsInstanceFeature(inst,"lifecycle") ? null : { key: "start", label: t("chat.start"), icon: "play", onSelect: () => action("start") },
|
|
337
|
+
running && supportsInstanceFeature(inst,"lifecycle") ? { key: "restart", label: t("chat.restart"), icon: "restart", onSelect: () => action("restart") } : null,
|
|
338
|
+
running && supportsInstanceFeature(inst,"lifecycle") ? { key: "stop", label: t("chat.stopInstance"), icon: "stop", onSelect: () => action("stop") } : null,
|
|
324
339
|
// #1958: its identity colour and where it shows (the public link too, as profile edits).
|
|
325
340
|
colorMenuItem(name, inst),
|
|
326
341
|
// #1629: a person's control over this agent's theme — not over the public link (it only views themes).
|
|
@@ -337,10 +352,12 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
337
352
|
// #1269 quick actions: the model and effort it runs, each a way to /model or /effort (the same pick list).
|
|
338
353
|
const pick = (command) => store.runCommand(name, command, "", { alive: () => lease.current() });
|
|
339
354
|
const sub = html`<span class="status"><span class=${`dot ${cls}`} aria-hidden="true"></span>${statusLabel(inst, exec, awaiting)}</span>
|
|
340
|
-
${inst.
|
|
341
|
-
${inst.
|
|
355
|
+
${inst.runtime === "windows" ? html`<span class="tag">Windows</span>` : null}
|
|
356
|
+
${inst.machine_id ? html`<span class="tag runtime-machine-tag" title=${t("chat.machineName",inst.machine_name || inst.machine_id)}>${inst.machine_name || inst.machine_id}</span>${inst.machine_status !== "online" ? html`<span class="tag runtime-machine-offline">${t(inst.machine_status === "revoked" ? "chat.machineRevoked" : "chat.machineOffline")}</span>` : null}` : null}
|
|
357
|
+
${inst.model ? html`<button type="button" class="hd-chip" title=${t("chat.chipModel", modelText(inst))} aria-label=${t("chat.chipModel", modelText(inst))} onClick=${() => supportsInstanceFeature(inst,"commands") ? pick("model") : navigate(sectionPath("agents"))}>${modelText(inst)}</button>` : null}
|
|
358
|
+
${inst.effort && supportsInstanceFeature(inst,"effort") ? html`<button type="button" class="hd-chip" title=${t("chat.chipEffort", inst.effort)} aria-label=${t("chat.chipEffort", inst.effort)} onClick=${() => pick("effort")}>${inst.effort}</button>` : null}`;
|
|
342
359
|
return html`<div class=${`panel p-chat${wrap ? " wrap-code" : ""}`} data-width=${wide ? "full" : undefined} ref=${view}>
|
|
343
|
-
<${PanelHeader} title=${headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${guest ? (guestTrusted() ? html`<${InstanceSwitch} name=${name} current="chat" guest=${true} />` : null) : html`<${InstanceSwitch} name=${name} current="chat" />`}><${CardMenu} card=${card} /><${HeaderTools} navKey=${navKey} />${guest ? null : html`<${Menu} items=${items} label=${t("app.more")} />`}</${PanelHeader}>
|
|
360
|
+
<${PanelHeader} title=${inst.runtime === "windows" ? inst.display_name || name : headerName(namesOf(inst || { name }, names.primary))} sub=${sub} nav=${guest ? (guestTrusted() ? html`<${InstanceSwitch} name=${name} instance=${inst} current="chat" guest=${true} />` : null) : html`<${InstanceSwitch} name=${name} instance=${inst} current="chat" />`}><${CardMenu} card=${card} /><${HeaderTools} navKey=${navKey} usage=${supportsInstanceFeature(inst,"usage")} />${guest ? null : html`<${Menu} items=${items} label=${t("app.more")} />`}</${PanelHeader}>
|
|
344
361
|
<${ChatCard} key=${name} name=${name} card=${card} />
|
|
345
362
|
<${AgentWidget} slot="chat_header" widget=${widgetFor(name, "chat_header")} binds=${instanceBinds(inst, exec, awaiting)} />
|
|
346
363
|
<div class="chat-split" ref=${split}>
|
|
@@ -348,7 +365,9 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
348
365
|
${theme && theme.background ? html`<img ref=${bg} class=${`th-bg th-bg-${theme.background.fit}`} src=${themeImageUrl(name, theme.background.image)} alt="" aria-hidden="true" decoding="async" />` : null}
|
|
349
366
|
<${ChatAmbient} name=${name} surface=${effectSurface} part="layer" />
|
|
350
367
|
<${AmbientMotionNote} name=${name} />
|
|
351
|
-
<${
|
|
368
|
+
<${NativePaneRecovery} name=${name} inst=${inst} lease=${lease} />
|
|
369
|
+
${inst.native_blocked ? html`<div class="windows-agent-blocked" role="alert"><p>${t("chat.nativeBlocked")}</p><button type="button" class="btn" disabled=${recovering} onClick=${recoverNative}>${t("chat.nativeRecover")}</button></div>` : null}
|
|
370
|
+
<${Thread} name=${name} label=${inst.runtime === "windows" ? inst.display_name || name : name} th=${thread} />
|
|
352
371
|
<${Dock} name=${name} inst=${inst} lease=${lease} exec=${exec} awaiting=${awaiting} workingLabel=${theme ? theme.workingLabel : undefined} />
|
|
353
372
|
<${ChatAmbient} name=${name} surface=${effectSurface} part="front" />
|
|
354
373
|
</div>
|
|
@@ -356,8 +375,8 @@ function ChatView({ name, inst, lease, navKey, exec, awaiting }) {
|
|
|
356
375
|
keyOf=${msgKey} reveal=${(k) => thread.current && thread.current.reveal(k)} download=${downloadHtml} allow=${(commit) => setPreviewOptIn(true, commit)} />
|
|
357
376
|
</div>
|
|
358
377
|
<${LightboxHost} />
|
|
359
|
-
|
|
360
|
-
${dialog === "delete" ? html`<${DeleteDialog} name=${name} onClose=${() => setDialog(null)} />` : null}
|
|
378
|
+
${attachmentsSupported ? html`<div data-trust="drop" class="drop-overlay" aria-hidden="true"><div class="drop-card"><${Icon} name="attach" size=${32} /><span>${t("chat.dropHere")}</span></div></div>` : null}
|
|
379
|
+
${dialog === "delete" ? html`<${DeleteDialog} name=${name} inst=${inst} onClose=${() => setDialog(null)} />` : null}
|
|
361
380
|
</div>`;
|
|
362
381
|
}
|
|
363
382
|
|
|
@@ -467,10 +486,11 @@ function follow(name, thread) {
|
|
|
467
486
|
}
|
|
468
487
|
|
|
469
488
|
/** The thread's view: Preact owns the scroller and the jump pill; the kept list (chat-thread.js) is put inside it. */
|
|
470
|
-
function Thread({ name, th }) {
|
|
489
|
+
function Thread({ name, label = name, th }) {
|
|
471
490
|
const scroller = useRef(null), host = useRef(null);
|
|
472
491
|
const [jump, setJump] = useState({ show: false, unseen: 0 });
|
|
473
492
|
const [empty, setEmpty] = useState(false);
|
|
493
|
+
useLayoutEffect(() => { keptThreads.get(name)?.list.setAttribute("aria-label",t("chat.conversationWith",label)); },[name,label]);
|
|
474
494
|
useLayoutEffect(() => {
|
|
475
495
|
const doc = host.current.ownerDocument;
|
|
476
496
|
let e = keptThreads.get(name);
|
|
@@ -484,6 +504,7 @@ function Thread({ name, th }) {
|
|
|
484
504
|
if (kept) trimKept(name);
|
|
485
505
|
const shownAt = now();
|
|
486
506
|
const thread = e.thread, sc = scroller.current, ls = e.list;
|
|
507
|
+
ls.setAttribute("aria-label",t("chat.conversationWith",label));
|
|
487
508
|
e.sink = { setJump, setEmpty };
|
|
488
509
|
host.current.append(ls);
|
|
489
510
|
thread.attach(sc);
|
|
@@ -544,9 +565,9 @@ function Dock({ name, inst, lease, exec, awaiting, workingLabel }) {
|
|
|
544
565
|
<${CommandCard} name=${name} lease=${lease} />
|
|
545
566
|
<${QuickActions} name=${name} inst=${inst} lease=${lease} />
|
|
546
567
|
${s.waking[name] ? html`<p class="send-waking" role="status">${t("chat.waking", name)}</p>` : null}
|
|
547
|
-
<${Composer} name=${name} label=${composerLabel(inst, name)} lease=${lease} busy=${R().isBusy(exec)} />
|
|
568
|
+
<${Composer} name=${name} inst=${inst} label=${composerLabel(inst, name)} lease=${lease} busy=${R().isBusy(exec)} />
|
|
548
569
|
<div class="composer-row">${guestPage() ? html`<span class="guest-note" title=${t("chat.guestNote")}>${t("chat.guestNote")}</span>` : html`<${ComposerAuth} />`}<span
|
|
549
|
-
class="hint" title=${t(guestPage() ? "chat.guestMore" : "chat.composerMore")}>${t(touchScreen() ? "chat.composerHintTouch" : "chat.composerHint")}</span></div>
|
|
570
|
+
class="hint" title=${t(guestPage() || !supportsInstanceFeature(inst,"commands") ? "chat.guestMore" : "chat.composerMore")}>${t(supportsInstanceFeature(inst,"commands") ? touchScreen() ? "chat.composerHintTouch" : "chat.composerHint" : touchScreen() ? "chat.nativeComposerHintTouch" : "chat.nativeComposerHint")}</span></div>
|
|
550
571
|
</div></div>`;
|
|
551
572
|
}
|
|
552
573
|
|
|
@@ -641,7 +662,7 @@ function replySnippet(r) {
|
|
|
641
662
|
return text || (r.media === "photo" ? t("chat.replyPhoto") : r.media === "document" ? t("chat.replyFile") : "");
|
|
642
663
|
}
|
|
643
664
|
|
|
644
|
-
function Composer({ name, label = name, lease, busy }) {
|
|
665
|
+
function Composer({ name, inst, label = name, lease, busy }) {
|
|
645
666
|
const s = store.state;
|
|
646
667
|
const input = useRef(null), fileIn = useRef(null);
|
|
647
668
|
const [, tick] = useState(0);
|
|
@@ -662,7 +683,10 @@ function Composer({ name, label = name, lease, busy }) {
|
|
|
662
683
|
const guest = guestPage();
|
|
663
684
|
const app = useStore(appStore);
|
|
664
685
|
const guestAllowed = guest ? (app.instances.find((i) => i.name === name)?.commands ?? []) : null;
|
|
665
|
-
const
|
|
686
|
+
const attachmentsSupported = supportsInstanceFeature(inst,"attachments"), commandsSupported = supportsInstanceFeature(inst,"commands");
|
|
687
|
+
const machineUnavailable = !!inst?.machine_id && inst.machine_status !== "online";
|
|
688
|
+
const sendingBlocked = machineUnavailable || inst?.native_blocked || inst?.native_busy || (busy && !supportsInstanceFeature(inst,"send_while_busy"));
|
|
689
|
+
const palette = !commandsSupported || files.length || closedFor === value || (guest && !guestAllowed.length) ? null : paletteFor(value, guestAllowed || undefined);
|
|
666
690
|
const pick = palette && palette.length ? palette[Math.min(sel, palette.length - 1)] : null;
|
|
667
691
|
// Grow with the text, up to a limit: set through the CSSOM, never a style attribute (#1300). Empty, it is one line
|
|
668
692
|
// (#2478 — a long name's placeholder made the box two lines tall).
|
|
@@ -684,6 +708,9 @@ function Composer({ name, label = name, lease, busy }) {
|
|
|
684
708
|
// #2342: a completion is a step Ctrl+Z goes back from (to what was typed); a command that runs consumes the line.
|
|
685
709
|
const complete = (c) => { recordEdit(input.current); setValue(`/${c.name}${c.arg ? " " : ""}`); };
|
|
686
710
|
const submit = () => {
|
|
711
|
+
if (sendingBlocked) return;
|
|
712
|
+
if (!attachmentsSupported && files.length) { toast(t("chat.runtimeNoAttachments"),false); return; }
|
|
713
|
+
if (!commandsSupported && parseCommandLine(value)) { toast(t("chat.runtimeNoCommands"),false); return; }
|
|
687
714
|
if (pick) {
|
|
688
715
|
if (value === `/${pick.name}` && !needsArgument(pick.name)) runLine({ command: pick.name, args: "" });
|
|
689
716
|
else complete(pick);
|
|
@@ -719,11 +746,13 @@ function Composer({ name, label = name, lease, busy }) {
|
|
|
719
746
|
const onPaste = (e) => {
|
|
720
747
|
const list = e.clipboardData && e.clipboardData.files;
|
|
721
748
|
const text = e.clipboardData ? e.clipboardData.getData("text") : "";
|
|
722
|
-
if (list && list.length) { store.addFiles(name, list); if (!text) e.preventDefault(); return; }
|
|
723
|
-
if (R().isLongPaste(text) && store.attachPastedText(name, text)) e.preventDefault();
|
|
749
|
+
if (list && list.length) { if (!attachmentsSupported) { if (!text) e.preventDefault(); toast(t("chat.runtimeNoAttachments"),false); return; } store.addFiles(name, list); if (!text) e.preventDefault(); return; }
|
|
750
|
+
if (attachmentsSupported && R().isLongPaste(text) && store.attachPastedText(name, text)) e.preventDefault();
|
|
724
751
|
};
|
|
725
752
|
const optId = (c) => `cmd-${c.name}`;
|
|
726
753
|
return html`<div class="composer-box">
|
|
754
|
+
${machineUnavailable ? html`<p class="note runtime-machine-unavailable" role="status">${t(inst.machine_status === "revoked" ? "chat.machineRevokedHint" : "chat.machineUnavailable",inst.machine_name || inst.machine_id)}</p>` : null}
|
|
755
|
+
${inst?.native_busy && !busy && !inst?.native_blocked ? html`<p class="note" role="status">${t("chat.nativeBusy")}</p>` : null}
|
|
727
756
|
${palette ? html`<ul data-trust="palette" class="cmd-palette" id="cmdPalette" role="listbox" aria-label=${t("chat.cmdCommands")}>
|
|
728
757
|
${palette.length ? palette.map((c) => html`<li key=${c.name} id=${optId(c)} role="option" aria-selected=${c === pick ? "true" : "false"}
|
|
729
758
|
class=${c === pick ? "on" : ""} onMouseDown=${(e) => { e.preventDefault(); complete(c); }}>
|
|
@@ -737,9 +766,9 @@ function Composer({ name, label = name, lease, busy }) {
|
|
|
737
766
|
</div>` : null}
|
|
738
767
|
${files.length ? html`<div class="pending-files">${files.map((f, i) => html`<${FileChip} key=${`${f.name}-${i}`} f=${f} i=${i} name=${name} />`)}</div>` : null}
|
|
739
768
|
<div class="composer">
|
|
740
|
-
|
|
769
|
+
${attachmentsSupported ? html`<button id="attachBtn" type="button" class="icon-btn" title=${t("chat.attachTitle")} aria-label=${t("chat.attachTitle")} onClick=${() => fileIn.current && fileIn.current.click()}><${Icon} name="attach" /></button>
|
|
741
770
|
<input ref=${fileIn} type="file" multiple hidden
|
|
742
|
-
onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }}
|
|
771
|
+
onChange=${(e) => { store.addFiles(name, e.target.files); e.target.value = ""; }} />` : null}
|
|
743
772
|
${/* #2478: the placeholder is drawn beside the box, one line ending in "…" — a textarea's own placeholder is clipped,
|
|
744
773
|
never ellipsized, and a long name wrapped it to two lines. The box keeps the words as its accessible name. Its own
|
|
745
774
|
placeholder is one space: :placeholder-shown hides the drawn one (app.css) the moment the box holds text, an
|
|
@@ -751,7 +780,7 @@ function Composer({ name, label = name, lease, busy }) {
|
|
|
751
780
|
placeholder=" "></textarea><span class="composer-ph" aria-hidden="true">${t("chat.composerPlaceholder", label)}</span></span>
|
|
752
781
|
<button id="stopBtn" type="button" class="btn btn-stop" hidden=${!busy} title=${t("chat.stopReplyTitle")}
|
|
753
782
|
disabled=${!!s.cancelling[name]} onClick=${() => store.cancelReply(name)}><${Icon} name="stop" size=${14} /><span class="stop-label">${t("chat.stopReply")}</span></button>
|
|
754
|
-
<button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content}
|
|
783
|
+
<button id="sendBtn" type="button" class="btn btn-primary btn-send" hidden=${busy && !content} disabled=${!content || sendingBlocked}
|
|
755
784
|
onMouseDown=${(e) => e.preventDefault()}
|
|
756
785
|
aria-label=${t("chat.send")} title=${t("chat.send")} onClick=${submit}><${Icon} name="send" size=${16} /><span class="send-label">${t("chat.send")}</span></button>
|
|
757
786
|
</div></div>`;
|
|
@@ -775,7 +804,7 @@ function FileChip({ f, i, name }) {
|
|
|
775
804
|
|
|
776
805
|
// ── Dialogs ──
|
|
777
806
|
|
|
778
|
-
function DeleteDialog({ name, onClose }) {
|
|
807
|
+
function DeleteDialog({ name, inst, onClose }) {
|
|
779
808
|
// The dialog's own lease: it ends when the dialog goes (closed, or the chat left for another page). The delete
|
|
780
809
|
// may still finish on the server, and says so, but a dialog that is gone never closes or navigates the page the
|
|
781
810
|
// person is on now (#1425 review).
|
|
@@ -791,7 +820,7 @@ function DeleteDialog({ name, onClose }) {
|
|
|
791
820
|
// follows the request. From then on the dialog is done: the decision is said in a toast, and it never closes or
|
|
792
821
|
// navigates anything (the person may be anywhere by then).
|
|
793
822
|
try {
|
|
794
|
-
const res = await confirmedWrite(`/ui/instances/${encodeURIComponent(name)}/delete`, { method: "POST", body: { confirm: typed }, label: t("chat.deleteTitle", name),
|
|
823
|
+
const res = await confirmedWrite(inst?.runtime === "windows" ? `/ui/instances/${encodeURIComponent(name)}` : `/ui/instances/${encodeURIComponent(name)}/delete`, { method: inst?.runtime === "windows" ? "DELETE" : "POST", body: inst?.runtime === "windows" ? {} : { confirm: typed }, label: t("chat.deleteTitle", name),
|
|
795
824
|
onPending: (_id, view) => { handedOver = true; toast(t(selfConfirmed(view) ? "app.pendingSelfSent" : "app.pendingSent")); if (lease.current()) { setBusy(false); onClose(); } } });
|
|
796
825
|
r = res.ok ? res.body || {} : { error: (res.body && res.body.error) || `HTTP ${res.status}` };
|
|
797
826
|
} catch (err) { r = { error: err && err.message ? err.message : t("chat.disconnected") }; }
|
package/dist/ui/panel-details.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { NativePaneRecovery } from "./native-pane-recovery.js";
|
|
1
2
|
// #1523 N2 (§3.3, Q1 = B): Details — "the Fleet side of one instance", at /ui/fleet/agent/<name>. Behind the session
|
|
2
3
|
// gate (/ui/js/). What it holds:
|
|
3
4
|
// - runtime and recent activity (what Chat's details dialog showed, now a page);
|
|
@@ -158,7 +159,7 @@ function DetailsView({ name, inst, navKey, exec, awaiting }) {
|
|
|
158
159
|
}
|
|
159
160
|
return html`<div data-effect-panel="details" class="panel p-details">
|
|
160
161
|
<${PanelHeader} title=${name} sub=${sub} nav=${html`<${InstanceSwitch} name=${name} current="details" />`}><${HeaderTools} navKey=${navKey} /><${Menu} items=${items} label=${t("app.more")} /></${PanelHeader}>
|
|
161
|
-
<div data-trust="panel-content" class="panel-body"><div class="col" ref=${body}
|
|
162
|
+
<div data-trust="panel-content" class="panel-body"><div class="col" ref=${body}><${NativePaneRecovery} name=${name} inst=${inst} lease=${lease} />${busy ? html`<p class="note" role="status">${t("chat.dWorking")}</p>` : null}<${IdentityCard} name=${name} card=${card} />
|
|
162
163
|
<${AgentWidget} slot="details" widget=${widgetFor(name, "details")} binds=${instanceBinds(inst, exec, awaiting)} />${content}</div></div>
|
|
163
164
|
</div>`;
|
|
164
165
|
}
|
package/dist/ui/panel-fleet.js
CHANGED
|
@@ -11,7 +11,11 @@ import { appStore, useStore } from "/assets/app-store.js";
|
|
|
11
11
|
import { useLease } from "/assets/app-ctx.js";
|
|
12
12
|
import { PanelHeader, setTitle } from "/assets/app-shell.js";
|
|
13
13
|
import { HeaderTools } from "/assets/header-tools.js";
|
|
14
|
-
import { renavigate } from "/assets/app-nav.js";
|
|
14
|
+
import { navigate, renavigate } from "/assets/app-nav.js";
|
|
15
|
+
import { RuntimePicker, AgentDraftAssistant, NativeAgendToolsHint, tr } from "./agent-runtime.js";
|
|
16
|
+
import { CliPicker, cliLabel as productLabel } from "./cli-picker.js";
|
|
17
|
+
import { useWindowsCliCatalog, WindowsCliDiscoveryStatus, detectionText } from "./windows-cli-catalog.js";
|
|
18
|
+
import { useRuntimeCatalog } from "./runtime-catalog.js";
|
|
15
19
|
import { chatPath, fleetPath, FLEET_TABS, rememberFleetTab } from "/assets/app-route.js";
|
|
16
20
|
import { Dialog } from "/assets/ui-dialog.js";
|
|
17
21
|
import { Empty, ErrorState, Skeleton } from "/assets/ui-states.js";
|
|
@@ -266,7 +270,7 @@ function Cache({ lease }) {
|
|
|
266
270
|
* that finishes after that still reports its toast (it may have created something), but never closes or refreshes
|
|
267
271
|
* whatever is on screen now (#1425 review). `ready` false keeps Create off until the form has what it needs.
|
|
268
272
|
*/
|
|
269
|
-
export function FormDialog({ title, onClose, submit, children, ready = true }) {
|
|
273
|
+
export function FormDialog({ title, onClose, submit, children, ready = true, renderActions, onCreated, onSubmit, wide = false }) {
|
|
270
274
|
const lease = useLease("form-dialog");
|
|
271
275
|
const [busy, setBusy] = useState(false);
|
|
272
276
|
async function go() {
|
|
@@ -287,13 +291,15 @@ export function FormDialog({ title, onClose, submit, children, ready = true }) {
|
|
|
287
291
|
toast(submit.done);
|
|
288
292
|
if (handedOver || !lease.current()) return;
|
|
289
293
|
setBusy(false);
|
|
294
|
+
if (onCreated) { onCreated(r); return; }
|
|
290
295
|
onClose();
|
|
291
296
|
if (submit.after) submit.after();
|
|
297
|
+
if (submit.afterResult) submit.afterResult(r);
|
|
292
298
|
}
|
|
293
|
-
return html`<${Dialog} title=${title} onClose=${onClose} busy=${busy}
|
|
294
|
-
actions=${html`<button type="button" class="btn" onClick=${onClose} disabled=${busy}>${t("fleet.cancel")}</button>
|
|
299
|
+
return html`<${Dialog} title=${title} onClose=${onClose} busy=${busy} wide=${wide}
|
|
300
|
+
actions=${renderActions ? renderActions({ busy, go }) : html`<button type="button" class="btn" onClick=${onClose} disabled=${busy}>${t("fleet.cancel")}</button>
|
|
295
301
|
<button type="button" class="btn btn-primary" onClick=${go} disabled=${busy || !ready}>${busy ? t("fleet.creating") : submit.label || t("fleet.create")}</button>`}>
|
|
296
|
-
<form class="form" onSubmit=${(e) => { e.preventDefault(); go(); }}><fieldset class="form" disabled=${busy}>${children}</fieldset></form></${Dialog}>`;
|
|
302
|
+
<form class="form" onSubmit=${(e) => { e.preventDefault(); if (onSubmit) onSubmit({ busy, go }); else go(); }}><fieldset class="form" disabled=${busy}>${children}</fieldset></form></${Dialog}>`;
|
|
297
303
|
}
|
|
298
304
|
const field = (label, input) => html`<label class="field"><span>${label}</span>${input}</label>`;
|
|
299
305
|
|
|
@@ -418,9 +424,26 @@ function CreateTeam({ onClose, onDone, team = null }) {
|
|
|
418
424
|
export function CreateInstanceDialog({ onClose }) {
|
|
419
425
|
const lease = useLease("create-instance");
|
|
420
426
|
const [backends, setBackends] = useState(null);
|
|
427
|
+
const { windowsCli } = useStore(appStore);
|
|
428
|
+
const [runtime,setRuntime] = useState("");
|
|
429
|
+
const [machineId,setMachineId] = useState("");
|
|
430
|
+
const [nativeAdapter,setNativeAdapter]=useState("exec");
|
|
431
|
+
const machineCatalog = useRuntimeCatalog();
|
|
432
|
+
const machines = machineCatalog.data?.machines || [];
|
|
433
|
+
const machine = machines.find(item => item.id === machineId);
|
|
434
|
+
const [step,setStep] = useState(1), [created,setCreated] = useState(null);
|
|
435
|
+
const drafts = useRef({}), draftGoals = useRef({}), heading = useRef(null);
|
|
436
|
+
const discovery = useWindowsCliCatalog(windowsCli === true);
|
|
437
|
+
const native = machineId ? machine?.inventory : discovery.data;
|
|
438
|
+
const [assistBusy,setAssistBusy] = useState(false);
|
|
439
|
+
const windows = runtime === "windows";
|
|
421
440
|
// The backend "fleet default" means (#2098 review P3-3): its models are the ones the model list offers then.
|
|
422
441
|
const [defaultBackend, setDefaultBackend] = useState("claude-code");
|
|
423
442
|
const [f, setF] = useState({ dir: "", topic: "", desc: "", backend: "", model: "", branch: "", tags: "", prompt: "" });
|
|
443
|
+
useEffect(() => {
|
|
444
|
+
heading.current?.focus({ preventScroll:true });
|
|
445
|
+
heading.current?.closest(".dlg-body")?.scrollTo?.({ top:0 });
|
|
446
|
+
},[step]);
|
|
424
447
|
useEffect(() => {
|
|
425
448
|
(async () => {
|
|
426
449
|
try {
|
|
@@ -430,15 +453,43 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
430
453
|
const first = list.find(b => b.installed && !b.deprecated) || list.find(b => b.installed);
|
|
431
454
|
if (typeof bd.defaultBackend === "string" && bd.defaultBackend) setDefaultBackend(bd.defaultBackend);
|
|
432
455
|
setBackends(list);
|
|
433
|
-
setF(x => ({ ...x, backend: first ? first.name : "" }));
|
|
456
|
+
setF(x => ({ ...x, backend: x.backend || (first ? first.name : "") }));
|
|
434
457
|
} catch {
|
|
435
458
|
// The list could not be read: offer the known backends, and keep the explicit choice "fleet default" selected.
|
|
436
|
-
if (lease.current()) { setBackends(BACKENDS.map(name => ({ name, installed: true }))); setF(x => ({ ...x, backend: "" })); }
|
|
459
|
+
if (lease.current()) { setBackends(BACKENDS.map(name => ({ name, installed: true, fallback: true }))); setF(x => ({ ...x, backend: "" })); }
|
|
437
460
|
}
|
|
438
461
|
})();
|
|
439
462
|
}, [lease]);
|
|
440
463
|
const set = (k) => (e) => setF({ ...f, [k]: e.target.value });
|
|
441
|
-
const
|
|
464
|
+
const targetKey = `${runtime}:${machineId || "local"}`;
|
|
465
|
+
const chooseRuntime = (value, nextMachineId = "") => {
|
|
466
|
+
if (value === runtime && nextMachineId === machineId) return;
|
|
467
|
+
if (runtime) drafts.current[targetKey] = { form:{...f}, sandbox,nativeAdapter };
|
|
468
|
+
const saved = drafts.current[`${value}:${nextMachineId || "local"}`];
|
|
469
|
+
const nextInventory = nextMachineId ? machines.find(item => item.id === nextMachineId)?.inventory : discovery.data;
|
|
470
|
+
const { sandbox: savedFileAccess } = saved || {};
|
|
471
|
+
setRuntime(value);
|
|
472
|
+
setMachineId(nextMachineId);
|
|
473
|
+
setF(saved?.form || {dir:"",topic:"",desc:"",prompt:"",tags:"",branch:"",model:"",backend:value === "windows" ? nextInventory?.clis?.find(b => b.path && b.runnable)?.id || "codex" : backends?.find(b => b.installed && !b.deprecated && !b.fallback)?.name || ""});
|
|
474
|
+
setSandbox(savedFileAccess || "read-only");setNativeAdapter(saved?.nativeAdapter||"exec");
|
|
475
|
+
};
|
|
476
|
+
const [sandbox,setSandbox] = useState("read-only");
|
|
477
|
+
const nativeCli = native?.clis?.find(cli => cli.id === f.backend);
|
|
478
|
+
const nativeChoices = (native?.clis || []).filter(cli => cli.path && cli.supports?.execution !== false && cli.supports?.resume !== false).map(cli => ({value:cli.id,label:productLabel(cli.id),disabled:!cli.runnable,description:cli.authenticated === false ? t("fleet.notSignedIn") : cli.version || "Windows"}));
|
|
479
|
+
useEffect(() => {
|
|
480
|
+
if(nativeAdapter==="pane"&&!['codex','claude-code'].includes(f.backend))setNativeAdapter("exec");
|
|
481
|
+
if (windows && native && !nativeChoices.some(choice => choice.value === f.backend)) {
|
|
482
|
+
const first = nativeChoices.find(choice => !choice.disabled) || nativeChoices[0];
|
|
483
|
+
if (first) setF(current => ({...current,backend:first.value,model:""}));
|
|
484
|
+
}
|
|
485
|
+
},[native,runtime,machineId,f.backend,nativeAdapter]);
|
|
486
|
+
const submit = { path: "/ui/instances", done: windows ? tr("created") : t("fleet.instanceCreated"),
|
|
487
|
+
collect: () => {
|
|
488
|
+
if (windows) {
|
|
489
|
+
const name = f.topic.trim() || f.dir.trim().replace(/[\\/]+$/, "").split(/[\\/]/).pop();
|
|
490
|
+
if (!name) { toast(tr("required"),false); return null; }
|
|
491
|
+
return {runtime:"windows",...(nativeAdapter==="pane"?{native_adapter:"pane"}:{}),...(machineId ? {machineId} : {}),topic_name:name,backend:f.backend,...(f.model.trim() ? {model:f.model.trim()} : {}),...(f.dir.trim() ? {directory:f.dir.trim()} : {}),description:f.desc.trim(),systemPrompt:f.prompt.trim(),sandbox};
|
|
492
|
+
}
|
|
442
493
|
if (!f.dir.trim() && !f.topic.trim()) { toast(t("fleet.topicRequired"), false); return null; }
|
|
443
494
|
const body = {};
|
|
444
495
|
if (f.dir.trim()) body.directory = f.dir.trim();
|
|
@@ -451,18 +502,48 @@ export function CreateInstanceDialog({ onClose }) {
|
|
|
451
502
|
if (f.prompt.trim()) body.systemPrompt = f.prompt.trim();
|
|
452
503
|
return body;
|
|
453
504
|
} };
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
505
|
+
const canNext = !!runtime && (!machineId || machine?.status === "online") && (windows ? nativeCli?.runnable === true : backends !== null);
|
|
506
|
+
const permissionReady = !windows || sandbox !== "read-only" || nativeCli?.readOnlySupport !== false;
|
|
507
|
+
const canCreate = step === 2 && canNext && permissionReady && !assistBusy && (!windows || !!(f.topic.trim() || f.dir.trim()));
|
|
508
|
+
const advance = () => { if (canNext && !assistBusy) setStep(2); };
|
|
509
|
+
const destination = created?.name ? chatPath(created.name) : created?.agent?.id ? `/ui/windows-agents/${created.agent.id}` : null;
|
|
510
|
+
const createdName = created?.agent?.name || created?.name || f.topic || f.dir.replace(/[\\/]+$/, "").split(/[\\/]/).pop();
|
|
511
|
+
const runtimeLabel = machineId ? `${machine?.name || machineId} · ${machine?.platform || tr(runtime)}` : runtime ? tr(runtime) : "";
|
|
512
|
+
const cliLabel = productLabel(f.backend) || `${t("fleet.fleetDefault")} (${productLabel(defaultBackend)})`;
|
|
513
|
+
const backendField = windows ? html`<div class="field"><div class="cli-discovery-heading"><span>${tr("backend")}</span>${native && !machineId ? html`<button type="button" class="btn btn-ghost btn-sm" disabled=${discovery.loading} onClick=${discovery.refresh}><${Icon} name="restart" size=${14} />${detectionText("refresh")}</button>` : null}</div>
|
|
514
|
+
<div aria-busy=${!machineId && discovery.loading ? "true" : "false"}>${native ? html`<${CliPicker} label=${tr("backend")} value=${f.backend} choices=${nativeChoices} onChange=${value => setF(x => ({...x,backend:value,model:""}))} disabled=${!nativeChoices.length || !!machineId && machine?.status !== "online"} />` : null}</div>
|
|
515
|
+
${machineId ? html`<p class="note">${tr(!nativeChoices.length ? "noRemoteCli" : "remotePolicy")}</p>${machine?.inventoryUpdatedAt ? html`<p class="note">${t("runtime.inventoryAt",new Date(machine.inventoryUpdatedAt).toLocaleString())}</p>` : null}` : html`<${WindowsCliDiscoveryStatus} state=${discovery} />`}
|
|
516
|
+
${!machineId && native && !discovery.loading && !discovery.error ? html`<p class="note">${!nativeChoices.length ? tr("noCli") : tr("nativeHint")}</p>` : null}</div>`
|
|
517
|
+
: field(tr("backend"),backends ? html`<${CliPicker} label=${tr("backend")} value=${f.backend} choices=${[{value:"",label:t("fleet.fleetDefault")},...backends.filter(b => backendState(b) !== "not_installed" && !b.deprecated).map(b => ({value:b.name,label:productLabel(b.name),disabled:backendBlocked(b),description:backendNote(b)}))]} onChange=${value => setF(x => ({...x,backend:value,model:""}))} />` : html`<div role="status" aria-busy="true"><${Skeleton} lines=${1} /><span class="note">${tr("loadingClis")}</span></div>`);
|
|
518
|
+
const actions = ({busy,go}) => step === 3 ? html`<button type="button" class="btn" onClick=${onClose}>${tr("finish")}</button><button type="button" class="btn btn-primary" onClick=${() => { onClose(); navigate(destination || "/settings/agents"); }}>${tr(destination ? "openChat" : "viewAgents")}<${Icon} name="forward" size=${16} /></button>`
|
|
519
|
+
: html`<button type="button" class="btn" disabled=${busy || assistBusy} onClick=${step === 1 ? onClose : () => setStep(1)}>${step === 1 ? t("fleet.cancel") : tr("back")}</button>
|
|
520
|
+
<button type="button" class="btn btn-primary" disabled=${busy || (step === 1 ? !canNext : !canCreate)} onClick=${step === 1 ? advance : go}>${step === 1 ? tr("next") : busy ? t("fleet.creating") : t("fleet.create")}${step === 1 ? html`<${Icon} name="forward" size=${16} />` : null}</button>`;
|
|
521
|
+
return html`<${FormDialog} title=${tr("title")} onClose=${onClose} submit=${submit} ready=${canCreate} wide=${true} renderActions=${actions}
|
|
522
|
+
onSubmit=${({busy,go}) => { if (!busy) { if (step === 1) advance(); else if (step === 2) go(); } }}
|
|
523
|
+
onCreated=${result => { setCreated(result); setStep(3); }}>
|
|
524
|
+
<div class="agent-create-wizard" data-step=${step}>
|
|
525
|
+
<ol class="agent-create-steps" aria-label=${tr("progress")}>${["runtimeStep","configureStep","doneStep"].map((label,index) => html`<li key=${label} class=${index+1 === step ? "current" : index+1 < step ? "complete" : ""} aria-current=${index+1 === step ? "step" : undefined}><span class="agent-create-step-number">${index+1 < step ? html`<${Icon} name="check" size=${13} />` : index+1}</span><span>${tr(label)}</span></li>`)}</ol>
|
|
526
|
+
${step === 1 ? html`<section class="agent-create-stage"><h3 ref=${heading} tabindex="-1">${tr("where")}</h3><p class="note">${tr("runtimeLead")}</p>
|
|
527
|
+
<${RuntimePicker} value=${runtime} onChange=${chooseRuntime} windowsEnabled=${windowsCli} legend=${tr("environment")} machineId=${machineId} machines=${machines} onMachineChange=${id => chooseRuntime("windows",id)} />
|
|
528
|
+
${machineCatalog.loading && !machineCatalog.data ? html`<p class="note" role="status">${tr("machineLoading")}</p>` : null}
|
|
529
|
+
${machineCatalog.error ? html`<p class="note" role="alert">${tr("machineFailed")} <button type="button" class="btn btn-ghost btn-sm" onClick=${machineCatalog.refresh}>${tr("machineRetry")}</button></p>` : null}
|
|
530
|
+
<a class="note runtime-manage-link" href="/settings/runtimes">${tr("manage")}</a>
|
|
531
|
+
${runtime ? html`<div class="agent-create-cli">${backendField}</div>` : null}
|
|
532
|
+
</section>` : step === 2 ? html`<section class="agent-create-stage"><h3 ref=${heading} tabindex="-1">${tr("configureStep")}</h3><p class="note">${tr("configureLead")}</p>
|
|
533
|
+
<div class="agent-create-selection"><${Icon} name=${windows ? "view" : "code"} size=${19} /><div><strong>${runtimeLabel}</strong><span>${cliLabel}</span></div><button type="button" class="btn btn-ghost btn-sm" disabled=${assistBusy} onClick=${() => setStep(1)}>${tr("changeRuntime")}</button></div>
|
|
534
|
+
${machineId ? html`<p class="note">${tr("remoteDraft")}</p>` : windowsCli ? html`<${AgentDraftAssistant} key=${targetKey} runtime=${runtime} probe=${native} initialGoal=${draftGoals.current[targetKey] || ""} onGoalChange=${value => { draftGoals.current[targetKey] = value; }} onBusy=${setAssistBusy} onDraft=${draft => setF(x => ({...x,topic:draft.name,dir:draft.cwd,desc:draft.description,prompt:draft.instructions}))} />` : null}
|
|
535
|
+
<fieldset class="form agent-runtime-fields" disabled=${assistBusy}>
|
|
536
|
+
${field(machineId ? t("runtime.remoteDirectory",machine?.name || machineId) : windows ? tr("directory") : t("fleet.directory"), html`<input value=${f.dir} onInput=${set("dir")} placeholder=${windows ? tr("directoryHint") : t("fleet.directoryHint")} />`)}
|
|
537
|
+
${field(`${t("fleet.topic")}${f.dir.trim() ? "" : " *"}`, html`<input value=${f.topic} onInput=${set("topic")} placeholder=${windows ? tr("nameHint") : t("fleet.topicHint")} />`)}
|
|
459
538
|
${field(t("fleet.description"), html`<input value=${f.desc} onInput=${set("desc")} placeholder=${t("fleet.descriptionHint")} />`)}
|
|
460
|
-
${field(t("fleet.
|
|
461
|
-
${
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
539
|
+
${windows ? html`${field(tr("adapter"),html`<select id="native-create-adapter" value=${nativeAdapter} onChange=${e=>setNativeAdapter(e.target.value)}><option value="exec">${tr("adapterExec")}</option><option value="pane" disabled=${!["codex","claude-code"].includes(f.backend)}>${tr("adapterPane")}</option></select>`)}<p class="note">${tr(nativeAdapter==="pane"?"paneTopic":"execTopic")}</p>${field(t("fleet.model"),html`<${ModelSelect} key=${`${targetKey}:${f.backend}`} value=${f.model} onChange=${value => setF(x => ({...x,model:value}))} backend=${f.backend} runtime="windows" machineId=${machineId} inherit=${tr("model")} />`)}${field(tr("access"),html`<select id="native-create-sandbox" value=${sandbox} onChange=${e => setSandbox(e.target.value)}><option value="read-only" disabled=${nativeCli?.readOnlySupport === false}>${tr("read")}</option><option value="workspace-write">${tr("write")}</option></select>`)}<p class="note">${!permissionReady ? tr("noReadOnly") : sandbox === "workspace-write" ? tr(nativeAdapter==="pane"?"panePolicy":nativeCli?.id === "claude-code" ? "nativePolicyClaude" : "nativePolicy") : ""}</p><${NativeAgendToolsHint} cli=${nativeCli} sandbox=${sandbox} />` : html`
|
|
540
|
+
${field(t("fleet.model"), html`<${ModelSelect} value=${f.model} onChange=${(v) => setF(x => ({ ...x, model: v }))} backend=${f.backend || defaultBackend} inherit=${t("fleet.fleetDefault")} />`)}
|
|
541
|
+
${field(t("fleet.branch"), html`<input value=${f.branch} onInput=${set("branch")} placeholder=${t("fleet.branchHint")} />`)}
|
|
542
|
+
${field(t("fleet.tags"), html`<input value=${f.tags} onInput=${set("tags")} placeholder=${t("fleet.tagsHint")} />`)}
|
|
543
|
+
`}
|
|
465
544
|
${field(t("fleet.systemPrompt"), html`<textarea rows="3" value=${f.prompt} onInput=${set("prompt")} placeholder=${t("fleet.systemPromptHint")}></textarea>`)}
|
|
545
|
+
</fieldset></section>` : html`<section class="agent-create-stage agent-create-success"><span class="agent-create-success-icon"><${Icon} name="check" size=${28} /></span><h3 ref=${heading} tabindex="-1">${tr("success")}</h3><p class="note">${tr("successLead")}</p><dl><dt>${t("fleet.topic")}</dt><dd>${createdName}</dd><dt>Runtime</dt><dd>${runtimeLabel} · ${cliLabel}</dd><dt>${t("fleet.directory")}</dt><dd><code>${created?.agent?.cwd || created?.worktree_path || f.dir || tr("autoWorkspace")}</code></dd></dl></section>`}
|
|
546
|
+
</div>
|
|
466
547
|
</${FormDialog}>`;
|
|
467
548
|
}
|
|
468
549
|
|