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
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// WP-13 (D-15): the canonical agent resolver, used by `remoteCreateAgentKey` below so a DECLARED
|
|
4
|
+
// `agentType` is classified through the same table server.js's create path resolves the CLI with
|
|
5
|
+
// (aliases such as 'deepseek-harness'/'deepseek_harness'/'claude-code' included). agent-capabilities.js
|
|
6
|
+
// is a leaf module (it requires nothing), so this edge adds no cycle to the lib/** graph.
|
|
7
|
+
const { normalizeAgentType } = require('./agent-capabilities');
|
|
8
|
+
|
|
3
9
|
// WP-2 (docs/remote-machine-hub-sessions-2026-10-22.html §5 D-6/D-10, §6.3, §9 S-1/S-2/S-26):
|
|
4
10
|
// hub-side remote create. A `create` frame that names a REGISTERED REMOTE machine is never
|
|
5
11
|
// prepared locally — it is filtered through the §6.3 allowlist and forwarded to that box over a
|
|
@@ -64,8 +70,23 @@ const REMOTE_CREATE_FORWARD_ALLOWLIST = Object.freeze([
|
|
|
64
70
|
'chatSessionId',
|
|
65
71
|
]);
|
|
66
72
|
|
|
73
|
+
// WP-3 (docs/session-create-accept-then-complete-2026-11-01.html §6.3 D-3/D-4): PHASE A — dial to
|
|
74
|
+
// the box's `create-accepted` (or to a final `created`/`error`). Unchanged.
|
|
67
75
|
const DEFAULT_TIMEOUT_MS = 15000;
|
|
68
76
|
|
|
77
|
+
// WP-3 (D-4): PHASE B — `create-accepted` to `created`. The hub must never give up before the box
|
|
78
|
+
// legitimately could, so this ceiling is derived from the BOX's own bounded create phases (97.5 s,
|
|
79
|
+
// §1.1 of the design doc) and is the same 120 s as the box-local create-intent lease
|
|
80
|
+
// (lib/create-intent-lease.js CREATE_INTENT_LEASE_MS) and the push's BACKGROUND_CEILING_MS
|
|
81
|
+
// (lib/agent-bundle/push.js). tests/remote-create-ceiling-covers-box.test.js fails if a box-side
|
|
82
|
+
// bound is raised without raising this one with it.
|
|
83
|
+
const CREATE_ACCEPTED_CEILING_MS = 120000;
|
|
84
|
+
|
|
85
|
+
// WP-3 (D-5): an accepted create that then goes quiet is NOT a failure — the box is still starting
|
|
86
|
+
// the session and the hub never cancels it (D-6). It gets its own code so the client can show a
|
|
87
|
+
// warning and keep the pending create instead of a machine error.
|
|
88
|
+
const MACHINE_CREATE_UNCONFIRMED = 'machine_create_unconfirmed';
|
|
89
|
+
|
|
69
90
|
// WP-1 (D-2): a missing peer credential is a CONFIGURATION error, not an unreachable box. The box
|
|
70
91
|
// may be perfectly reachable — the hub simply has nothing to authenticate with, and no retry can
|
|
71
92
|
// change that (env is read at process start). Every hub→box lane reports this one code, so an
|
|
@@ -108,6 +129,21 @@ function _productionDeps() {
|
|
|
108
129
|
db: require('../db-owner-client'),
|
|
109
130
|
registry: require('./machine-registry'),
|
|
110
131
|
parseSyncPeers: require('./hosted-sync-worker').parseSyncPeers,
|
|
132
|
+
// WP-3 §6.5: the agent-skill push (D-4/D-6). Required lazily like everything else here so a
|
|
133
|
+
// caller that only wants the pure filter never loads the bundle builder.
|
|
134
|
+
pushAgentBundle: (machine, options) => require('./agent-bundle/push').pushBundleToBox(machine, options),
|
|
135
|
+
// The plugins this hub must NOT ship to a box (WP-8 §6.5). The list is per MACHINE and lives in
|
|
136
|
+
// one hub-side JSON file (`<data dir>/agent-bundle-exclude-plugins.json`), read through
|
|
137
|
+
// lib/agent-bundle/skill-transfer.js's in-memory cache — the same reader the Machines row and the
|
|
138
|
+
// PUT route use, so the push and the UI can never disagree about what is excluded. `getDataDir()`
|
|
139
|
+
// is required lazily like everything else here: a caller that only wants the pure filter must not
|
|
140
|
+
// load the data-dir module.
|
|
141
|
+
agentBundleExcludePlugins: async (instanceId) => {
|
|
142
|
+
const skillTransfer = require('./agent-bundle/skill-transfer');
|
|
143
|
+
const dataDir = require('./data-dir').getDataDir();
|
|
144
|
+
await skillTransfer.loadExcludePlugins(dataDir);
|
|
145
|
+
return skillTransfer.excludePluginsFor(dataDir, instanceId);
|
|
146
|
+
},
|
|
111
147
|
// WP-6 §8: the post-create catalog refresh (lazy: the catalog module requires THIS one).
|
|
112
148
|
refreshCatalog: (machineId) => {
|
|
113
149
|
try {
|
|
@@ -141,19 +177,29 @@ function filterRemoteCreateForward(msg) {
|
|
|
141
177
|
/**
|
|
142
178
|
* WP-3 (DGX Spark coding readiness §2): which coding agent — if any — a forwarded create is about.
|
|
143
179
|
* The create path picks the CLI from `msg.agentType || detectAgentType(cmd)` (server.js:34024, whose
|
|
144
|
-
* canonical values are
|
|
180
|
+
* canonical values are 'claude' | 'codex' | 'dsh' — lib/agent-capabilities.js and
|
|
145
181
|
* lib/create/resolve-launch-params.js:692, which also writes the matching bare `cmd` at :772/:824,
|
|
146
|
-
* the same shape public/js/reviews.js:858 sends
|
|
147
|
-
*
|
|
148
|
-
*
|
|
182
|
+
* the same shape public/js/reviews.js:858 sends; a dsh create carries `agentType:'dsh'`/`cmd:'dsh'`,
|
|
183
|
+
* public/m/app.js:13868). WP-13 (D-15) adds 'dsh': a forwarded dsh create runs the SAME handleCreate
|
|
184
|
+
* on the box, which resolves its OWN dsh binary, proxy port and route (D-11 box-side), so the hub
|
|
185
|
+
* sends nothing dsh-specific. Only claude/codex can be signed out, so a `null` (shell / gemini /
|
|
186
|
+
* walle / anything else) is never refused — the caller checks the sign-in verdict per agent, and a
|
|
187
|
+
* `'dsh'` verdict is never consulted (there is no CTM sign-in for dsh). Pure and total: any other
|
|
188
|
+
* shape reads `null`.
|
|
149
189
|
*/
|
|
190
|
+
const DSH_AGENT_KEY = 'dsh';
|
|
191
|
+
|
|
150
192
|
function remoteCreateAgentKey(payload) {
|
|
151
|
-
|
|
152
|
-
|
|
193
|
+
// The DECLARED agentType goes through the canonical normalizer — the same read the box's own
|
|
194
|
+
// create path makes of it — so an alias resolves to the agent the box will actually launch.
|
|
195
|
+
const declared = normalizeAgentType(_string(payload && payload.agentType, 40));
|
|
196
|
+
if (declared === 'claude' || declared === 'codex' || declared === DSH_AGENT_KEY) return declared;
|
|
197
|
+
// The bare `cmd` is matched EXACTLY on its basename: the create path writes that bare token, and a
|
|
198
|
+
// hub must not guess at a path/flag tokenization the box would do differently.
|
|
153
199
|
const cmd = _string(payload && payload.cmd, 300);
|
|
154
200
|
if (!cmd) return null;
|
|
155
201
|
const base = cmd.split(/[\\/]/).pop().toLowerCase();
|
|
156
|
-
return base === 'claude' || base === 'codex' ? base : null;
|
|
202
|
+
return base === 'claude' || base === 'codex' || base === DSH_AGENT_KEY ? base : null;
|
|
157
203
|
}
|
|
158
204
|
|
|
159
205
|
/** The hub's own minted instance id, or '' when identity is not available yet. */
|
|
@@ -191,6 +237,42 @@ function _findMachine(machineId, deps) {
|
|
|
191
237
|
return machines.find((row) => _string(row.instanceId, 160) === machineId) || null;
|
|
192
238
|
}
|
|
193
239
|
|
|
240
|
+
/**
|
|
241
|
+
* WP-13 (D-15): does the TARGET BOX say it does not have `agentKey`? This is the box's OWN
|
|
242
|
+
* available-agents answer — the same read the New Session card disables the dsh card from — and it
|
|
243
|
+
* is INJECTED (`deps.registry.agentAvailabilityFor(instanceId)`), exactly like the WP-3 sign-in read
|
|
244
|
+
* above and for the same reason: the verdict belongs to the box, and a host/test registry that does
|
|
245
|
+
* not carry the read holds NO verdict.
|
|
246
|
+
*
|
|
247
|
+
* P3 applies to the failure direction: only an explicit "the box does not have it" refuses. A
|
|
248
|
+
* missing provider, a non-object answer, a 404/shape the hub does not recognise, or an entry the
|
|
249
|
+
* block does not carry all read `false` ("no verdict") and the create is dialled exactly as before —
|
|
250
|
+
* the box's own create path is then the authority, and its failure is relayed verbatim.
|
|
251
|
+
*
|
|
252
|
+
* Accepted shapes (the hub must not care which one its registry serves):
|
|
253
|
+
* { agents: { dsh: { available: false, path: null } } } - `/api/models/available-agents`, verbatim
|
|
254
|
+
* { dsh: false } | { dsh: true } | { dsh: { available: false } }
|
|
255
|
+
*
|
|
256
|
+
* @returns {boolean} true only for an explicit "this box does not have it".
|
|
257
|
+
*/
|
|
258
|
+
function _boxAgentMissing(d, row, agentKey) {
|
|
259
|
+
const registry = d && d.registry;
|
|
260
|
+
if (!registry || typeof registry.agentAvailabilityFor !== 'function') return false;
|
|
261
|
+
let answer = null;
|
|
262
|
+
try {
|
|
263
|
+
answer = registry.agentAvailabilityFor(_string(row && row.instanceId, 160));
|
|
264
|
+
} catch {
|
|
265
|
+
return false; // REPORT: an unreadable verdict is "no verdict", never a refusal
|
|
266
|
+
}
|
|
267
|
+
if (!answer || typeof answer !== 'object') return false;
|
|
268
|
+
const block = answer.agents && typeof answer.agents === 'object' ? answer.agents : answer;
|
|
269
|
+
const detail = block[agentKey];
|
|
270
|
+
if (detail === undefined || detail === null) return false;
|
|
271
|
+
if (typeof detail === 'boolean') return detail === false;
|
|
272
|
+
if (typeof detail === 'object') return detail.available === false;
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
|
|
194
276
|
function _parsePeerTokenMap(raw = '') {
|
|
195
277
|
const value = _string(raw, 10000);
|
|
196
278
|
const map = new Map();
|
|
@@ -253,13 +335,19 @@ function _ownForwardId(env, deps) {
|
|
|
253
335
|
* `machine_forward_loop`. The value is the sender's minted instance id (the same id the
|
|
254
336
|
* `machine` routing field uses), read through lib/instance-identity-access; it falls back to
|
|
255
337
|
* the env instance tag so a pre-boot dial still names the sender.
|
|
338
|
+
* D-1 (docs/remote-skills-hub-peer-identity-2026-10-26.html §5 D-1/D-2) the ENV leg's peer id
|
|
339
|
+
* falls back to the sender's minted instance id when the process env names none — a
|
|
340
|
+
* launchd/systemd hub has no shell `HOSTNAME`, and the box keys every peer-scoped route by
|
|
341
|
+
* this header (403 agent_bundle_peer_unidentified otherwise). An env-named id still wins.
|
|
256
342
|
*
|
|
257
343
|
* Returns null when NO credential is configured (then the caller reports peer_credential_missing).
|
|
258
344
|
*/
|
|
259
345
|
function buildPeerControlHeaders(env = process.env, deps = {}) {
|
|
260
|
-
const peerId = _string(env.CTM_INSTANCE_ID || env.CTM_INSTANCE_TAG || env.HOSTNAME || '', 160);
|
|
261
346
|
const forwardedBy = _ownForwardId(env, deps);
|
|
262
|
-
|
|
347
|
+
// An env-named id wins; else the minted instance id — a launchd/systemd hub has no shell HOSTNAME,
|
|
348
|
+
// and the box keys every peer-scoped route by this header (remote-skills-hub-peer-identity D-1).
|
|
349
|
+
const peerId = _string(env.CTM_INSTANCE_ID || env.CTM_INSTANCE_TAG || env.HOSTNAME || '', 160) || forwardedBy;
|
|
350
|
+
const envToken = _envPeerToken(env, peerId); // D-2: the SAME id for the map lookup
|
|
263
351
|
const poolToken = _poolToken();
|
|
264
352
|
// P-8: the per-target decision. A named non-member is dialled with the env bearer so a legacy
|
|
265
353
|
// box keeps answering; an unnamed target keeps the pool-if-pool behaviour this function had.
|
|
@@ -397,35 +485,91 @@ function peerClientInfoFrame(hello) {
|
|
|
397
485
|
terminalProtocol: Number((hello && hello.terminalProtocol) || appVersion.terminalProtocol || 0),
|
|
398
486
|
assetEpoch: String(appVersion.assetEpoch || ''),
|
|
399
487
|
surface: 'machine-hub',
|
|
488
|
+
// WP-2 (docs/relay-frame-v2-negotiation-box-session-mouse-2026-10-31.html §5 R-4, §6): the hub's
|
|
489
|
+
// peer identity is a hub viewer too, so it negotiates frame protocol v2 on its own behalf and
|
|
490
|
+
// lib/remote-machine-link.js projects each `frame-v2` down per RECEIVING hub socket (R-3).
|
|
491
|
+
terminalCapabilities: { frameContentV2: true },
|
|
400
492
|
};
|
|
401
493
|
}
|
|
402
494
|
|
|
495
|
+
/**
|
|
496
|
+
* WP-0 (§6.3, D-8): the four dial timings, all measured from the DIAL START and `-1` for a phase
|
|
497
|
+
* that was never reached. WP-3 fills `acceptedMs` in — it is the `create-accepted` frame's offset
|
|
498
|
+
* (D-3) — and it stays `-1` for every dial whose box never accepted, which is exactly the failed
|
|
499
|
+
* phase A the line has to show.
|
|
500
|
+
*/
|
|
501
|
+
function _noTimings() {
|
|
502
|
+
return { connectMs: -1, helloMs: -1, acceptedMs: -1, replyMs: -1 };
|
|
503
|
+
}
|
|
504
|
+
|
|
403
505
|
/**
|
|
404
506
|
* One short-lived forward: connect, HELLO handshake, send the filtered create, wait for the
|
|
405
507
|
* box's `created` (or `error`) frame, close. Never throws — the caller turns a failure into the
|
|
406
508
|
* `machine_unreachable` (or box-error) frame.
|
|
407
509
|
*
|
|
408
|
-
*
|
|
510
|
+
* WP-3 (§6.3 D-3..D-6): the wait is in TWO phases. Phase A (DEFAULT_TIMEOUT_MS, unchanged) runs
|
|
511
|
+
* from the dial to the box's `create-accepted` — or straight through to `created`/`error`, which is
|
|
512
|
+
* what an older box answers. A matching `create-accepted` ends phase A, calls `onAccepted` exactly
|
|
513
|
+
* once and arms phase B (CREATE_ACCEPTED_CEILING_MS). After acceptance, silence, a socket close or a
|
|
514
|
+
* socket error is `machine_create_unconfirmed` — the box owns the create from that moment and the hub
|
|
515
|
+
* never cancels it, so NOTHING is sent to the box on any phase-B exit (D-6).
|
|
516
|
+
*
|
|
517
|
+
* @param {object} options
|
|
518
|
+
* @param {string} options.machineUrl the box's registered url
|
|
519
|
+
* @param {object} options.payload the §6.3-filtered create (its `id` is the acceptance key, D-1)
|
|
520
|
+
* @param {object} options.deps the dial deps (env/registry/authHeaders/WebSocket/timers…)
|
|
521
|
+
* @param {number} [options.acceptedCeilingMs] phase B; also readable as `deps.acceptedCeilingMs`
|
|
522
|
+
* @param {null|function} [options.onAccepted] called once with the accepted frame; also `deps.onAccepted`
|
|
523
|
+
* @returns {Promise<{ok: true, frame: object, accepted: boolean, timings: object}
|
|
524
|
+
* |{ok: false, code: string, error: string, accepted: boolean, timings: object}>}
|
|
409
525
|
*/
|
|
410
|
-
function sendRemoteCreate({ machineUrl, payload, deps }) {
|
|
526
|
+
function sendRemoteCreate({ machineUrl, payload, deps, acceptedCeilingMs, onAccepted }) {
|
|
411
527
|
return new Promise((resolve) => {
|
|
412
528
|
const dial = preparePeerDial(machineUrl, deps);
|
|
413
|
-
if (!dial.ok) return resolve(dial);
|
|
529
|
+
if (!dial.ok) return resolve({ ...dial, accepted: false, timings: _noTimings() });
|
|
414
530
|
const { url, headers, WebSocketImpl } = dial;
|
|
415
531
|
const timeoutMs = Number(deps.timeoutMs || DEFAULT_TIMEOUT_MS) || DEFAULT_TIMEOUT_MS;
|
|
532
|
+
// WP-3 (§6.3): phase B's ceiling and the acceptance hook. Both may ride the dial deps instead —
|
|
533
|
+
// the seam every other dial option already uses (`deps.timeoutMs`, `deps.setTimeoutFn`) — so a
|
|
534
|
+
// caller that drives this through forwardRemoteCreate passes neither of them here.
|
|
535
|
+
const ceilingMs = Number(acceptedCeilingMs || (deps && deps.acceptedCeilingMs) || CREATE_ACCEPTED_CEILING_MS) || CREATE_ACCEPTED_CEILING_MS;
|
|
536
|
+
const acceptedHook = typeof onAccepted === 'function'
|
|
537
|
+
? onAccepted
|
|
538
|
+
: (deps && typeof deps.onAccepted === 'function' ? deps.onAccepted : null);
|
|
416
539
|
const setTimeoutFn = typeof deps.setTimeoutFn === 'function' ? deps.setTimeoutFn : setTimeout;
|
|
417
540
|
const clearTimeoutFn = typeof deps.clearTimeoutFn === 'function' ? deps.clearTimeoutFn : clearTimeout;
|
|
418
541
|
|
|
542
|
+
// WP-0 (§6.3): the dial clock. `dialStartedAt` is the dial's own origin and every timing below
|
|
543
|
+
// is an offset from it; a phase that never happened stays -1 (see _noTimings).
|
|
544
|
+
const dialStartedAt = Date.now();
|
|
545
|
+
let openedAt = -1;
|
|
546
|
+
let helloAt = -1;
|
|
547
|
+
let acceptedAt = -1;
|
|
548
|
+
let replyAt = -1;
|
|
549
|
+
const _since = (at) => (at < 0 ? -1 : at - dialStartedAt);
|
|
550
|
+
const timings = () => ({
|
|
551
|
+
connectMs: _since(openedAt),
|
|
552
|
+
helloMs: _since(helloAt),
|
|
553
|
+
acceptedMs: _since(acceptedAt), // WP-3 (D-3): the create-accepted offset, -1 when never accepted
|
|
554
|
+
replyMs: _since(replyAt),
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
// D-3/D-1: the create idempotency key this dial is for. A `create-accepted` naming any OTHER id
|
|
558
|
+
// is not this create's acceptance (one box WS endpoint serves many clients) and must not end
|
|
559
|
+
// phase A.
|
|
560
|
+
const createId = _string(payload && payload.id, 200);
|
|
561
|
+
|
|
419
562
|
let socket = null;
|
|
420
563
|
let timer = null;
|
|
421
564
|
let settled = false;
|
|
565
|
+
let accepted = false;
|
|
422
566
|
let sent = false;
|
|
423
567
|
const finish = (result) => {
|
|
424
568
|
if (settled) return;
|
|
425
569
|
settled = true;
|
|
426
570
|
if (timer) clearTimeoutFn(timer);
|
|
427
571
|
try { if (socket) socket.close(); } catch { /* expected: close on an already-dead socket */ }
|
|
428
|
-
resolve(result);
|
|
572
|
+
resolve({ ...result, accepted, timings: timings() });
|
|
429
573
|
};
|
|
430
574
|
// Only ever after client-info (see the dial-order note above): a create sent earlier is
|
|
431
575
|
// dropped by the box's protocol gate and the forward would hang until the timeout.
|
|
@@ -442,9 +586,25 @@ function sendRemoteCreate({ machineUrl, payload, deps }) {
|
|
|
442
586
|
try {
|
|
443
587
|
socket = new WebSocketImpl(url, { headers });
|
|
444
588
|
} catch (error) {
|
|
445
|
-
return resolve({ ok: false, code: 'machine_unreachable', error: `connect_failed:${error?.message || error}
|
|
589
|
+
return resolve({ ok: false, code: 'machine_unreachable', error: `connect_failed:${error?.message || error}`, accepted: false, timings: _noTimings() });
|
|
446
590
|
}
|
|
447
591
|
|
|
592
|
+
// WP-0: the connect phase is the socket's own 'open'. Nothing is SENT here (see the dial-order
|
|
593
|
+
// note below) — the listener exists only to stamp connectMs, and an implementation that never
|
|
594
|
+
// fires it leaves the phase at -1 rather than inventing a number.
|
|
595
|
+
socket.on('open', () => { if (openedAt < 0) openedAt = Date.now(); });
|
|
596
|
+
|
|
597
|
+
// WP-3 (D-4/D-6): phase B. Its expiry ends the WAIT only — the box already owns the create and
|
|
598
|
+
// runs it to completion, so nothing is sent to the box here (D-6).
|
|
599
|
+
const armPhaseB = () => {
|
|
600
|
+
timer = setTimeoutFn(() => {
|
|
601
|
+
try { socket.terminate(); } catch { /* expected: the socket is already gone */ }
|
|
602
|
+
finish({ ok: false, code: MACHINE_CREATE_UNCONFIRMED, error: 'accepted_then_timeout' });
|
|
603
|
+
}, ceilingMs);
|
|
604
|
+
if (timer && typeof timer.unref === 'function') timer.unref();
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
// Phase A (D-3): unchanged 15 s from the dial to the box's acceptance or its final reply.
|
|
448
608
|
timer = setTimeoutFn(() => {
|
|
449
609
|
try { socket.terminate(); } catch { /* expected: the socket is already gone */ }
|
|
450
610
|
finish({ ok: false, code: 'machine_unreachable', error: 'timeout' });
|
|
@@ -453,6 +613,9 @@ function sendRemoteCreate({ machineUrl, payload, deps }) {
|
|
|
453
613
|
|
|
454
614
|
// No create on 'open': the box has not said hello yet, so its protocol gate is not satisfied.
|
|
455
615
|
socket.on('message', (raw) => {
|
|
616
|
+
// D-6: once this dial is settled nothing may cross to the box again — a frame arriving after
|
|
617
|
+
// the phase-B timeout (a late hello, the box's eventual `created`) is dropped, never answered.
|
|
618
|
+
if (settled) return;
|
|
456
619
|
let frame = null;
|
|
457
620
|
try {
|
|
458
621
|
frame = JSON.parse(raw.toString());
|
|
@@ -460,21 +623,52 @@ function sendRemoteCreate({ machineUrl, payload, deps }) {
|
|
|
460
623
|
return; // expected: a non-JSON frame (the box's binary/other lanes) is not the reply
|
|
461
624
|
}
|
|
462
625
|
if (!frame || typeof frame !== 'object') return;
|
|
626
|
+
// WP-0: a REPLY is any frame that settles this dial (created / box error / the box's protocol
|
|
627
|
+
// or auth verdict on the create). Timeout, socket error and close-before-reply are NOT replies,
|
|
628
|
+
// so replyMs stays -1 there.
|
|
629
|
+
const markReply = () => { if (replyAt < 0) replyAt = Date.now(); };
|
|
630
|
+
if (frame.type === 'create-accepted') {
|
|
631
|
+
// WP-3 (D-3): the box admitted the create and is starting it. Only THIS create's id counts
|
|
632
|
+
// (any other id belongs to another client on the same box endpoint) and only the first one.
|
|
633
|
+
// Phase A is cleared, the caller is told once, and phase B starts.
|
|
634
|
+
if (!createId || _string(frame.id, 200) !== createId) return;
|
|
635
|
+
if (accepted) return;
|
|
636
|
+
accepted = true;
|
|
637
|
+
acceptedAt = Date.now();
|
|
638
|
+
if (timer) clearTimeoutFn(timer);
|
|
639
|
+
timer = null;
|
|
640
|
+
if (acceptedHook) {
|
|
641
|
+
try {
|
|
642
|
+
acceptedHook(frame);
|
|
643
|
+
} catch (error) {
|
|
644
|
+
// REPORT: the caller's own bookkeeping (mirror registration + browser relay) must never
|
|
645
|
+
// break a live dial — the box is starting the session either way (D-6).
|
|
646
|
+
console.warn(`[remote-create] onAccepted failed: ${_string(error && error.message, 200) || 'unknown error'}`);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
armPhaseB();
|
|
650
|
+
return;
|
|
651
|
+
}
|
|
463
652
|
if (frame.type === 'hello') {
|
|
464
653
|
// The box gates every terminal-protocol message (including 'create') behind a compatible
|
|
465
654
|
// client-info, so the hub identifies itself with the epoch the box just advertised — the
|
|
466
655
|
// same handshake a browser does. Both frames go out in this order on this one socket.
|
|
656
|
+
if (helloAt < 0) helloAt = Date.now();
|
|
467
657
|
try {
|
|
468
658
|
socket.send(JSON.stringify(peerClientInfoFrame(frame)));
|
|
469
659
|
} catch { /* expected: the socket died between hello and client-info */ }
|
|
470
660
|
sendCreate();
|
|
471
661
|
return;
|
|
472
662
|
}
|
|
473
|
-
if (frame.type === 'created')
|
|
663
|
+
if (frame.type === 'created') {
|
|
664
|
+
markReply();
|
|
665
|
+
return finish({ ok: true, frame });
|
|
666
|
+
}
|
|
474
667
|
if (frame.type === 'terminal-protocol-incompatible') {
|
|
475
668
|
// The box refused the frame on protocol grounds. Fail now with the reason instead of
|
|
476
669
|
// waiting out the timeout for a reply that will never come (this is the frame that made a
|
|
477
670
|
// mis-ordered dial look like an unreachable box).
|
|
671
|
+
markReply();
|
|
478
672
|
return finish({
|
|
479
673
|
ok: false,
|
|
480
674
|
code: 'machine_unreachable',
|
|
@@ -492,11 +686,13 @@ function sendRemoteCreate({ machineUrl, payload, deps }) {
|
|
|
492
686
|
// non-fatal — treating every auth-error as fatal would break every forward.
|
|
493
687
|
if (_string(frame.messageType, 40) === 'create') {
|
|
494
688
|
const code = _string(frame.code, 80) || 'machine_unreachable';
|
|
689
|
+
markReply();
|
|
495
690
|
return finish({ ok: false, code, error: `box_refused_create:${code}` });
|
|
496
691
|
}
|
|
497
692
|
return;
|
|
498
693
|
}
|
|
499
694
|
if (frame.type === 'error') {
|
|
695
|
+
markReply();
|
|
500
696
|
return finish({
|
|
501
697
|
ok: false,
|
|
502
698
|
code: _string(frame.code, 80) || 'machine_unreachable',
|
|
@@ -507,14 +703,183 @@ function sendRemoteCreate({ machineUrl, payload, deps }) {
|
|
|
507
703
|
// handshake or the box's ambient traffic on this lane and is not a reply to the create.
|
|
508
704
|
});
|
|
509
705
|
socket.on('error', (error) => {
|
|
706
|
+
// D-5: an error AFTER acceptance is not "unreachable" — the box already claimed the create and
|
|
707
|
+
// is still starting it, so the hub reports it as unconfirmed and keeps the session.
|
|
708
|
+
if (accepted) return finish({ ok: false, code: MACHINE_CREATE_UNCONFIRMED, error: 'accepted_then_socket_error' });
|
|
510
709
|
finish({ ok: false, code: 'machine_unreachable', error: _string(error?.message || error, 300) });
|
|
511
710
|
});
|
|
512
711
|
socket.on('close', () => {
|
|
712
|
+
if (accepted) return finish({ ok: false, code: MACHINE_CREATE_UNCONFIRMED, error: 'accepted_then_closed' });
|
|
513
713
|
finish({ ok: false, code: 'machine_unreachable', error: 'closed_before_reply' });
|
|
514
714
|
});
|
|
515
715
|
});
|
|
516
716
|
}
|
|
517
717
|
|
|
718
|
+
// ---------------------------------------------------------------------------------------------
|
|
719
|
+
// WP-3 (docs/remote-agent-skills-bundles-2026-10-25.html §5 D-4/D-6, §6.5) — the hub's agent-skill
|
|
720
|
+
// push at forwarded create.
|
|
721
|
+
//
|
|
722
|
+
// The push happens BEFORE the dial and can never change its outcome (D-6): the box's container gets
|
|
723
|
+
// the hub's skills and plugins, and a hub that cannot deliver them still forwards the create exactly
|
|
724
|
+
// as it did before this work package existed.
|
|
725
|
+
// ---------------------------------------------------------------------------------------------
|
|
726
|
+
|
|
727
|
+
/** D-9: the box os values a bundle can be built for; anything else means "do not guess". */
|
|
728
|
+
const AGENT_BUNDLE_PLATFORMS = Object.freeze(['darwin', 'linux', 'win32']);
|
|
729
|
+
|
|
730
|
+
/** D-6/D-9: the create-time push failures this lane names itself (push.js owns the transport ones). */
|
|
731
|
+
const BUNDLE_PLATFORM_UNKNOWN = 'agent_bundle_platform_unknown';
|
|
732
|
+
const BUNDLE_NO_CREDENTIAL = 'agent_bundle_push_no_credential';
|
|
733
|
+
const BUNDLE_PUSH_NETWORK = 'agent_bundle_push_network';
|
|
734
|
+
|
|
735
|
+
/**
|
|
736
|
+
* D-9: the platform to build for = the box's `os` from the registry's cached manifest host block.
|
|
737
|
+
* Unknown, unreadable or unlisted reads null — the caller then SKIPS the push rather than guessing a
|
|
738
|
+
* platform (a wrong guess would ship the box a skill its own CLI cannot load).
|
|
739
|
+
*/
|
|
740
|
+
function agentBundlePlatformFor(machineId, deps = {}) {
|
|
741
|
+
const d = _deps(deps);
|
|
742
|
+
const registry = d.registry;
|
|
743
|
+
if (!registry || typeof registry.manifestHostFor !== 'function') return null;
|
|
744
|
+
let host = null;
|
|
745
|
+
try {
|
|
746
|
+
host = registry.manifestHostFor(_string(machineId, 160));
|
|
747
|
+
} catch {
|
|
748
|
+
return null; // expected: a registry read fault means "unknown platform", never a failed create
|
|
749
|
+
}
|
|
750
|
+
const platform = _string(host && host.os, 40).toLowerCase();
|
|
751
|
+
return AGENT_BUNDLE_PLATFORMS.includes(platform) ? platform : null;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
/**
|
|
755
|
+
* The headers the push is authenticated with: exactly the choice `preparePeerDial` makes for the
|
|
756
|
+
* dial of the same target (an injected `authHeaders` builder wins; otherwise
|
|
757
|
+
* `buildPeerControlHeaders(env, {instanceId})`), so the push and the dial can never drift onto
|
|
758
|
+
* different credentials. `null` means no credential is configured — a configuration error the caller
|
|
759
|
+
* reports as `agent_bundle_push_no_credential` (the dial reports its own `peer_credential_missing`).
|
|
760
|
+
*/
|
|
761
|
+
function bundlePeerHeadersFor(deps = {}, instanceId) {
|
|
762
|
+
const d = _deps(deps);
|
|
763
|
+
if (typeof d.authHeaders === 'function') {
|
|
764
|
+
try {
|
|
765
|
+
const headers = d.authHeaders();
|
|
766
|
+
return headers && typeof headers === 'object' ? headers : null;
|
|
767
|
+
} catch {
|
|
768
|
+
return null; // expected: a credential-builder fault is a missing credential, not a throw
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return buildPeerControlHeaders(d.env || process.env, { instanceId: _string(instanceId, 160) });
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
/** The sync bookkeeping every outcome shares: the machine row's `skills` block, then the diagnostic. */
|
|
775
|
+
function _recordPushOutcome(d, instanceId, result, payload, platform) {
|
|
776
|
+
const registry = d.registry;
|
|
777
|
+
if (registry && typeof registry.recordAgentBundlePush === 'function') {
|
|
778
|
+
try {
|
|
779
|
+
registry.recordAgentBundlePush(instanceId, result);
|
|
780
|
+
} catch { /* expected: a registry write fault must never fail a create (D-6) */ }
|
|
781
|
+
}
|
|
782
|
+
if (result.ok !== true && typeof d.recordSessionDiagnostic === 'function') {
|
|
783
|
+
try {
|
|
784
|
+
const details = { code: _string(result.code, 80), machine: instanceId, platform: platform || '' };
|
|
785
|
+
// WP-6 D-13: the push now says WHICH phase cost the wait and, when the push ran on without the
|
|
786
|
+
// caller (D-11), that it is still going. Both are present only when the push reported them.
|
|
787
|
+
const phase = _string(result.phase, 40);
|
|
788
|
+
if (phase) details.phase = phase;
|
|
789
|
+
if (result.continuing === true) details.continuing = true;
|
|
790
|
+
if (result.timings && typeof result.timings === 'object') details.timings = result.timings;
|
|
791
|
+
// The session id here is the FORWARDED create's id (the hub has no local session row for it —
|
|
792
|
+
// OQ-4); when the client supplied none, the machine id is the correlation we do have.
|
|
793
|
+
d.recordSessionDiagnostic(_string(payload && payload.id, 200) || instanceId, 'agent-bundle-push-failed', details);
|
|
794
|
+
} catch { /* expected: diagnostic bookkeeping must never fail a create (D-6) */ }
|
|
795
|
+
}
|
|
796
|
+
return result;
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Push this hub's bundle for `row` before the dial, or `null` when there is nothing to push.
|
|
801
|
+
*
|
|
802
|
+
* SYNCHRONOUS DECISION, async only when a push actually runs: a shell create, an unknown platform and
|
|
803
|
+
* a missing credential all resolve here without yielding to the event loop, so the create's critical
|
|
804
|
+
* path has no avoidable interleaving point (and a caller driving this seam in a test can still rely on
|
|
805
|
+
* "nothing has been dialled yet" being observable synchronously).
|
|
806
|
+
*
|
|
807
|
+
* Always resolves (never throws) on the async path: a failure is recorded on the machine row and
|
|
808
|
+
* emitted as the `agent-bundle-push-failed` session diagnostic, and the caller forwards the create
|
|
809
|
+
* regardless (D-6). Only claude/codex creates push (`agentKey`), and `excludePlugins` comes from a
|
|
810
|
+
* dep so WP-8 can wire the per-machine list without touching this call site.
|
|
811
|
+
*
|
|
812
|
+
* @returns {null|Promise<object>} the push result, or null when nothing was pushed
|
|
813
|
+
*/
|
|
814
|
+
function pushAgentBundleForCreate(d, row, payload, agentKey) {
|
|
815
|
+
if (!agentKey) return null; // a plain shell create runs no coding CLI, so it ships no skills
|
|
816
|
+
const instanceId = _string(row && row.instanceId, 160);
|
|
817
|
+
const platform = agentBundlePlatformFor(instanceId, d);
|
|
818
|
+
const headers = platform ? bundlePeerHeadersFor(d, instanceId) : null;
|
|
819
|
+
if (!platform) {
|
|
820
|
+
// D-9: guessing a platform would ship the box a skill its own CLI cannot load. Report the skip.
|
|
821
|
+
_recordPushOutcome(d, instanceId, {
|
|
822
|
+
ok: false,
|
|
823
|
+
code: BUNDLE_PLATFORM_UNKNOWN,
|
|
824
|
+
detail: 'the machine has never reported a platform this hub can build a bundle for',
|
|
825
|
+
}, payload, '');
|
|
826
|
+
return null;
|
|
827
|
+
}
|
|
828
|
+
if (!headers) {
|
|
829
|
+
_recordPushOutcome(d, instanceId, {
|
|
830
|
+
ok: false,
|
|
831
|
+
code: BUNDLE_NO_CREDENTIAL,
|
|
832
|
+
detail: 'no peer credential is configured for this machine',
|
|
833
|
+
}, payload, platform);
|
|
834
|
+
return null;
|
|
835
|
+
}
|
|
836
|
+
const machine = { instanceId, url: _string(row.url, 1000), displayName: _string(row.displayName, 200) };
|
|
837
|
+
return (async () => {
|
|
838
|
+
// WP-8: the production exclusion dep loads its store with async fs (never a sync read on the
|
|
839
|
+
// create's main thread) and so answers a promise; a dep that answers a plain list is used as-is,
|
|
840
|
+
// without a yield, so the push still starts in the same synchronous turn as before.
|
|
841
|
+
let excludePlugins = [];
|
|
842
|
+
if (typeof d.agentBundleExcludePlugins === 'function') {
|
|
843
|
+
try {
|
|
844
|
+
const answer = d.agentBundleExcludePlugins(instanceId);
|
|
845
|
+
excludePlugins = (answer && typeof answer.then === 'function' ? await answer : answer) || [];
|
|
846
|
+
} catch {
|
|
847
|
+
excludePlugins = []; // expected: an unreadable exclude list ships every plugin, never nothing
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
const options = {
|
|
851
|
+
fetchImpl: d.fetchImpl,
|
|
852
|
+
peerHeaders: headers,
|
|
853
|
+
targetPlatform: platform,
|
|
854
|
+
excludePlugins,
|
|
855
|
+
// WP-6 D-11: the push outlives this create's wait. When it settles late — after the create has
|
|
856
|
+
// already been forwarded — its result still has to reach the row and the diagnostic, exactly
|
|
857
|
+
// once (push.js calls this only for a call that timed out AND started the push). The create
|
|
858
|
+
// itself is NEVER held for it: `_recordPushOutcome` cannot reject and the frame goes out as
|
|
859
|
+
// soon as the budgeted call resolves.
|
|
860
|
+
onLateResult: (late) => _recordPushOutcome(
|
|
861
|
+
d,
|
|
862
|
+
instanceId,
|
|
863
|
+
(late && typeof late === 'object') ? late : { ok: false, code: BUNDLE_PUSH_NETWORK, detail: 'the push returned no result' },
|
|
864
|
+
payload,
|
|
865
|
+
platform,
|
|
866
|
+
),
|
|
867
|
+
};
|
|
868
|
+
let result;
|
|
869
|
+
try {
|
|
870
|
+
result = await d.pushAgentBundle(machine, options);
|
|
871
|
+
} catch (error) {
|
|
872
|
+
// REPORT: push.js resolves its own failures, so a THROW here is a bug in the seam — it must
|
|
873
|
+
// still not touch the create.
|
|
874
|
+
result = { ok: false, code: BUNDLE_PUSH_NETWORK, detail: _string(error && error.message, 200) || 'push failed' };
|
|
875
|
+
}
|
|
876
|
+
if (!result || typeof result !== 'object') {
|
|
877
|
+
result = { ok: false, code: BUNDLE_PUSH_NETWORK, detail: 'the push returned no result' };
|
|
878
|
+
}
|
|
879
|
+
return _recordPushOutcome(d, instanceId, result, payload, platform);
|
|
880
|
+
})();
|
|
881
|
+
}
|
|
882
|
+
|
|
518
883
|
function _sendFrame(ctx, frame) {
|
|
519
884
|
try {
|
|
520
885
|
if (ctx && ctx.ws && ctx.ws.readyState === 1 && typeof ctx.send === 'function') {
|
|
@@ -526,81 +891,242 @@ function _sendFrame(ctx, frame) {
|
|
|
526
891
|
}
|
|
527
892
|
}
|
|
528
893
|
|
|
529
|
-
|
|
530
|
-
|
|
894
|
+
/**
|
|
895
|
+
* Refuse a forwarded create on the requesting browser socket (one frame, one result).
|
|
896
|
+
*
|
|
897
|
+
* WP-0 (§6.5): `machine`/`machineName` ride every refusal so it is VISIBLE. Both clients gate the
|
|
898
|
+
* machine-error toast on `/^machine_/.test(code) || !!msg.machine` (public/index.html:4467,
|
|
899
|
+
* public/m/app.js:5638), so a hub code such as `peer_credential_missing`, or any box code relayed
|
|
900
|
+
* from a forwarded create (`rate_limited`, `create_in_progress`, …), was console-only before this.
|
|
901
|
+
* `machine` is the requested instance id as a STRING — the desktop dedupes its toast key on
|
|
902
|
+
* `String(msg.machine)`. Same shape as `forwardLifecycleRequest`'s 503 body
|
|
903
|
+
* (lib/remote-machine-catalog.js). `code`/`message` are untouched.
|
|
904
|
+
*
|
|
905
|
+
* WP-3 (§6.3 S-3): the optional 6th `id` is the create's own idempotency key (D-1). It is added
|
|
906
|
+
* AFTER the machine fields and only when non-empty, so the client can match the refusal to the
|
|
907
|
+
* pending create it is holding (D-7) and an id-less caller's frame keeps its exact key set.
|
|
908
|
+
*/
|
|
909
|
+
function _fail(ctx, code, detail, machineId, machineName, id = '') {
|
|
910
|
+
const frame = {
|
|
531
911
|
type: 'error',
|
|
532
912
|
code: code || 'machine_unreachable',
|
|
533
913
|
message: detail || 'The machine could not be reached.',
|
|
534
|
-
|
|
914
|
+
machine: _string(machineId, 200),
|
|
915
|
+
machineName: _string(machineName, 200),
|
|
916
|
+
};
|
|
917
|
+
const refusalId = _string(id, 200);
|
|
918
|
+
if (refusalId) frame.id = refusalId;
|
|
919
|
+
_sendFrame(ctx, frame);
|
|
535
920
|
return { ok: false, code: code || 'machine_unreachable', error: detail || '' };
|
|
536
921
|
}
|
|
537
922
|
|
|
923
|
+
/**
|
|
924
|
+
* WP-0 (D-8, §6.3): the hub's ONE line per forwarded create. Keys are a FIXED set in a fixed order,
|
|
925
|
+
* and `-` is the "no value" token (`<n|-1>` for the two durations, where `-1` means the push/dial
|
|
926
|
+
* phase was never reached). The line is built for humans and greps, never parsed by code — the
|
|
927
|
+
* machine-readable form of the same facts is the frame's own `timings` and the `_fail` code.
|
|
928
|
+
*
|
|
929
|
+
* @param {{id: string, machine: string, agent: string,
|
|
930
|
+
* outcome: 'created'|'failed'|'unconfirmed'|'refused',
|
|
931
|
+
* code: string, pushMs: number, pushCode: string, timings: object, totalMs: number}} f
|
|
932
|
+
*/
|
|
933
|
+
function formatRemoteCreateLine(f) {
|
|
934
|
+
const fields = f && typeof f === 'object' ? f : {};
|
|
935
|
+
const t = fields.timings && typeof fields.timings === 'object' ? fields.timings : _noTimings();
|
|
936
|
+
const ms = (value) => {
|
|
937
|
+
const n = Number(value);
|
|
938
|
+
return Number.isFinite(n) && n >= 0 ? Math.round(n) : -1;
|
|
939
|
+
};
|
|
940
|
+
const token = (value, max) => _string(value, max) || '-';
|
|
941
|
+
return `[remote-create] id=${_string(fields.id, 200).slice(0, 8) || '-'}`
|
|
942
|
+
+ ` machine=${token(fields.machine, 160)} agent=${token(fields.agent, 40)}`
|
|
943
|
+
+ ` outcome=${token(fields.outcome, 40)} code=${token(fields.code, 80)}`
|
|
944
|
+
+ ` pushMs=${ms(fields.pushMs)} pushCode=${token(fields.pushCode, 80)}`
|
|
945
|
+
+ ` connectMs=${ms(t.connectMs)} helloMs=${ms(t.helloMs)} acceptedMs=${ms(t.acceptedMs)} replyMs=${ms(t.replyMs)}`
|
|
946
|
+
+ ` totalMs=${ms(fields.totalMs)}`;
|
|
947
|
+
}
|
|
948
|
+
|
|
538
949
|
/**
|
|
539
950
|
* Forward an already-classified remote create (S-1). On success the box's own `created` frame is
|
|
540
951
|
* re-emitted to the requesting browser socket with `machine`/`machineName` added (AC-2a) and the
|
|
541
952
|
* mirror registration is recorded; on failure the caller's socket gets exactly one typed `error`
|
|
542
953
|
* frame and NOTHING is created locally (AC-2c).
|
|
954
|
+
*
|
|
955
|
+
* WP-0 (D-8): every exit writes exactly ONE `[remote-create]` line (see formatRemoteCreateLine).
|
|
956
|
+
* `outcome` is `created` on the box's `created`, `refused` for the hub-side refusals taken BEFORE the
|
|
957
|
+
* dial (loop guard, the machine-row verdicts, the dsh/sign-in verdicts, a missing peer credential),
|
|
958
|
+
* `failed` for everything after the dial starts (dial failures, timeouts, box error frames) and
|
|
959
|
+
* `unconfirmed` for an ACCEPTED create the hub stopped waiting for (WP-3 D-5 — the box still has it).
|
|
543
960
|
*/
|
|
544
961
|
async function forwardRemoteCreate(ctx, msg, deps = {}) {
|
|
545
962
|
const d = _deps(deps);
|
|
963
|
+
const startedAt = Date.now();
|
|
546
964
|
const machineId = _string(msg && msg.machine, 160);
|
|
965
|
+
// WP-3 (§6.3 S-3): the create's own idempotency key as the client minted it (D-1). It rides every
|
|
966
|
+
// refusal frame (`_fail`'s 6th argument) so the client can match a failure to its pending create.
|
|
967
|
+
const createId = _string(msg && msg.id, 200);
|
|
968
|
+
// WP-0 bookkeeping, shared by every exit below. `_report` is the ONLY writer of the line, so a new
|
|
969
|
+
// return path cannot silently skip it: route the return through `_refuse`/`_report`.
|
|
970
|
+
let logMachine = machineId;
|
|
971
|
+
let logId = createId;
|
|
972
|
+
let pushMs = -1;
|
|
973
|
+
let pushCode = '-';
|
|
974
|
+
let dialTimings = null;
|
|
975
|
+
const _report = (result, outcome) => {
|
|
976
|
+
console.log(formatRemoteCreateLine({
|
|
977
|
+
id: logId,
|
|
978
|
+
machine: logMachine,
|
|
979
|
+
agent: remoteCreateAgentKey(msg) || '-',
|
|
980
|
+
outcome,
|
|
981
|
+
code: _string(result && result.code, 80),
|
|
982
|
+
pushMs,
|
|
983
|
+
pushCode,
|
|
984
|
+
timings: dialTimings,
|
|
985
|
+
totalMs: Date.now() - startedAt,
|
|
986
|
+
}));
|
|
987
|
+
return result;
|
|
988
|
+
};
|
|
989
|
+
/** A hub-side refusal taken before any dial — never counted as a dial failure. */
|
|
990
|
+
const _refuse = (result) => _report(result, 'refused');
|
|
547
991
|
// P-3 one-hop guard. This function is reached ONLY when the create names a machine that is not
|
|
548
992
|
// this instance (lib/ws/terminal-attach.js checks isRemoteMachineTarget first), so a request that
|
|
549
993
|
// already carries the hop header is by definition a request to forward a forwarded create — the
|
|
550
994
|
// A→B→C chain (and the A→B→A loop) P-3 forbids. Nothing is dialled and nothing is created.
|
|
551
995
|
const forwardedBy = forwardedByOf(ctx);
|
|
552
996
|
if (forwardedBy) {
|
|
553
|
-
return _fail(ctx, MACHINE_FORWARD_LOOP, `This create was already forwarded by ${forwardedBy}; a forwarded request is never forwarded again
|
|
997
|
+
return _refuse(_fail(ctx, MACHINE_FORWARD_LOOP, `This create was already forwarded by ${forwardedBy}; a forwarded request is never forwarded again`, machineId, '', createId));
|
|
554
998
|
}
|
|
555
999
|
const row = _findMachine(machineId, d);
|
|
556
|
-
if (!row) return _fail(ctx, 'machine_unreachable', `machine ${machineId} is not registered on this hub
|
|
557
|
-
|
|
558
|
-
|
|
1000
|
+
if (!row) return _refuse(_fail(ctx, 'machine_unreachable', `machine ${machineId} is not registered on this hub`, machineId, '', createId));
|
|
1001
|
+
// The row is authoritative for the line's `machine` once it exists (mirrors `_fail`'s frame field).
|
|
1002
|
+
logMachine = _string(row.instanceId, 160) || logMachine;
|
|
1003
|
+
if (!row.enabled) return _refuse(_fail(ctx, 'machine_unreachable', `machine ${machineId} is disabled`, machineId, row.displayName, createId));
|
|
1004
|
+
if (!_string(row.url, 1000)) return _refuse(_fail(ctx, 'machine_unreachable', `machine ${machineId} has no url`, machineId, row.displayName, createId));
|
|
559
1005
|
if (_string(row.health, 40) === 'unreachable') {
|
|
560
|
-
return _fail(ctx, 'machine_unreachable', `machine ${machineId} is unreachable
|
|
1006
|
+
return _refuse(_fail(ctx, 'machine_unreachable', `machine ${machineId} is unreachable`, machineId, row.displayName, createId));
|
|
561
1007
|
}
|
|
562
1008
|
|
|
563
1009
|
const payload = filterRemoteCreateForward(msg);
|
|
1010
|
+
// WP-13 (D-15): the WP-2 `dsh_remote_pending` refusal is GONE — a forwarded dsh create runs the
|
|
1011
|
+
// same handleCreate on the box, which resolves its own dsh binary, proxy port and route. The ONE
|
|
1012
|
+
// remaining dsh gate is the box's own installation verdict below: CTM never installs dsh on a box
|
|
1013
|
+
// (843 MB), so a hub that holds "this box has no dsh" refuses rather than dialling into a box-side
|
|
1014
|
+
// PATH failure. A box with no verdict at all is dialled exactly like any other agent (P3).
|
|
1015
|
+
const agentKey = remoteCreateAgentKey(payload);
|
|
1016
|
+
if (agentKey === DSH_AGENT_KEY) {
|
|
1017
|
+
const notInstalled = _boxAgentMissing(d, row, DSH_AGENT_KEY);
|
|
1018
|
+
if (notInstalled) {
|
|
1019
|
+
return _refuse(_fail(ctx, 'machine_agent_not_installed', `DeepSeek Harness is not installed on ${_string(row.displayName, 200) || row.instanceId}`, machineId, row.displayName, createId));
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
564
1022
|
// WP-3: the box judges a create only by its manifest probe, so a box that is REACHABLE but has no
|
|
565
1023
|
// signed-in Claude/Codex CLI would happily spawn a session that opens at a login prompt. Refuse at
|
|
566
1024
|
// the hub instead, naming the box and the URL that fixes it. 'unknown' (never probed, unreachable
|
|
567
1025
|
// probe, unreadable body) and 'signed_in' both proceed: only an explicit `signedIn:false` refuses.
|
|
568
|
-
|
|
569
|
-
|
|
1026
|
+
// 'dsh' is skipped by construction: it has no CTM sign-in (S-88), so no verdict is consulted —
|
|
1027
|
+
// `agentAuthFor`'s map is `{claude, codex}` and reading `auth['dsh']` would be meaningless.
|
|
1028
|
+
const auth = agentKey && agentKey !== DSH_AGENT_KEY
|
|
1029
|
+
&& d.registry && typeof d.registry.agentAuthFor === 'function'
|
|
570
1030
|
? d.registry.agentAuthFor(row.instanceId)
|
|
571
1031
|
: null;
|
|
572
1032
|
if (auth && auth[agentKey] === 'signed_out') {
|
|
573
1033
|
const label = agentKey === 'codex' ? 'Codex' : 'Claude Code';
|
|
574
|
-
return _fail(ctx, 'machine_agent_signed_out', `${label} is not signed in on ${_string(row.displayName, 200) || row.instanceId} — open ${row.url} → AI Providers → Coding Agents → Sign in
|
|
1034
|
+
return _refuse(_fail(ctx, 'machine_agent_signed_out', `${label} is not signed in on ${_string(row.displayName, 200) || row.instanceId} — open ${row.url} → AI Providers → Coding Agents → Sign in`, machineId, row.displayName, createId));
|
|
1035
|
+
}
|
|
1036
|
+
// WP-3 (D-4/D-6, §6.5): hand the box this hub's skills/plugins BEFORE the dial. A failure here is
|
|
1037
|
+
// recorded on the machine row and emitted as a diagnostic, and the forward below runs either way —
|
|
1038
|
+
// skills are a convenience, and NOTHING about this call may change the create's outcome (AC-3c:
|
|
1039
|
+
// the forwarded frame is untouched by construction; the push never touches `payload`). The decision
|
|
1040
|
+
// is synchronous, so a create with nothing to push (a shell create, an unknown platform, no
|
|
1041
|
+
// credential) reaches the dial without yielding.
|
|
1042
|
+
const bundlePush = pushAgentBundleForCreate(d, row, payload, agentKey);
|
|
1043
|
+
if (bundlePush) {
|
|
1044
|
+
// D-9/OQ-3: the push wait (≤10 s) used to be invisible; it is now pushMs/pushCode on the line.
|
|
1045
|
+
// pushMs prefers push.js's own D-13 `timings.totalMs` (the push's account of itself) and falls
|
|
1046
|
+
// back to the caller's measured wait, so a push that RAN is never reported as `-1`.
|
|
1047
|
+
const pushStartedAt = Date.now();
|
|
1048
|
+
const pushResult = await bundlePush;
|
|
1049
|
+
pushMs = _pushMs(pushResult, Date.now() - pushStartedAt);
|
|
1050
|
+
pushCode = pushResult && pushResult.ok === true ? 'ok' : _string(pushResult && pushResult.code, 80) || '-';
|
|
575
1051
|
}
|
|
576
1052
|
// The machine's DISPLAY name rides the dial deps so a credential-less refusal can name the box
|
|
577
1053
|
// the operator has to configure (D-2), not just "the machine".
|
|
578
1054
|
const result = await sendRemoteCreate({
|
|
579
1055
|
machineUrl: row.url,
|
|
580
1056
|
payload,
|
|
1057
|
+
// WP-3 (§6.3 D-3): the moment the box says it ADMITTED the create (phase A ends here, D-4 arms
|
|
1058
|
+
// phase B). The mirror registration is recorded and the frame is relayed to the browser with the
|
|
1059
|
+
// same machine/machineName shape as the `created` relay below — so the client's pending create
|
|
1060
|
+
// turns into "starting on ‹machine›" instead of waiting out the whole box-side create.
|
|
1061
|
+
onAccepted: (frame) => {
|
|
1062
|
+
recordMirrorSession(frame.id, row);
|
|
1063
|
+
_sendFrame(ctx, {
|
|
1064
|
+
...frame,
|
|
1065
|
+
machine: _string(row.instanceId, 160),
|
|
1066
|
+
machineName: _string(row.displayName, 200),
|
|
1067
|
+
});
|
|
1068
|
+
},
|
|
581
1069
|
// P-8: the TARGET's instanceId rides the dial deps so the credential is chosen per machine —
|
|
582
1070
|
// a legacy env-token box that never joined the pool is dialled with the env bearer, not with a
|
|
583
1071
|
// pool secret it would 401.
|
|
584
1072
|
deps: { ...d, machineName: _string(row.displayName, 200), instanceId: _string(row.instanceId, 160) },
|
|
585
1073
|
});
|
|
586
|
-
|
|
1074
|
+
dialTimings = result.timings || null;
|
|
1075
|
+
// The box's own refusal (`rate_limited`, `create_in_progress`, `session_exists`, …) and every
|
|
1076
|
+
// local dial failure reach the browser HERE: `_fail` re-emits `code`/`message` verbatim and adds
|
|
1077
|
+
// the machine fields (WP-0 §6.5), so neither a hub code nor a relayed box code is console-only.
|
|
1078
|
+
// A missing peer credential is the one dial-path code that is NOT a dial failure: nothing was
|
|
1079
|
+
// dialled, so it reads as a hub refusal (D-2) exactly like the refusals above.
|
|
1080
|
+
if (!result.ok) {
|
|
1081
|
+
const failure = _fail(ctx, result.code, result.error, machineId, row.displayName, createId);
|
|
1082
|
+
if (result.code === PEER_CREDENTIAL_MISSING) return _refuse(failure);
|
|
1083
|
+
// WP-3 (D-5): the box ACCEPTED this create (hence the code) and then went quiet — a warning, not
|
|
1084
|
+
// a failure. The box runs the create to completion (D-6), so the catalog refresh runs here too,
|
|
1085
|
+
// exactly as it does on the success path: the session must appear the moment the box commits it.
|
|
1086
|
+
if (result.code === MACHINE_CREATE_UNCONFIRMED) {
|
|
1087
|
+
_refreshCatalog(d, row.instanceId);
|
|
1088
|
+
return _report(failure, 'unconfirmed');
|
|
1089
|
+
}
|
|
1090
|
+
return _report(failure, 'failed');
|
|
1091
|
+
}
|
|
587
1092
|
|
|
588
1093
|
const frame = result.frame || {};
|
|
589
1094
|
const sessionId = _string(frame.id || payload.id, 200);
|
|
590
|
-
if (sessionId)
|
|
1095
|
+
if (sessionId) {
|
|
1096
|
+
logId = sessionId;
|
|
1097
|
+
recordMirrorSession(sessionId, row);
|
|
1098
|
+
}
|
|
591
1099
|
_sendFrame(ctx, {
|
|
592
1100
|
...frame,
|
|
593
1101
|
machine: _string(row.instanceId, 160),
|
|
594
1102
|
machineName: _string(row.displayName, 200),
|
|
595
1103
|
});
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
1104
|
+
_refreshCatalog(d, row.instanceId);
|
|
1105
|
+
return _report({ ok: true, sessionId, machineInstanceId: _string(row.instanceId, 160) }, 'created');
|
|
1106
|
+
}
|
|
1107
|
+
|
|
1108
|
+
/**
|
|
1109
|
+
* WP-6 §8 / WP-3 (D-5): the fire-and-forget catalog refresh. "right after a forwarded create" is a
|
|
1110
|
+
* catalog refresh trigger — the new session must appear in the hub's merged list at once, not on the
|
|
1111
|
+
* next ~15 s probe sweep. Fire-and-forget: the in-memory mirror registration already makes the relay
|
|
1112
|
+
* work, so a catalog fault is a stale list, never a failed create. Both the `created` outcome and the
|
|
1113
|
+
* `unconfirmed` one (D-5) take it — the box owns the create in either case.
|
|
1114
|
+
*/
|
|
1115
|
+
function _refreshCatalog(d, instanceId) {
|
|
1116
|
+
if (typeof d.refreshCatalog !== 'function') return;
|
|
1117
|
+
try { d.refreshCatalog(_string(instanceId, 160)); } catch { /* expected: a catalog fault never fails a create */ }
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* WP-0 (D-9): the push's own account of how long it cost (D-13 `timings.totalMs`) when it has one,
|
|
1122
|
+
* else the caller's measured wait. Never a negative number: a push that RAN is `>= 0`, and only a
|
|
1123
|
+
* push that never ran is `-1` (which the call site simply does not call this for).
|
|
1124
|
+
*/
|
|
1125
|
+
function _pushMs(result, measuredMs) {
|
|
1126
|
+
const total = Number(result && result.timings && result.timings.totalMs);
|
|
1127
|
+
if (Number.isFinite(total) && total >= 0) return Math.round(total);
|
|
1128
|
+
const measured = Number(measuredMs);
|
|
1129
|
+
return Number.isFinite(measured) && measured >= 0 ? Math.round(measured) : -1;
|
|
604
1130
|
}
|
|
605
1131
|
|
|
606
1132
|
/** Record (or refresh) the in-memory mirror registration for a session this hub created on a box. */
|
|
@@ -665,11 +1191,25 @@ function _resetMirrorSessionsForTests() {
|
|
|
665
1191
|
module.exports = {
|
|
666
1192
|
REMOTE_CREATE_FORWARD_ALLOWLIST,
|
|
667
1193
|
DEFAULT_TIMEOUT_MS,
|
|
1194
|
+
// WP-3 (§6.3 D-3/D-4/D-5): phase B's ceiling, the code an accepted-but-silent box reports, and the
|
|
1195
|
+
// shared dial prologue below. The ceiling's coverage of every box-side bound is enforced by
|
|
1196
|
+
// tests/remote-create-ceiling-covers-box.test.js.
|
|
1197
|
+
CREATE_ACCEPTED_CEILING_MS,
|
|
1198
|
+
MACHINE_CREATE_UNCONFIRMED,
|
|
668
1199
|
configure,
|
|
669
1200
|
filterRemoteCreateForward,
|
|
670
1201
|
remoteCreateAgentKey,
|
|
671
1202
|
isRemoteMachineTarget,
|
|
672
1203
|
forwardRemoteCreate,
|
|
1204
|
+
// WP-0 (D-8, §6.3): the hub's one-line-per-create formatter — exported so the line's fixed key
|
|
1205
|
+
// set is assertable without driving a whole dial through a console capture.
|
|
1206
|
+
formatRemoteCreateLine,
|
|
1207
|
+
// WP-3 (docs/remote-agent-skills-bundles-2026-10-25.html §6.5): the platform reader, the peer
|
|
1208
|
+
// header choice the push shares with the dial, and the push call site itself.
|
|
1209
|
+
AGENT_BUNDLE_PLATFORMS,
|
|
1210
|
+
agentBundlePlatformFor,
|
|
1211
|
+
bundlePeerHeadersFor,
|
|
1212
|
+
pushAgentBundleForCreate,
|
|
673
1213
|
sendRemoteCreate,
|
|
674
1214
|
buildPeerControlHeaders,
|
|
675
1215
|
// WP-1 (D-2/D-4): the missing-credential code, its operator copy, and the hub-local state.
|