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
|
@@ -20,8 +20,11 @@
|
|
|
20
20
|
|
|
21
21
|
const crypto = require('crypto');
|
|
22
22
|
const path = require('path');
|
|
23
|
+
const url = require('node:url');
|
|
23
24
|
|
|
24
25
|
const { WORKER_MODEL_ALIAS } = require('../model-proxy/lead-worker-picks');
|
|
26
|
+
const { compareSemver } = require('../semver-compare');
|
|
27
|
+
const { CODEX_NATIVE_SUBAGENT_DENY_MIN_VERSION } = require('../codex-harness-qualification');
|
|
25
28
|
// The ONE literal effort grammar CTM accepts for a launch effort (same validator the lead-launch
|
|
26
29
|
// override and the launch-truth store use), so a malformed value can never be projected as known.
|
|
27
30
|
const { isValidEffort } = require('../launch-model-override');
|
|
@@ -34,12 +37,17 @@ const { isValidEffort } = require('../launch-model-override');
|
|
|
34
37
|
const SUBAGENT_PICK_READ_TIMEOUT_MS = 1500;
|
|
35
38
|
|
|
36
39
|
const LANES = new Set(['native', 'routed', 'inherit']);
|
|
37
|
-
const AGENTS = new Set(['claude', 'codex']);
|
|
40
|
+
const AGENTS = new Set(['claude', 'codex', 'opencode']);
|
|
38
41
|
// D-5: a route resolution failure with one of these codes is a first-class 'unservable' pick
|
|
39
42
|
// (surfaced in the picker, WP-6), never a silent fall-back to another route or model.
|
|
40
43
|
const UNSERVABLE_ROUTE_CODES = new Set([
|
|
41
44
|
'route_unknown', 'route_disabled', 'route_not_routable', 'route_not_servable',
|
|
42
45
|
]);
|
|
46
|
+
// R-1/R-3 (subagent-pick-unreachable-origin-2026-10-28.html): a route that resolves fine here can
|
|
47
|
+
// still be dialed from THIS machine only if its upstream origin answers. These are the two probe
|
|
48
|
+
// verdicts that mean "no request from this host can succeed", so the pick is refused up front
|
|
49
|
+
// instead of handing every native subagent an HTTP 502 at its first request.
|
|
50
|
+
const ORIGIN_UNREACHABLE_REACH = new Set(['unreachable', 'no_server']);
|
|
43
51
|
|
|
44
52
|
// Codex's built-in subagent role texts, copied VERBATIM from
|
|
45
53
|
// codex-rs/core/src/agent/role.rs:349-381 @ 322bbf4d848; re-copy on Codex bumps.
|
|
@@ -165,8 +173,12 @@ function _originFields(origin, mirroredAt) {
|
|
|
165
173
|
}
|
|
166
174
|
|
|
167
175
|
/**
|
|
168
|
-
* @param {{ ctmSessionId?: string, agent?: 'claude'|'codex', leadRoute?: object|null }} input
|
|
169
|
-
* @param {{ walleClient?: object, readiness?: object, mirror?: object, setTimeout?: Function, clearTimeout?: Function }} deps
|
|
176
|
+
* @param {{ ctmSessionId?: string, agent?: 'claude'|'codex'|'opencode', leadRoute?: object|null }} input
|
|
177
|
+
* @param {{ walleClient?: object, readiness?: object, mirror?: object, setTimeout?: Function, clearTimeout?: Function, originVerdict?: Function }} deps
|
|
178
|
+
* `originVerdict` is OPTIONAL: the credential-less upstream-origin probe
|
|
179
|
+
* (lib/model-availability.js `originVerdict`) consulted for a ROUTED pick, so a route whose origin
|
|
180
|
+
* cannot be reached from this machine is refused as `origin_unreachable` rather than launched
|
|
181
|
+
* into a 502.
|
|
170
182
|
* `mirror` is OPTIONAL (lib/storage/delegate-pref-mirror-store.js). When present, a successful read
|
|
171
183
|
* backfills it from the read's ladder[] and a FAILED read folds it (M-1/M-2a) instead of giving up.
|
|
172
184
|
* @returns {Promise<{kind:'none', reason:'vendor'|'read_failed'|'not_lead', detail?: string}
|
|
@@ -221,6 +233,13 @@ async function resolveSubagentPick(input, deps) {
|
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
// The lane gate above already dropped everything else, so this is 'routed' (D-4/D-5/D-11).
|
|
236
|
+
if (agent === 'opencode') {
|
|
237
|
+
// §6.5/§9 S-20 (opencode-lead-and-delegate-model-selection-2026-10-28.html): an OpenCode lead
|
|
238
|
+
// reaches a routed worker through the Wall-E MCP server, not through CTM's lead proxy -- the
|
|
239
|
+
// routed lane is WP-5. Until it lands this is a typed, non-fallback refusal, so a saved routed
|
|
240
|
+
// pick is visibly unserved rather than silently projected as something the lead cannot carry.
|
|
241
|
+
return { kind: 'unservable', agent, reason: 'opencode_routed_requires_mcp', source, ..._originFields(origin, mirroredAt) };
|
|
242
|
+
}
|
|
224
243
|
if (agent === 'codex' && !args.leadRoute) {
|
|
225
244
|
// D-11: checked BEFORE readiness -- the lead's transport is what makes this unservable, and
|
|
226
245
|
// there is no point resolving a route we can never reach.
|
|
@@ -254,6 +273,25 @@ async function resolveSubagentPick(input, deps) {
|
|
|
254
273
|
|| metadataRevision.length > 256 || /[\u0000-\u001f\u007f]/.test(metadataRevision)) {
|
|
255
274
|
return { kind: 'unservable', agent, reason: 'model_metadata_unavailable', source, ..._originFields(origin, mirroredAt) };
|
|
256
275
|
}
|
|
276
|
+
// R-1/R-3/R-4: the route exists, but it is only servable if THIS machine can dial the
|
|
277
|
+
// upstream origin. Same cached, single-flighted probe buildAvailability uses -- no second
|
|
278
|
+
// probe path, and never a header off the route row.
|
|
279
|
+
const upstreamOrigin = require('../model-availability').originOf(resolved.baseUrl);
|
|
280
|
+
if (upstreamOrigin) {
|
|
281
|
+
const verdictFn = typeof d.originVerdict === 'function'
|
|
282
|
+
? d.originVerdict
|
|
283
|
+
: require('../model-availability').originVerdict;
|
|
284
|
+
let verdict = null;
|
|
285
|
+
try {
|
|
286
|
+
verdict = await verdictFn(upstreamOrigin);
|
|
287
|
+
} catch (err) {
|
|
288
|
+
/* REPORT: a probe failure is "no gate" (R-3), never a refusal. */
|
|
289
|
+
process.stderr.write('[subagent-pick] origin verdict failed: ' + _detail(err) + '\n');
|
|
290
|
+
}
|
|
291
|
+
if (verdict && ORIGIN_UNREACHABLE_REACH.has(verdict.reach)) {
|
|
292
|
+
return { kind: 'unservable', agent, reason: 'origin_unreachable', source, ..._originFields(origin, mirroredAt) };
|
|
293
|
+
}
|
|
294
|
+
}
|
|
257
295
|
return {
|
|
258
296
|
kind: 'routed',
|
|
259
297
|
agent,
|
|
@@ -326,22 +364,397 @@ function _codexPlan(model, effort, dataDir) {
|
|
|
326
364
|
codexArgs,
|
|
327
365
|
leadProxy: null,
|
|
328
366
|
roleFile: { path: rolePath, contents: _roleFileBody(model, effort) },
|
|
367
|
+
nativeSubagents: '',
|
|
329
368
|
};
|
|
330
369
|
}
|
|
331
370
|
|
|
371
|
+
// WP-1 (codex-native-subagent-pick-enforcement-2026-10-28.html §5 D-2, §10.1): the two ROOT config
|
|
372
|
+
// overrides that make an injected `collaboration.spawn_agent` call answer `unsupported call` on the
|
|
373
|
+
// installed Codex. NOT `--disable multi_agent[_v2]`: for a model whose catalog entry declares
|
|
374
|
+
// `multi_agent_version: "v2"` (every GPT-6 model, §1.4) the session resolves to V2 and the flag form
|
|
375
|
+
// is bypassed. `agents.enabled=false` alone is overridden when `features.multi_agent_v2` is on, so
|
|
376
|
+
// BOTH tokens ship together. The worker deny tokens (lib/session-create/worker-launch-sandbox.js,
|
|
377
|
+
// codexWorkerDenyArgs) are a separate, unchanged mechanism (OQ-1).
|
|
378
|
+
const CODEX_NATIVE_SUBAGENT_DENY_ARGS = Object.freeze([
|
|
379
|
+
'-c', 'agents.enabled=false',
|
|
380
|
+
'-c', 'features.multi_agent_v2.disable_native_tools=true',
|
|
381
|
+
]);
|
|
382
|
+
|
|
383
|
+
// WP-1b: the deny prefix's own version floor lives with every other Codex version fact
|
|
384
|
+
// (lib/codex-harness-qualification.js, CODEX_NATIVE_SUBAGENT_DENY_MIN_VERSION); see D-2b.
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* D-3. True when a Codex lead's native children cannot be redirected to the stored ROUTED pick, so
|
|
388
|
+
* the launch must unregister the native multi-agent tools. An `inherit` pick means "same as the
|
|
389
|
+
* lead", which a native child already satisfies, so nothing is denied; every Claude pick is env-only.
|
|
390
|
+
* PURE.
|
|
391
|
+
* @param {{kind?: string, agent?: string, reason?: string}|null} pick resolveSubagentPick() result
|
|
392
|
+
* @returns {boolean}
|
|
393
|
+
*/
|
|
394
|
+
function nativeSubagentsDenied(pick) {
|
|
395
|
+
return !!pick && pick.kind === 'unservable' && pick.agent === 'codex'
|
|
396
|
+
&& pick.reason !== 'codex_inherit_unenforceable';
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// R-6/§6.5 (§5 R-10): OpenCode's per-launch carrier. CTM NEVER writes OpenCode's user config or
|
|
400
|
+
// state -- the pick rides `OPENCODE_CONFIG_CONTENT`, the inline runtime config OpenCode merges OVER
|
|
401
|
+
// its file config for this process only, as an agent-config model for OpenCode's own built-in
|
|
402
|
+
// subagents (`general`, `explore`). PURE, apart from reading the string it is handed.
|
|
403
|
+
//
|
|
404
|
+
// native name the pick's model on agent.general.model and agent.explore.model
|
|
405
|
+
// inherit OpenCode's own default IS "a subagent follows the model of the primary agent that
|
|
406
|
+
// invoked it", so nothing is emitted at all -- and, decisively, no existing
|
|
407
|
+
// OPENCODE_CONFIG_CONTENT in the launch env is rewritten on the way past
|
|
408
|
+
//
|
|
409
|
+
// `existingConfigContent` is the launch env's current OPENCODE_CONFIG_CONTENT, if any. It is parsed
|
|
410
|
+
// as a JSON object and EVERY other key is carried through verbatim. A value that is not a JSON
|
|
411
|
+
// object (unparseable, null, an array, a bare scalar) is replaced by `{}` and reported in `applied`
|
|
412
|
+
// as `existing_config_unparseable`, so a caller can see that something was discarded rather than
|
|
413
|
+
// silently losing it.
|
|
414
|
+
function _asConfigObject(value) {
|
|
415
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : {};
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
function opencodeLaunchProjection({ pick, existingConfigContent } = {}) {
|
|
419
|
+
if (!pick || pick.kind !== 'native') return { env: {}, applied: [] };
|
|
420
|
+
const applied = [];
|
|
421
|
+
const raw = existingConfigContent === undefined || existingConfigContent === null ? '' : String(existingConfigContent);
|
|
422
|
+
let config = {};
|
|
423
|
+
if (raw.trim() !== '') {
|
|
424
|
+
let parsed = null;
|
|
425
|
+
try {
|
|
426
|
+
parsed = JSON.parse(raw);
|
|
427
|
+
} catch {
|
|
428
|
+
parsed = null; // expected: an unparseable existing value is replaced and reported below
|
|
429
|
+
}
|
|
430
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) config = parsed;
|
|
431
|
+
else applied.push('existing_config_unparseable');
|
|
432
|
+
}
|
|
433
|
+
const model = String(pick.model || '');
|
|
434
|
+
const currentAgent = _asConfigObject(config.agent);
|
|
435
|
+
const next = {
|
|
436
|
+
...config,
|
|
437
|
+
agent: {
|
|
438
|
+
...currentAgent,
|
|
439
|
+
general: { ..._asConfigObject(currentAgent.general), model },
|
|
440
|
+
explore: { ..._asConfigObject(currentAgent.explore), model },
|
|
441
|
+
},
|
|
442
|
+
};
|
|
443
|
+
applied.push('agent.general.model', 'agent.explore.model');
|
|
444
|
+
return { env: { OPENCODE_CONFIG_CONTENT: JSON.stringify(next) }, applied };
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// §5 R-6 / §9 S-21 (opencode-lead-and-delegate-model-selection-2026-10-28.html §6.5, WP-5): the
|
|
448
|
+
// Wall-E MCP server for an OpenCode LEAD, carried inline for THIS launch only.
|
|
449
|
+
//
|
|
450
|
+
// R-6 is absolute: CTM NEVER writes `~/.config/opencode/*`. OpenCode merges `OPENCODE_CONFIG_CONTENT`
|
|
451
|
+
// OVER its file config for this process only, so the server is injected by extending the very same
|
|
452
|
+
// inline document an OpenCode pick already rides (see opencodeLaunchProjection). OpenCode's `mcp`
|
|
453
|
+
// entries are LOCAL (stdio) servers: { type: 'local', command: [executable, ...args], enabled: true }.
|
|
454
|
+
//
|
|
455
|
+
// The entry itself (`walleMcp`) is the canonical stdio-proxy command resolved by Wall-E's own
|
|
456
|
+
// mcp-integration (`{ command, args }`) -- never a hand-built path (see
|
|
457
|
+
// CTM lib/walle-mcp-auto-config.js's opencodeWalleMcpEntry). `command` is the resolved real node
|
|
458
|
+
// binary and `args` is the walle-mcp-stdio.js script; CTM_SESSION_ID / WALL_E_API_TOKEN already ride
|
|
459
|
+
// the lead's own env, so nothing else has to be injected here.
|
|
460
|
+
//
|
|
461
|
+
// PURE: no fs, no env reads. `existingConfigContent` is parsed as a JSON object and EVERY other key
|
|
462
|
+
// is carried through verbatim -- including the other servers of an existing `mcp` map. A value that
|
|
463
|
+
// is not a JSON object (unparseable, null, an array, a bare scalar) is replaced by `{}` and REPORTED
|
|
464
|
+
// to stderr (the same `existing_config_unparseable` fact opencodeLaunchProjection exposes in its
|
|
465
|
+
// `applied` list), so a caller can see that something was discarded rather than silently losing it.
|
|
466
|
+
// `walleMcp` null/absent -> null: nothing to add, and the caller keeps whatever it already had.
|
|
467
|
+
function opencodeWalleMcpProjection({ existingConfigContent, walleMcp } = {}) {
|
|
468
|
+
if (!walleMcp) return null;
|
|
469
|
+
const command = [walleMcp.command, ...(Array.isArray(walleMcp.args) ? walleMcp.args : [])];
|
|
470
|
+
const raw = existingConfigContent === undefined || existingConfigContent === null ? '' : String(existingConfigContent);
|
|
471
|
+
let config = {};
|
|
472
|
+
if (raw.trim() !== '') {
|
|
473
|
+
let parsed = null;
|
|
474
|
+
try {
|
|
475
|
+
parsed = JSON.parse(raw);
|
|
476
|
+
} catch {
|
|
477
|
+
parsed = null; // expected: an unparseable existing value is replaced and reported below
|
|
478
|
+
}
|
|
479
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) config = parsed;
|
|
480
|
+
else {
|
|
481
|
+
/* REPORT: the launch's existing inline config could not be parsed and is being discarded. */
|
|
482
|
+
process.stderr.write('[subagent-pick] existing OPENCODE_CONFIG_CONTENT discarded (existing_config_unparseable)\n');
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
const next = {
|
|
486
|
+
...config,
|
|
487
|
+
mcp: {
|
|
488
|
+
..._asConfigObject(config.mcp),
|
|
489
|
+
'wall-e': { type: 'local', command, enabled: true },
|
|
490
|
+
},
|
|
491
|
+
};
|
|
492
|
+
return JSON.stringify(next);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// WP-2 (opencode-model-selection-round-2-shared-route-logic-2026-10-30.html §5 R-3/R-6, §6.2, §9
|
|
496
|
+
// S-7): a routed OpenCode LEAD's `ctm-lead` provider entry rides the SAME inline
|
|
497
|
+
// OPENCODE_CONFIG_CONTENT document the subagent pick and the Wall-E MCP server ride -- ONE writer,
|
|
498
|
+
// one blob, never ~/.config/opencode/*. `configPatch` is prepareLeadRoute's §6.2 patch
|
|
499
|
+
// (`{ provider: { 'ctm-lead': opencodeLeadProvider(...) }, model: 'ctm-lead/lead' }`).
|
|
500
|
+
//
|
|
501
|
+
// PURE: no fs, no env reads. Same parse/carry-through/report rules as opencodeWalleMcpProjection
|
|
502
|
+
// above: every other key is carried through verbatim, `provider` is DEEP-merged so a launch that
|
|
503
|
+
// already holds other providers keeps them, and the top-level `model` ref is set (a stable ref,
|
|
504
|
+
// R-4 -- the route/wire model live inside the provider entry, not the ref). A value that is not a
|
|
505
|
+
// JSON object (unparseable, null, an array, a bare scalar) is replaced by `{}` and REPORTED to
|
|
506
|
+
// stderr. `configPatch` null/absent -> null: nothing to add, and the caller keeps what it had.
|
|
507
|
+
function opencodeLeadRouteProjection({ existingConfigContent, configPatch } = {}) {
|
|
508
|
+
if (!configPatch) return null;
|
|
509
|
+
const raw = existingConfigContent === undefined || existingConfigContent === null ? '' : String(existingConfigContent);
|
|
510
|
+
let config = {};
|
|
511
|
+
if (raw.trim() !== '') {
|
|
512
|
+
let parsed = null;
|
|
513
|
+
try {
|
|
514
|
+
parsed = JSON.parse(raw);
|
|
515
|
+
} catch {
|
|
516
|
+
parsed = null; // expected: an unparseable existing value is replaced and reported below
|
|
517
|
+
}
|
|
518
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) config = parsed;
|
|
519
|
+
else {
|
|
520
|
+
/* REPORT: the launch's existing inline config could not be parsed and is being discarded. */
|
|
521
|
+
process.stderr.write('[subagent-pick] existing OPENCODE_CONFIG_CONTENT discarded (existing_config_unparseable)\n');
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const next = {
|
|
525
|
+
...config,
|
|
526
|
+
provider: {
|
|
527
|
+
..._asConfigObject(config.provider),
|
|
528
|
+
..._asConfigObject(configPatch.provider),
|
|
529
|
+
},
|
|
530
|
+
model: configPatch.model,
|
|
531
|
+
};
|
|
532
|
+
return JSON.stringify(next);
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// WP-5 (opencode-v2-model-switch-standalone-plugin-2026-10-30.html §5 R-9, §5.2 D-9/D-10/D-11, §6.7):
|
|
536
|
+
// the OpenCode 1.x MODEL CARRIER, added to the launch's inline `plugin` array for the ONE switch
|
|
537
|
+
// relaunch. 1.x plugin entries are just strings, so an entry is a `file://` URL of a repo file and the
|
|
538
|
+
// target rides the spawn env instead (D-10: no installer change, no `~/.config/opencode` write, R-6).
|
|
539
|
+
//
|
|
540
|
+
// The URL is derived from THIS module's location, so it points at the checkout CTM is running from.
|
|
541
|
+
const OPENCODE_V1_CARRIER_URL = url.pathToFileURL(
|
|
542
|
+
path.join(__dirname, '..', 'opencode-v1-plugin', 'model-carrier.mjs'),
|
|
543
|
+
).href;
|
|
544
|
+
|
|
545
|
+
// PURE: no fs, no env reads. Same parse/carry-through/report rules as opencodeLeadRouteProjection
|
|
546
|
+
// above: every other key of the inline document is carried through verbatim, and a value that is not
|
|
547
|
+
// a JSON object (unparseable, null, an array, a bare scalar) is replaced by `{}` and REPORTED to
|
|
548
|
+
// stderr. `plugin` is the existing array MINUS any entry equal to the carrier URL, PLUS the carrier
|
|
549
|
+
// URL — so two calls leave exactly one carrier entry and any other plugin entry survives. A non-array
|
|
550
|
+
// `plugin` is replaced by `[]` and reported the same way. Always returns a JSON string.
|
|
551
|
+
function opencodeV1CarrierProjection({ existingConfigContent } = {}) {
|
|
552
|
+
const raw = existingConfigContent === undefined || existingConfigContent === null ? '' : String(existingConfigContent);
|
|
553
|
+
let config = {};
|
|
554
|
+
if (raw.trim() !== '') {
|
|
555
|
+
let parsed = null;
|
|
556
|
+
try {
|
|
557
|
+
parsed = JSON.parse(raw);
|
|
558
|
+
} catch {
|
|
559
|
+
parsed = null; // expected: an unparseable existing value is replaced and reported below
|
|
560
|
+
}
|
|
561
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) config = parsed;
|
|
562
|
+
else {
|
|
563
|
+
/* REPORT: the launch's existing inline config could not be parsed and is being discarded. */
|
|
564
|
+
process.stderr.write('[subagent-pick] existing OPENCODE_CONFIG_CONTENT discarded (existing_config_unparseable)\n');
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
const existingPlugins = Array.isArray(config.plugin) ? config.plugin : null;
|
|
568
|
+
if (existingPlugins === null && config.plugin !== undefined) {
|
|
569
|
+
/* REPORT: a non-array plugin list is discarded so the carrier entry has somewhere to live. */
|
|
570
|
+
process.stderr.write('[subagent-pick] existing OpenCode plugin list discarded (plugin_not_array)\n');
|
|
571
|
+
}
|
|
572
|
+
const next = {
|
|
573
|
+
...config,
|
|
574
|
+
plugin: [...(existingPlugins || []).filter((entry) => entry !== OPENCODE_V1_CARRIER_URL), OPENCODE_V1_CARRIER_URL],
|
|
575
|
+
};
|
|
576
|
+
return JSON.stringify(next);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// PURE: the OpenCode session id a RESTORE resumes, read from the launch argv. The value after
|
|
580
|
+
// `--session` / `-s`, or after `--session=`. '' when there is none (e.g. `--continue`/`-c`, or a fresh
|
|
581
|
+
// launch) — which is exactly the "no carrier" decision the spawn path makes.
|
|
582
|
+
function opencodeResumeSessionId(args) {
|
|
583
|
+
const list = Array.isArray(args) ? args : [];
|
|
584
|
+
for (let i = 0; i < list.length; i += 1) {
|
|
585
|
+
const arg = String(list[i] == null ? '' : list[i]);
|
|
586
|
+
if (arg.startsWith('--session=')) return arg.slice('--session='.length);
|
|
587
|
+
if (arg === '--session' || arg === '-s') {
|
|
588
|
+
const next = list[i + 1];
|
|
589
|
+
return next == null ? '' : String(next);
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
return '';
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
// WP-5 (opencode-v2-model-switch-standalone-plugin-2026-10-30.html §5.1 R-9, §6.7): the PURE decision
|
|
596
|
+
// for the OpenCode 1.x model carrier — a prompted v1 tab switches by park + resume relaunch, and the
|
|
597
|
+
// carrier rides the ONE relaunch's inline config + env. Nothing is injected for a fresh tab (its
|
|
598
|
+
// `--model` argv already works), a worker, a v2/unreadable binary, or a restore with no session id.
|
|
599
|
+
// Returns `spawnEnv` UNCHANGED (same object) unless a carrier was armed, so a caller compares identity.
|
|
600
|
+
function opencodeV1CarrierEnv({ agentType, isRestore, agentRole, line, nativeModel, launchLeadRoute, launchArgs, spawnEnv }) {
|
|
601
|
+
if (!spawnEnv || agentType !== 'opencode' || isRestore !== true || agentRole === 'worker' || line !== 'v1') return spawnEnv;
|
|
602
|
+
const target = typeof nativeModel === 'string' && nativeModel
|
|
603
|
+
? nativeModel
|
|
604
|
+
: (launchLeadRoute && typeof launchLeadRoute === 'object' ? require('./opencode-lead-provider').OPENCODE_LEAD_MODEL_REF : '');
|
|
605
|
+
const sid = opencodeResumeSessionId(launchArgs);
|
|
606
|
+
if (!target || !sid) return spawnEnv;
|
|
607
|
+
return {
|
|
608
|
+
...spawnEnv,
|
|
609
|
+
OPENCODE_CONFIG_CONTENT: opencodeV1CarrierProjection({ existingConfigContent: spawnEnv.OPENCODE_CONFIG_CONTENT }),
|
|
610
|
+
CTM_OPENCODE_SWITCH_MODEL: target,
|
|
611
|
+
CTM_OPENCODE_SWITCH_SESSION: sid,
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// WP-2 (opencode-v2-model-switch-standalone-plugin-2026-10-30.html §5.1 R-3, §5.2 D-4/D-6, §6.2): the
|
|
616
|
+
// OpenCode 2.x CTM plugin, carried inline in OPENCODE_CONFIG_CONTENT. 2.x plugin entries are objects
|
|
617
|
+
// `{ package, options }` and the package must be an absolute DIRECTORY (a single file path is skipped,
|
|
618
|
+
// P-S7), so the entry points at this repo directory and OpenCode loads its `server.js`. Nothing is
|
|
619
|
+
// written under ~/.config/opencode (R-6): there is no install step and upgrades ship with CTM.
|
|
620
|
+
const OPENCODE_V2_PLUGIN_DIR = path.join(__dirname, '..', 'opencode-v2-plugin');
|
|
621
|
+
|
|
622
|
+
// PURE: no fs, no env reads. Same parse/carry-through/report rules as opencodeV1CarrierProjection
|
|
623
|
+
// above: every other key of the inline document survives verbatim, and a value that is not a JSON
|
|
624
|
+
// object (unparseable, null, an array, a bare scalar) is replaced by `{}` and REPORTED to stderr.
|
|
625
|
+
// `plugins` is the existing array MINUS any entry whose package is this plugin dir, PLUS one
|
|
626
|
+
// `{ package: OPENCODE_V2_PLUGIN_DIR, options }` — so two calls leave exactly one entry and any other
|
|
627
|
+
// plugin entry survives. A non-array `plugins` is replaced by `[]` and reported the same way. When the
|
|
628
|
+
// caller passes `options.switch_model` (WP-4), the inline `model` is set too (R-4, D-4). Always
|
|
629
|
+
// returns a JSON string.
|
|
630
|
+
function opencodeV2PluginProjection({ existingConfigContent, options } = {}) {
|
|
631
|
+
const raw = existingConfigContent === undefined || existingConfigContent === null ? '' : String(existingConfigContent);
|
|
632
|
+
let config = {};
|
|
633
|
+
if (raw.trim() !== '') {
|
|
634
|
+
let parsed = null;
|
|
635
|
+
try {
|
|
636
|
+
parsed = JSON.parse(raw);
|
|
637
|
+
} catch {
|
|
638
|
+
parsed = null; // expected: an unparseable existing value is replaced and reported below
|
|
639
|
+
}
|
|
640
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) config = parsed;
|
|
641
|
+
else {
|
|
642
|
+
/* REPORT: the launch's existing inline config could not be parsed and is being discarded. */
|
|
643
|
+
process.stderr.write('[subagent-pick] existing OPENCODE_CONFIG_CONTENT discarded (existing_config_unparseable)\n');
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
const existingPlugins = Array.isArray(config.plugins) ? config.plugins : null;
|
|
647
|
+
if (existingPlugins === null && config.plugins !== undefined) {
|
|
648
|
+
/* REPORT: a non-array plugin list is discarded so the CTM plugin entry has somewhere to live. */
|
|
649
|
+
process.stderr.write('[subagent-pick] existing OpenCode plugins list discarded (plugins_not_array)\n');
|
|
650
|
+
}
|
|
651
|
+
const kept = (existingPlugins || []).filter((entry) => !(entry && typeof entry === 'object' && entry.package === OPENCODE_V2_PLUGIN_DIR));
|
|
652
|
+
const next = { ...config, plugins: [...kept, { package: OPENCODE_V2_PLUGIN_DIR, options }] };
|
|
653
|
+
const sw = options && typeof options === 'object' ? options.switch_model : null;
|
|
654
|
+
if (sw && typeof sw === 'object') next.model = String(sw.model && sw.model.providerID) + '/' + String(sw.model && sw.model.id);
|
|
655
|
+
return JSON.stringify(next);
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// PURE: the switch target of a v2 RESTORE launch, derived EXACTLY like opencodeV1CarrierEnv's target:
|
|
659
|
+
// a native `provider/model` pick wins, else a routed relaunch's fixed ref ctm-lead/lead, else ''. ''
|
|
660
|
+
// means "no switch" -- a fresh launch, a worker, or a restore with no target at all.
|
|
661
|
+
function opencodeV2SwitchTarget({ isRestore, nativeModel, launchLeadRoute } = {}) {
|
|
662
|
+
if (isRestore !== true) return '';
|
|
663
|
+
if (typeof nativeModel === 'string' && nativeModel) return nativeModel;
|
|
664
|
+
if (launchLeadRoute && typeof launchLeadRoute === 'object') return require('./opencode-lead-provider').OPENCODE_LEAD_MODEL_REF;
|
|
665
|
+
return '';
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
// PURE: split a target ref at the FIRST '/' into the 2.x plugin's `switch_model` argument (R-4). null
|
|
669
|
+
// for a missing ref, no '/', an empty side, or an empty session id -- the cases where nothing may be
|
|
670
|
+
// switched (an unqualified target simply carries no `switch_model`, it is never guessed).
|
|
671
|
+
function opencodeV2SwitchModel(target, sessionID) {
|
|
672
|
+
if (typeof target !== 'string' || !target) return null;
|
|
673
|
+
const sid = String(sessionID == null ? '' : sessionID);
|
|
674
|
+
if (!sid) return null;
|
|
675
|
+
const at = target.indexOf('/');
|
|
676
|
+
if (at <= 0 || at === target.length - 1) return null;
|
|
677
|
+
const providerID = target.slice(0, at);
|
|
678
|
+
const id = target.slice(at + 1);
|
|
679
|
+
if (!providerID || !id) return null;
|
|
680
|
+
return { sessionID: sid, model: { providerID, id } };
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
// PURE: the `switch_model` a v2 plugin env carries (or null), read from the SAME inline blob OpenCode
|
|
684
|
+
// receives -- so the spawn diagnostic can never disagree with what the plugin was handed.
|
|
685
|
+
function opencodeV2SwitchModelOf(spawnEnv) {
|
|
686
|
+
if (!spawnEnv) return null;
|
|
687
|
+
let config = null;
|
|
688
|
+
try {
|
|
689
|
+
config = JSON.parse(String(spawnEnv.OPENCODE_CONFIG_CONTENT || ''));
|
|
690
|
+
} catch {
|
|
691
|
+
/* expected: a blob that is not JSON is one the projection never wrote, so it carries no switch */
|
|
692
|
+
return null;
|
|
693
|
+
}
|
|
694
|
+
const entries = config && typeof config === 'object' && Array.isArray(config.plugins) ? config.plugins : [];
|
|
695
|
+
const mine = entries.find((e) => e && typeof e === 'object' && e.package === OPENCODE_V2_PLUGIN_DIR);
|
|
696
|
+
const sw = mine && mine.options && typeof mine.options === 'object' ? mine.options.switch_model : null;
|
|
697
|
+
return sw && typeof sw === 'object' ? sw : null;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
// PURE: the OpenCode 2.x spawn-path decision, the v2 twin of opencodeV1CarrierEnv above. Every v2 LEAD
|
|
701
|
+
// launch carries the plugin, so unlike the 1.x carrier this is not gated on a restore. Returns
|
|
702
|
+
// `spawnEnv` UNCHANGED (same object) unless agentType==='opencode', agentRole!=='worker', line==='v2'
|
|
703
|
+
// and the launch argv names a session id (WP-1 puts `--session ses_…` on every v2 launch); then
|
|
704
|
+
// `{ ...spawnEnv, OPENCODE_CONFIG_CONTENT: projection }` carrying identity from the same spawn env.
|
|
705
|
+
// WP-4 (R-4): a RESTORE also carries `switch_model` (native target, else ctm-lead/lead) -- the project-
|
|
706
|
+
// ion then sets the inline `model` too, so a never-prompted tab is seeded and an existing one switched.
|
|
707
|
+
function opencodeV2PluginEnv({ agentType, isRestore, agentRole, line, nativeModel, launchLeadRoute, launchArgs, spawnEnv }) {
|
|
708
|
+
if (!spawnEnv || agentType !== 'opencode' || agentRole === 'worker' || line !== 'v2') return spawnEnv;
|
|
709
|
+
const sessionId = opencodeResumeSessionId(launchArgs);
|
|
710
|
+
if (!sessionId) return spawnEnv;
|
|
711
|
+
const options = {
|
|
712
|
+
ctm_port: String(spawnEnv.CTM_PORT || ''),
|
|
713
|
+
ctm_session_id: String(spawnEnv.CTM_SESSION_ID || ''),
|
|
714
|
+
process_generation: String(spawnEnv.CTM_MODEL_PROCESS_GENERATION || ''),
|
|
715
|
+
session_id: sessionId,
|
|
716
|
+
};
|
|
717
|
+
const switchModel = opencodeV2SwitchModel(opencodeV2SwitchTarget({ isRestore, nativeModel, launchLeadRoute }), sessionId);
|
|
718
|
+
if (switchModel) options.switch_model = switchModel;
|
|
719
|
+
return { ...spawnEnv, OPENCODE_CONFIG_CONTENT: opencodeV2PluginProjection({ existingConfigContent: spawnEnv.OPENCODE_CONFIG_CONTENT, options }) };
|
|
720
|
+
}
|
|
721
|
+
|
|
332
722
|
/**
|
|
333
723
|
* PURE: no fs, no env reads. Same input -> same output.
|
|
334
|
-
* @param {{ kind: string, agent?: 'claude'|'codex', model?: string, effort?: string }|null} pick
|
|
335
|
-
* @param {{ agent?: 'claude'|'codex', leadRoute?: object|null, dataDir?: string
|
|
724
|
+
* @param {{ kind: string, agent?: 'claude'|'codex'|'opencode', model?: string, effort?: string }|null} pick
|
|
725
|
+
* @param {{ agent?: 'claude'|'codex'|'opencode', leadRoute?: object|null, dataDir?: string, codexVersion?: string,
|
|
726
|
+
* existingConfigContent?: string }} options
|
|
727
|
+
* `codexVersion` is the version of the Codex binary THIS launch spawns (''/absent = unknown). The
|
|
728
|
+
* deny prefix carries a config key that older Codex builds cannot load, so it is applied only at or
|
|
729
|
+
* above CODEX_NATIVE_SUBAGENT_DENY_MIN_VERSION; an unknown version never denies.
|
|
336
730
|
* @returns {{ env: Record<string,string>, deleteEnv: string[], codexArgs: string[],
|
|
337
|
-
* leadProxy: null|{alias:string}, roleFile: null|{path:string, contents:string}
|
|
731
|
+
* leadProxy: null|{alias:string}, roleFile: null|{path:string, contents:string},
|
|
732
|
+
* nativeSubagents: ''|'disabled' }}
|
|
338
733
|
*/
|
|
339
734
|
function planSubagentLaunch(pick, options) {
|
|
340
735
|
const opts = options || {};
|
|
341
|
-
//
|
|
342
|
-
//
|
|
736
|
+
// WP-1 (§5 D-2/D-3): a Codex pick that resolves `unservable` for any reason OTHER than
|
|
737
|
+
// `codex_inherit_unenforceable` is a ROUTED stored lane native children cannot reach, so this
|
|
738
|
+
// launch refuses them: the deny prefix rides the SAME `codexArgs` prepend as a native role file
|
|
739
|
+
// (D-5), with no env, no role file and no lead proxy. WP-1b: only when this launch's own Codex is
|
|
740
|
+
// new enough to know the config key. Unknown = not denied, so the compare's `null` (an unparseable
|
|
741
|
+
// side) is excluded EXPLICITLY: `null >= 0` is true in JavaScript (relational comparison coerces
|
|
742
|
+
// null to 0), so relying on the bare `>=` would deny every unprobed launch.
|
|
743
|
+
const codexVersionRank = compareSemver(opts.codexVersion, CODEX_NATIVE_SUBAGENT_DENY_MIN_VERSION);
|
|
744
|
+
if (nativeSubagentsDenied(pick) && codexVersionRank !== null && codexVersionRank >= 0) {
|
|
745
|
+
return {
|
|
746
|
+
env: {},
|
|
747
|
+
deleteEnv: [],
|
|
748
|
+
codexArgs: [...CODEX_NATIVE_SUBAGENT_DENY_ARGS],
|
|
749
|
+
leadProxy: null,
|
|
750
|
+
roleFile: null,
|
|
751
|
+
nativeSubagents: 'disabled',
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
// kind 'none' | `codex_inherit_unenforceable` | any Claude unservable | a missing pick is the
|
|
755
|
+
// byte-identical baseline: zero env keys, zero argv items, no role file (D-1).
|
|
343
756
|
if (!pick || (pick.kind !== 'native' && pick.kind !== 'inherit' && pick.kind !== 'routed')) {
|
|
344
|
-
return { env: {}, deleteEnv: [], codexArgs: [], leadProxy: null, roleFile: null };
|
|
757
|
+
return { env: {}, deleteEnv: [], codexArgs: [], leadProxy: null, roleFile: null, nativeSubagents: '' };
|
|
345
758
|
}
|
|
346
759
|
|
|
347
760
|
const agent = pick.agent || opts.agent;
|
|
@@ -355,6 +768,14 @@ function planSubagentLaunch(pick, options) {
|
|
|
355
768
|
return _codexPlan(model, effort, opts.dataDir);
|
|
356
769
|
}
|
|
357
770
|
|
|
771
|
+
if (agent === 'opencode') {
|
|
772
|
+
// §5 R-10/§6.5: the OpenCode lane is per-launch inline config only. A native pick names the
|
|
773
|
+
// model on OpenCode's two built-in subagents; an inherit pick emits nothing (see
|
|
774
|
+
// opencodeLaunchProjection). No role file, no argv, no lead proxy.
|
|
775
|
+
const projection = opencodeLaunchProjection({ pick, existingConfigContent: opts.existingConfigContent });
|
|
776
|
+
return { env: projection.env, deleteEnv: [], codexArgs: [], leadProxy: null, roleFile: null, nativeSubagents: '' };
|
|
777
|
+
}
|
|
778
|
+
|
|
358
779
|
// D-2/D-3/D-4: the Claude side is env-only. FORCE is what defeats per-call models, Explore and
|
|
359
780
|
// frontmatter (C5-C7); without it a pick would be advisory only.
|
|
360
781
|
if (pick.kind === 'inherit') {
|
|
@@ -364,6 +785,7 @@ function planSubagentLaunch(pick, options) {
|
|
|
364
785
|
codexArgs: [],
|
|
365
786
|
leadProxy: null,
|
|
366
787
|
roleFile: null,
|
|
788
|
+
nativeSubagents: '',
|
|
367
789
|
};
|
|
368
790
|
}
|
|
369
791
|
if (pick.kind === 'native') {
|
|
@@ -373,6 +795,7 @@ function planSubagentLaunch(pick, options) {
|
|
|
373
795
|
codexArgs: [],
|
|
374
796
|
leadProxy: null,
|
|
375
797
|
roleFile: null,
|
|
798
|
+
nativeSubagents: '',
|
|
376
799
|
};
|
|
377
800
|
}
|
|
378
801
|
|
|
@@ -383,6 +806,7 @@ function planSubagentLaunch(pick, options) {
|
|
|
383
806
|
codexArgs: [],
|
|
384
807
|
leadProxy: null,
|
|
385
808
|
roleFile: null,
|
|
809
|
+
nativeSubagents: '',
|
|
386
810
|
};
|
|
387
811
|
if (!opts.leadRoute) {
|
|
388
812
|
// D-4: the lead is not on the proxy, so the launcher has to put it on one. It registers the
|
|
@@ -444,6 +868,11 @@ function projectSubagentPick(subagentPick) {
|
|
|
444
868
|
source: String(subagentPick.source || ''),
|
|
445
869
|
origin: String(subagentPick.origin || ''),
|
|
446
870
|
...(subagentPick.reason ? { reason: subagentPick.reason } : {}),
|
|
871
|
+
// WP-1 (§5 D-4): the fact the LAUNCH recorded, never a re-derivation from today's rule. The
|
|
872
|
+
// key is additive and only present when this launch actually carried the deny ('' is the
|
|
873
|
+
// separate "known not denied" state, null/absent = unknown) -- a session launched before the
|
|
874
|
+
// deny existed must never be described as denied.
|
|
875
|
+
...(typeof subagentPick.nativeSubagents === 'string' ? { native_subagents: subagentPick.nativeSubagents } : {}),
|
|
447
876
|
};
|
|
448
877
|
}
|
|
449
878
|
return subagentPick.reason && subagentPick.reason !== 'vendor'
|
|
@@ -467,6 +896,10 @@ function projectLaunchRow(row) {
|
|
|
467
896
|
source: String(row.source || ''),
|
|
468
897
|
origin: String(row.origin || ''),
|
|
469
898
|
...(row.reason ? { reason: row.reason } : {}),
|
|
899
|
+
// WP-1 (§5 D-4, v58): what the persisted launch DID about native sub-agents. null = a pre-v58
|
|
900
|
+
// row (unknown), '' = a known "not denied", 'disabled' = this launch carried the deny prefix.
|
|
901
|
+
// A malformed stored value never claims a deny -- the WRITE validates, the read does not trust.
|
|
902
|
+
native_subagents: row.native_subagents === '' || row.native_subagents === 'disabled' ? row.native_subagents : null,
|
|
470
903
|
};
|
|
471
904
|
}
|
|
472
905
|
|
|
@@ -474,6 +907,22 @@ module.exports = {
|
|
|
474
907
|
resolveSubagentPick,
|
|
475
908
|
reconcileMirrorDefaults,
|
|
476
909
|
planSubagentLaunch,
|
|
910
|
+
nativeSubagentsDenied,
|
|
911
|
+
CODEX_NATIVE_SUBAGENT_DENY_ARGS,
|
|
912
|
+
CODEX_NATIVE_SUBAGENT_DENY_MIN_VERSION,
|
|
913
|
+
opencodeLaunchProjection,
|
|
914
|
+
opencodeWalleMcpProjection,
|
|
915
|
+
opencodeLeadRouteProjection,
|
|
916
|
+
opencodeV1CarrierProjection,
|
|
917
|
+
opencodeResumeSessionId,
|
|
918
|
+
opencodeV1CarrierEnv,
|
|
919
|
+
OPENCODE_V1_CARRIER_URL,
|
|
920
|
+
opencodeV2PluginProjection,
|
|
921
|
+
opencodeV2PluginEnv,
|
|
922
|
+
opencodeV2SwitchModel,
|
|
923
|
+
opencodeV2SwitchTarget,
|
|
924
|
+
opencodeV2SwitchModelOf,
|
|
925
|
+
OPENCODE_V2_PLUGIN_DIR,
|
|
477
926
|
appliedNativeEffort,
|
|
478
927
|
projectSubagentPick,
|
|
479
928
|
projectLaunchRow,
|
|
@@ -148,7 +148,7 @@ async function createWalleSession(state) {
|
|
|
148
148
|
deps.sendWalleSessionHistory(ws, session, { source: 'walle-resume' })
|
|
149
149
|
.catch((error) => deps.sendWalleHistoryErrorFrame(ws, id, {}, error));
|
|
150
150
|
}
|
|
151
|
-
deps.broadcastSessionList(true);
|
|
151
|
+
deps.broadcastSessionList(true, { reason: 'walle-session-created' });
|
|
152
152
|
if (initialMessage) {
|
|
153
153
|
setImmediate(() => {
|
|
154
154
|
deps.handleWalleMessage(ws, {
|
|
@@ -10,14 +10,24 @@ function isProxiedRecipe(recipe) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// Copy the proxy's journaled stop record into the worker row, so every later status read sees it.
|
|
13
|
+
// C7-B2-P1 (docs/worker-deferred-stop-record-generation-2026-10-27.html §5 D1/D3, §6): the copy is
|
|
14
|
+
// fenced by the numeric generation this call was HANDED -- the one the exit path read BEFORE the awaited
|
|
15
|
+
// journal read -- against the row's existing `Number(row.generation) || 1` interpretation. A row that is
|
|
16
|
+
// absent, or whose number moved while the read was in flight, is observed only: no merge and no write.
|
|
17
|
+
// This is a fence across one await on a numeric column, not a claim of physical launch identity and not
|
|
18
|
+
// a claim that a numeric generation can never be reused (D4).
|
|
13
19
|
async function persistStopRecord(db, ctmSessionId, generation) {
|
|
14
20
|
const record = await readStopRecord({ ctmSessionId, generation });
|
|
15
21
|
if (!record) return null;
|
|
16
22
|
try {
|
|
17
23
|
const row = db.getWorkerSession(ctmSessionId);
|
|
24
|
+
// Observation only: nothing is copied into a row this call cannot confirm still owns `generation`.
|
|
25
|
+
if (!row || (Number(row.generation) || 1) !== generation) return record;
|
|
18
26
|
let runtime = {};
|
|
19
27
|
try { runtime = JSON.parse(row.runtime_json) || {}; } catch { /* expected: a malformed runtime row is treated as empty */ }
|
|
20
|
-
|
|
28
|
+
// The merge is unchanged; the captured generation travels beside the patch as the existing helper's
|
|
29
|
+
// storage predicate, so a row that advanced since the read above matches zero rows.
|
|
30
|
+
db.updateWorkerSession(ctmSessionId, { runtime_json: JSON.stringify({ ...runtime, requestBudget: { ...(runtime.requestBudget || {}), stop: record, terminalCode: record.kind } }) }, { expectedGeneration: generation });
|
|
21
31
|
} catch (error) {
|
|
22
32
|
console.warn(`[worker-budget-setup] stop record not persisted (${String(error && error.code || 'persist_failed').slice(0, 48)})`);
|
|
23
33
|
}
|
|
@@ -46,11 +56,20 @@ function initialSnapshot(generation, policy, capabilityRevision = '') {
|
|
|
46
56
|
}
|
|
47
57
|
|
|
48
58
|
// Proxied: persist the journaled stop (if any), then run(); proxyless (or no readable row): run() synchronously, as before.
|
|
59
|
+
// C7-B2-P1 (§5 D2/D3, §6): the numeric generation read BEFORE the await is retained across it and handed
|
|
60
|
+
// to run(), which never re-derives its target from a post-await status or runtime. The post-await recheck
|
|
61
|
+
// sits INSIDE the existing catch boundary (it is part of the returned chain), so an unreadable current row
|
|
62
|
+
// reports on the existing path and no callback runs -- including when the journal read returned null.
|
|
49
63
|
function retireAfterStopRecord(getDb, ctmSessionId, readRecipe, run) {
|
|
50
64
|
let db = null, row = null;
|
|
51
65
|
try { db = getDb(); row = db.getWorkerSession(ctmSessionId); } catch { row = null; /* expected: an unreadable row is handled as proxyless */ }
|
|
52
66
|
if (!row || !isProxiedRecipe(readRecipe(row))) return run();
|
|
53
|
-
|
|
67
|
+
const generation = Number(row.generation) || 1;
|
|
68
|
+
return persistStopRecord(db, ctmSessionId, generation).then(() => {
|
|
69
|
+
const current = db.getWorkerSession(ctmSessionId);
|
|
70
|
+
if (!current || (Number(current.generation) || 1) !== generation) return;
|
|
71
|
+
return run(generation);
|
|
72
|
+
})
|
|
54
73
|
.catch((error) => { console.warn(`[worker-budget-setup] retire after exit failed (${String(error && error.code || 'retire_failed').slice(0, 48)})`); /* REPORT: the row stays un-stamped; the boot reload retries */ });
|
|
55
74
|
}
|
|
56
75
|
|
|
@@ -128,7 +128,7 @@ function _validRecipe(row) {
|
|
|
128
128
|
const agent = String(row.agent || recipe.agent || '');
|
|
129
129
|
const route = String(row.route_alias || recipe.route || '');
|
|
130
130
|
const model = String(row.model || recipe.model || '');
|
|
131
|
-
if (!['claude', 'codex'].includes(agent) || !route || !model) return null;
|
|
131
|
+
if (!['claude', 'codex', 'dsh'].includes(agent) || !route || !model) return null;
|
|
132
132
|
if (recipe.route && recipe.route !== route) return null;
|
|
133
133
|
if (recipe.model && recipe.model !== model) return null;
|
|
134
134
|
return { ...recipe, briefPath: recipe.renderedBriefPath || recipe.briefPath, agent, route, model };
|
|
@@ -326,6 +326,12 @@ function createWorkerContinuation({ getDeps, admission = admissionDefault, helpe
|
|
|
326
326
|
|
|
327
327
|
const recipe = _validRecipe(row);
|
|
328
328
|
if (!recipe) return blocked('worker_recipe_missing', 'The durable worker launch recipe is unavailable.', { ctm_session_id: id });
|
|
329
|
+
// WP-14 (deepseek-harness-acp-agent-2026-10-28 D-21): a dsh worker is an ACP child, and this path
|
|
330
|
+
// relaunches an argv CLI against its provider transcript. Until ACP session/resume is wired here,
|
|
331
|
+
// refuse explicitly instead of launching the wrong process.
|
|
332
|
+
if (recipe.agent === 'dsh') {
|
|
333
|
+
return blocked('worker_resume_unsupported', 'A DeepSeek Harness worker cannot be resumed yet; delegate a new worker with the correction.', { ctm_session_id: id, retryable: false });
|
|
334
|
+
}
|
|
329
335
|
const oldProfile = recipe.workerProfile || 'legacy';
|
|
330
336
|
// D-11 (routed-worker-visibility-and-review-workers-2026-10-16.html): a review recipe is
|
|
331
337
|
// capability-pinned exactly like an implementation one.
|