create-walle 0.10.0 → 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 +88 -41
- 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 +7 -5
- package/package.json +10 -4
- 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/capture-codex-rollout-records.js +417 -0
- package/template/coding-task-manager/bin/census-codex-lead-history.js +325 -0
- 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/seam-replay-codex.js +301 -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 +25 -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-history-terminal-renderer.js +33 -33
- 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-local-links.js +267 -0
- package/template/coding-task-manager/lib/codex-markdown-lists.js +160 -0
- package/template/coding-task-manager/lib/codex-native-history-prefix.js +4 -0
- package/template/coding-task-manager/lib/codex-paths.js +2 -1
- package/template/coding-task-manager/lib/codex-source-handoff.js +186 -0
- 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-owner-worker-client.js +22 -0
- package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
- package/template/coding-task-manager/lib/feature-manifest.js +1 -3
- package/template/coding-task-manager/lib/fs-watcher.js +3 -2
- package/template/coding-task-manager/lib/harness-history-transition.js +41 -14
- 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 +60 -23
- package/template/coding-task-manager/lib/history-transition-read-op.js +179 -23
- 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/kernel/diag-log.js +12 -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-observer.js +17 -2
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
- package/template/coding-task-manager/lib/lead-model-switch.js +11 -2
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +42 -4
- 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 +18 -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 +56 -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-execution-recipe-metadata.js +16 -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/collaboration-codec.js +94 -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/responses-face.js +24 -2
- 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 +62 -14
- 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-admission-trace.js +69 -0
- 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/raw-history-block.js +3 -1
- package/template/coding-task-manager/lib/read-pool-client.js +36 -24
- package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
- package/template/coding-task-manager/lib/read-pool-telemetry.js +14 -5
- 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 +18 -5
- 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 +43 -11
- package/template/coding-task-manager/lib/root-prompt-flow.js +175 -38
- package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
- package/template/coding-task-manager/lib/server-memory-sampler-runtime.js +10 -0
- package/template/coding-task-manager/lib/server-memory-sampler.js +41 -2
- 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 +38 -39
- 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 +20 -50
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +68 -3
- package/template/coding-task-manager/lib/session-resource-sampler.js +67 -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-status.js +2 -2
- 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 +136 -2
- package/template/coding-task-manager/lib/typing-worker-stages.js +208 -1
- package/template/coding-task-manager/lib/usage-ledger/claude-parse.js +53 -2
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +13 -4
- package/template/coding-task-manager/lib/usage-ledger/read.js +14 -0
- package/template/coding-task-manager/lib/usage-ledger/schema.js +49 -1
- package/template/coding-task-manager/lib/usage-ledger/store.js +337 -6
- package/template/coding-task-manager/lib/usage-ledger/task-read.js +123 -5
- 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/input.js +1 -0
- 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/protocol/message-table.json +0 -10
- package/template/coding-task-manager/protocol/messages.js +0 -10
- package/template/coding-task-manager/protocol/ws-schema.json +0 -20
- package/template/coding-task-manager/protocol/ws-schema.md +0 -2
- 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 -2
- package/template/coding-task-manager/public/css/reviews.css +4 -0
- package/template/coding-task-manager/public/index.html +297 -240
- 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/client-health-core.js +81 -0
- package/template/coding-task-manager/public/js/client-health-main.js +61 -0
- 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 -4
- package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
- package/template/coding-task-manager/public/js/protocol.js +0 -10
- 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 +18 -3
- 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/session-usage-view.js +1 -0
- 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 -3
- 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 +964 -425
- 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/host-capabilities.js +2 -2
- 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/fly.toml +1 -1
- 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 +12 -5
- 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
- package/template/coding-task-manager/lib/giant-freeze-park.js +0 -31
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// §5 D-7 / §6 (docs/model-proxy-survives-ctm-restart-2026-10-22.html): the CLI half of oauth-proxy.js.
|
|
4
|
+
// It lives here so oauth-proxy.js stays inside its hard 1500-line ceiling, and nothing in this file is
|
|
5
|
+
// reachable from an embedded start() -- only from `node oauth-proxy.js`.
|
|
6
|
+
//
|
|
7
|
+
// Two ways in, distinguished by ONE thing: whether there is an IPC channel.
|
|
8
|
+
// * the supervisor spawns the proxy with the admin secret on STDIN (WP-0 §5 D-4): read one line, start
|
|
9
|
+
// serving, and the secret is only ever used for the loopback admin calls above;
|
|
10
|
+
// * a PREDECESSOR performing an upgrade handoff (§5 D-7) spawns it with an `ipc` channel and no stdin
|
|
11
|
+
// at all (`stdio: ['ignore', logFd, logFd, 'ipc']`): the three `handoff/v1*` messages carry the two
|
|
12
|
+
// listening sockets, the admin secret and the memory-only token state, and this process is the
|
|
13
|
+
// SUCCESSOR. It publishes its own identity and answers `ready` -- the predecessor stops accepting
|
|
14
|
+
// only after that answer, which is why the answer is sent from start(), not from here.
|
|
15
|
+
|
|
16
|
+
const readline = require('node:readline');
|
|
17
|
+
|
|
18
|
+
const LOOPBACK_HOST = '127.0.0.1';
|
|
19
|
+
const LOOPBACK_REFUSAL_EXIT_CODE = 78;
|
|
20
|
+
const STARTUP_FAILURE_EXIT_CODE = 1;
|
|
21
|
+
const SIGTERM_EXIT_CODE = 143;
|
|
22
|
+
|
|
23
|
+
// Test seam (§6, AC-2b): truthy => the successor does everything a successor does EXCEPT send `ready`, so
|
|
24
|
+
// a test can observe the predecessor's bounded wait from outside both processes. It is read through a
|
|
25
|
+
// local `env` binding -- the shape lib/hermetic-agent-launch-guard.js uses for its own test-only
|
|
26
|
+
// switches -- and it reaches a successor because the supervisor's `childEnv = { ...process.env }` and the
|
|
27
|
+
// predecessor's `env: process.env` both pass it through. There is no operator flag here.
|
|
28
|
+
function _holdReadyFlag(env = process.env) {
|
|
29
|
+
return String(env.CTM_TEST_HANDOFF_SUCCESSOR_HOLD_READY || '') === '1';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* §5 D-8 test seam: the CLI is the ONLY caller that arms the proxy's own linger timer, and this is its
|
|
34
|
+
* idle window. It is read through a local `env` binding, exactly like `_holdReadyFlag` above, so a child
|
|
35
|
+
* spawned by a test can be observed leaving its window in about a second instead of fifteen minutes; the
|
|
36
|
+
* production value comes from handoff.js (`PROXY_LINGER_MS`, === HOST_LINGER_MS). No operator flag, and
|
|
37
|
+
* no production path ever sets this.
|
|
38
|
+
*/
|
|
39
|
+
function _lingerMsFlag(env = process.env, handoff) {
|
|
40
|
+
const raw = Number(env.CTM_TEST_PROXY_LINGER_MS);
|
|
41
|
+
return Number.isSafeInteger(raw) && raw > 0 ? raw : handoff.PROXY_LINGER_MS;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** D-1: the CLI's bind host, resolved once from the environment exactly as it always was. */
|
|
45
|
+
function _resolveHost(env = process.env) {
|
|
46
|
+
const raw = env.OAUTH_PROXY_HOST;
|
|
47
|
+
return typeof raw === 'string' && raw.length > 0 ? raw : LOOPBACK_HOST;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// ── D-4 stdin secret (CLI mode only) ────────────────────────────────────
|
|
51
|
+
// Reads exactly one line from stdin at process start. Never env, never argv.
|
|
52
|
+
// Resolves null (no admin secret; admin endpoints stay locked) if stdin is a
|
|
53
|
+
// TTY or closes without producing a line.
|
|
54
|
+
function _readAdminSecretFromStdin() {
|
|
55
|
+
return new Promise((resolve) => {
|
|
56
|
+
if (!process.stdin || process.stdin.isTTY) {
|
|
57
|
+
resolve(null);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const rl = readline.createInterface({ input: process.stdin, terminal: false });
|
|
61
|
+
let resolved = false;
|
|
62
|
+
const finish = (value) => {
|
|
63
|
+
if (resolved) return;
|
|
64
|
+
resolved = true;
|
|
65
|
+
rl.close();
|
|
66
|
+
resolve(value);
|
|
67
|
+
};
|
|
68
|
+
rl.once('line', (line) => finish((line || '').trim() || null));
|
|
69
|
+
rl.once('close', () => finish(null));
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* §9 S-5: record the exit, say it once, then leave. Semantics are unchanged -- nothing drains, and
|
|
75
|
+
* 128+15 keeps the supervisor's `code !== 0` classification of a SIGTERM exactly as it is today.
|
|
76
|
+
*/
|
|
77
|
+
function _installSigtermHandler({ startedContext, streamAccountingMod, instanceRecord, handoff }) {
|
|
78
|
+
process.on('SIGTERM', () => {
|
|
79
|
+
const ctx = startedContext();
|
|
80
|
+
if (!ctx) {
|
|
81
|
+
process.exit(SIGTERM_EXIT_CODE);
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const openStreams = ctx.streamAccounting.count();
|
|
85
|
+
// §5 D-5: an explicit stop leaves no record, so the next boot spawns rather than adopts. A
|
|
86
|
+
// PREDECESSOR that is draining a handoff is the one exception: the record on disk already names its
|
|
87
|
+
// successor, and removing it would take the live proxy out of the next boot's reach.
|
|
88
|
+
if (!handoff.isDraining()) instanceRecord.unlinkInstanceRecord({ dataDir: ctx.dataDir });
|
|
89
|
+
streamAccountingMod.writeExitRecord({ dataDir: ctx.dataDir, reason: 'SIGTERM', openStreams })
|
|
90
|
+
.catch((err) => {
|
|
91
|
+
// REPORT: the exit code below still classifies this stop, so a failed write is not fatal.
|
|
92
|
+
console.error(`[model-proxy] exit record not written: ${(err && err.code) || 'unknown'}`);
|
|
93
|
+
})
|
|
94
|
+
.then(() => {
|
|
95
|
+
console.log(`[model-proxy] exit reason=SIGTERM open_streams=${openStreams}`);
|
|
96
|
+
process.exit(SIGTERM_EXIT_CODE);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The CLI entry point, called by oauth-proxy.js's `require.main === module` block with its own
|
|
103
|
+
* dependencies injected (so this module never requires oauth-proxy.js back).
|
|
104
|
+
*
|
|
105
|
+
* @param {object} input
|
|
106
|
+
* @param {Function} input.start oauth-proxy.js's start()
|
|
107
|
+
* @param {object} input.streamAccountingMod lib/model-proxy/stream-accounting.js
|
|
108
|
+
* @param {object} input.instanceRecord lib/model-proxy/proxy-instance-record.js
|
|
109
|
+
* @param {object} input.handoff lib/model-proxy/handoff.js
|
|
110
|
+
* @param {Function} [input.startedContext] getter for the context of the server this process started
|
|
111
|
+
* @param {string} [input.loopbackErrorCode] the named refusal code start() rejects with
|
|
112
|
+
*/
|
|
113
|
+
function run({
|
|
114
|
+
start,
|
|
115
|
+
streamAccountingMod,
|
|
116
|
+
instanceRecord,
|
|
117
|
+
handoff,
|
|
118
|
+
startedContext = () => null,
|
|
119
|
+
loopbackErrorCode = 'EBIND_NOT_LOOPBACK',
|
|
120
|
+
} = {}) {
|
|
121
|
+
const host = _resolveHost();
|
|
122
|
+
if (host !== LOOPBACK_HOST) {
|
|
123
|
+
// D-1: refuse before ever touching stdin, so a bad --host/env doesn't hang waiting for an admin
|
|
124
|
+
// secret that will never matter.
|
|
125
|
+
console.error(`[oauth-proxy] ${loopbackErrorCode}: refusing to bind to "${host}" — must be 127.0.0.1 (D-1)`);
|
|
126
|
+
process.exit(LOOPBACK_REFUSAL_EXIT_CODE);
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const successor = typeof process.send === 'function';
|
|
130
|
+
const startup = successor
|
|
131
|
+
// §5 D-7: the successor never reads stdin -- its secret and its data directory arrive in the
|
|
132
|
+
// handoff message, and its listener is a socket it did not open.
|
|
133
|
+
? handoff.receiveInheritance().then((received) => ({
|
|
134
|
+
host,
|
|
135
|
+
adminSecret: received.inherited.adminSecret,
|
|
136
|
+
dataDir: received.inherited.dataDir,
|
|
137
|
+
inherited: { ...received.inherited, state: received.state, holdReady: _holdReadyFlag() },
|
|
138
|
+
}))
|
|
139
|
+
// A TTY, or stdin closing before a line arrived, is not a failure: the process simply serves with no
|
|
140
|
+
// admin secret, and its admin surface stays locked. The read itself already resolves null in both of
|
|
141
|
+
// those cases, so this tail exists only for an unexpected readline error.
|
|
142
|
+
: _readAdminSecretFromStdin()
|
|
143
|
+
.catch((/* expected: no line on stdin -- start with no admin secret rather than not at all */) => null)
|
|
144
|
+
.then((adminSecret) => ({ host, adminSecret }));
|
|
145
|
+
|
|
146
|
+
startup
|
|
147
|
+
// §5 D-8: `lingerMs` is what makes THIS process (and only this one: an embedded start() passes none)
|
|
148
|
+
// leave on its own once CTM has been gone for a full window.
|
|
149
|
+
.then((args) => start({ ...args, lingerMs: _lingerMsFlag(process.env, handoff) }))
|
|
150
|
+
.then(() => {
|
|
151
|
+
if (successor) {
|
|
152
|
+
// §5 D-7: the successor outlives the predecessor that spawned it, so the channel it was born
|
|
153
|
+
// with must never be what keeps it alive -- nor may its closure end it. `ready` is already sent.
|
|
154
|
+
try {
|
|
155
|
+
process.channel.unref();
|
|
156
|
+
} catch (err) {
|
|
157
|
+
// expected: nothing to detach when this process has no channel state left to manage.
|
|
158
|
+
void err;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
_installSigtermHandler({ startedContext, streamAccountingMod, instanceRecord, handoff });
|
|
162
|
+
})
|
|
163
|
+
.catch((err) => {
|
|
164
|
+
if (err && err.code === loopbackErrorCode) {
|
|
165
|
+
console.error(`[oauth-proxy] ${err.code}: ${err.message}`);
|
|
166
|
+
process.exit(LOOPBACK_REFUSAL_EXIT_CODE);
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
console.error('[oauth-proxy] failed to start:', err.message);
|
|
170
|
+
process.exit(STARTUP_FAILURE_EXIT_CODE);
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The `require.main === module` entry: oauth-proxy.js calls exactly this, so its own CLI seams
|
|
176
|
+
* (start, the accounting/record modules, the SIGTERM context) do not have to be spelled out at the bottom
|
|
177
|
+
* of a file that is at its hard line ceiling. The require is deferred to call time, so loading THIS module
|
|
178
|
+
* never loads oauth-proxy.js back -- and when the proxy is its own main, this module is loaded from
|
|
179
|
+
* inside that same evaluation, by which point oauth-proxy.js's exports are complete.
|
|
180
|
+
*/
|
|
181
|
+
function runCli() {
|
|
182
|
+
const proxy = require('../../oauth-proxy.js');
|
|
183
|
+
return run({
|
|
184
|
+
start: proxy.start,
|
|
185
|
+
streamAccountingMod: require('./stream-accounting'),
|
|
186
|
+
instanceRecord: require('./proxy-instance-record'),
|
|
187
|
+
handoff: require('./handoff'),
|
|
188
|
+
startedContext: proxy._startedContext,
|
|
189
|
+
loopbackErrorCode: proxy.EBIND_NOT_LOOPBACK,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
module.exports = {
|
|
194
|
+
run,
|
|
195
|
+
runCli,
|
|
196
|
+
_readAdminSecretFromStdin,
|
|
197
|
+
_holdReadyFlag,
|
|
198
|
+
_lingerMsFlag,
|
|
199
|
+
_resolveHost,
|
|
200
|
+
};
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// §5 D-1/D-2/D-3 + §6 (docs/model-proxy-survives-ctm-restart-2026-10-22.html): the durable identity of a
|
|
4
|
+
// LIVE model proxy, and the four paths it lives on. The supervisor binds the child's stdout/stderr to
|
|
5
|
+
// proxyLogPath() (the proxy never writes into a CTM-owned pipe -- incident 7: an orphaned child dies on
|
|
6
|
+
// its second write with EPIPE); the child publishes instance.json after listen and removes it on an
|
|
7
|
+
// explicit stop; CTM writes the per-spawn admin secret BEFORE the spawn, so a CTM that dies between the
|
|
8
|
+
// spawn and its own exit still leaves a readable secret for the next boot -- which is also the CTM that
|
|
9
|
+
// adopts the survivor (§5 D-4). Both processes must agree on these paths byte-for-byte, so they are
|
|
10
|
+
// defined once, here.
|
|
11
|
+
//
|
|
12
|
+
// Publications are atomic in the shape lib/host-registry.js uses (a nonce-scoped tmp file created with
|
|
13
|
+
// flag 'wx' and mode 0600, renamed over the final path), and the directory is 0700 -- the same tree the
|
|
14
|
+
// native mTLS authority already lives in. The same 0600 secret authenticates loopback admin calls only;
|
|
15
|
+
// nothing here is a transport credential.
|
|
16
|
+
|
|
17
|
+
const crypto = require('node:crypto');
|
|
18
|
+
const fs = require('node:fs');
|
|
19
|
+
const path = require('node:path');
|
|
20
|
+
const { execFileSync } = require('node:child_process');
|
|
21
|
+
const { computeClosure } = require('../host-closure-hash');
|
|
22
|
+
|
|
23
|
+
// lib/host-closure-hash.js resolves its own default repo root from ITS __dirname (coding-task-manager/lib
|
|
24
|
+
// -> the repo root). This module sits one level deeper, so the same root is re-stated explicitly: the
|
|
25
|
+
// file list the PROXY hashes and the file list CTM hashes from disk must name the same files.
|
|
26
|
+
const REPO_ROOT = path.resolve(__dirname, '..', '..', '..');
|
|
27
|
+
|
|
28
|
+
const RECORD_VERSION = 1;
|
|
29
|
+
const PROXY_PROTOCOL = 'proxy-v1';
|
|
30
|
+
const PROXY_FLOOR_VERSION = 0;
|
|
31
|
+
const PROXY_SCRIPT_NAME = 'oauth-proxy.js';
|
|
32
|
+
// §5 D-1: one generation of log is kept. 203,900 request lines in 26 days is ~1.5 MB/day, so ~40 days
|
|
33
|
+
// per generation; the rotation is a rename, not a truncation, so the previous generation stays readable.
|
|
34
|
+
const LOG_ROTATE_BYTES = 64 * 1024 * 1024;
|
|
35
|
+
const LOG_ROTATE_SUFFIX = '.1';
|
|
36
|
+
const PS_TIMEOUT_MS = 2000;
|
|
37
|
+
|
|
38
|
+
function proxyDir(dataDir) {
|
|
39
|
+
return path.join(dataDir, 'oauth-proxy');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function instanceRecordPath(dataDir) {
|
|
43
|
+
return path.join(proxyDir(dataDir), 'instance.json');
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function adminSecretPath(dataDir) {
|
|
47
|
+
return path.join(proxyDir(dataDir), 'admin-secret');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function proxyLogPath(dataDir) {
|
|
51
|
+
return path.join(proxyDir(dataDir), 'oauth-proxy.log');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// 0700, created on demand. Synchronous because the spawn-time log descriptor below needs the directory to
|
|
55
|
+
// exist before the child does; one call per spawn and one per publication, never a request path.
|
|
56
|
+
function ensureProxyDir(dataDir) {
|
|
57
|
+
const dir = proxyDir(dataDir);
|
|
58
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
59
|
+
return dir;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function _writeAtomic0600Sync(target, text) {
|
|
63
|
+
const tmp = `${target}.${process.pid}.${crypto.randomUUID()}.tmp`;
|
|
64
|
+
fs.writeFileSync(tmp, text, { mode: 0o600, flag: 'wx' });
|
|
65
|
+
fs.renameSync(tmp, target);
|
|
66
|
+
return target;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function _unlinkQuiet(target) {
|
|
70
|
+
try {
|
|
71
|
+
fs.unlinkSync(target);
|
|
72
|
+
return true;
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (err && err.code === 'ENOENT') return false;
|
|
75
|
+
// REPORT: a file that cannot be removed is stale truth on disk; the caller's decision still stands.
|
|
76
|
+
process.stderr.write(`[model-proxy] record not removed: ${path.basename(target)} ${(err && err.code) || 'unknown'}\n`);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* §5 D-2: the record the proxy publishes after listen. Written before the first request is answered, so
|
|
83
|
+
* a CTM that restarts immediately afterwards still finds the exact identity of what is listening.
|
|
84
|
+
* @returns {object} the record written
|
|
85
|
+
*/
|
|
86
|
+
function writeInstanceRecord({ dataDir, record }) {
|
|
87
|
+
ensureProxyDir(dataDir);
|
|
88
|
+
_writeAtomic0600Sync(instanceRecordPath(dataDir), `${JSON.stringify(record)}\n`);
|
|
89
|
+
return record;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* §5 D-3/D-2: compute this process's loaded-code hash ONCE, publish the record, and hand the same two
|
|
94
|
+
* fields to the accounting the /_health answer reports from -- so identity and the published record can
|
|
95
|
+
* never describe different code. §5 D-7 (WP-2): a handoff successor publishes `generation: n+1` and the
|
|
96
|
+
* predecessor it took over from, so the record names the LIVE socket owner and who handed it over.
|
|
97
|
+
*/
|
|
98
|
+
function publishInstanceRecord({ dataDir, port, nativePort = null, accounting = null, generation = 1, predecessorPid = null }) {
|
|
99
|
+
const { closureHash, closureFiles } = computeClosure({ repoRoot: REPO_ROOT });
|
|
100
|
+
const record = writeInstanceRecord({
|
|
101
|
+
dataDir,
|
|
102
|
+
record: {
|
|
103
|
+
v: RECORD_VERSION,
|
|
104
|
+
pid: process.pid,
|
|
105
|
+
port,
|
|
106
|
+
nativePort,
|
|
107
|
+
protocol: PROXY_PROTOCOL,
|
|
108
|
+
floorVersion: PROXY_FLOOR_VERSION,
|
|
109
|
+
closureHash,
|
|
110
|
+
closureFiles,
|
|
111
|
+
generation,
|
|
112
|
+
predecessorPid,
|
|
113
|
+
startedAt: new Date().toISOString(),
|
|
114
|
+
draining: false,
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
if (accounting && typeof accounting.setIdentity === 'function') {
|
|
118
|
+
accounting.setIdentity({ closureHash, closureFiles, generation, draining: false });
|
|
119
|
+
}
|
|
120
|
+
return record;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* §5 D-7 step 2: rewrite the published record in place with one patch (today: `draining`). Written
|
|
125
|
+
* BEFORE the successor is spawned, so a predecessor that dies mid-handoff leaves a truthful record --
|
|
126
|
+
* and rewritten with `draining:false` on any abort, because the predecessor kept serving.
|
|
127
|
+
* @returns {object|null} the rewritten record, or null when this process published none
|
|
128
|
+
*/
|
|
129
|
+
function updateInstanceRecord({ dataDir, patch }) {
|
|
130
|
+
const current = readInstanceRecord({ dataDir });
|
|
131
|
+
if (!current) return null;
|
|
132
|
+
return writeInstanceRecord({ dataDir, record: { ...current, ...patch } });
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* @returns {object|null} the published record, or null when there is none. A truncated, hand-edited or
|
|
137
|
+
* unusable record is reported as absent AND removed: writes are atomic via rename, so it can only be
|
|
138
|
+
* leftover garbage, and leaving it behind would make the next boot re-read the same lie.
|
|
139
|
+
*/
|
|
140
|
+
function readInstanceRecord({ dataDir }) {
|
|
141
|
+
let raw;
|
|
142
|
+
try {
|
|
143
|
+
raw = fs.readFileSync(instanceRecordPath(dataDir), 'utf8');
|
|
144
|
+
} catch (err) {
|
|
145
|
+
if (err && err.code === 'ENOENT') return null; // expected: no proxy has published one
|
|
146
|
+
throw err;
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
const record = JSON.parse(raw);
|
|
150
|
+
if (record && typeof record === 'object' && Number.isSafeInteger(record.pid) && record.pid > 0) return record;
|
|
151
|
+
} catch {
|
|
152
|
+
// expected: an unparseable record is not evidence -- fall through and remove it
|
|
153
|
+
}
|
|
154
|
+
unlinkInstanceRecord({ dataDir });
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function unlinkInstanceRecord({ dataDir }) {
|
|
159
|
+
return _unlinkQuiet(instanceRecordPath(dataDir));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** §5 D-2: written before each fresh spawn, read back when CTM adopts a proxy it did not spawn. */
|
|
163
|
+
function writeAdminSecret({ dataDir, secret }) {
|
|
164
|
+
ensureProxyDir(dataDir);
|
|
165
|
+
_writeAtomic0600Sync(adminSecretPath(dataDir), `${String(secret)}\n`);
|
|
166
|
+
return secret;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/** @returns {string|null} the current secret, or null when there is none to authenticate with. */
|
|
170
|
+
function readAdminSecret({ dataDir }) {
|
|
171
|
+
let raw;
|
|
172
|
+
try {
|
|
173
|
+
raw = fs.readFileSync(adminSecretPath(dataDir), 'utf8');
|
|
174
|
+
} catch (err) {
|
|
175
|
+
if (err && err.code === 'ENOENT') return null; // expected: this CTM never spawned a proxy here
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
const secret = raw.trim();
|
|
179
|
+
return secret || null;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function unlinkAdminSecret({ dataDir }) {
|
|
183
|
+
return _unlinkQuiet(adminSecretPath(dataDir));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* §5 D-1: the descriptor the child's stdout and stderr are bound to. One synchronous open per spawn
|
|
188
|
+
* (never a request path), rotating one generation at 64 MiB first: every proxy generation passes through
|
|
189
|
+
* this spawn, including WP-2's handoff successor, so this is the one place rotation has to happen.
|
|
190
|
+
*/
|
|
191
|
+
function openProxyLogFd(dataDir) {
|
|
192
|
+
ensureProxyDir(dataDir);
|
|
193
|
+
const target = proxyLogPath(dataDir);
|
|
194
|
+
const fd = fs.openSync(target, 'a');
|
|
195
|
+
if (fs.fstatSync(fd).size <= LOG_ROTATE_BYTES) return fd;
|
|
196
|
+
fs.closeSync(fd);
|
|
197
|
+
fs.renameSync(target, `${target}${LOG_ROTATE_SUFFIX}`);
|
|
198
|
+
return fs.openSync(target, 'a');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/** The supervisor's own copy of the descriptor; the child holds its own. */
|
|
202
|
+
function closeProxyLogFd(fd) {
|
|
203
|
+
try {
|
|
204
|
+
fs.closeSync(fd);
|
|
205
|
+
} catch (err) {
|
|
206
|
+
// expected: a descriptor that is already closed costs nothing -- the child has its own copy
|
|
207
|
+
void err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** §5 D-4: is this pid a live proxy? `ps` is the authority -- a recycled pid must never be signalled. */
|
|
212
|
+
function isProxyProcess(pid) {
|
|
213
|
+
if (!Number.isSafeInteger(pid) || pid < 1) return false;
|
|
214
|
+
try {
|
|
215
|
+
return execFileSync('ps', ['-o', 'command=', '-p', String(pid)], { encoding: 'utf8', timeout: PS_TIMEOUT_MS })
|
|
216
|
+
.includes(PROXY_SCRIPT_NAME);
|
|
217
|
+
} catch (err) {
|
|
218
|
+
// expected: ps exits non-zero for a pid that is gone, and a failed probe is never a proxy
|
|
219
|
+
void err;
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/** D-4 step 1: `process.kill(pid, 0)` -- EPERM means alive but not ours, which is still alive. */
|
|
225
|
+
function isProcessAlive(pid) {
|
|
226
|
+
if (!Number.isSafeInteger(pid) || pid < 1) return false;
|
|
227
|
+
try {
|
|
228
|
+
process.kill(pid, 0);
|
|
229
|
+
return true;
|
|
230
|
+
} catch (err) {
|
|
231
|
+
return err && err.code === 'EPERM';
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = {
|
|
236
|
+
RECORD_VERSION,
|
|
237
|
+
PROXY_PROTOCOL,
|
|
238
|
+
PROXY_FLOOR_VERSION,
|
|
239
|
+
LOG_ROTATE_BYTES,
|
|
240
|
+
proxyDir,
|
|
241
|
+
instanceRecordPath,
|
|
242
|
+
adminSecretPath,
|
|
243
|
+
proxyLogPath,
|
|
244
|
+
ensureProxyDir,
|
|
245
|
+
publishInstanceRecord,
|
|
246
|
+
updateInstanceRecord,
|
|
247
|
+
writeInstanceRecord,
|
|
248
|
+
readInstanceRecord,
|
|
249
|
+
unlinkInstanceRecord,
|
|
250
|
+
writeAdminSecret,
|
|
251
|
+
readAdminSecret,
|
|
252
|
+
unlinkAdminSecret,
|
|
253
|
+
openProxyLogFd,
|
|
254
|
+
closeProxyLogFd,
|
|
255
|
+
isProxyProcess,
|
|
256
|
+
isProcessAlive,
|
|
257
|
+
};
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
const crypto = require('node:crypto');
|
|
19
19
|
const chatUpstream = require('./chat-upstream');
|
|
20
20
|
const routes = require('./routes');
|
|
21
|
+
const { dropHandedOffCompactions } = require('./collaboration-codec');
|
|
21
22
|
const { effectiveOutputCap } = require('./output-cap');
|
|
22
23
|
const { upstreamRequest, readBodyBuffer } = require('./upstream-http');
|
|
23
24
|
const { parseRetryAfterMs } = require('./retry-after');
|
|
@@ -39,6 +40,10 @@ function errorEnvelope(type, message) {
|
|
|
39
40
|
return { error: { type: String(type), code: String(type), message: String(message) } };
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
function isPlainObject(value) {
|
|
44
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
function writeJson(res, status, obj) {
|
|
43
48
|
res.writeHead(status, { 'content-type': 'application/json' });
|
|
44
49
|
res.end(JSON.stringify(obj));
|
|
@@ -355,10 +360,27 @@ async function handle(req, res, ctx) {
|
|
|
355
360
|
writeJson(res, 502, errorEnvelope('route_not_servable', `route "${route.alias}" (${route.upstreamApis.join(',')}) is not servable from the Responses face`));
|
|
356
361
|
return;
|
|
357
362
|
}
|
|
363
|
+
// WP-3 (docs/codex-lead-routed-switch-history-handoff-2026-10-21.html §5 D-7): a `compaction`
|
|
364
|
+
// item whose plaintext handoff replacement travels in the SAME request is omitted
|
|
365
|
+
// -- the replacement IS the readable form of that turn, so both faces get the
|
|
366
|
+
// rewritten body (chat-translate translates the summary; passthrough relays it).
|
|
367
|
+
// An UNMATCHED compaction item is untouched, so chat-translate still refuses it
|
|
368
|
+
// through the existing preflight and passthrough still forwards it verbatim.
|
|
369
|
+
const handedOff = isPlainObject(ctx.body) ? dropHandedOffCompactions(ctx.body) : null;
|
|
370
|
+
let requestCtx = ctx;
|
|
371
|
+
if (handedOff && handedOff.droppedHandedOffCompactions > 0) {
|
|
372
|
+
const body = handedOff.body;
|
|
373
|
+
// Same tag/wording policy as chat-upstream.js's reasoning-ciphertext line: one
|
|
374
|
+
// line, once per request, counts only -- never provider text.
|
|
375
|
+
process.stderr.write(`[model-proxy] dropped ${handedOff.droppedHandedOffCompactions} handed-off compaction item(s)\n`);
|
|
376
|
+
// ctx.body was JSON-parsed from the request, so it always serializes; passthrough sends
|
|
377
|
+
// this buffer (the caller's content-length is in CALLER_HEADER_DENYLIST).
|
|
378
|
+
requestCtx = { ...ctx, body, rawBody: Buffer.from(JSON.stringify(body)) };
|
|
379
|
+
}
|
|
358
380
|
if (sel.mode === 'passthrough') {
|
|
359
|
-
return _passthrough(req, res,
|
|
381
|
+
return _passthrough(req, res, requestCtx);
|
|
360
382
|
}
|
|
361
|
-
return _handleChatTranslate(req, res,
|
|
383
|
+
return _handleChatTranslate(req, res, requestCtx);
|
|
362
384
|
}
|
|
363
385
|
|
|
364
386
|
module.exports = { handle, errorEnvelope, notFound, writeJson, recordTranslatedNonstreamTelemetry };
|