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
|
@@ -8,16 +8,13 @@ const DEFAULT_DEADLINES_MS = Object.freeze({
|
|
|
8
8
|
applied: 100,
|
|
9
9
|
painted: 50,
|
|
10
10
|
});
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
applied: 'supersede-obsolete',
|
|
19
|
-
painted: 'report-only',
|
|
20
|
-
});
|
|
11
|
+
// Every incident is report-only: a missed boundary deadline is latency evidence, never proof
|
|
12
|
+
// that a frame was lost. Under host load keydown->frame-sent runs 400-800 ms on a healthy
|
|
13
|
+
// session, and the replacement keyframe the 'frame-sent' miss used to force re-delivered a
|
|
14
|
+
// screen the client had already applied (B3: a duplicate full-grid write per late frame). Lost
|
|
15
|
+
// frames are repaired by the delivery path's own seq/baseline checks, not by a clock.
|
|
16
|
+
// (codex-running-composer-latency-round5-2026-08-09 §11 WP-6 rollback: "Return watchdog to
|
|
17
|
+
// report-only; preserve diagnostics".)
|
|
21
18
|
|
|
22
19
|
function createVisibleRevisionWatchdog(options = {}) {
|
|
23
20
|
const now = options.now || Date.now;
|
|
@@ -88,12 +85,11 @@ function createVisibleRevisionWatchdog(options = {}) {
|
|
|
88
85
|
deadlineMs,
|
|
89
86
|
previousBoundary,
|
|
90
87
|
missingBoundary,
|
|
91
|
-
recoveryAction: RECOVERY_ACTION[missingBoundary] || 'report-only',
|
|
92
88
|
};
|
|
93
89
|
row.reported = true;
|
|
94
90
|
incidents.push(incident); report(incident);
|
|
95
91
|
recover({ ...incident, scope: row.clientId ? 'client' : 'session' });
|
|
96
|
-
|
|
92
|
+
pending.delete(id);
|
|
97
93
|
}
|
|
98
94
|
return incidents;
|
|
99
95
|
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// WP-3 (§5 D-8): the closed skip-reason vocabulary for the deferred-restore wake pipeline --
|
|
3
|
+
// CamelCase machine tokens (P-3, the K8s-condition convention) that wakeSkips aggregates on; the
|
|
4
|
+
// human sentence lives beside them in a session diagnostic. Closed per release: additive only.
|
|
5
|
+
//
|
|
6
|
+
// Extracted from lib/session-lifecycle.js (which re-exports it) so server.js can name these tokens
|
|
7
|
+
// without growing past its file-size ratchet and so the vocabulary closure test can read the list
|
|
8
|
+
// without pulling in the lifecycle module.
|
|
9
|
+
const WAKE_SKIP_REASONS = Object.freeze([
|
|
10
|
+
'OwnerRecoveryFence',
|
|
11
|
+
'BackoffTaskMissingSessionId',
|
|
12
|
+
'BackoffSessionReplaced',
|
|
13
|
+
'CapacityRejected',
|
|
14
|
+
'WakeBlocked',
|
|
15
|
+
'GateBusy',
|
|
16
|
+
'ResumeBlocked',
|
|
17
|
+
'CodexLinkStale',
|
|
18
|
+
'CodexLinkUnavailable',
|
|
19
|
+
'CodexLinkBlocked',
|
|
20
|
+
'LaunchPlanStale',
|
|
21
|
+
'SessionReplaced',
|
|
22
|
+
'McpWaitStale',
|
|
23
|
+
'ResumeStormStale',
|
|
24
|
+
'LaunchPlanNotSpawning',
|
|
25
|
+
'RetryNotDeferred',
|
|
26
|
+
'RetryAlreadyStarting',
|
|
27
|
+
'RetryNoClients',
|
|
28
|
+
'DrainPaused',
|
|
29
|
+
'DrainPressureSkipped',
|
|
30
|
+
'DrainIneligible',
|
|
31
|
+
'SourceRecoveryEnvironmentInvalid',
|
|
32
|
+
'LaunchGenerationUnavailable',
|
|
33
|
+
'PrepareFailed',
|
|
34
|
+
'LaunchBlocked',
|
|
35
|
+
'LeadTransitionTargetOwnerMismatch',
|
|
36
|
+
'LeadTransitionSourceOwnerMismatch',
|
|
37
|
+
'LeadTransitionReconciliationFailed',
|
|
38
|
+
'LeadTransitionReconciliationUnavailable',
|
|
39
|
+
'LeadSourceRecoveryResumeMismatch',
|
|
40
|
+
'TranscriptRestoreFailed',
|
|
41
|
+
'LeadTransitionAttemptIssueRefused',
|
|
42
|
+
'CreateRollback',
|
|
43
|
+
'AutoStartNotAttach',
|
|
44
|
+
]);
|
|
45
|
+
|
|
46
|
+
module.exports = { WAKE_SKIP_REASONS };
|
|
@@ -196,6 +196,21 @@ async function listPidsWithPpid() {
|
|
|
196
196
|
return rows;
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
+
// D-8 (WP-5, docs/clock-free-tests-followups-2026-10-23.html §6): proving that ONE pid is ours
|
|
200
|
+
// needs only that pid's row. The whole `ps -axo` table is 82-474 ms and ~1 MB on a busy host; a
|
|
201
|
+
// `ps -p <pid>` read is a single process. Rows are parsed by the SAME parser the whole-table read
|
|
202
|
+
// uses, so ownership (isOwnedRow) sees an identical row shape.
|
|
203
|
+
// rows -> ps ran; [] means the pid has no row (not ours, or already gone)
|
|
204
|
+
// null -> the probe itself failed; callers must treat that as "not proven owned", never as [].
|
|
205
|
+
async function listPidRow(pid) {
|
|
206
|
+
const { err, stdout } = await execFileCapture('ps', ['-p', String(pid), '-o', 'pid=,ppid=,command='], {
|
|
207
|
+
timeout: PS_SNAPSHOT_TIMEOUT_MS,
|
|
208
|
+
maxBuffer: 1024 * 1024,
|
|
209
|
+
});
|
|
210
|
+
if (err) return null;
|
|
211
|
+
return parsePsWithPpid(stdout);
|
|
212
|
+
}
|
|
213
|
+
|
|
199
214
|
// WP-P2 orphan reap path signature — a small, pure, directly-testable
|
|
200
215
|
// predicate deliberately kept separate from the exact-title match. A bare
|
|
201
216
|
// `command.includes(rootMarker)` has two failure modes: (1) no path-boundary
|
|
@@ -223,6 +238,7 @@ const defaultProcessOps = {
|
|
|
223
238
|
findListeningPidsByExactCommand,
|
|
224
239
|
getProcessUptime,
|
|
225
240
|
listPidsWithPpid,
|
|
241
|
+
listPidRow,
|
|
226
242
|
};
|
|
227
243
|
|
|
228
244
|
function isSourceCheckout(rootDir) {
|
|
@@ -391,6 +407,13 @@ function createWalleSupervisor({
|
|
|
391
407
|
// spawned child (see spawnWalle()) — childPid always takes precedence.
|
|
392
408
|
let adoptedPid = null;
|
|
393
409
|
let lastBootAdoption = null; // WP-7 (S-26)
|
|
410
|
+
// D-7 (WP-5): the status GET's snapshot, declared HERE (not next to getStatusSnapshot())
|
|
411
|
+
// because killAll/apiStop/restartQuiet — any of which may run before that statement — clear it.
|
|
412
|
+
// `statusSnapshot` is the last computed status + its timestamp; `statusSnapshotInFlight` is the
|
|
413
|
+
// single-flight slot concurrent getStatusSnapshot() callers share.
|
|
414
|
+
let statusSnapshot = null; // { status, at }
|
|
415
|
+
let statusSnapshotInFlight = null;
|
|
416
|
+
let statusSnapshotGeneration = 0; // bumped by every invalidation; an older in-flight result is never stored
|
|
394
417
|
// WP-S2 crash-streak/backoff state. crashStreak counts consecutive
|
|
395
418
|
// unexpected exits (reset once the child has stayed ready for
|
|
396
419
|
// restartStreakResetMs — see markChildReady()/childBecameReadyAt).
|
|
@@ -1797,6 +1820,7 @@ function createWalleSupervisor({
|
|
|
1797
1820
|
// longer childExitWaitMs so a wall-e mid-drain has room to exit cleanly
|
|
1798
1821
|
// instead of being SIGKILLed out from under itself.
|
|
1799
1822
|
async function killAll(waitMs = 2000) {
|
|
1823
|
+
invalidateStatusSnapshot();
|
|
1800
1824
|
artifactAuthorityRegistry.clear('wall_e_stopping');
|
|
1801
1825
|
const pidSet = await collectManagedPids();
|
|
1802
1826
|
if (pidSet.size === 0) return waitForPortFree();
|
|
@@ -2180,6 +2204,10 @@ function createWalleSupervisor({
|
|
|
2180
2204
|
// the failure exactly once — Node can emit `error`, `exit`, or both for a
|
|
2181
2205
|
// failed spawn depending on platform/failure mode.
|
|
2182
2206
|
function handleChildFailure({ code = null, signal = null, spawnError = null } = {}) {
|
|
2207
|
+
// D-7 (WP-5): the child is gone — drop any cached status that still says running:true.
|
|
2208
|
+
// Invalidate BEFORE the pid guard below: a duplicate/late exit event for an older pid is
|
|
2209
|
+
// still an exit, and the cached snapshot is stale either way.
|
|
2210
|
+
invalidateStatusSnapshot();
|
|
2183
2211
|
if (childPid !== myPid) return;
|
|
2184
2212
|
const failureAuthorityGeneration = childFailureAuthorityGeneration;
|
|
2185
2213
|
artifactAuthorityRegistry.clear('wall_e_exit');
|
|
@@ -2454,9 +2482,33 @@ function createWalleSupervisor({
|
|
|
2454
2482
|
// D-3/D-4 (kill authority): adoption (pid-file OR ps-title discovery)
|
|
2455
2483
|
// requires the SAME ownership check as collectManagedPids() — a
|
|
2456
2484
|
// same-title pid with a foreign ppid is never adopted and never becomes
|
|
2457
|
-
// pidSource:'discovered'.
|
|
2485
|
+
// pidSource:'discovered'.
|
|
2458
2486
|
const myPid = processOps.pid || process.pid;
|
|
2459
2487
|
const titles = new Set(processTitles());
|
|
2488
|
+
|
|
2489
|
+
// D-8 (WP-5): a live pid-file pid is proven ours by ONE `ps -p <pid>` row, never the
|
|
2490
|
+
// whole ~1 MB table (82-474 ms busy); injected processOps without listPidRow answer from
|
|
2491
|
+
// THEIR table, never a real ps. A null result (probe FAILED) is "not proven owned".
|
|
2492
|
+
if (savedPid && isAlive(savedPid)) {
|
|
2493
|
+
let savedRows = null;
|
|
2494
|
+
try {
|
|
2495
|
+
savedRows = processOps.listPidRow ? await processOps.listPidRow(savedPid) : processOps.listPidsWithPpid
|
|
2496
|
+
? (await processOps.listPidsWithPpid() || []).filter((row) => row.pid === savedPid) : await listPidRow(savedPid);
|
|
2497
|
+
} catch { /* expected: best-effort per-pid ownership probe; a failure leaves savedRows=null, so the saved pid is never (wrongly) adopted */ }
|
|
2498
|
+
const savedRow = Array.isArray(savedRows) ? indexRowsByPid(savedRows).get(savedPid) : undefined;
|
|
2499
|
+
if (isOwnedRow(savedRow, { titles, myPid })) {
|
|
2500
|
+
// WP-S3 (B11): this pid was NOT spawned by us this process lifetime —
|
|
2501
|
+
// adopt it into the same supervision the interval monitor gives a real
|
|
2502
|
+
// child (arm the monitor if it isn't already, and start tracking it so
|
|
2503
|
+
// a later tick's isAlive(adoptedPid) poll can detect it disappearing).
|
|
2504
|
+
adoptedPid = savedPid;
|
|
2505
|
+
if (!intentionallyStopped) ensureMcpReadinessMonitor(); // R-5: a status read must never re-arm after an intentional stop
|
|
2506
|
+
return statusWithDiagnostics({ running: true, pid: savedPid, pidSource: 'pid_file' });
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
if (savedPid) stalePid = savedPid;
|
|
2510
|
+
|
|
2511
|
+
// Discovery branch: the ONE consumer of the whole-table snapshot.
|
|
2460
2512
|
let rows = null;
|
|
2461
2513
|
try {
|
|
2462
2514
|
rows = processOps.listPidsWithPpid ? await processOps.listPidsWithPpid() : await listPidsWithPpid();
|
|
@@ -2467,17 +2519,6 @@ function createWalleSupervisor({
|
|
|
2467
2519
|
const psFailedFlag = Array.isArray(rows) ? {} : { ps_snapshot_failed: true };
|
|
2468
2520
|
const owned = (pid) => isOwnedRow(byPid.get(pid), { titles, myPid });
|
|
2469
2521
|
|
|
2470
|
-
if (savedPid && isAlive(savedPid) && owned(savedPid)) {
|
|
2471
|
-
// WP-S3 (B11): this pid was NOT spawned by us this process lifetime —
|
|
2472
|
-
// adopt it into the same supervision the interval monitor gives a real
|
|
2473
|
-
// child (arm the monitor if it isn't already, and start tracking it so
|
|
2474
|
-
// a later tick's isAlive(adoptedPid) poll can detect it disappearing).
|
|
2475
|
-
adoptedPid = savedPid;
|
|
2476
|
-
ensureMcpReadinessMonitor();
|
|
2477
|
-
return statusWithDiagnostics({ running: true, pid: savedPid, pidSource: 'pid_file' });
|
|
2478
|
-
}
|
|
2479
|
-
if (savedPid) stalePid = savedPid;
|
|
2480
|
-
|
|
2481
2522
|
let discovered = [];
|
|
2482
2523
|
try {
|
|
2483
2524
|
const titleRowPids = [...byPid.values()]
|
|
@@ -2494,7 +2535,7 @@ function createWalleSupervisor({
|
|
|
2494
2535
|
const pid = discovered[0];
|
|
2495
2536
|
// WP-S3 (B11): same adoption as the pid-file branch above.
|
|
2496
2537
|
adoptedPid = pid;
|
|
2497
|
-
ensureMcpReadinessMonitor();
|
|
2538
|
+
if (!intentionallyStopped) ensureMcpReadinessMonitor(); // R-5: same guard as the pid-file branch above
|
|
2498
2539
|
return statusWithDiagnostics({
|
|
2499
2540
|
running: true,
|
|
2500
2541
|
pid,
|
|
@@ -2514,7 +2555,45 @@ function createWalleSupervisor({
|
|
|
2514
2555
|
}, { checkNative: true });
|
|
2515
2556
|
}
|
|
2516
2557
|
|
|
2558
|
+
// D-7 (WP-5, docs/clock-free-tests-followups-2026-10-23.html §6): the status GET serves this
|
|
2559
|
+
// snapshot instead of running kill-authority discovery (a whole-table ps) on every request.
|
|
2560
|
+
// A cached status is served only while it is (a) younger than one monitor interval AND
|
|
2561
|
+
// (b) still consistent with the process table — `!running` is trivially consistent, otherwise
|
|
2562
|
+
// one isAlive() syscall (kill(pid, 0), ~5 µs) must pass, so a daemon that died since the
|
|
2563
|
+
// snapshot is never reported as running. Anything else is ONE single-flight getStatus():
|
|
2564
|
+
// concurrent callers share the promise instead of each spawning their own ps.
|
|
2565
|
+
// KILL PATHS MUST NEVER CALL THIS — collectManagedPids/reapOrphans/stop need a fresh, whole
|
|
2566
|
+
// table read taken immediately before they signal (see the anti-goal in the design doc §11).
|
|
2567
|
+
function invalidateStatusSnapshot() { statusSnapshot = null; statusSnapshotInFlight = null; statusSnapshotGeneration += 1; }
|
|
2568
|
+
|
|
2569
|
+
async function getStatusSnapshot() {
|
|
2570
|
+
if (pendingChildFailure) return { running: false, pid: null, pidSource: null, recovering: true, snapshotAgeMs: 0 }; // D-6 (WP-3): never wait on the crash-recovery pipeline (npm install, up to 180 s); this answer is not cached, getStatus() still awaits it.
|
|
2571
|
+
const cached = statusSnapshot;
|
|
2572
|
+
const cacheAgeMs = cached ? Date.now() - cached.at : null;
|
|
2573
|
+
if (cached && cacheAgeMs < mcpMonitorIntervalMs && (!cached.status.running || isAlive(cached.status.pid))) {
|
|
2574
|
+
return { ...cached.status, snapshotAgeMs: cacheAgeMs };
|
|
2575
|
+
}
|
|
2576
|
+
if (!statusSnapshotInFlight) {
|
|
2577
|
+
// A getStatus() that began before an invalidation (stop/start/restart/exit) must not write its
|
|
2578
|
+
// pre-invalidation answer back into the cache: store only if no invalidation happened since.
|
|
2579
|
+
const generation = statusSnapshotGeneration;
|
|
2580
|
+
const inFlight = getStatus().then((status) => {
|
|
2581
|
+
const entry = { status, at: Date.now() };
|
|
2582
|
+
if (generation === statusSnapshotGeneration) statusSnapshot = entry;
|
|
2583
|
+
return entry;
|
|
2584
|
+
});
|
|
2585
|
+
statusSnapshotInFlight = inFlight;
|
|
2586
|
+
// Clear the in-flight slot without turning a rejected getStatus() into an unhandled
|
|
2587
|
+
// rejection: the awaiter below still sees the rejection, and the next call retries.
|
|
2588
|
+
const clear = () => { if (statusSnapshotInFlight === inFlight) statusSnapshotInFlight = null; };
|
|
2589
|
+
void inFlight.then(clear, clear);
|
|
2590
|
+
}
|
|
2591
|
+
const fresh = await statusSnapshotInFlight;
|
|
2592
|
+
return { ...fresh.status, snapshotAgeMs: Date.now() - fresh.at };
|
|
2593
|
+
}
|
|
2594
|
+
|
|
2517
2595
|
function apiStop(req, res) {
|
|
2596
|
+
invalidateStatusSnapshot();
|
|
2518
2597
|
intentionallyStopped = true;
|
|
2519
2598
|
stopGeneration += 1;
|
|
2520
2599
|
clearRestartTimer();
|
|
@@ -2535,6 +2614,7 @@ function createWalleSupervisor({
|
|
|
2535
2614
|
}
|
|
2536
2615
|
starting = true;
|
|
2537
2616
|
intentionallyStopped = false;
|
|
2617
|
+
invalidateStatusSnapshot();
|
|
2538
2618
|
appendLifecycleEvent('api_start', { pid: childPid, trigger: 'api_start' });
|
|
2539
2619
|
// WP-S2 (Req #7): an explicit human-initiated start is a clean slate —
|
|
2540
2620
|
// same as apiRestart/restartQuiet below.
|
|
@@ -2674,6 +2754,7 @@ function createWalleSupervisor({
|
|
|
2674
2754
|
}
|
|
2675
2755
|
|
|
2676
2756
|
function restartQuiet() {
|
|
2757
|
+
invalidateStatusSnapshot();
|
|
2677
2758
|
return ensureNativeDependenciesReady('config_change').then((ready) => {
|
|
2678
2759
|
if (!ready.ok) {
|
|
2679
2760
|
console.error(`[setup] Wall-E restart blocked: ${ready.health.message}`);
|
|
@@ -2742,6 +2823,7 @@ function createWalleSupervisor({
|
|
|
2742
2823
|
}
|
|
2743
2824
|
|
|
2744
2825
|
async function apiRestart(req, res) {
|
|
2826
|
+
invalidateStatusSnapshot();
|
|
2745
2827
|
restartInFlight = restartInFlight || runRestart().finally(() => { restartInFlight = null; });
|
|
2746
2828
|
const { status, body } = await restartInFlight;
|
|
2747
2829
|
return writeJson(res, status, body);
|
|
@@ -2846,6 +2928,8 @@ function createWalleSupervisor({
|
|
|
2846
2928
|
// last-resort safety-net timer comfortably above it instead of guessing.
|
|
2847
2929
|
childExitWaitMs,
|
|
2848
2930
|
getStatus,
|
|
2931
|
+
// D-7 (WP-5): the status-GET snapshot. NEVER call this from a kill path.
|
|
2932
|
+
getStatusSnapshot,
|
|
2849
2933
|
getNativeDependencyStatus,
|
|
2850
2934
|
isIntentionallyStopped: () => intentionallyStopped,
|
|
2851
2935
|
killAll,
|
|
@@ -2861,4 +2945,4 @@ function createWalleSupervisor({
|
|
|
2861
2945
|
};
|
|
2862
2946
|
}
|
|
2863
2947
|
|
|
2864
|
-
module.exports = { createWalleSupervisor: (opts) => require('./walle-restart-settled').withRestartSettled(createWalleSupervisor(opts)), rotateBoundedLog, classifyExit, resolveCrashLogPath, psSnapshotRows, PS_SNAPSHOT_MAX_BUFFER };
|
|
2948
|
+
module.exports = { createWalleSupervisor: (opts) => require('./walle-restart-settled').withRestartSettled(createWalleSupervisor(opts)), rotateBoundedLog, classifyExit, resolveCrashLogPath, parsePsWithPpid, psSnapshotRows, PS_SNAPSHOT_MAX_BUFFER };
|
|
@@ -110,6 +110,18 @@ function nextStepFor({ code = '', klass = '', reason = null } = {}) {
|
|
|
110
110
|
return CLASS_NEXT_STEPS[klass] || CLASS_NEXT_STEPS.terminal;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
+
/** WP-2 (session-stops-mid-job-2026-10-24.html D-9, §6): the one lead_next_step text on a
|
|
114
|
+
* non-terminal wait answer. RC-1's stall was a lead that read a wake with its worker still
|
|
115
|
+
* live ("I'm waiting for its report") and ended its turn with no wait in flight, so the text
|
|
116
|
+
* names the still-live worker and the two verbs that keep the lead attached to it.
|
|
117
|
+
* @param {object} [input] @param {string} [input.workerId] the still-live worker
|
|
118
|
+
* @param {string} [input.status] that worker's status string
|
|
119
|
+
* @returns {string|null} null when no worker is named (no live worker to wait on) */
|
|
120
|
+
function waitNextStep({ workerId, status } = {}) {
|
|
121
|
+
if (!workerId) return null;
|
|
122
|
+
return `Worker ${workerId} is still ${status}. Call walle_worker_wait again to keep waiting -- ending your turn with no wait in flight leaves you idle until your user types -- or walle_stop_worker if you no longer need it.`;
|
|
123
|
+
}
|
|
124
|
+
|
|
113
125
|
// Worker-session-tree WP-0 ("every worker death gets a name"): an exit-sourced failure used to
|
|
114
126
|
// carry `code: ''` -- exit 129 (SIGHUP) had no field explaining it. This names the signal
|
|
115
127
|
// behind an exit: from `exit.signal` (a node-pty string like 'SIGHUP', or a positive numeric OS
|
|
@@ -256,4 +268,4 @@ function classifyFailure({ source, http_status = null, code = '', message = '',
|
|
|
256
268
|
};
|
|
257
269
|
}
|
|
258
270
|
|
|
259
|
-
module.exports = { classifyFailure, signalName, extractHttpStatus, extractCode, extractRetryAfterMs, nextStepFor, NEXT_STEPS, CLASS_NEXT_STEPS };
|
|
271
|
+
module.exports = { classifyFailure, signalName, extractHttpStatus, extractCode, extractRetryAfterMs, nextStepFor, waitNextStep, NEXT_STEPS, CLASS_NEXT_STEPS };
|
|
@@ -67,6 +67,9 @@ function _serializeKey(sessionId, cols, rows, scrollbackRows, rolloutHistory) {
|
|
|
67
67
|
// softMs, hardMs, makeReqId(), buildPost(reqId), refreshCache(entry, resp),
|
|
68
68
|
// scrollbackRows, rolloutHistory }. The last two are KEY inputs (I8) -- every
|
|
69
69
|
// caller must supply them, whether or not it ends up creating the entry.
|
|
70
|
+
// WP-3: the hard timer passes `issuedAt: entry.startedAt` to recordMiss and
|
|
71
|
+
// routes a 'degraded' verdict to a 'host-degraded' diagnostic -- never to
|
|
72
|
+
// _reviveUnresponsiveHost, which only an 'unresponsive' verdict reaches.
|
|
70
73
|
function _attachOrPostSerialize(sessionId, cols, rows, waiter, entryHooks) {
|
|
71
74
|
const key = _serializeKey(sessionId, cols, rows, entryHooks.scrollbackRows, entryHooks.rolloutHistory);
|
|
72
75
|
const existing = _inflightSerialize.get(key);
|
|
@@ -98,9 +101,23 @@ function _attachOrPostSerialize(sessionId, cols, rows, waiter, entryHooks) {
|
|
|
98
101
|
for (const w of entry.waiters) w.onHardTimeout?.(entry);
|
|
99
102
|
// WP-1 (D-1/D-2): the readiness ledger's one deadlined-miss input for a
|
|
100
103
|
// hosted session -- ONE call per coalesced request, never per waiter.
|
|
104
|
+
// WP-3 (session-stops-mid-job-2026-10-24.html, D-11): `issuedAt` is this
|
|
105
|
+
// entry's own creation time, so the ledger can tell "this request family
|
|
106
|
+
// timed out" (readiness) from "the host has said nothing at all" (liveness).
|
|
101
107
|
if (entryHooks.hostId) {
|
|
102
|
-
const verdictResult = entryHooks.hostHealth?.recordMiss?.(entryHooks.hostId, { sessionId, kind: 'hard', deadlineMs: 60000 });
|
|
103
|
-
if (verdictResult) {
|
|
108
|
+
const verdictResult = entryHooks.hostHealth?.recordMiss?.(entryHooks.hostId, { sessionId, kind: 'hard', deadlineMs: 60000, issuedAt: entry.startedAt });
|
|
109
|
+
if (verdictResult?.verdict === 'degraded') {
|
|
110
|
+
// D-11: a host that relayed a frame after this request was issued is
|
|
111
|
+
// talking -- its serialize path is slow/wedged, but it and every agent
|
|
112
|
+
// in it stay alive. Report once per session for this host; NEVER revive.
|
|
113
|
+
_deps?.recordSessionDiagnostic?.(sessionId, 'host-degraded', {
|
|
114
|
+
hostId: entryHooks.hostId,
|
|
115
|
+
reason: 'serialize-timeout',
|
|
116
|
+
lastFrameAgoMs: verdictResult.lastFrameAgoMs,
|
|
117
|
+
});
|
|
118
|
+
} else if (verdictResult?.verdict === 'unresponsive') {
|
|
119
|
+
// D-12: only a host that has sent NO frame of any kind since the first
|
|
120
|
+
// missed request was issued reaches here.
|
|
104
121
|
// Final-review fix C2: this is fire-and-forget from a timer. Without a
|
|
105
122
|
// `.catch`, ANY rejection inside the revive (registry read, crash-cap
|
|
106
123
|
// read, diagnostic write, host shutdown) becomes an unhandled rejection
|
|
@@ -25,6 +25,19 @@ const {
|
|
|
25
25
|
// socket reference rewritten to `ctx.ws` inline.
|
|
26
26
|
|
|
27
27
|
const { registerWsHandlers } = require('../ws-router');
|
|
28
|
+
// WP-2 (docs/remote-machine-hub-sessions-2026-10-22.html §5 D-10, §6.3, §9 S-1): the remote-create
|
|
29
|
+
// seam. Required here (not injected through configure()) so server.js needs no edit for the
|
|
30
|
+
// branch; its production dependencies are resolved LAZILY inside the module, so loading this file
|
|
31
|
+
// still never touches db.js.
|
|
32
|
+
const remoteMachineCreate = require('../remote-machine-create');
|
|
33
|
+
// WP-3 (docs/remote-machine-hub-sessions-2026-10-22.html §6.4, S-22): the live-plane relay seam.
|
|
34
|
+
// Loading this file installs lib/remote-machine-link.js's pre-dispatch hook (lib/ws-router.js),
|
|
35
|
+
// which diverts any message naming a MIRROR session to the box and returns false — leaving the
|
|
36
|
+
// local path byte-identical — for every other session. Installed here, at boot, because this file
|
|
37
|
+
// is where the WP-2 remote-create branch already lives; the hook is inert until a mirror session
|
|
38
|
+
// exists, so no server.js edit is needed for it.
|
|
39
|
+
const remoteMachineLink = require('../remote-machine-link');
|
|
40
|
+
remoteMachineLink.installIntercept();
|
|
28
41
|
|
|
29
42
|
let _deps = null;
|
|
30
43
|
|
|
@@ -50,6 +63,7 @@ function configure({
|
|
|
50
63
|
_subscribeTerminalTail,
|
|
51
64
|
_unsubscribeTerminalTail,
|
|
52
65
|
_ensureCodexThreadLinkOffThread,
|
|
66
|
+
remoteCreateDeps,
|
|
53
67
|
} = {}) {
|
|
54
68
|
_deps = {
|
|
55
69
|
handleCreatePrepared,
|
|
@@ -73,6 +87,7 @@ function configure({
|
|
|
73
87
|
_subscribeTerminalTail,
|
|
74
88
|
_unsubscribeTerminalTail,
|
|
75
89
|
_ensureCodexThreadLinkOffThread,
|
|
90
|
+
remoteCreateDeps: remoteCreateDeps && typeof remoteCreateDeps === 'object' ? remoteCreateDeps : null,
|
|
76
91
|
};
|
|
77
92
|
}
|
|
78
93
|
|
|
@@ -225,6 +240,14 @@ async function handleCreate(ctx, msg) {
|
|
|
225
240
|
}
|
|
226
241
|
return;
|
|
227
242
|
}
|
|
243
|
+
// WP-2 (S-1): a create naming a REGISTERED REMOTE machine is forwarded to that box instead of
|
|
244
|
+
// being prepared here. The check is BEFORE the local plan, so nothing local (generation,
|
|
245
|
+
// prepared plan, PTY) is ever created for it; `machine` absent or naming THIS instance's minted
|
|
246
|
+
// id falls through to the unchanged local path (S-2 — the local deny list above is untouched
|
|
247
|
+
// and is never widened for forwarded frames).
|
|
248
|
+
if (remoteMachineCreate.isRemoteMachineTarget(msg, _deps.remoteCreateDeps || {})) {
|
|
249
|
+
return remoteMachineCreate.forwardRemoteCreate(ctx, msg, _deps.remoteCreateDeps || {});
|
|
250
|
+
}
|
|
228
251
|
return _deps.handleCreatePrepared(ctx.ws, msg);
|
|
229
252
|
}
|
|
230
253
|
|
|
@@ -250,4 +273,12 @@ function register() {
|
|
|
250
273
|
});
|
|
251
274
|
}
|
|
252
275
|
|
|
253
|
-
module.exports = {
|
|
276
|
+
module.exports = {
|
|
277
|
+
configure,
|
|
278
|
+
register,
|
|
279
|
+
// Exported for the WP-2 enforcement test (tests/create-forward-allowlist.test.js): the LIVE
|
|
280
|
+
// deny list and the LIVE §6.3 allowlist, so the test can never drift from either constant.
|
|
281
|
+
PUBLIC_CREATE_INTERNAL_CONTROLS,
|
|
282
|
+
REMOTE_CREATE_FORWARD_ALLOWLIST: remoteMachineCreate.REMOTE_CREATE_FORWARD_ALLOWLIST,
|
|
283
|
+
filterRemoteCreateForward: remoteMachineCreate.filterRemoteCreateForward,
|
|
284
|
+
};
|
|
@@ -77,6 +77,64 @@ class WsRouter {
|
|
|
77
77
|
|
|
78
78
|
const _router = new WsRouter();
|
|
79
79
|
|
|
80
|
+
// ---------------------------------------------------------------------------------------------
|
|
81
|
+
// Pre-dispatch hooks (WP-3, docs/remote-machine-hub-sessions-2026-10-22.html §6.4)
|
|
82
|
+
//
|
|
83
|
+
// The ONE seam a relay needs. A machine-hub session lives on a REMOTE box, not in this process's
|
|
84
|
+
// `sessions` Map (OQ-4), so every message that NAMES such a session must be diverted before a
|
|
85
|
+
// local handler dereferences it. Rather than touch each lib/ws/*.js group, a module registers a
|
|
86
|
+
// hook here and `dispatchWs` offers it every inbound frame first (in registration order):
|
|
87
|
+
//
|
|
88
|
+
// hook(ctx, msg) -> true the hook handled it; the local dispatch is SKIPPED
|
|
89
|
+
// hook(ctx, msg) -> false not mine; the next hook, then the normal handler, runs
|
|
90
|
+
//
|
|
91
|
+
// Hooks are consulted OUTSIDE `WsRouter.dispatch` on purpose: `dispatch` is the pure registry
|
|
92
|
+
// primitive (unit-tested directly), and a hook is a deployment-level concern of the process's
|
|
93
|
+
// singleton. A hook that throws is REPORTED and treated as "not handled" — one broken relay must
|
|
94
|
+
// never take the whole WS surface down with it.
|
|
95
|
+
// ---------------------------------------------------------------------------------------------
|
|
96
|
+
const _preDispatchHooks = [];
|
|
97
|
+
|
|
98
|
+
function registerWsPreDispatchHook(name, hook) {
|
|
99
|
+
if (!name || typeof name !== 'string') {
|
|
100
|
+
throw new TypeError('registerWsPreDispatchHook: name must be a non-empty string');
|
|
101
|
+
}
|
|
102
|
+
if (typeof hook !== 'function') {
|
|
103
|
+
throw new TypeError(`registerWsPreDispatchHook(${name}): hook must be a function`);
|
|
104
|
+
}
|
|
105
|
+
if (_preDispatchHooks.some((entry) => entry.name === name)) {
|
|
106
|
+
throw new Error(`registerWsPreDispatchHook: duplicate hook name: ${name}`);
|
|
107
|
+
}
|
|
108
|
+
const entry = { name, hook };
|
|
109
|
+
_preDispatchHooks.push(entry);
|
|
110
|
+
return () => {
|
|
111
|
+
const at = _preDispatchHooks.indexOf(entry);
|
|
112
|
+
if (at >= 0) _preDispatchHooks.splice(at, 1);
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// SYNCHRONOUS on purpose: hooks return a plain boolean, so a message no hook claims (every local
|
|
117
|
+
// keystroke) reaches its handler in the same tick it always did — no added microtask.
|
|
118
|
+
function runWsPreDispatchHooks(ctx, msg) {
|
|
119
|
+
for (const entry of _preDispatchHooks) {
|
|
120
|
+
let handled = false;
|
|
121
|
+
try {
|
|
122
|
+
handled = entry.hook(ctx, msg);
|
|
123
|
+
} catch (error) {
|
|
124
|
+
// REPORT, then fall through: the relay is a routing optimization, never a gate — a hook
|
|
125
|
+
// fault must degrade to the pre-WP-3 behaviour (the local handler), not drop the frame.
|
|
126
|
+
console.error(`[ws-router] pre-dispatch hook ${entry.name} failed:`, error?.stack || error);
|
|
127
|
+
handled = false;
|
|
128
|
+
}
|
|
129
|
+
if (handled === true) return true;
|
|
130
|
+
}
|
|
131
|
+
return false;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function listWsPreDispatchHooks() {
|
|
135
|
+
return _preDispatchHooks.map((entry) => entry.name);
|
|
136
|
+
}
|
|
137
|
+
|
|
80
138
|
function registerWsHandler(type, handler) {
|
|
81
139
|
_router.registerOne('_default', type, handler);
|
|
82
140
|
}
|
|
@@ -86,6 +144,7 @@ function registerWsHandlers(groupName, handlersByType) {
|
|
|
86
144
|
}
|
|
87
145
|
|
|
88
146
|
async function dispatchWs(ctx, msg) {
|
|
147
|
+
if (runWsPreDispatchHooks(ctx, msg)) return true;
|
|
89
148
|
return _router.dispatch(ctx, msg);
|
|
90
149
|
}
|
|
91
150
|
|
|
@@ -93,4 +152,12 @@ function listRegisteredWsTypes() {
|
|
|
93
152
|
return _router.list();
|
|
94
153
|
}
|
|
95
154
|
|
|
96
|
-
module.exports = {
|
|
155
|
+
module.exports = {
|
|
156
|
+
registerWsHandler,
|
|
157
|
+
registerWsHandlers,
|
|
158
|
+
registerWsPreDispatchHook,
|
|
159
|
+
listWsPreDispatchHooks,
|
|
160
|
+
dispatchWs,
|
|
161
|
+
listRegisteredWsTypes,
|
|
162
|
+
WsRouter,
|
|
163
|
+
};
|
|
@@ -79,15 +79,33 @@ function prepareExactJsonEnvelope(payload, stringify = JSON.stringify) {
|
|
|
79
79
|
return { encoded, bytes: measuredBytes };
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
-
|
|
82
|
+
// T1 (docs/walle-turn-lifecycle-delivery-2026-10-26.html §3): the `walle-transcript-append`
|
|
83
|
+
// channel carries BOTH replaceable stream samples and turn LIFECYCLE control notifications —
|
|
84
|
+
// server.js's _recordWalleTranscriptAppend emits recordType=user/assistant rows and bare
|
|
85
|
+
// partType=turn_terminal parts, and the client (public/js/walle-session.js
|
|
86
|
+
// handleTranscriptAppend) flips generating state or forces a refetch on exactly those.
|
|
87
|
+
// Shedding them as replaceable stream samples is what left a live turn reading
|
|
88
|
+
// isGenerating=false while its interactive user echo still arrived. Classification therefore
|
|
89
|
+
// reads the already-resolved channel plus the payload's decoded recordType/partType, normalized
|
|
90
|
+
// exactly like the client's String(value || '').toLowerCase(); every other append stays stream.
|
|
91
|
+
// `explicitChannel` is the caller's already-resolved channel (wsSend's `options.channel`).
|
|
92
|
+
function classifyWsPayload(payload, explicitClass = '', explicitChannel = '') {
|
|
83
93
|
if (['interactive', 'stream', 'bulk'].includes(explicitClass)) return explicitClass;
|
|
84
|
-
const channel = payloadChannel(payload);
|
|
94
|
+
const channel = payloadChannel(payload, explicitChannel);
|
|
85
95
|
if (process.env.CTM_WS_SOLICITED_REPLY_INTERACTIVE !== '0' && SOLICITED_REPLY_CHANNELS.has(channel)) {
|
|
86
96
|
return 'interactive';
|
|
87
97
|
}
|
|
88
98
|
if (process.env.CTM_WALLE_WS_CLASS_SPLIT !== '0') {
|
|
89
99
|
if (WALLE_TERMINAL_CHANNELS.has(channel) || channel.startsWith('walle-permission')) return 'interactive';
|
|
90
|
-
if (WALLE_STREAM_CHANNELS.has(channel))
|
|
100
|
+
if (WALLE_STREAM_CHANNELS.has(channel)) {
|
|
101
|
+
if (channel === 'walle-transcript-append') {
|
|
102
|
+
const decoded = normalizePayload(payload);
|
|
103
|
+
const recordType = String(decoded?.recordType || '').toLowerCase();
|
|
104
|
+
const partType = String(decoded?.partType || '').toLowerCase();
|
|
105
|
+
if (recordType === 'user' || recordType === 'assistant' || partType === 'turn_terminal') return 'interactive';
|
|
106
|
+
}
|
|
107
|
+
return 'stream';
|
|
108
|
+
}
|
|
91
109
|
}
|
|
92
110
|
if (['frame', 'terminal-tail', 'terminal-dirty', 'session-activity', 'fingerprint'].includes(channel)) return 'stream';
|
|
93
111
|
if (['sessions', 'wake-digest', 'wake-manifest', 'conversation-updated', 'data-changed'].includes(channel)
|
|
@@ -133,7 +151,7 @@ function wsSend(ws, payload, options = {}) {
|
|
|
133
151
|
const state = ws._ctmAdmission || createAdmissionState(options);
|
|
134
152
|
ws._ctmAdmission = state;
|
|
135
153
|
const now = Number(options.now) || Date.now();
|
|
136
|
-
const trafficClass = classifyWsPayload(payload, options.class);
|
|
154
|
+
const trafficClass = classifyWsPayload(payload, options.class, options.channel);
|
|
137
155
|
const channel = payloadChannel(payload, options.channel);
|
|
138
156
|
const decision = options.admissionEnabled === false
|
|
139
157
|
? { result: ws.readyState === 1 ? ADMISSION_RESULTS.SENT : ADMISSION_RESULTS.DROPPED_BACKPRESSURE, reason: 'disabled' }
|