create-walle 0.10.2 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/lib/release-schema.json +2 -2
- package/lib/runtime-closure-policy.json +8 -7
- package/package.json +1 -1
- package/template/coding-task-manager/api-prompts.js +45 -7
- package/template/coding-task-manager/bin/claude-hook.js +55 -1
- package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
- package/template/coding-task-manager/bin/land.js +2069 -0
- package/template/coding-task-manager/bin/memory-census.js +4 -2
- package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
- package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
- package/template/coding-task-manager/db.js +156 -32
- package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
- package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
- package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
- package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
- package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
- package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
- package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
- package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
- package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
- package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
- package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
- package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
- package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
- package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
- package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
- package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
- package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
- package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
- package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
- package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
- package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
- package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
- package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
- package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
- package/template/coding-task-manager/lib/agent-definitions.js +19 -0
- package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
- package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
- package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
- package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
- package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
- package/template/coding-task-manager/lib/agent-presets.js +84 -1
- package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
- package/template/coding-task-manager/lib/agent-version-service.js +74 -11
- package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
- package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
- package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
- package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
- package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
- package/template/coding-task-manager/lib/api/fleet.js +118 -0
- package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
- package/template/coding-task-manager/lib/api/ingress.js +9 -1
- package/template/coding-task-manager/lib/api/landing.js +403 -0
- package/template/coding-task-manager/lib/api/machines.js +691 -2
- package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
- package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
- package/template/coding-task-manager/lib/api/session-attach.js +623 -0
- package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
- package/template/coding-task-manager/lib/api/sessions.js +77 -51
- package/template/coding-task-manager/lib/api/setup-network.js +544 -16
- package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
- package/template/coding-task-manager/lib/api/setup.js +829 -10
- package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
- package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
- package/template/coding-task-manager/lib/auth-rules.js +292 -21
- package/template/coding-task-manager/lib/background-stop.js +369 -0
- package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
- package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
- package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
- package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
- package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
- package/template/coding-task-manager/lib/build-info.js +4 -2
- package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
- package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
- package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
- package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
- package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
- package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
- package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
- package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
- package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
- package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
- package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
- package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
- package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
- package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
- package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
- package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
- package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
- package/template/coding-task-manager/lib/conversation-sync.js +355 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
- package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
- package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
- package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
- package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
- package/template/coding-task-manager/lib/db-domains.js +1 -0
- package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
- package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
- package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
- package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
- package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
- package/template/coding-task-manager/lib/feature-manifest.js +4 -0
- package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
- package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
- package/template/coding-task-manager/lib/fleet-hub.js +762 -0
- package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
- package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
- package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
- package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
- package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
- package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
- package/template/coding-task-manager/lib/fleet-update.js +798 -0
- package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
- package/template/coding-task-manager/lib/fs-watcher.js +58 -42
- package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
- package/template/coding-task-manager/lib/headless-term-service.js +84 -8
- package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
- package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
- package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
- package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
- package/template/coding-task-manager/lib/host-handoff.js +16 -19
- package/template/coding-task-manager/lib/host-stats.js +150 -3
- package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
- package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
- package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
- package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
- package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
- package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
- package/template/coding-task-manager/lib/hub-bridge.js +398 -0
- package/template/coding-task-manager/lib/idle-notify.js +491 -0
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
- package/template/coding-task-manager/lib/instance-manifest.js +5 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
- package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
- package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
- package/template/coding-task-manager/lib/landing/config.js +536 -0
- package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
- package/template/coding-task-manager/lib/landing/gate.js +360 -0
- package/template/coding-task-manager/lib/landing/git.js +344 -0
- package/template/coding-task-manager/lib/landing/lease.js +247 -0
- package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
- package/template/coding-task-manager/lib/landing/proof.js +223 -0
- package/template/coding-task-manager/lib/landing/queue.js +998 -0
- package/template/coding-task-manager/lib/landing/radar.js +489 -0
- package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
- package/template/coding-task-manager/lib/landing/state.js +263 -0
- package/template/coding-task-manager/lib/landing/stop.js +49 -0
- package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
- package/template/coding-task-manager/lib/landing/sync.js +456 -0
- package/template/coding-task-manager/lib/landing/train.js +885 -0
- package/template/coding-task-manager/lib/landing/window.js +57 -0
- package/template/coding-task-manager/lib/launch-environment.js +121 -0
- package/template/coding-task-manager/lib/launch-model-override.js +36 -10
- package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
- package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
- package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
- package/template/coding-task-manager/lib/lead-model-options.js +160 -3
- package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
- package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
- package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
- package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
- package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
- package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
- package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
- package/template/coding-task-manager/lib/machine-registry.js +750 -5
- package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
- package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
- package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
- package/template/coding-task-manager/lib/memory-join.js +337 -0
- package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
- package/template/coding-task-manager/lib/memory-sync.js +565 -0
- package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
- package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
- package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
- package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
- package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
- package/template/coding-task-manager/lib/model-availability.js +727 -0
- package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
- package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
- package/template/coding-task-manager/lib/model-id-rule.js +19 -0
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
- package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
- package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
- package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
- package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
- package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
- package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
- package/template/coding-task-manager/lib/native-history-control.js +6 -4
- package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
- package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
- package/template/coding-task-manager/lib/opencode-cli.js +480 -0
- package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
- package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
- package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
- package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
- package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
- package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
- package/template/coding-task-manager/lib/perf-summary.js +7 -0
- package/template/coding-task-manager/lib/perf-tracker.js +63 -2
- package/template/coding-task-manager/lib/project-config.js +7 -0
- package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
- package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
- package/template/coding-task-manager/lib/read-pool-client.js +89 -2
- package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
- package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
- package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
- package/template/coding-task-manager/lib/relay-client.js +57 -3
- package/template/coding-task-manager/lib/relink-audit.js +34 -5
- package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
- package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
- package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
- package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
- package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
- package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
- package/template/coding-task-manager/lib/restart-guard.js +18 -6
- package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
- package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
- package/template/coding-task-manager/lib/resume-admission.js +17 -3
- package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
- package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
- package/template/coding-task-manager/lib/runtime-contract.js +3 -1
- package/template/coding-task-manager/lib/schema-indexes.js +1 -0
- package/template/coding-task-manager/lib/session-context-locator.js +742 -0
- package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
- package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
- package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
- package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
- package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
- package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
- package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
- package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
- package/template/coding-task-manager/lib/session-create/response.js +7 -7
- package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
- package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
- package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
- package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
- package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
- package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
- package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
- package/template/coding-task-manager/lib/session-empty.js +13 -10
- package/template/coding-task-manager/lib/session-host-manager.js +60 -0
- package/template/coding-task-manager/lib/session-jobs.js +3 -1
- package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
- package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
- package/template/coding-task-manager/lib/session-park.js +738 -0
- package/template/coding-task-manager/lib/session-path-grant.js +13 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
- package/template/coding-task-manager/lib/session-transport.js +17 -0
- package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
- package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
- package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
- package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
- package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
- package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
- package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
- package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
- package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
- package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
- package/template/coding-task-manager/lib/submission-ack.js +90 -2
- package/template/coding-task-manager/lib/systemd-scope.js +115 -0
- package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
- package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
- package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
- package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
- package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
- package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
- package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
- package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
- package/template/coding-task-manager/lib/transcript-source.js +36 -4
- package/template/coding-task-manager/lib/transcript-store.js +9 -0
- package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
- package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
- package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
- package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
- package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
- package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
- package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
- package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
- package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
- package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
- package/template/coding-task-manager/lib/walle/turn.js +1 -1
- package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
- package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
- package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
- package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
- package/template/coding-task-manager/lib/walle-row-history.js +495 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
- package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
- package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
- package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
- package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
- package/template/coding-task-manager/lib/ws/misc.js +1 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
- package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
- package/template/coding-task-manager/oauth-proxy.js +112 -115
- package/template/coding-task-manager/package-lock.json +10 -94
- package/template/coding-task-manager/package.json +4 -3
- package/template/coding-task-manager/protocol/message-table.json +10 -0
- package/template/coding-task-manager/protocol/messages.js +10 -0
- package/template/coding-task-manager/protocol/ws-schema.json +20 -0
- package/template/coding-task-manager/protocol/ws-schema.md +2 -0
- package/template/coding-task-manager/public/css/app.css +170 -0
- package/template/coding-task-manager/public/css/walle-session.css +4 -0
- package/template/coding-task-manager/public/index.html +473 -243
- package/template/coding-task-manager/public/js/agent-command.js +86 -0
- package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
- package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
- package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
- package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
- package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
- package/template/coding-task-manager/public/js/background-work.js +196 -27
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
- package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
- package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
- package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
- package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
- package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
- package/template/coding-task-manager/public/js/document-review-links.js +9 -1
- package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
- package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
- package/template/coding-task-manager/public/js/message-renderer.js +4 -3
- package/template/coding-task-manager/public/js/model-picker.js +3 -1
- package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
- package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
- package/template/coding-task-manager/public/js/pasted-content.js +236 -0
- package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
- package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
- package/template/coding-task-manager/public/js/prompts.js +11 -10
- package/template/coding-task-manager/public/js/protocol.js +10 -0
- package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
- package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
- package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
- package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
- package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
- package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
- package/template/coding-task-manager/public/js/stream-view.js +6 -5
- package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
- package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
- package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
- package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
- package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
- package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
- package/template/coding-task-manager/public/js/walle.js +6 -0
- package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
- package/template/coding-task-manager/public/m/app.css +140 -0
- package/template/coding-task-manager/public/m/app.js +507 -68
- package/template/coding-task-manager/public/m/index.html +17 -0
- package/template/coding-task-manager/public/m/machines.js +838 -4
- package/template/coding-task-manager/public/m/private-api.js +31 -1
- package/template/coding-task-manager/public/usage.html +1 -0
- package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
- package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
- package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
- package/template/coding-task-manager/server.js +1950 -11679
- package/template/coding-task-manager/session-integrity.js +64 -4
- package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
- package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
- package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
- package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
- package/template/coding-task-manager/workers/session-host-process.js +5 -5
- package/template/coding-task-manager/workers/watch-worker.js +105 -0
- package/template/coding-task-manager/workers/worktree-worker.js +168 -33
- package/template/ctm-relay/lib/protocol.js +14 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/shared/backup-retention.js +82 -0
- package/template/shared/conversation-admission.js +2 -0
- package/template/shared/conversation-provenance.js +3 -7
- package/template/shared/conversation-storage-v2-contract.js +5 -0
- package/template/shared/linked-worktree.js +51 -0
- package/template/shared/macos-fda-target.js +57 -0
- package/template/shared/orphan-parents.js +55 -0
- package/template/shared/release-schema.json +2 -2
- package/template/shared/storage/contract.js +3 -0
- package/template/wall-e/adapters/imessage.js +12 -0
- package/template/wall-e/agent.js +71 -4
- package/template/wall-e/api-walle.js +21 -1
- package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
- package/template/wall-e/brain/brain-join.js +859 -0
- package/template/wall-e/brain/brain-sync-membership.js +20 -0
- package/template/wall-e/brain/brain-sync-store.js +131 -77
- package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
- package/template/wall-e/brain/brain-sync-v2.js +578 -0
- package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
- package/template/wall-e/brain/deterministic-id.js +51 -0
- package/template/wall-e/brain/memories.js +2 -2
- package/template/wall-e/brain/migrations.js +40 -6
- package/template/wall-e/brain/model-identity-migration.js +1 -1
- package/template/wall-e/brain/owner-call-whitelist.js +23 -7
- package/template/wall-e/brain/schema.js +45 -85
- package/template/wall-e/brain.js +171 -35
- package/template/wall-e/chat.js +3 -3
- package/template/wall-e/coding/agent-shell-env.js +7 -2
- package/template/wall-e/coding/prompt-bundle.js +66 -21
- package/template/wall-e/config/main-loop-exemptions.json +2 -2
- package/template/wall-e/http/admin-api.js +37 -0
- package/template/wall-e/http/gws-api.js +4 -0
- package/template/wall-e/http/helper-rules-api.js +7 -1
- package/template/wall-e/http/model-admin.js +8 -1
- package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
- package/template/wall-e/http/model-proxy-providers.js +174 -1
- package/template/wall-e/http/model-route-health.js +157 -0
- package/template/wall-e/http/sync-api.js +278 -464
- package/template/wall-e/jobs/job-runner.js +28 -1
- package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
- package/template/wall-e/lib/ctm-data-dir.js +26 -0
- package/template/wall-e/lib/derivation-home.js +147 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
- package/template/wall-e/lib/helper-rules/grade.js +51 -8
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
- package/template/wall-e/lib/helper-rules/store.js +4 -2
- package/template/wall-e/lib/landing-mcp-tools.js +170 -0
- package/template/wall-e/lib/mcp-client-config.js +133 -14
- package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
- package/template/wall-e/lib/mcp-integration.js +58 -13
- package/template/wall-e/lib/parent-death.js +7 -1
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
- package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
- package/template/wall-e/llm/delegate-vendor-default.js +12 -5
- package/template/wall-e/llm/model-roles.js +1 -1
- package/template/wall-e/llm/native-delegate-selection.js +56 -8
- package/template/wall-e/llm/worker-delegate-args.js +72 -7
- package/template/wall-e/llm/worker-mcp-routing.js +66 -17
- package/template/wall-e/loops/brain-custom-writes.js +19 -0
- package/template/wall-e/loops/ingest.js +17 -1
- package/template/wall-e/loops/think.js +19 -4
- package/template/wall-e/mcp-contract.js +222 -6
- package/template/wall-e/mcp-protocol-contract.js +40 -5
- package/template/wall-e/mcp-server.js +128 -29
- package/template/wall-e/mcp-worker-schemas.js +5 -5
- package/template/wall-e/memory/ctm-context-client.js +85 -0
- package/template/wall-e/memory/ctm-session-context.js +9 -3
- package/template/wall-e/memory/hub-bridge-client.js +388 -0
- package/template/wall-e/memory/replica-origin.js +56 -0
- package/template/wall-e/memory/session-pool.js +135 -0
- package/template/wall-e/runtime/live-capabilities.js +8 -3
- package/template/wall-e/server.js +4 -2
- package/template/wall-e/shared/release-schema.json +2 -2
- package/template/wall-e/shared/sqlite-file-set.js +39 -0
- package/template/wall-e/shared/storage/contract.js +3 -0
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
- package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
- package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
- package/template/wall-e/tools/chrome-automation.js +6 -0
- package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
- package/template/wall-e/workers/brain-owner-worker.js +18 -4
- package/template/wall-e/workers/mcp-gateway-process.js +5 -5
|
@@ -224,10 +224,10 @@
|
|
|
224
224
|
<div id="nav-more-wrap">
|
|
225
225
|
<button class="nav-pill" id="nav-more-button" onclick="toggleNavMore()" title="More pages" aria-haspopup="menu" aria-expanded="false">More <span style="font-size:10px;">▾</span></button>
|
|
226
226
|
<div id="nav-more-dropdown" role="menu" aria-label="More pages">
|
|
227
|
+
<button class="nav-pill" data-nav="machines" onclick="navTo('machines');closeNavMore()" title="Machines — how this Mac is used and by which sessions" role="menuitem">Machines</button>
|
|
227
228
|
<button class="nav-pill" data-nav="insights" onclick="navTo('insights');closeNavMore()" role="menuitem">Insights</button>
|
|
228
229
|
<button class="nav-pill" data-nav="permissions" onclick="navTo('permissions');closeNavMore()" role="menuitem">Permissions</button>
|
|
229
230
|
<button class="nav-pill" data-nav="worktrees" onclick="navTo('worktrees');closeNavMore()" role="menuitem">Worktrees</button>
|
|
230
|
-
<button class="nav-pill" data-nav="machines" onclick="navTo('machines');closeNavMore()" role="menuitem">Machines</button>
|
|
231
231
|
</div>
|
|
232
232
|
</div>
|
|
233
233
|
<button class="nav-pill" data-nav="setup" onclick="navTo('setup')" title="Setup & Settings" style="margin-left:auto;font-size:16px;padding:4px 8px;">⚙</button>
|
|
@@ -310,6 +310,10 @@
|
|
|
310
310
|
</span>
|
|
311
311
|
<button id="restart-banner-reload" type="button" onclick="reloadForInstalledUpdate('banner')" hidden>Reload</button>
|
|
312
312
|
<button id="restart-banner-dismiss" type="button" onclick="dismissRestartBanner()" title="Dismiss" aria-label="Dismiss restart notice" hidden>×</button>
|
|
313
|
+
<!-- WP-2 (docs/external-causality-kill-session-loss-2026-10-27.html §5 D-5, §6, §8 WP-2, AC-2a):
|
|
314
|
+
the dead adopted host's ONE action. Sits after the restart/reload pair so the existing
|
|
315
|
+
restart-group geometry is untouched; visibility + copy come from RestartBannerPolicy. -->
|
|
316
|
+
<button id="restart-banner-resume" type="button" onclick="resumeHostDeadSessions()" hidden>Resume</button>
|
|
313
317
|
</span>
|
|
314
318
|
</div>
|
|
315
319
|
<div class="modal-overlay update-wizard-overlay hidden" id="update-wizard" role="dialog" aria-modal="true" aria-labelledby="update-wizard-heading">
|
|
@@ -346,6 +350,7 @@
|
|
|
346
350
|
<div class="sidebar-section">
|
|
347
351
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:6px;">
|
|
348
352
|
<h3 style="margin-bottom:0">Active Sessions</h3>
|
|
353
|
+
<span id="active-computer-filter-chip" style="margin-left:auto"></span>
|
|
349
354
|
<select id="active-sort" onchange="setActiveSort(this.value)" title="Sort active sessions" style="background:var(--bg);color:var(--fg-dim);border:1px solid var(--border);padding:2px 4px;border-radius:3px;font-size:10px;cursor:pointer;">
|
|
350
355
|
<option value="by_activity">By activity</option>
|
|
351
356
|
<option value="tab_order">Tab order</option>
|
|
@@ -359,7 +364,7 @@
|
|
|
359
364
|
</div>
|
|
360
365
|
<div class="sidebar-section" style="border-top:1px solid var(--border)">
|
|
361
366
|
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:6px;">
|
|
362
|
-
<h3 style="margin-bottom:0;display:flex;align-items:center;gap:6px;">Recent Sessions <span id="recent-refreshing-chip" class="recent-refreshing-chip" hidden>Refreshing</span></h3>
|
|
367
|
+
<h3 style="margin-bottom:0;display:flex;align-items:center;gap:6px;">Recent Sessions <span id="recent-refreshing-chip" class="recent-refreshing-chip" hidden>Refreshing</span> <span id="recent-computer-filter-chip"></span></h3>
|
|
363
368
|
<button class="mini-btn delete" onclick="cleanEmptySessions()" title="Delete all empty sessions" style="background:var(--bg);border:1px solid var(--border);color:var(--red);padding:2px 6px;border-radius:3px;font-size:9px;cursor:pointer;">Clean Empty</button>
|
|
364
369
|
</div>
|
|
365
370
|
<div id="prefs-load-error" role="status">
|
|
@@ -2274,7 +2279,7 @@ url = "http://localhost:<span id="setup-mcp-port-display-toml">3457</span>/mcp"<
|
|
|
2274
2279
|
<button class="btn primary small" onclick="PE.createManualBackup()">Backup Now</button>
|
|
2275
2280
|
</div>
|
|
2276
2281
|
<div style="padding:12px;font-size:12px;color:var(--fg-dim);border-bottom:1px solid var(--border);background:var(--bg-light);">
|
|
2277
|
-
Daily auto-backups are created automatically.
|
|
2282
|
+
Daily auto-backups are created automatically. Old backups are removed after each backup — set how many days to keep in Setup → Backups (default 3).
|
|
2278
2283
|
</div>
|
|
2279
2284
|
<div id="pe-backup-list" style="flex:1;overflow-y:auto;padding:10px;"></div>
|
|
2280
2285
|
</div>
|
|
@@ -2778,7 +2783,7 @@ url = "http://localhost:<span id="setup-mcp-port-display-toml">3457</span>/mcp"<
|
|
|
2778
2783
|
<script src="https://cdn.jsdelivr.net/npm/html2canvas@1.4.1/dist/html2canvas.min.js" crossorigin="anonymous"></script>
|
|
2779
2784
|
<script src="js/session-surface-runtime.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2780
2785
|
<script src="js/presentation-arbiter.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2781
|
-
<script src="js/lifecycle-cover-render.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/porter-stemmer.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/session-search-utils.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/recent-active-identity-index.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2786
|
+
<script src="js/lifecycle-cover-render.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/porter-stemmer.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/agent-command.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/session-search-utils.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/recent-active-identity-index.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2782
2787
|
<script src="js/session-activity-utils.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2783
2788
|
<script src="js/session-status-precedence.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2784
2789
|
<script src="js/session-phase.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
@@ -2846,6 +2851,7 @@ url = "http://localhost:<span id="setup-mcp-port-display-toml">3457</span>/mcp"<
|
|
|
2846
2851
|
<script src="js/walle-session-runtime-helpers.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2847
2852
|
<script src="js/core/01-util.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2848
2853
|
<script src="js/core/02-toast.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2854
|
+
<script src="js/core/pending-create.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2849
2855
|
<script src="js/model-picker.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2850
2856
|
<script src="js/private-model-lifetime.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2851
2857
|
<script src="js/session-model-picker.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
@@ -2857,6 +2863,7 @@ url = "http://localhost:<span id="setup-mcp-port-display-toml">3457</span>/mcp"<
|
|
|
2857
2863
|
<script src="js/terminal-reference-adapter.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/document-review-links.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2858
2864
|
<script src="js/conversation-turn-cards.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2859
2865
|
<script src="js/root-review-actions.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2866
|
+
<script src="js/pasted-content.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2860
2867
|
<script src="js/message-renderer.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2861
2868
|
<script src="js/native-subagent-review.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2862
2869
|
<script src="js/main-thread-budget.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
@@ -2927,7 +2934,10 @@ url = "http://localhost:<span id="setup-mcp-port-display-toml">3457</span>/mcp"<
|
|
|
2927
2934
|
<script src="js/background-work.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2928
2935
|
<script src="js/core/session-selectors.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2929
2936
|
<script src="js/core/session-commands.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2937
|
+
<script src="js/core/active-session-groups.js?v=__CTM_ASSET_EPOCH__"></script><script src="js/core/recent-machine-source.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2930
2938
|
<script src="js/core/worker-tree.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2939
|
+
<!-- S-43 (WP-7): the New Session agent grid + the dsh card's Model select. -->
|
|
2940
|
+
<script src="js/new-session-agent-grid.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2931
2941
|
<script src="js/terminal-input-policy.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2932
2942
|
<script src="js/session-transport-state.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2933
2943
|
<script src="js/terminal-surface-resume-policy.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
@@ -2955,6 +2965,7 @@ url = "http://localhost:<span id="setup-mcp-port-display-toml">3457</span>/mcp"<
|
|
|
2955
2965
|
<script src="js/terminal/runtime-18.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2956
2966
|
<script src="js/terminal/runtime-19.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2957
2967
|
<script src="js/terminal/runtime-20.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2968
|
+
<script src="js/terminal/conv-only-session.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2958
2969
|
<script src="js/sidebar-active-reveal.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2959
2970
|
<script src="js/terminal/runtime-21.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
2960
2971
|
<script src="js/terminal/runtime-23.js?v=__CTM_ASSET_EPOCH__"></script>
|
|
@@ -3677,7 +3688,7 @@ const state = window._ctmState = {
|
|
|
3677
3688
|
minClientBuildStale: false,
|
|
3678
3689
|
clientUpdateDiskEpoch: '',
|
|
3679
3690
|
},
|
|
3680
|
-
restartBanner: { walle: null, ctm: { reloadRequired: false, backendRestartRequired: false, protocolIncompatible: false, loadedBuildId: '', latestBuildId: '' } },
|
|
3691
|
+
restartBanner: { walle: null, hostDead: null, ctm: { reloadRequired: false, backendRestartRequired: false, protocolIncompatible: false, loadedBuildId: '', latestBuildId: '' } },
|
|
3681
3692
|
splitRoot: null, // Binary tree of panes (null = single-pane mode)
|
|
3682
3693
|
savedSplitLayout: null, // Suspended split tree (saved when switching to non-split tab)
|
|
3683
3694
|
focusedPaneId: null, // ID of focused leaf pane
|
|
@@ -4411,10 +4422,11 @@ function _dispatchWebSocketMessage(msg) {
|
|
|
4411
4422
|
case 'ui-prefs-changed': onUiPrefsChanged(msg); break;
|
|
4412
4423
|
case 'session-surface-preference': onSessionSurfacePreference(msg); break;
|
|
4413
4424
|
case 'created': onCreated(msg); break;
|
|
4425
|
+
case 'create-accepted': if (typeof PendingCreate !== 'undefined') PendingCreate.accepted(msg); break;
|
|
4414
4426
|
case 'submit-prompt-status':
|
|
4415
4427
|
window.CTMPromptSubmission?.handleStatus(msg);
|
|
4416
4428
|
_handlePromptOutboxStatus(msg);
|
|
4417
|
-
qpReconcileSubmissionMessage(msg);
|
|
4429
|
+
qpReconcileSubmissionMessage(msg); if (typeof _convOnlyHandleSubmitStatus === 'function') _convOnlyHandleSubmitStatus(msg);
|
|
4418
4430
|
break;
|
|
4419
4431
|
case 'input-dropped': onInputDropped(msg); break;
|
|
4420
4432
|
case 'output': onOutput(msg); break;
|
|
@@ -4461,6 +4473,7 @@ function _dispatchWebSocketMessage(msg) {
|
|
|
4461
4473
|
case 'resume-blocked': onSessionResumeBlocked(msg); break;
|
|
4462
4474
|
case 'resume-retry-noop': onResumeRetryNoop(msg); break;
|
|
4463
4475
|
case 'error': {
|
|
4476
|
+
if (typeof PendingCreate !== 'undefined' && PendingCreate.settleError(msg)) break;
|
|
4464
4477
|
// WP-5 (§6.3/AC-5a): a machine-scoped failure (machine_unreachable, or the box's own code)
|
|
4465
4478
|
// is SHOWN — the user picks another machine or fixes the box. Never retried here, and every
|
|
4466
4479
|
// other error frame keeps its existing console-only path.
|
|
@@ -4556,6 +4569,12 @@ function _dispatchWebSocketMessage(msg) {
|
|
|
4556
4569
|
state._restarting = true;
|
|
4557
4570
|
_transitionSessionTransport({ type: 'server-restarting' });
|
|
4558
4571
|
break;
|
|
4572
|
+
case 'host-dead':
|
|
4573
|
+
// WP-2 (docs/external-causality-kill-session-loss-2026-10-27.html §5 D-5, §6, §8 WP-2,
|
|
4574
|
+
// AC-2a): an adopted host died with sessions still attached. Same banner element as every
|
|
4575
|
+
// other restart fact — see public/js/app/restart-banner.js's setHostDeadFacts.
|
|
4576
|
+
setHostDeadFacts(msg);
|
|
4577
|
+
break;
|
|
4559
4578
|
case 'server-ready':
|
|
4560
4579
|
window.CtmSessionModelPicker?.resetRequestCache?.();
|
|
4561
4580
|
state._serverReady = true;
|
|
@@ -5444,13 +5463,22 @@ function applyTabOrder(saved) {
|
|
|
5444
5463
|
const restorableIds = state.tabOrder.filter(id => state.sessions.has(id) || id === 'review');
|
|
5445
5464
|
const nonRestorableIds = state.tabOrder.filter(id => !state.sessions.has(id) && id !== 'review');
|
|
5446
5465
|
const ordered = [];
|
|
5466
|
+
// WP-5 (S-12, remote-session-child-transcript-model-truth-and-restart-2026-10-27.html): a saved id
|
|
5467
|
+
// this client has never been told about — a BOX session whose catalog has not landed yet after a hub
|
|
5468
|
+
// restart. It is NOT pushed into state.tabOrder (no renderer or keyboard cycle may see a tab with no
|
|
5469
|
+
// session); it is remembered so the next saveTabOrder() writes it back where it was saved, instead of
|
|
5470
|
+
// losing the pref and letting the session append at the END of the strip when the catalog arrives.
|
|
5471
|
+
const undelivered = new Set();
|
|
5447
5472
|
for (const id of saved) {
|
|
5448
5473
|
if (restorableIds.includes(id)) ordered.push(id);
|
|
5474
|
+
else if (!nonRestorableIds.includes(id) && id !== 'review') undelivered.add(id);
|
|
5449
5475
|
}
|
|
5450
5476
|
for (const id of restorableIds) {
|
|
5451
5477
|
if (!ordered.includes(id)) ordered.push(id);
|
|
5452
5478
|
}
|
|
5453
5479
|
state.tabOrder = [...nonRestorableIds, ...ordered];
|
|
5480
|
+
state._undeliveredTabIds = undelivered;
|
|
5481
|
+
state._undeliveredTabSaved = saved.slice();
|
|
5454
5482
|
}
|
|
5455
5483
|
|
|
5456
5484
|
function _sessionOwnershipMeta(session) {
|
|
@@ -7071,33 +7099,73 @@ async function loadInstanceSourceLedgers() {
|
|
|
7071
7099
|
// to macOS) or NFS mounts, so macOS re-prompts for file access on every restart. Full Disk
|
|
7072
7100
|
// Access is a superset that, under the stable Dev-ID identity, persists — one grant ends it.
|
|
7073
7101
|
// We can only detect + guide; Apple forbids granting FDA programmatically.
|
|
7102
|
+
// WP-3 (D-9, docs/signin-flow-restart-settle-and-fda-target-identity-2026-10-26.html): the
|
|
7103
|
+
// banner names the EXACT exec the grant must be charged to (cap.fdaTargets / messages.fdaTarget,
|
|
7104
|
+
// both probed from the running exec path by the server — never a fixed "Coding Task Manager"),
|
|
7105
|
+
// stays up after Grant with a "Check again" button, re-checks once per window focus (no new
|
|
7106
|
+
// timer: release/ownership-registry.json keys client timers by ordinal), and offers
|
|
7107
|
+
// "Restart Wall-E" on the split verdict (CTM's probe says granted, Wall-E's Messages probe
|
|
7108
|
+
// still says denied — a running daemon may need the relaunch).
|
|
7074
7109
|
let _fdaCapabilityChecked = false;
|
|
7110
|
+
let _fdaFocusHandlerBound = false;
|
|
7111
|
+
let _fdaBannerAfterGrant = false;
|
|
7112
|
+
let _fdaBannerOpts = null;
|
|
7075
7113
|
const FDA_BANNER_DISMISS_KEY = 'ctm_fda_banner_dismissed_v1';
|
|
7076
7114
|
|
|
7077
|
-
|
|
7115
|
+
// One probe, two callers: the boot check and the re-check. Best-effort — a failed probe
|
|
7116
|
+
// returns null and never blocks the UI.
|
|
7117
|
+
async function _fetchFdaState() {
|
|
7078
7118
|
try {
|
|
7079
|
-
if (localStorage.getItem(FDA_BANNER_DISMISS_KEY) === '1') return;
|
|
7080
7119
|
const r = await fetch(`/api/capabilities?token=${state.token}`);
|
|
7081
|
-
if (!r.ok) return;
|
|
7120
|
+
if (!r.ok) return null;
|
|
7082
7121
|
const cap = await r.json();
|
|
7083
7122
|
// S-44 (imessage-memory-source WP-4): reuse this banner for the iMessage FDA
|
|
7084
7123
|
// need too, rather than building a second one — best-effort, never blocks.
|
|
7085
|
-
let
|
|
7124
|
+
let messages = null;
|
|
7086
7125
|
if (cap && cap.platform === 'darwin') {
|
|
7087
7126
|
try {
|
|
7088
7127
|
const mr = await fetch('/api/wall-e/gws/accounts?summary=1', { cache: 'no-store' });
|
|
7089
7128
|
if (mr.ok) {
|
|
7090
7129
|
const md = await mr.json();
|
|
7091
|
-
|
|
7092
|
-
if (messages && messages.dbPresent && !messages.fullDiskAccess) imessageNeedsFda = true;
|
|
7130
|
+
messages = (md && md.macosNative && md.macosNative.messages) || null;
|
|
7093
7131
|
}
|
|
7094
|
-
} catch { /* expected: the Wall-E gws/accounts probe is best-effort — a failed fetch just
|
|
7132
|
+
} catch { /* expected: the Wall-E gws/accounts probe is best-effort — a failed fetch just leaves messages null and the banner falls back to CTM's own needsAttention check */ }
|
|
7133
|
+
}
|
|
7134
|
+
return { cap, messages, imessageNeedsFda: !!(messages && messages.dbPresent && !messages.fullDiskAccess) };
|
|
7135
|
+
} catch { /* best-effort — never block the UI on this */ return null; }
|
|
7136
|
+
}
|
|
7137
|
+
|
|
7138
|
+
async function checkFdaCapability() {
|
|
7139
|
+
try {
|
|
7140
|
+
if (localStorage.getItem(FDA_BANNER_DISMISS_KEY) === '1') return;
|
|
7141
|
+
const probed = await _fetchFdaState();
|
|
7142
|
+
if (!probed) return;
|
|
7143
|
+
if (probed.cap && (probed.cap.needsAttention || probed.imessageNeedsFda)) {
|
|
7144
|
+
showFdaBanner(probed.cap, { imessageNeedsFda: probed.imessageNeedsFda, messages: probed.messages });
|
|
7145
|
+
} else {
|
|
7146
|
+
hideFdaBanner();
|
|
7095
7147
|
}
|
|
7096
|
-
if (cap && (cap.needsAttention || imessageNeedsFda)) showFdaBanner(cap, { imessageNeedsFda });
|
|
7097
|
-
else hideFdaBanner();
|
|
7098
7148
|
} catch { /* best-effort — never block the UI on this */ }
|
|
7099
7149
|
}
|
|
7100
7150
|
|
|
7151
|
+
// Re-check an EXISTING banner in place (window focus / "Check again"). Never creates one —
|
|
7152
|
+
// checkFdaCapability owns first paint. When nothing is left to fix it hides the banner and,
|
|
7153
|
+
// if the user had already granted, confirms once.
|
|
7154
|
+
async function refreshFdaBanner() {
|
|
7155
|
+
const banner = document.getElementById('fda-capability-banner');
|
|
7156
|
+
if (!banner) return;
|
|
7157
|
+
const probed = await _fetchFdaState();
|
|
7158
|
+
if (!probed) return;
|
|
7159
|
+
if (probed.cap && (probed.cap.needsAttention || probed.imessageNeedsFda)) {
|
|
7160
|
+
_applyFdaBannerState(banner, probed.cap, { imessageNeedsFda: probed.imessageNeedsFda, messages: probed.messages });
|
|
7161
|
+
} else {
|
|
7162
|
+
const wasAfterGrant = _fdaBannerAfterGrant;
|
|
7163
|
+
_fdaBannerAfterGrant = false;
|
|
7164
|
+
hideFdaBanner();
|
|
7165
|
+
if (wasAfterGrant) toast('Full Disk Access granted. Wall-E can read Messages now.', { type: 'success', duration: 7000 });
|
|
7166
|
+
}
|
|
7167
|
+
}
|
|
7168
|
+
|
|
7101
7169
|
function hideFdaBanner() {
|
|
7102
7170
|
const b = document.getElementById('fda-capability-banner');
|
|
7103
7171
|
if (b) b.remove();
|
|
@@ -7105,40 +7173,83 @@ function hideFdaBanner() {
|
|
|
7105
7173
|
|
|
7106
7174
|
function showFdaBanner(cap, opts) {
|
|
7107
7175
|
opts = opts || {};
|
|
7108
|
-
|
|
7109
|
-
|
|
7110
|
-
const
|
|
7176
|
+
const existing = document.getElementById('fda-capability-banner');
|
|
7177
|
+
if (existing) { _applyFdaBannerState(existing, cap, opts); return; } // one banner, updated in place
|
|
7178
|
+
const banner = document.createElement('div');
|
|
7179
|
+
banner.id = 'fda-capability-banner';
|
|
7180
|
+
banner.className = 'fda-capability-banner';
|
|
7181
|
+
_applyFdaBannerState(banner, cap, opts);
|
|
7182
|
+
document.body.appendChild(banner);
|
|
7183
|
+
}
|
|
7184
|
+
|
|
7185
|
+
function _escFda(s) {
|
|
7186
|
+
return String(s == null ? '' : s).replace(/[&<>"']/g, (c) => (
|
|
7187
|
+
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
7188
|
+
}
|
|
7189
|
+
|
|
7190
|
+
// The exec identities a grant must be charged to: Wall-E's Messages target when the Messages
|
|
7191
|
+
// need is what raised the banner, else every target CTM reported. Empty (an older server that
|
|
7192
|
+
// sends no fdaTargets) keeps the pre-WP-3, path-less sentence.
|
|
7193
|
+
function _fdaBannerTargets(cap, messages, messagesLed) {
|
|
7194
|
+
if (messagesLed && messages && messages.fdaTarget && messages.fdaTarget.path) return [messages.fdaTarget];
|
|
7195
|
+
const out = [];
|
|
7196
|
+
for (const t of (cap && Array.isArray(cap.fdaTargets) ? cap.fdaTargets : [])) if (t && t.path) out.push(t);
|
|
7197
|
+
return out;
|
|
7198
|
+
}
|
|
7199
|
+
|
|
7200
|
+
function _fdaBannerMessage(cap, opts) {
|
|
7201
|
+
const count = Number(cap && cap.networkSessionCount) || 0;
|
|
7202
|
+
const denials = cap && Array.isArray(cap.accessDenials) ? cap.accessDenials : [];
|
|
7203
|
+
const messages = opts.messages || null;
|
|
7204
|
+
const messagesLed = !!opts.imessageNeedsFda && !(cap && cap.needsAttention);
|
|
7205
|
+
const targets = _fdaBannerTargets(cap, messages, messagesLed);
|
|
7206
|
+
const named = targets.length ? ` to <code>${_escFda(targets.map((t) => t.path).join(' and '))}</code>` : '';
|
|
7111
7207
|
// Prefer the specific, observed cause (e.g. Codex couldn't read ~/.codex) over the generic
|
|
7112
7208
|
// count — it's actionable and explains why a session just failed to load.
|
|
7113
7209
|
let message;
|
|
7114
7210
|
if (denials.length) {
|
|
7115
|
-
const esc = (s) => String(s == null ? '' : s).replace(/[&<>"']/g, (c) => (
|
|
7116
|
-
{ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
|
|
7117
7211
|
const d = denials[0];
|
|
7118
7212
|
const who = d.provider ? d.provider.charAt(0).toUpperCase() + d.provider.slice(1) : 'An agent';
|
|
7119
7213
|
const where = (d.path || '').replace(/\/config\.toml$/, '') || d.path || 'a file';
|
|
7120
|
-
message = `${
|
|
7121
|
-
} else if (
|
|
7122
|
-
message = `<strong>Grant Full Disk Access once</strong
|
|
7214
|
+
message = `${_escFda(who)} couldn't read <code>${_escFda(where)}</code> (it's on Dropbox/iCloud or a network volume), so the session failed to load. <strong>Grant Full Disk Access once</strong>${named} to fix it.`;
|
|
7215
|
+
} else if (messagesLed) {
|
|
7216
|
+
message = `<strong>Grant Full Disk Access once</strong>${named} so Wall-E can read your Messages history.`;
|
|
7123
7217
|
} else {
|
|
7124
|
-
message = `CTM has ${count} session${count === 1 ? '' : 's'} on a network/Dropbox volume, so macOS keeps asking for file access on every restart. <strong>Grant Full Disk Access once</strong
|
|
7218
|
+
message = `CTM has ${count} session${count === 1 ? '' : 's'} on a network/Dropbox volume, so macOS keeps asking for file access on every restart. <strong>Grant Full Disk Access once</strong>${named} to stop it.`;
|
|
7125
7219
|
}
|
|
7126
7220
|
// S-44: append the iMessage sentence whenever Wall-E's Messages mirror also needs
|
|
7127
7221
|
// FDA, even if the banner's primary trigger was CTM's own network-volume/denial check.
|
|
7128
|
-
if (opts.imessageNeedsFda && cap.needsAttention) {
|
|
7222
|
+
if (opts.imessageNeedsFda && cap && cap.needsAttention) {
|
|
7129
7223
|
message += ' iMessage history needs Full Disk Access for Wall-E.';
|
|
7130
7224
|
}
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7225
|
+
// Homebrew's `node` is ad-hoc signed under a versioned Cellar path, so its row can look
|
|
7226
|
+
// granted while the running binary never matches — name the trap only when it applies.
|
|
7227
|
+
if (targets.some((t) => t.displayName === 'node')) {
|
|
7228
|
+
message += ' System Settings lists it as “node”; another “node” row does not count.';
|
|
7229
|
+
}
|
|
7230
|
+
return message;
|
|
7231
|
+
}
|
|
7232
|
+
|
|
7233
|
+
// Write the banner's text + buttons for the current state. Idempotent on an existing node, so a
|
|
7234
|
+
// re-check updates the banner in place instead of stacking a second one (D-9).
|
|
7235
|
+
function _applyFdaBannerState(banner, cap, opts) {
|
|
7236
|
+
opts = opts || {};
|
|
7237
|
+
_fdaBannerOpts = opts;
|
|
7238
|
+
const afterGrant = _fdaBannerAfterGrant;
|
|
7239
|
+
const splitVerdict = !!(cap && cap.fdaGranted === true && opts.messages && opts.messages.fullDiskAccess === false);
|
|
7134
7240
|
banner.innerHTML = `
|
|
7135
7241
|
<span class="fda-banner-icon">🔒</span>
|
|
7136
|
-
<span class="fda-banner-text">${
|
|
7137
|
-
<button class="fda-banner-grant" type="button"
|
|
7242
|
+
<span class="fda-banner-text">${_fdaBannerMessage(cap, opts)}</span>
|
|
7243
|
+
<button class="fda-banner-grant" type="button">${afterGrant ? 'Open Settings again' : 'Grant Full Disk Access'}</button>
|
|
7244
|
+
${afterGrant ? '<button class="fda-banner-check" type="button">Check again</button>' : ''}
|
|
7245
|
+
${splitVerdict ? '<button class="fda-banner-restart-walle" type="button">Restart Wall-E</button>' : ''}
|
|
7138
7246
|
<button class="fda-banner-later" type="button">Later</button>
|
|
7139
7247
|
<button class="fda-banner-never" type="button" title="Don't show again">Don't show again</button>`;
|
|
7140
|
-
document.body.appendChild(banner);
|
|
7141
7248
|
banner.querySelector('.fda-banner-grant').addEventListener('click', () => grantFdaAccess(cap));
|
|
7249
|
+
const check = banner.querySelector('.fda-banner-check');
|
|
7250
|
+
if (check) check.addEventListener('click', refreshFdaBanner);
|
|
7251
|
+
const restart = banner.querySelector('.fda-banner-restart-walle');
|
|
7252
|
+
if (restart) restart.addEventListener('click', () => svcAction('walle', 'restart', { confirmed: true }));
|
|
7142
7253
|
banner.querySelector('.fda-banner-later').addEventListener('click', hideFdaBanner);
|
|
7143
7254
|
banner.querySelector('.fda-banner-never').addEventListener('click', () => {
|
|
7144
7255
|
try { localStorage.setItem(FDA_BANNER_DISMISS_KEY, '1'); } catch {}
|
|
@@ -7148,19 +7259,30 @@ function showFdaBanner(cap, opts) {
|
|
|
7148
7259
|
|
|
7149
7260
|
async function grantFdaAccess(cap) {
|
|
7150
7261
|
const fallbackHint = (cap && cap.hint) ||
|
|
7151
|
-
'Open System Settings → Privacy & Security → Full Disk Access and
|
|
7262
|
+
'Open System Settings → Privacy & Security → Full Disk Access and turn on the highlighted file.';
|
|
7263
|
+
// One focus listener per page: the user leaving/returning from System Settings is the
|
|
7264
|
+
// natural re-check trigger, and there is no orderly ordinal left for a new client timer.
|
|
7265
|
+
if (!_fdaFocusHandlerBound) {
|
|
7266
|
+
_fdaFocusHandlerBound = true;
|
|
7267
|
+
window.addEventListener('focus', () => {
|
|
7268
|
+
if (document.getElementById('fda-capability-banner')) refreshFdaBanner();
|
|
7269
|
+
});
|
|
7270
|
+
}
|
|
7152
7271
|
try {
|
|
7153
7272
|
const r = await fetch(`/api/capabilities/open-fda?token=${state.token}`, { method: 'POST' });
|
|
7154
7273
|
const d = await r.json().catch(() => ({}));
|
|
7155
7274
|
if (d && d.ok) {
|
|
7156
|
-
toast(
|
|
7157
|
-
|
|
7158
|
-
|
|
7275
|
+
toast(`Full Disk Access opened on ${d.hostname || 'this Mac'}: add the highlighted file, turn it on, then click Check again.`, { type: 'info', title: 'Full Disk Access', duration: 14000 });
|
|
7276
|
+
// The banner stays up (D-9) in its after-grant state, so the user can confirm or retry.
|
|
7277
|
+
_fdaBannerAfterGrant = true;
|
|
7278
|
+
const banner = document.getElementById('fda-capability-banner');
|
|
7279
|
+
if (banner) _applyFdaBannerState(banner, cap, _fdaBannerOpts || {});
|
|
7280
|
+
return;
|
|
7159
7281
|
}
|
|
7282
|
+
toast(fallbackHint, { type: 'warning', title: 'Grant Full Disk Access', duration: 14000 });
|
|
7160
7283
|
} catch {
|
|
7161
7284
|
toast(fallbackHint, { type: 'warning', title: 'Grant Full Disk Access', duration: 14000 });
|
|
7162
7285
|
}
|
|
7163
|
-
hideFdaBanner();
|
|
7164
7286
|
}
|
|
7165
7287
|
|
|
7166
7288
|
// --- Global services-health banner (golden-four alarms + actionable Wall-E
|
|
@@ -7964,15 +8086,7 @@ window.addEventListener('storage', (event) => {
|
|
|
7964
8086
|
// Enabled per-session by agent type. Shell + unknown agents skip overlay
|
|
7965
8087
|
// (their prompt format is variable; bad overlay positioning is user-visible).
|
|
7966
8088
|
function _clientDetectAgentType(cmd) {
|
|
7967
|
-
|
|
7968
|
-
const c = String(cmd).toLowerCase();
|
|
7969
|
-
if (c.includes('claude')) return 'claude';
|
|
7970
|
-
if (c.includes('codex')) return 'codex';
|
|
7971
|
-
if (c.includes('gemini')) return 'gemini';
|
|
7972
|
-
if (c.includes('opencode') || c.includes('open-code')) return 'opencode';
|
|
7973
|
-
if (c.includes('cursor')) return 'cursor';
|
|
7974
|
-
if (c.includes('wall-e') || c.includes('walle')) return 'walle';
|
|
7975
|
-
return null;
|
|
8089
|
+
return window.CtmAgentCommand.detectAgentType(cmd);
|
|
7976
8090
|
}
|
|
7977
8091
|
|
|
7978
8092
|
const CTM_AGENT_UI_FLAGS = __CTM_AGENT_UI_FLAGS__;
|
|
@@ -7980,14 +8094,17 @@ window.CTM_AGENT_UI_FLAGS = CTM_AGENT_UI_FLAGS;
|
|
|
7980
8094
|
const CTM_LIFECYCLE_FLAGS = __CTM_LIFECYCLE_FLAGS__;
|
|
7981
8095
|
window.CTM_LIFECYCLE_FLAGS = CTM_LIFECYCLE_FLAGS;
|
|
7982
8096
|
const CLIENT_AGENT_CAPABILITIES = {
|
|
7983
|
-
claude: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:true,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: true, inputFidelity: 'pty', controlFidelity: 'hook', steer:'queue-interrupt',rewind:false,branch:false,modelControl: false, branching: false, export: true, messages: {stream:true,durable:true,branching:false}, input:{send:true,queue:true,interrupt:true,attachments:true}, approvals:{authoritative:true,resolve:true}, work:{phase:true,tasks:true,subagents:true}, controls:{model:false,cwd:false,sandbox:false}, artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'transcript', review: true, resume: true, runningHoldMs: 15000 },
|
|
7984
|
-
'claude-desktop': { surfaces:{conv:{available:true,live:false,source:'canonical-rows'},cli:{available:false}}, structuredTranscript: true, semanticInput: false, semanticQueue: false, semanticApprovals: false, promptNavigation: 'transcript', review: true, resume: false, convert: true },
|
|
7985
|
-
codex: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:true,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: true, inputFidelity: 'pty', controlFidelity: 'runtime', steer:'queue-interrupt',rewind:false,branch:false,modelControl: false, branching: false, export: true, messages:{stream:true,durable:true,branching:false},input:{send:true,queue:true,interrupt:true,attachments:true},approvals:{authoritative:true,resolve:true},work:{phase:true,tasks:true,subagents:true},controls:{model:false,cwd:false,sandbox:false},artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'transcript', review: true, resume: true },
|
|
7986
|
-
gemini: { surfaces:{conv:{available:false},cli:{available:true,source:'pty'}}, structuredTranscript: false, semanticInput: true, semanticQueue: true, semanticApprovals: false, promptNavigation: 'terminal', review: false, resume: true },
|
|
7987
|
-
opencode: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:true,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: false, inputFidelity:'pty',controlFidelity:'runtime',steer:'queue-interrupt',rewind:false,branch:false,modelControl:false,branching:false,export:true,messages:{stream:true,durable:true,branching:false},input:{send:true,queue:true,interrupt:true,attachments:true},approvals:{authoritative:false,resolve:false},work:{phase:true,tasks:true,subagents:false},controls:{model:false,cwd:false,sandbox:false},artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'transcript', review: true, resume: true },
|
|
7988
|
-
walle: { surfaces:{web:{available:true,interactive:true,source:'walle-native'},conv:{available:true,live:true,source:'canonical-rows'},cli:{available:false,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: true, inputFidelity:'native',controlFidelity:'native',steer:true,rewind:true,branch:true,modelControl:true,branching:true,export:true,messages:{stream:true,durable:true,branching:true},input:{send:true,queue:true,interrupt:true,attachments:true},approvals:{authoritative:true,resolve:true},work:{phase:true,tasks:true,subagents:true},controls:{model:true,cwd:true,sandbox:true},artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'chat', review: true, resume: false },
|
|
7989
|
-
cursor: { surfaces:{conv:{available:false},cli:{available:true,source:'pty'}}, structuredTranscript: false, semanticInput: true, semanticQueue: true, semanticApprovals: false, promptNavigation: 'terminal', review: false, resume: true, runningHoldMs: 10000 },
|
|
7990
|
-
shell: { surfaces:{conv:{available:false},cli:{available:true,source:'pty'}}, structuredTranscript: false, semanticInput: false, semanticQueue: false, semanticApprovals: false, promptNavigation: 'terminal', review: false, resume: false },
|
|
8097
|
+
claude: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:true,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: true, inputFidelity: 'pty', controlFidelity: 'hook', steer:'queue-interrupt',rewind:false,branch:false,modelControl: false, branching: false, export: true, messages: {stream:true,durable:true,branching:false}, input:{send:true,queue:true,interrupt:true,attachments:true}, approvals:{authoritative:true,resolve:true}, work:{phase:true,tasks:true,subagents:true}, controls:{model:false,cwd:false,sandbox:false}, artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'transcript', historyReader: 'transcript', review: true, resume: true, runningHoldMs: 15000 },
|
|
8098
|
+
'claude-desktop': { surfaces:{conv:{available:true,live:false,source:'canonical-rows'},cli:{available:false}}, structuredTranscript: true, semanticInput: false, semanticQueue: false, semanticApprovals: false, promptNavigation: 'transcript', historyReader: 'none', review: true, resume: false, convert: true },
|
|
8099
|
+
codex: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:true,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: true, inputFidelity: 'pty', controlFidelity: 'runtime', steer:'queue-interrupt',rewind:false,branch:false,modelControl: false, branching: false, export: true, messages:{stream:true,durable:true,branching:false},input:{send:true,queue:true,interrupt:true,attachments:true},approvals:{authoritative:true,resolve:true},work:{phase:true,tasks:true,subagents:true},controls:{model:false,cwd:false,sandbox:false},artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'transcript', historyReader: 'transcript', review: true, resume: true },
|
|
8100
|
+
gemini: { surfaces:{conv:{available:false},cli:{available:true,source:'pty'}}, structuredTranscript: false, semanticInput: true, semanticQueue: true, semanticApprovals: false, promptNavigation: 'terminal', historyReader: 'none', review: false, resume: true },
|
|
8101
|
+
opencode: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:true,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: false, inputFidelity:'pty',controlFidelity:'runtime',steer:'queue-interrupt',rewind:false,branch:false,modelControl:false,branching:false,export:true,messages:{stream:true,durable:true,branching:false},input:{send:true,queue:true,interrupt:true,attachments:true},approvals:{authoritative:false,resolve:false},work:{phase:true,tasks:true,subagents:false},controls:{model:false,cwd:false,sandbox:false},artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'transcript', historyReader: 'none', review: true, resume: true },
|
|
8102
|
+
walle: { surfaces:{web:{available:true,interactive:true,source:'walle-native'},conv:{available:true,live:true,source:'canonical-rows'},cli:{available:false,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: true, semanticApprovals: true, inputFidelity:'native',controlFidelity:'native',steer:true,rewind:true,branch:true,modelControl:true,branching:true,export:true,messages:{stream:true,durable:true,branching:true},input:{send:true,queue:true,interrupt:true,attachments:true},approvals:{authoritative:true,resolve:true},work:{phase:true,tasks:true,subagents:true},controls:{model:true,cwd:true,sandbox:true},artifacts:{files:true,diffs:true,tests:true}, promptNavigation: 'chat', historyReader: 'none', review: true, resume: false },
|
|
8103
|
+
cursor: { surfaces:{conv:{available:false},cli:{available:true,source:'pty'}}, structuredTranscript: false, semanticInput: true, semanticQueue: true, semanticApprovals: false, promptNavigation: 'terminal', historyReader: 'none', review: false, resume: true, runningHoldMs: 10000 },
|
|
8104
|
+
shell: { surfaces:{conv:{available:false},cli:{available:true,source:'pty'}}, structuredTranscript: false, semanticInput: false, semanticQueue: false, semanticApprovals: false, promptNavigation: 'terminal', historyReader: 'none', review: false, resume: false },
|
|
8105
|
+
// WP-7 (S-44): mirrors lib/agent-capabilities.js's dsh row — conv-only (surfaces.cli.available
|
|
8106
|
+
// false ⇒ no xterm anywhere, AC-7b), ACP input/control fidelity, no attachments until WP-10.
|
|
8107
|
+
dsh: { surfaces:{conv:{available:true,live:true,source:'canonical-rows'},cli:{available:false,source:'pty'}}, structuredTranscript: true, semanticInput: true, semanticQueue: false, semanticApprovals: true, inputFidelity:'acp', controlFidelity:'acp', steer:'queue-interrupt', rewind:false, branch:false, modelControl:false, branching:false, export:true, messages:{stream:true,durable:true,branching:false}, input:{send:true,queue:false,interrupt:true,attachments:false}, approvals:{authoritative:true,resolve:true}, work:{phase:true,tasks:false,subagents:false}, controls:{model:false,cwd:false,sandbox:false}, artifacts:{files:true,diffs:false,tests:false}, historyReader: 'none', promptNavigation: 'transcript', review: false, resume: true },
|
|
7991
8108
|
};
|
|
7992
8109
|
|
|
7993
8110
|
function _clientNormalizeAgentType(value) {
|
|
@@ -7995,6 +8112,7 @@ function _clientNormalizeAgentType(value) {
|
|
|
7995
8112
|
const v = String(value).toLowerCase().replace(/_/g, '-');
|
|
7996
8113
|
if (CLIENT_AGENT_CAPABILITIES[v]) return v;
|
|
7997
8114
|
if (v === 'claude-code') return 'claude';
|
|
8115
|
+
if (v === 'deepseek-harness' || v === 'deepseek') return 'dsh';
|
|
7998
8116
|
if (v === 'claude-desktop-session' || v === 'desktop') return 'claude-desktop';
|
|
7999
8117
|
if (v === 'gemini-cli') return 'gemini';
|
|
8000
8118
|
if (v === 'open-code' || v === 'opencode-cli') return 'opencode';
|
|
@@ -10562,8 +10680,8 @@ function _liveWheelSteps(e, writer, rowHeightPx, rows, wheelRecorder) {
|
|
|
10562
10680
|
// (a frozen function-size ceiling, lint/file-size-baseline.json) only pays ONE line per call
|
|
10563
10681
|
// site for reader-enter/prefetch/frame-note -- the actual logic lives in history-app-seam.js.
|
|
10564
10682
|
function _continuousController(id) { return window.TerminalHistoryViewport && window.TerminalHistoryViewport.get(id); }
|
|
10565
|
-
function _enterContinuousForSession(id, deltaYPx) { const c = _continuousController(id); if (c && window.HistoryAppSeam) window.HistoryAppSeam.enterContinuous(c, 'reader-enter', { deltaYPx }); }
|
|
10566
|
-
function _prefetchContinuousForSession(id, reason) { const c = _continuousController(id); if (c && window.HistoryAppSeam) window.HistoryAppSeam.prefetchContinuousPage(c, { reason }); }
|
|
10683
|
+
function _enterContinuousForSession(id, deltaYPx) { const c = _continuousController(id); if (c && window.HistoryAppSeam && window.HistoryAppSeam.historyReader(state.sessions.get(id))) window.HistoryAppSeam.enterContinuous(c, 'reader-enter', { deltaYPx }); }
|
|
10684
|
+
function _prefetchContinuousForSession(id, reason) { const c = _continuousController(id); if (c && window.HistoryAppSeam && window.HistoryAppSeam.historyReader(state.sessions.get(id))) window.HistoryAppSeam.prefetchContinuousPage(c, { reason }); }
|
|
10567
10685
|
function _wheelDeltaPx(e, term) {
|
|
10568
10686
|
let ch = 0;
|
|
10569
10687
|
try { ch = term._core._renderService.dimensions.css.cell.height; } catch {}
|
|
@@ -10588,6 +10706,24 @@ function _requestTerminalScrollOverlayRefresh(id, writer, current) {
|
|
|
10588
10706
|
_updateTerminalCurrentPromptChip(id);
|
|
10589
10707
|
});
|
|
10590
10708
|
}
|
|
10709
|
+
// WP-7 (S-52/S-57, AC-7b): a session whose CLIENT capabilities have no CLI surface (dsh today) is
|
|
10710
|
+
// conv-only — it must never build an xterm, and no bootstrap may wait for a PTY attach that will
|
|
10711
|
+
// never arrive. The rule reads the capability table, never the agent name, so it generalizes: every
|
|
10712
|
+
// existing createTerminal() call site (the `created` ack, the lazy activateTab path, the sessions-list
|
|
10713
|
+
// race path) goes through this wrapper and is answered with the conv-only shape instead.
|
|
10714
|
+
const _createTerminalPtySurface = createTerminal;
|
|
10715
|
+
createTerminal = function createTerminalGated(id, opts) {
|
|
10716
|
+
// The `created` ack calls this BEFORE it writes the session meta, so the frame's own agent identity
|
|
10717
|
+
// (opts) decides first; every later call site already has the meta.
|
|
10718
|
+
const optCaps = (opts && opts.agentCapabilities) || (opts && opts.agentType && typeof _clientAgentCaps === 'function' ? _clientAgentCaps(opts.agentType) : null);
|
|
10719
|
+
const optsConvOnly = !!(optCaps && optCaps.surfaces && optCaps.surfaces.cli && optCaps.surfaces.cli.available === false);
|
|
10720
|
+
if ((optsConvOnly || (typeof _sessionCliUnavailable === 'function' && _sessionCliUnavailable(id)))
|
|
10721
|
+
&& typeof _ensureConvOnlySessionClientShape === 'function') {
|
|
10722
|
+
const shaped = _ensureConvOnlySessionClientShape(id, { ...((state.sessions.get(id) || {}).meta || {}), ...(opts && opts.agentType ? { agentType: opts.agentType } : {}), ...(optCaps ? { agentCapabilities: optCaps } : {}) });
|
|
10723
|
+
return shaped || { term: null, fitAddon: null };
|
|
10724
|
+
}
|
|
10725
|
+
return _createTerminalPtySurface(id, opts);
|
|
10726
|
+
};
|
|
10591
10727
|
function createTerminal(id, opts) {
|
|
10592
10728
|
opts = opts || {};
|
|
10593
10729
|
const candidateRecovery = opts.candidateRecovery === true;
|
|
@@ -11140,7 +11276,9 @@ function createTerminal(id, opts) {
|
|
|
11140
11276
|
}
|
|
11141
11277
|
if (_terminalShouldHandleCtrlVPasteShortcut(e) && _terminalImagePasteEnabledForSession(id)) {
|
|
11142
11278
|
e.preventDefault();
|
|
11143
|
-
_terminalHandleShortcutPaste(
|
|
11279
|
+
_terminalHandleShortcutPaste(_terminalIsMacPlatform()
|
|
11280
|
+
? { mode: 'token' }
|
|
11281
|
+
: { mode: 'token', controlVPassthrough: true });
|
|
11144
11282
|
return false;
|
|
11145
11283
|
}
|
|
11146
11284
|
return true;
|
|
@@ -12085,7 +12223,7 @@ function createTerminal(id, opts) {
|
|
|
12085
12223
|
async function _terminalRemoveDraftAttachment(id, attachment, metricEvent, options = {}) {
|
|
12086
12224
|
const Draft = window.TerminalAttachmentDraft;
|
|
12087
12225
|
const draft = _terminalAttachmentDraftState(id);
|
|
12088
|
-
if (!Draft || !draft || !attachment || !draft.attachments.includes(attachment)) return false;
|
|
12226
|
+
if (!Draft || !draft || !attachment || !draft.attachments.includes(attachment) || !Draft.canRemoveAttachment(draft, attachment.clientId)) return false;
|
|
12089
12227
|
const owned = _terminalDraftUploadOwnership(id);
|
|
12090
12228
|
const current = () => Boolean(owned.lifetime && owned.lifetime === _terminalRootDraftLifetime
|
|
12091
12229
|
&& !owned.reason && owned.context && owned.context.draft === draft
|
|
@@ -12115,7 +12253,7 @@ function createTerminal(id, opts) {
|
|
|
12115
12253
|
const draft = _terminalAttachmentDraftState(id);
|
|
12116
12254
|
const action = Draft && Draft.dismissDisposition(draft, attachment);
|
|
12117
12255
|
if (action === 'disabled' || !action) return false;
|
|
12118
|
-
if (action === 'cancel') return _terminalCancelAttachmentUpload(id, attachment);
|
|
12256
|
+
if (action === 'cancel' || action === 'dismiss-submitted') return action === 'cancel' ? _terminalCancelAttachmentUpload(id, attachment) : (Draft.dismissSubmitted(draft) && (_terminalAttachmentMetric(id, 'indeterminate_dismissed_by_user', { attachmentCount: draft.attachments.length }), _terminalRenderAttachmentDraft(id), true)); // R-3b: post-Enter removal is LOCAL only -- no server DELETE, no re-send
|
|
12119
12257
|
const metric = action === 'detach' ? 'attached_detached_by_user' : 'chip_dismissed_by_user';
|
|
12120
12258
|
const owned = _terminalDraftUploadOwnership(id);
|
|
12121
12259
|
const removed = _terminalRemoveDraftAttachment(id, attachment, metric);
|
|
@@ -12335,7 +12473,7 @@ function createTerminal(id, opts) {
|
|
|
12335
12473
|
if (membershipSnapshot.known) {
|
|
12336
12474
|
const tokenAttachments = draft.attachments.filter(item => item && item.mode !== 'path');
|
|
12337
12475
|
const derived = Orchestration.deriveObservedMembership({
|
|
12338
|
-
attachments: tokenAttachments,
|
|
12476
|
+
attachments: tokenAttachments, provider: _clientAgentTypeForSession(s),
|
|
12339
12477
|
observedTokens: membershipSnapshot.tokens,
|
|
12340
12478
|
scan: {
|
|
12341
12479
|
known: membershipSnapshot.known === true,
|
|
@@ -13145,7 +13283,7 @@ function createTerminal(id, opts) {
|
|
|
13145
13283
|
: 'confirmed',
|
|
13146
13284
|
}));
|
|
13147
13285
|
if (!list.length) return { ok: true, links: [], skipped: [] };
|
|
13148
|
-
const response = await fetch('/api/
|
|
13286
|
+
const response = await fetch('/api/sessions/' + encodeURIComponent(id) + '/image-refs?token=' + encodeURIComponent(state.token || ''), {
|
|
13149
13287
|
method: 'POST',
|
|
13150
13288
|
headers: { 'Content-Type': 'application/json' },
|
|
13151
13289
|
keepalive: true,
|
|
@@ -13433,10 +13571,12 @@ function createTerminal(id, opts) {
|
|
|
13433
13571
|
return true;
|
|
13434
13572
|
} catch (error) {
|
|
13435
13573
|
if (!current()) return false;
|
|
13436
|
-
Draft.
|
|
13574
|
+
const outcome = Draft.failSubmission(draft, error && error.message ? error.message : error);
|
|
13437
13575
|
_terminalRenderAttachmentDraft(id);
|
|
13438
13576
|
if (typeof toast === 'function') {
|
|
13439
|
-
toast(
|
|
13577
|
+
toast(outcome === 'indeterminate'
|
|
13578
|
+
? 'Image submission is still interrupted. Retry when the connection recovers.'
|
|
13579
|
+
: 'Image send failed: ' + (error && error.message ? error.message : error), {
|
|
13440
13580
|
type: 'error', duration: 4000,
|
|
13441
13581
|
});
|
|
13442
13582
|
}
|
|
@@ -13641,14 +13781,12 @@ function createTerminal(id, opts) {
|
|
|
13641
13781
|
// second time: if the deadline branch (or a supersession) already cleared/replaced
|
|
13642
13782
|
// the submission, this is a no-op.
|
|
13643
13783
|
if (!ownerCurrent() || !draft.submission || draft.submission.id !== started.submission.id) return false;
|
|
13644
|
-
const
|
|
13645
|
-
|
|
13646
|
-
else Draft.abortSubmission(draft, err && err.message ? err.message : err);
|
|
13647
|
-
_terminalSettleAttachmentDeferredInput(id, 'submit-catch', frozen > 0 ? 'indeterminate' : 'aborted'); // F-D: before the render, which settles too
|
|
13784
|
+
const outcome = Draft.failSubmission(draft, err && err.message ? err.message : err);
|
|
13785
|
+
_terminalSettleAttachmentDeferredInput(id, 'submit-catch', outcome === 'indeterminate' ? 'indeterminate' : 'aborted'); // F-D: before the render, which settles too
|
|
13648
13786
|
_terminalRenderAttachmentDraft(id);
|
|
13649
|
-
_terminalAttachmentMetric(id,
|
|
13787
|
+
_terminalAttachmentMetric(id, outcome === 'indeterminate' ? 'freeze_indeterminate' : 'submission_blocked', { attachmentCount: attachments.length });
|
|
13650
13788
|
if (typeof toast === 'function') {
|
|
13651
|
-
toast(
|
|
13789
|
+
toast(outcome === 'indeterminate'
|
|
13652
13790
|
? 'Image send state is unknown. Review the provider prompt before retrying.'
|
|
13653
13791
|
: 'Image attachment failed: ' + (err && err.message ? err.message : err), {
|
|
13654
13792
|
type: 'error',
|
|
@@ -13992,9 +14130,16 @@ function createTerminal(id, opts) {
|
|
|
13992
14130
|
if (!e || e.type !== 'keydown') return false;
|
|
13993
14131
|
if (!e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return false;
|
|
13994
14132
|
if (String(e.key || '').toLowerCase() !== 'v') return false;
|
|
13995
|
-
//
|
|
13996
|
-
//
|
|
13997
|
-
//
|
|
14133
|
+
// xterm.js 6 handles Ctrl+V keydown itself off macOS: it cancels the keydown and sends
|
|
14134
|
+
// \x16 to the PTY, so the browser never fires a `paste` event with clipboardData
|
|
14135
|
+
// (xterm.js #2478). Claim the keydown on every platform and read the clipboard here;
|
|
14136
|
+
// off macOS a non-image clipboard forwards exactly \x16 (controlVPassthrough, see
|
|
14137
|
+
// _terminalHandleShortcutPaste). Design: docs/nightly-linux-host-portability-2026-10-27.html
|
|
14138
|
+
// section 9.4 WP-14 (decision D-13 option d).
|
|
14139
|
+
return true;
|
|
14140
|
+
}
|
|
14141
|
+
|
|
14142
|
+
function _terminalIsMacPlatform() {
|
|
13998
14143
|
return /Mac|iPhone|iPad|iPod/i.test(String(navigator.platform || ''));
|
|
13999
14144
|
}
|
|
14000
14145
|
|
|
@@ -14022,6 +14167,12 @@ function createTerminal(id, opts) {
|
|
|
14022
14167
|
await _terminalInsertPastedImageFiles(files, opts);
|
|
14023
14168
|
return;
|
|
14024
14169
|
}
|
|
14170
|
+
if (opts.controlVPassthrough) {
|
|
14171
|
+
// Off macOS, Ctrl+V with no image keeps its terminal meaning (^V for Claude Code's own
|
|
14172
|
+
// image paste, vim, shells). Text paste is Ctrl+Shift+V / the native paste event.
|
|
14173
|
+
_terminalForwardObservedInput(id, '\x16', { resolveWaiting: false });
|
|
14174
|
+
return;
|
|
14175
|
+
}
|
|
14025
14176
|
if (navigator.clipboard && typeof navigator.clipboard.readText === 'function') {
|
|
14026
14177
|
const text = await navigator.clipboard.readText();
|
|
14027
14178
|
if (text) {
|
|
@@ -14358,7 +14509,7 @@ function createTerminal(id, opts) {
|
|
|
14358
14509
|
exited: current._exited,
|
|
14359
14510
|
agentKind,
|
|
14360
14511
|
hasScrollback,
|
|
14361
|
-
readerOpen: !!(window.TerminalHistoryViewport && window.TerminalHistoryViewport.isActive(id)), // WP-0 §5 D-3
|
|
14512
|
+
readerOpen: !!(window.TerminalHistoryViewport && window.TerminalHistoryViewport.isActive(id)), historyReader: !!(window.HistoryAppSeam && window.HistoryAppSeam.historyReader(current)), // WP-0 §5 D-3; WP-1 D-1/D-3: reader only where a source exists
|
|
14362
14513
|
}),
|
|
14363
14514
|
{
|
|
14364
14515
|
terminalMouseState: window.StateSyncClient?.terminalMouseState,
|
|
@@ -14454,7 +14605,7 @@ function createTerminal(id, opts) {
|
|
|
14454
14605
|
wheelRecorder.record(wheelDecisionForDiag.route, {
|
|
14455
14606
|
bufferType: buf.type, baseY: buf.baseY, deltaY: e.deltaY, action: wheelDecisionForDiag.action,
|
|
14456
14607
|
appMouse: wheelDecisionForDiag.mouseActive, isAlt: wheelDecisionForDiag.isAlt,
|
|
14457
|
-
mouseSource: wheelDecisionForDiag.mouseState && wheelDecisionForDiag.mouseState.source, frameTracking: wheelDecisionForDiag.frameTracking,
|
|
14608
|
+
mouseSource: wheelDecisionForDiag.mouseState && wheelDecisionForDiag.mouseState.source, frameTracking: wheelDecisionForDiag.frameTracking, historyReader: wheelDecisionForDiag.historyReader,
|
|
14458
14609
|
});
|
|
14459
14610
|
}
|
|
14460
14611
|
}
|
|
@@ -17744,9 +17895,8 @@ function syncNavPills(activeId) {
|
|
|
17744
17895
|
const navPills = document.querySelectorAll('#topbar-nav .nav-pill');
|
|
17745
17896
|
const specialPanels = ['rules', 'insights', 'permissions', 'prompts', 'codereview', 'walle', 'models', 'backups', 'worktrees', 'machines', 'setup'];
|
|
17746
17897
|
const navId = specialPanels.includes(activeId) ? activeId : 'sessions';
|
|
17747
|
-
navPills.forEach(pill =>
|
|
17748
|
-
|
|
17749
|
-
});
|
|
17898
|
+
navPills.forEach(pill => pill.classList.toggle('active', pill.dataset.nav === navId));
|
|
17899
|
+
document.getElementById('nav-more-button')?.classList.toggle('nav-more-has-active', !!document.querySelector('#nav-more-dropdown .nav-pill.active'));
|
|
17750
17900
|
}
|
|
17751
17901
|
|
|
17752
17902
|
function updateTopbarContext(activeId) {
|
|
@@ -18217,7 +18367,7 @@ async function standupPerformDeepSearch(query) {
|
|
|
18217
18367
|
standupRerender();
|
|
18218
18368
|
const liveQuery = () => String(state.standup.searchQuery || '').trim();
|
|
18219
18369
|
try {
|
|
18220
|
-
const url = `/api/sessions/standup/search?q=${encodeURIComponent(query)}&token=${state.token}`;
|
|
18370
|
+
const url = `/api/sessions/standup/search?q=${encodeURIComponent(query)}&token=${state.token}&scope=fleet`;
|
|
18221
18371
|
const resp = await fetch(url, { cache: 'no-store', signal: controller.signal });
|
|
18222
18372
|
// Stale-response drop: the user may have kept typing while this was in flight — compare
|
|
18223
18373
|
// against the LIVE input, not the query we sent (same guard the sidebar's
|
|
@@ -18790,7 +18940,7 @@ function renderStandupHistoryCard(result) {
|
|
|
18790
18940
|
<div class="standup-card-top">
|
|
18791
18941
|
<div style="min-width:0;">
|
|
18792
18942
|
<div class="standup-card-title">${standupEsc(title)}</div>
|
|
18793
|
-
<div class="standup-card-subtitle">${standupEsc(meta || result.sessionId || '')}</div>
|
|
18943
|
+
<div class="standup-card-subtitle">${typeof machineChipHtml === 'function' ? machineChipHtml(result) : ''}${standupEsc(meta || result.sessionId || '')}</div>
|
|
18794
18944
|
</div>
|
|
18795
18945
|
${ago ? `<span class="standup-badge">${standupEsc(ago)}</span>` : ''}
|
|
18796
18946
|
</div>
|
|
@@ -19094,6 +19244,7 @@ function showWallePanel() {
|
|
|
19094
19244
|
const _pendingResumeProjections = new Map();
|
|
19095
19245
|
|
|
19096
19246
|
function onCreated(msg) {
|
|
19247
|
+
if (typeof PendingCreate !== 'undefined') PendingCreate.settleCreated(msg);
|
|
19097
19248
|
// A `created` carrying a restore/deferred-start source is a BACKGROUND
|
|
19098
19249
|
// announcement (e.g. the server auto-resuming a deferred Wall-E session and
|
|
19099
19250
|
// broadcasting `created` to every connected client), not a session this user
|
|
@@ -19163,13 +19314,18 @@ function onCreated(msg) {
|
|
|
19163
19314
|
if (priorSession) _clearTerminalBootstrapAttempt(priorSession, id, 'created-replacement');
|
|
19164
19315
|
// wp1-fix1-brief.md Failure B: a sessions-list race can pre-build this session's surface (createTerminal via _ensureTerminalSessionClientShape, runtime-20.js) before this `created` ack; createTerminal only removes a PRIOR container when it has no `.term`, so a fully-built one was left mounted while this call appended a second `#term-<id>` -- two `.resume-chip`s, strict-mode violation. Retire it with the same full teardown first.
|
|
19165
19316
|
if (priorSession?.term && priorSession.container) _disposeSessionClientSurface(priorSession);
|
|
19166
|
-
const { term, fitAddon } = createTerminal(id, { active: true });
|
|
19317
|
+
const { term, fitAddon } = createTerminal(id, { active: true, agentType: msg.agentType || _clientDetectAgentType(msg.cmd || ''), agentCapabilities: msg.agentCapabilities || null });
|
|
19167
19318
|
|
|
19168
19319
|
// Set initial meta so the session appears in the active sessions list immediately
|
|
19169
19320
|
const s = state.sessions.get(id);
|
|
19170
19321
|
if (s) {
|
|
19171
19322
|
s._exited = false;
|
|
19172
19323
|
s.needsAttach = true;
|
|
19324
|
+
// AC-7b (WP-7): a conv-only session (client caps have no CLI surface) never waits for a PTY
|
|
19325
|
+
// attach — the server sends no terminal frames for it, so the attach bookkeeping above is
|
|
19326
|
+
// cleared immediately. The literal assignment stays so the source-text wiring assertion in
|
|
19327
|
+
// tests/terminal-bootstrap-client-wiring.test.js:2442 still describes this handler.
|
|
19328
|
+
if (s._convOnly === true) s.needsAttach = false;
|
|
19173
19329
|
s._terminalBootstrapPending = false;
|
|
19174
19330
|
s._terminalBootstrapRequestSocketGeneration = 0;
|
|
19175
19331
|
s._terminalBootstrapRequestProcessGeneration = 0;
|
|
@@ -20816,7 +20972,7 @@ function _stateSyncFrameDeps(s, msg) {
|
|
|
20816
20972
|
writeFrame: (out, complete) => (pane ? pane.writeFrame(out, complete) : _writeStateSyncFrame(s, out, complete)),
|
|
20817
20973
|
onStage: (stage, seq) => _noteStateSyncFrameStage(s, msg, stage, seq),
|
|
20818
20974
|
sendAck: (seq) => send(_stateSyncFrameAckPayload(s, msg, seq)),
|
|
20819
|
-
requestKeyframe: (reason) => _requestStateSyncKeyframe(msg.id, { reason }),
|
|
20975
|
+
requestKeyframe: (reason, opts) => _requestStateSyncKeyframe(msg.id, { reason, ...(opts || {}) }),
|
|
20820
20976
|
cellCodec: window.StateSyncCellCodec,
|
|
20821
20977
|
send,
|
|
20822
20978
|
onRendered: pane ? pane.onRendered : _stateSyncFrameOnRendered(s, msg, contentFrameV2),
|
|
@@ -23716,6 +23872,10 @@ function onSessionRestoreDeferred(msg) {
|
|
|
23716
23872
|
if (!s) return;
|
|
23717
23873
|
_clearExhaustedResumeBlock(s);
|
|
23718
23874
|
const starting = !!msg.starting;
|
|
23875
|
+
// WP-2 (live-defer-wake-adopted-host-death-2026-10-28.html R-4): a NON-starting deferred frame means
|
|
23876
|
+
// this session's process is gone and nothing is being resumed -- retire the route (no preserveRoute)
|
|
23877
|
+
// so the next activation of this tab sends a fresh terminal-bootstrap and the server can wake it.
|
|
23878
|
+
if (!starting) _clearTerminalBootstrapAttempt(s, id, 'restore-deferred', { keepCold: true });
|
|
23719
23879
|
if (!starting && /parked/i.test(String(msg.message || ''))) {
|
|
23720
23880
|
_applySessionLifecycleState(s, 'parked');
|
|
23721
23881
|
if (s.meta) s.meta.parkReason = String(msg.reason || '');
|
|
@@ -24428,7 +24588,16 @@ async function onSessionsList(msg, options = {}) {
|
|
|
24428
24588
|
if (s) s.needsAttach = true;
|
|
24429
24589
|
}
|
|
24430
24590
|
if (!state.tabOrder.includes(sess.id)) {
|
|
24431
|
-
|
|
24591
|
+
// WP-5 (AC-5b/5c): a tab the restore deferred because the hub had not delivered its session
|
|
24592
|
+
// yet (a BOX session before the catalog lands) comes back at its SAVED place — right after the
|
|
24593
|
+
// nearest saved predecessor present now — instead of appending at the end of the strip.
|
|
24594
|
+
if (state._undeliveredTabIds && state._undeliveredTabIds.has(sess.id)) {
|
|
24595
|
+
state.tabOrder.splice(
|
|
24596
|
+
_undeliveredTabInsertIndex(sess.id, state._undeliveredTabSaved || [], state.tabOrder), 0, sess.id);
|
|
24597
|
+
state._undeliveredTabIds.delete(sess.id);
|
|
24598
|
+
} else {
|
|
24599
|
+
state.tabOrder.push(sess.id);
|
|
24600
|
+
}
|
|
24432
24601
|
}
|
|
24433
24602
|
// Store metadata (preserve type for Wall-E sessions)
|
|
24434
24603
|
const existing = state.sessions.get(sess.id);
|
|
@@ -24703,12 +24872,22 @@ async function onSessionsList(msg, options = {}) {
|
|
|
24703
24872
|
_maybeRearmTerminalBootstrapFromInventory(existing, sess, { restoring });
|
|
24704
24873
|
}
|
|
24705
24874
|
}
|
|
24875
|
+
// WP-1 (D-7/§6.4): the stubs are in place — adopt a pending New Session that showed up as a row.
|
|
24876
|
+
if (typeof PendingCreate !== 'undefined') PendingCreate.adoptFromList(msg.sessions || []);
|
|
24706
24877
|
// D-7: a FULL list that omits the id is the server's own evidence that the close landed — only
|
|
24707
24878
|
// then is the pending entry dropped (no timer). A restoring list is partial by design, so it is no evidence.
|
|
24708
24879
|
if (!restoring) for (const pendingId of Array.from(state.pendingCloseIds.keys())) {
|
|
24709
24880
|
if (!serverIds.has(pendingId)) state.pendingCloseIds.delete(pendingId);
|
|
24710
24881
|
}
|
|
24711
24882
|
|
|
24883
|
+
// WP-5 (AC-5d): SETTLE the undelivered set. A non-restoring list from a hub with every machine's
|
|
24884
|
+
// catalog delivered (`machinesPending` absent or 0) classifies every id: present ⇒ it is in
|
|
24885
|
+
// state.sessions (the arrival above already placed it); absent ⇒ the hub is done telling us, so the
|
|
24886
|
+
// id is dropped at the next save instead of being rewritten forever. Settling itself saves nothing.
|
|
24887
|
+
if (!restoring && !(msg.machinesPending > 0) && state._undeliveredTabIds && state._undeliveredTabIds.size) {
|
|
24888
|
+
state._undeliveredTabIds.clear();
|
|
24889
|
+
}
|
|
24890
|
+
|
|
24712
24891
|
if (!options.skipStreamStatusRefresh && !restoring && typeof refreshStreamStatuses === 'function') {
|
|
24713
24892
|
await refreshStreamStatuses({ force: true, render: false, timeoutMs: 3000,
|
|
24714
24893
|
lease: options.lease, socket: options.socket, isCurrent: ownsRoot });
|
|
@@ -25324,7 +25503,7 @@ function populateModelSwitcher(sessionId, opts) {
|
|
|
25324
25503
|
leadHost.appendChild(tagEl);
|
|
25325
25504
|
});
|
|
25326
25505
|
if (chipView.showTokens) _renderModelTokenBadge(leadHost, s.meta?.tokens);
|
|
25327
|
-
chip.title = chipView.title;
|
|
25506
|
+
chip.title = chipView.title; if (window.CTMCodingModelPresentation) window.CTMCodingModelPresentation.syncNativeSubagentChip(chip, s, _clientAgentTypeForSession(s)); // WP-3 D-6: passive 'native sub-agents off' chip
|
|
25328
25507
|
|
|
25329
25508
|
// The request decoration + delegate segment (never the lead label: D-3), then the registry names.
|
|
25330
25509
|
if (window.CtmSessionModelPicker) window.CtmSessionModelPicker.syncChip(sessionId, chip);
|
|
@@ -25384,6 +25563,9 @@ let currentActiveSort = 'by_activity';
|
|
|
25384
25563
|
let activeListExpanded = false;
|
|
25385
25564
|
let activeListVisibleLimit = 6;
|
|
25386
25565
|
let activeProjectCollapsedKeys = new Set();
|
|
25566
|
+
let currentActiveComputerFilter = null; // null = all computers, '' = Local, else an instanceId (pref active_computer_filter)
|
|
25567
|
+
let activeComputerMenuSig = null;
|
|
25568
|
+
let activeComputerSeenLabels = new Map();
|
|
25387
25569
|
const ACTIVE_LIST_VISIBLE_LIMIT = 6;
|
|
25388
25570
|
const ACTIVE_LIST_MIN_VISIBLE = 3;
|
|
25389
25571
|
const ACTIVE_LIST_ROW_STEP_PX = 44;
|
|
@@ -25790,6 +25972,35 @@ function toggleActiveProjectGroup(key, event) {
|
|
|
25790
25972
|
renderSessionList(true);
|
|
25791
25973
|
}
|
|
25792
25974
|
|
|
25975
|
+
function setActiveComputerFilter(v) {
|
|
25976
|
+
currentActiveComputerFilter = typeof v === 'string' ? v : null;
|
|
25977
|
+
savePref('active_computer_filter', currentActiveComputerFilter);
|
|
25978
|
+
if (recentSourceChanged(currentActiveComputerFilter)) loadRecentSessions().catch(() => {});
|
|
25979
|
+
renderSessionList(true);
|
|
25980
|
+
refreshRecentSearchAfterFilterChange();
|
|
25981
|
+
}
|
|
25982
|
+
|
|
25983
|
+
// G-3: the removable chip in both list headers (empty when the filter is null).
|
|
25984
|
+
function syncActiveComputerFilterChips(label) {
|
|
25985
|
+
const html = currentActiveComputerFilter === null ? '' : activeComputerFilterChipHtml({ label, escHtml });
|
|
25986
|
+
for (const id of ['active-computer-filter-chip', 'recent-computer-filter-chip']) {
|
|
25987
|
+
const el = document.getElementById(id);
|
|
25988
|
+
if (el && el.innerHTML !== html) el.innerHTML = html;
|
|
25989
|
+
}
|
|
25990
|
+
}
|
|
25991
|
+
|
|
25992
|
+
// F-5: rewrite the Computers optgroup only when its signature changed (an open native menu survives re-renders).
|
|
25993
|
+
function syncActiveComputerMenu() {
|
|
25994
|
+
const sel = document.getElementById('active-sort');
|
|
25995
|
+
if (!sel) return;
|
|
25996
|
+
const f = currentActiveComputerFilter;
|
|
25997
|
+
const m = activeComputerMenuModel(Array.from(state.sessions.values()).filter(s => s && s.meta), f, activeComputerSeenLabels);
|
|
25998
|
+
for (const [k, l] of m.labels) activeComputerSeenLabels.set(k, l);
|
|
25999
|
+
sel.classList.toggle('computer-filtered', f !== null);
|
|
26000
|
+
sel.title = f === null ? 'Sort active sessions' : 'Sort active sessions \u00b7 showing ' + m.labels.get(f) + ' only';
|
|
26001
|
+
activeComputerMenuSig = applyActiveComputerOptgroup(sel, m, activeComputerMenuSig);
|
|
26002
|
+
syncActiveComputerFilterChips(f === null ? '' : (m.labels.get(f) || (f === '' ? 'Local' : f)));
|
|
26003
|
+
}
|
|
25793
26004
|
|
|
25794
26005
|
function activeListSessionCount() {
|
|
25795
26006
|
return Array.from(state.sessions.values()).filter(s => s && s.meta).length;
|
|
@@ -25909,18 +26120,68 @@ function workspaceTrackingAttentionStatus(tracking) {
|
|
|
25909
26120
|
};
|
|
25910
26121
|
}
|
|
25911
26122
|
|
|
26123
|
+
// WP-3b (docs/local-landing-queue-multi-session-integration-2026-10-26.html §8 WP-3): the landing
|
|
26124
|
+
// queue's chip TEXT. This pair of formatters (here and public/m/app.js) must produce byte-identical
|
|
26125
|
+
// strings for the same input — that is the phone parity rule, so they change together. Nothing here
|
|
26126
|
+
// is HTML: the callers escape, and the parts carry only numbers and closed-set state words.
|
|
26127
|
+
function landingEtaText(etaSeconds) {
|
|
26128
|
+
return typeof etaSeconds === 'number' && Number.isFinite(etaSeconds)
|
|
26129
|
+
? `${Math.round(etaSeconds / 60)}m` : '';
|
|
26130
|
+
}
|
|
26131
|
+
function landingQueueText(landing) {
|
|
26132
|
+
if (!landing || typeof landing !== 'object') return '';
|
|
26133
|
+
if (typeof landing.position === 'number' && Number.isFinite(landing.position)) {
|
|
26134
|
+
const total = Number.isFinite(Number(landing.total)) ? Number(landing.total) : 0;
|
|
26135
|
+
const eta = landingEtaText(landing.eta_s);
|
|
26136
|
+
return `⇡#${landing.position}/${total}${eta ? ` · ${eta}` : ''}`;
|
|
26137
|
+
}
|
|
26138
|
+
const state = typeof landing.state === 'string' ? landing.state.trim() : '';
|
|
26139
|
+
return state ? `⇡${state}` : '';
|
|
26140
|
+
}
|
|
26141
|
+
// The conflict radar is deliberately NOT on the session row: a pairwise count ("⚠L3 ×14") on every
|
|
26142
|
+
// idle row was mostly ledger-file noise nobody could act on. It stays in the details panel's Radar
|
|
26143
|
+
// row (session-workspace-tracking.js) and the walle_landing_conflicts MCP tool.
|
|
26144
|
+
function landingChipText(landing) {
|
|
26145
|
+
return landingQueueText(landing);
|
|
26146
|
+
}
|
|
26147
|
+
function landingBadgeParts(landing) {
|
|
26148
|
+
const parts = [];
|
|
26149
|
+
const titles = [];
|
|
26150
|
+
const queueText = landingQueueText(landing);
|
|
26151
|
+
if (queueText) {
|
|
26152
|
+
parts.push(`<span class="wt-part wt-landing" aria-hidden="true">${escHtml(queueText)}</span>`);
|
|
26153
|
+
const state = typeof landing.state === 'string' && landing.state.trim() ? ` ${landing.state.trim()}` : '';
|
|
26154
|
+
const eta = landingEtaText(landing.eta_s);
|
|
26155
|
+
titles.push(`landing queue${state}${eta ? `, ETA ${eta}` : ''}`);
|
|
26156
|
+
}
|
|
26157
|
+
return { parts, titles };
|
|
26158
|
+
}
|
|
26159
|
+
// The badge for a session whose ONLY news is the landing queue — same element, same class, no
|
|
26160
|
+
// attention parts. Returns '' when there is nothing to show, which is what keeps a session with
|
|
26161
|
+
// neither attention nor landing byte-identical to before.
|
|
26162
|
+
function landingBadgeHtml(s, parts, titles) {
|
|
26163
|
+
if (!parts.length) return '';
|
|
26164
|
+
const label = [activeSessionBranchName(s), ...titles].filter(Boolean).join(': ');
|
|
26165
|
+
return `<span class="worktree-attn-badge" title="${escHtml(label)}" aria-label="${escHtml(label)}">${parts.join('')}</span>`;
|
|
26166
|
+
}
|
|
26167
|
+
|
|
25912
26168
|
function worktreeAttentionBadge(s) {
|
|
25913
26169
|
const tracking = typeof SessionWorkspaceTracking !== 'undefined' ? SessionWorkspaceTracking.trackingForSession(s) : null;
|
|
26170
|
+
// WP-3b: the landing parts are computed BEFORE the attention guard, because a session on a clean
|
|
26171
|
+
// branch can still be #1 in the queue and must show its position (design §8 WP-3, the guard gotcha).
|
|
26172
|
+
const landing = tracking && tracking.landing && typeof tracking.landing === 'object'
|
|
26173
|
+
? landingBadgeParts(tracking.landing)
|
|
26174
|
+
: { parts: [], titles: [] };
|
|
25914
26175
|
const wt = tracking ? workspaceTrackingAttentionStatus(tracking) : s?.meta?.worktreeStatus;
|
|
25915
|
-
if (!wt || !wt.needsAttention) return
|
|
26176
|
+
if (!wt || !wt.needsAttention) return landingBadgeHtml(s, landing.parts, landing.titles);
|
|
25916
26177
|
const branch = wt.branch || activeSessionBranchName(s);
|
|
25917
|
-
if (!branch || branch === 'main' || branch === 'master') return
|
|
26178
|
+
if (!branch || branch === 'main' || branch === 'master') return landingBadgeHtml(s, landing.parts, landing.titles);
|
|
25918
26179
|
const dirtyFiles = Number(wt.dirtyFiles || 0);
|
|
25919
26180
|
const trackedDirtyFiles = wt.trackedDirtyFiles != null
|
|
25920
26181
|
? Number(wt.trackedDirtyFiles || 0)
|
|
25921
26182
|
: Math.max(0, dirtyFiles - Number(wt.untrackedFiles || 0));
|
|
25922
26183
|
const unmergedCommits = Number(wt.unmergedCommits || 0);
|
|
25923
|
-
if (trackedDirtyFiles <= 0 && unmergedCommits <= 0) return
|
|
26184
|
+
if (trackedDirtyFiles <= 0 && unmergedCommits <= 0) return landingBadgeHtml(s, landing.parts, landing.titles);
|
|
25924
26185
|
|
|
25925
26186
|
const parts = [];
|
|
25926
26187
|
const titleParts = [];
|
|
@@ -25944,8 +26205,8 @@ function worktreeAttentionBadge(s) {
|
|
|
25944
26205
|
parts.push('<span class="wt-part" aria-hidden="true">◆</span>');
|
|
25945
26206
|
titleParts.push('merge content differs relative to configured base');
|
|
25946
26207
|
}
|
|
25947
|
-
const title = `${branch}: ${titleParts.join(', ')}`;
|
|
25948
|
-
return `<span class="worktree-attn-badge" title="${escHtml(title)}" aria-label="${escHtml(title)}">${parts.join('')}</span>`;
|
|
26208
|
+
const title = `${branch}: ${titleParts.concat(landing.titles).join(', ')}`;
|
|
26209
|
+
return `<span class="worktree-attn-badge" title="${escHtml(title)}" aria-label="${escHtml(title)}">${parts.join('')}${landing.parts.join('')}</span>`;
|
|
25949
26210
|
}
|
|
25950
26211
|
|
|
25951
26212
|
|
|
@@ -26076,11 +26337,9 @@ function titleMatchesWorktreeLeaf(title, cwd) {
|
|
|
26076
26337
|
function activeSessionFallbackLabel(s, id) {
|
|
26077
26338
|
const meta = s?.meta || {};
|
|
26078
26339
|
if (meta.type === 'walle') return 'Wall-E session';
|
|
26079
|
-
|
|
26080
|
-
|
|
26081
|
-
if (
|
|
26082
|
-
if (cmd.includes('opencode') || cmd.includes('open-code')) return 'OpenCode session';
|
|
26083
|
-
if (cmd.includes('claude')) return 'Claude Code session';
|
|
26340
|
+
// S-11 (opencode-followups D-5): the ONE command-word detector, shared with the server.
|
|
26341
|
+
const label = { dsh: 'DeepSeek Harness session', codex: 'Codex session', gemini: 'Gemini session', opencode: 'OpenCode session', claude: 'Claude Code session' }[window.CtmAgentCommand.detectAgentType(meta.cmd || '')];
|
|
26342
|
+
if (label) return label;
|
|
26084
26343
|
return id ? `Session ${String(id).slice(0, 8)}` : 'Session';
|
|
26085
26344
|
}
|
|
26086
26345
|
|
|
@@ -26732,7 +26991,15 @@ function closeWorkerRow(id) {
|
|
|
26732
26991
|
async function retireWorkers(body) {
|
|
26733
26992
|
try {
|
|
26734
26993
|
const res = await fetch('/api/sessions/workers/retire', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + state.token }, body: JSON.stringify(body) });
|
|
26735
|
-
|
|
26994
|
+
// WP-1b (§8): a box-owned lead's clear is forwarded by the hub to the box that owns the session, so
|
|
26995
|
+
// a machine_unreachable refusal names that box; any other refusal (and a body that is absent or not
|
|
26996
|
+
// JSON) keeps today's bare-status toast.
|
|
26997
|
+
if (!res.ok) {
|
|
26998
|
+
let refusal = null;
|
|
26999
|
+
try { refusal = await res.json(); } catch { refusal = null; }
|
|
27000
|
+
showToast(refusal?.code === 'machine_unreachable' ? `${refusal.machineName || 'The machine that owns this session'} is unreachable. Retry when ready.` : `Could not clear workers (${res.status})`);
|
|
27001
|
+
return null;
|
|
27002
|
+
}
|
|
26736
27003
|
return await res.json();
|
|
26737
27004
|
} catch (err) { showToast(`Could not clear workers: ${err && err.message}`); return null; }
|
|
26738
27005
|
}
|
|
@@ -26751,7 +27018,16 @@ function clearFinishedWorkers(parentId) {
|
|
|
26751
27018
|
// items are not CTM sessions, so they cannot ride retireWorkers' worker list; the sidecar
|
|
26752
27019
|
// endpoint drops them from the lead's meta.native_subagents. Fire-and-forget: the next
|
|
26753
27020
|
// sessions frame reconciles the rows.
|
|
26754
|
-
if ((state.sessions.get(parentId)?.meta?.native_subagents?.items || []).some(i => i.status !== 'running' && i.status !== 'unknown'))
|
|
27021
|
+
if ((state.sessions.get(parentId)?.meta?.native_subagents?.items || []).some(i => i.status !== 'running' && i.status !== 'unknown')) {
|
|
27022
|
+
// WP-1b (§8): same refusal copy as retireWorkers above — an unreachable box names itself, and a
|
|
27023
|
+
// body that is absent or not JSON keeps today's bare-status toast.
|
|
27024
|
+
fetch('/api/sessions/' + encodeURIComponent(parentId) + '/native-subagents/clear', { method: 'POST', headers: { 'Authorization': 'Bearer ' + state.token } }).then(async res => {
|
|
27025
|
+
if (res.ok) return;
|
|
27026
|
+
let refusal = null;
|
|
27027
|
+
try { refusal = await res.json(); } catch { refusal = null; }
|
|
27028
|
+
showToast(refusal?.code === 'machine_unreachable' ? `${refusal.machineName || 'The machine that owns this session'} is unreachable. Retry when ready.` : `Could not clear subagents (${res.status})`);
|
|
27029
|
+
}).catch(err => showToast('Could not clear subagents: ' + (err && err.message)));
|
|
27030
|
+
}
|
|
26755
27031
|
retireWorkers({ lead: parentId });
|
|
26756
27032
|
}
|
|
26757
27033
|
|
|
@@ -26884,7 +27160,9 @@ function renderSessionList(force) {
|
|
|
26884
27160
|
// always shrink-wraps its actual rows and cannot leave a blank canyon.
|
|
26885
27161
|
list.style.height = '';
|
|
26886
27162
|
list.style.flexShrink = '';
|
|
27163
|
+
syncActiveComputerMenu();
|
|
26887
27164
|
let sessions = Array.from(state.sessions.entries()).filter(([id, s]) => s.meta);
|
|
27165
|
+
if (currentActiveComputerFilter !== null) sessions = sessions.filter(([, s]) => activeComputerFilterPasses(s, currentActiveComputerFilter));
|
|
26888
27166
|
const statusSnapshot = new Map();
|
|
26889
27167
|
const statusFor = (id, s) => {
|
|
26890
27168
|
if (!statusSnapshot.has(id)) statusSnapshot.set(id, getSessionStatus(s));
|
|
@@ -26937,39 +27215,16 @@ function renderSessionList(force) {
|
|
|
26937
27215
|
});
|
|
26938
27216
|
}
|
|
26939
27217
|
}
|
|
26940
|
-
let
|
|
27218
|
+
let groupInfo = null; // { keyOf, labels, unitPrefix } while a grouped sort mode is active
|
|
26941
27219
|
if (currentActiveSort === 'tab_order') {
|
|
26942
27220
|
sessions.sort(tabOrderSortCmp);
|
|
26943
|
-
} else if (currentActiveSort
|
|
26944
|
-
|
|
26945
|
-
|
|
26946
|
-
|
|
26947
|
-
|
|
26948
|
-
|
|
26949
|
-
|
|
26950
|
-
: { running: 0, resuming: 1, waiting: 2, idle: 3, exited: 4 };
|
|
26951
|
-
const projectAgg = new Map(); // key -> { rank, recent }
|
|
26952
|
-
for (const [id, s] of sessions) {
|
|
26953
|
-
const k = activeSessionProjectKey(s);
|
|
26954
|
-
const rank = ranks[activeStatusBucket(statusFor(id, s).cls)] ?? 4;
|
|
26955
|
-
const recent = activeSessionLastActivityMs(s);
|
|
26956
|
-
const cur = projectAgg.get(k) || { rank: 99, recent: 0 };
|
|
26957
|
-
if (rank < cur.rank) cur.rank = rank;
|
|
26958
|
-
if (recent > cur.recent) cur.recent = recent;
|
|
26959
|
-
projectAgg.set(k, cur);
|
|
26960
|
-
}
|
|
26961
|
-
projectLabelByKey = activeSessionProjectLabels(Array.from(projectAgg.keys()));
|
|
26962
|
-
sessions.sort((a, b) => {
|
|
26963
|
-
const ka = activeSessionProjectKey(a[1]);
|
|
26964
|
-
const kb = activeSessionProjectKey(b[1]);
|
|
26965
|
-
if (ka !== kb) {
|
|
26966
|
-
const ga = projectAgg.get(ka);
|
|
26967
|
-
const gb = projectAgg.get(kb);
|
|
26968
|
-
if (ga.rank !== gb.rank) return ga.rank - gb.rank;
|
|
26969
|
-
if (ga.recent !== gb.recent) return gb.recent - ga.recent;
|
|
26970
|
-
return ka < kb ? -1 : 1;
|
|
26971
|
-
}
|
|
26972
|
-
return compareActiveByStatusThenDisplayOrder(a, b, (entry) => activeStatusBucket(statusFor(entry[0], entry[1]).cls));
|
|
27221
|
+
} else if (activeSortIsGrouped(currentActiveSort)) {
|
|
27222
|
+
groupInfo = sortActiveGroupedSessions(currentActiveSort, sessions, {
|
|
27223
|
+
bucketOf: (id, s) => activeStatusBucket(statusFor(id, s).cls),
|
|
27224
|
+
projectKey: activeSessionProjectKey,
|
|
27225
|
+
projectLabels: activeSessionProjectLabels,
|
|
27226
|
+
lastActivityMs: activeSessionLastActivityMs,
|
|
27227
|
+
compareRows: (a, b) => compareActiveByStatusThenDisplayOrder(a, b, (entry) => activeStatusBucket(statusFor(entry[0], entry[1]).cls)),
|
|
26973
27228
|
});
|
|
26974
27229
|
} else {
|
|
26975
27230
|
sessions.sort((a, b) => compareActiveByStatusThenDisplayOrder(
|
|
@@ -27031,27 +27286,15 @@ function renderSessionList(force) {
|
|
|
27031
27286
|
return acc;
|
|
27032
27287
|
}, {})
|
|
27033
27288
|
: {};
|
|
27034
|
-
const
|
|
27035
|
-
|
|
27036
|
-
|
|
27037
|
-
|
|
27038
|
-
|
|
27039
|
-
|
|
27040
|
-
|
|
27041
|
-
|
|
27042
|
-
|
|
27043
|
-
const hasActive = projectActiveKeys.has(key);
|
|
27044
|
-
const title = key || 'No project directory';
|
|
27045
|
-
const verb = collapsed ? 'Expand' : 'Collapse';
|
|
27046
|
-
return `<button type="button" class="session-group-sep project-sep${collapsed ? ' collapsed' : ''}${hasActive ? ' has-active' : ''}" data-project-key="${escHtml(key)}" title="${escHtml(title)}" aria-expanded="${collapsed ? 'false' : 'true'}" aria-label="${verb} ${escHtml(label)} project group, ${count} active session${count === 1 ? '' : 's'}" onclick="toggleActiveProjectGroup('${activeSessionJsArg(key)}', event)"><span class="project-sep-chevron" aria-hidden="true">▾</span>${projectGroupFolderIconSvg(13)}<span class="project-sep-label">${escHtml(label)}</span><span class="project-sep-count">${count}</span></button>`;
|
|
27047
|
-
};
|
|
27048
|
-
const projectCounts = currentActiveSort === 'by_project'
|
|
27049
|
-
? sessions.reduce((acc, [, s]) => {
|
|
27050
|
-
const k = activeSessionProjectKey(s);
|
|
27051
|
-
acc[k] = (acc[k] || 0) + 1;
|
|
27052
|
-
return acc;
|
|
27053
|
-
}, {})
|
|
27054
|
-
: {};
|
|
27289
|
+
const groupCountByKey = new Map();
|
|
27290
|
+
const groupActiveKeys = new Set();
|
|
27291
|
+
if (groupInfo) {
|
|
27292
|
+
for (const [id, s] of sessions) {
|
|
27293
|
+
const k = groupInfo.keyOf(s);
|
|
27294
|
+
groupCountByKey.set(k, (groupCountByKey.get(k) || 0) + 1);
|
|
27295
|
+
if (id === state.activeTab) groupActiveKeys.add(k);
|
|
27296
|
+
}
|
|
27297
|
+
}
|
|
27055
27298
|
|
|
27056
27299
|
let lastGroup = null;
|
|
27057
27300
|
const units = [];
|
|
@@ -27062,10 +27305,14 @@ function renderSessionList(force) {
|
|
|
27062
27305
|
units.push({ key: 'sep:' + cls, html: renderGroupSep(cls, groupCounts[cls] || 0) });
|
|
27063
27306
|
lastGroup = cls;
|
|
27064
27307
|
}
|
|
27065
|
-
} else if (
|
|
27066
|
-
const k =
|
|
27308
|
+
} else if (groupInfo) {
|
|
27309
|
+
const k = groupInfo.keyOf(s);
|
|
27067
27310
|
if (k !== lastGroup) {
|
|
27068
|
-
units.push({ key:
|
|
27311
|
+
units.push({ key: groupInfo.unitPrefix + k, html: activeGroupHeaderHtml(currentActiveSort, {
|
|
27312
|
+
key: k, label: groupInfo.labels.get(k), count: groupCountByKey.get(k) || 0,
|
|
27313
|
+
collapsed: isActiveProjectCollapsed(k), hasActive: groupActiveKeys.has(k),
|
|
27314
|
+
iconHtml: projectGroupFolderIconSvg(13), escHtml, jsArg: activeSessionJsArg,
|
|
27315
|
+
}) });
|
|
27069
27316
|
lastGroup = k;
|
|
27070
27317
|
}
|
|
27071
27318
|
if (!isActiveFiltering && isActiveProjectCollapsed(k)) return;
|
|
@@ -27220,7 +27467,13 @@ async function dismissSessionAttention(id, event) {
|
|
|
27220
27467
|
// Switch the Active Sessions sort mode and persist. Defaults are set at
|
|
27221
27468
|
// the top of this block; loadPrefs() hydrates from the server-side store.
|
|
27222
27469
|
function setActiveSort(mode) {
|
|
27223
|
-
|
|
27470
|
+
const computerFilter = parseActiveComputerOptionValue(mode);
|
|
27471
|
+
if (computerFilter !== undefined) {
|
|
27472
|
+
const csel = document.getElementById('active-sort');
|
|
27473
|
+
if (csel) csel.value = currentActiveSort;
|
|
27474
|
+
return setActiveComputerFilter(computerFilter);
|
|
27475
|
+
}
|
|
27476
|
+
mode = normalizeActiveSort(mode);
|
|
27224
27477
|
currentActiveSort = mode;
|
|
27225
27478
|
savePref('active_sort', mode);
|
|
27226
27479
|
const sel = document.getElementById('active-sort');
|
|
@@ -28652,7 +28905,30 @@ function moveTabToStart(id) {
|
|
|
28652
28905
|
function saveTabOrder() {
|
|
28653
28906
|
// Save session IDs and review tab (for position restore after restart)
|
|
28654
28907
|
const sessionOrder = state.tabOrder.filter(id => state.sessions.has(id) || id === 'review');
|
|
28655
|
-
savePref('tab_order', sessionOrder);
|
|
28908
|
+
savePref('tab_order', _withUndeliveredTabIds(sessionOrder));
|
|
28909
|
+
}
|
|
28910
|
+
|
|
28911
|
+
// WP-5 (S-12): an undelivered id is written back where it was saved — right after its nearest
|
|
28912
|
+
// SAVED predecessor that is present now (or at the front) — so a user reorder of the present tabs
|
|
28913
|
+
// is honoured and the box tab still comes back in its place when the catalog lands.
|
|
28914
|
+
function _withUndeliveredTabIds(order) {
|
|
28915
|
+
const pending = state._undeliveredTabIds;
|
|
28916
|
+
if (!pending || pending.size === 0) return order;
|
|
28917
|
+
const saved = state._undeliveredTabSaved || [];
|
|
28918
|
+
const out = order.slice();
|
|
28919
|
+
for (const id of saved) {
|
|
28920
|
+
if (!pending.has(id) || out.includes(id)) continue;
|
|
28921
|
+
out.splice(_undeliveredTabInsertIndex(id, saved, out), 0, id);
|
|
28922
|
+
}
|
|
28923
|
+
return out;
|
|
28924
|
+
}
|
|
28925
|
+
|
|
28926
|
+
function _undeliveredTabInsertIndex(id, saved, out) {
|
|
28927
|
+
for (let k = saved.indexOf(id) - 1; k >= 0; k -= 1) {
|
|
28928
|
+
const at = out.indexOf(saved[k]);
|
|
28929
|
+
if (at !== -1) return at + 1;
|
|
28930
|
+
}
|
|
28931
|
+
return 0;
|
|
28656
28932
|
}
|
|
28657
28933
|
|
|
28658
28934
|
// --- Actions ---
|
|
@@ -28697,7 +28973,7 @@ var _nsPathPickerState = {
|
|
|
28697
28973
|
activeIndex: -1,
|
|
28698
28974
|
items: [],
|
|
28699
28975
|
projects: [],
|
|
28700
|
-
projectsLoaded: false,
|
|
28976
|
+
projectsLoaded: false, projectsError: false, // projectsError (D-6): the list FAILED to read — never an empty list
|
|
28701
28977
|
projectsLoading: false,
|
|
28702
28978
|
worktreeTimer: 0,
|
|
28703
28979
|
expanded: {}, // { projectKey: true } — worktrees the user has revealed
|
|
@@ -28967,12 +29243,12 @@ function _nsPathLoadProjects() {
|
|
|
28967
29243
|
// are the only place the same-repo prefill looks — plain gitRemoteKey equality inside
|
|
28968
29244
|
// nsMachineAfterProjectsLoaded(), against the keys of the machine we are pointed at.
|
|
28969
29245
|
fetch('/api/projects?token=' + encodeURIComponent(state.token || '') + machineQueryParam())
|
|
28970
|
-
.then(function(r) {
|
|
29246
|
+
.then(function(r) { if (!r.ok) throw new Error('projects read failed'); return r.json(); })
|
|
28971
29247
|
.then(function(projects) {
|
|
28972
29248
|
_nsPathPickerState.projects = Array.isArray(projects)
|
|
28973
29249
|
? projects.filter(function(p) { return p && p.path; })
|
|
28974
29250
|
: [];
|
|
28975
|
-
_nsPathPickerState.projectsLoaded = true;
|
|
29251
|
+
_nsPathPickerState.projectsLoaded = true; _nsPathPickerState.projectsError = false;
|
|
28976
29252
|
_nsPathPickerState.projectsLoading = false;
|
|
28977
29253
|
_nsPathRefreshHostStatus();
|
|
28978
29254
|
if (typeof nsMachineAfterProjectsLoaded === 'function') nsMachineAfterProjectsLoaded(_nsPathPickerState);
|
|
@@ -28981,7 +29257,8 @@ function _nsPathLoadProjects() {
|
|
|
28981
29257
|
if (typeof _storagePathRepaintOpen === 'function') _storagePathRepaintOpen();
|
|
28982
29258
|
})
|
|
28983
29259
|
.catch(function() {
|
|
28984
|
-
_nsPathPickerState.projectsLoading = false;
|
|
29260
|
+
_nsPathPickerState.projectsLoading = false; _nsPathPickerState.projectsError = true; // D-6: UNKNOWN, not "no projects"; not latched
|
|
29261
|
+
if (typeof nsMachineAfterProjectsLoaded === 'function') nsMachineAfterProjectsLoaded(_nsPathPickerState);
|
|
28985
29262
|
});
|
|
28986
29263
|
}
|
|
28987
29264
|
|
|
@@ -29831,7 +30108,7 @@ function _nsWorktreeNamespaceForAgent(agentType) {
|
|
|
29831
30108
|
function _nsAgentUsesCodeWorkspace() {
|
|
29832
30109
|
var val = document.getElementById('ns-agent').value || 'claude:';
|
|
29833
30110
|
var agent = val.replace(/:$/, '');
|
|
29834
|
-
return ['claude', 'codex', 'gemini', 'walle'].indexOf(agent) !== -1;
|
|
30111
|
+
return ['claude', 'codex', 'gemini', 'walle', 'dsh'].indexOf(agent) !== -1;
|
|
29835
30112
|
}
|
|
29836
30113
|
|
|
29837
30114
|
function _nsHasActiveSessionInCwd(cwd) {
|
|
@@ -29949,73 +30226,13 @@ function showNewSessionModal() {
|
|
|
29949
30226
|
var createBtn = document.querySelector('#new-session-modal .btn.primary');
|
|
29950
30227
|
if (createBtn) { createBtn.disabled = false; createBtn.textContent = 'Create'; }
|
|
29951
30228
|
|
|
29952
|
-
var grid = document.getElementById('ns-agent-grid');
|
|
29953
30229
|
document.getElementById('ns-agent').value = 'claude:';
|
|
29954
30230
|
|
|
29955
|
-
//
|
|
29956
|
-
//
|
|
29957
|
-
//
|
|
29958
|
-
//
|
|
29959
|
-
|
|
29960
|
-
var agentDefs = [
|
|
29961
|
-
{ id: 'claude:', name: 'Claude Code', sub: 'Anthropic', color: '#D97757', agent: 'claude', detect: 'claude' },
|
|
29962
|
-
{ id: 'codex:', name: 'Codex', sub: 'OpenAI', color: '#22c55e', agent: 'codex', detect: 'codex' },
|
|
29963
|
-
{ id: 'opencode:', name: 'OpenCode', sub: 'GLM 5.2', color: '#e0af68', agent: 'opencode', detect: 'opencode' },
|
|
29964
|
-
{ id: 'walle:', name: 'Wall-E', sub: 'Smart routing', color: '#f59e0b', agent: 'walle', detect: null },
|
|
29965
|
-
{ id: 'cursor:', name: 'Cursor', sub: 'Anysphere', color: '#111827', accent: '#7aa2f7', agent: 'cursor', detect: 'cursor-agent' },
|
|
29966
|
-
{ id: 'gemini:', name: 'Gemini CLI', sub: 'Google', color: '#3b82f6', agent: 'gemini', detect: 'gemini' },
|
|
29967
|
-
{ id: 'shell:', name: 'Shell', sub: 'Terminal', color: '#6b7280', icon: '$', detect: null },
|
|
29968
|
-
];
|
|
29969
|
-
|
|
29970
|
-
function agentAvailability(agents, detectKey) {
|
|
29971
|
-
if (!detectKey) return { known: true, available: true, path: null };
|
|
29972
|
-
if (!agents) return { known: false, available: true, path: null };
|
|
29973
|
-
var detail = agents.agents && agents.agents[detectKey] ? agents.agents[detectKey] : agents[detectKey];
|
|
29974
|
-
if (detail && typeof detail === 'object') {
|
|
29975
|
-
return { known: true, available: detail.available !== false, path: detail.path || detail.realpath || null };
|
|
29976
|
-
}
|
|
29977
|
-
if (typeof detail === 'boolean') return { known: true, available: detail, path: null };
|
|
29978
|
-
return { known: false, available: true, path: null };
|
|
29979
|
-
}
|
|
29980
|
-
|
|
29981
|
-
function renderAgentCards(agents) {
|
|
29982
|
-
var selected = document.getElementById('ns-agent').value || 'claude:';
|
|
29983
|
-
grid.innerHTML = '';
|
|
29984
|
-
agentDefs.forEach(function(def) {
|
|
29985
|
-
var availability = agentAvailability(agents, def.detect);
|
|
29986
|
-
var available = availability.available;
|
|
29987
|
-
var card = document.createElement('div');
|
|
29988
|
-
card.className = 'ns-agent-card' + (available ? '' : ' not-detected');
|
|
29989
|
-
card.style.setProperty('--ns-card-accent', def.accent || def.color);
|
|
29990
|
-
card.dataset.value = def.id;
|
|
29991
|
-
if (availability.path) card.title = def.name + ': ' + availability.path;
|
|
29992
|
-
else if (availability.known && available === false) card.title = def.name + ' CLI not detected on PATH. You can still select it; launch will show the real terminal error if the command is unavailable.';
|
|
29993
|
-
// Render a real brand icon when the agent maps to one (claude/codex/
|
|
29994
|
-
// gemini/opencode/walle); otherwise the textual fallback ($, >) stays.
|
|
29995
|
-
// Inline color forces the brand tint regardless of theme — these
|
|
29996
|
-
// tiles always show on the dark agent-picker background, never on
|
|
29997
|
-
// an active highlight, so we don't need the dark-on-light invert
|
|
29998
|
-
// that the session-list .active rule applies via currentColor.
|
|
29999
|
-
var iconHtml = def.agent
|
|
30000
|
-
? '<div class="ns-card-icon" style="background:' + def.color + ';color:#fff;">'
|
|
30001
|
-
+ providerIconSvg(def.agent, 22) + '</div>'
|
|
30002
|
-
: '<div class="ns-card-icon" style="background:' + def.color + '">' + def.icon + '</div>';
|
|
30003
|
-
card.innerHTML =
|
|
30004
|
-
iconHtml +
|
|
30005
|
-
'<div class="ns-card-name">' + def.name + '</div>' +
|
|
30006
|
-
'<div class="ns-card-sub">' + def.sub + (available === false ? ' (not found)' : '') + '</div>';
|
|
30007
|
-
card.onclick = function() { _selectAgentCard(def.id); };
|
|
30008
|
-
grid.appendChild(card);
|
|
30009
|
-
});
|
|
30010
|
-
_selectAgentCard(selected);
|
|
30011
|
-
}
|
|
30012
|
-
|
|
30013
|
-
renderAgentCards(null);
|
|
30014
|
-
|
|
30015
|
-
fetch('/api/models/available-agents?token=' + encodeURIComponent(state.token))
|
|
30016
|
-
.then(function(r) { return r.json(); })
|
|
30017
|
-
.then(function(agents) { renderAgentCards(agents); })
|
|
30018
|
-
.catch(function() { renderAgentCards(null); });
|
|
30231
|
+
// S-43 (WP-7, docs/deepseek-harness-acp-agent-2026-10-28.html §9): the agent grid — the card
|
|
30232
|
+
// table (with the dsh card between gemini: and shell:), PATH availability, the OQ-4 Model select,
|
|
30233
|
+
// the D-15 remote-machine disable and the D-16 MCP disable — now lives in
|
|
30234
|
+
// public/js/new-session-agent-grid.js; index.html is at its file-size ceiling.
|
|
30235
|
+
NewSessionAgentGrid.open();
|
|
30019
30236
|
|
|
30020
30237
|
document.getElementById('ns-label').focus();
|
|
30021
30238
|
_maybeRecommendWorktreeForNewSession(true);
|
|
@@ -31602,8 +31819,9 @@ async function loadPrefs(opts = {}) {
|
|
|
31602
31819
|
}
|
|
31603
31820
|
|
|
31604
31821
|
// Restore Active Sessions sort + expand prefs.
|
|
31605
|
-
|
|
31606
|
-
|
|
31822
|
+
const restoredSort = normalizeActiveSort(prefs.active_sort);
|
|
31823
|
+
if (prefs.active_sort === restoredSort) {
|
|
31824
|
+
currentActiveSort = restoredSort;
|
|
31607
31825
|
const sel = document.getElementById('active-sort');
|
|
31608
31826
|
if (sel) sel.value = currentActiveSort;
|
|
31609
31827
|
}
|
|
@@ -31616,6 +31834,7 @@ async function loadPrefs(opts = {}) {
|
|
|
31616
31834
|
if (Array.isArray(prefs.active_project_collapsed_keys)) {
|
|
31617
31835
|
activeProjectCollapsedKeys = new Set(normalizeActiveProjectCollapsedKeys(prefs.active_project_collapsed_keys));
|
|
31618
31836
|
}
|
|
31837
|
+
currentActiveComputerFilter = typeof prefs.active_computer_filter === 'string' ? prefs.active_computer_filter : null;
|
|
31619
31838
|
// Lifecycle WP-6 (§5 D-3, §9 S-25): hydrate the per-parent worker-group toggle state.
|
|
31620
31839
|
// Pruning against state.sessions happens lazily in toggleWorkerGroup (loadPrefs can
|
|
31621
31840
|
// race the session-list fetch at boot, so pruning here risks discarding a real toggle
|
|
@@ -31985,34 +32204,35 @@ async function loadRecentSessions(options = {}) {
|
|
|
31985
32204
|
// Keep the Claude Desktop fork map fresh so review actions show Convert vs Resume-fork.
|
|
31986
32205
|
refreshDesktopForkMap();
|
|
31987
32206
|
if (!_isRootPrivateLeaseCurrent(lease)) return;
|
|
32207
|
+
const _recent = beginRecentLoad(currentActiveComputerFilter), _live = () => _isRootPrivateLeaseCurrent(lease) && _recent.token === recentMachineLoad.token;
|
|
31988
32208
|
let res;
|
|
31989
32209
|
let data;
|
|
31990
32210
|
try {
|
|
31991
|
-
res = await fetch(`/api/recent-sessions?token=${state.token}&limit=200
|
|
31992
|
-
if (!
|
|
31993
|
-
data = await res.json();
|
|
31994
|
-
if (!
|
|
31995
|
-
if (!res.ok && res.status !== 202) {
|
|
32211
|
+
res = _recent.source.kind === 'machine' ? null : await fetch(recentRequestUrl(`/api/recent-sessions?token=${state.token}&limit=200`, _recent.source));
|
|
32212
|
+
if (!_live()) return;
|
|
32213
|
+
data = res ? recentNoteFleetBody(await res.json()) : recentNoteFleetBody(await fetchMachineRecentRows(fetch, `/api/recent-sessions?token=${state.token}&limit=200`, _recent, activeComputerSeenLabels));
|
|
32214
|
+
if (!_live()) return;
|
|
32215
|
+
if (res && !res.ok && res.status !== 202) {
|
|
31996
32216
|
throw new Error(`Recent sessions request failed (${res.status})`);
|
|
31997
32217
|
}
|
|
31998
32218
|
} catch (error) {
|
|
31999
|
-
if (!
|
|
32219
|
+
if (!_live()) return;
|
|
32000
32220
|
const lastGood = allRecentSessions.length > 0 ? allRecentSessions : _readRecentSessionsLastGood(lease);
|
|
32001
|
-
if (!
|
|
32221
|
+
if (!_live()) return;
|
|
32002
32222
|
if (lastGood.length === 0) throw error;
|
|
32003
32223
|
data = { sessions: lastGood, stale: true, catalogDegraded: true };
|
|
32004
32224
|
}
|
|
32005
|
-
if (!
|
|
32225
|
+
if (!_live()) return;
|
|
32006
32226
|
const degraded = res?.status === 202
|
|
32007
32227
|
|| (!Array.isArray(data) && (data?.stale === true || data?.catalogDegraded === true));
|
|
32008
32228
|
// Support both old (array) and new (object with sessions + lastActiveProject) response formats
|
|
32009
32229
|
const incoming = Array.isArray(data) ? data : (Array.isArray(data?.sessions) ? data.sessions : []);
|
|
32010
32230
|
if (incoming.length > 0) {
|
|
32011
32231
|
allRecentSessions = incoming;
|
|
32012
|
-
if (!degraded) _writeRecentSessionsLastGood(incoming, lease);
|
|
32232
|
+
if (!degraded && res) _writeRecentSessionsLastGood(incoming, lease);
|
|
32013
32233
|
} else if (degraded) {
|
|
32014
32234
|
const lastGood = allRecentSessions.length > 0 ? allRecentSessions : _readRecentSessionsLastGood(lease);
|
|
32015
|
-
if (!
|
|
32235
|
+
if (!_live()) return;
|
|
32016
32236
|
if (lastGood.length > 0) allRecentSessions = lastGood;
|
|
32017
32237
|
} else {
|
|
32018
32238
|
allRecentSessions = incoming;
|
|
@@ -32161,6 +32381,7 @@ function _recentSidebarFilterState() {
|
|
|
32161
32381
|
emptyMode: showEmptyOnly ? 'only' : 'exclude',
|
|
32162
32382
|
project: currentProjectFilter,
|
|
32163
32383
|
agent: currentAgentFilter,
|
|
32384
|
+
machine: currentActiveComputerFilter,
|
|
32164
32385
|
};
|
|
32165
32386
|
}
|
|
32166
32387
|
|
|
@@ -32466,6 +32687,8 @@ function recentSessionAgentProviderFallback(s) {
|
|
|
32466
32687
|
if (agentTypes.includes('gemini')) return 'google';
|
|
32467
32688
|
if (agentTypes.includes('claude') || agentTypes.includes('claude-desktop')) return 'anthropic';
|
|
32468
32689
|
if (agentTypes.includes('cursor')) return 'cursor';
|
|
32690
|
+
// WP-7 (S-50): dsh chips use the DeepSeek brand colour (recent-model-provider-deepseek).
|
|
32691
|
+
if (agentTypes.includes('dsh')) return 'deepseek';
|
|
32469
32692
|
return '';
|
|
32470
32693
|
}
|
|
32471
32694
|
|
|
@@ -32511,6 +32734,7 @@ function recentSessionAgentBadgeColorKey(agentType) {
|
|
|
32511
32734
|
case 'walle': return 'walle';
|
|
32512
32735
|
case 'cursor': return 'cursor';
|
|
32513
32736
|
case 'opencode': return 'opencode';
|
|
32737
|
+
case 'dsh': return 'deepseek';
|
|
32514
32738
|
default: return '';
|
|
32515
32739
|
}
|
|
32516
32740
|
}
|
|
@@ -32701,9 +32925,10 @@ function recentSearchEmptyHtml(label) {
|
|
|
32701
32925
|
}
|
|
32702
32926
|
if (currentAgentFilter) details.push('agent ' + escHtml(SessionSearchUtils.recentAgentFilterLabel(currentAgentFilter)));
|
|
32703
32927
|
if (currentProjectFilter) details.push('project ' + escHtml(currentProjectFilter.replace(/^\/Users\/[^/]+/, '~')));
|
|
32928
|
+
if (currentActiveComputerFilter !== null) details.push('computer ' + escHtml(activeComputerSeenLabels.get(currentActiveComputerFilter) || (currentActiveComputerFilter === '' ? 'Local' : currentActiveComputerFilter)));
|
|
32704
32929
|
details.push(showEmptyOnly ? 'empty only' : 'non-empty');
|
|
32705
32930
|
if (document.getElementById('this-device')?.checked !== false) details.push('this device');
|
|
32706
|
-
const detailHtml = rawQuery && details.length
|
|
32931
|
+
const detailHtml = (rawQuery || currentActiveComputerFilter !== null) && details.length
|
|
32707
32932
|
? '<div style="margin-top:6px;color:var(--fg-dim);line-height:1.35">Filters: ' + details.join(', ') + '</div>'
|
|
32708
32933
|
: '';
|
|
32709
32934
|
return '<div style="padding:10px;font-size:12px;color:var(--fg-dim)">' + escHtml(label || 'No sessions found') + detailHtml + '</div>';
|
|
@@ -32713,7 +32938,7 @@ function renderRecentSessions(sessions) {
|
|
|
32713
32938
|
const list = document.getElementById('recent-session-list');
|
|
32714
32939
|
|
|
32715
32940
|
if (sessions.length === 0) {
|
|
32716
|
-
list.innerHTML = recentSearchEmptyHtml('No sessions found');
|
|
32941
|
+
list.innerHTML = recentSearchEmptyHtml(recentMachineLoad.unavailable || 'No sessions found');
|
|
32717
32942
|
return;
|
|
32718
32943
|
}
|
|
32719
32944
|
|
|
@@ -33115,7 +33340,7 @@ function _reviewMessagesUrl(sessionId, projectEntry, opts) {
|
|
|
33115
33340
|
let url = `/api/session/messages?id=${encodeURIComponent(sessionId)}`
|
|
33116
33341
|
+ `&project=${encodeURIComponent(projectEntry || '')}`
|
|
33117
33342
|
+ `&token=${state.token}`
|
|
33118
|
-
+ '&conversationContract=4';
|
|
33343
|
+
+ '&conversationContract=4' + recentRequestUrl('', recentSourceForFilter(state.reviewMachine));
|
|
33119
33344
|
if (opts.entries) return url + `&entries=${encodeURIComponent(opts.entries)}`;
|
|
33120
33345
|
url += `&limitTurns=${REVIEW_TURN_PAGE_SIZE}`;
|
|
33121
33346
|
if (opts.before) url += `&before=${encodeURIComponent(opts.before)}`;
|
|
@@ -34324,7 +34549,7 @@ function toggleEmptyFilter() {
|
|
|
34324
34549
|
// element (.session-item or .tab) sets color via CSS so the icon inherits the
|
|
34325
34550
|
// per-provider hue and inverts cleanly when the row is active (light bg, dark text).
|
|
34326
34551
|
// Returns an HTML string with title for screen-readers + hover tooltip.
|
|
34327
|
-
const AGENT_LABELS = { walle: 'Wall-E', codex: 'Codex', gemini: 'Gemini', opencode: 'OpenCode', cursor: 'Cursor', claude: 'Claude Code', 'claude-desktop': 'Claude Desktop', shell: 'Shell' };
|
|
34552
|
+
const AGENT_LABELS = { walle: 'Wall-E', codex: 'Codex', gemini: 'Gemini', opencode: 'OpenCode', cursor: 'Cursor', claude: 'Claude Code', 'claude-desktop': 'Claude Desktop', dsh: 'DeepSeek Harness', shell: 'Shell' };
|
|
34328
34553
|
function getAgentType(s) {
|
|
34329
34554
|
return _clientAgentTypeForSession(s);
|
|
34330
34555
|
}
|
|
@@ -34385,6 +34610,11 @@ function providerIconSvg(agentType, sizePx) {
|
|
|
34385
34610
|
+ '<path fill="currentColor" fill-opacity="0.55" d="M2.2 4.75 L8 8 L8 14.5 L2.2 11.25 Z"/>'
|
|
34386
34611
|
+ '<path fill="currentColor" fill-opacity="0.3" d="M8 8 L13.8 4.75 L13.8 11.25 L8 14.5 Z"/>';
|
|
34387
34612
|
break;
|
|
34613
|
+
case 'dsh':
|
|
34614
|
+
// WP-7 (S-48): the DeepSeek Harness mark is the DeepSeek brand whale — the same
|
|
34615
|
+
// simple-icons path the model-provider rows use (brand colour #4D6BFE, currentColor here so
|
|
34616
|
+
// the sidebar/tab .session-item[data-agent="dsh"] rules keep theming control).
|
|
34617
|
+
return brandIconSvg('deepseek', size);
|
|
34388
34618
|
case 'shell':
|
|
34389
34619
|
// >_ prompt — universal terminal metaphor.
|
|
34390
34620
|
inner = '<path fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" d="M2.5 5 L6 8 L2.5 11 M8.5 12 L13.5 12"/>';
|
|
@@ -34765,7 +34995,7 @@ function _buildSessionPaletteRow(item, idx) {
|
|
|
34765
34995
|
const badge = document.createElement('span');
|
|
34766
34996
|
badge.style.cssText = 'font-size:9px;padding:1px 6px;border-radius:4px;flex-shrink:0;' + (isActive ? 'background:rgba(0,0,0,0.2);' : 'background:var(--bg);color:var(--fg-dim);');
|
|
34767
34997
|
badge.textContent = item.kind === 'active' ? 'Tab' : 'View';
|
|
34768
|
-
row.appendChild(badge);
|
|
34998
|
+
row.appendChild(badge); const machineChip = typeof machineSessionChipNode === 'function' && machineSessionChipNode(item.kind === 'active' ? (state.sessions.get(item.sessionId) || item) : item, item.sessionId); if (machineChip) row.insertBefore(machineChip, badge); // fleet WP-5
|
|
34769
34999
|
|
|
34770
35000
|
return row;
|
|
34771
35001
|
}
|
|
@@ -34815,12 +35045,12 @@ async function performServerSearch(query) {
|
|
|
34815
35045
|
const grammarQuery = (typeof SessionSearchGrammar !== 'undefined')
|
|
34816
35046
|
? (SessionSearchGrammar.parseSearchQuery(query).core || query)
|
|
34817
35047
|
: query;
|
|
34818
|
-
const res = await fetch(`/api/sessions/search?q=${encodeURIComponent(grammarQuery)}&limit=50&token=${state.token}
|
|
34819
|
-
const data = await res.json().catch(() => ({}));
|
|
35048
|
+
const _searchSource = recentSourceForFilter(currentActiveComputerFilter), res = await fetch(recentRequestUrl(`/api/sessions/search?q=${encodeURIComponent(grammarQuery)}&limit=50&token=${state.token}`, _searchSource));
|
|
35049
|
+
const data = stampRecentBodyFromSource(await res.json().catch(() => ({})), _searchSource, activeComputerSeenLabels);
|
|
34820
35050
|
|
|
34821
|
-
// Check if query is still current (user may have typed more)
|
|
35051
|
+
// Check if query is still current (user may have typed more, or switched the computer filter below)
|
|
34822
35052
|
const currentQ = document.getElementById('recent-search').value.trim();
|
|
34823
|
-
if (currentQ.toLowerCase() !== query.toLowerCase()) return;
|
|
35053
|
+
if (currentQ.toLowerCase() !== query.toLowerCase() || (data._sourceKey || '') !== recentSourceKey(recentSourceForFilter(currentActiveComputerFilter))) return;
|
|
34824
35054
|
if (!res.ok || data.error || !Array.isArray(data.results)) {
|
|
34825
35055
|
showSearchDegradedFallback(
|
|
34826
35056
|
query,
|
|
@@ -34993,7 +35223,7 @@ function renderServerSearchResults(results, total, query, degraded) {
|
|
|
34993
35223
|
|
|
34994
35224
|
// Note: html is safe — all user data is escaped via escHtml(), highlights use <mark> only.
|
|
34995
35225
|
// DOMPurify was stripping onclick/ondblclick/oncontextmenu handlers, breaking click-to-review.
|
|
34996
|
-
list.innerHTML = html || recentSearchEmptyHtml('No results found');
|
|
35226
|
+
list.innerHTML = html || recentSearchEmptyHtml(recentMachineLoad.unavailable || 'No results found');
|
|
34997
35227
|
}
|
|
34998
35228
|
|
|
34999
35229
|
function updateSearchCount(count, degraded) {
|