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,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Codex list markers, at parity with what the TUI paints, ported from
|
|
4
|
+
// codex-rs/tui/src/markdown_render.rs start_item (:1014-1090), prefix_spans (:2535-2565)
|
|
5
|
+
// codex-rs/tui/src/markdown_render/task_lists.rs task_list_marker
|
|
6
|
+
// codex-rs/tui/src/markdown_render_tests.rs list_unordered_single / list_nested / nested_five_levels_mixed_lists / ...
|
|
7
|
+
//
|
|
8
|
+
// Why this module exists: the reader joins CTM's rendered rows to the rows the live Codex screen
|
|
9
|
+
// actually shows, by identity of the row text. Codex draws unordered items as `• ` (PR #47322, on
|
|
10
|
+
// by default) and nests by a FIXED 4-column step, while the first CTM renderer drew `- ` indented by
|
|
11
|
+
// floor(spaces/2)*2 — so every list row missed the join and the reader drew it twice.
|
|
12
|
+
//
|
|
13
|
+
// The two facts that make the port non-obvious:
|
|
14
|
+
// * depth is the list nesting level; `width = depth*4 - 3`. The marker of an item at depth d is
|
|
15
|
+
// ONLY its own marker: prefix_spans skips every list context except the innermost, so an outer
|
|
16
|
+
// level contributes nothing to a nested row (no cumulative indent).
|
|
17
|
+
// * depth is NOT floor(spaces/2). It comes from a CommonMark container stack over the source
|
|
18
|
+
// lines; each open level remembers its item's CONTENT column, and a list line either opens a
|
|
19
|
+
// deeper level, lands as a sibling, or closes levels.
|
|
20
|
+
//
|
|
21
|
+
// Pure: no DOM, no clock, no I/O. Same input => same output. Tabs are already expanded to 4 spaces
|
|
22
|
+
// by the caller (renderMarkdownTextBlock does this before the line loop).
|
|
23
|
+
|
|
24
|
+
const BULLET = '•';
|
|
25
|
+
const UNCHECKED = '☐';
|
|
26
|
+
const CHECKED = '☑';
|
|
27
|
+
|
|
28
|
+
// Codex nests by a fixed 4-column step (markdown_render.rs:1042: `width = depth * 4 - 3`).
|
|
29
|
+
const HANG_STEP = 4;
|
|
30
|
+
|
|
31
|
+
// `- x` / `* x` / `+ x` and `1. x` / `1) x`. The content must be non-blank, so a bare rule or a
|
|
32
|
+
// lone marker keeps falling through to the renderer's paragraph path exactly as before.
|
|
33
|
+
const BULLET_LINE = /^( *)([-*+])( +)(\S.*)$/;
|
|
34
|
+
const ORDERED_LINE = /^( *)(\d{1,4})([.)])( +)(\S.*)$/;
|
|
35
|
+
// A GFM task checkbox, right after the marker (task_lists.rs:8-24). `[X]` counts as checked.
|
|
36
|
+
const TASK = /^\[([ xX])\](?: |$)/;
|
|
37
|
+
|
|
38
|
+
// `width` for a depth (markdown_render.rs:1042): depth 1 => 1, depth 2 => 5, depth 3 => 9 ...
|
|
39
|
+
function markerWidthFor(depth) {
|
|
40
|
+
return depth * HANG_STEP - 3;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// The column a wrapped (continuation) row of this item hangs at (markdown_render.rs:1057-1061):
|
|
44
|
+
// unordered `width + 1`, ordered `width + 2` whatever the digit count. A task item hangs at its whole
|
|
45
|
+
// marker's display width instead (itemOf).
|
|
46
|
+
function hangFor(entry) {
|
|
47
|
+
return entry.width + (entry.ordered ? 2 : 1);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// markdown_render.rs:1043-1056 + task_lists.rs:15-19: the task checkbox REPLACES a trailing `• `
|
|
51
|
+
// and keeps an ordered `N. ` prefix.
|
|
52
|
+
function markerFor(entry, index) {
|
|
53
|
+
const glyph = entry.task ? (entry.checked ? CHECKED : UNCHECKED) : BULLET;
|
|
54
|
+
if (!entry.ordered) return ' '.repeat(Math.max(0, entry.width - 1)) + glyph + ' ';
|
|
55
|
+
return String(index).padStart(entry.width) + '. ' + (entry.task ? glyph + ' ' : '');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Builds the row a list item paints: the marker (which already carries its own leading columns)
|
|
59
|
+
// plus the source text. `indent` is 0 for an item row because the marker string holds its column,
|
|
60
|
+
// and `hang` is the absolute continuation column. The caller adds the message prefix in front
|
|
61
|
+
// (rule R-1.6) and strips inline markdown from `text` alone, so the marker never joins a match.
|
|
62
|
+
function itemOf(entry, index, text) {
|
|
63
|
+
const marker = markerFor(entry, index);
|
|
64
|
+
// A task item hangs at its WHOLE marker's display width (task_lists.rs:22), so `10. ☐ ` hangs 6;
|
|
65
|
+
// every char of a marker is one column wide.
|
|
66
|
+
return { marker, text, indent: 0, hang: entry.task ? marker.length : hangFor(entry) };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// A tracker owns the CommonMark container stack for one text block. `consume(line)` must be called
|
|
70
|
+
// with EVERY source line, in order, including blank ones: a blank line does not close the stack,
|
|
71
|
+
// and a non-list line may be a continuation paragraph of the innermost item.
|
|
72
|
+
function createListTracker() {
|
|
73
|
+
const stack = []; // innermost last; each entry = { ordered, task, checked, width, contentCol, next }
|
|
74
|
+
|
|
75
|
+
const push = (fields) => {
|
|
76
|
+
const entry = {
|
|
77
|
+
ordered: fields.ordered,
|
|
78
|
+
task: fields.task,
|
|
79
|
+
checked: fields.checked,
|
|
80
|
+
width: markerWidthFor(stack.length + 1),
|
|
81
|
+
contentCol: fields.contentCol,
|
|
82
|
+
next: fields.next,
|
|
83
|
+
};
|
|
84
|
+
stack.push(entry);
|
|
85
|
+
return entry;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
// -> null when the line is not a list item (or a continuation of one); otherwise the row
|
|
90
|
+
// { marker, text, indent, hang } the renderer draws. `marker` is '' for a continuation row.
|
|
91
|
+
consume(rawLine) {
|
|
92
|
+
const line = String(rawLine == null ? '' : rawLine);
|
|
93
|
+
if (!line.trim()) return null; // a blank line does not close (R-1.5)
|
|
94
|
+
|
|
95
|
+
const bullet = BULLET_LINE.exec(line);
|
|
96
|
+
const ordered = bullet ? null : ORDERED_LINE.exec(line);
|
|
97
|
+
|
|
98
|
+
if (!bullet && !ordered) {
|
|
99
|
+
const top = stack[stack.length - 1];
|
|
100
|
+
const indent = line.length - line.replace(/^ */, '').length;
|
|
101
|
+
if (top && indent >= top.contentCol) {
|
|
102
|
+
// A non-blank, non-list line indented to the item's content column continues that item,
|
|
103
|
+
// rendered at the level's hang (R-1.5).
|
|
104
|
+
const hang = hangFor(top);
|
|
105
|
+
return { marker: '', text: line.trim(), indent: hang, hang };
|
|
106
|
+
}
|
|
107
|
+
stack.length = 0; // any other non-blank line closes every level
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const markerIndent = (bullet ? bullet[1] : ordered[1]).length;
|
|
112
|
+
// The marker LENGTH is `-` (1) or `N.` (digits + the bracket) — the checkbox is not part of
|
|
113
|
+
// it, so `- [ ] a` still has content column 2.
|
|
114
|
+
const markerLen = bullet ? 1 : ordered[2].length + 1;
|
|
115
|
+
const gap = (bullet ? bullet[3] : ordered[4]).length;
|
|
116
|
+
const contentCol = markerIndent + markerLen + gap;
|
|
117
|
+
const content = bullet ? bullet[4] : ordered[5];
|
|
118
|
+
const task = TASK.exec(content);
|
|
119
|
+
const isOrdered = !bullet;
|
|
120
|
+
const text = task ? content.slice(task[0].length) : content;
|
|
121
|
+
const fields = {
|
|
122
|
+
ordered: isOrdered,
|
|
123
|
+
task: !!task,
|
|
124
|
+
checked: !!task && task[1] !== ' ',
|
|
125
|
+
contentCol,
|
|
126
|
+
next: isOrdered ? Number(ordered[2]) : 0,
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const top = stack[stack.length - 1];
|
|
130
|
+
let entry;
|
|
131
|
+
if (top && markerIndent >= top.contentCol) {
|
|
132
|
+
entry = push(fields); // deeper than the innermost item's content -> a nested level
|
|
133
|
+
} else {
|
|
134
|
+
// Pop while the marker column sits left of the content column of the level BELOW the top.
|
|
135
|
+
while (stack.length >= 2 && markerIndent < stack[stack.length - 2].contentCol) stack.pop();
|
|
136
|
+
const sibling = stack[stack.length - 1];
|
|
137
|
+
if (!sibling || sibling.ordered !== isOrdered) {
|
|
138
|
+
// A sibling of the other type starts a NEW list at this depth; its numbering restarts
|
|
139
|
+
// from its own first source number.
|
|
140
|
+
if (sibling) stack.pop();
|
|
141
|
+
entry = push(fields);
|
|
142
|
+
} else {
|
|
143
|
+
entry = sibling; // same list: keep the level, refresh the per-item task state
|
|
144
|
+
entry.task = fields.task;
|
|
145
|
+
entry.checked = fields.checked;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
let index = 0;
|
|
150
|
+
if (entry.ordered) {
|
|
151
|
+
index = entry.next; // counts from the list's first source number, per sibling
|
|
152
|
+
entry.next += 1;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return itemOf(entry, index, text);
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
module.exports = { createListTracker };
|
|
@@ -191,6 +191,10 @@ function findCanonicalRolloutsByFilenameId(threadId, storageRoots) {
|
|
|
191
191
|
const candidate = path.join(dir, entry.name);
|
|
192
192
|
if (entry.isDirectory()) { stack.push({ dir: candidate, depth: depth + 1 }); continue; }
|
|
193
193
|
if (!entry.isFile() || sessionHistory.codexRolloutIdFromPath(candidate).toLowerCase() !== wanted) continue;
|
|
194
|
+
// Codex rust-v0.163.0 rollout/src/compression.rs should_skip_compressed_sibling: one rollout is
|
|
195
|
+
// one physical file, and a `.jsonl.zst` is hidden while its plain `.jsonl` exists (Codex
|
|
196
|
+
// compresses an idle thread, then appends to a re-materialized plain file beside the stale copy).
|
|
197
|
+
if (candidate.endsWith('.jsonl.zst') && transcriptSource.statTranscriptSync(candidate.slice(0, -'.zst'.length)).exists) continue;
|
|
194
198
|
const info = transcriptSource.statTranscriptSync(candidate);
|
|
195
199
|
if (!info.exists) throw Object.assign(new Error('native history candidate changed during lookup'), { code: 'native_history_lineage_changed' });
|
|
196
200
|
let canonical;
|
|
@@ -26,6 +26,7 @@ const fs = require('fs');
|
|
|
26
26
|
const path = require('path');
|
|
27
27
|
const os = require('os');
|
|
28
28
|
const crypto = require('crypto');
|
|
29
|
+
const { isWritableOnlyByOwner } = require('../../shared/owner-write-safety');
|
|
29
30
|
|
|
30
31
|
function homeBase(homeDir) {
|
|
31
32
|
return homeDir || process.env.HOME || process.env.USERPROFILE || os.homedir() || '';
|
|
@@ -77,7 +78,7 @@ function resolveCodexSessionsRoot(options = {}) {
|
|
|
77
78
|
out.refusalCode = 'wrong-owner';
|
|
78
79
|
return out;
|
|
79
80
|
}
|
|
80
|
-
if ((
|
|
81
|
+
if (!isWritableOnlyByOwner(stat)) {
|
|
81
82
|
out.validationStatus = 'refused';
|
|
82
83
|
out.refusalCode = 'unsafe-permissions';
|
|
83
84
|
return out;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* WP-2 D-5/D-6 (docs/codex-lead-routed-switch-history-handoff-2026-10-21.html §6.3, §5 D-5/D-6).
|
|
5
|
+
*
|
|
6
|
+
* A GPT Codex lead whose last compaction holds an OpenAI-encrypted `compaction` item cannot carry
|
|
7
|
+
* that ciphertext to a routed target model. The user's decision (2026-10-21) is that the SOURCE GPT
|
|
8
|
+
* model writes a plaintext handoff summary before the switch.
|
|
9
|
+
*
|
|
10
|
+
* The summary is produced by an EPHEMERAL fork of the source thread, so:
|
|
11
|
+
* - the user's live source PTY and source thread are never touched (anti-goal: never submit into
|
|
12
|
+
* the live source session);
|
|
13
|
+
* - no rollout file is persisted for the fork (measured on Codex 0.163.0: the rollout-file set is
|
|
14
|
+
* byte-identical before and after);
|
|
15
|
+
* - the turn is a real model turn on the user's own ChatGPT account (risk R-1: it spends quota and
|
|
16
|
+
* can take up to HANDOFF_DEADLINE_MS, which is why the caller shows a "compacting" phase).
|
|
17
|
+
*
|
|
18
|
+
* This module owns the child process, the app-server JSON-RPC sequence, and the text validation. It
|
|
19
|
+
* writes nothing anywhere: the returned text travels in memory to lib/native-history-control.js,
|
|
20
|
+
* which injects it into the forked target as one developer message.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
const { spawn } = require('node:child_process');
|
|
24
|
+
const crypto = require('node:crypto');
|
|
25
|
+
const path = require('node:path');
|
|
26
|
+
// The ONE process-launch/RPC implementation. `allowTurn: true` is what makes the handoff turn legal:
|
|
27
|
+
// every fork and readback path keeps the fail-closed `native_fork_emitted_model_turn` guard.
|
|
28
|
+
const { createBoundedRpc, terminateChild } = require('./native-history-control');
|
|
29
|
+
|
|
30
|
+
// Codex rust-v0.163.0 codex-rs/prompts/templates/compact/prompt.md, verbatim (Apache-2.0; the file
|
|
31
|
+
// carries the upstream copyright header). Kept byte-identical on purpose: the source model must be
|
|
32
|
+
// asked the same question Codex asks its own compactor, or the summary is a different artifact.
|
|
33
|
+
const CODEX_COMPACT_PROMPT = 'You are performing a CONTEXT CHECKPOINT COMPACTION. Create a handoff summary for another LLM that will resume the task.\n'
|
|
34
|
+
+ '\n'
|
|
35
|
+
+ 'Include:\n'
|
|
36
|
+
+ '- Current progress and key decisions made\n'
|
|
37
|
+
+ '- Important context, constraints, or user preferences\n'
|
|
38
|
+
+ '- What remains to be done (clear next steps)\n'
|
|
39
|
+
+ '- Any critical data, examples, or references needed to continue\n'
|
|
40
|
+
+ '\n'
|
|
41
|
+
+ 'Be concise, structured, and focused on helping the next LLM seamlessly continue the work.\n';
|
|
42
|
+
// The CTM addition (§6.3): what a provider switch additionally needs that a same-provider compaction
|
|
43
|
+
// does not. It is not a tool-using turn -- the fork is read-only and every tool is refused.
|
|
44
|
+
const CTM_HANDOFF_ADDITION = '\n\nAlso include, verbatim and complete: every file path you changed or must change, exact error strings and test names still failing, commands that must run next, and the single next action. Do not call tools. Reply with the summary only.';
|
|
45
|
+
const HANDOFF_PROMPT = `${CODEX_COMPACT_PROMPT}${CTM_HANDOFF_ADDITION}`;
|
|
46
|
+
|
|
47
|
+
const HANDOFF_MIN_CHARS = 200;
|
|
48
|
+
const HANDOFF_MAX_CHARS = 32_768;
|
|
49
|
+
const HANDOFF_DEADLINE_MS = 240_000;
|
|
50
|
+
const HANDOFF_APP_SERVER_ARGS = Object.freeze(['app-server', '-c', 'approval_policy="never"',
|
|
51
|
+
'-c', 'sandbox_mode="read-only"']);
|
|
52
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
53
|
+
const MARKER_PREFIX = '<ctm_handoff';
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Ask the SOURCE model for the plaintext handoff that will cover its sealed compaction.
|
|
57
|
+
*
|
|
58
|
+
* @param {{executablePath:string, homeDir:string, codexHome:string, workingDirectory:string, tmpDir?:string|null}} source
|
|
59
|
+
* @param {{sourceThreadId:string, lastTurnId:string}} checkpoint
|
|
60
|
+
* @param {{signal?:AbortSignal, timeoutMs?:number}} [opts]
|
|
61
|
+
* @returns {Promise<{ok:true, text:string, textSha256:string, turnId:string, model:string|null}
|
|
62
|
+
* | {ok:false, reason:'handoff_fork_failed'|'handoff_turn_failed'|'handoff_empty'|'handoff_too_long'
|
|
63
|
+
* |'handoff_marker_in_text'|'handoff_timeout'|'transition_cancelled'}>}
|
|
64
|
+
*/
|
|
65
|
+
async function writeSourceHandoff(source, checkpoint, { signal, timeoutMs } = {}) {
|
|
66
|
+
if (signal?.aborted) return failure('transition_cancelled');
|
|
67
|
+
const sourceThreadId = nonempty(checkpoint?.sourceThreadId);
|
|
68
|
+
const lastTurnId = nonempty(checkpoint?.lastTurnId);
|
|
69
|
+
const executablePath = nonempty(source?.executablePath);
|
|
70
|
+
if (![executablePath, source?.homeDir, source?.codexHome, source?.workingDirectory]
|
|
71
|
+
.every((value) => typeof value === 'string' && path.isAbsolute(value))
|
|
72
|
+
|| (source?.tmpDir != null && !path.isAbsolute(source.tmpDir))
|
|
73
|
+
|| !UUID_RE.test(sourceThreadId) || !lastTurnId) return failure('handoff_fork_failed');
|
|
74
|
+
const deadlineMs = Number.isSafeInteger(timeoutMs) && timeoutMs > 0
|
|
75
|
+
? Math.min(timeoutMs, HANDOFF_DEADLINE_MS) : HANDOFF_DEADLINE_MS;
|
|
76
|
+
const state = { turnId: '', completed: null, agentText: '', onComplete: null };
|
|
77
|
+
let stage = 'fork';
|
|
78
|
+
let child = null;
|
|
79
|
+
let rpc = null;
|
|
80
|
+
try {
|
|
81
|
+
child = spawn(executablePath, [...HANDOFF_APP_SERVER_ARGS], { cwd: source.workingDirectory,
|
|
82
|
+
env: handoffEnv(source), stdio: ['pipe', 'pipe', 'pipe'] });
|
|
83
|
+
child.stderr.resume();
|
|
84
|
+
rpc = createBoundedRpc(child, { signal, timeoutMs: deadlineMs, allowTurn: true,
|
|
85
|
+
onNotification: (method, params) => observe(state, method, params) });
|
|
86
|
+
const initialized = await rpc.request('initialize', {
|
|
87
|
+
clientInfo: { name: 'ctm-source-handoff', version: '1' }, capabilities: {},
|
|
88
|
+
});
|
|
89
|
+
if (!initialized || rpc.closed) return failure('handoff_fork_failed');
|
|
90
|
+
await rpc.notify('initialized', {});
|
|
91
|
+
const forked = await rpc.request('thread/fork', { threadId: sourceThreadId, lastTurnId,
|
|
92
|
+
ephemeral: true, excludeTurns: true });
|
|
93
|
+
const ephemeralId = nonempty(forked?.thread?.id || forked?.threadId || forked?.thread_id);
|
|
94
|
+
if (!UUID_RE.test(ephemeralId) || ephemeralId === sourceThreadId) return failure('handoff_fork_failed');
|
|
95
|
+
stage = 'turn';
|
|
96
|
+
const started = await rpc.request('turn/start', { threadId: ephemeralId,
|
|
97
|
+
input: [{ type: 'text', text: HANDOFF_PROMPT }] });
|
|
98
|
+
state.turnId = nonempty(started?.turn?.id || started?.turnId || started?.id);
|
|
99
|
+
if (!state.turnId) return failure('handoff_turn_failed');
|
|
100
|
+
const turn = await waitForTurn(state, signal, deadlineMs);
|
|
101
|
+
if (signal?.aborted) return failure('transition_cancelled');
|
|
102
|
+
if (!turn || turn.timedOut) return failure(turn ? 'handoff_timeout' : 'handoff_turn_failed');
|
|
103
|
+
if (nonempty(turn.status) !== 'completed') return failure('handoff_turn_failed');
|
|
104
|
+
const text = (agentMessageText(turn) || state.agentText).trim();
|
|
105
|
+
if (text.length < HANDOFF_MIN_CHARS) return failure('handoff_empty');
|
|
106
|
+
if (text.length > HANDOFF_MAX_CHARS) return failure('handoff_too_long');
|
|
107
|
+
// The target's own readback looks for this prefix; a summary that already contains one could
|
|
108
|
+
// forge coverage for an unrelated sealed item.
|
|
109
|
+
if (text.includes(MARKER_PREFIX)) return failure('handoff_marker_in_text');
|
|
110
|
+
return { ok: true, text, textSha256: sha256(text), turnId: state.turnId,
|
|
111
|
+
model: nonempty(turn.model || turn.modelId) || null };
|
|
112
|
+
} catch (error) {
|
|
113
|
+
if (signal?.aborted || error?.code === 'transition_cancelled') return failure('transition_cancelled');
|
|
114
|
+
if (error?.code === 'native_rpc_deadline') return failure('handoff_timeout');
|
|
115
|
+
return failure(stage === 'turn' ? 'handoff_turn_failed' : 'handoff_fork_failed');
|
|
116
|
+
} finally {
|
|
117
|
+
// The ephemeral fork must never outlive this call, on any path -- including a refusal or an abort.
|
|
118
|
+
if (rpc) await rpc.close();
|
|
119
|
+
else await terminateChild(child);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function handoffEnv(source) {
|
|
124
|
+
const env = { PATH: process.env.PATH || '/usr/bin:/bin', HOME: source.homeDir,
|
|
125
|
+
CODEX_HOME: source.codexHome, LANG: process.env.LANG || 'C',
|
|
126
|
+
CTM_PROCESS_ROLE: 'ctm-source-handoff' };
|
|
127
|
+
if (source.tmpDir) env.TMPDIR = source.tmpDir;
|
|
128
|
+
return env;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// The single notification sink: turn/completed carries the finished turn (with its items), and
|
|
132
|
+
// item/completed is the fallback if a Codex version reports the agent message only there.
|
|
133
|
+
function observe(state, method, params) {
|
|
134
|
+
if (method === 'item/completed') {
|
|
135
|
+
const item = params?.item;
|
|
136
|
+
if (nonempty(item?.type) === 'agentMessage' && typeof item.text === 'string'
|
|
137
|
+
&& (!state.turnId || nonempty(params?.turnId || item.turn_id) === state.turnId)) {
|
|
138
|
+
state.agentText = item.text;
|
|
139
|
+
}
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (method !== 'turn/completed') return;
|
|
143
|
+
const turn = params?.turn;
|
|
144
|
+
if (state.turnId && nonempty(turn?.id) !== state.turnId) return;
|
|
145
|
+
state.completed = isRecord(turn) ? turn : null;
|
|
146
|
+
const settle = state.onComplete;
|
|
147
|
+
state.onComplete = null;
|
|
148
|
+
if (settle) settle(state.completed);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function waitForTurn(state, signal, deadlineMs) {
|
|
152
|
+
if (state.completed) return Promise.resolve(state.completed);
|
|
153
|
+
return new Promise((resolve) => {
|
|
154
|
+
let settled = false;
|
|
155
|
+
const timer = setTimeout(() => finish({ timedOut: true }), deadlineMs);
|
|
156
|
+
const onAbort = () => finish(null);
|
|
157
|
+
function finish(value) {
|
|
158
|
+
if (settled) return;
|
|
159
|
+
settled = true;
|
|
160
|
+
clearTimeout(timer);
|
|
161
|
+
state.onComplete = null;
|
|
162
|
+
signal?.removeEventListener('abort', onAbort);
|
|
163
|
+
resolve(value);
|
|
164
|
+
}
|
|
165
|
+
state.onComplete = finish;
|
|
166
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
167
|
+
if (state.completed) finish(state.completed);
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function agentMessageText(turn) {
|
|
172
|
+
if (!Array.isArray(turn?.items)) return '';
|
|
173
|
+
for (let index = turn.items.length - 1; index >= 0; index -= 1) {
|
|
174
|
+
const item = turn.items[index];
|
|
175
|
+
if (nonempty(item?.type) === 'agentMessage' && typeof item.text === 'string') return item.text;
|
|
176
|
+
}
|
|
177
|
+
return '';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function failure(reason) { return { ok: false, reason }; }
|
|
181
|
+
function sha256(value) { return crypto.createHash('sha256').update(value).digest('hex'); }
|
|
182
|
+
function nonempty(value) { return typeof value === 'string' && value.trim() ? value.trim() : ''; }
|
|
183
|
+
function isRecord(value) { return !!value && typeof value === 'object' && !Array.isArray(value); }
|
|
184
|
+
|
|
185
|
+
module.exports = { writeSourceHandoff, HANDOFF_PROMPT, HANDOFF_MIN_CHARS, HANDOFF_MAX_CHARS,
|
|
186
|
+
HANDOFF_DEADLINE_MS };
|
|
@@ -288,18 +288,6 @@ function isGenericCodingSessionLabel(label, agentType = 'codex') {
|
|
|
288
288
|
|| lower === 'session';
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
-
function shouldApplyCodexAutoTitle({ session, existingTitle } = {}) {
|
|
292
|
-
if (existingTitle && existingTitle.userRenamed) return false;
|
|
293
|
-
for (const candidate of [session?.label, existingTitle?.title]) {
|
|
294
|
-
const text = normalizeTitleText(candidate);
|
|
295
|
-
if (text && !/^Codex:\s*/i.test(text) && !isGenericCodingSessionLabel(text, 'codex')) return false;
|
|
296
|
-
}
|
|
297
|
-
const title = normalizeTitleText(existingTitle?.title || session?.label || '');
|
|
298
|
-
if (!title) return true;
|
|
299
|
-
if (/^Codex:\s*/i.test(title) && !isGenericCodingSessionLabel(title, 'codex')) return true;
|
|
300
|
-
return isGenericCodingSessionLabel(title, 'codex');
|
|
301
|
-
}
|
|
302
|
-
|
|
303
291
|
function resolveModelToRegistry(modelName) {
|
|
304
292
|
if (!modelName) return null;
|
|
305
293
|
let provider = null;
|
|
@@ -561,11 +549,23 @@ function createCodingAgentModelSync({
|
|
|
561
549
|
if (!modelStateRuntime) _persistSessionModel(session, model);
|
|
562
550
|
changed = true;
|
|
563
551
|
}
|
|
564
|
-
if (agentType === 'codex'
|
|
552
|
+
if (agentType === 'codex') {
|
|
565
553
|
const existingTitle = titlesById.get(session.id) || null;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
554
|
+
// WP-4/D-10: the thread's OWN name (threads.name) is read on EVERY tick — there is no
|
|
555
|
+
// one-shot `_codexTitleSynced` latch any more, and no title-vs-label precedence test that
|
|
556
|
+
// could let a CTM title block the name (S-22). applyProviderTitle owns the tier rules: it
|
|
557
|
+
// re-asserts a drifted tier-1 (user-renamed) row outward, skips a row already carrying this
|
|
558
|
+
// exact name, and writes the agent name over an older tier-2/tier-3 title.
|
|
559
|
+
const agentName = session._claudeSessionId ? getCodexThreadAgentName(session._claudeSessionId) : '';
|
|
560
|
+
if (agentName) {
|
|
561
|
+
const outcome = applyProviderTitle({ db: dbModule, sessionId: session.id, provider: 'codex', agentName }); // not a dbModule method — no dispatchOwnerMutation
|
|
562
|
+
if (outcome.applied === 'agent') {
|
|
563
|
+
session.label = `Codex: ${agentName}`;
|
|
564
|
+
changed = true;
|
|
565
|
+
}
|
|
566
|
+
} else {
|
|
567
|
+
// No thread name at all: the CTM live label is the only signal left, so it repairs a
|
|
568
|
+
// stale auto-title. Never a tier-2 (`provider_title`) row, never the user's own name.
|
|
569
569
|
const liveLabel = normalizeTitleText(session.label);
|
|
570
570
|
if (liveLabel
|
|
571
571
|
&& !isGenericCodingSessionLabel(liveLabel, 'codex')
|
|
@@ -573,16 +573,6 @@ function createCodingAgentModelSync({
|
|
|
573
573
|
dispatchOwnerMutation(dbModule, 'setSessionTitleNew', [session.id, liveLabel, false], 'coding-agent title');
|
|
574
574
|
changed = true;
|
|
575
575
|
}
|
|
576
|
-
session._codexTitleSynced = true;
|
|
577
|
-
} else {
|
|
578
|
-
// Requires the exact Codex thread id — cwd/spawn-time matching is unsafe for periodic refresh.
|
|
579
|
-
const agentName = session._claudeSessionId ? getCodexThreadAgentName(session._claudeSessionId) : '';
|
|
580
|
-
if (agentName) {
|
|
581
|
-
session.label = `Codex: ${agentName}`;
|
|
582
|
-
session._codexTitleSynced = true;
|
|
583
|
-
applyProviderTitle({ db: dbModule, sessionId: session.id, provider: 'codex', agentName }); // not a dbModule method — no dispatchOwnerMutation
|
|
584
|
-
changed = true;
|
|
585
|
-
}
|
|
586
576
|
}
|
|
587
577
|
}
|
|
588
578
|
}
|
|
@@ -688,7 +678,7 @@ function createCodingAgentModelSync({
|
|
|
688
678
|
if (agentType === 'gemini' && !modelStateRuntime) {
|
|
689
679
|
context.result = { id: session.id, modelName: _modelArgValue(session.args) };
|
|
690
680
|
} else {
|
|
691
|
-
const item = { id: session.id, agentType, directory: session.cwd || (session.meta && session.meta.cwd) || '', knownSourceRevision: agentType === 'opencode' ? modelSyncState.knownSourceRevision(session.id) : '', claudeJsonlPath, codexThreadId: agentType === 'codex' ? (session._providerResumeId || session._claudeSessionId || '') : (session._claudeSessionId || session._providerResumeId || null), wantCodexTitle: agentType === 'codex' &&
|
|
681
|
+
const item = { id: session.id, agentType, directory: session.cwd || (session.meta && session.meta.cwd) || '', knownSourceRevision: agentType === 'opencode' ? modelSyncState.knownSourceRevision(session.id) : '', claudeJsonlPath, codexThreadId: agentType === 'codex' ? (session._providerResumeId || session._claudeSessionId || '') : (session._claudeSessionId || session._providerResumeId || null), wantCodexTitle: agentType === 'codex' && !!(session._providerResumeId || session._claudeSessionId) }; // WP-4/D-10: no one-shot latch gates the name read
|
|
692
682
|
if (modelStateRuntime) {
|
|
693
683
|
Object.assign(item, { owner: context.ownerSnapshot, providerSessionId: context.ownerSnapshot.providerSessionId, processGeneration: context.ownerSnapshot.processGeneration, providerEnv: modelProviderEnv(session), geminiTranscriptPath: session._geminiTranscriptPath || '' });
|
|
694
684
|
// The pre-await half of the source binding, and the prior checkpoint this request may carry.
|
|
@@ -758,7 +748,7 @@ function createCodingAgentModelSync({
|
|
|
758
748
|
&& (session.model_id !== model.model_id || session.model_provider !== model.model_provider);
|
|
759
749
|
if (agentType === 'claude' && model) claudeResolved += 1; if (modelChanged && session.model_id && String(session.model_source || '') === 'db:restore') repairedStamps += 1; // I-6: ONLY a stamp still carrying the pre-apply db:restore provenance is a repair; a mid-session /model switch on a live-sourced row is a normal change
|
|
760
750
|
const truthChanged = _claudeTruthNeedsApply(session, truth);
|
|
761
|
-
const needsTitleDecision = agentType === 'codex'
|
|
751
|
+
const needsTitleDecision = agentType === 'codex'; // WP-4/D-10: a title decision is taken on every probe, never once
|
|
762
752
|
// Codex: the effort of the rollout's latest turn_context (observed, never requested).
|
|
763
753
|
const codexEffortChanged = agentType === 'codex' && result.modelEffort !== undefined
|
|
764
754
|
&& String(session.model_effort || '') !== String(result.modelEffort || '');
|
|
@@ -853,7 +843,9 @@ function createCodingAgentModelSync({
|
|
|
853
843
|
changed = true;
|
|
854
844
|
}
|
|
855
845
|
}
|
|
856
|
-
|
|
846
|
+
// entry.titleSynced is the batch's "the title decision ran" receipt (the completion cache
|
|
847
|
+
// below and the durable applier's latch both consume it); the session no longer carries a
|
|
848
|
+
// one-shot `_codexTitleSynced` gate of its own.
|
|
857
849
|
const complete = (!context.intent.modelId || entry.modelPersisted)
|
|
858
850
|
&& (agentType !== 'codex' || entry.titleSynced);
|
|
859
851
|
if (complete) modelSyncState.cacheCompletedContext(context);
|
|
@@ -942,5 +934,4 @@ module.exports = {
|
|
|
942
934
|
opencodeDataDbPath,
|
|
943
935
|
isGenericCodingSessionLabel,
|
|
944
936
|
resolveModelToRegistry,
|
|
945
|
-
shouldApplyCodexAutoTitle,
|
|
946
937
|
};
|
|
@@ -3,9 +3,28 @@
|
|
|
3
3
|
// Pure decoders for native provider evidence. These functions deliberately do no discovery or I/O.
|
|
4
4
|
const SYNTHETIC = '<synthetic>';
|
|
5
5
|
const { CLAUDE_ALIASES } = require('./claude-model-selection-vocabulary');
|
|
6
|
+
const { WORKER_MODEL_ALIAS } = require('./model-proxy/lead-worker-picks');
|
|
6
7
|
const ALIASES = new Set([...CLAUDE_ALIASES, 'default']);
|
|
7
8
|
const GEMINI_ALIASES = new Set(['auto', 'default', 'pro', 'flash', 'flash-lite']);
|
|
8
9
|
|
|
10
|
+
/*
|
|
11
|
+
* The ONE reserved delegate alias (WORKER_MODEL_ALIAS in lib/model-proxy/lead-worker-picks.js). It names
|
|
12
|
+
* what a lead's SUBAGENTS ask the proxy for -- never a model a session's own CLI runs -- so it is
|
|
13
|
+
* refused by VALUE (trimmed, case-insensitive) before any producer can hand it to the ledger as a
|
|
14
|
+
* lead fact. Normal CLI aliases and policies keep their existing behavior.
|
|
15
|
+
*
|
|
16
|
+
* The value is normalised through the SAME one marker rule `modelClass` applies below:
|
|
17
|
+
* The reserved alias with `[1m]` is wearing the session's context marker, not a different
|
|
18
|
+
* model, so it can never be a lead fact either. Exactly one marker, and no blacklist of lookalikes
|
|
19
|
+
* (lookalikes, `claude-opus-5-5[1m]` and every real id keep their existing behavior).
|
|
20
|
+
*/
|
|
21
|
+
function isReservedWorkerModel(value) {
|
|
22
|
+
if (typeof value !== 'string') return false;
|
|
23
|
+
const folded = value.trim().toLowerCase();
|
|
24
|
+
const base = folded.endsWith('[1m]') ? folded.slice(0, -4) : folded;
|
|
25
|
+
return base === WORKER_MODEL_ALIAS;
|
|
26
|
+
}
|
|
27
|
+
|
|
9
28
|
function bounded(value, required = false) {
|
|
10
29
|
if (typeof value !== 'string') return null;
|
|
11
30
|
const valueTrimmed = value.trim();
|
|
@@ -46,6 +65,7 @@ function emit(out, args) { const item = observation(...args); if (item) out.push
|
|
|
46
65
|
function modelClass(raw) {
|
|
47
66
|
const name = bounded(raw, true);
|
|
48
67
|
if (!name) return null;
|
|
68
|
+
if (isReservedWorkerModel(name)) return null;
|
|
49
69
|
const suffix = name.toLowerCase().endsWith('[1m]') ? '[1m]' : '';
|
|
50
70
|
const base = suffix ? name.slice(0, -4) : name;
|
|
51
71
|
const folded = base.toLowerCase();
|
|
@@ -165,7 +185,9 @@ function codexSettingsCandidate(owner, rec, index, resumeId) {
|
|
|
165
185
|
if (rec.type !== 'event_msg' || p.type !== 'thread_settings_applied' || !p.thread_id ||
|
|
166
186
|
(p.thread_id !== owner.providerSessionId && p.thread_id !== resumeId) || !p.thread_settings) return null;
|
|
167
187
|
const model = bounded(p.thread_settings.model, true);
|
|
168
|
-
|
|
188
|
+
// Codex carries the same reserved alias in its thread settings and turn contexts when a lead's
|
|
189
|
+
// subagents are routed through the proxy; neither can be the lead's own model (AC-0d).
|
|
190
|
+
if (!model || isReservedWorkerModel(model)) return null;
|
|
169
191
|
const ordinal = Number.isSafeInteger(rec.ordinal) && rec.ordinal >= 0 ? String(rec.ordinal) : null;
|
|
170
192
|
return { at: time(rec.timestamp), index, value: model, effort: p.thread_settings.reasoning_effort ?? null,
|
|
171
193
|
sourceRevision: ordinal, source: 'codex:thread-settings' };
|
|
@@ -186,6 +208,7 @@ function codexTurnContextCandidate(owner, rec, index, resumeId) {
|
|
|
186
208
|
if (!(rec.type === 'turn_context' || (rec.type === 'event_msg' && p.type === 'turn_context'))) return null;
|
|
187
209
|
const model = bounded(p.model ?? p.model_id, true);
|
|
188
210
|
const turnId = p.turn_id == null ? null : String(p.turn_id);
|
|
211
|
+
if (isReservedWorkerModel(model)) return null;
|
|
189
212
|
return { at: time(rec.timestamp), index, payload: p, model, turnId };
|
|
190
213
|
}
|
|
191
214
|
|
|
@@ -297,4 +320,6 @@ module.exports = { claudeModelObservations, claudeHookModelObservations, codexMo
|
|
|
297
320
|
laterRank: later, isCodexRelatedRecord, codexThreadMatches,
|
|
298
321
|
claudeIdentityCandidate, claudeTurnCandidate, claudeIdentityObservations, claudeTurnObservations,
|
|
299
322
|
codexSettingsCandidate, codexSettingsObservations, codexTurnContextCandidate, codexTerminalCandidate,
|
|
300
|
-
codexTurnObservations
|
|
323
|
+
codexTurnObservations,
|
|
324
|
+
// WP-0: the one shared "is this the reserved delegate alias?" predicate (see the doc comment above).
|
|
325
|
+
isReservedWorkerModel };
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { isReservedWorkerModel } = require('./coding-session-model-adapters');
|
|
4
|
+
|
|
3
5
|
const PRIORITY = Object.freeze({ resolved: 3, lastCompletedTurn: 2, selection: 1 });
|
|
4
6
|
const MODEL_RESULT_PAGE_SIZE = 8;
|
|
5
7
|
const MAX_MODEL_RESULT_BYTES = 1024 * 1024;
|
|
6
8
|
|
|
7
9
|
function concrete(fact, allowNullKind) {
|
|
8
10
|
return !!fact && typeof fact.value === 'string' && fact.value.trim() !== '' && Buffer.byteLength(fact.value.trim(), 'utf8') <= 256 && !fact.value.includes('<synthetic>') && Number.isFinite(fact.observedAtMs) && fact.observedAtMs >= 0 &&
|
|
11
|
+
// WP-0 (AC-0c): a historical row may still HOLD the reserved alias (it is kept for audit), but it
|
|
12
|
+
// is never a compatibility model -- the real selection/turn wins by the existing precedence below.
|
|
13
|
+
!isReservedWorkerModel(fact.value) &&
|
|
9
14
|
(fact.selectionKind === 'concrete' || (allowNullKind && fact.selectionKind === null));
|
|
10
15
|
}
|
|
11
16
|
|
|
@@ -1470,7 +1470,7 @@ function createDurableModelResultApplier({ modelStateRuntime, isCurrent, isOwner
|
|
|
1470
1470
|
context.capturedSignature = refreshSignature(context.session, context.agentType);
|
|
1471
1471
|
if (context.agentType === 'opencode' && context.result.sourceRevision) context.nextSourceRevision = String(context.result.sourceRevision);
|
|
1472
1472
|
cacheCompleted(context);
|
|
1473
|
-
if (context.agentType === 'codex'
|
|
1473
|
+
if (context.agentType === 'codex') { // WP-4/D-10: the thread name is decided on every completed probe, never once
|
|
1474
1474
|
context.needsTitleDecision = true;
|
|
1475
1475
|
intents.push({ id: context.session.id, agentType: 'codex', generation, capturedSignature: context.capturedSignature,
|
|
1476
1476
|
agentSessionId: context.ownerSnapshot.providerSessionId, liveLabel: normalizeTitle(context.session.label),
|
|
@@ -1488,7 +1488,6 @@ function createDurableModelResultApplier({ modelStateRuntime, isCurrent, isOwner
|
|
|
1488
1488
|
const entry = byId.get(String(context.session.id));
|
|
1489
1489
|
if (!entry || Number(entry.generation) !== generation || entry.capturedSignature !== context.capturedSignature) continue;
|
|
1490
1490
|
if (entry.titleApplied && entry.title && entry.titleKind !== 'live') { const label = `Codex: ${entry.title}`; if (context.session.label !== label) { context.session.label = label; titleChanged = true; } }
|
|
1491
|
-
if (entry.titleSynced) context.session._codexTitleSynced = true;
|
|
1492
1491
|
if (entry.titleSynced) cacheCompleted(context);
|
|
1493
1492
|
}
|
|
1494
1493
|
if (titleChanged && typeof broadcastSessionList === 'function') broadcastSessionList();
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { isReservedWorkerModel } = require('./coding-session-model-adapters');
|
|
4
|
+
|
|
3
5
|
const SOURCE_GROUPS = {
|
|
4
6
|
claude: {
|
|
5
7
|
selection: ['hook:PostModelSwitch', 'hook:SessionStart', 'transcript:identity', 'db:restore'],
|
|
@@ -54,6 +56,14 @@ function validateObservation(input) {
|
|
|
54
56
|
if (fields.outcome !== 'completed' || fields.value === null || fields.selectionKind !== 'concrete') return null;
|
|
55
57
|
}
|
|
56
58
|
if (fields.kind === 'selection' && ['alias', 'policy'].includes(fields.selectionKind) && fields.value === null) return null;
|
|
59
|
+
/*
|
|
60
|
+
* WP-0 (AC-0b): the reserved delegate alias is refused by VALUE for every kind, whatever
|
|
61
|
+
* selectionKind a caller claims for it -- a lead session's own model can never be what its
|
|
62
|
+
* subagents ask the proxy for. `validFact`/`validPrior` deliberately stay permissive, so a row
|
|
63
|
+
* already persisted with the alias still loads (the projection skips the fact; history is never
|
|
64
|
+
* rewritten or dropped).
|
|
65
|
+
*/
|
|
66
|
+
if (isReservedWorkerModel(fields.value)) return null;
|
|
57
67
|
return { ...fields, processGeneration: input.processGeneration, observedAtMs: input.observedAtMs };
|
|
58
68
|
}
|
|
59
69
|
|
|
@@ -127,7 +127,6 @@ function createCodingSessionModelSyncState({
|
|
|
127
127
|
String(session?.model_source || ''),
|
|
128
128
|
Number(session?.model_observed_at || 0),
|
|
129
129
|
String(session?.label || ''),
|
|
130
|
-
session?._codexTitleSynced === true,
|
|
131
130
|
String(session?._claudeSessionId || session?.agentSessionId || ''),
|
|
132
131
|
/*
|
|
133
132
|
* Corrective finding 10: the PURE provider-source inputs a checkpoint's offsets are relative to.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const path = require('path');
|
|
3
4
|
const { ANCHOR_PREFIX, cursorAnchorGuard } = require('./source-checkpoint');
|
|
4
5
|
|
|
5
6
|
// WP-1 (design D-3/D-4): the conversation cursor is an anchor guard and nothing else.
|
|
@@ -77,13 +78,22 @@ function commitConversationBatchAndTranscriptCursor(db, options) {
|
|
|
77
78
|
OR transcript_files.status IS NOT 'ok'
|
|
78
79
|
OR transcript_files.last_error IS NOT ''
|
|
79
80
|
`);
|
|
81
|
+
// A row's `ctm_session_id` changing under this upsert used to be silent -- one committed import
|
|
82
|
+
// re-keys the file to a different owner (a re-import of the same rollout under another session)
|
|
83
|
+
// with nothing in the log to say so. Read the prior owner inside the transaction, before the write.
|
|
84
|
+
const readPriorOwner = db.prepare('SELECT ctm_session_id FROM transcript_files WHERE jsonl_path = ?');
|
|
80
85
|
return db.transaction(() => {
|
|
86
|
+
const priorOwner = readPriorOwner.get(options.filePath);
|
|
81
87
|
const committed = options.commitBatch(db, options.batch);
|
|
82
88
|
// WP-1 (S-3b): the guard is stored as-is; the manifest receipt binding is deleted with
|
|
83
89
|
// the whole-prefix proof it fenced.
|
|
84
90
|
upsert.run(options.filePath, options.agentSessionId, options.ctmSessionId, options.provider,
|
|
85
91
|
String(options.stat.dev ?? ''), String(options.stat.ino ?? ''), Number(options.stat.size || 0),
|
|
86
92
|
Number(options.stat.mtimeMs || 0), options.offset, options.parserVersion, options.guard);
|
|
93
|
+
// One line, ids only (truncated). The basename, never the full path, and never any content.
|
|
94
|
+
const prior = priorOwner ? String(priorOwner.ctm_session_id || '') : '';
|
|
95
|
+
const next = String(options.ctmSessionId || '');
|
|
96
|
+
if (priorOwner && prior !== next) console.log(`[transcript-owner] cursor commit changed owner ${path.basename(String(options.filePath || ''))}: ${prior.slice(0, 8)} -> ${next.slice(0, 8)}`);
|
|
87
97
|
return committed;
|
|
88
98
|
})();
|
|
89
99
|
}
|