create-walle 0.10.2 → 0.10.3
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 +3 -1
- package/lib/release-schema.json +2 -2
- package/lib/runtime-closure-policy.json +8 -7
- package/package.json +1 -1
- package/template/coding-task-manager/api-prompts.js +45 -7
- package/template/coding-task-manager/bin/claude-hook.js +55 -1
- package/template/coding-task-manager/bin/ctm-relay-client.js +35 -12
- package/template/coding-task-manager/bin/land.js +2069 -0
- package/template/coding-task-manager/bin/memory-census.js +4 -2
- package/template/coding-task-manager/bin/memory-sync-verify.js +151 -0
- package/template/coding-task-manager/bin/probe-refusal-scope.js +530 -0
- package/template/coding-task-manager/db.js +156 -32
- package/template/coding-task-manager/lib/acp/acp-client.js +383 -0
- package/template/coding-task-manager/lib/acp/dsh-context-truth.js +176 -0
- package/template/coding-task-manager/lib/acp/dsh-launch-model.js +302 -0
- package/template/coding-task-manager/lib/acp/dsh-mcp-servers.js +194 -0
- package/template/coding-task-manager/lib/acp/dsh-prompt-lane.js +616 -0
- package/template/coding-task-manager/lib/acp/dsh-route-patch.js +308 -0
- package/template/coding-task-manager/lib/acp/dsh-session-runtime.js +522 -0
- package/template/coding-task-manager/lib/acp/dsh-transcript.js +395 -0
- package/template/coding-task-manager/lib/acp/dsh-usage-log.js +162 -0
- package/template/coding-task-manager/lib/acp/ndjson-stdio-transport.js +225 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +47 -11
- package/template/coding-task-manager/lib/adopted-launch-environment.js +123 -0
- package/template/coding-task-manager/lib/agent-auto-update-runtime.js +0 -3
- package/template/coding-task-manager/lib/agent-auto-update.js +26 -10
- package/template/coding-task-manager/lib/agent-bundle/build.js +657 -0
- package/template/coding-task-manager/lib/agent-bundle/codex-install.js +558 -0
- package/template/coding-task-manager/lib/agent-bundle/drift.js +120 -0
- package/template/coding-task-manager/lib/agent-bundle/gc.js +522 -0
- package/template/coding-task-manager/lib/agent-bundle/inventory.js +346 -0
- package/template/coding-task-manager/lib/agent-bundle/push.js +717 -0
- package/template/coding-task-manager/lib/agent-bundle/skill-transfer.js +724 -0
- package/template/coding-task-manager/lib/agent-bundle/spawn.js +119 -0
- package/template/coding-task-manager/lib/agent-bundle/store.js +568 -0
- package/template/coding-task-manager/lib/agent-capabilities.js +142 -12
- package/template/coding-task-manager/lib/agent-cli-cache.js +30 -5
- package/template/coding-task-manager/lib/agent-definitions.js +19 -0
- package/template/coding-task-manager/lib/agent-hooks-codex-toml.js +28 -16
- package/template/coding-task-manager/lib/agent-hooks-codex-workspace.js +9 -1
- package/template/coding-task-manager/lib/agent-hooks-installer.js +23 -8
- package/template/coding-task-manager/lib/agent-hooks-prune.js +37 -11
- package/template/coding-task-manager/lib/agent-hooks-shim.js +105 -0
- package/template/coding-task-manager/lib/agent-presets.js +84 -1
- package/template/coding-task-manager/lib/agent-upgrade-jobs.js +12 -1
- package/template/coding-task-manager/lib/agent-version-service.js +74 -11
- package/template/coding-task-manager/lib/all-sessions-query.js +6 -2
- package/template/coding-task-manager/lib/api/agent-bundles.js +436 -0
- package/template/coding-task-manager/lib/api/backup-retention.js +83 -0
- package/template/coding-task-manager/lib/api/conversation-sync-routes.js +124 -0
- package/template/coding-task-manager/lib/api/diagnostics.js +549 -69
- package/template/coding-task-manager/lib/api/fleet.js +118 -0
- package/template/coding-task-manager/lib/api/hub-bridge.js +243 -0
- package/template/coding-task-manager/lib/api/ingress.js +9 -1
- package/template/coding-task-manager/lib/api/landing.js +403 -0
- package/template/coding-task-manager/lib/api/machines.js +691 -2
- package/template/coding-task-manager/lib/api/memory-sync-routes.js +335 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +8 -3
- package/template/coding-task-manager/lib/api/remote-sessions.js +1326 -0
- package/template/coding-task-manager/lib/api/session-attach.js +623 -0
- package/template/coding-task-manager/lib/api/session-messages.js +1384 -0
- package/template/coding-task-manager/lib/api/sessions.js +77 -51
- package/template/coding-task-manager/lib/api/setup-network.js +544 -16
- package/template/coding-task-manager/lib/api/setup-wizard-handlers.js +884 -0
- package/template/coding-task-manager/lib/api/setup.js +829 -10
- package/template/coding-task-manager/lib/api/walle-bridge.js +9 -1
- package/template/coding-task-manager/lib/attachment-reconciler-wiring.js +5 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +23 -0
- package/template/coding-task-manager/lib/auth-rules.js +292 -21
- package/template/coding-task-manager/lib/background-stop.js +369 -0
- package/template/coding-task-manager/lib/best-of-attempts.js +4 -1
- package/template/coding-task-manager/lib/best-of-launch-plan.js +52 -0
- package/template/coding-task-manager/lib/box-keep-awake.js +89 -0
- package/template/coding-task-manager/lib/broadcast-reason-stats.js +152 -0
- package/template/coding-task-manager/lib/broadcast-serialize.js +5 -0
- package/template/coding-task-manager/lib/build-info.js +4 -2
- package/template/coding-task-manager/lib/claude-model-truth.js +13 -2
- package/template/coding-task-manager/lib/claude-transcript-terminal-renderer.js +17 -13
- package/template/coding-task-manager/lib/client-diag-handler.js +64 -2
- package/template/coding-task-manager/lib/codex-app-server-client.js +14 -16
- package/template/coding-task-manager/lib/codex-configuration-history.js +55 -0
- package/template/coding-task-manager/lib/codex-harness-qualification.js +43 -20
- package/template/coding-task-manager/lib/codex-rollout-history.js +743 -0
- package/template/coding-task-manager/lib/coding-agent-models.js +27 -12
- package/template/coding-task-manager/lib/coding-cli-skill-installer.js +44 -14
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +43 -2
- package/template/coding-task-manager/lib/coding-session-model-owner.js +1 -1
- package/template/coding-task-manager/lib/coding-session-model-public.js +7 -1
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +23 -4
- package/template/coding-task-manager/lib/coding-session-model-scan.js +31 -0
- package/template/coding-task-manager/lib/coding-session-model-state.js +7 -0
- package/template/coding-task-manager/lib/conversation-freshness-diagnostics.js +280 -0
- package/template/coding-task-manager/lib/conversation-item-clock.js +261 -0
- package/template/coding-task-manager/lib/conversation-source-registry.js +5 -0
- package/template/coding-task-manager/lib/conversation-store-v2.js +44 -0
- package/template/coding-task-manager/lib/conversation-sync.js +355 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +96 -12
- package/template/coding-task-manager/lib/create/worker-role-profile.js +4 -4
- package/template/coding-task-manager/lib/create-intent-lease.js +51 -0
- package/template/coding-task-manager/lib/ctm-session-context-api.js +97 -6
- package/template/coding-task-manager/lib/db-domain-tables.js +1 -0
- package/template/coding-task-manager/lib/db-domains.js +1 -0
- package/template/coding-task-manager/lib/db-owner-dispatch-probe.js +32 -0
- package/template/coding-task-manager/lib/db-owner-request-counts.js +178 -0
- package/template/coding-task-manager/lib/db-owner-worker-client.js +68 -14
- package/template/coding-task-manager/lib/db-schema-source-files.js +5 -0
- package/template/coding-task-manager/lib/deferred-restore-attempt.js +6 -2
- package/template/coding-task-manager/lib/feature-manifest.js +4 -0
- package/template/coding-task-manager/lib/fleet-conversation-route.js +97 -0
- package/template/coding-task-manager/lib/fleet-fanout.js +78 -0
- package/template/coding-task-manager/lib/fleet-hub.js +762 -0
- package/template/coding-task-manager/lib/fleet-origin-follower.js +252 -0
- package/template/coding-task-manager/lib/fleet-resolver-effects.js +147 -0
- package/template/coding-task-manager/lib/fleet-resolver-tick.js +148 -0
- package/template/coding-task-manager/lib/fleet-resolver.js +454 -0
- package/template/coding-task-manager/lib/fleet-session-list-routes.js +114 -0
- package/template/coding-task-manager/lib/fleet-session-lists.js +672 -0
- package/template/coding-task-manager/lib/fleet-update.js +798 -0
- package/template/coding-task-manager/lib/fs-watcher-root-watch.js +141 -0
- package/template/coding-task-manager/lib/fs-watcher.js +58 -42
- package/template/coding-task-manager/lib/harness-history-transition.js +50 -9
- package/template/coding-task-manager/lib/headless-term-service.js +84 -8
- package/template/coding-task-manager/lib/hermetic-agent-launch-guard.js +10 -0
- package/template/coding-task-manager/lib/history-transition-read-op.js +175 -8
- package/template/coding-task-manager/lib/host-cpu-categories.js +310 -0
- package/template/coding-task-manager/lib/host-fseventsd-sampler.js +399 -0
- package/template/coding-task-manager/lib/host-handoff.js +16 -19
- package/template/coding-task-manager/lib/host-stats.js +150 -3
- package/template/coding-task-manager/lib/hosted-sync-worker.js +43 -320
- package/template/coding-task-manager/lib/hub-bridge-access.js +798 -0
- package/template/coding-task-manager/lib/hub-bridge-box.js +409 -0
- package/template/coding-task-manager/lib/hub-bridge-mcp.js +380 -0
- package/template/coding-task-manager/lib/hub-bridge-mesh.js +313 -0
- package/template/coding-task-manager/lib/hub-bridge-ops.js +721 -0
- package/template/coding-task-manager/lib/hub-bridge.js +398 -0
- package/template/coding-task-manager/lib/idle-notify.js +491 -0
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +8 -0
- package/template/coding-task-manager/lib/instance-manifest.js +5 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +18 -8
- package/template/coding-task-manager/lib/landing/agent-rule.txt +1 -0
- package/template/coding-task-manager/lib/landing/batch-runner.js +366 -0
- package/template/coding-task-manager/lib/landing/config.js +536 -0
- package/template/coding-task-manager/lib/landing/ctm-view.js +256 -0
- package/template/coding-task-manager/lib/landing/gate.js +360 -0
- package/template/coding-task-manager/lib/landing/git.js +344 -0
- package/template/coding-task-manager/lib/landing/lease.js +247 -0
- package/template/coding-task-manager/lib/landing/merge-delta.js +380 -0
- package/template/coding-task-manager/lib/landing/proof.js +223 -0
- package/template/coding-task-manager/lib/landing/queue.js +998 -0
- package/template/coding-task-manager/lib/landing/radar.js +489 -0
- package/template/coding-task-manager/lib/landing/resolve-cli.js +424 -0
- package/template/coding-task-manager/lib/landing/state.js +263 -0
- package/template/coding-task-manager/lib/landing/stop.js +49 -0
- package/template/coding-task-manager/lib/landing/sync-cli.js +475 -0
- package/template/coding-task-manager/lib/landing/sync.js +456 -0
- package/template/coding-task-manager/lib/landing/train.js +885 -0
- package/template/coding-task-manager/lib/landing/window.js +57 -0
- package/template/coding-task-manager/lib/launch-environment.js +121 -0
- package/template/coding-task-manager/lib/launch-model-override.js +36 -10
- package/template/coding-task-manager/lib/launch-plan-pool.js +2 -1
- package/template/coding-task-manager/lib/launch-plan-warm-cache.js +6 -0
- package/template/coding-task-manager/lib/lead-model-observer.js +57 -1
- package/template/coding-task-manager/lib/lead-model-options.js +160 -3
- package/template/coding-task-manager/lib/lead-model-restore-reconciliation.js +106 -24
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +352 -68
- package/template/coding-task-manager/lib/lead-model-switch.js +114 -10
- package/template/coding-task-manager/lib/lead-model-transition-runtime.js +14 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +24 -6
- package/template/coding-task-manager/lib/lead-route-preflight.js +179 -0
- package/template/coding-task-manager/lib/lead-switch-history-mode.js +110 -0
- package/template/coding-task-manager/lib/lead-wire-agent.js +7 -0
- package/template/coding-task-manager/lib/lifecycle-wake-experiment.js +9 -2
- package/template/coding-task-manager/lib/live-defer-announce.js +45 -0
- package/template/coding-task-manager/lib/machine-registry.js +750 -5
- package/template/coding-task-manager/lib/macos-capabilities.js +32 -15
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +433 -53
- package/template/coding-task-manager/lib/mainloop-observability.js +14 -9
- package/template/coding-task-manager/lib/media-draft-owner.js +16 -3
- package/template/coding-task-manager/lib/memory-join.js +337 -0
- package/template/coding-task-manager/lib/memory-sync-verify.js +393 -0
- package/template/coding-task-manager/lib/memory-sync.js +565 -0
- package/template/coding-task-manager/lib/migrations/v57-delegate-pref-mirror-opencode.js +68 -0
- package/template/coding-task-manager/lib/migrations/v58-subagent-launch-native-subagents.js +42 -0
- package/template/coding-task-manager/lib/migrations/v59-coding-session-model-state-dsh.js +73 -0
- package/template/coding-task-manager/lib/migrations/v60-delegate-pref-mirror-dsh.js +78 -0
- package/template/coding-task-manager/lib/migrations/v61-conversation-item-clock.js +71 -0
- package/template/coding-task-manager/lib/model-availability.js +727 -0
- package/template/coding-task-manager/lib/model-execution-recipe-metadata.js +4 -6
- package/template/coding-task-manager/lib/model-host-receipt.js +6 -3
- package/template/coding-task-manager/lib/model-id-rule.js +19 -0
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/auto-mode-review.js +110 -23
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/generation-probe.js +60 -17
- package/template/coding-task-manager/lib/model-proxy/historical-tool-declarations.js +37 -0
- package/template/coding-task-manager/lib/model-proxy/lead-request-validation.js +1 -0
- package/template/coding-task-manager/lib/model-proxy/lead-token-admin.js +130 -0
- package/template/coding-task-manager/lib/model-proxy/refusal-scope.js +151 -0
- package/template/coding-task-manager/lib/model-proxy/request-budget-store.js +11 -2
- package/template/coding-task-manager/lib/model-proxy/tokens.js +69 -5
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-agent-model-args.js +25 -22
- package/template/coding-task-manager/lib/native-history-control.js +6 -4
- package/template/coding-task-manager/lib/native-lead-settings-client.js +44 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +173 -36
- package/template/coding-task-manager/lib/online-backup-worker-client.js +2 -0
- package/template/coding-task-manager/lib/opencode-cli.js +480 -0
- package/template/coding-task-manager/lib/opencode-launch-model.js +471 -0
- package/template/coding-task-manager/lib/opencode-v1-plugin/model-carrier.mjs +35 -0
- package/template/coding-task-manager/lib/opencode-v2-plugin/server.js +118 -0
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +1 -1
- package/template/coding-task-manager/lib/park-composer-draft.js +54 -0
- package/template/coding-task-manager/lib/park-fresh-relaunch.js +41 -10
- package/template/coding-task-manager/lib/perf-phase-trace.js +72 -0
- package/template/coding-task-manager/lib/perf-summary.js +7 -0
- package/template/coding-task-manager/lib/perf-tracker.js +63 -2
- package/template/coding-task-manager/lib/project-config.js +7 -0
- package/template/coding-task-manager/lib/prompt-purge-v2.js +52 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +72 -15
- package/template/coding-task-manager/lib/provider-login-tab.js +10 -2
- package/template/coding-task-manager/lib/read-pool-client.js +89 -2
- package/template/coding-task-manager/lib/read-pool-worker-handle.js +29 -4
- package/template/coding-task-manager/lib/recent-catalog-query.js +15 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +67 -10
- package/template/coding-task-manager/lib/recovery-evidence.js +26 -11
- package/template/coding-task-manager/lib/relay-client.js +57 -3
- package/template/coding-task-manager/lib/relink-audit.js +34 -5
- package/template/coding-task-manager/lib/remote-machine-catalog.js +321 -26
- package/template/coding-task-manager/lib/remote-machine-create.js +576 -36
- package/template/coding-task-manager/lib/remote-machine-link.js +546 -59
- package/template/coding-task-manager/lib/remote-machine-proxy.js +143 -9
- package/template/coding-task-manager/lib/remote-session-forward.js +65 -7
- package/template/coding-task-manager/lib/remote-terminal-target.js +9 -2
- package/template/coding-task-manager/lib/restart-guard.js +18 -6
- package/template/coding-task-manager/lib/restore-seed-modes.js +87 -0
- package/template/coding-task-manager/lib/restored-scrollback-hydration-runtime.js +22 -2
- package/template/coding-task-manager/lib/resume-admission.js +17 -3
- package/template/coding-task-manager/lib/review-repo-registry.js +58 -19
- package/template/coding-task-manager/lib/root-delivery-runtime.js +2 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +1 -1
- package/template/coding-task-manager/lib/runtime-contract.js +3 -1
- package/template/coding-task-manager/lib/schema-indexes.js +1 -0
- package/template/coding-task-manager/lib/session-context-locator.js +742 -0
- package/template/coding-task-manager/lib/session-create/attachment.js +2 -2
- package/template/coding-task-manager/lib/session-create/create-phases.js +118 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +86 -16
- package/template/coding-task-manager/lib/session-create/dsh-session.js +930 -0
- package/template/coding-task-manager/lib/session-create/lead-route-launch.js +186 -13
- package/template/coding-task-manager/lib/session-create/native-lead-lifetime.js +55 -3
- package/template/coding-task-manager/lib/session-create/opencode-lead-provider.js +50 -0
- package/template/coding-task-manager/lib/session-create/prepared.js +162 -38
- package/template/coding-task-manager/lib/session-create/provider-process.js +160 -28
- package/template/coding-task-manager/lib/session-create/response.js +7 -7
- package/template/coding-task-manager/lib/session-create/structured-session.js +27 -0
- package/template/coding-task-manager/lib/session-create/subagent-pick-table.js +3 -1
- package/template/coding-task-manager/lib/session-create/subagent-pick.js +458 -9
- package/template/coding-task-manager/lib/session-create/transcript.js +1 -1
- package/template/coding-task-manager/lib/session-create/worker-budget-setup.js +21 -2
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +7 -1
- package/template/coding-task-manager/lib/session-create/worker-launch-sandbox.js +39 -10
- package/template/coding-task-manager/lib/session-create/worker-observability.js +2 -2
- package/template/coding-task-manager/lib/session-empty.js +13 -10
- package/template/coding-task-manager/lib/session-host-manager.js +60 -0
- package/template/coding-task-manager/lib/session-jobs.js +3 -1
- package/template/coding-task-manager/lib/session-lifecycle.js +10 -4
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +6 -2
- package/template/coding-task-manager/lib/session-model-intent-contract.js +11 -8
- package/template/coding-task-manager/lib/session-park.js +738 -0
- package/template/coding-task-manager/lib/session-path-grant.js +13 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +106 -4
- package/template/coding-task-manager/lib/session-transport.js +17 -0
- package/template/coding-task-manager/lib/session-workspace-projection.js +54 -5
- package/template/coding-task-manager/lib/session-workspace-tracking-runtime.js +213 -1
- package/template/coding-task-manager/lib/skill-autocomplete-query.js +120 -3
- package/template/coding-task-manager/lib/skill-autocomplete.js +15 -1
- package/template/coding-task-manager/lib/sqlite-file-set.js +39 -0
- package/template/coding-task-manager/lib/standup-search-adapter.js +16 -2
- package/template/coding-task-manager/lib/state-sync/client-ledger.js +12 -0
- package/template/coding-task-manager/lib/state-sync/frame-emitter.js +319 -100
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +4 -1
- package/template/coding-task-manager/lib/storage/delegate-pref-mirror-store.js +32 -9
- package/template/coding-task-manager/lib/structured-session-boot-sweep.js +235 -0
- package/template/coding-task-manager/lib/submission-ack.js +90 -2
- package/template/coding-task-manager/lib/systemd-scope.js +115 -0
- package/template/coding-task-manager/lib/telemetry-receiver.js +39 -0
- package/template/coding-task-manager/lib/terminal-attach-geometry.js +985 -0
- package/template/coding-task-manager/lib/terminal-bootstrap.js +30 -0
- package/template/coding-task-manager/lib/terminal-draft-operation.js +357 -0
- package/template/coding-task-manager/lib/terminal-input-admission.js +17 -1
- package/template/coding-task-manager/lib/terminal-input-prepared.js +563 -0
- package/template/coding-task-manager/lib/transcript-cache-temps.js +171 -0
- package/template/coding-task-manager/lib/transcript-frame-cache.js +8 -3
- package/template/coding-task-manager/lib/transcript-source.js +36 -4
- package/template/coding-task-manager/lib/transcript-store.js +9 -0
- package/template/coding-task-manager/lib/typing-interaction-trace.js +106 -3
- package/template/coding-task-manager/lib/usage-ledger/backfill.js +40 -5
- package/template/coding-task-manager/lib/usage-ledger/client.js +4 -1
- package/template/coding-task-manager/lib/usage-ledger/dsh-parse.js +152 -0
- package/template/coding-task-manager/lib/usage-ledger/dsh-read.js +73 -0
- package/template/coding-task-manager/lib/usage-ledger/ingest.js +34 -6
- package/template/coding-task-manager/lib/usage-ledger/live.js +35 -8
- package/template/coding-task-manager/lib/usage-ledger/read.js +29 -20
- package/template/coding-task-manager/lib/usage-ledger/schema.js +61 -1
- package/template/coding-task-manager/lib/usage-ledger/task-coordinator.js +4 -2
- package/template/coding-task-manager/lib/wake-skip-reasons.js +1 -0
- package/template/coding-task-manager/lib/walle/turn.js +1 -1
- package/template/coding-task-manager/lib/walle-history-serve.js +838 -0
- package/template/coding-task-manager/lib/walle-kill-authority.js +2 -2
- package/template/coding-task-manager/lib/walle-log-rotation.js +164 -1
- package/template/coding-task-manager/lib/walle-mcp-auto-config.js +56 -2
- package/template/coding-task-manager/lib/walle-row-history.js +495 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +33 -14
- package/template/coding-task-manager/lib/watch-worker-client.js +203 -0
- package/template/coding-task-manager/lib/worker-terminal-output.js +5 -2
- package/template/coding-task-manager/lib/ws/hub-bridge.js +35 -0
- package/template/coding-task-manager/lib/ws/lifecycle.js +6 -11
- package/template/coding-task-manager/lib/ws/misc.js +1 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +106 -4
- package/template/coding-task-manager/lib/ws/terminal-attach.js +91 -0
- package/template/coding-task-manager/oauth-proxy.js +112 -115
- package/template/coding-task-manager/package-lock.json +10 -94
- package/template/coding-task-manager/package.json +4 -3
- package/template/coding-task-manager/protocol/message-table.json +10 -0
- package/template/coding-task-manager/protocol/messages.js +10 -0
- package/template/coding-task-manager/protocol/ws-schema.json +20 -0
- package/template/coding-task-manager/protocol/ws-schema.md +2 -0
- package/template/coding-task-manager/public/css/app.css +170 -0
- package/template/coding-task-manager/public/css/walle-session.css +4 -0
- package/template/coding-task-manager/public/index.html +473 -243
- package/template/coding-task-manager/public/js/agent-command.js +86 -0
- package/template/coding-task-manager/public/js/app/backups-panel.js +111 -5
- package/template/coding-task-manager/public/js/app/coding-agent-upgrade.js +103 -4
- package/template/coding-task-manager/public/js/app/machines-page.js +1328 -8
- package/template/coding-task-manager/public/js/app/models-tab.js +113 -38
- package/template/coding-task-manager/public/js/app/restart-banner.js +53 -0
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +184 -2
- package/template/coding-task-manager/public/js/background-work.js +196 -27
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +138 -10
- package/template/coding-task-manager/public/js/core/active-session-groups.js +179 -0
- package/template/coding-task-manager/public/js/core/pending-create.js +238 -0
- package/template/coding-task-manager/public/js/core/recent-machine-source.js +228 -0
- package/template/coding-task-manager/public/js/core/session-commands.js +180 -14
- package/template/coding-task-manager/public/js/dashboard-client-function-order.json +8 -0
- package/template/coding-task-manager/public/js/document-review-links.js +9 -1
- package/template/coding-task-manager/public/js/file-resolution-client.js +52 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +22 -2
- package/template/coding-task-manager/public/js/machine-pool-shared.js +558 -3
- package/template/coding-task-manager/public/js/message-renderer.js +4 -3
- package/template/coding-task-manager/public/js/model-picker.js +3 -1
- package/template/coding-task-manager/public/js/native-subagent-review.js +15 -2
- package/template/coding-task-manager/public/js/new-session-agent-grid.js +346 -0
- package/template/coding-task-manager/public/js/pasted-content.js +236 -0
- package/template/coding-task-manager/public/js/pe-private-content-lifetime.js +39 -7
- package/template/coding-task-manager/public/js/private-model-lifetime.js +6 -2
- package/template/coding-task-manager/public/js/prompts.js +11 -10
- package/template/coding-task-manager/public/js/protocol.js +10 -0
- package/template/coding-task-manager/public/js/restart-banner-policy.js +29 -1
- package/template/coding-task-manager/public/js/session-model-picker.js +447 -57
- package/template/coding-task-manager/public/js/session-search-utils.js +18 -2
- package/template/coding-task-manager/public/js/session-usage-view.js +2 -1
- package/template/coding-task-manager/public/js/session-workspace-tracking.js +51 -1
- package/template/coding-task-manager/public/js/state-sync-client.js +10 -1
- package/template/coding-task-manager/public/js/stream-view.js +6 -5
- package/template/coding-task-manager/public/js/terminal/conv-only-session.js +287 -0
- package/template/coding-task-manager/public/js/terminal/runtime-09.js +2 -0
- package/template/coding-task-manager/public/js/terminal/runtime-10.js +5 -5
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +8 -1
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +1 -1
- package/template/coding-task-manager/public/js/terminal-attachment-draft.js +67 -13
- package/template/coding-task-manager/public/js/terminal-image-orchestration.js +52 -6
- package/template/coding-task-manager/public/js/terminal-wheel.js +23 -3
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +4 -4
- package/template/coding-task-manager/public/js/walle.js +6 -0
- package/template/coding-task-manager/public/js/ws/live-dispatch.js +10 -0
- package/template/coding-task-manager/public/m/app.css +140 -0
- package/template/coding-task-manager/public/m/app.js +507 -68
- package/template/coding-task-manager/public/m/index.html +17 -0
- package/template/coding-task-manager/public/m/machines.js +838 -4
- package/template/coding-task-manager/public/m/private-api.js +31 -1
- package/template/coding-task-manager/public/usage.html +1 -0
- package/template/coding-task-manager/scripts/dev-route-fixture/synthetic-route-upstream.js +104 -0
- package/template/coding-task-manager/scripts/dev-route-fixture.js +339 -0
- package/template/coding-task-manager/scripts/opencode-lead-route-probe.js +195 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/fake-anthropic.js +168 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe/probe-plugin/server.js +95 -0
- package/template/coding-task-manager/scripts/opencode-v2-contract-probe.js +673 -0
- package/template/coding-task-manager/server.js +1950 -11679
- package/template/coding-task-manager/session-integrity.js +64 -4
- package/template/coding-task-manager/workers/db-owner-worker.js +19 -2
- package/template/coding-task-manager/workers/online-backup-worker.js +1 -0
- package/template/coding-task-manager/workers/read-pool-model-collector.js +51 -3
- package/template/coding-task-manager/workers/read-pool-worker.js +76 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +6 -6
- package/template/coding-task-manager/workers/session-host-process.js +5 -5
- package/template/coding-task-manager/workers/watch-worker.js +105 -0
- package/template/coding-task-manager/workers/worktree-worker.js +168 -33
- package/template/ctm-relay/lib/protocol.js +14 -1
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/shared/backup-retention.js +82 -0
- package/template/shared/conversation-admission.js +2 -0
- package/template/shared/conversation-provenance.js +3 -7
- package/template/shared/conversation-storage-v2-contract.js +5 -0
- package/template/shared/linked-worktree.js +51 -0
- package/template/shared/macos-fda-target.js +57 -0
- package/template/shared/orphan-parents.js +55 -0
- package/template/shared/release-schema.json +2 -2
- package/template/shared/storage/contract.js +3 -0
- package/template/wall-e/adapters/imessage.js +12 -0
- package/template/wall-e/agent.js +71 -4
- package/template/wall-e/api-walle.js +21 -1
- package/template/wall-e/bin/train-gemma-e4b-tooluse.js +2 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +5 -5
- package/template/wall-e/brain/brain-join.js +859 -0
- package/template/wall-e/brain/brain-sync-membership.js +20 -0
- package/template/wall-e/brain/brain-sync-store.js +131 -77
- package/template/wall-e/brain/brain-sync-v2-schema.js +203 -0
- package/template/wall-e/brain/brain-sync-v2.js +578 -0
- package/template/wall-e/brain/delegate-model-prefs-store.js +13 -4
- package/template/wall-e/brain/deterministic-id.js +51 -0
- package/template/wall-e/brain/memories.js +2 -2
- package/template/wall-e/brain/migrations.js +40 -6
- package/template/wall-e/brain/model-identity-migration.js +1 -1
- package/template/wall-e/brain/owner-call-whitelist.js +23 -7
- package/template/wall-e/brain/schema.js +45 -85
- package/template/wall-e/brain.js +171 -35
- package/template/wall-e/chat.js +3 -3
- package/template/wall-e/coding/agent-shell-env.js +7 -2
- package/template/wall-e/coding/prompt-bundle.js +66 -21
- package/template/wall-e/config/main-loop-exemptions.json +2 -2
- package/template/wall-e/http/admin-api.js +37 -0
- package/template/wall-e/http/gws-api.js +4 -0
- package/template/wall-e/http/helper-rules-api.js +7 -1
- package/template/wall-e/http/model-admin.js +8 -1
- package/template/wall-e/http/model-delegate-pref-admin.js +3 -3
- package/template/wall-e/http/model-proxy-providers.js +174 -1
- package/template/wall-e/http/model-route-health.js +157 -0
- package/template/wall-e/http/sync-api.js +278 -464
- package/template/wall-e/jobs/job-runner.js +28 -1
- package/template/wall-e/lib/brain-owner-op-priority.js +9 -1
- package/template/wall-e/lib/ctm-data-dir.js +26 -0
- package/template/wall-e/lib/derivation-home.js +147 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +50 -9
- package/template/wall-e/lib/helper-rules/grade.js +51 -8
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +38 -5
- package/template/wall-e/lib/helper-rules/store.js +4 -2
- package/template/wall-e/lib/landing-mcp-tools.js +170 -0
- package/template/wall-e/lib/mcp-client-config.js +133 -14
- package/template/wall-e/lib/mcp-gateway-migrate.js +28 -64
- package/template/wall-e/lib/mcp-integration.js +58 -13
- package/template/wall-e/lib/parent-death.js +7 -1
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -4
- package/template/wall-e/llm/delegate-lead-prefs.js +16 -1
- package/template/wall-e/llm/delegate-vendor-default.js +12 -5
- package/template/wall-e/llm/model-roles.js +1 -1
- package/template/wall-e/llm/native-delegate-selection.js +56 -8
- package/template/wall-e/llm/worker-delegate-args.js +72 -7
- package/template/wall-e/llm/worker-mcp-routing.js +66 -17
- package/template/wall-e/loops/brain-custom-writes.js +19 -0
- package/template/wall-e/loops/ingest.js +17 -1
- package/template/wall-e/loops/think.js +19 -4
- package/template/wall-e/mcp-contract.js +222 -6
- package/template/wall-e/mcp-protocol-contract.js +40 -5
- package/template/wall-e/mcp-server.js +128 -29
- package/template/wall-e/mcp-worker-schemas.js +5 -5
- package/template/wall-e/memory/ctm-context-client.js +85 -0
- package/template/wall-e/memory/ctm-session-context.js +9 -3
- package/template/wall-e/memory/hub-bridge-client.js +388 -0
- package/template/wall-e/memory/replica-origin.js +56 -0
- package/template/wall-e/memory/session-pool.js +135 -0
- package/template/wall-e/runtime/live-capabilities.js +8 -3
- package/template/wall-e/server.js +4 -2
- package/template/wall-e/shared/release-schema.json +2 -2
- package/template/wall-e/shared/sqlite-file-set.js +39 -0
- package/template/wall-e/shared/storage/contract.js +3 -0
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +7 -1
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +4 -4
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +6 -6
- package/template/wall-e/skills/_templates/fleet-sync/VERSION +1 -0
- package/template/wall-e/skills/_templates/fleet-sync/released-sha256.json +16 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/claude/SKILL.md +64 -0
- package/template/wall-e/skills/_templates/fleet-sync/templates/codex/SKILL.md +64 -0
- package/template/wall-e/tools/chrome-automation.js +6 -0
- package/template/wall-e/training/setup-cuda-train-env.sh +2 -2
- package/template/wall-e/workers/brain-owner-worker.js +18 -4
- package/template/wall-e/workers/mcp-gateway-process.js +5 -5
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* WP-1 root-dir watch — design doc cross-machine-session-context-2026-10-31.html
|
|
4
|
+
* §1.3 C-6, §5 R-10/D-9, §8 WP-1, §9 S-4.
|
|
5
|
+
*
|
|
6
|
+
* The watcher watched each *project* directory but never a provider ROOT, so a brand-new
|
|
7
|
+
* project dir (a fresh worktree: ~/.claude/projects/<new-slug>/) stayed unreadable until the
|
|
8
|
+
* 60 s scan. This module owns the ONE extra non-recursive fs.watch per canonical root that
|
|
9
|
+
* closes that gap — a new dir is attached in ~one debounce window instead of a scan tick.
|
|
10
|
+
*
|
|
11
|
+
* Extracted out of lib/fs-watcher.js purely to keep that file inside its file-size ratchet
|
|
12
|
+
* (bin/file-size-lint.js). Behaviour is identical to the in-file version, including the
|
|
13
|
+
* per-layout rules, the symlink/containment refusals via the watcher's own
|
|
14
|
+
* `_warnRefusalOnce` + the caller's `isInside`, the error→reattach after `reattachDelayMs`,
|
|
15
|
+
* and the `closeHandles` contract. WP-3 (§9 S-5) moved the handle itself onto the caller's
|
|
16
|
+
* watch client (`watcher._watch`, the watch worker thread) so open/close never blocks the main
|
|
17
|
+
* loop; a sync attach failure now arrives as an `'error'` with `syncAttach` instead of a throw.
|
|
18
|
+
* The scan is untouched and stays the backstop: a dir
|
|
19
|
+
* created while this watch is closed/errored is still attached by `_scanAndWatchAsync`.
|
|
20
|
+
*
|
|
21
|
+
* createRootWatch(watcher, { isInside, reattachDelayMs }) →
|
|
22
|
+
* { ensure(root), close(root), closeAll({ closeHandles }), handleChild(root, filename), handles,
|
|
23
|
+
* events }
|
|
24
|
+
*
|
|
25
|
+
* isInside — the caller's canonical containment predicate (isInside in fs-watcher.js)
|
|
26
|
+
* reattachDelayMs — the caller's REATTACH_DELAY_MS (same delay the per-dir watchers use)
|
|
27
|
+
* watcher — the JsonlWatcher this watch belongs to; this module reads `_stopped`
|
|
28
|
+
* and calls `emit`, `_warnRefusalOnce`, `_refreshRootAsync`,
|
|
29
|
+
* `_ensureWatchedDirAsync` and `_scanDatedRootStepsAsync` on it.
|
|
30
|
+
* events — read-only, monotonically increasing count of raw fs.watch callbacks
|
|
31
|
+
* (every event, before any filtering). It is the liveness probe for the
|
|
32
|
+
* tests: on macOS libuv keeps ONE FSEventStream per process and restarts it
|
|
33
|
+
* whenever a watcher is added or removed, so a test that wants to measure
|
|
34
|
+
* the root watch has to prove the stream is delivering — and proving it by
|
|
35
|
+
* attaching a probe directory would itself add a watcher and restart the
|
|
36
|
+
* stream. Reading this counter for a probe FILE under the root is watcher-free.
|
|
37
|
+
*/
|
|
38
|
+
const fs = require('fs');
|
|
39
|
+
const path = require('path');
|
|
40
|
+
|
|
41
|
+
function createRootWatch(watcher, { isInside, reattachDelayMs }) {
|
|
42
|
+
// root object → FSWatcher. Deliberately separate from the watcher's `_watchers`/
|
|
43
|
+
// `_watchedDirs`, so `watchedDirsSnapshot()`/`rootHealthSnapshot()` keep meaning
|
|
44
|
+
// "session dirs we read from".
|
|
45
|
+
const handles = new Map();
|
|
46
|
+
// Monotonic count of raw fs.watch callbacks, bumped BEFORE any filtering so it counts every
|
|
47
|
+
// event the OS stream delivered for any canonical root. Exposed read-only as `events`.
|
|
48
|
+
let events = 0;
|
|
49
|
+
|
|
50
|
+
function close(root) {
|
|
51
|
+
const handle = handles.get(root);
|
|
52
|
+
if (!handle) return;
|
|
53
|
+
handles.delete(root);
|
|
54
|
+
try { handle.close(); } catch { /* expected: the watcher may already be closed (its own 'error' handler) */ }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function ensure(root) {
|
|
58
|
+
if (watcher._stopped || !root.canonical || handles.has(root)) return;
|
|
59
|
+
// WP-3 §9 S-5: the handle comes from the caller's watch client (the watch worker), so its
|
|
60
|
+
// close() posts instead of blocking this loop on the libuv FSEvents semaphore.
|
|
61
|
+
const handle = watcher._watch(root.canonical, {}, (eventType, filename) => {
|
|
62
|
+
events += 1; // WP-1c: count first — the callback firing is the liveness signal itself.
|
|
63
|
+
if (!filename || watcher._stopped) return;
|
|
64
|
+
handleChild(root, filename).catch((e) => watcher.emit('error', e));
|
|
65
|
+
});
|
|
66
|
+
handle.on('error', (err) => {
|
|
67
|
+
// WP-3 §9 S-5: a sync attach failure (root vanished) now arrives here, not as a throw —
|
|
68
|
+
// report it with no reattach timer, exactly like the try/catch this replaced.
|
|
69
|
+
if (err && err.syncAttach) { watcher.emit('error', err); return; }
|
|
70
|
+
// Same shape as the per-dir watchers: drop the handle, then re-resolve the root (its
|
|
71
|
+
// canonical target may have moved while we were down) and reattach.
|
|
72
|
+
close(root);
|
|
73
|
+
if (watcher._stopped) return;
|
|
74
|
+
setTimeout(() => {
|
|
75
|
+
if (watcher._stopped) return;
|
|
76
|
+
watcher._refreshRootAsync(root).then(() => ensure(root)).catch((e) => watcher.emit('error', e));
|
|
77
|
+
}, reattachDelayMs);
|
|
78
|
+
});
|
|
79
|
+
handles.set(root, handle);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// A direct child of a canonical root appeared. Per layout (mirrors what `_scanTickSteps`
|
|
83
|
+
// derives for that layout — see `_scanProjectsRootStepsAsync`/`_scanDatedRootStepsAsync`):
|
|
84
|
+
// - 'projects': attach it through the SAME path the scan and the fs.watch reattach use,
|
|
85
|
+
// with the same watchKey/projectEntry (`entry`, prefixed by root name for non-claude
|
|
86
|
+
// roots) and the same symlink + isInside containment refusals.
|
|
87
|
+
// - 'dated': the immediate child of the root is a YEAR dir, never a leaf day dir, so
|
|
88
|
+
// there is nothing to attach here; re-run the (idempotent) dated step for the root so
|
|
89
|
+
// a brand-new year attaches its newest leaf dirs on this tick instead of 60 s later.
|
|
90
|
+
// NOTE: a new day dir under an EXISTING year is not visible to a root watcher; it still
|
|
91
|
+
// rides the 60 s scan.
|
|
92
|
+
// - 'flat': the root itself is the watched dir and its *.jsonl files are already covered
|
|
93
|
+
// by that per-dir watcher — a sub-directory is not a session location, so nothing to do
|
|
94
|
+
// (and the non-directory early return above means no duplicate file events).
|
|
95
|
+
async function handleChild(root, filename) {
|
|
96
|
+
if (filename.includes(path.sep)) return; // direct children only
|
|
97
|
+
const child = path.join(root.canonical, filename);
|
|
98
|
+
let lstat;
|
|
99
|
+
try { lstat = await fs.promises.lstat(child); } catch { return; } // vanished again
|
|
100
|
+
if (lstat.isSymbolicLink()) {
|
|
101
|
+
watcher._warnRefusalOnce('project-symlink', child, `[jsonl-watcher] skipping symlinked project dir: ${filename}`);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!lstat.isDirectory()) return;
|
|
105
|
+
let canonical;
|
|
106
|
+
try { canonical = await fs.promises.realpath(child); } catch { return; }
|
|
107
|
+
if (!isInside(root.canonical, canonical)) {
|
|
108
|
+
watcher._warnRefusalOnce('project-escape', canonical, `[jsonl-watcher] skipping project dir that escapes root: ${filename}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (root.layout === 'projects') {
|
|
112
|
+
const watchKey = root.name === 'claude' ? filename : `${root.name}:${filename}`;
|
|
113
|
+
await watcher._ensureWatchedDirAsync(root, child, watchKey, filename);
|
|
114
|
+
} else if (root.layout === 'dated' && /^\d{4}$/.test(filename)) {
|
|
115
|
+
// eslint-disable-next-line no-unused-vars
|
|
116
|
+
for await (const _step of watcher._scanDatedRootStepsAsync(root)) { /* no-op: drain without a tick budget */ }
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Rides the same closeHandles contract as the per-dir watchers: fast-exit callers leave the
|
|
121
|
+
// OS handles for the process to reclaim. The map is always cleared.
|
|
122
|
+
function closeAll({ closeHandles = true } = {}) {
|
|
123
|
+
if (closeHandles) {
|
|
124
|
+
for (const [root, handle] of handles) {
|
|
125
|
+
try { handle.close(); } catch (e) { console.error(`[jsonl-watcher] close error for root ${root.name}:`, e.message); }
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
handles.clear();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
ensure,
|
|
133
|
+
close,
|
|
134
|
+
closeAll,
|
|
135
|
+
handleChild,
|
|
136
|
+
handles,
|
|
137
|
+
get events() { return events; },
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
module.exports = { createRootWatch };
|
|
@@ -5,6 +5,8 @@ const EventEmitter = require('events');
|
|
|
5
5
|
const { namedInterval, runSync, wrap } = require('./perf-tracker');
|
|
6
6
|
const envFlags = require('./env-flags');
|
|
7
7
|
const { isWritableOnlyByOwner } = require('../../shared/owner-write-safety');
|
|
8
|
+
const { createRootWatch } = require('./fs-watcher-root-watch');
|
|
9
|
+
const { getDefaultWatchClient } = require('./watch-worker-client');
|
|
8
10
|
|
|
9
11
|
// Design doc mainloop-composer-freeze-2026-08-20.html §5 D-1, §6, §9 S-1/S-4. The 60s
|
|
10
12
|
// scan tick and the attach/reattach directory walk ran completely unwrapped, so the
|
|
@@ -228,8 +230,12 @@ class JsonlWatcher extends EventEmitter {
|
|
|
228
230
|
extraRoots = [],
|
|
229
231
|
scanBudgetMs = DEFAULT_SCAN_BUDGET_MS,
|
|
230
232
|
scanContinuationJitterMs = DEFAULT_SCAN_CONTINUATION_JITTER_MS,
|
|
233
|
+
watch = null,
|
|
231
234
|
} = {}) {
|
|
232
235
|
super();
|
|
236
|
+
// WP-3 (§5 D-4, §9 S-3): directory watchers are created in ONE worker thread
|
|
237
|
+
// (./watch-worker-client.js); `watch` is the injection seam (tests needing a real fs.watch).
|
|
238
|
+
this._watch = typeof watch === 'function' ? watch : getDefaultWatchClient().watch;
|
|
233
239
|
this._rescanDirsPerTick = Math.max(1, Number(rescanDirsPerTick) || DEFAULT_RESCAN_DIRS_PER_TICK);
|
|
234
240
|
this._rescanCursor = 0;
|
|
235
241
|
// WP-1 async scan budget/continuation state (§9 S-1..S-4). `_scanBudgetMs` is
|
|
@@ -277,6 +283,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
277
283
|
configuredPath: r.configuredPath || r.dir,
|
|
278
284
|
enrolled: r.enrolled === true,
|
|
279
285
|
allowRootSymlink: r.allowRootSymlink === true,
|
|
286
|
+
ensureDir: r.ensureDir === true, // a CTM-owned root its writer creates lazily (dsh): the scan tick mkdirs it
|
|
280
287
|
dev: 0,
|
|
281
288
|
ino: 0,
|
|
282
289
|
validationStatus: r.validationStatus || 'missing',
|
|
@@ -288,6 +295,10 @@ class JsonlWatcher extends EventEmitter {
|
|
|
288
295
|
];
|
|
289
296
|
this._watchers = new Map(); // watchKey → FSWatcher
|
|
290
297
|
this._watchedDirs = new Map(); // watchKey → { dir, root, projectEntry }
|
|
298
|
+
// WP-1 root-dir watch: one extra fs.watch per canonical root (./fs-watcher-root-watch.js).
|
|
299
|
+
this._rootWatch = createRootWatch(this, { isInside, reattachDelayMs: REATTACH_DELAY_MS });
|
|
300
|
+
// WP-1b: watchKeys with an attach in flight — reserved before the first await (see below).
|
|
301
|
+
this._attaching = new Set();
|
|
291
302
|
this._debounceTimers = new Map(); // filePath → timer
|
|
292
303
|
this._knownFiles = new Map(); // filePath → { size, mtimeMs }
|
|
293
304
|
this._refusalWarnings = new Set();
|
|
@@ -323,6 +334,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
323
334
|
}
|
|
324
335
|
|
|
325
336
|
_detachRoot(root, previousCanonical = root.canonical) {
|
|
337
|
+
this._rootWatch.close(root);
|
|
326
338
|
for (const [watchKey, rec] of this._watchedDirs) {
|
|
327
339
|
if (rec.root !== root) continue;
|
|
328
340
|
const watcher = this._watchers.get(watchKey);
|
|
@@ -354,6 +366,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
354
366
|
|| previousDev !== root.dev
|
|
355
367
|
|| previousIno !== root.ino
|
|
356
368
|
)) this._detachRoot(root, previousCanonical);
|
|
369
|
+
this._rootWatch.ensure(root); // WP-1: (re)attach the root-dir watcher for this canonical root
|
|
357
370
|
return !!root.canonical;
|
|
358
371
|
}
|
|
359
372
|
|
|
@@ -395,10 +408,10 @@ class JsonlWatcher extends EventEmitter {
|
|
|
395
408
|
/**
|
|
396
409
|
* Stop scanning and event delivery.
|
|
397
410
|
* @param {{ closeHandles?: boolean }} [options]
|
|
398
|
-
* closeHandles (default true): close every
|
|
399
|
-
*
|
|
400
|
-
*
|
|
401
|
-
* the shared FSEventStream
|
|
411
|
+
* closeHandles (default true): close every directory watch handle. WP-3 posts those closes to
|
|
412
|
+
* the watch worker, so the libuv FSEvents semaphore wait no longer blocks this loop.
|
|
413
|
+
* false: leave the OS handles open for the process to reclaim at exit. ONLY for callers about to
|
|
414
|
+
* exit (each close rebuilds the shared FSEventStream; 417 closes took 41-360 s). The maps are
|
|
402
415
|
* still cleared so getKnownFiles()/introspection stay consistent.
|
|
403
416
|
*/
|
|
404
417
|
stop({ closeHandles = true } = {}) {
|
|
@@ -412,6 +425,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
412
425
|
try { watcher.close(); } catch (e) { console.error(`[jsonl-watcher] close error for ${entry}:`, e.message); }
|
|
413
426
|
}
|
|
414
427
|
}
|
|
428
|
+
this._rootWatch.closeAll({ closeHandles }); // WP-1 root watchers: same closeHandles contract
|
|
415
429
|
this._watchers.clear();
|
|
416
430
|
this._watchedDirs.clear();
|
|
417
431
|
}
|
|
@@ -542,6 +556,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
542
556
|
if (this._stopped) return;
|
|
543
557
|
// Late canonicalization mirrors the sync path: a root whose directory
|
|
544
558
|
// didn't exist at start() attaches on the first tick that sees it.
|
|
559
|
+
if (root.ensureDir && !root.canonical) await fs.promises.mkdir(root.dir, { recursive: true }).catch((e) => this.emit('error', e));
|
|
545
560
|
const attached = await this._refreshRootAsync(root);
|
|
546
561
|
if (!attached) {
|
|
547
562
|
await this._refuseRootOnceAsync(root);
|
|
@@ -577,6 +592,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
577
592
|
|| previousDev !== root.dev
|
|
578
593
|
|| previousIno !== root.ino
|
|
579
594
|
)) this._detachRoot(root, previousCanonical);
|
|
595
|
+
this._rootWatch.ensure(root); // WP-1: covers roots that appear/retarget after start()
|
|
580
596
|
return !!root.canonical;
|
|
581
597
|
}
|
|
582
598
|
|
|
@@ -712,28 +728,32 @@ class JsonlWatcher extends EventEmitter {
|
|
|
712
728
|
// are NOT on the budgeted periodic path (the fs.watch reattach-on-error handler
|
|
713
729
|
// below) drain it to completion via `_ensureWatchedDirAsync`.
|
|
714
730
|
async *_ensureWatchedDirAsyncSteps(root, dir, watchKey, projectEntry) {
|
|
715
|
-
if (this._stopped || this._watchers.has(watchKey)) return;
|
|
716
|
-
|
|
731
|
+
if (this._stopped || this._watchers.has(watchKey) || this._attaching.has(watchKey)) return;
|
|
732
|
+
this._attaching.add(watchKey); // reserve synchronously: the walk below awaits before fs.watch
|
|
733
|
+
try {
|
|
734
|
+
const rec = { dir, root, projectEntry };
|
|
717
735
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
736
|
+
// Reconcile against prior _knownFiles entries for this dir (fresh attach: none;
|
|
737
|
+
// reattach: catches files that appeared/grew/vanished while unwatched).
|
|
738
|
+
yield* this._attachWalkStepsAsync(dir, root, projectEntry);
|
|
721
739
|
|
|
722
|
-
|
|
723
|
-
try {
|
|
740
|
+
// Set up the watcher (non-recursive). Symlink checks happen in _handleFileEvent.
|
|
724
741
|
this._watchedDirs.set(watchKey, rec);
|
|
725
|
-
const watcher =
|
|
742
|
+
const watcher = this._watch(dir, {}, (eventType, filename) => {
|
|
726
743
|
if (!filename || this._stopped) return;
|
|
727
744
|
if (!filename.endsWith('.jsonl') && !filename.endsWith('.jsonl.bak')) return;
|
|
728
745
|
const filePath = path.join(dir, filename);
|
|
729
746
|
this._debouncedHandle(filePath, dir, watchKey);
|
|
730
747
|
});
|
|
731
748
|
|
|
732
|
-
watcher.on('error', () => {
|
|
733
|
-
// Reattach on error with delay — same delay/shape as the sync path, but
|
|
734
|
-
// resumes via the async attach so it never touches sync fs.
|
|
749
|
+
watcher.on('error', (err) => {
|
|
735
750
|
this._watchers.delete(watchKey);
|
|
736
751
|
this._watchedDirs.set(watchKey, rec);
|
|
752
|
+
// WP-3 §9 S-3: the sync attach throw (missing dir) is now this async 'error' — same shape
|
|
753
|
+
// as the catch it replaced: keep the dir recorded and report, with NO reattach timer.
|
|
754
|
+
if (err && err.syncAttach) { this.emit('error', err); return; }
|
|
755
|
+
// Reattach on error with delay — same delay/shape as the sync path, but
|
|
756
|
+
// resumes via the async attach so it never touches sync fs.
|
|
737
757
|
try { watcher.close(); } catch { /* expected: the watcher already errored, close() may throw again harmlessly */ }
|
|
738
758
|
if (!this._stopped) {
|
|
739
759
|
setTimeout(() => {
|
|
@@ -743,9 +763,8 @@ class JsonlWatcher extends EventEmitter {
|
|
|
743
763
|
});
|
|
744
764
|
|
|
745
765
|
this._watchers.set(watchKey, watcher);
|
|
746
|
-
}
|
|
747
|
-
this.
|
|
748
|
-
this.emit('error', e);
|
|
766
|
+
} finally {
|
|
767
|
+
this._attaching.delete(watchKey);
|
|
749
768
|
}
|
|
750
769
|
}
|
|
751
770
|
|
|
@@ -832,7 +851,7 @@ class JsonlWatcher extends EventEmitter {
|
|
|
832
851
|
}
|
|
833
852
|
|
|
834
853
|
_ensureWatchedDir(root, dir, watchKey, projectEntry) {
|
|
835
|
-
if (this._stopped || this._watchers.has(watchKey)) return;
|
|
854
|
+
if (this._stopped || this._watchers.has(watchKey) || this._attaching.has(watchKey)) return; // WP-1b: an async attach owns it
|
|
836
855
|
const rec = { dir, root, projectEntry };
|
|
837
856
|
|
|
838
857
|
// Reconcile against any prior _knownFiles entries for this dir. On a
|
|
@@ -899,33 +918,30 @@ class JsonlWatcher extends EventEmitter {
|
|
|
899
918
|
if (MAINLOOP_JOB_SPANS_ENABLED) runSync('fs-watcher:attach-walk', attachWalk);
|
|
900
919
|
else attachWalk();
|
|
901
920
|
|
|
902
|
-
// Set up
|
|
921
|
+
// Set up the watcher (non-recursive — one watcher per directory). The
|
|
903
922
|
// actual symlink check happens in _handleFileEvent so that a file that
|
|
904
923
|
// is later replaced by a symlink is still filtered.
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
watcher.on('error', (err) => {
|
|
915
|
-
// Reattach on error with delay
|
|
916
|
-
this._watchers.delete(watchKey);
|
|
917
|
-
this._watchedDirs.set(watchKey, rec);
|
|
918
|
-
try { watcher.close(); } catch {}
|
|
919
|
-
if (!this._stopped) {
|
|
920
|
-
setTimeout(() => this._ensureWatchedDir(root, dir, watchKey, projectEntry), REATTACH_DELAY_MS);
|
|
921
|
-
}
|
|
922
|
-
});
|
|
924
|
+
this._watchedDirs.set(watchKey, rec);
|
|
925
|
+
const watcher = this._watch(dir, {}, (eventType, filename) => {
|
|
926
|
+
if (!filename || this._stopped) return;
|
|
927
|
+
if (!filename.endsWith('.jsonl') && !filename.endsWith('.jsonl.bak')) return;
|
|
928
|
+
const filePath = path.join(dir, filename);
|
|
929
|
+
this._debouncedHandle(filePath, dir, watchKey);
|
|
930
|
+
});
|
|
923
931
|
|
|
924
|
-
|
|
925
|
-
|
|
932
|
+
watcher.on('error', (err) => {
|
|
933
|
+
this._watchers.delete(watchKey);
|
|
926
934
|
this._watchedDirs.set(watchKey, rec);
|
|
927
|
-
|
|
928
|
-
|
|
935
|
+
// WP-3 §9 S-4: the sync attach throw arrives here now — the try/catch it replaced, no timer.
|
|
936
|
+
if (err && err.syncAttach) { this.emit('error', err); return; }
|
|
937
|
+
// Reattach on error with delay
|
|
938
|
+
try { watcher.close(); } catch { /* expected: the watcher already errored, close() may throw again harmlessly */ }
|
|
939
|
+
if (!this._stopped) {
|
|
940
|
+
setTimeout(() => this._ensureWatchedDir(root, dir, watchKey, projectEntry), REATTACH_DELAY_MS);
|
|
941
|
+
}
|
|
942
|
+
});
|
|
943
|
+
|
|
944
|
+
this._watchers.set(watchKey, watcher);
|
|
929
945
|
}
|
|
930
946
|
|
|
931
947
|
// Async so the readdir/lstat run on libuv's threadpool, NOT the main event loop. On a primary
|
|
@@ -2,12 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
const NATIVE_ORIGINS = new Set(['native-codex', 'native-claude', 'native-opencode', 'native-gemini']);
|
|
4
4
|
const KNOWN_ORIGINS = new Set([...NATIVE_ORIGINS, 'routed-proxy', 'portable-transcript']);
|
|
5
|
-
// S-3: the adapter/schema ids and the
|
|
5
|
+
// S-3: the adapter/schema ids and the non-elevated cyber program set come from the one table. A
|
|
6
|
+
// Codex version is recorded provenance only (WP-0): the gates below refuse a missing/blank version
|
|
7
|
+
// string but never compare it against a qualified-version table, range or floor.
|
|
6
8
|
const { CODEX_CONTROL_ADAPTER, CODEX_COMPACTION_SCHEMA_RECEIPT, CODEX_PRE_TURN_COMPACTION_SCHEMA,
|
|
7
|
-
|
|
9
|
+
isNonElevatedCyberAccessProgram } = require('./codex-harness-qualification');
|
|
10
|
+
// D-4 (docs/codex-0164-historical-tools-safe-handoff-2026-10-27.html §5): the ONE versioned
|
|
11
|
+
// proxy-filter receipt. A cross-provider Codex target may carry historical tool declarations only
|
|
12
|
+
// when its durable recipe proves the local authenticated proxy launch path issued this exact receipt.
|
|
13
|
+
const { CODEX_HISTORICAL_TOOLS_FILTER_RECEIPT } = require('./model-proxy/historical-tool-declarations');
|
|
8
14
|
// S-4: the ONE portable-kind set. `native-media` is a message whose only non-text content parts are
|
|
9
15
|
// input_image parts (D-2); it is portable only to a Codex target, which is the planner's job below.
|
|
10
|
-
|
|
16
|
+
// `native-tool-declarations` is listed because its portability is CONDITIONAL, and the condition is
|
|
17
|
+
// evaluated below (routed Codex target plus the exact durable receipt); membership here only stops
|
|
18
|
+
// the generic `history_kind_unsupported` refusal from firing first.
|
|
19
|
+
// D-7 (docs/codex-upgrades-without-version-approval-2026-10-28.html §configuration-followup):
|
|
20
|
+
// `native-configuration-update` is the harness-authored reasoning-configuration control record.
|
|
21
|
+
// Codex keeps it for the native OpenAI models that advertise support and drops it from the outgoing
|
|
22
|
+
// request copy for non-OpenAI / unsupported-override targets -- not for all targets -- so it travels
|
|
23
|
+
// only to a Codex target; membership here keeps the generic refusal from firing first and the
|
|
24
|
+
// condition is evaluated below, exactly like `native-media`.
|
|
25
|
+
const PORTABLE_HISTORY_KINDS = new Set(['text', 'tool-pair', 'native-world-state', 'native-lifecycle',
|
|
26
|
+
'native-media', 'native-tool-declarations', 'native-configuration-update']);
|
|
11
27
|
// D-5/D-6 (docs/codex-lead-routed-switch-history-handoff-2026-10-21.html §6.2): the plaintext handoff
|
|
12
28
|
// the source model writes is injected into the forked target, so it consumes target context that the
|
|
13
29
|
// source estimate does not count. 32 KiB of summary text is 8_192 tokens at four characters each.
|
|
@@ -23,9 +39,10 @@ function inspectTransition({ sourceRecipe, targetRecipe, snapshot, signal } = {}
|
|
|
23
39
|
const targetProviderId = nonempty(targetRecipe.providerId);
|
|
24
40
|
const origin = nonempty(sourceRecipe.origin);
|
|
25
41
|
const codexHistorySource = origin === 'native-codex' || sourceRecipe.harness === 'codex-cli';
|
|
26
|
-
// D-3:
|
|
27
|
-
// rollout's CREATOR version (session_meta.cli_version, written once at creation) is
|
|
28
|
-
// of its later records, so it is
|
|
42
|
+
// D-3/WP-0: admission is per record (D-1) plus the adapter identity of the binary that performs
|
|
43
|
+
// the fork. The rollout's CREATOR version (session_meta.cli_version, written once at creation) is
|
|
44
|
+
// not the writer of its later records, so it is never a gate; the target wire version below is
|
|
45
|
+
// required provenance only, not membership in a qualified-version table.
|
|
29
46
|
const qualifiedCodexHistorySource = codexHistorySource
|
|
30
47
|
&& sourceRecipe.controlAdapterVersion === CODEX_CONTROL_ADAPTER;
|
|
31
48
|
if (!origin) return refuse('source_origin_unknown');
|
|
@@ -50,7 +67,7 @@ function inspectTransition({ sourceRecipe, targetRecipe, snapshot, signal } = {}
|
|
|
50
67
|
&& (!qualifiedCodexHistorySource
|
|
51
68
|
|| !isNonElevatedCyberAccessProgram(sourceRecipe.cyberAccessProgram)
|
|
52
69
|
|| !isNonElevatedCyberAccessProgram(snapshot.previousSettings?.cyberAccessProgram)
|
|
53
|
-
|| targetRecipe.harness !== 'codex-cli' || !
|
|
70
|
+
|| targetRecipe.harness !== 'codex-cli' || !nonempty(targetRecipe.harnessVersion))) {
|
|
54
71
|
return refuse('native_fork_adapter_unqualified', { sourceCheckpoint });
|
|
55
72
|
}
|
|
56
73
|
if (qualifiedCodexHistorySource) {
|
|
@@ -75,12 +92,36 @@ function inspectTransition({ sourceRecipe, targetRecipe, snapshot, signal } = {}
|
|
|
75
92
|
}
|
|
76
93
|
}
|
|
77
94
|
if (snapshot.opaque === true || has(snapshot.historyKinds, 'opaque')) return refuse('opaque_history_not_portable', { sourceCheckpoint });
|
|
95
|
+
// D-4: a persisted AdditionalTools declaration is its own kind and is portable ONLY to the local
|
|
96
|
+
// authenticated proxy Codex target whose durable recipe carries the exact versioned filter receipt
|
|
97
|
+
// (the target that proved it strips the source declaration before the upstream request). Anything
|
|
98
|
+
// else -- a non-Codex target, a route-less native target, or a missing/foreign receipt -- keeps a
|
|
99
|
+
// fixed refusal instead of forwarding an unexamined tool schema across providers. Same-provider
|
|
100
|
+
// native reuse was already decided above, and this never widens the generic opaque allowance.
|
|
101
|
+
// The name is deliberately in the `target_` refusal family so the existing user-facing sentence map
|
|
102
|
+
// (lib/session-model-action-preconditions.js, out of this package's file list) already names it.
|
|
103
|
+
if (has(snapshot.historyKinds, 'native-tool-declarations')
|
|
104
|
+
&& !(targetRecipe.harness === 'codex-cli' && nonempty(targetRecipe.routeId)
|
|
105
|
+
&& targetRecipe.historyToolFilterReceipt === CODEX_HISTORICAL_TOOLS_FILTER_RECEIPT)) {
|
|
106
|
+
return refuse('target_tool_declarations_filter_unqualified', { sourceCheckpoint });
|
|
107
|
+
}
|
|
78
108
|
// D-2: native media is portable exactly when the target is Codex, which strips images the target
|
|
79
109
|
// cannot accept (core/src/context_manager/normalize.rs:346 @ rust-v0.163.0). A non-Codex target
|
|
80
110
|
// gets a plain refusal instead of a silent rewrite of the source history.
|
|
81
111
|
if (has(snapshot.historyKinds, 'native-media') && targetRecipe.harness !== 'codex-cli') {
|
|
82
112
|
return refuse('history_media_not_portable', { sourceCheckpoint });
|
|
83
113
|
}
|
|
114
|
+
// D-7: the harness-authored reasoning-configuration control record is portable exactly when the
|
|
115
|
+
// target is Codex: the only harness that produced it, and the one whose request copy drops this
|
|
116
|
+
// item before the provider on non-OpenAI / unsupported-override targets
|
|
117
|
+
// (core/src/client.rs:596-599,935-939 @ rust-v0.165.0), so a routed Codex target never forwards
|
|
118
|
+
// it upstream. Any other target keeps a named refusal rather than a silent rewrite of the source
|
|
119
|
+
// history, and no replacement-history form of this record is accepted: no producer of one has been
|
|
120
|
+
// observed. The same-provider native reuse above already decided the matching case, and the
|
|
121
|
+
// earlier source/harness gates are untouched, so they can still refuse first.
|
|
122
|
+
if (has(snapshot.historyKinds, 'native-configuration-update') && targetRecipe.harness !== 'codex-cli') {
|
|
123
|
+
return refuse('history_configuration_not_portable', { sourceCheckpoint });
|
|
124
|
+
}
|
|
84
125
|
// D-5/D-6: a sealed `compaction` item is carried by a plaintext handoff the SOURCE model writes
|
|
85
126
|
// before the switch. Exactly one sealed digest can be covered, and only the native openai source
|
|
86
127
|
// (whose own model can be asked to write it) can produce one; everything else keeps a named refusal.
|
|
@@ -114,12 +155,12 @@ function inspectTransition({ sourceRecipe, targetRecipe, snapshot, signal } = {}
|
|
|
114
155
|
&& targetRecipe.compactHashStatus === 'known-absent-with-native-schema-receipt'
|
|
115
156
|
&& targetRecipe.compactHashSchemaReceipt === CODEX_COMPACTION_SCHEMA_RECEIPT
|
|
116
157
|
&& /^[a-f0-9]{64}$/.test(String(targetRecipe.catalogSha256 || ''))
|
|
117
|
-
&& targetRecipe.harness === 'codex-cli' &&
|
|
158
|
+
&& targetRecipe.harness === 'codex-cli' && !!nonempty(targetRecipe.harnessVersion);
|
|
118
159
|
const targetHashKnownPresent = targetHash !== ''
|
|
119
160
|
&& targetRecipe.compactHashStatus === 'known-present-with-native-schema-receipt'
|
|
120
161
|
&& targetRecipe.compactHashSchemaReceipt === CODEX_COMPACTION_SCHEMA_RECEIPT
|
|
121
162
|
&& /^[a-f0-9]{64}$/.test(String(targetRecipe.catalogSha256 || ''))
|
|
122
|
-
&& targetRecipe.harness === 'codex-cli' &&
|
|
163
|
+
&& targetRecipe.harness === 'codex-cli' && !!nonempty(targetRecipe.harnessVersion);
|
|
123
164
|
if (!targetHashKnownAbsent && !targetHashKnownPresent) return refuse('target_compaction_hash_unknown', { sourceCheckpoint });
|
|
124
165
|
// In pinned Codex 0.162.1, compaction for a hash change is triggered only
|
|
125
166
|
// when both turns declare hashes and those hashes differ. A qualified
|
|
@@ -54,7 +54,7 @@ const {
|
|
|
54
54
|
MIRROR_RIGHTSIZE_ENABLED,
|
|
55
55
|
normalizeSnapshotScrollbackRows,
|
|
56
56
|
} = require('./terminal-snapshot-options');
|
|
57
|
-
const { createFrameEmitter
|
|
57
|
+
const { createFrameEmitter } = require('./state-sync/frame-emitter'); // WP-1: EMPTY_GRID/computeAnsiPatch moved into terminal-bootstrap.js's keyframeBootstrapAnsi (one diff core, one empty grid)
|
|
58
58
|
const HEADLESS_VIEWPORT_CONTRACT_VERSION = 2; // WP-1/D-1: getViewportText wire-contract version
|
|
59
59
|
const { noteOutput: _stateSyncNoteOutput, isFlooding: _stateSyncIsFlooding, settleDelay: _stateSyncSettleDelay } = require('./state-sync/frame-rate');
|
|
60
60
|
const { serializeRow, serializeRowState, wrapRowAnsi, ROW_DIGEST_ALGORITHM } = require('./state-sync/row-serializer');
|
|
@@ -75,7 +75,7 @@ const { composerSnapshotFromTerm, boundedBufferTokens } = require('./image-compo
|
|
|
75
75
|
const { createErrorSink } = require('./error-sink');
|
|
76
76
|
const { createPendingClientRegistry } = require('./state-sync/pending-client-registry');
|
|
77
77
|
const { createApprovalTrailingRecheck } = require('./approval-trailing-recheck');
|
|
78
|
-
const { composeTerminalBootstrapResult, truncateBootstrapAnsi } = require('./terminal-bootstrap');
|
|
78
|
+
const { composeTerminalBootstrapResult, truncateBootstrapAnsi, keyframeBootstrapAnsi } = require('./terminal-bootstrap');
|
|
79
79
|
const { createInFlightOps } = require('./headless-inflight-ops'); // WP-0 (D-6): per-session in-flight op table
|
|
80
80
|
|
|
81
81
|
let postMessage = () => {};
|
|
@@ -214,6 +214,10 @@ function _deliverKeyframeRequest(msg) {
|
|
|
214
214
|
requestToken: String(msg.requestToken || '').slice(0, 128),
|
|
215
215
|
contentRecoveryEpoch: Math.max(0, Number(msg.contentRecoveryEpoch || 0)),
|
|
216
216
|
...(serverHealAttempt >= 1 ? { serverHealAttempt } : {}),
|
|
217
|
+
// WP-2 (D-6, §9 S-8): thread the client's committed baseline seq to the emitter when the relay
|
|
218
|
+
// carried a valid one; an absent haveSeq stays absent (the emitter then serves every request,
|
|
219
|
+
// exactly as before this WP).
|
|
220
|
+
...(Number.isSafeInteger(msg.haveSeq) && msg.haveSeq >= 0 ? { haveSeq: msg.haveSeq } : {}),
|
|
217
221
|
};
|
|
218
222
|
const delivered = _stateSyncEmitter.onKeyframeReq(msg.sessionId, reqCtx);
|
|
219
223
|
// WP-2 (state-sync-delivery-path-reset-2026-09-18.html §5): onKeyframeReq never refuses and
|
|
@@ -438,6 +442,19 @@ const _stateSyncEmitter = createFrameEmitter({
|
|
|
438
442
|
if (!ids) return;
|
|
439
443
|
_noteWorkerTypingStage(id, ids, 'emit-refused', { gate, ...fields });
|
|
440
444
|
},
|
|
445
|
+
// WP-0 (S-11, docs/state-sync-pipelined-deltas-echo-never-waits-for-ack-2026-10-31.html §5 D-5,
|
|
446
|
+
// §9 S-11): report ONE matched ack's in-flight round trip -- the host leg measured in the emitter,
|
|
447
|
+
// the main leg passed through from the server's ledger lookup -- against whichever interactions
|
|
448
|
+
// are still pending on this session's mirror. `inflight-acked` is an OPTIONAL trace stage, so it
|
|
449
|
+
// can never move an interaction's completed boundary (lib/typing-interaction-trace.js).
|
|
450
|
+
onAcked: (id, info) => {
|
|
451
|
+
const ids = _pendingTypingInteractionIds(terminals.get(id));
|
|
452
|
+
if (!ids) return;
|
|
453
|
+
_noteWorkerTypingStage(id, ids, 'inflight-acked', {
|
|
454
|
+
inFlightSeq: info.seq, hostRttMs: info.hostRttMs, mainRttMs: info.mainRttMs,
|
|
455
|
+
windowFrames: info.windowFrames,
|
|
456
|
+
});
|
|
457
|
+
},
|
|
441
458
|
// WP-5 (§9 S-8/S-9): surface the no-audience stand-down latch in ctm.log the same way the W5c
|
|
442
459
|
// ack-stall warning below does (console.warn from this worker is captured like any other log line).
|
|
443
460
|
log: (event, data) => {
|
|
@@ -477,25 +494,81 @@ const _stateSyncPendingClearScrollback = new Set(); // sessionIds whose next fra
|
|
|
477
494
|
// Lost-ack watchdog: the emitter's diff base only advances on a client ACK, so a frame whose ACK never
|
|
478
495
|
// reaches the worker would pin the session in-flight forever and freeze its terminal. Periodically sweep
|
|
479
496
|
// for frames stuck in flight past STATE_SYNC_INFLIGHT_STALL_MS and force a fresh keyframe (full repaint).
|
|
497
|
+
// WP-2 (D-5, docs/remote-relay-ack-rate-limit-typing-delay-2026-10-28.html §5 D-5, §9 S-10): the stall
|
|
498
|
+
// is now PER DELIVERY — each delivery's own measured ack RTT sizes its own watchdog (`stallMsFor`,
|
|
499
|
+
// RFC 6298 §2 SRTT/RTTVAR). This constant is now the CEILING (unchanged value, unchanged env
|
|
500
|
+
// override), and the floor is the private-low-RTT-path bound below (mosh MIN_RTO intuition).
|
|
480
501
|
const STATE_SYNC_INFLIGHT_STALL_MS = Math.max(
|
|
481
502
|
STATE_SYNC_FRAME_MAX_MS * 4,
|
|
482
503
|
Number(process.env.CTM_STATE_SYNC_INFLIGHT_STALL_MS) || 3000,
|
|
483
504
|
);
|
|
484
|
-
const
|
|
505
|
+
const STATE_SYNC_INFLIGHT_STALL_FLOOR_MS = Math.max(50, Number(require('./env-flags').intFlag('CTM_STATE_SYNC_INFLIGHT_STALL_FLOOR_MS', 250)));
|
|
506
|
+
// The recovery sweep runs every 100 ms (was max(250, stall/3) = 1000 ms), so an adaptive stall at the
|
|
507
|
+
// 250 ms floor still recovers within 350 ms of the send instead of 3–4 s.
|
|
508
|
+
const STATE_SYNC_INFLIGHT_SWEEP_MS = 100;
|
|
485
509
|
// W5c: a session whose client ack stream is dead gets NO benefit from sweepStalledInFlight's
|
|
486
510
|
// per-frame recovery (it keeps re-arming inFlightAt every sweep, forever masking the stall) — the
|
|
487
511
|
// browser mirror can freeze for minutes with zero console output (production incident 2026-07-12).
|
|
488
|
-
// Query listStalledSessions
|
|
512
|
+
// Query listStalledSessions on its own (unchanged) 1000 ms cadence and surface a throttled warning.
|
|
513
|
+
const STATE_SYNC_STALL_LIST_MS = 1000;
|
|
489
514
|
const _stalledSessionWarnings = createStallWarningRegistry();
|
|
490
515
|
const STATE_SYNC_STALL_WARN_THROTTLE_MS = 60000;
|
|
516
|
+
|
|
517
|
+
// WP-0 (D-4c, docs/remote-relay-ack-rate-limit-typing-delay-2026-10-28.html §5 D-4c, §9 S-10):
|
|
518
|
+
// every lost-ack recovery the sweep performs is reported to main, throttled per session per 30 s
|
|
519
|
+
// with the count accumulated in the window (the first recovery reports immediately, mirroring
|
|
520
|
+
// lib/remote-machine-link.js's _noteUndelivered). Diagnostics only — the recovery itself has
|
|
521
|
+
// already happened when this runs.
|
|
522
|
+
const STATE_SYNC_INFLIGHT_RECOVERED_THROTTLE_MS = 30000;
|
|
523
|
+
const _inflightRecovered = new Map(); // sessionId -> { count, hiddenCount, inFlightKind, lastInFlightAgeMs, stallMs, lastReportMs }
|
|
524
|
+
function _noteInflightRecovered(sessionId, info) {
|
|
525
|
+
if (!sessionId) return;
|
|
526
|
+
const now = Date.now();
|
|
527
|
+
let entry = _inflightRecovered.get(sessionId);
|
|
528
|
+
if (!entry) {
|
|
529
|
+
entry = { count: 0, hiddenCount: 0, inFlightKind: '', lastInFlightAgeMs: 0, stallMs: 0, lastReportMs: null };
|
|
530
|
+
_inflightRecovered.set(sessionId, entry);
|
|
531
|
+
}
|
|
532
|
+
entry.count += 1;
|
|
533
|
+
entry.hiddenCount += info && info.hidden ? 1 : 0;
|
|
534
|
+
entry.inFlightKind = String((info && info.inFlightKind) || '');
|
|
535
|
+
entry.lastInFlightAgeMs = Number((info && info.inFlightAgeMs) || 0);
|
|
536
|
+
entry.stallMs = Number((info && info.stallMs) || 0);
|
|
537
|
+
if (entry.lastReportMs != null && now - entry.lastReportMs < STATE_SYNC_INFLIGHT_RECOVERED_THROTTLE_MS) return;
|
|
538
|
+
postMessage({
|
|
539
|
+
type: 'state-sync-inflight-recovered',
|
|
540
|
+
sessionId,
|
|
541
|
+
count: entry.count,
|
|
542
|
+
inFlightKind: entry.inFlightKind,
|
|
543
|
+
lastInFlightAgeMs: entry.lastInFlightAgeMs,
|
|
544
|
+
stallMs: entry.stallMs,
|
|
545
|
+
hiddenCount: entry.hiddenCount,
|
|
546
|
+
});
|
|
547
|
+
entry.count = 0;
|
|
548
|
+
entry.hiddenCount = 0;
|
|
549
|
+
entry.lastReportMs = now;
|
|
550
|
+
}
|
|
551
|
+
|
|
491
552
|
if (_stateSyncEmitter) {
|
|
492
|
-
|
|
553
|
+
// WP-2 (D-5, §9 S-10): the recovery sweep runs on its own 100 ms cadence so a per-delivery stall at
|
|
554
|
+
// the 250 ms floor is noticed within one sweep of elapsing (recovered ≤ 350 ms after send), not on
|
|
555
|
+
// the old 1000 ms tick. `stallMs` is the ceiling; `floorMs` bounds a fast delivery from below.
|
|
556
|
+
const _recoverySweep = setInterval(() => {
|
|
493
557
|
try {
|
|
494
558
|
_stateSyncEmitter.sweepStalledInFlight(Date.now(), {
|
|
495
559
|
stallMs: STATE_SYNC_INFLIGHT_STALL_MS,
|
|
560
|
+
floorMs: STATE_SYNC_INFLIGHT_STALL_FLOOR_MS,
|
|
496
561
|
hasViewers: (id) => _mirrorVisibility(id) === 'visible',
|
|
562
|
+
onRecovered: (id, info) => _noteInflightRecovered(id, info),
|
|
497
563
|
});
|
|
498
564
|
} catch {}
|
|
565
|
+
}, STATE_SYNC_INFLIGHT_SWEEP_MS);
|
|
566
|
+
if (typeof _recoverySweep.unref === 'function') _recoverySweep.unref();
|
|
567
|
+
|
|
568
|
+
// W5c: listStalledSessions keeps its own (unchanged) 1000 ms cadence and 3×-ceiling warning
|
|
569
|
+
// semantics. A dead ack stream gets no benefit from the recovery sweep above (it keeps re-arming
|
|
570
|
+
// inFlightAt every sweep, forever masking the stall) — this is the separate, louder signal.
|
|
571
|
+
const _stalledList = setInterval(() => {
|
|
499
572
|
try {
|
|
500
573
|
const stalls = _stateSyncEmitter.listStalledSessions(Date.now(), { stallMs: STATE_SYNC_INFLIGHT_STALL_MS });
|
|
501
574
|
const now = Date.now();
|
|
@@ -540,8 +613,8 @@ if (_stateSyncEmitter) {
|
|
|
540
613
|
}
|
|
541
614
|
_stalledSessionWarnings.reconcile(activeWarningKeys);
|
|
542
615
|
} catch {}
|
|
543
|
-
},
|
|
544
|
-
if (typeof
|
|
616
|
+
}, STATE_SYNC_STALL_LIST_MS);
|
|
617
|
+
if (typeof _stalledList.unref === 'function') _stalledList.unref();
|
|
545
618
|
}
|
|
546
619
|
|
|
547
620
|
// Sessions mid-"repaint nudge": the server resizes the PTY+mirror to cols-1 (to force a SIGWINCH so a
|
|
@@ -1630,6 +1703,9 @@ async function handleMessage(msg) {
|
|
|
1630
1703
|
_stateSyncEmitter.onAck(msg.sessionId, Number(msg.seq || 0), {
|
|
1631
1704
|
clientId: msg.clientId || '', socketGeneration: Number(msg.socketGeneration || 0),
|
|
1632
1705
|
holdCatchUp,
|
|
1706
|
+
// WP-0 (S-11): the main process measured this ack's main-side round trip on the ledger's
|
|
1707
|
+
// own sentAt; -1 when it had no evidence. Report-only, forwarded to the typing trace.
|
|
1708
|
+
mainRttMs: Number.isFinite(Number(msg.mainRttMs)) ? Number(msg.mainRttMs) : -1,
|
|
1633
1709
|
});
|
|
1634
1710
|
if (holdCatchUp) _stateSyncScheduleHeldEmit(msg.sessionId);
|
|
1635
1711
|
}
|
|
@@ -1895,7 +1971,7 @@ async function handleMessage(msg) {
|
|
|
1895
1971
|
const keyframe = Number(msg.clientBufferLength || 0) > Number(entry.term.rows || 0);
|
|
1896
1972
|
if (keyframe) bootstrapMode = 'keyframe';
|
|
1897
1973
|
const sourceAnsi = keyframe
|
|
1898
|
-
?
|
|
1974
|
+
? keyframeBootstrapAnsi(extractGrid(msg.sessionId, { scrollbackRows: STATE_SYNC_SCROLLBACK_ROWS }))
|
|
1899
1975
|
: data;
|
|
1900
1976
|
// WP-3: byte-budget probe only (server.js builds the wire result from `bootstrap` below);
|
|
1901
1977
|
// gridRevision/surfaceEpoch are required by the schema, so 0 placeholders satisfy it.
|