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
|
@@ -22,6 +22,11 @@ function closedReceipt(stream) {
|
|
|
22
22
|
return stream.closed ? Promise.resolve() : new Promise((resolve) => stream.once('close', resolve));
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
// A hit refreshes the entry's mtime (LRU order), but a shared 4 GiB cache does not need 5 s LRU
|
|
26
|
+
// granularity: touching at most once per minute per file drops mtime-only writes by ~12x while keeping
|
|
27
|
+
// the eviction order of multi-GB entries unchanged (fsevents-churn D-10b).
|
|
28
|
+
const TOUCH_MIN_INTERVAL_MS = 60_000;
|
|
29
|
+
|
|
25
30
|
function createDecodedFrameCache({ withDecoder, cacheRoot, cacheMax, now, frameFile, pruneCache, codeError }) {
|
|
26
31
|
const entries = new Map();
|
|
27
32
|
|
|
@@ -34,7 +39,12 @@ function createDecodedFrameCache({ withDecoder, cacheRoot, cacheMax, now, frameF
|
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
async function touch(handle, opts) {
|
|
37
|
-
const
|
|
42
|
+
const st = await handle.stat();
|
|
43
|
+
const at = now(opts);
|
|
44
|
+
// The inode's mtime IS the last-touch record: entries are dropped from the map when their last
|
|
45
|
+
// lease closes, so anything in memory would reset on every acquisition cycle (model-sync, 5 s).
|
|
46
|
+
if (at - st.mtimeMs < TOUCH_MIN_INTERVAL_MS) return; // fresh enough: a touch this minute already happened
|
|
47
|
+
const when = new Date(at);
|
|
38
48
|
await handle.utimes(when, when);
|
|
39
49
|
}
|
|
40
50
|
|
|
@@ -241,4 +251,4 @@ function createDecodedFrameCache({ withDecoder, cacheRoot, cacheMax, now, frameF
|
|
|
241
251
|
return { acquireDecodedFrame };
|
|
242
252
|
}
|
|
243
253
|
|
|
244
|
-
module.exports = { createDecodedFrameCache };
|
|
254
|
+
module.exports = { createDecodedFrameCache, TOUCH_MIN_INTERVAL_MS };
|
|
@@ -24,12 +24,35 @@ const ARCHIVE_INTERVAL_MS = 60 * 60 * 1000;
|
|
|
24
24
|
const SCRUB_INTERVAL_MS = 24 * 60 * 60 * 1000;
|
|
25
25
|
const ARCHIVE_TIMEOUT_MS = 60 * 60 * 1000; // a multi-GB compress + verify outlasts the 10 minute default
|
|
26
26
|
|
|
27
|
+
// D-24/WP-6: the counters the `restoredOriginals` recovery result carries, flattened for the job result and
|
|
28
|
+
// its log line. A cycle that skipped the phase (or a guard that never reported one) counts as all zeros.
|
|
29
|
+
function _restoredCounts(restoredOriginals) {
|
|
30
|
+
const r = restoredOriginals || {};
|
|
31
|
+
const count = (v) => (Array.isArray(v) ? v.length : 0);
|
|
32
|
+
return {
|
|
33
|
+
units: Number(r.restoredUnits) || 0,
|
|
34
|
+
files: Number(r.restoredFiles) || 0,
|
|
35
|
+
linked: Number(r.linkedFromTrash) || 0,
|
|
36
|
+
decodedBytes: Number(r.decodedBytes) || 0,
|
|
37
|
+
deferredLowDisk: Number(r.deferredLowDisk) || 0,
|
|
38
|
+
targetExists: count(r.targetExists),
|
|
39
|
+
failed: count(r.failed),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
27
43
|
// Scheduler job `transcript-archive` (D-16..D-19, D-23): archive idle transcripts, purge the trash after the
|
|
28
44
|
// grace period, scrub archives once a day. `env` supplies what only the host knows:
|
|
29
45
|
// paths() -> { projectsRoot, codexSessionsDir, walleSessionsDir, trashRoot, dataDir? }
|
|
30
46
|
// liveCtmSessionIds() -> ids of CTM sessions with a live PTY (a live session owns its transcript)
|
|
31
47
|
// hostShedding() -> true while the load-pressure controller is shedding work
|
|
32
|
-
|
|
48
|
+
// now() -> (optional, test seam) ms clock; defaults to Date.now
|
|
49
|
+
// log(line) -> (optional, test seam) one-line sink; defaults to console.log
|
|
50
|
+
// WP-6 observability: a skipped cycle and an empty cycle used to look identical in the logs. Every primary run
|
|
51
|
+
// now logs exactly one `ran` line, and a run that the `shouldRun` guard held back logs a `deferred` line at most
|
|
52
|
+
// once per ARCHIVE_INTERVAL_MS -- with how long it has been waiting and why. A successful run resets the
|
|
53
|
+
// deferral state. `deferReason` (5th argument, optional) is a function returning a short reason string ('' when
|
|
54
|
+
// none); without it the reason is 'guard'.
|
|
55
|
+
function registerArchiveJob(scheduler, guard, env, shouldRun, deferReason) {
|
|
33
56
|
let first = true;
|
|
34
57
|
let claudeAfter = '';
|
|
35
58
|
let codexAfter = '';
|
|
@@ -37,6 +60,26 @@ function registerArchiveJob(scheduler, guard, env, shouldRun) {
|
|
|
37
60
|
let claudeDigestAfter = '';
|
|
38
61
|
let digestAfter = '';
|
|
39
62
|
let lastScrubAt = 0;
|
|
63
|
+
const nowMs = () => (typeof env.now === 'function' ? Number(env.now()) : Date.now());
|
|
64
|
+
const log = typeof env.log === 'function' ? env.log : console.log;
|
|
65
|
+
// Deferral state (WP-6): startedAt = when the guard first held a cycle back, checks/reasons = what it saw,
|
|
66
|
+
// loggedAt = the last `deferred` line, so a long deferral is one line per interval, not one per tick.
|
|
67
|
+
const defer = { startedAt: 0, checks: 0, reasons: new Map(), loggedAt: 0 };
|
|
68
|
+
const resetDefer = () => { defer.startedAt = 0; defer.checks = 0; defer.reasons.clear(); defer.loggedAt = 0; };
|
|
69
|
+
const guardedShouldRun = typeof shouldRun === 'function' ? (...args) => {
|
|
70
|
+
if (shouldRun(...args)) return true;
|
|
71
|
+
const now = nowMs();
|
|
72
|
+
if (!defer.startedAt) defer.startedAt = now;
|
|
73
|
+
defer.checks += 1;
|
|
74
|
+
const reason = String((typeof deferReason === 'function' ? deferReason() : '') || 'guard');
|
|
75
|
+
defer.reasons.set(reason, (defer.reasons.get(reason) || 0) + 1);
|
|
76
|
+
if (!defer.loggedAt || now - defer.loggedAt >= ARCHIVE_INTERVAL_MS) {
|
|
77
|
+
defer.loggedAt = now;
|
|
78
|
+
const reasons = Array.from(defer.reasons, ([name, count]) => `${name}:${count}`).join(',');
|
|
79
|
+
log(`[transcript-archive] deferred ${Math.floor((now - defer.startedAt) / 60000)}m checks=${defer.checks} reasons=${reasons}`);
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
} : shouldRun;
|
|
40
83
|
scheduler.register({
|
|
41
84
|
name: 'transcript-archive',
|
|
42
85
|
heavy: true,
|
|
@@ -44,10 +87,10 @@ function registerArchiveJob(scheduler, guard, env, shouldRun) {
|
|
|
44
87
|
startDelayMs: 10 * 60 * 1000,
|
|
45
88
|
priority: 9,
|
|
46
89
|
pool: 'io',
|
|
47
|
-
shouldRun,
|
|
90
|
+
shouldRun: guardedShouldRun,
|
|
48
91
|
run: async () => {
|
|
49
92
|
if (typeof guard.isPrimary !== 'function' || !guard.isPrimary()) return { skipped: { not_primary: 1 } };
|
|
50
|
-
const now =
|
|
93
|
+
const now = nowMs();
|
|
51
94
|
const scrub = now - lastScrubAt >= SCRUB_INTERVAL_MS;
|
|
52
95
|
const result = await guard.run('archive', {
|
|
53
96
|
isPrimary: true,
|
|
@@ -77,9 +120,17 @@ function registerArchiveJob(scheduler, guard, env, shouldRun) {
|
|
|
77
120
|
digestAfter = digest.nextCodexDigestAfter || '';
|
|
78
121
|
}
|
|
79
122
|
const { archived, bytesIn, bytesOut, trashed, purged, verified, corrupt, skipped } = result;
|
|
123
|
+
const restored = _restoredCounts(result.restoredOriginals);
|
|
124
|
+
const errors = (result.errors || []).length + (digest ? digest.errors.length : 0);
|
|
125
|
+
const waitedMs = defer.startedAt ? Math.max(0, nowMs() - defer.startedAt) : 0;
|
|
126
|
+
log(`[transcript-archive] ran archived=${Number(archived) || 0} restoredUnits=${restored.units} `
|
|
127
|
+
+ `restoredFiles=${restored.files} linked=${restored.linked} decodedBytes=${restored.decodedBytes} `
|
|
128
|
+
+ `deferredLowDisk=${restored.deferredLowDisk} targetExists=${restored.targetExists} `
|
|
129
|
+
+ `restoreFailed=${restored.failed} errors=${errors} waitedMs=${waitedMs}`);
|
|
130
|
+
resetDefer();
|
|
80
131
|
return {
|
|
81
132
|
archived, bytesIn, bytesOut, trashed, purged, verified, corrupt, skipped, digested: digest ? digest.digested : 0,
|
|
82
|
-
errors
|
|
133
|
+
errors, restored,
|
|
83
134
|
};
|
|
84
135
|
},
|
|
85
136
|
});
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// WP-2 (worker-transcript-link-derived-not-discovered-2026-10-25.html §5 D-4/D-7, §6, §8):
|
|
4
|
+
// the resume preflight predicts a Claude worker's transcript path from (cwd, agent_session_id)
|
|
5
|
+
// when agent_sessions.jsonl_path is empty, and must VERIFY the prediction before it links it --
|
|
6
|
+
// a predicted path is not evidence. This module is the one place that verification happens.
|
|
7
|
+
//
|
|
8
|
+
// It is the only module besides lib/transcript-source.js that names a transcript path, and it
|
|
9
|
+
// touches that path ONLY through transcript-source functions: `require('fs')` here would be a
|
|
10
|
+
// new site for bin/transcript-open-lint.js (the read has to stay on the one sanctioned reader,
|
|
11
|
+
// so archive/trash states, sibling `.zst` archives and the manifest all resolve identically).
|
|
12
|
+
|
|
13
|
+
const transcriptSource = require('./transcript-source');
|
|
14
|
+
|
|
15
|
+
// 64 KiB: enough to reach the session id in Claude Code's first record while never pulling a
|
|
16
|
+
// multi-megabyte transcript into memory. It is NOT a bound on the first LINE: a measured 34 of
|
|
17
|
+
// the 300 most recent real transcripts start with a single `queue-operation` record of 66 KB to
|
|
18
|
+
// 387 KB, so the head routinely ends mid-record. Only the `sessionId` key is read out of it
|
|
19
|
+
// (a bounded scan, never a JSON.parse of the whole record, which such a line would fail).
|
|
20
|
+
const HEAD_BYTES = 65536;
|
|
21
|
+
|
|
22
|
+
// Claude Code writes `"sessionId"` before any user content in the first record. Measured offset in
|
|
23
|
+
// real transcripts: byte 87. The value is a session uuid, so no escapes are possible.
|
|
24
|
+
const SESSION_ID_RE = /"sessionId"\s*:\s*"([^"]*)"/;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Verify that `derivedPath` really is the transcript of `agentSessionId`.
|
|
28
|
+
*
|
|
29
|
+
* States (the caller reports them verbatim in a refusal, so the lead can tell "the file is not
|
|
30
|
+
* where Claude writes it" from "the file is there but belongs to someone else"):
|
|
31
|
+
* plain -- a local file whose first JSON line names this session (ok: true)
|
|
32
|
+
* missing -- nothing at the path (or the resolver itself threw)
|
|
33
|
+
* archived -- resolved to an archived/trashed source; resume restores those by path, so a
|
|
34
|
+
* predicted path that is not plain is never linked
|
|
35
|
+
* unreadable -- the head could not be read, or its first line names no sessionId at all
|
|
36
|
+
* (empty file, non-JSON bytes, a record without the key)
|
|
37
|
+
* id-mismatch -- readable, but its sessionId is a different session
|
|
38
|
+
*
|
|
39
|
+
* @returns {Promise<{ok: boolean, state: string, headSessionId: string|null}>}
|
|
40
|
+
*/
|
|
41
|
+
async function verifyClaudeTranscriptAt(derivedPath, agentSessionId, {
|
|
42
|
+
resolveTranscriptSource = transcriptSource.resolveTranscriptSource,
|
|
43
|
+
readTranscriptHead = transcriptSource.readTranscriptHead,
|
|
44
|
+
} = {}) {
|
|
45
|
+
let source = null;
|
|
46
|
+
try {
|
|
47
|
+
source = await resolveTranscriptSource(derivedPath);
|
|
48
|
+
} catch {
|
|
49
|
+
// expected: an unreadable/absent path is exactly the "no transcript there" answer
|
|
50
|
+
return { ok: false, state: 'missing', headSessionId: null };
|
|
51
|
+
}
|
|
52
|
+
if (!source || source.state === 'missing') return { ok: false, state: 'missing', headSessionId: null };
|
|
53
|
+
// Anything not plain (archived, trashed, ...) is reported as archived: the resolve path below
|
|
54
|
+
// restores an archived transcript at the LINKED path, and a derived path has no link yet.
|
|
55
|
+
if (source.state !== 'plain') return { ok: false, state: 'archived', headSessionId: null };
|
|
56
|
+
|
|
57
|
+
let head = '';
|
|
58
|
+
try {
|
|
59
|
+
head = String(await readTranscriptHead(derivedPath, HEAD_BYTES));
|
|
60
|
+
} catch {
|
|
61
|
+
// expected: an unreadable/absent file -- unverifiable, never a link
|
|
62
|
+
return { ok: false, state: 'unreadable', headSessionId: null };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Scan the first line only (or the whole head when it is one unterminated line, i.e. the record
|
|
66
|
+
// is longer than HEAD_BYTES). Never parse the record: a first line over 64 KiB is normal and
|
|
67
|
+
// would throw. The key precedes user content, so the first match is the record's own session id.
|
|
68
|
+
const firstLine = head.split('\n')[0];
|
|
69
|
+
const match = SESSION_ID_RE.exec(firstLine);
|
|
70
|
+
if (!match) return { ok: false, state: 'unreadable', headSessionId: null };
|
|
71
|
+
|
|
72
|
+
const headSessionId = match[1];
|
|
73
|
+
if (headSessionId !== String(agentSessionId || '')) return { ok: false, state: 'id-mismatch', headSessionId };
|
|
74
|
+
return { ok: true, state: 'plain', headSessionId };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { verifyClaudeTranscriptAt };
|
|
@@ -42,14 +42,18 @@ function _row(input) {
|
|
|
42
42
|
const ALL_STATES = Object.freeze([...ARCHIVE_STATES, ...LEGACY_STATES]);
|
|
43
43
|
|
|
44
44
|
// WP-9. Rows in any of `states`, optionally changed before `olderThanMs` (state_changed_at_ms) or with a source
|
|
45
|
-
// mtime before `mtimeBeforeMs`. orderBy: 'mtime' (oldest source first) | '
|
|
46
|
-
// first) | 'changed' (oldest state change first, the
|
|
45
|
+
// mtime before `mtimeBeforeMs`. orderBy: 'mtime' (oldest source first) | 'mtime_desc' (newest source first, the
|
|
46
|
+
// WP-2 recovery order) | 'verified' (least recently verified first) | 'changed' (oldest state change first, the
|
|
47
|
+
// default). Bounded to 2000 rows.
|
|
47
48
|
function listByState(db, { states = [], olderThanMs = 0, mtimeBeforeMs = 0, orderBy = 'changed', limit = 500 } = {}) {
|
|
48
49
|
const wanted = (states || []).map(String).filter((s) => ALL_STATES.includes(s));
|
|
49
50
|
if (!wanted.length) return [];
|
|
50
51
|
const bounded = Math.max(1, Math.min(2000, Number(limit) || 500));
|
|
51
|
-
const order = {
|
|
52
|
-
|
|
52
|
+
const order = {
|
|
53
|
+
mtime: 'source_mtime_ms, source_path',
|
|
54
|
+
mtime_desc: 'source_mtime_ms DESC, source_path',
|
|
55
|
+
verified: 'last_verified_at_ms, source_path',
|
|
56
|
+
}[orderBy] || 'state_changed_at_ms, source_path';
|
|
53
57
|
const marks = wanted.map(() => '?').join(',');
|
|
54
58
|
const where = [`state IN (${marks})`];
|
|
55
59
|
const params = [...wanted];
|
|
@@ -22,7 +22,7 @@ const { finished } = require('node:stream/promises');
|
|
|
22
22
|
const zlib = require('node:zlib');
|
|
23
23
|
const { decodeFrame, waitForDecodedFrameClose, decompressParams } = require('./transcript-archive-codec');
|
|
24
24
|
const { getDataDir } = require('./data-dir');
|
|
25
|
-
const { createDecodedFrameCache } = require('./transcript-frame-cache');
|
|
25
|
+
const { createDecodedFrameCache, TOUCH_MIN_INTERVAL_MS } = require('./transcript-frame-cache');
|
|
26
26
|
|
|
27
27
|
const CACHE_DIR_NAME = 'transcript-archive-cache';
|
|
28
28
|
const DEFAULT_CACHE_MAX_BYTES = 4 * 1024 * 1024 * 1024;
|
|
@@ -1109,8 +1109,13 @@ function _decodeFrameSync(archive, index, opts, learnLength) {
|
|
|
1109
1109
|
// The cached bytes are this request's decoded bytes: charge them before returning, so a warm
|
|
1110
1110
|
// cache hit is accounted for exactly like a cold decode would be.
|
|
1111
1111
|
if (ledger && learnLength) _chargeLearnedDecoded(ledger, `${archive.cacheKey}:${index}`, hit.size);
|
|
1112
|
-
|
|
1113
|
-
|
|
1112
|
+
// Same rule as the async cache's touch (D-10b): the hit's own mtime is the last-touch record, so a
|
|
1113
|
+
// file touched in the last minute is left alone instead of paying a utimes write per read.
|
|
1114
|
+
const at = _now(opts);
|
|
1115
|
+
if (at - hit.mtimeMs >= TOUCH_MIN_INTERVAL_MS) {
|
|
1116
|
+
const when = new Date(at);
|
|
1117
|
+
fs.utimesSync(file, when, when);
|
|
1118
|
+
}
|
|
1114
1119
|
return file;
|
|
1115
1120
|
}
|
|
1116
1121
|
const max = _cacheMax(opts);
|
|
@@ -59,6 +59,11 @@ function setErrorSink(sink) {
|
|
|
59
59
|
}
|
|
60
60
|
const { shouldYieldAfterChunk } = require('./conversation-import/chunk-runner');
|
|
61
61
|
const { createImportMetrics } = require('./conversation-import/import-metrics');
|
|
62
|
+
// WP-2 (D-4): the ingest lane already parses every record, so the agent's own session name
|
|
63
|
+
// (`ai-title` / `custom-title`) is captured here and applied at the ingestJsonlFile choke point.
|
|
64
|
+
const { extractTitleSignalFromRecord } = require('./session-title-signals');
|
|
65
|
+
const { getAgentCapabilities } = require('./agent-capabilities');
|
|
66
|
+
const { applyAgentTitleSignals } = require('./agent-title-apply');
|
|
62
67
|
|
|
63
68
|
const PARSER_VERSION = 3;
|
|
64
69
|
const DEFAULT_INITIAL_TAIL_BYTES = 2 * 1024 * 1024;
|
|
@@ -1008,6 +1013,16 @@ function claudeMessagesFromEntry(entry, state = {}) {
|
|
|
1008
1013
|
if (queued) entry = queued;
|
|
1009
1014
|
if (shouldDropInheritedClaudeEntry(entry, state)) return [];
|
|
1010
1015
|
|
|
1016
|
+
// WP-2 (D-4): a `custom-title`/`ai-title` record produces no rows, but CTM is already reading
|
|
1017
|
+
// every record here — keep the last non-inherited one of each kind so ingestJsonlFile can apply
|
|
1018
|
+
// it (lib/agent-title-apply.js). Last wins; an ai-title never clears a custom-title.
|
|
1019
|
+
const titleSignal = extractTitleSignalFromRecord(entry);
|
|
1020
|
+
if (titleSignal.userTitle || titleSignal.providerTitle) {
|
|
1021
|
+
if (!state.agentTitleSignals) state.agentTitleSignals = {};
|
|
1022
|
+
if (titleSignal.userTitle) state.agentTitleSignals.customTitle = titleSignal.userTitle;
|
|
1023
|
+
else state.agentTitleSignals.aiTitle = titleSignal.providerTitle;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1011
1026
|
if (entry.type === 'system' && entry.subtype === 'compact_boundary'
|
|
1012
1027
|
&& structuredCapture.structuredCaptureEnabled()) {
|
|
1013
1028
|
const compactMeta = entry.compactMetadata || {};
|
|
@@ -1458,6 +1473,8 @@ function healSelfLinkedTranscriptRows(db, agentSessionId, ownerCtmSessionId) {
|
|
|
1458
1473
|
try {
|
|
1459
1474
|
const f = db.prepare('UPDATE transcript_files SET ctm_session_id=? WHERE agent_session_id=? AND ctm_session_id=?').run(owner, agentId, agentId);
|
|
1460
1475
|
const e = db.prepare('UPDATE transcript_events SET ctm_session_id=? WHERE agent_session_id=? AND ctm_session_id=?').run(owner, agentId, agentId);
|
|
1476
|
+
// Rows whose owner changed used to change silently. Ids only, truncated; never a path, never content.
|
|
1477
|
+
if (f.changes || e.changes) console.log(`[transcript-owner] re-keyed self-link agent=${agentId.slice(0, 8)} -> owner=${owner.slice(0, 8)} files=${f.changes || 0} events=${e.changes || 0}`);
|
|
1461
1478
|
return { files: f.changes || 0, events: e.changes || 0 };
|
|
1462
1479
|
} catch { return { files: 0, events: 0 }; }
|
|
1463
1480
|
}
|
|
@@ -1688,6 +1705,10 @@ function parseTranscriptTail(options = {}) {
|
|
|
1688
1705
|
return {
|
|
1689
1706
|
...base,
|
|
1690
1707
|
messages: projected.messages,
|
|
1708
|
+
// WP-2 (D-4): the agent-name signal rides with the parsed window so both the inline ingest
|
|
1709
|
+
// and the read-pool → preParsed apply path (workers/read-pool-worker.js parseTranscriptDelta)
|
|
1710
|
+
// carry it without a second read.
|
|
1711
|
+
agentTitleSignals: state.agentTitleSignals || null,
|
|
1691
1712
|
metrics,
|
|
1692
1713
|
read: { nextOffset: read.nextOffset, partialLine: read.partialLine || '', bytesRead: read.bytesRead },
|
|
1693
1714
|
};
|
|
@@ -2034,6 +2055,8 @@ function _ingestJsonlFileV2(db, options = {}) {
|
|
|
2034
2055
|
agentSessionId,
|
|
2035
2056
|
generation: 1,
|
|
2036
2057
|
fileSize: stat.size,
|
|
2058
|
+
// WP-2 (D-4): the parsed window's agent-name signal (title records produce no rows).
|
|
2059
|
+
agentTitleSignals: (preParsed || parsed).agentTitleSignals || null,
|
|
2037
2060
|
oversizedNonRetainedRecordsSkipped,
|
|
2038
2061
|
metrics: {
|
|
2039
2062
|
...(parsed.metrics || {}),
|
|
@@ -2466,6 +2489,8 @@ function _ingestJsonlFile(db, options = {}) {
|
|
|
2466
2489
|
|
|
2467
2490
|
let read;
|
|
2468
2491
|
let messages;
|
|
2492
|
+
// WP-2 (D-4): the agent-name signal (title records produce no rows) from whichever parse ran.
|
|
2493
|
+
let agentTitleSignals = null;
|
|
2469
2494
|
if (preParsed) {
|
|
2470
2495
|
// Off-thread parse already produced the delta (parseTranscriptTail) — apply is writes only.
|
|
2471
2496
|
read = {
|
|
@@ -2474,6 +2499,7 @@ function _ingestJsonlFile(db, options = {}) {
|
|
|
2474
2499
|
bytesRead: Number(preParsed.read?.bytesRead || 0),
|
|
2475
2500
|
};
|
|
2476
2501
|
messages = Array.isArray(preParsed.messages) ? preParsed.messages : [];
|
|
2502
|
+
agentTitleSignals = preParsed.agentTitleSignals || null;
|
|
2477
2503
|
} else {
|
|
2478
2504
|
const maxBytes = Math.min(
|
|
2479
2505
|
Math.max(0, readCeiling - cursor.offset),
|
|
@@ -2501,6 +2527,7 @@ function _ingestJsonlFile(db, options = {}) {
|
|
|
2501
2527
|
});
|
|
2502
2528
|
metrics.parseMs += Math.max(0, Date.now() - parseStartedAt);
|
|
2503
2529
|
messages = projected.messages;
|
|
2530
|
+
agentTitleSignals = state.agentTitleSignals || null;
|
|
2504
2531
|
metrics.recordsParsed += projected.recordsParsed;
|
|
2505
2532
|
}
|
|
2506
2533
|
metrics.sourceBytesRead += Number(read.bytesRead || 0);
|
|
@@ -3266,6 +3293,7 @@ function _ingestJsonlFile(db, options = {}) {
|
|
|
3266
3293
|
values: insertChunkStatementCounts,
|
|
3267
3294
|
},
|
|
3268
3295
|
overBudget: elapsedMs > IMPORT_SLICE_HARD_MAX_MS,
|
|
3296
|
+
agentTitleSignals,
|
|
3269
3297
|
messages: partial ? messages.slice(0, processedCount) : messages,
|
|
3270
3298
|
rowCursorDelta: {
|
|
3271
3299
|
ctmSessionId,
|
|
@@ -3367,6 +3395,18 @@ function ingestJsonlFile(db, options = {}) {
|
|
|
3367
3395
|
result,
|
|
3368
3396
|
coreDb: options.coreDb,
|
|
3369
3397
|
});
|
|
3398
|
+
// WP-2 (D-4/D-5/D-9, S-17): the single choke point for every ingest (event delta, targeted
|
|
3399
|
+
// import, v13 migration) — apply the agent's own name to its CTM row. Gated on the provider
|
|
3400
|
+
// declaring it reads ai-titles; a refused delta applied nothing, so it has no signal to apply.
|
|
3401
|
+
// D-11: the caller publishes the outcome (titleApply) on the main thread.
|
|
3402
|
+
if (result && !result.refused && result.agentTitleSignals && ownerCtmSessionId
|
|
3403
|
+
&& getAgentCapabilities(normalizeProvider(options.provider, filePath)).titleSpec?.readsAiTitle) {
|
|
3404
|
+
result.titleApply = applyAgentTitleSignals(options.coreDb || db, {
|
|
3405
|
+
ctmSessionId: ownerCtmSessionId,
|
|
3406
|
+
provider: normalizeProvider(options.provider, filePath),
|
|
3407
|
+
...result.agentTitleSignals,
|
|
3408
|
+
});
|
|
3409
|
+
}
|
|
3370
3410
|
return result;
|
|
3371
3411
|
}
|
|
3372
3412
|
function dryRunTranscriptByteBackfill(db, options = {}) {
|
|
@@ -65,10 +65,13 @@ function _socketBytesWritten(socket) {
|
|
|
65
65
|
// The traffic class of a message, from the channel the caller already resolved (`opts.channel`
|
|
66
66
|
// || the decoded `type`) plus the class `_wsSend` may have resolved explicitly (`opts.class` —
|
|
67
67
|
// the live-status heartbeat sends `session-activity` as `{ class: 'stream', channel: 'heartbeat' }`).
|
|
68
|
-
//
|
|
68
|
+
// T2 (docs/walle-turn-lifecycle-delivery-2026-10-26.html §3): the classification is delegated with
|
|
69
|
+
// the ACTUAL payload plus the caller's class and channel — a synthetic `{ type: channel }` object
|
|
70
|
+
// threw away the lifecycle fields (recordType/partType) that make a walle-transcript-append opener
|
|
71
|
+
// a control notification rather than a replaceable sample, which in economy let progress replace a
|
|
72
|
+
// queued opener. `classifyWsPayload` stays the one authority for the class split.
|
|
69
73
|
function _channelClass(channel, payload, cls) {
|
|
70
|
-
|
|
71
|
-
return classifyWsPayload(ch ? { type: ch } : payload, cls);
|
|
74
|
+
return classifyWsPayload(payload, cls, channel);
|
|
72
75
|
}
|
|
73
76
|
|
|
74
77
|
function createTunnelLaneGovernor({ now = Date.now, setIntervalFn = setInterval, clearIntervalFn = clearInterval, send } = {}) {
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
// WP-3 (composer-freeze-followups-load-sigstop-attribution-2026-10-20.html §5 D-C1/D-C2):
|
|
4
|
+
// READ THE STAGE NAMES HONESTLY. Every stage after `pty-written` EXCEPT `echo-bytes`/`echo-cell`
|
|
5
|
+
// is stamped on the session's whole pending id set by the FIRST output/feed/frame after the key
|
|
6
|
+
// -- "first X after the key", which is approximate, not causal (D-C1; the pty-output leg lands
|
|
7
|
+
// 0-3 ms after pty-written in 14 of 18 retained slow rows, which no agent CLI can do).
|
|
8
|
+
// `echo-bytes`/`echo-cell` are the only per-key matches today, and they are shadow-only
|
|
9
|
+
// instrumentation for WP-4/WP-5's decision (D-C2) -- nothing reads them as truth yet.
|
|
3
10
|
const STAGES = Object.freeze([
|
|
4
11
|
'browser-keydown',
|
|
5
12
|
'server-received',
|
|
@@ -10,7 +17,18 @@ const STAGES = Object.freeze([
|
|
|
10
17
|
// echoed). Required (not in OPTIONAL_STAGES below), same as 'pty-written'.
|
|
11
18
|
'host-posted',
|
|
12
19
|
'pty-written',
|
|
20
|
+
// WP-4 (clock-free-tests-followups-r4-2026-10-24.html §5 R-9, §6.5): the host's OWN receipt
|
|
21
|
+
// of the PTY batch -- the instant its first byte entered the mirror-feed coalescer, carried on
|
|
22
|
+
// the 'feed' op as `hostReceivedAt`. `pty-output` below is the MAIN-thread arrival, which is a
|
|
23
|
+
// different clock hop; neither is causal for the other (every post-`pty-written` stage is
|
|
24
|
+
// "first X after the key"). Optional, like its neighbours.
|
|
25
|
+
'host-pty-data',
|
|
13
26
|
'pty-output',
|
|
27
|
+
// WP-3 D-C2: the two shadow, per-key echo matchers -- VS Code typeahead's byte rule and
|
|
28
|
+
// mosh's cell rule (see lib/typing-worker-stages.js, which runs them in the mirror). Both are
|
|
29
|
+
// OPTIONAL (OPTIONAL_STAGES below), so neither can move lastCompletedBoundary.
|
|
30
|
+
'echo-bytes',
|
|
31
|
+
'echo-cell',
|
|
14
32
|
// WP-0 (composer-freeze-stop-and-wait-emitter-2026-09-16.html §5 D-1, §6 API specs, §8 WP-0):
|
|
15
33
|
// four stages stamped INSIDE the worker/pool-host, between `pty-output` and `mirror-revision`
|
|
16
34
|
// -- the 16.1 s gap the census found with zero instrumentation coverage (§1.5). All four are
|
|
@@ -22,6 +40,19 @@ const STAGES = Object.freeze([
|
|
|
22
40
|
'mirror-written',
|
|
23
41
|
'emit-offered',
|
|
24
42
|
'emit-refused',
|
|
43
|
+
// WP-4 (clock-free-tests-followups-r3-2026-10-24.html §5 R-7, §6 WP-4): stamped by the worker
|
|
44
|
+
// (lib/typing-worker-stages.js's noteFramePostedForEchoed, called from emitSettled) for exactly
|
|
45
|
+
// the pending ids the frame that just SHIPPED already covers (their own inputSeq <= that
|
|
46
|
+
// frame's echoedThroughSeq, carried as `offeredEchoSeq`). Optional, like its neighbours above:
|
|
47
|
+
// a key whose frame is refused, or that is delivered without ever being pending, never gets it.
|
|
48
|
+
'frame-posted',
|
|
49
|
+
// WP-4 (clock-free-tests-followups-r4-2026-10-24.html §5 R-7/R-8, §6.5): the FIRST main-thread
|
|
50
|
+
// touch of the frame that carries this key's echo (`session-host-manager.js`'s
|
|
51
|
+
// `_stampMainArrival`), split out from `mirror-revision` (where main stamped the interaction)
|
|
52
|
+
// to price the receiver-loop leg alone. Carries `mainSpans` (R-8) -- the top recent main-thread
|
|
53
|
+
// spans overlapping [frame-posted.at, frame-arrived.at]. Optional, same reason as
|
|
54
|
+
// `frame-posted`: a key whose frame was refused, or that never had a pending row, never gets it.
|
|
55
|
+
'frame-arrived',
|
|
25
56
|
'mirror-revision',
|
|
26
57
|
'frame-sent',
|
|
27
58
|
'socket-received',
|
|
@@ -52,7 +83,19 @@ const STAGE_INDEX = new Map(STAGES.map((stage, index) => [stage, index]));
|
|
|
52
83
|
// D-1: stages that may be absent from an otherwise-complete row without that absence meaning
|
|
53
84
|
// "the interaction stalled here" -- the sequential boundary walk in note() skips these rather
|
|
54
85
|
// than treating a miss as the end of the row's progress (see the walk below).
|
|
55
|
-
|
|
86
|
+
// WP-3 D-C2: 'echo-bytes'/'echo-cell' join them -- they are shadow stamps most keys never get.
|
|
87
|
+
const OPTIONAL_STAGES = new Set([
|
|
88
|
+
'pty-output', 'worker-received', 'mirror-written', 'emit-offered', 'emit-refused',
|
|
89
|
+
// WP-4 (R-7): 'frame-posted' joins them -- its absence must never move
|
|
90
|
+
// lastCompletedBoundary/missingBoundary for a row that shipped its frame before anything was
|
|
91
|
+
// pending (the common case).
|
|
92
|
+
'frame-posted',
|
|
93
|
+
// WP-4 r4 (R-7/R-8/R-9): 'frame-arrived' and 'host-pty-data' join for the same reason -- both
|
|
94
|
+
// are first-touch stamps on paths most interactions never take (a refused frame; a host whose
|
|
95
|
+
// coalescer delivered before any keystroke was pending), so neither may end the boundary walk.
|
|
96
|
+
'frame-arrived', 'host-pty-data',
|
|
97
|
+
'echo-bytes', 'echo-cell',
|
|
98
|
+
]);
|
|
56
99
|
const WORKLOAD_FIELDS = Object.freeze([
|
|
57
100
|
'rawBatchClass', 'synchronizedFrameCount', 'mirrorChangedRows', 'frameKind',
|
|
58
101
|
'frameBytes', 'queuedBytes', 'queueDepth', 'codexRepairActions',
|
|
@@ -69,6 +112,27 @@ function boundedText(value, max = 80) {
|
|
|
69
112
|
return String(value || '').replace(/[^A-Za-z0-9_.:-]/g, '').slice(0, max);
|
|
70
113
|
}
|
|
71
114
|
|
|
115
|
+
// WP-4 r4 (R-8, §6.5): R-8's floor -- mainSpans are computed only for a frame-posted ->
|
|
116
|
+
// frame-arrived window at or above this, so the fast path allocates nothing.
|
|
117
|
+
const MAIN_SPANS_MIN_WINDOW_MS = 15;
|
|
118
|
+
|
|
119
|
+
// The `frame-arrived` record's mainSpans shape: a malformed entry is DROPPED rather than repaired
|
|
120
|
+
// into a plausible-looking 0 -- a reader may only ever see entries the sender actually produced.
|
|
121
|
+
// Name is bounded to 120 chars and overlapMs floored to a non-negative integer, and the record is
|
|
122
|
+
// the TOP 3 by overlapMs whatever order the sender used: the ranking is part of this record's
|
|
123
|
+
// contract (§6.5), not of the sender's good manners.
|
|
124
|
+
function sanitizeMainSpans(rawSpans) {
|
|
125
|
+
const mainSpans = [];
|
|
126
|
+
for (const entry of Array.isArray(rawSpans) ? rawSpans : []) {
|
|
127
|
+
if (!entry || typeof entry !== 'object') continue;
|
|
128
|
+
const name = String(entry.name == null ? '' : entry.name).slice(0, 120);
|
|
129
|
+
if (!name) continue;
|
|
130
|
+
mainSpans.push({ name, overlapMs: Math.max(0, Math.floor(finite(entry.overlapMs))) });
|
|
131
|
+
}
|
|
132
|
+
mainSpans.sort((a, b) => b.overlapMs - a.overlapMs);
|
|
133
|
+
return mainSpans.slice(0, 3);
|
|
134
|
+
}
|
|
135
|
+
|
|
72
136
|
function percentile(values, pct) {
|
|
73
137
|
if (!values.length) return 0;
|
|
74
138
|
const sorted = values.slice().sort((a, b) => a - b);
|
|
@@ -99,6 +163,23 @@ function stageMsFromKeydown(row, stage) {
|
|
|
99
163
|
return stageDelta(row, stage, 'browser-keydown');
|
|
100
164
|
}
|
|
101
165
|
|
|
166
|
+
// WP-3 D-C3: an expired row must not report an OPTIONAL stage as "where it got to" -- the
|
|
167
|
+
// reverse scan it used to do picked `pty-output` over `pty-written` for a key that stalled
|
|
168
|
+
// after a stray output chunk (I-6 / R9). `lastObservedStage` is therefore the last observed
|
|
169
|
+
// REQUIRED stage (fallback `browser-keydown`), and the last observed OPTIONAL stage is reported
|
|
170
|
+
// separately as `lastOptionalStage` ('' when there is none) so the shadow echo stamps stay
|
|
171
|
+
// readable without ever being mistaken for progress.
|
|
172
|
+
function observedStageSplit(row) {
|
|
173
|
+
let lastObservedStage = 'browser-keydown';
|
|
174
|
+
let lastOptionalStage = '';
|
|
175
|
+
for (const stage of STAGES) {
|
|
176
|
+
if (!row.stages[stage]) continue;
|
|
177
|
+
if (OPTIONAL_STAGES.has(stage)) lastOptionalStage = stage;
|
|
178
|
+
else lastObservedStage = stage;
|
|
179
|
+
}
|
|
180
|
+
return { lastObservedStage, lastOptionalStage };
|
|
181
|
+
}
|
|
182
|
+
|
|
102
183
|
// D-7/S-14: builds the flat, ≤24-key `typing-interaction-slow` payload from the SAME stage
|
|
103
184
|
// vector `note()`/`begin()` already maintain -- no second clock, no parallel bookkeeping.
|
|
104
185
|
// `lastStage` is passed explicitly by the two callers below rather than read from
|
|
@@ -118,6 +199,10 @@ function buildSlowInteractionRow(row, ktpMs, lastStage) {
|
|
|
118
199
|
printable: row.printable === true,
|
|
119
200
|
ktpMs: Math.max(0, finite(ktpMs, 0)),
|
|
120
201
|
lastStage: lastStage || row.lastCompletedBoundary || 'browser-keydown',
|
|
202
|
+
// WP-3 D-C3: the last OPTIONAL stage observed for this row ('' when none) -- carried on
|
|
203
|
+
// every slow/worst vector so `lastStage` can stay required-only without losing the shadow
|
|
204
|
+
// echo stamps.
|
|
205
|
+
lastOptionalStage: observedStageSplit(row).lastOptionalStage,
|
|
121
206
|
dropReason: dropped ? boundedText(dropped.reason, 64) : '',
|
|
122
207
|
gate: refused ? refused.gate : '',
|
|
123
208
|
gateTop: refused ? refused.gate : '',
|
|
@@ -131,7 +216,12 @@ function buildSlowInteractionRow(row, ktpMs, lastStage) {
|
|
|
131
216
|
serverReceivedMs: stageMsFromKeydown(row, 'server-received'),
|
|
132
217
|
ptyWrittenMs: stageMsFromKeydown(row, 'pty-written'),
|
|
133
218
|
ptyOutputMs: stageMsFromKeydown(row, 'pty-output'),
|
|
219
|
+
// WP-3 D-C2: the shadow per-key matchers' legs, same keydown rule as ptyOutputMs above.
|
|
220
|
+
echoBytesMs: stageMsFromKeydown(row, 'echo-bytes'),
|
|
221
|
+
echoCellMs: stageMsFromKeydown(row, 'echo-cell'),
|
|
134
222
|
mirrorRevisionMs: stageMsFromKeydown(row, 'mirror-revision'),
|
|
223
|
+
// WP-4 (R-7): the shipped-frame leg, same keydown rule as mirrorRevisionMs above.
|
|
224
|
+
framePostedMs: stageMsFromKeydown(row, 'frame-posted'),
|
|
135
225
|
frameSentMs: stageMsFromKeydown(row, 'frame-sent'),
|
|
136
226
|
socketReceivedMs: stageMsFromKeydown(row, 'socket-received'),
|
|
137
227
|
applyStartedMs: stageMsFromKeydown(row, 'apply-started'),
|
|
@@ -181,6 +271,9 @@ function createTypingInteractionTraceStore(options = {}) {
|
|
|
181
271
|
// emitting workerLoopMaxMs: 0 / host: {0,0,0} exactly as before this WP.
|
|
182
272
|
const getRollupExtras = typeof options.getRollupExtras === 'function' ? options.getRollupExtras : null;
|
|
183
273
|
const rollupIntervalMs = Math.max(1000, finite(options.rollupIntervalMs, 60_000));
|
|
274
|
+
// WP-4 r4 (R-8): `(startMs, endMs) => [{ name, overlapMs }]`, a pure read of the caller's
|
|
275
|
+
// main-thread span ring (server.js `_mainSpansForWindow`). Absent -> no spans are computed.
|
|
276
|
+
const mainSpansForWindow = typeof options.mainSpansForWindow === 'function' ? options.mainSpansForWindow : null;
|
|
184
277
|
const rows = new Map();
|
|
185
278
|
const publicRows = new Map();
|
|
186
279
|
const pendingClosed = new Map();
|
|
@@ -280,6 +373,8 @@ function createTypingInteractionTraceStore(options = {}) {
|
|
|
280
373
|
workerReceivedMs: stageDelta(row, 'worker-received', 'browser-keydown'),
|
|
281
374
|
mirrorWrittenMs: stageDelta(row, 'mirror-written', 'browser-keydown'),
|
|
282
375
|
emitOfferedMs: stageDelta(row, 'emit-offered', 'browser-keydown'),
|
|
376
|
+
// WP-4 (R-7): the shipped-frame leg for the closed interaction, read the same way.
|
|
377
|
+
framePostedMs: stageMsFromKeydown(row, 'frame-posted'),
|
|
283
378
|
gate,
|
|
284
379
|
gateHoldMs,
|
|
285
380
|
clientId: row.clientId,
|
|
@@ -302,12 +397,14 @@ function createTypingInteractionTraceStore(options = {}) {
|
|
|
302
397
|
|
|
303
398
|
function captureExpiredInteraction(row, at) {
|
|
304
399
|
if (row.stages.painted) return;
|
|
305
|
-
|
|
400
|
+
// WP-3 D-C3: required-only `lastObservedStage` + a separate `lastOptionalStage` (I-6/R9).
|
|
401
|
+
const { lastObservedStage, lastOptionalStage } = observedStageSplit(row);
|
|
306
402
|
const ageMs = Math.max(0, at - row.startedAt);
|
|
307
403
|
pushBounded(pendingExpired, rollupGroupKey(row.sessionId, row.agentType), {
|
|
308
404
|
ageMs,
|
|
309
405
|
clientId: row.clientId,
|
|
310
406
|
lastObservedStage,
|
|
407
|
+
lastOptionalStage,
|
|
311
408
|
});
|
|
312
409
|
windowExpiredHistory.push({ at });
|
|
313
410
|
if (windowExpiredHistory.length > WINDOW_HISTORY_CAP) windowExpiredHistory.shift();
|
|
@@ -411,10 +508,43 @@ function createTypingInteractionTraceStore(options = {}) {
|
|
|
411
508
|
rejectedSurfaceEpoch: Math.max(0, finite(event.rejectedSurfaceEpoch)),
|
|
412
509
|
sweepSkipReason: boundedText(event.sweepSkipReason, 24),
|
|
413
510
|
};
|
|
511
|
+
} else if (stage === 'emit-offered' || stage === 'frame-posted') {
|
|
512
|
+
// WP-4 (R-7): these two stages carry the OFFERED/SHIPPED frame's echoedThroughSeq, so a
|
|
513
|
+
// reader can compare it against this row's own `inputSeq` (set once by begin(), above) --
|
|
514
|
+
// that comparison is what separates "the offered frame did not yet contain this key's
|
|
515
|
+
// echo" from "worker->main delivery / main-thread queueing". A sender that carries no seq
|
|
516
|
+
// records `{ at }` alone: an absent seq must never read as a real 0.
|
|
517
|
+
// First-wins like every other stage: the `!previous || at < previous.at` guard above owns
|
|
518
|
+
// idempotency, so a later note never rewrites this record.
|
|
519
|
+
const offeredEchoSeq = event.offeredEchoSeq;
|
|
520
|
+
row.stages[stage] = offeredEchoSeq == null
|
|
521
|
+
? { at }
|
|
522
|
+
: { at, offeredEchoSeq: Math.max(0, Math.floor(finite(offeredEchoSeq))) };
|
|
523
|
+
} else if (stage === 'frame-arrived') {
|
|
524
|
+
// WP-4 r4 (R-8, §6.5): the arrival stamp may carry `mainSpans` -- the top ≤3 main-thread
|
|
525
|
+
// spans overlapping [frame-posted.at, frame-arrived.at], each `{ name, overlapMs }`.
|
|
526
|
+
// Same shape rule as `offeredEchoSeq` above: no array offered means `{ at }` alone (the
|
|
527
|
+
// store may still attach computed spans below, once both stamps are present).
|
|
528
|
+
row.stages[stage] = Array.isArray(event.mainSpans)
|
|
529
|
+
? { at, mainSpans: sanitizeMainSpans(event.mainSpans) }
|
|
530
|
+
: { at };
|
|
414
531
|
} else {
|
|
415
532
|
row.stages[stage] = { at };
|
|
416
533
|
}
|
|
417
534
|
}
|
|
535
|
+
// R-8, order-independent: the worker posts `frame-posted` as its own 'typing-stage' message
|
|
536
|
+
// AFTER the frame shipped (headless-term-service.js emitSettled -> noteFramePostedForEchoed),
|
|
537
|
+
// so main usually stamps `frame-arrived` BEFORE it knows `frame-posted`. Compute the spans when
|
|
538
|
+
// the SECOND of the two lands, once per row; the window is in the past either way, and the
|
|
539
|
+
// span ring (lib/perf-tracker.js, 3 s+) still holds it.
|
|
540
|
+
if (mainSpansForWindow && (stage === 'frame-posted' || stage === 'frame-arrived')) {
|
|
541
|
+
const arrival = row.stages['frame-arrived'];
|
|
542
|
+
const postedAt = row.stages['frame-posted']?.at;
|
|
543
|
+
if (arrival && !arrival.mainSpans && Number.isFinite(postedAt)
|
|
544
|
+
&& arrival.at - postedAt >= MAIN_SPANS_MIN_WINDOW_MS) {
|
|
545
|
+
arrival.mainSpans = sanitizeMainSpans(mainSpansForWindow(postedAt, arrival.at));
|
|
546
|
+
}
|
|
547
|
+
}
|
|
418
548
|
if (stage === 'painted' && !previous) captureClosedInteraction(row);
|
|
419
549
|
row.updatedAt = Math.max(row.updatedAt, at);
|
|
420
550
|
row.lastCompletedBoundary = '';
|
|
@@ -740,6 +870,9 @@ function noteWorkerStageMessage(store, msg = {}) {
|
|
|
740
870
|
sessionId: msg.sessionId,
|
|
741
871
|
stage: msg.stage,
|
|
742
872
|
at: msg.at,
|
|
873
|
+
// WP-4 (R-7): the offered/shipped frame's echoedThroughSeq rides the same worker message as
|
|
874
|
+
// the stage stamp, so emit-offered/frame-posted rows can be read against their inputSeq.
|
|
875
|
+
offeredEchoSeq: msg.offeredEchoSeq,
|
|
743
876
|
gate: msg.gate,
|
|
744
877
|
inFlightSeq: msg.inFlightSeq,
|
|
745
878
|
inFlightKind: msg.inFlightKind,
|
|
@@ -769,4 +902,5 @@ function pendingInteractionIdsFor(byInputSeqMap, limit = 24) {
|
|
|
769
902
|
|
|
770
903
|
module.exports = {
|
|
771
904
|
createTypingInteractionTraceStore, STAGES, GATES, noteWorkerStageMessage, pendingInteractionIdsFor,
|
|
905
|
+
MAIN_SPANS_MIN_WINDOW_MS,
|
|
772
906
|
};
|