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
|
@@ -25,6 +25,9 @@
|
|
|
25
25
|
const { createLeadModelSwitch } = require('./lead-model-switch');
|
|
26
26
|
const { writeCodexDefaultModel, writeCodexDefaultEffort, listCodexModels } = require('./codex-default-model');
|
|
27
27
|
const { createLeadModelOptions, claudeEfforts } = require('./lead-model-options');
|
|
28
|
+
const { createOpencodeLaunchModel } = require('./opencode-launch-model');
|
|
29
|
+
const { createDshLaunchModel } = require('./acp/dsh-launch-model');
|
|
30
|
+
const opencodeCli = require('./opencode-cli');
|
|
28
31
|
const { requestWalleAllPages } = require('./walle-paged-read');
|
|
29
32
|
const { modelsMatch, CLAUDE_ALIASES } = require('./lead-model-switch');
|
|
30
33
|
const leadModelObserver = require('./lead-model-observer');
|
|
@@ -33,15 +36,28 @@ const { ADAPTER_VERSION } = require('./harness-model-profile');
|
|
|
33
36
|
const { CODEX_CONTROL_ADAPTER } = require('./codex-harness-qualification');
|
|
34
37
|
const { summarizeRestartGuard } = require('./restart-guard');
|
|
35
38
|
const { getLeadRouteReadiness } = require('./session-create/proxy-route-readiness');
|
|
39
|
+
// WP-1 (docs/lead-route-model-invocability-preflight-2026-10-28.html §5 D-1/D-2, §6.2): the routed
|
|
40
|
+
// switch proves the pair can generate BEFORE the session is parked.
|
|
41
|
+
const { createLeadRoutePreflight } = require('./lead-route-preflight');
|
|
36
42
|
const { workerCapabilityForModel } = require('./model-proxy/routes');
|
|
37
43
|
const { effectiveOutputCap } = require('./model-proxy/output-cap');
|
|
38
44
|
const { modelExecutionRecipeMetadata } = require('./model-execution-recipe-metadata');
|
|
39
45
|
const { capturePrivateSourceEnvironment, attachPrivateSourceRecoveryEnvironment } = require('./lead-model-restore-reconciliation');
|
|
40
46
|
const { createLeadModelTransitionRuntime } = require('./lead-model-transition-runtime');
|
|
47
|
+
// R-1: the ONE wire mapping. An OpenCode lead (and its routed workers) speaks the claude wire, so
|
|
48
|
+
// route servability and the harness profile are asked of 'claude' -- never of 'opencode'.
|
|
49
|
+
const { leadWireAgent } = require('./lead-wire-agent');
|
|
41
50
|
const {
|
|
42
51
|
readSessionModelActionIntent, projectSessionModelConflictContext, sameCoreOwner, coreOwnerTuple,
|
|
43
52
|
} = require('./session-model-action-preconditions');
|
|
44
53
|
const { validateRecipe } = require('./lead-model-transition-store');
|
|
54
|
+
// WP-0/WP-1 (§6/§7): the ONE history-mode module. The planner's pre-park thread check and the
|
|
55
|
+
// restore receipt call the SAME clause, and the recipes carry the SAME mode the controller resolved.
|
|
56
|
+
const { resolveSourceHistoryMode, recipeHistoryMode, sourceModelOrigin, claudeSessionIdArg,
|
|
57
|
+
targetThreadClause } = require('./lead-switch-history-mode');
|
|
58
|
+
const { freshEvidenceInputs, freshRelaunchArgs } = require('./park-fresh-relaunch');
|
|
59
|
+
// WP-2 S-9: the ONE versioned proxy-filter receipt this launch path stamps onto its own recipe.
|
|
60
|
+
const { CODEX_HISTORICAL_TOOLS_FILTER_RECEIPT } = require('./model-proxy/historical-tool-declarations');
|
|
45
61
|
const { validateSessionModelIntentRequest } = require('./session-model-intent-contract');
|
|
46
62
|
const { canonicalJson } = require('../../shared/coding-model-deployment');
|
|
47
63
|
const { findCatalogRoute } = require('./coding-catalog-route');
|
|
@@ -306,12 +322,49 @@ function createLeadTransitionTargetPlanner(deps = {}) {
|
|
|
306
322
|
return { ok: false, reason: 'lead_target_resume_changed' };
|
|
307
323
|
}
|
|
308
324
|
}
|
|
325
|
+
// WP-1 D-4 (§7): the SAME thread clause the restore receipt will run, evaluated here so a plan the
|
|
326
|
+
// receipt would refuse is never allowed to park the source. The recipe pair this planner's
|
|
327
|
+
// transition will be written with is exactly (source thread, resolved mode), so the clause is
|
|
328
|
+
// asked of that pair. A 'present' source REQUIRES the plan to resume its own thread -- a
|
|
329
|
+
// both-absent plan under 'present' is the drift this refuses (the never-prompted source is
|
|
330
|
+
// declared 'none' by the controller instead); a 'none' source requires a plan with no resume
|
|
331
|
+
// target at all.
|
|
332
|
+
const historyMode = recipeHistoryMode(source);
|
|
333
|
+
if (!targetThreadClause({ source: { historyMode }, target: { historyMode, providerThreadId: canonicalThreadId },
|
|
334
|
+
launchPlan: result.plan })) {
|
|
335
|
+
return { ok: false, reason: 'lead_target_resume_inconsistent' };
|
|
336
|
+
}
|
|
309
337
|
noteLaunchPlanReadinessGeneration(result.plan);
|
|
310
338
|
return { ok: true, generation, launchPlan: result.plan, sourceGeneration };
|
|
311
339
|
};
|
|
312
340
|
|
|
313
341
|
}
|
|
314
342
|
|
|
343
|
+
/**
|
|
344
|
+
* WP-0 D-1/D-2 (§6): the SOURCE history-mode resolver the switch asks BEFORE it claims a transition.
|
|
345
|
+
* It composes the SAME inputs park already injects (`prepareLifecycleTranscript` + the off-thread
|
|
346
|
+
* resume ladder), so the switch and park can never disagree about whether a source has history. The
|
|
347
|
+
* composition lives here; server.js only supplies the two inputs.
|
|
348
|
+
*/
|
|
349
|
+
function createSourceHistoryModeResolver({ getSession, detectAgentType, lifecycleResumeTarget, prepareLifecycleTranscript, resolveRung } = {}) {
|
|
350
|
+
return async function resolveSourceHistoryModeForSession(sessionId) {
|
|
351
|
+
try {
|
|
352
|
+
const session = getSession(String(sessionId || ''));
|
|
353
|
+
if (!session) return 'present';
|
|
354
|
+
const agentType = detectAgentType(String(session.cmd || ''));
|
|
355
|
+
const resumeId = lifecycleResumeTarget(session, agentType) || '';
|
|
356
|
+
const prepared = typeof prepareLifecycleTranscript === 'function' ? await prepareLifecycleTranscript(session) : null;
|
|
357
|
+
const { resumeEvidence, jsonlPath } = freshEvidenceInputs({ agentType,
|
|
358
|
+
transcript: prepared?.transcript || null, codexRolloutPath: prepared?.codexRolloutPath || '' });
|
|
359
|
+
return await resolveSourceHistoryMode({ agentType, ctmSessionId: String(sessionId), jsonlPath, resumeId, resumeEvidence, resolveRung });
|
|
360
|
+
} catch {
|
|
361
|
+
// HANDLE: D-2 -- a missing session, an unreadable transcript or a failed ladder read is never
|
|
362
|
+
// evidence that the conversation is gone, so the switch keeps today's 'present' path.
|
|
363
|
+
return 'present';
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
|
|
315
368
|
function createLeadModelSwitchRuntime(host) {
|
|
316
369
|
const {
|
|
317
370
|
getSession, detectAgentType, lifecycleResumeTarget, extractResumeTarget,
|
|
@@ -335,6 +388,41 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
335
388
|
finalizeHistoryHandoff: typeof host.finalizeHistoryHandoff === 'function'
|
|
336
389
|
? host.finalizeHistoryHandoff : async () => ({ ok: false, reason: 'history_runtime_unavailable' }) };
|
|
337
390
|
|
|
391
|
+
// WP-0 D-1: an INJECTED resolver wins (tests drive the mode directly); otherwise it is composed
|
|
392
|
+
// here from the two park inputs the server already injects. A host with neither keeps the
|
|
393
|
+
// controller's own 'present' default rather than failing the switch.
|
|
394
|
+
const resolveSourceHistoryModeForSession = typeof host.resolveSourceHistoryMode === 'function'
|
|
395
|
+
? host.resolveSourceHistoryMode
|
|
396
|
+
: createSourceHistoryModeResolver({ getSession, detectAgentType, lifecycleResumeTarget,
|
|
397
|
+
prepareLifecycleTranscript: host.prepareLifecycleTranscript, resolveRung: host.resolveSourceRung });
|
|
398
|
+
|
|
399
|
+
// WP-2 D-6 (§5/§6): the captured launch argv a source lifetime was started with. `getSessionInfo`
|
|
400
|
+
// already exposes it as `launchContext`; a session with no capture has no argv to read.
|
|
401
|
+
function capturedLaunchArgs(session) {
|
|
402
|
+
const args = session?._modelTransitionLaunchContext?.args;
|
|
403
|
+
return Array.isArray(args) ? args : null;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// D-6: an argv that names a model of its own is never a CLI default. `--model=x` is the same flag
|
|
407
|
+
// as `--model x` for the Claude CLI, so both spellings are matched here (and `-m`).
|
|
408
|
+
function argvNamesModel(args) {
|
|
409
|
+
return args.some((arg) => {
|
|
410
|
+
const text = String(arg);
|
|
411
|
+
return text === '--model' || text === '-m' || text.startsWith('--model=');
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// D-6: CTM's own record of an explicit lead-model choice for this session (`session_lead_model`).
|
|
416
|
+
// The read is deliberately three-valued: a missing getter or a THROWN read is 'unknown', never
|
|
417
|
+
// 'absent' — "an unreadable store is not evidence that the conversation is gone" applied to the pin,
|
|
418
|
+
// so an unreadable row can never be read as proof of a CLI default (AC-2a: a pin READ ERROR records
|
|
419
|
+
// modelOrigin 'observed').
|
|
420
|
+
function leadModelPinEvidence(sessionId) {
|
|
421
|
+
if (!dbModule || typeof dbModule.getSessionLeadModel !== 'function') return 'unknown';
|
|
422
|
+
try { return dbModule.getSessionLeadModel(sessionId) ? 'present' : 'absent'; }
|
|
423
|
+
catch { return 'unknown'; } // REPORT: the caller reads 'unknown' as "a pin may exist", not as absence
|
|
424
|
+
}
|
|
425
|
+
|
|
338
426
|
function getSessionInfo(sessionId) {
|
|
339
427
|
const session = getSession(sessionId);
|
|
340
428
|
if (!session) return null;
|
|
@@ -350,17 +438,27 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
350
438
|
? session._modelProcessGeneration : Number(session._launchPlanGeneration || 0);
|
|
351
439
|
const executionRecipe = modelExecutionRecipeMetadata(session._modelExecutionRecipe,
|
|
352
440
|
{ sessionId, processGeneration: actualGeneration }).modelExecutionRecipe || null;
|
|
441
|
+
// §5 D-6: the projector names WHY an uncapturable environment is uncapturable, so the gate's refusal
|
|
442
|
+
// (WP-3) and the client sentence can say it. 'no_capture' is the only reason 'unknown' can mean.
|
|
353
443
|
const launchEnvironment = session._launchEnvOverridesCaptureState === 'unavailable'
|
|
354
|
-
? Object.freeze({ state: 'unavailable', values: null })
|
|
444
|
+
? Object.freeze({ state: 'unavailable', values: null, reason: session._launchEnvUnavailableReason || null })
|
|
355
445
|
: session._launchEnvOverridesCaptureState === 'unknown' || !Object.hasOwn(session, '_launchEnvOverrides')
|
|
356
|
-
? Object.freeze({ state: 'unknown', values: null })
|
|
446
|
+
? Object.freeze({ state: 'unknown', values: null, reason: 'no_capture' })
|
|
357
447
|
: session._launchEnvOverridesCaptureState === 'captured-empty'
|
|
358
|
-
? Object.freeze({ state: 'captured', values: Object.freeze({}) })
|
|
359
|
-
: capturePrivateSourceEnvironment(session._launchEnvOverrides);
|
|
448
|
+
? Object.freeze({ state: 'captured', values: Object.freeze({}), reason: null })
|
|
449
|
+
: Object.freeze({ ...capturePrivateSourceEnvironment(session._launchEnvOverrides), reason: null });
|
|
450
|
+
// D-6: the source model's ORIGIN, captured where the facts are. True only when every proof of a
|
|
451
|
+
// real model choice is absent: no session model_id, a captured argv that names no `--model`/`-m`
|
|
452
|
+
// (and must exist at all to be evidence), and no readable `session_lead_model` pin row.
|
|
453
|
+
const launchArgs = capturedLaunchArgs(session);
|
|
454
|
+
const cliDefaultModel = (typeof session.model_id !== 'string' || session.model_id === '')
|
|
455
|
+
&& launchArgs !== null && !argvNamesModel(launchArgs)
|
|
456
|
+
&& leadModelPinEvidence(sessionId) === 'absent';
|
|
360
457
|
return {
|
|
361
458
|
type: session.type || 'pty',
|
|
362
459
|
agent,
|
|
363
460
|
modelId: typeof session.model_id === 'string' ? session.model_id : '',
|
|
461
|
+
cliDefaultModel,
|
|
364
462
|
resumeId,
|
|
365
463
|
processGeneration: executionRecipe?.processGeneration || actualGeneration || 0,
|
|
366
464
|
providerThreadId: executionRecipe?.providerThreadId || resumeId || session.agentSessionId || session._claudeSessionId || session._providerResumeId || '',
|
|
@@ -488,6 +586,22 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
488
586
|
return await requestOwner('getSessionModelIntent', { ctmSessionId: String(sessionId || '') });
|
|
489
587
|
}
|
|
490
588
|
|
|
589
|
+
// WP-1 (§6.2): the ONE preflight, built once per runtime. It reuses the SAME readiness the
|
|
590
|
+
// resolver already reads (`host.leadRouteDeps?.readiness`, else the configured singleton) and the
|
|
591
|
+
// SAME proxy supervisor the launch path uses; `host.leadRouteDeps.preflight` is the test seam.
|
|
592
|
+
// The report is a fire-and-forget health hint for Wall-E's per-pair circuit — it never changes
|
|
593
|
+
// the switch (REPORT disposition inside the preflight).
|
|
594
|
+
const leadRoutePreflight = typeof host.leadRouteDeps?.preflight === 'function'
|
|
595
|
+
? host.leadRouteDeps.preflight
|
|
596
|
+
: createLeadRoutePreflight({
|
|
597
|
+
// Production injects no leadRouteDeps and uses the real supervisor. An injected leadRouteDeps
|
|
598
|
+
// (tests) without an `oauthProxySupervisor` probes nothing, so no test can start a real proxy.
|
|
599
|
+
getSupervisor: () => (host.leadRouteDeps ? host.leadRouteDeps.oauthProxySupervisor || null : require('./oauth-proxy-supervisor')),
|
|
600
|
+
getReadiness: () => host.leadRouteDeps?.readiness || getLeadRouteReadiness(),
|
|
601
|
+
report: (body) => requestWalleData('/api/models/route-health', { method: 'POST', body }, 5000),
|
|
602
|
+
diagnostic: recordSessionDiagnostic,
|
|
603
|
+
});
|
|
604
|
+
|
|
491
605
|
// D-2 (§6.5): the routed-lead resolver. Every refusal carries a SPECIFIC reason AND a specific
|
|
492
606
|
// sentence (never the generic "The target route is unavailable." that pointed at a healthy route).
|
|
493
607
|
// The service entry below is the ONE refusal-logging seam, so a refused switch is diagnosable from
|
|
@@ -508,9 +622,13 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
508
622
|
}
|
|
509
623
|
const readiness = host.leadRouteDeps?.readiness || getLeadRouteReadiness();
|
|
510
624
|
if (!readiness) return refusal('proxy_unconfigured');
|
|
625
|
+
// R-1/S-8: an OpenCode lead dials the claude wire -- the route must be servable to 'claude', and
|
|
626
|
+
// the harness profile below is the claude-code one. `agent` itself still decides the native
|
|
627
|
+
// shortcut above (isNativeLeadModel, codex's model/list), so Claude/Codex stay byte-identical.
|
|
628
|
+
const wire = leadWireAgent(agent);
|
|
511
629
|
let readinessResult;
|
|
512
630
|
try {
|
|
513
|
-
readinessResult = await readiness.resolveRoute({ route: routeId, model: modelId, agent, signal });
|
|
631
|
+
readinessResult = await readiness.resolveRoute({ route: routeId, model: modelId, agent: wire, signal });
|
|
514
632
|
} catch (err) {
|
|
515
633
|
return refusal(err && err.code ? err.code : 'route_invalid', String(err && err.message || err).slice(0, 200));
|
|
516
634
|
}
|
|
@@ -527,12 +645,21 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
527
645
|
const deployment = readinessResult?.deployment || null;
|
|
528
646
|
if (!deployment || readinessResult.metadataUnavailable) return refusal('model_metadata_unavailable');
|
|
529
647
|
if (!Number.isSafeInteger(deployment.contextTokens) || deployment.contextTokens <= 0) return refusal('model_context_unknown');
|
|
648
|
+
// WP-1 D-1: prove the pair can generate BEFORE anything touches the session. This runs after
|
|
649
|
+
// every existence/metadata check (so a probe is never spent on a pair that cannot be prepared
|
|
650
|
+
// anyway) and before `prepareLeadProfile`/park. Its refusal already has the
|
|
651
|
+
// `{ ok:false, reason, message }` shape the resolver returns everywhere else.
|
|
652
|
+
// §12a rule 1: probe the wire THIS harness will dial. Codex reads the Responses face, Claude (and
|
|
653
|
+
// an OpenCode lead, R-1) the Anthropic Messages face; without it the probe would dial its own
|
|
654
|
+
// preferred wire and could refuse a pair the lead's harness runs perfectly well.
|
|
655
|
+
const pre = await leadRoutePreflight({ routeId, modelId, sessionId, signal, face: wire === 'codex' ? 'responses' : 'anthropic' });
|
|
656
|
+
if (pre && pre.refusal) return pre.refusal;
|
|
530
657
|
let preparedProfile;
|
|
531
658
|
try {
|
|
532
659
|
if (typeof prepareLeadProfile !== 'function' || typeof getDataDir !== 'function') return refusal('model_profile_unavailable');
|
|
533
660
|
const session = getSession(sessionId);
|
|
534
661
|
preparedProfile = await prepareLeadProfile({
|
|
535
|
-
deployment, harness:
|
|
662
|
+
deployment, harness: wire === 'codex' ? 'codex-cli' : 'claude-code',
|
|
536
663
|
binaryPath: session?.cmd || '', adapterVersion: ADAPTER_VERSION,
|
|
537
664
|
routeId, providerId: readinessResult.providerId || routeId, wireModelId: modelId,
|
|
538
665
|
artifactRoot: path.join(getDataDir(), 'model-profiles'), signal,
|
|
@@ -561,40 +688,71 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
561
688
|
};
|
|
562
689
|
}
|
|
563
690
|
|
|
691
|
+
// The ONE park and the ONE restore, defined once. The journaled Claude/Codex machine and the
|
|
692
|
+
// OpenCode launch controller (WP-1) receive these EXACT lambdas: a relaunch has one implementation,
|
|
693
|
+
// so the private restore intent, the one-shot override and the deferred-restore handoff cannot
|
|
694
|
+
// drift between the two controllers.
|
|
695
|
+
const parkForLeadSwitch = (sessionId, opts) => parkSession(sessionId, opts);
|
|
696
|
+
const restoreForLeadSwitch = async (sessionId, opts) => {
|
|
697
|
+
const placeholder = getSession(sessionId);
|
|
698
|
+
// A wake already in flight (or a session that already woke) started WITHOUT the override:
|
|
699
|
+
// starting again would dedupe onto it and lose the flag, so report "not restarted on the
|
|
700
|
+
// requested model" instead of pretending.
|
|
701
|
+
if (!placeholder || placeholder._restoreDeferred !== true || placeholder._activationStartPromise) return false;
|
|
702
|
+
const restoreOptions = {
|
|
703
|
+
// Preserve the controller's trusted private restore intent across this wrapper. The
|
|
704
|
+
// server later normalizes activation priority but uses the original options to forward
|
|
705
|
+
// the non-enumerable transition claim into its exact target-owner authorization boundary.
|
|
706
|
+
source: opts.source === 'lead-model' && opts.leadModelTransition ? 'lead-model' : 'remote-activate',
|
|
707
|
+
clientKind: 'api',
|
|
708
|
+
launchModelOverride: opts.launchModelOverride,
|
|
709
|
+
launchEffortOverride: opts.launchEffortOverride,
|
|
710
|
+
// D-6: the routed launch plan rides this ONE restore (an object = routed, null = explicitly
|
|
711
|
+
// native so the stored route row does not re-apply). Absent = the stored row applies.
|
|
712
|
+
...(Object.hasOwn(opts, 'launchLeadRoute') ? { launchLeadRoute: opts.launchLeadRoute } : {}),
|
|
713
|
+
transitionSignal: opts.signal,
|
|
714
|
+
preparedLaunchPlan: opts.preparedLaunchPlan,
|
|
715
|
+
expectedProviderThreadId: opts.expectedProviderThreadId || null,
|
|
716
|
+
expectedSourceGeneration: opts.expectedSourceGeneration || null,
|
|
717
|
+
};
|
|
718
|
+
if (opts.leadModelTransition) Object.defineProperty(restoreOptions, 'leadModelTransition', {
|
|
719
|
+
value: opts.leadModelTransition, enumerable: false,
|
|
720
|
+
});
|
|
721
|
+
const started = await startDeferredRestoreSession(placeholder, restoreOptions);
|
|
722
|
+
return started === true;
|
|
723
|
+
};
|
|
724
|
+
|
|
725
|
+
// A routed lead switch is validated against the SAME readiness the delegate path uses (WP-1b):
|
|
726
|
+
// the route must resolve (exist/enabled/routable/servable) for this agent, and the model must be
|
|
727
|
+
// in Wall-E's coding catalog (that is where its display name and context window come from).
|
|
728
|
+
// D-2: the ONE refusal-logging seam. The specific reason reaches ctm.log, and the message it
|
|
729
|
+
// returns (never generic) is what the caller stores as the request's detail. It is a named lambda
|
|
730
|
+
// because the OpenCode launch adapter (WP-3) is handed the SAME one -- no second resolver.
|
|
731
|
+
const resolveLeadRoute = async (agent, routeId, modelId, sessionId, opts) => {
|
|
732
|
+
const v = await resolveLeadRouteChecked(agent, routeId, modelId, sessionId, opts);
|
|
733
|
+
if (!v.ok) {
|
|
734
|
+
process.stderr.write(`[lead-model] route refused session=${String(sessionId).slice(0, 8)} route=${routeId} model=${modelId} reason=${v.reason}\n`);
|
|
735
|
+
}
|
|
736
|
+
return v;
|
|
737
|
+
};
|
|
738
|
+
// WP-3: the session_lead_route reader/writer the journaled machine already uses. Named lambdas so
|
|
739
|
+
// the OpenCode launch adapter gets the EXACT same persistence -- one row, one agent encoding.
|
|
740
|
+
const getLeadRoute = (sessionId) => {
|
|
741
|
+
try { return dbModule.getSessionLeadRoute(sessionId); }
|
|
742
|
+
catch (error) { recordSessionDiagnostic(sessionId, 'lead-route-read-failed', { message: String(error && error.message || error).slice(0, 200) }); /* REPORT: treated as no stored route, so a native switch adds no explicit null */ return null; }
|
|
743
|
+
};
|
|
744
|
+
const persistLeadRoute = async (sessionId, agent, route) => {
|
|
745
|
+
if (route) return await dbModule.setSessionLeadRoute({ ctmSessionId: sessionId, agent, routeId: route.routeId, model: route.model, display: route.display, contextWindow: route.contextWindow, profileFingerprint: route.profileFingerprint, factsRevision: route.factsRevision, processGeneration: route.processGeneration, transitionId: route.transitionId });
|
|
746
|
+
return await dbModule.deleteSessionLeadRoute(sessionId);
|
|
747
|
+
};
|
|
748
|
+
|
|
564
749
|
const service = createLeadModelSwitch({
|
|
565
750
|
getSessionInfo,
|
|
566
751
|
isIdle,
|
|
567
752
|
captureActionContext,
|
|
568
753
|
readModelIntent,
|
|
569
|
-
park:
|
|
570
|
-
restore:
|
|
571
|
-
const placeholder = getSession(sessionId);
|
|
572
|
-
// A wake already in flight (or a session that already woke) started WITHOUT the override:
|
|
573
|
-
// starting again would dedupe onto it and lose the flag, so report "not restarted on the
|
|
574
|
-
// requested model" instead of pretending.
|
|
575
|
-
if (!placeholder || placeholder._restoreDeferred !== true || placeholder._activationStartPromise) return false;
|
|
576
|
-
const restoreOptions = {
|
|
577
|
-
// Preserve the controller's trusted private restore intent across this wrapper. The
|
|
578
|
-
// server later normalizes activation priority but uses the original options to forward
|
|
579
|
-
// the non-enumerable transition claim into its exact target-owner authorization boundary.
|
|
580
|
-
source: opts.source === 'lead-model' && opts.leadModelTransition ? 'lead-model' : 'remote-activate',
|
|
581
|
-
clientKind: 'api',
|
|
582
|
-
launchModelOverride: opts.launchModelOverride,
|
|
583
|
-
launchEffortOverride: opts.launchEffortOverride,
|
|
584
|
-
// D-6: the routed launch plan rides this ONE restore (an object = routed, null = explicitly
|
|
585
|
-
// native so the stored route row does not re-apply). Absent = the stored row applies.
|
|
586
|
-
...(Object.hasOwn(opts, 'launchLeadRoute') ? { launchLeadRoute: opts.launchLeadRoute } : {}),
|
|
587
|
-
transitionSignal: opts.signal,
|
|
588
|
-
preparedLaunchPlan: opts.preparedLaunchPlan,
|
|
589
|
-
expectedProviderThreadId: opts.expectedProviderThreadId || null,
|
|
590
|
-
expectedSourceGeneration: opts.expectedSourceGeneration || null,
|
|
591
|
-
};
|
|
592
|
-
if (opts.leadModelTransition) Object.defineProperty(restoreOptions, 'leadModelTransition', {
|
|
593
|
-
value: opts.leadModelTransition, enumerable: false,
|
|
594
|
-
});
|
|
595
|
-
const started = await startDeferredRestoreSession(placeholder, restoreOptions);
|
|
596
|
-
return started === true;
|
|
597
|
-
},
|
|
754
|
+
park: parkForLeadSwitch,
|
|
755
|
+
restore: restoreForLeadSwitch,
|
|
598
756
|
writeCodexDefault: (model) => writeCodexDefaultModel({ model }),
|
|
599
757
|
writeCodexDefaultEffort: (effort) => writeCodexDefaultEffort({ effort }),
|
|
600
758
|
// The efforts the Lead options row of the target model supports (a Claude model the rows do not
|
|
@@ -611,22 +769,13 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
611
769
|
if (effort) return await dbModule.setSessionLeadEffort(sessionId, agent, effort);
|
|
612
770
|
return await dbModule.deleteSessionLeadEffort(sessionId);
|
|
613
771
|
},
|
|
614
|
-
|
|
615
|
-
// the route must resolve (exist/enabled/routable/servable) for this agent, and the model must be
|
|
616
|
-
// in Wall-E's coding catalog (that is where its display name and context window come from).
|
|
617
|
-
// D-2: the ONE refusal-logging seam. The specific reason reaches ctm.log, and the message it
|
|
618
|
-
// returns (never generic) is what the caller stores as the request's detail.
|
|
619
|
-
resolveLeadRoute: async (agent, routeId, modelId, sessionId, opts) => {
|
|
620
|
-
const v = await resolveLeadRouteChecked(agent, routeId, modelId, sessionId, opts);
|
|
621
|
-
if (!v.ok) {
|
|
622
|
-
process.stderr.write(`[lead-model] route refused session=${String(sessionId).slice(0, 8)} route=${routeId} model=${modelId} reason=${v.reason}\n`);
|
|
623
|
-
}
|
|
624
|
-
return v;
|
|
625
|
-
},
|
|
772
|
+
resolveLeadRoute,
|
|
626
773
|
prepareHistory: historyRuntime.prepareHistoryTransition,
|
|
627
774
|
revalidateHistoryBeforePark: historyRuntime.revalidateHistoryBeforePark,
|
|
628
775
|
finalizeHistoryHandoff: historyRuntime.finalizeHistoryHandoff,
|
|
629
776
|
prepareTargetPlan: host.prepareTargetPlan,
|
|
777
|
+
// WP-0 D-1: resolved ONCE per request, before the claim, and journaled on both recipes.
|
|
778
|
+
resolveSourceHistoryMode: resolveSourceHistoryModeForSession,
|
|
630
779
|
stageTarget: host.stageLeadTarget || (async ({ sessionId, transitionId, journal, expectedRevision, source, targetPlan, route, signal }) => {
|
|
631
780
|
if (!journal || !Number.isSafeInteger(targetPlan?.generation) || !route?.preparedProfile) return { ok: false, reason: 'lead_transition_owner_unavailable' };
|
|
632
781
|
const session = getSession(sessionId);
|
|
@@ -641,14 +790,8 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
641
790
|
}, { ...(leadRouteDeps || {}), profileLoader: async () => route.preparedProfile });
|
|
642
791
|
return result.ok ? { ok: true, preparedLeadRoute: result } : result;
|
|
643
792
|
}),
|
|
644
|
-
getLeadRoute
|
|
645
|
-
|
|
646
|
-
catch (error) { recordSessionDiagnostic(sessionId, 'lead-route-read-failed', { message: String(error && error.message || error).slice(0, 200) }); /* REPORT: treated as no stored route, so a native switch adds no explicit null */ return null; }
|
|
647
|
-
},
|
|
648
|
-
persistLeadRoute: async (sessionId, agent, route) => {
|
|
649
|
-
if (route) return await dbModule.setSessionLeadRoute({ ctmSessionId: sessionId, agent, routeId: route.routeId, model: route.model, display: route.display, contextWindow: route.contextWindow, profileFingerprint: route.profileFingerprint, factsRevision: route.factsRevision, processGeneration: route.processGeneration, transitionId: route.transitionId });
|
|
650
|
-
return await dbModule.deleteSessionLeadRoute(sessionId);
|
|
651
|
-
},
|
|
793
|
+
getLeadRoute,
|
|
794
|
+
persistLeadRoute,
|
|
652
795
|
beginTransition: async ({ transitionId, sessionId, source, target, targetPlan, request, actionContext = null }) => {
|
|
653
796
|
// WP-2A: a NEW action always needs the durable journal. A runtime whose db module has none
|
|
654
797
|
// refuses the action outright rather than falling back to a direct effort/route write.
|
|
@@ -687,8 +830,16 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
687
830
|
const targetProfile = target.preparedProfile || null;
|
|
688
831
|
const sourceRecipeId = `${transitionId}:source`;
|
|
689
832
|
const targetRecipeId = `${transitionId}:target`;
|
|
833
|
+
// WP-0 D-1/S-2-S-3: ONE mode per transition, resolved by the controller BEFORE this claim and
|
|
834
|
+
// written on BOTH recipes. Absent reads as 'present' (the pre-change behaviour), so only a
|
|
835
|
+
// deliberately resolved 'none' ever takes the resume-less path.
|
|
836
|
+
const historyMode = recipeHistoryMode(source);
|
|
837
|
+
// WP-2 D-6: beside the mode, the ONE other fact rollback needs — where the source model came
|
|
838
|
+
// from. 'cli-default' is the only spelling that lets a resume-less recovery omit the one-shot
|
|
839
|
+
// model override; everything else is 'observed'.
|
|
840
|
+
const modelOrigin = sourceModelOrigin(source);
|
|
690
841
|
const sourceRecipe = {
|
|
691
|
-
id: sourceRecipeId, sessionId, providerThreadId: source.providerThreadId, harness,
|
|
842
|
+
id: sourceRecipeId, sessionId, providerThreadId: source.providerThreadId, harness, historyMode, modelOrigin,
|
|
692
843
|
providerId: source.routeId || source.modelExecutionRecipe?.routeId || source.modelProvider || 'unknown',
|
|
693
844
|
origin: source.routeId ? 'routed-proxy' : (source.agent === 'codex' ? 'native-codex' : 'native-claude'),
|
|
694
845
|
controlAdapterVersion: source.agent === 'codex' ? CODEX_CONTROL_ADAPTER : null,
|
|
@@ -707,12 +858,18 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
707
858
|
approvalPolicyReference: 'host:session-policy', compactHash: null,
|
|
708
859
|
};
|
|
709
860
|
const targetRecipe = {
|
|
710
|
-
id: targetRecipeId, sessionId, providerThreadId: source.providerThreadId, harness,
|
|
861
|
+
id: targetRecipeId, sessionId, providerThreadId: source.providerThreadId, harness, historyMode,
|
|
711
862
|
providerId: target.routeId || 'unknown', origin: target.routeId ? 'routed-proxy' : 'unknown',
|
|
712
863
|
// The marker names the Codex app-server conversion adapter. A Claude target -- routed or
|
|
713
864
|
// native -- has no conversion adapter at all, so recording the Codex one there would claim a
|
|
714
865
|
// conversion this operation never performs (same-harness native resume).
|
|
715
866
|
controlAdapterVersion: harness === 'codex-cli' && target.routeId ? CODEX_CONTROL_ADAPTER : null,
|
|
867
|
+
// WP-2 D-4: the versioned historical-tool filter receipt is a claim about THIS launch path
|
|
868
|
+
// only -- a server-built Codex target launched through the local authenticated CTM proxy
|
|
869
|
+
// route. A native/no-route target, a non-Codex target, a source recipe and any client input
|
|
870
|
+
// never carry it (the store validator rejects every value but null or the exact constant).
|
|
871
|
+
historyToolFilterReceipt: harness === 'codex-cli' && target.routeId
|
|
872
|
+
? CODEX_HISTORICAL_TOOLS_FILTER_RECEIPT : null,
|
|
716
873
|
programVersion: null, cyberAccessProgram: null,
|
|
717
874
|
routeId: target.routeId || null,
|
|
718
875
|
wireModelId: target.routeId ? (target.modelId || null) : null, processGeneration: targetPlan.generation,
|
|
@@ -778,7 +935,12 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
778
935
|
|| (source.agent === 'codex' && (!sourceContext.homeDir || !sourceContext.codexHome))) {
|
|
779
936
|
return { ok: false, reason: 'source_launch_context_unavailable' };
|
|
780
937
|
}
|
|
781
|
-
|
|
938
|
+
// WP-2 D-6 (S-7): WHERE this source's model came from decides how it is restored. An empty
|
|
939
|
+
// model_id is only legitimate for a source whose recipe recorded the explicit 'cli-default'
|
|
940
|
+
// origin (nothing was ever chosen, so the original launch argv IS the model); every other empty
|
|
941
|
+
// model stays today's `source_model_unknown` refusal.
|
|
942
|
+
const sourceOrigin = sourceModelOrigin(source);
|
|
943
|
+
if (!source?.modelId && sourceOrigin !== 'cli-default') return { ok: false, reason: 'source_model_unknown' };
|
|
782
944
|
let route = source?.leadRoute || null;
|
|
783
945
|
if (!route) {
|
|
784
946
|
try { route = dbModule.getSessionLeadRoute(sessionId) || null; } catch { route = null; }
|
|
@@ -795,8 +957,37 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
795
957
|
factsRevision: route.facts_revision || route.factsRevision || null,
|
|
796
958
|
providerThreadId: source.providerThreadId,
|
|
797
959
|
} : null;
|
|
960
|
+
// WP-2 D-5 (§5/§6, S-7): a 'none' source has nothing to resume, so a rollback relaunches the
|
|
961
|
+
// ORIGINAL captured argv instead of forcing `--resume` on a conversation that was never written.
|
|
962
|
+
// The mode is RE-READ here through the same host function WP-0 added, because the user may have
|
|
963
|
+
// typed while the switch was in flight (R-1): the moment a transcript exists, today's `--resume`
|
|
964
|
+
// path is the right one again. Any other answer -- including a thrown or non-committal one --
|
|
965
|
+
// keeps that path (D-2: an unreadable store is never evidence of absence).
|
|
966
|
+
let freshRelaunch = false;
|
|
967
|
+
if (recipeHistoryMode(source) === 'none') {
|
|
968
|
+
let modeNow = 'present';
|
|
969
|
+
try { modeNow = await resolveSourceHistoryModeForSession(sessionId); }
|
|
970
|
+
catch { modeNow = 'present'; } // HANDLE: D-2 -- a broken re-check is not proof the transcript is gone
|
|
971
|
+
freshRelaunch = modeNow === 'none';
|
|
972
|
+
}
|
|
798
973
|
let recoveryArgs = sourceContext.args;
|
|
799
|
-
if (
|
|
974
|
+
if (freshRelaunch) {
|
|
975
|
+
// The fresh argv is the launch args MINUS resume forms (lib/park-fresh-relaunch.js): the
|
|
976
|
+
// captured startup argv names its own thread through `--session-id`, which is exactly the
|
|
977
|
+
// identity this source owns, so no `--resume <thread>` is ever issued for it.
|
|
978
|
+
const fresh = freshRelaunchArgs({ agentType: source.agent,
|
|
979
|
+
storedTask: { cmd: sourceContext.executablePath, args: sourceContext.args },
|
|
980
|
+
sessionCmd: sourceContext.executablePath, sessionId, resumeId: source.providerThreadId });
|
|
981
|
+
if (!fresh) return { ok: false, reason: 'source_launch_context_unavailable' };
|
|
982
|
+
recoveryArgs = fresh.args;
|
|
983
|
+
// Asserted, not assumed: the fresh relaunch must hand the process the SOURCE's own thread. The
|
|
984
|
+
// captured argv normally already carries it from launch; when it does not, it is added here.
|
|
985
|
+
if (source.agent === 'claude') {
|
|
986
|
+
const argvThread = claudeSessionIdArg(recoveryArgs);
|
|
987
|
+
if (!argvThread) recoveryArgs = [...recoveryArgs, '--session-id', source.providerThreadId];
|
|
988
|
+
else if (argvThread !== sourceThreadId) return { ok: false, reason: 'source_launch_context_unavailable' };
|
|
989
|
+
}
|
|
990
|
+
} else if (source.agent === 'codex') {
|
|
800
991
|
const rebound = require('./launch-model-override').ensureCodexResumeTarget(recoveryArgs, source.providerThreadId);
|
|
801
992
|
if (!rebound.ok) return { ok: false, reason: rebound.reason };
|
|
802
993
|
recoveryArgs = rebound.args;
|
|
@@ -813,10 +1004,17 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
813
1004
|
recoveryArgs = rebound.args;
|
|
814
1005
|
}
|
|
815
1006
|
const selectedSourceThread = extractResumeTarget(source.agent, recoveryArgs)?.sessionId || '';
|
|
816
|
-
|
|
1007
|
+
// The fresh branch carries no resume selector AT ALL, so '' is its only acceptable answer; the
|
|
1008
|
+
// resume path keeps today's exact-source-thread requirement.
|
|
1009
|
+
if (selectedSourceThread !== (freshRelaunch ? '' : sourceThreadId)) {
|
|
1010
|
+
return { ok: false, reason: 'source_launch_context_unavailable' };
|
|
1011
|
+
}
|
|
817
1012
|
const restoreOptions = {
|
|
818
1013
|
source: 'remote-activate', clientKind: 'api',
|
|
819
|
-
|
|
1014
|
+
// D-5/D-6: a 'cli-default' source's rollback passes NO `launchModelOverride` key at all, so
|
|
1015
|
+
// the relaunched process lands on the CLI's own default by construction; every other source
|
|
1016
|
+
// keeps today's one-shot override.
|
|
1017
|
+
...(sourceOrigin === 'cli-default' ? {} : { launchModelOverride: source.modelId }),
|
|
820
1018
|
...(typeof source.effort === 'string' ? { launchEffortOverride: source.effort } : {}),
|
|
821
1019
|
launchLeadRoute,
|
|
822
1020
|
expectedProviderThreadId: source.providerThreadId,
|
|
@@ -834,7 +1032,8 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
834
1032
|
};
|
|
835
1033
|
if (!attachPrivateSourceRecoveryEnvironment(restoreOptions, { ctmSessionId: sessionId,
|
|
836
1034
|
agent: source.agent, modelId: source.modelId, providerThreadId: source.providerThreadId,
|
|
837
|
-
routeId: source.routeId || null, environment: source.launchEnvironment
|
|
1035
|
+
routeId: source.routeId || null, environment: source.launchEnvironment,
|
|
1036
|
+
modelOrigin: sourceOrigin, freshRelaunch })) {
|
|
838
1037
|
return { ok: false, reason: 'source_launch_environment_unavailable' };
|
|
839
1038
|
}
|
|
840
1039
|
const currentOwner = () => {
|
|
@@ -876,7 +1075,9 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
876
1075
|
const parkedFrom = session;
|
|
877
1076
|
const parked = await parkSession(sessionId, { manual: true, reason: 'model-transition-source-recovery',
|
|
878
1077
|
sourceGeneration: expectedAbandoned.processGeneration });
|
|
879
|
-
if (!parked || (parked.ok !== true && parked.alreadyParked !== true))
|
|
1078
|
+
if (!parked || (parked.ok !== true && parked.alreadyParked !== true)) {
|
|
1079
|
+
return { ok: false, reason: parked?.code || 'source_recovery_park_failed', protections: parked?.protections || [], retryAt: parked?.retryAt ?? null };
|
|
1080
|
+
}
|
|
880
1081
|
const placeholder = getSession(sessionId);
|
|
881
1082
|
if (!placeholder || placeholder._restoreDeferred !== true || placeholder.ptyProcess) {
|
|
882
1083
|
return { ok: false, reason: 'recovery_owner_changed_after_park' };
|
|
@@ -1022,14 +1223,90 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
1022
1223
|
diagnostic: recordSessionDiagnostic,
|
|
1023
1224
|
});
|
|
1024
1225
|
|
|
1226
|
+
// WP-1 (docs/opencode-lead-and-delegate-model-selection-2026-10-28.html §5 R-2..R-9, §6.3):
|
|
1227
|
+
// the never-prompted OpenCode 1.x switch is its own small controller (no owner tuple, no provider
|
|
1228
|
+
// thread, no journal). It is handed the SAME park/restore lambdas the journaled machine uses, so a
|
|
1229
|
+
// relaunch has one implementation. `opencodeVersion`/`listOpencodeModels` are overridable host DI in
|
|
1230
|
+
// the same way `listCodexModels` is (a test never spawns the real CLI).
|
|
1231
|
+
const opencodeLaunchModel = createOpencodeLaunchModel({
|
|
1232
|
+
getSessionInfo,
|
|
1233
|
+
getSession,
|
|
1234
|
+
opencodeVersion: typeof host.opencodeVersion === 'function' ? host.opencodeVersion : opencodeCli.opencodeVersion,
|
|
1235
|
+
listModels: typeof host.listOpencodeModels === 'function' ? host.listOpencodeModels : opencodeCli.opencodeCatalog,
|
|
1236
|
+
// WP-3 (S-9): the SAME shared pieces the journaled machine uses -- the one route resolver (D-2,
|
|
1237
|
+
// which logs refusals) and the one session_lead_route row reader/writer. A routed OpenCode target
|
|
1238
|
+
// is validated, persisted and re-adopted exactly like a Claude/Codex one; only the relaunch step
|
|
1239
|
+
// differs (R-3).
|
|
1240
|
+
resolveLeadRoute,
|
|
1241
|
+
getLeadRoute,
|
|
1242
|
+
persistLeadRoute,
|
|
1243
|
+
park: parkForLeadSwitch,
|
|
1244
|
+
restore: restoreForLeadSwitch,
|
|
1245
|
+
isIdle,
|
|
1246
|
+
broadcast,
|
|
1247
|
+
diagnostic: recordSessionDiagnostic,
|
|
1248
|
+
});
|
|
1249
|
+
|
|
1250
|
+
// WP-8 step 3 (docs/deepseek-harness-acp-agent-2026-10-28.html §6.7, §9b M-15): the DeepSeek
|
|
1251
|
+
// Harness live switch. dsh is an ACP child, so the switch is `session/set_config_option` on the
|
|
1252
|
+
// SAME child (one route) or one intentional stop + the existing restore path (another route) — no
|
|
1253
|
+
// park/argv machinery and no journal. Its public request shape is OpenCode's byte for byte, so the
|
|
1254
|
+
// picker renders both with one code path; `dshRuntime`/`dshSession` are the server's own instances.
|
|
1255
|
+
const dshLaunchModel = createDshLaunchModel({
|
|
1256
|
+
getSessionInfo,
|
|
1257
|
+
getSession,
|
|
1258
|
+
dshRuntime: host.dshRuntime,
|
|
1259
|
+
dshSession: host.dshSession,
|
|
1260
|
+
broadcast,
|
|
1261
|
+
diagnostic: recordSessionDiagnostic,
|
|
1262
|
+
});
|
|
1263
|
+
|
|
1264
|
+
// WP-6a: warm the spawn-free OpenCode line (package.json of the resolved binary) at boot, so the
|
|
1265
|
+
// synchronous launch path's peekOpencodeLine() already knows whether `--model` is safe to pass.
|
|
1266
|
+
if (host.warmOpencodeLine !== false) {
|
|
1267
|
+
void opencodeCli.opencodeLine().catch(() => { /* expected: an undetected line keeps today's launch args */ });
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
// One owner per agent: an OpenCode tab is answered by the launch controller, a dsh tab by the ACP
|
|
1271
|
+
// controller, everything else by the journaled machine, byte-for-byte unchanged.
|
|
1272
|
+
const isOpencodeSession = (sessionId) => {
|
|
1273
|
+
const info = getSessionInfo(String(sessionId || ''));
|
|
1274
|
+
return !!info && info.agent === 'opencode';
|
|
1275
|
+
};
|
|
1276
|
+
const isDshSession = (sessionId) => {
|
|
1277
|
+
const info = getSessionInfo(String(sessionId || ''));
|
|
1278
|
+
return !!info && info.agent === 'dsh';
|
|
1279
|
+
};
|
|
1280
|
+
const routeLeadModelCall = (name) => {
|
|
1281
|
+
const base = service[name].bind(service);
|
|
1282
|
+
return (sessionId, ...rest) => {
|
|
1283
|
+
if (isOpencodeSession(sessionId)) return opencodeLaunchModel[name](sessionId, ...rest);
|
|
1284
|
+
if (isDshSession(sessionId)) return dshLaunchModel[name](sessionId, ...rest);
|
|
1285
|
+
return base(sessionId, ...rest);
|
|
1286
|
+
};
|
|
1287
|
+
};
|
|
1288
|
+
service.request = routeLeadModelCall('request');
|
|
1289
|
+
service.get = routeLeadModelCall('get');
|
|
1290
|
+
service.cancel = routeLeadModelCall('cancel');
|
|
1291
|
+
|
|
1025
1292
|
leadModelObserver.attach({
|
|
1026
1293
|
wants: (sessionId) => service.wantsObservation(sessionId),
|
|
1027
|
-
observe: (sessionId, obs) =>
|
|
1294
|
+
observe: (sessionId, obs) => {
|
|
1295
|
+
const verdict = service.observe(sessionId, obs);
|
|
1296
|
+
// OpenCode settles from the session's own `opencode:message` observation fields (R-7); the
|
|
1297
|
+
// lead-model observer knows only Claude/Codex turn sources, so the controller is told directly.
|
|
1298
|
+
opencodeLaunchModel.notifyObservation(sessionId);
|
|
1299
|
+
return verdict;
|
|
1300
|
+
},
|
|
1028
1301
|
observeSelection: (sessionId, obs) => service.observeSelection(sessionId, obs),
|
|
1029
1302
|
});
|
|
1030
1303
|
|
|
1031
1304
|
statusBus.on('verdict', (ev) => {
|
|
1032
|
-
if (ev && ev.sessionId && ev.status !== 'running' && ev.status !== 'stalled')
|
|
1305
|
+
if (ev && ev.sessionId && ev.status !== 'running' && ev.status !== 'stalled') {
|
|
1306
|
+
void service.notifyStatus(ev.sessionId);
|
|
1307
|
+
// A turn that just ended is when an OpenCode observation lands: settle without waiting for a poll.
|
|
1308
|
+
opencodeLaunchModel.notifyObservation(ev.sessionId);
|
|
1309
|
+
}
|
|
1033
1310
|
});
|
|
1034
1311
|
|
|
1035
1312
|
// The Lead tab's rows (GET /api/sessions/:id/lead-model/options): what this CLI can switch to.
|
|
@@ -1041,8 +1318,11 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
1041
1318
|
getSessionInfo,
|
|
1042
1319
|
loadCatalog: () => loadCatalogItems(),
|
|
1043
1320
|
listCodexModels: typeof host.listCodexModels === 'function' ? host.listCodexModels : () => listCodexModels(),
|
|
1321
|
+
...(typeof host.listOpencodeModels === 'function' ? { listOpencodeModels: host.listOpencodeModels } : {}),
|
|
1044
1322
|
...(typeof host.readClaudeSettings === 'function' ? { readClaudeSettings: host.readClaudeSettings } : {}),
|
|
1045
1323
|
...(typeof host.readCodexConfigToml === 'function' ? { readCodexConfigToml: host.readCodexConfigToml } : {}),
|
|
1324
|
+
// WP-8 step 4: a dsh tab's in-route rows and efforts come from its live ACP child's configOptions.
|
|
1325
|
+
getDshConfigOptions: (id) => { const live = host.dshRuntime && host.dshRuntime.get(id); return live && Array.isArray(live.configOptions) ? live.configOptions : null; },
|
|
1046
1326
|
});
|
|
1047
1327
|
// D-7 (picker design WP-4): Wall-E's registry-changed notify routes here, so the Lead tab never
|
|
1048
1328
|
// serves a catalog that predates the change.
|
|
@@ -1096,9 +1376,13 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
1096
1376
|
return superseded();
|
|
1097
1377
|
}
|
|
1098
1378
|
if (!intent.ok) return { status: intent.status, body: { ok: false, code: intent.code } };
|
|
1099
|
-
|
|
1379
|
+
const body = { ...out.body, stored_effort: stored, lead_route: leadRoute,
|
|
1100
1380
|
expected_owner: capture.owner, intent_revision: intent.revision, intent: intent.intent,
|
|
1101
|
-
action_state: capture.owner ? 'ready' : 'owner_not_ready' }
|
|
1381
|
+
action_state: capture.owner ? 'ready' : 'owner_not_ready' };
|
|
1382
|
+
// WP-1 (§6.2): ONE added field, for an OpenCode tab only. Every existing field stays exactly as
|
|
1383
|
+
// it is for Claude/Codex, so no client has to learn a second response shape.
|
|
1384
|
+
if (isOpencodeSession(sessionId)) body.opencode = await opencodeLaunchModel.sessionMode(sessionId);
|
|
1385
|
+
return { status: out.status, body };
|
|
1102
1386
|
}
|
|
1103
1387
|
};
|
|
1104
1388
|
|
|
@@ -1107,4 +1391,4 @@ function createLeadModelSwitchRuntime(host) {
|
|
|
1107
1391
|
|
|
1108
1392
|
// WP-5 (F-1): `refusal` is exported so its own fallback contract (never an inherited
|
|
1109
1393
|
// Object.prototype member) has a direct unit test; it is the same function every route refusal uses.
|
|
1110
|
-
module.exports = { createLeadModelSwitchRuntime, createLeadTransitionTargetPlanner, ROUTE_REFUSAL_MESSAGES, refusal };
|
|
1394
|
+
module.exports = { createLeadModelSwitchRuntime, createLeadTransitionTargetPlanner, createSourceHistoryModeResolver, ROUTE_REFUSAL_MESSAGES, refusal };
|