create-walle 0.10.1 → 0.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/lib/owner-write-safety.js +135 -0
- package/lib/public-runtime.js +13 -5
- package/lib/release-schema.json +3 -2
- package/lib/runtime-closure-policy.json +6 -4
- package/package.json +1 -1
- package/template/bin/deps-doctor.js +1 -1
- package/template/bin/node-bin.sh +11 -0
- package/template/coding-task-manager/api-prompts.js +10 -22
- package/template/coding-task-manager/bin/claude-hook.js +7 -3
- package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
- package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
- package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
- package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
- package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
- package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
- package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
- package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
- package/template/coding-task-manager/db.js +705 -286
- package/template/coding-task-manager/git-utils.js +44 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
- package/template/coding-task-manager/lib/agent-presets.js +15 -0
- package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
- package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
- package/template/coding-task-manager/lib/api/host-stats.js +98 -0
- package/template/coding-task-manager/lib/api/ingress.js +35 -12
- package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
- package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
- package/template/coding-task-manager/lib/api/machines.js +461 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
- package/template/coding-task-manager/lib/api/providers.js +53 -6
- package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
- package/template/coding-task-manager/lib/api/sessions.js +160 -8
- package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
- package/template/coding-task-manager/lib/api/worktrees.js +38 -37
- package/template/coding-task-manager/lib/approval-drift.js +16 -1
- package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
- package/template/coding-task-manager/lib/approval-scope.js +68 -4
- package/template/coding-task-manager/lib/auth-context.js +32 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
- package/template/coding-task-manager/lib/auth-rules.js +186 -45
- package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
- package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
- package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
- package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
- package/template/coding-task-manager/lib/claude-session-title.js +6 -30
- package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
- package/template/coding-task-manager/lib/client-diag-handler.js +5 -0
- package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
- package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
- package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
- package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
- package/template/coding-task-manager/lib/codex-paths.js +2 -1
- package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
- package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
- package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
- package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
- package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
- package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
- package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
- package/template/coding-task-manager/lib/db-domains.js +21 -2
- package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
- package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
- package/template/coding-task-manager/lib/feature-manifest.js +1 -1
- package/template/coding-task-manager/lib/fs-watcher.js +3 -2
- package/template/coding-task-manager/lib/harness-history-transition.js +3 -3
- package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
- package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
- package/template/coding-task-manager/lib/headless-term-service.js +55 -17
- package/template/coding-task-manager/lib/host-control-dial.js +167 -0
- package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
- package/template/coding-task-manager/lib/host-handoff.js +51 -13
- package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
- package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
- package/template/coding-task-manager/lib/host-stats.js +328 -0
- package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
- package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
- package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
- package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
- package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
- package/template/coding-task-manager/lib/instance-manifest.js +13 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
- package/template/coding-task-manager/lib/launch-model-override.js +160 -1
- package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
- package/template/coding-task-manager/lib/lead-model-switch.js +3 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
- package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
- package/template/coding-task-manager/lib/login-tripwire.js +1 -0
- package/template/coding-task-manager/lib/machine-pairing.js +238 -0
- package/template/coding-task-manager/lib/machine-pool.js +409 -0
- package/template/coding-task-manager/lib/machine-registry.js +749 -0
- package/template/coding-task-manager/lib/machine-self.js +66 -0
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +52 -4
- package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
- package/template/coding-task-manager/lib/media-journal.js +84 -27
- package/template/coding-task-manager/lib/media-store-options.js +43 -15
- package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
- package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
- package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
- package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
- package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
- package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
- package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
- package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-history-control.js +2 -2
- package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
- package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
- package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
- package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
- package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
- package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
- package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
- package/template/coding-task-manager/lib/read-pool-client.js +5 -2
- package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
- package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
- package/template/coding-task-manager/lib/remote-command-context.js +12 -0
- package/template/coding-task-manager/lib/remote-command-runtime.js +13 -1
- package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
- package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
- package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
- package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
- package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
- package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
- package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
- package/template/coding-task-manager/lib/restart-preflight.js +9 -2
- package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
- package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
- package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
- package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
- package/template/coding-task-manager/lib/resume-cwd.js +13 -0
- package/template/coding-task-manager/lib/resume-from-store.js +12 -1
- package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
- package/template/coding-task-manager/lib/resume-preflight.js +21 -16
- package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
- package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
- package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
- package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
- package/template/coding-task-manager/lib/root-prompt-admission.js +33 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +167 -33
- package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
- package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
- package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
- package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
- package/template/coding-task-manager/lib/session-create/identity.js +20 -1
- package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
- package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
- package/template/coding-task-manager/lib/session-create/provider-process.js +34 -37
- package/template/coding-task-manager/lib/session-create/response.js +30 -12
- package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
- package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
- package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
- package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
- package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
- package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
- package/template/coding-task-manager/lib/session-host-manager.js +152 -206
- package/template/coding-task-manager/lib/session-jobs.js +17 -9
- package/template/coding-task-manager/lib/session-lifecycle.js +18 -0
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +9 -0
- package/template/coding-task-manager/lib/session-resource-sampler.js +19 -9
- package/template/coding-task-manager/lib/session-standup.js +67 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
- package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
- package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
- package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
- package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
- package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
- package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
- package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
- package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
- package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
- package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
- package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
- package/template/coding-task-manager/lib/transcript-link.js +77 -0
- package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
- package/template/coding-task-manager/lib/transcript-source.js +8 -3
- package/template/coding-task-manager/lib/transcript-store.js +40 -0
- package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
- package/template/coding-task-manager/lib/typing-interaction-trace.js +92 -0
- package/template/coding-task-manager/lib/typing-worker-stages.js +60 -0
- package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
- package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
- package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
- package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
- package/template/coding-task-manager/lib/ws-router.js +68 -1
- package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
- package/template/coding-task-manager/oauth-proxy.js +99 -55
- package/template/coding-task-manager/package-lock.json +24 -0
- package/template/coding-task-manager/package.json +3 -0
- package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
- package/template/coding-task-manager/providers/codex-mcp.js +14 -5
- package/template/coding-task-manager/public/css/app.css +279 -1
- package/template/coding-task-manager/public/css/reviews.css +4 -0
- package/template/coding-task-manager/public/index.html +294 -223
- package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
- package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
- package/template/coding-task-manager/public/js/background-work.js +13 -7
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
- package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
- package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
- package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
- package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
- package/template/coding-task-manager/public/js/history-scroller.js +187 -9
- package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
- package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
- package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
- package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -8
- package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
- package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
- package/template/coding-task-manager/public/js/reviews.js +7 -3
- package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
- package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
- package/template/coding-task-manager/public/js/row-join.js +4 -2
- package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
- package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
- package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
- package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
- package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
- package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
- package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
- package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
- package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
- package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -1
- package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
- package/template/coding-task-manager/public/js/walle-session.js +30 -7
- package/template/coding-task-manager/public/m/app.css +226 -8
- package/template/coding-task-manager/public/m/app.js +502 -9
- package/template/coding-task-manager/public/m/index.html +38 -1
- package/template/coding-task-manager/public/m/machines.js +1313 -0
- package/template/coding-task-manager/public/m/pool-failover.js +391 -0
- package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
- package/template/coding-task-manager/public/m/sw.js +5 -0
- package/template/coding-task-manager/queue-engine.js +13 -0
- package/template/coding-task-manager/server/approvals.js +5 -6
- package/template/coding-task-manager/server/boot.js +136 -17
- package/template/coding-task-manager/server.js +902 -239
- package/template/coding-task-manager/session-integrity.js +7 -9
- package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
- package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
- package/template/coding-task-manager/workers/session-host-process.js +14 -3
- package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
- package/template/coding-task-manager/workers/worktree-worker.js +34 -0
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/scripts/conversation-storage-v2-migration.js +78 -11
- package/template/shared/conversation-storage-v2-contract.js +42 -6
- package/template/shared/fixtures/worker-status-payloads.json +392 -0
- package/template/shared/git-remote-key.js +83 -0
- package/template/shared/host-identity.js +98 -2
- package/template/shared/owner-write-safety.js +135 -0
- package/template/shared/public-runtime.js +13 -5
- package/template/shared/release-schema.json +3 -2
- package/template/shared/report-citations.js +14 -0
- package/template/shared/sqlite-write-lock.js +361 -223
- package/template/shared/storage/contract.js +35 -3
- package/template/wall-e/agent.js +21 -0
- package/template/wall-e/api-walle.js +5 -0
- package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
- package/template/wall-e/brain/helper-rules-schema.js +129 -0
- package/template/wall-e/brain/migrations.js +51 -22
- package/template/wall-e/brain/model-deployment-facts.js +31 -1
- package/template/wall-e/brain/owner-call-whitelist.js +16 -0
- package/template/wall-e/brain/schema-ensures.js +22 -0
- package/template/wall-e/brain.js +1 -1
- package/template/wall-e/coding/journal-dispatcher.js +51 -0
- package/template/wall-e/coding/session-journal.js +28 -40
- package/template/wall-e/coding-orchestrator.js +23 -26
- package/template/wall-e/http/auth.js +18 -1
- package/template/wall-e/http/blog-views-api.js +318 -0
- package/template/wall-e/http/helper-rules-api.js +321 -0
- package/template/wall-e/http/model-proxy-providers.js +3 -1
- package/template/wall-e/http/telemetry-api.js +12 -0
- package/template/wall-e/lib/blog-view-policy.js +88 -0
- package/template/wall-e/lib/blog-view-store.js +169 -0
- package/template/wall-e/lib/client-config-lock.js +2 -1
- package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
- package/template/wall-e/lib/error-kinds.js +10 -1
- package/template/wall-e/lib/feature-manifest.js +1 -1
- package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
- package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
- package/template/wall-e/lib/helper-rules/grade.js +277 -0
- package/template/wall-e/lib/helper-rules/label.js +247 -0
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
- package/template/wall-e/lib/helper-rules/store.js +667 -0
- package/template/wall-e/lib/mcp-integration.js +5 -6
- package/template/wall-e/lib/runtime-worker-pool.js +1 -0
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
- package/template/wall-e/lib/telemetry-db-schema.js +37 -0
- package/template/wall-e/lib/telemetry-process-client.js +76 -1
- package/template/wall-e/llm/default-fallback.js +4 -0
- package/template/wall-e/llm/model-catalog.js +13 -4
- package/template/wall-e/llm/model-roles.js +6 -0
- package/template/wall-e/llm/portkey-http.js +150 -0
- package/template/wall-e/llm/portkey-sync.js +15 -145
- package/template/wall-e/llm/worker-mcp-routing.js +61 -0
- package/template/wall-e/llm/worker-route.js +43 -1
- package/template/wall-e/loops/brain-custom-writes.js +45 -0
- package/template/wall-e/mcp-protocol-contract.js +41 -1
- package/template/wall-e/mcp-server.js +13 -2
- package/template/wall-e/mcp-worker-schemas.js +20 -2
- package/template/wall-e/memory/ctm-context-client.js +25 -5
- package/template/wall-e/shared/release-schema.json +3 -2
- package/template/wall-e/shared/sqlite-write-lock.js +361 -223
- package/template/wall-e/shared/storage/contract.js +35 -3
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
- package/template/wall-e/tools/local-tools.js +6 -4
- package/template/wall-e/workers/brain-owner-worker.js +9 -0
- package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
- package/template/wall-e/workers/telemetry-worker.js +8 -0
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// The one capability that selects the wide `peer` bucket below (WP-3). Spelled as a literal on
|
|
4
|
+
// purpose: lib/auth-rules.js owns the constant, and requiring that module here would put this
|
|
5
|
+
// low-level limiter behind the rule tables (and their consumers) for a one-string comparison.
|
|
6
|
+
const PEER_CONTROL_SCOPE = 'peer-control';
|
|
7
|
+
|
|
3
8
|
const DEFAULT_LIMITS = Object.freeze({
|
|
4
9
|
// Remote/mobile page bootstrap fans out across settings, sessions, standup,
|
|
5
10
|
// auth state, WebSocket setup, and cached refresh probes. Keep read traffic
|
|
@@ -8,6 +13,22 @@ const DEFAULT_LIMITS = Object.freeze({
|
|
|
8
13
|
mutation: { refillPerMinute: 30, burst: 5 },
|
|
9
14
|
admin: { refillPerMinute: 10, burst: 2 },
|
|
10
15
|
step_up: { refillPerMinute: 10, burst: 3 },
|
|
16
|
+
// peer (WP-3, docs/remote-machine-hub-sessions-2026-10-22.html §6.4/§10): a remote machine HUB
|
|
17
|
+
// relays the keystrokes, prompts, approvals and lifecycle frames of every session it views on
|
|
18
|
+
// this box through ONE peer-control bearer — so the whole hub is one rate-limit key here and the
|
|
19
|
+
// `admin`/`mutation` buckets (10/min burst 2, 30/min burst 5) would throttle a hub that is simply
|
|
20
|
+
// relaying several sessions. Sized like `default` (600/min, burst 120) because a relay is
|
|
21
|
+
// read-shaped traffic that happens to be carried by mutating message types. Selected ONLY for an
|
|
22
|
+
// authenticated `peer-control` principal (see rateKindForRule below); every other caller keeps
|
|
23
|
+
// the exact bucket it had before.
|
|
24
|
+
peer: { refillPerMinute: 600, burst: 120 },
|
|
25
|
+
// machine_pair (WP-3, docs/machine-pool-picker-pairing-dashboard-2026-10-24.html §6.3/D-13): the
|
|
26
|
+
// PRE-AUTH POST /api/machine-pairing/join. `sync`'s own bucket. It is the ONLY bucket selected by
|
|
27
|
+
// route rather than by principal: the join has no authenticated caller yet (the request carries the
|
|
28
|
+
// pool secret it is trying to prove), so it is keyed by REMOTE ADDRESS and sized for a human typing
|
|
29
|
+
// a code plus a retry or two — 5/min, burst 5 — never for a script. A code is 20^8; this bucket
|
|
30
|
+
// plus lib/machine-pairing.js's 5-wrong-redeem burn is what keeps the search space out of reach.
|
|
31
|
+
machine_pair: { refillPerMinute: 5, burst: 5 },
|
|
11
32
|
});
|
|
12
33
|
|
|
13
34
|
const DEFAULT_FAILED_AUTH = Object.freeze({
|
|
@@ -131,9 +152,28 @@ class AuthRateLimiter {
|
|
|
131
152
|
}
|
|
132
153
|
}
|
|
133
154
|
|
|
134
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Which bucket a rule is charged to.
|
|
157
|
+
*
|
|
158
|
+
* The optional third argument is the AUTH CONTEXT the decision is being made for. It exists for
|
|
159
|
+
* exactly one case (WP-3 §6.4): a `peer-control` principal is a machine hub relaying many sessions
|
|
160
|
+
* through one bearer, so its mutations are charged to the wide `peer` bucket instead of
|
|
161
|
+
* `admin`/`mutation`. Callers that pass no auth (or an auth with no `peer-control` scope) get
|
|
162
|
+
* byte-identical behaviour to before this argument existed — it can only ever WIDEN a bucket for a
|
|
163
|
+
* peer-control principal, never narrow one.
|
|
164
|
+
*
|
|
165
|
+
* @param {{mutation?: boolean, scope?: string}} rule
|
|
166
|
+
* @param {string} [pathname]
|
|
167
|
+
* @param {{scopes?: Set<string>|string[]}} [auth]
|
|
168
|
+
*/
|
|
169
|
+
function rateKindForRule(rule, pathname = '', auth = null) {
|
|
135
170
|
if (pathname === '/api/auth/begin-step-up') return 'step_up';
|
|
136
171
|
if (!rule?.mutation) return 'default';
|
|
172
|
+
const scopes = auth && auth.scopes;
|
|
173
|
+
const isPeerControl = scopes instanceof Set
|
|
174
|
+
? scopes.has(PEER_CONTROL_SCOPE)
|
|
175
|
+
: Array.isArray(scopes) && scopes.includes(PEER_CONTROL_SCOPE);
|
|
176
|
+
if (isPeerControl) return 'peer';
|
|
137
177
|
if (rule.scope === 'admin' || rule.scope === 'create') return 'admin';
|
|
138
178
|
return 'mutation';
|
|
139
179
|
}
|
|
@@ -2,30 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
const ALL_SCOPES = Object.freeze(['read', 'respond', 'create', 'admin', 'sync']);
|
|
4
4
|
|
|
5
|
+
// WP-1 (docs/remote-machine-hub-sessions-2026-10-22.html §5 D-7, §6.2, §9 S-17): the
|
|
6
|
+
// `peer-control` capability. It is deliberately NOT a member of ALL_SCOPES: nothing that
|
|
7
|
+
// parses scopes (env-scoped test tokens, device tokens, the admin expansion in parseScopes)
|
|
8
|
+
// can ever mint it, so the only credential that carries it is the sync peer bearer
|
|
9
|
+
// (CTM_SYNC_PEER_TOKEN / CTM_SYNC_PEER_TOKENS — same secret, no new env var), resolved in
|
|
10
|
+
// lib/auth-context.js's resolveSyncPeerToken. `admin` never implies it and it implies nothing:
|
|
11
|
+
// hasScope()/authorize() grant it ONLY the rule rows that declare `peerControl: true`.
|
|
12
|
+
const PEER_CONTROL_SCOPE = 'peer-control';
|
|
13
|
+
|
|
14
|
+
// §6.2's allowlist of WS message types a peer-control identity may send:
|
|
15
|
+
// attach, keyframe-req, ack, nack, frame-receipt, wake-resync, submit-prompt,
|
|
16
|
+
// approval-resolve, model-change, park, wake, kill, list, create.
|
|
17
|
+
//
|
|
18
|
+
// WP-3b (docs/remote-machine-hub-sessions-2026-10-22.html §6.2, AC-3a) adds the nine types the
|
|
19
|
+
// relay forwards VERBATIM from the hub browser to the box PTY and whose reply the hub fans back
|
|
20
|
+
// out — without them the box answers `auth-error{code:'insufficient_scope'}` and the relayed
|
|
21
|
+
// terminal cannot be typed into: input, interrupt-session, resize, reflow, snapshot, reconcile,
|
|
22
|
+
// flow, terminal-bootstrap, terminal-bootstrap-ack. Their scope/mutation/step-up are UNCHANGED
|
|
23
|
+
// (only the `peerControl` flag is added, exactly like the §6.2 rows above). Every other WS type
|
|
24
|
+
// stays out of reach for the peer bearer (it keeps only the `sync` scope it already had) — in
|
|
25
|
+
// particular `view`, `detach`, `ping` and `client-info` are never forwarded upstream by the
|
|
26
|
+
// relay, so they remain unflagged.
|
|
5
27
|
const WS_AUTH_RULES = Object.freeze({
|
|
6
|
-
list:
|
|
7
|
-
attach:
|
|
28
|
+
list: peerControl('read', false, 'never'),
|
|
29
|
+
attach: peerControl('read', false, 'never'),
|
|
8
30
|
detach: rule('read', false, 'never'),
|
|
9
31
|
// D-1 (terminal-delivery-contract-simplification-2026-09-21.html): replaces
|
|
10
32
|
// subscribe/unsubscribe/set-visible/client-visibility with the one full-set
|
|
11
33
|
// declaration. Same viewer-local, read-only class as its predecessors.
|
|
12
34
|
view: rule('read', false, 'never'),
|
|
13
35
|
ping: rule('read', false, 'never'),
|
|
14
|
-
'wake-resync':
|
|
36
|
+
'wake-resync': peerControl('read', false, 'never'),
|
|
15
37
|
'subscribe-stream': rule('read', false, 'never'),
|
|
16
38
|
'unsubscribe-stream': rule('read', false, 'never'),
|
|
17
39
|
'subscribe-terminal-tail': rule('read', false, 'never'),
|
|
18
40
|
'unsubscribe-terminal-tail': rule('read', false, 'never'),
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
41
|
+
// WP-3b: the relay forwards these viewer-render frames verbatim (S-22 resync/repaint), so the
|
|
42
|
+
// peer bearer must reach them; scope/mutation/step-up are unchanged.
|
|
43
|
+
snapshot: peerControl('read', false, 'never'),
|
|
44
|
+
reconcile: peerControl('read', false, 'never'),
|
|
45
|
+
resize: peerControl('read', false, 'never'),
|
|
46
|
+
reflow: peerControl('read', false, 'never'),
|
|
23
47
|
// State-sync render protocol: clients acknowledge frames, request bounded
|
|
24
48
|
// keyframes, and report visible sessions. These only affect that viewer's
|
|
25
49
|
// stream/backpressure state, not agent execution or persisted session data.
|
|
26
|
-
ack:
|
|
27
|
-
'frame-receipt':
|
|
28
|
-
'keyframe-req':
|
|
50
|
+
ack: peerControl('read', false, 'never'),
|
|
51
|
+
'frame-receipt': peerControl('read', false, 'never'),
|
|
52
|
+
'keyframe-req': peerControl('read', false, 'never'),
|
|
29
53
|
'restore-applied': rule('read', false, 'never'),
|
|
30
54
|
// D-2/WP-2 verdict/adopt round-trip ack — same viewer-local render-protocol
|
|
31
55
|
// class as frame-receipt/restore-applied: acknowledges a server-shipped
|
|
@@ -35,8 +59,8 @@ const WS_AUTH_RULES = Object.freeze({
|
|
|
35
59
|
// this session's live output, read-only, no PTY input or persisted-state mutation.
|
|
36
60
|
// Cold bootstrap is the viewer-local bounded terminal baseline and its
|
|
37
61
|
// receipt. Neither message writes PTY input or persisted session state.
|
|
38
|
-
'terminal-bootstrap':
|
|
39
|
-
'terminal-bootstrap-ack':
|
|
62
|
+
'terminal-bootstrap': peerControl('read', false, 'never'),
|
|
63
|
+
'terminal-bootstrap-ack': peerControl('read', false, 'never'),
|
|
40
64
|
'session-inventory': rule('read', false, 'never'),
|
|
41
65
|
'terminal-geometry-claim-v2': rule('read', false, 'never'),
|
|
42
66
|
'secondary-snapshot-retry': rule('read', false, 'never'),
|
|
@@ -46,7 +70,7 @@ const WS_AUTH_RULES = Object.freeze({
|
|
|
46
70
|
// Backpressure flow control: a viewer asks to pause/resume its own output
|
|
47
71
|
// stream (the server further gates an actual PTY pause to raw-eligible
|
|
48
72
|
// primaries). Same render-control class as reconcile/reflow — read scope.
|
|
49
|
-
flow:
|
|
73
|
+
flow: peerControl('read', false, 'never'),
|
|
50
74
|
// Client-reported render diagnostics (rows-flap attribution probe) — telemetry
|
|
51
75
|
// into the per-session diagnostics ring; rate-limited + scalar-whitelisted in
|
|
52
76
|
// handleClientDiag, so viewer (read) scope is sufficient.
|
|
@@ -54,7 +78,7 @@ const WS_AUTH_RULES = Object.freeze({
|
|
|
54
78
|
'client-theme-report': rule('read', false, 'never'),
|
|
55
79
|
'client-diag': rule('read', false, 'never'),
|
|
56
80
|
'client-error-rollup': rule('read', false, 'never'),
|
|
57
|
-
nack:
|
|
81
|
+
nack: peerControl('read', false, 'never'),
|
|
58
82
|
'walle-history-request': rule('read', false, 'never'),
|
|
59
83
|
'walle-history-reproject': rule('read', false, 'never'),
|
|
60
84
|
'walle-history-repair-metrics': rule('read', false, 'never'),
|
|
@@ -62,37 +86,49 @@ const WS_AUTH_RULES = Object.freeze({
|
|
|
62
86
|
// metadata. It cannot write PTY input or persisted session state.
|
|
63
87
|
'lifecycle-draft': rule('read', false, 'never'),
|
|
64
88
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
89
|
+
// WP-3b AC-3a: typing in a hub browser reaches the box PTY through these two; the relay
|
|
90
|
+
// forwards them verbatim and the box echoes the result back. respond + stepUp:'remote' for
|
|
91
|
+
// every other caller is unchanged — only the peerControl flag is added.
|
|
92
|
+
input: peerControl('respond', true, 'remote'),
|
|
93
|
+
'interrupt-session': peerControl('respond', true, 'remote'),
|
|
94
|
+
'submit-prompt': peerControl('respond', true, 'remote'),
|
|
95
|
+
// WP-1 (docs/remote-resume-choice-click-2026-10-25.html §5 D-2, §9 S-4): the hub forwards the
|
|
96
|
+
// resume strip's choice chip for a MIRROR session, so the peer bearer must reach this row or the
|
|
97
|
+
// box answers with `auth-error insufficient_scope` and the click is lost. FLAG ONLY — the
|
|
98
|
+
// (`respond`, mutation true, step-up 'remote') tuple every other caller sees is unchanged, and the
|
|
99
|
+
// box still runs `_selectResumePromptChoice`'s generation/fingerprint/live-screen validation.
|
|
100
|
+
'resume-prompt-select': peerControl('respond', true, 'remote'),
|
|
69
101
|
'revalidate-recovered-prompt': rule('respond', true, 'remote'),
|
|
70
102
|
'attach-image': rule('respond', true, 'remote'),
|
|
71
103
|
'attach-image-reference': rule('respond', true, 'remote'),
|
|
72
104
|
// WP-3 "Deliver now" override on a stalled draft chip -- same class as attach-image (it
|
|
73
105
|
// triggers the same PTY paste path, just bypassing the reconciler's readiness gate).
|
|
74
|
-
|
|
106
|
+
// WP-1 (docs/remote-session-image-upload-forwarding-2026-10-24.html §6/§7): the hub forwards this
|
|
107
|
+
// verbatim for a mirror session, so the peer bearer must reach it (flag only; scope/mutation/stepUp
|
|
108
|
+
// unchanged). attach-image/attach-image-reference above stay rule(...) — server-side delivery is
|
|
109
|
+
// default ON, so the client never sends them for a box-owned draft.
|
|
110
|
+
'image-deliver-now': peerControl('respond', true, 'remote'),
|
|
75
111
|
'walle-message': rule('respond', true, 'remote'),
|
|
76
112
|
'walle-cancel': rule('respond', true, 'remote'),
|
|
77
113
|
'walle-permission-response': rule('respond', true, 'remote'),
|
|
78
114
|
// Resolve a parked protocol-hook approval (allow/deny) — same class as
|
|
79
115
|
// answering any other approval prompt.
|
|
80
|
-
'approval-resolve':
|
|
116
|
+
'approval-resolve': peerControl('respond', true, 'remote'),
|
|
81
117
|
// Session lifecycle controls share the same admin boundary as kill/model
|
|
82
118
|
// changes.
|
|
83
119
|
'resume-retry': rule('admin', true, 'remote'),
|
|
84
|
-
park:
|
|
120
|
+
park: peerControl('admin', true, 'remote'),
|
|
85
121
|
pin: rule('admin', true, 'remote'),
|
|
86
|
-
wake:
|
|
122
|
+
wake: peerControl('admin', true, 'remote'),
|
|
87
123
|
prewarm: rule('admin', true, 'remote'),
|
|
88
124
|
|
|
89
|
-
create:
|
|
125
|
+
create: peerControl('create', true, 'remote'),
|
|
90
126
|
|
|
91
|
-
kill:
|
|
127
|
+
kill: peerControl('admin', true, 'remote'),
|
|
92
128
|
rename: rule('admin', true, 'remote'),
|
|
93
129
|
'update-session-cwd': rule('admin', true, 'remote'),
|
|
94
130
|
'session-surface-preference': rule('admin', true, 'remote'),
|
|
95
|
-
'model-change':
|
|
131
|
+
'model-change': peerControl('admin', true, 'remote'),
|
|
96
132
|
});
|
|
97
133
|
|
|
98
134
|
const HTTP_AUTH_RULES = Object.freeze([
|
|
@@ -134,21 +170,25 @@ const HTTP_AUTH_RULES = Object.freeze([
|
|
|
134
170
|
exact('POST', '/api/remote/messages', 'respond', true, 'never'),
|
|
135
171
|
regex('POST', /^\/api\/remote\/devices\/[^/]+\/revoke$/, 'admin', true, 'remote', { loopbackOnly: true }),
|
|
136
172
|
exact('POST', '/api/mobile/attachments/upload', 'respond', true, 'never'),
|
|
137
|
-
|
|
173
|
+
// WP-1 (docs/remote-session-image-upload-forwarding-2026-10-24.html §6, D-3): the box-side
|
|
174
|
+
// image-draft family a hub forwards for a MIRROR session. Only the `peerControl` flag is added —
|
|
175
|
+
// scope, mutation and step-up are unchanged for every other caller — exactly like the hub §6.2 rows.
|
|
176
|
+
regex('GET', /^\/api\/sessions\/[^/]+\/image-drafts(?:\/[^/]+\/content)?$/, 'read', false, 'never', { peerControl: true }),
|
|
138
177
|
regex('GET', /^\/api\/sessions\/[^/]+\/native-subagents\/[^/]+\/messages$/, 'read', false, 'never'),
|
|
139
178
|
// WP-0 (docs/exited-worker-transcript-view-2026-10-17.html §6c): a routed worker's own read-only
|
|
140
179
|
// transcript page. Same posture as its native sibling -- GET, read scope, non-mutation, never
|
|
141
180
|
// step-up. Worker lifecycle (resume/kill/retire) stays on its own stronger rules.
|
|
142
181
|
regex('GET', /^\/api\/sessions\/[^/]+\/worker-transcript\/messages$/, 'read', false, 'never'),
|
|
143
|
-
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts$/, 'respond', true, 'remote'),
|
|
144
|
-
regex('PUT', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+$/, 'respond', true, 'remote'),
|
|
145
|
-
regex('DELETE', /^\/api\/sessions\/[^/]+\/image-drafts
|
|
146
|
-
regex('
|
|
147
|
-
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/
|
|
148
|
-
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/
|
|
182
|
+
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts$/, 'respond', true, 'remote', { peerControl: true }),
|
|
183
|
+
regex('PUT', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+$/, 'respond', true, 'remote', { peerControl: true }),
|
|
184
|
+
regex('DELETE', /^\/api\/sessions\/[^/]+\/image-drafts$/, 'respond', true, 'remote', { peerControl: true }),
|
|
185
|
+
regex('DELETE', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+$/, 'respond', true, 'remote', { peerControl: true }),
|
|
186
|
+
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/delivery$/, 'respond', true, 'remote', { peerControl: true }),
|
|
187
|
+
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/freeze$/, 'respond', true, 'remote', { peerControl: true }),
|
|
188
|
+
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/submit$/, 'respond', true, 'remote', { peerControl: true }),
|
|
149
189
|
// F2 (image-attach-latency-redesign fix round): retry only flips failed->ready so the
|
|
150
190
|
// reconciler re-delivers; same mutation class as the other draft-lifecycle routes above.
|
|
151
|
-
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/retry$/, 'respond', true, 'remote'),
|
|
191
|
+
regex('POST', /^\/api\/sessions\/[^/]+\/image-drafts\/[^/]+\/retry$/, 'respond', true, 'remote', { peerControl: true }),
|
|
152
192
|
|
|
153
193
|
exact('POST', '/api/permissions/walle-check', 'admin', true, 'remote', { loopbackOnly: true }),
|
|
154
194
|
exact('POST', '/api/permissions/check', 'admin', true, 'remote', { loopbackOnly: true }),
|
|
@@ -160,6 +200,55 @@ const HTTP_AUTH_RULES = Object.freeze([
|
|
|
160
200
|
exact('GET', '/api/permissions/doctor', 'admin', false, 'remote', { loopbackOnly: true }),
|
|
161
201
|
|
|
162
202
|
exact('GET', '/api/setup/network', 'admin', false, 'never'),
|
|
203
|
+
// WP-0 (remote-machine-hub-sessions-2026-10-22.html §6.1): the hub machine registry. Same
|
|
204
|
+
// posture as the setup routes above — the registry decides which boxes this CTM will dial and
|
|
205
|
+
// trusts them with peer-control later (WP-1), so it is an admin act and it is LOCAL-ONLY:
|
|
206
|
+
// loopbackOnly, which (shared/request-provenance.js) already refuses any forwarded/relayed
|
|
207
|
+
// request. Registration is a user action in Settings → Machines (D-11, no flag).
|
|
208
|
+
// WP-2a (machine-pool-picker-pairing-dashboard-2026-10-24.html §5 D-13 rev 2): loopbackOnly is
|
|
209
|
+
// RELAXED. The phone reads GET /api/machines off-LAN (public/m/app.js:13348) and the phone parity
|
|
210
|
+
// contract (no phone-only machine feature) makes the management rows reachable too. The rows carry
|
|
211
|
+
// URLs/hostnames and a credential CLASS WORD, never a token (peerCredential: 'pool'|'env'|'missing').
|
|
212
|
+
// GET /api/machines read, no step-up, NOT loopbackOnly
|
|
213
|
+
// POST /api/machines admin + step-up remote, NOT loopbackOnly
|
|
214
|
+
// POST /api/machines/:id/remove admin + step-up remote, NOT loopbackOnly
|
|
215
|
+
exact('GET', '/api/machines', 'read', false, 'never'),
|
|
216
|
+
exact('POST', '/api/machines', 'admin', true, 'remote'),
|
|
217
|
+
regex('POST', /^\/api\/machines\/[^/]+\/remove$/, 'admin', true, 'remote'),
|
|
218
|
+
// WP-3 (machine-pool-picker-pairing-dashboard-2026-10-24.html §6.3, D-13, §9 S-16): the MEMBER
|
|
219
|
+
// side of pairing — this machine joins a pool by redeeming the box's code. It mints nothing and
|
|
220
|
+
// reads nothing back, but it adopts a shared secret and rewrites this member's roster, so it is
|
|
221
|
+
// the same act as POST /api/machines: admin + step-up remote, NOT loopbackOnly, and NOT
|
|
222
|
+
// peerControl (a peer holds the pool bearer already and never needs to pair over it — pinning it
|
|
223
|
+
// false is what keeps tests/peer-control-auth.test.js's exact set honest).
|
|
224
|
+
exact('POST', '/api/machines/pair', 'admin', true, 'remote'),
|
|
225
|
+
// WP-3 §6.3/D-13: the BOX side's offer mint. Admin + step-up remote for the same reason: the
|
|
226
|
+
// response carries a live bearer code for one redemption, so it is a credential-minting act.
|
|
227
|
+
// POST /api/machine-pairing/join is deliberately ABSENT here — server.js dispatches phase
|
|
228
|
+
// 'api-cloudflare' before the auth wall (lib/api/machine-pairing.js states why), so a row would be
|
|
229
|
+
// a rule the dispatcher never consults.
|
|
230
|
+
exact('POST', '/api/machine-pairing/offers', 'admin', true, 'remote'),
|
|
231
|
+
// WP-2a D-4/D-13: this machine's OWN settings (name override, favorite, hidden). A user on any
|
|
232
|
+
// primary edits a REMOTE member through the ?machine= proxy, so the row must also admit the peer
|
|
233
|
+
// bearer — peerControl: true. Guest/user scope is `read`/never on GET (nothing here is a secret)
|
|
234
|
+
// and admin + step-up remote on POST. lib/api/machine-pool.js refuses a non-peer on the pool rows.
|
|
235
|
+
exact('GET', '/api/machine-self', 'read', false, 'never', { peerControl: true }),
|
|
236
|
+
exact('POST', '/api/machine-self', 'admin', true, 'remote', { peerControl: true }),
|
|
237
|
+
// WP-2a D-13/S-27: the pool routes. There is no dedicated "peer-only" rule mechanism in this
|
|
238
|
+
// codebase (peerControl ADDS the peer bearer to the row's normal reach), so the roster/rotate rows
|
|
239
|
+
// are declared `admin` + peerControl and the HANDLER refuses a non-peer principal with 403
|
|
240
|
+
// `peer_only` (lib/api/machine-pool.js) — the same reviewer-visible pattern as the machine-self
|
|
241
|
+
// rows. POST /api/pool/leave is an ordinary admin + step-up remote act.
|
|
242
|
+
exact('GET', '/api/pool/roster', 'admin', false, 'never', { peerControl: true }),
|
|
243
|
+
exact('POST', '/api/pool/roster', 'admin', true, 'remote', { peerControl: true }),
|
|
244
|
+
exact('POST', '/api/pool/rotate', 'admin', true, 'remote', { peerControl: true }),
|
|
245
|
+
exact('POST', '/api/pool/leave', 'admin', true, 'remote'),
|
|
246
|
+
// WP-1 (machine-pool-picker-pairing-dashboard-2026-10-24.html §6.1/D-13): this machine's own host
|
|
247
|
+
// stats. Read scope, and deliberately NOT loopbackOnly — the pool picker on a phone reaches a
|
|
248
|
+
// member through the hub's ?machine= proxy, which arrives as a non-loopback peer-control request,
|
|
249
|
+
// and the payload is hardware counters, not secrets. peerControl: true is the one reach delta this
|
|
250
|
+
// WP adds (tests/peer-control-auth.test.js pins it).
|
|
251
|
+
exact('GET', '/api/host/stats', 'read', false, 'never', { peerControl: true }),
|
|
163
252
|
exact('GET', '/api/setup/network/microsoft-dev-tunnel/probe', 'admin', false, 'never'),
|
|
164
253
|
// Go-Live wizard read view (design doc §Bootstrap & Auth): same posture as
|
|
165
254
|
// GET /api/setup/network — admin scope, no step-up, remote-readable. The
|
|
@@ -201,16 +290,23 @@ const HTTP_AUTH_RULES = Object.freeze([
|
|
|
201
290
|
regex('POST', /^\/api\/models\/workflows\/[^/]+\/runs$/, 'admin', true, 'remote'),
|
|
202
291
|
regex('POST', /^\/api\/models\/workflow-runs\/[^/]+\/(?:abort|skip|retry|decide|rate)$/, 'admin', true, 'remote'),
|
|
203
292
|
regex('POST', /^\/api\/models\/workflow-suggestions\/[^/]+$/, 'admin', true, 'remote'),
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
exact('GET', '/api/
|
|
208
|
-
|
|
293
|
+
// WP-3 (DGX Spark coding readiness §2): the hub reads a BOX's sign-in truth with the peer bearer
|
|
294
|
+
// before it forwards a create — a box whose Claude/Codex CLI is signed out must be refused at the
|
|
295
|
+
// hub, not answered with a login prompt. `peerControl: true` is the only change: plain `read`/never.
|
|
296
|
+
exact('GET', '/api/providers/login-state', 'read', false, 'never', { peerControl: true }),
|
|
297
|
+
|
|
298
|
+
// WP-1 (§6.2/§6.5): the hub's remote picker calls these three on the BOX, over the relay
|
|
299
|
+
// leg, with the peer bearer — hence `peerControl: true`. Scope/step-up are unchanged: they
|
|
300
|
+
// were already plain `read`/never for every other caller.
|
|
301
|
+
exact('GET', '/api/projects', 'read', false, 'never', { peerControl: true }),
|
|
302
|
+
exact('GET', '/api/path-suggest', 'read', false, 'never', { peerControl: true }),
|
|
303
|
+
exact('POST', '/api/path-status', 'read', false, 'never', { peerControl: true }),
|
|
209
304
|
exact('GET', '/api/rules', 'read', false, 'never'),
|
|
210
305
|
exact('GET', '/api/rules/list', 'read', false, 'never'),
|
|
211
306
|
exact('PUT', '/api/rules', 'admin', true, 'remote'),
|
|
212
307
|
|
|
213
|
-
|
|
308
|
+
// WP-1 (§6.2): the hub's catalog read for a mirrored box session.
|
|
309
|
+
exact('GET', '/api/recent-sessions', 'read', false, 'never', { peerControl: true }),
|
|
214
310
|
exact('GET', '/api/sessions/standup', 'read', false, 'never'),
|
|
215
311
|
exact('GET', '/api/sessions/git-status', 'read', false, 'never'),
|
|
216
312
|
exact('GET', '/api/session/prompts', 'read', false, 'never'),
|
|
@@ -243,6 +339,16 @@ const HTTP_AUTH_RULES = Object.freeze([
|
|
|
243
339
|
regex('POST', /^\/api\/sessions\/[^/]+\/attention-dismiss$/, 'respond', true, 'never'),
|
|
244
340
|
regex('POST', /^\/api\/sessions\/[^/]+\/submit-prompt$/, 'respond', true, 'never'),
|
|
245
341
|
regex('POST', /^\/api\/sessions\/[^/]+\/interrupt$/, 'respond', true, 'remote'),
|
|
342
|
+
// WP-1 (docs/remote-machine-hub-sessions-2026-10-22.html §6.2): the box-side lifecycle verbs the
|
|
343
|
+
// hub forwards verbatim for a session the BOX owns (WP-6 added pin: the hub's HTTP lifecycle route
|
|
344
|
+
// forwards all four, and a pin the box refused would surface as a bare 403). Declared explicitly (rather than flagging the
|
|
345
|
+
// /^\/api\/sessions\// catch-all below, which would hand the peer bearer every unmatched session
|
|
346
|
+
// POST) with the catch-all's own admin/step-up posture, plus peerControl.
|
|
347
|
+
regex('POST', /^\/api\/sessions\/[^/]+\/(?:park|wake|revive|pin)$/, 'admin', true, 'remote', { peerControl: true }),
|
|
348
|
+
// WP-1 (§6.2, hub WP-2/§E): where this session's provider transcripts live, read by the hub for a
|
|
349
|
+
// mirrored box session. Previously had NO rule (loopback-only in practice) — the explicit read row
|
|
350
|
+
// is what makes it reachable for the peer bearer; scope read / no step-up / no mutation.
|
|
351
|
+
regex('GET', /^\/api\/sessions\/[^/]+\/transcripts$/, 'read', false, 'never', { peerControl: true }),
|
|
246
352
|
// Lead-model switch (picker design WP-2): same class as the WS 'model-change' it replaces for
|
|
247
353
|
// Claude/Codex tabs (admin + step-up); reading the request state is a plain read.
|
|
248
354
|
regex('POST', /^\/api\/sessions\/[^/]+\/lead-model$/, 'admin', true, 'remote'),
|
|
@@ -272,7 +378,15 @@ const HTTP_AUTH_RULES = Object.freeze([
|
|
|
272
378
|
exact('POST', '/api/instance/update/apply', 'admin', true, 'remote'),
|
|
273
379
|
exact('GET', '/api/instance/migrate/export', 'admin', false, 'never'),
|
|
274
380
|
exact('POST', '/api/instance/migrate/import', 'admin', true, 'remote'),
|
|
275
|
-
|
|
381
|
+
// WP-3 (remote-agent-sign-in-from-hub-browser-2026-10-25.html §5 D-7/D-9, §6): the hub forwards
|
|
382
|
+
// a sign-in to the BOX that must run it, over the peer bearer — so both POSTs also declare
|
|
383
|
+
// `peerControl: true`. Scope/mutation/step-up are unchanged: a peer is still evaluated as
|
|
384
|
+
// `admin` + remote step-up, and every non-peer caller sees exactly what it saw before.
|
|
385
|
+
regex('POST', /^\/api\/providers\/[^/]+\/login$/, 'admin', true, 'remote', { peerControl: true }),
|
|
386
|
+
// WP-1 (design §6, S-8): the code-return route is the /login twin — a write on the box
|
|
387
|
+
// (one PTY frame into the flow's own tab), so the same admin scope and the same remote
|
|
388
|
+
// step-up ceremony as starting a sign-in.
|
|
389
|
+
regex('POST', /^\/api\/providers\/[^/]+\/login\/code$/, 'admin', true, 'remote', { peerControl: true }),
|
|
276
390
|
exact('GET', '/api/sync/outbox', 'sync', false, 'never'),
|
|
277
391
|
exact('GET', '/api/sync/manifest', 'sync', false, 'never'),
|
|
278
392
|
exact('POST', '/api/sync/outbox/ack', 'sync', true, 'never'),
|
|
@@ -331,10 +445,14 @@ const HTTP_AUTH_RULES = Object.freeze([
|
|
|
331
445
|
regex('PUT', /^\/api\/workspaces\//, 'admin', true, 'remote'),
|
|
332
446
|
regex('DELETE', /^\/api\/workspaces\//, 'admin', true, 'remote'),
|
|
333
447
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
448
|
+
// WP-1 (§6.2/§6.5): worktree LIST + CREATE are called on the box by the hub with the peer
|
|
449
|
+
// bearer, so both rows declare peerControl. Their scope/step-up are untouched.
|
|
450
|
+
exact('GET', '/api/worktrees', 'read', false, 'never', { peerControl: true }),
|
|
451
|
+
exact('POST', '/api/worktrees/create', 'create', true, 'remote', { peerControl: true }),
|
|
452
|
+
// Hub WP-5 amendment: an async create on the box returns a BOX job id, so the hub's progress poll
|
|
453
|
+
// and its cancel are proxied there too (lib/remote-machine-proxy.js) — same posture, plus the flag.
|
|
454
|
+
regex('GET', /^\/api\/worktrees\/create-jobs\/[a-zA-Z0-9_-]+(?:-[a-zA-Z0-9_-]+)*$/, 'read', false, 'never', { peerControl: true }),
|
|
455
|
+
regex('POST', /^\/api\/worktrees\/create-jobs\/[a-zA-Z0-9_-]+(?:-[a-zA-Z0-9_-]+)*\/cancel$/, 'admin', true, 'remote', { peerControl: true }),
|
|
338
456
|
regex('POST', /^\/api\/worktrees\//, 'admin', true, 'remote'),
|
|
339
457
|
regex('DELETE', /^\/api\/worktrees\//, 'admin', true, 'remote'),
|
|
340
458
|
|
|
@@ -412,6 +530,14 @@ function rule(scope, mutation, stepUp, extra = {}) {
|
|
|
412
530
|
return Object.freeze({ scope, mutation: !!mutation, stepUp, ...extra });
|
|
413
531
|
}
|
|
414
532
|
|
|
533
|
+
// WP-1 (§6.2): the one way a rule row declares that the peer bearer may reach it. Kept as a
|
|
534
|
+
// distinct flag rather than a scope union so `scope` (and everything that reads it — the
|
|
535
|
+
// step-up matrix, the owner manifest, /api/auth/rules consumers) keeps its existing meaning;
|
|
536
|
+
// authorize() consults the flag directly.
|
|
537
|
+
function peerControl(scope, mutation, stepUp) {
|
|
538
|
+
return rule(scope, mutation, stepUp, { peerControl: true });
|
|
539
|
+
}
|
|
540
|
+
|
|
415
541
|
function exact(method, path, scope, mutation, stepUp, extra = {}) {
|
|
416
542
|
return Object.freeze({ kind: 'http', method, path, exact: true, ...rule(scope, mutation, stepUp, extra) });
|
|
417
543
|
}
|
|
@@ -437,10 +563,18 @@ function getWsAuthRule(messageType) {
|
|
|
437
563
|
function hasScope(auth, requiredScope) {
|
|
438
564
|
if (!requiredScope) return true;
|
|
439
565
|
const scopes = auth?.scopes instanceof Set ? auth.scopes : new Set(auth?.scopes || []);
|
|
440
|
-
|
|
566
|
+
// Machine-to-machine capabilities are never implied by `admin`: sync keeps its existing
|
|
567
|
+
// special case, and peer-control (WP-1 §6.2) joins it — it is minted only on the peer
|
|
568
|
+
// bearer, so an admin/device token can never acquire it through the admin expansion.
|
|
569
|
+
if (requiredScope === 'sync' || requiredScope === PEER_CONTROL_SCOPE) return scopes.has(requiredScope);
|
|
441
570
|
return scopes.has('admin') || scopes.has(requiredScope);
|
|
442
571
|
}
|
|
443
572
|
|
|
573
|
+
function hasPeerControlScope(auth) {
|
|
574
|
+
const scopes = auth?.scopes instanceof Set ? auth.scopes : new Set(auth?.scopes || []);
|
|
575
|
+
return scopes.has(PEER_CONTROL_SCOPE);
|
|
576
|
+
}
|
|
577
|
+
|
|
444
578
|
function authorize(auth, rule, options = {}) {
|
|
445
579
|
if (!rule) return { ok: false, status: 403, code: 'auth_rule_missing' };
|
|
446
580
|
if (!auth?.authenticated) return { ok: false, status: 401, code: 'unauthorized' };
|
|
@@ -453,6 +587,10 @@ function authorize(auth, rule, options = {}) {
|
|
|
453
587
|
if (rule.bootstrapLocal && options.zeroWebauthnCredentials && !auth.isLoopback && !options.isLocalOrTailnet) {
|
|
454
588
|
return { ok: false, status: 403, code: 'bootstrap_local_required' };
|
|
455
589
|
}
|
|
590
|
+
// WP-1 (§6.2): a row that declares peerControl is satisfied by the peer-control scope in
|
|
591
|
+
// full — the peer is a machine credential that cannot perform a passkey step-up ceremony,
|
|
592
|
+
// exactly like the sync scope on /api/sync/*. No other rule grants it anything.
|
|
593
|
+
if (rule.peerControl && hasPeerControlScope(auth)) return { ok: true };
|
|
456
594
|
if (!hasScope(auth, rule.scope)) return { ok: false, status: 403, code: 'insufficient_scope' };
|
|
457
595
|
if (options.enforceStepUp && !auth.isLoopback && rule.stepUp === 'remote' && !auth.stepUpFresh) {
|
|
458
596
|
return { ok: false, status: 403, code: 'step_up_required' };
|
|
@@ -471,6 +609,7 @@ function publicRule(rule) {
|
|
|
471
609
|
stepUp: rule.stepUp,
|
|
472
610
|
loopbackOnly: !!rule.loopbackOnly,
|
|
473
611
|
bootstrapLocal: !!rule.bootstrapLocal,
|
|
612
|
+
peerControl: !!rule.peerControl,
|
|
474
613
|
};
|
|
475
614
|
}
|
|
476
615
|
|
|
@@ -484,11 +623,13 @@ function listWsAuthRules() {
|
|
|
484
623
|
|
|
485
624
|
module.exports = {
|
|
486
625
|
ALL_SCOPES,
|
|
626
|
+
PEER_CONTROL_SCOPE,
|
|
487
627
|
HTTP_AUTH_RULES,
|
|
488
628
|
WS_AUTH_RULES,
|
|
489
629
|
authorize,
|
|
490
630
|
getHttpAuthRule,
|
|
491
631
|
getWsAuthRule,
|
|
632
|
+
hasPeerControlScope,
|
|
492
633
|
hasScope,
|
|
493
634
|
listHttpAuthRules,
|
|
494
635
|
listWsAuthRules,
|
|
@@ -145,7 +145,8 @@ function createBroadcastPayloadSerializer({
|
|
|
145
145
|
if (options.isMemoBypass?.(session) || !memo) return null;
|
|
146
146
|
signature += memo.key + '\x1e';
|
|
147
147
|
}
|
|
148
|
-
|
|
148
|
+
// extraSignature: rows appended AFTER the local snapshot (the machine hub's remote rows).
|
|
149
|
+
return signature + '#' + options.sourceSessions.length + (options.extraSignature ? '#' + options.extraSignature : '');
|
|
149
150
|
}
|
|
150
151
|
|
|
151
152
|
function makeRecord(envelope, clients, signature) {
|
|
@@ -1189,6 +1189,7 @@ class BrowserSessionBroker {
|
|
|
1189
1189
|
if (this.profileMigration?.ensureProfile) record = await this.profileMigration.ensureProfile(record);
|
|
1190
1190
|
const existing = await this._boundHandle(record, { required: false });
|
|
1191
1191
|
if (existing && (typeof this.adapter.isLive !== 'function' || this.adapter.isLive(existing))) return existing;
|
|
1192
|
+
if (existing) await this._dropDeadHandle(record, existing);
|
|
1192
1193
|
let handle;
|
|
1193
1194
|
try {
|
|
1194
1195
|
handle = await this.adapter.open({ contextId: record.contextId, profileDir: record.profileDir,
|
|
@@ -1210,7 +1211,7 @@ class BrowserSessionBroker {
|
|
|
1210
1211
|
record = await this._ensureProcessOwnership(record, { allowTakeover: true });
|
|
1211
1212
|
const existing = await this._boundHandle(record, { required: false });
|
|
1212
1213
|
if (existing && (typeof this.adapter.isLive !== 'function' || this.adapter.isLive(existing))) return record;
|
|
1213
|
-
if (existing) this.
|
|
1214
|
+
if (existing) await this._dropDeadHandle(record, existing);
|
|
1214
1215
|
let recovered = record;
|
|
1215
1216
|
if (record.processGeneration !== this.processGeneration || existing || record.status === 'dormant') {
|
|
1216
1217
|
recovered = {
|
|
@@ -1292,6 +1293,13 @@ class BrowserSessionBroker {
|
|
|
1292
1293
|
return handle;
|
|
1293
1294
|
}
|
|
1294
1295
|
|
|
1296
|
+
// A handle that is no longer live (lost page, disconnected browser) still owns whatever of its
|
|
1297
|
+
// browser survives; close it rather than just forgetting it.
|
|
1298
|
+
async _dropDeadHandle(record, handle) {
|
|
1299
|
+
if (this.handles.get(record.contextId) === handle) this.handles.delete(record.contextId);
|
|
1300
|
+
await this.adapter.close(handle).catch((error) => this._reportFailure('dead-handle-close', error));
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1295
1303
|
_rebindLocalHandle(record) {
|
|
1296
1304
|
const handle = this.handles.get(record.contextId);
|
|
1297
1305
|
if (!handle) return;
|
|
@@ -31,6 +31,9 @@ function chromiumChildEnvironment(parentEnvironment = process.env) {
|
|
|
31
31
|
return environment;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
// Chromium ChildProcesses whose group has been reaped, so the exit and close paths reap it once.
|
|
35
|
+
const REAPED = new WeakSet();
|
|
36
|
+
|
|
34
37
|
class BrowserUseAdapter {
|
|
35
38
|
constructor({
|
|
36
39
|
puppeteer = null,
|
|
@@ -84,6 +87,11 @@ class BrowserUseAdapter {
|
|
|
84
87
|
allowedOrigins: new Set([allowedOrigin, ...allowedOrigins].filter(Boolean)),
|
|
85
88
|
blockedOrigin: '', disconnected: false, lastMainResponseStatus: 0 };
|
|
86
89
|
browser.once('disconnected', () => { handle.disconnected = true; });
|
|
90
|
+
// A browser lost without close() (crash, external kill) leaves the same helpers behind. Reap on
|
|
91
|
+
// the main process's exit, never on 'disconnected': the CDP socket drops while Chromium is still
|
|
92
|
+
// shutting down, and killing its group then loses storage it has not flushed yet.
|
|
93
|
+
const child = typeof browser.process === 'function' ? browser.process() : null;
|
|
94
|
+
child?.once?.('exit', () => this._reapBrowserProcess(child, 'browser-exit'));
|
|
87
95
|
page.on('response', (response) => {
|
|
88
96
|
try {
|
|
89
97
|
const request = response.request();
|
|
@@ -99,7 +107,7 @@ class BrowserUseAdapter {
|
|
|
99
107
|
handle.documentGeneration = await this._documentGeneration(handle);
|
|
100
108
|
return handle;
|
|
101
109
|
} catch (error) {
|
|
102
|
-
await
|
|
110
|
+
await this._closeBrowser(browser, 'open-cleanup');
|
|
103
111
|
throw error;
|
|
104
112
|
}
|
|
105
113
|
}
|
|
@@ -628,10 +636,33 @@ class BrowserUseAdapter {
|
|
|
628
636
|
|
|
629
637
|
async close(handle) {
|
|
630
638
|
if (!handle?.browser) return;
|
|
631
|
-
await handle.browser
|
|
639
|
+
await this._closeBrowser(handle.browser, 'browser-close');
|
|
632
640
|
handle.disconnected = true;
|
|
633
641
|
}
|
|
634
642
|
|
|
643
|
+
// browser.close() returns once Chromium's browser process exits, but its renderer/GPU helpers
|
|
644
|
+
// can outlive it (puppeteer launches Chromium detached, so they share its process group), and
|
|
645
|
+
// its crashpad handler daemonizes into a group of its own still holding the stdio Chromium
|
|
646
|
+
// inherited. Either one keeps this process's end of that stdio open: orphaned helpers pile up
|
|
647
|
+
// and a Node process that should exit never does. Reap the group, then drop our end.
|
|
648
|
+
async _closeBrowser(browser, operation) {
|
|
649
|
+
const child = typeof browser.process === 'function' ? browser.process() : null;
|
|
650
|
+
await browser.close().catch((error) => this._reportFailure(operation, error));
|
|
651
|
+
this._reapBrowserProcess(child, operation);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
_reapBrowserProcess(child, operation) {
|
|
655
|
+
if (!child || REAPED.has(child)) return;
|
|
656
|
+
REAPED.add(child);
|
|
657
|
+
if (Number.isSafeInteger(child.pid) && child.pid > 0) {
|
|
658
|
+
// ESRCH: the group is already empty. EPERM: macOS refuses a group whose members are all zombies
|
|
659
|
+
// (every member here is this user's Chromium, so it is never a foreign process).
|
|
660
|
+
try { process.kill(-child.pid, 'SIGKILL'); }
|
|
661
|
+
catch (error) { if (error?.code !== 'ESRCH' && error?.code !== 'EPERM') this._reportFailure(`${operation}-group`, error); }
|
|
662
|
+
}
|
|
663
|
+
for (const stream of child?.stdio || []) stream?.destroy?.();
|
|
664
|
+
}
|
|
665
|
+
|
|
635
666
|
_reportFailure(operation, error) {
|
|
636
667
|
const kind = String(error?.code || error?.name || 'unknown').slice(0, 128);
|
|
637
668
|
this.logger?.warn?.(`[browser-use-adapter] ${operation} failed (${kind})`);
|