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
|
@@ -9,8 +9,10 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Nothing here touches fs or sqlite: every read is injected through `deps`, so the whole
|
|
11
11
|
// short-circuit order is decidable in a pure unit test. The order is the design's:
|
|
12
|
-
// D-
|
|
13
|
-
//
|
|
12
|
+
// D-4a the lexical deny runs FIRST, before any filesystem call: a sensitive expanded path
|
|
13
|
+
// returns `sensitive-path` without a realpath/stat (2026-10-27 D-1, the Cortex fix)
|
|
14
|
+
// D-3 canonical file (realpath; missing / directory / non-regular → not-found)
|
|
15
|
+
// D-4b the deny list beats the grant on the canonical realpath too (the symlink case)
|
|
14
16
|
// D-5 only link-detector extensions
|
|
15
17
|
// E1 admitted Review Hub worktree → root = that worktree
|
|
16
18
|
// E2 the session's stored text mentions the path → root = dirname(real)
|
|
@@ -121,6 +123,12 @@ async function grantForAbsolutePath(p, deps) {
|
|
|
121
123
|
const sessionId = String(p?.sessionId || '');
|
|
122
124
|
if (!expanded || !path.isAbsolute(expanded) || !sessionId) return { ok: false, code: 'not-printed' };
|
|
123
125
|
|
|
126
|
+
// D-4: deny beats grant — the lexical form the user typed, decided BEFORE any filesystem
|
|
127
|
+
// call. A request for `~/.ssh/anything` is refused without a realpath/stat, so the server
|
|
128
|
+
// never touches a credential path on behalf of a link (and a test can assert the deny on a
|
|
129
|
+
// path that does not exist). The canonical form is re-checked after realpath below.
|
|
130
|
+
if (isSensitive(expanded)) return { ok: false, code: 'sensitive-path' };
|
|
131
|
+
|
|
124
132
|
// D-3: decide on the canonical file, never on the request string.
|
|
125
133
|
let real;
|
|
126
134
|
try {
|
|
@@ -137,8 +145,9 @@ async function grantForAbsolutePath(p, deps) {
|
|
|
137
145
|
}
|
|
138
146
|
if (!stat || typeof stat.isFile !== 'function' || !stat.isFile()) return { ok: false, code: 'not-found' };
|
|
139
147
|
|
|
140
|
-
// D-4: deny beats grant — the canonical target
|
|
141
|
-
|
|
148
|
+
// D-4: deny beats grant — the canonical target (the lexical form was checked above, before
|
|
149
|
+
// realpath, so a symlink whose target is a credential path is still refused here).
|
|
150
|
+
if (isSensitive(real)) return { ok: false, code: 'sensitive-path' };
|
|
142
151
|
|
|
143
152
|
// D-5: only what the terminal could have linked.
|
|
144
153
|
if (!GRANT_EXTENSIONS.includes(path.extname(real).slice(1).toLowerCase())) {
|
|
@@ -91,6 +91,14 @@ function normalizeBackgroundTask(task) {
|
|
|
91
91
|
|
|
92
92
|
function bgCapFor(type) { return type === 'shell' ? BG_SHELL_MAX_MS : BG_DECLARED_MAX_MS; }
|
|
93
93
|
|
|
94
|
+
// R4/WP-1: the lifetime a launcher declared inside its own command line (`bin/bounded.js --deadline-ms N -- ...`).
|
|
95
|
+
// Read from the label only; nothing here parses, signals or inspects a process.
|
|
96
|
+
const BG_DEADLINE_RE = /\bbin\/bounded\.js\s+--deadline-ms\s+(\d+)/;
|
|
97
|
+
function bgDeadlineMsOf(label) {
|
|
98
|
+
const m = BG_DEADLINE_RE.exec(String(label || ''));
|
|
99
|
+
return m ? Number(m[1]) : null;
|
|
100
|
+
}
|
|
101
|
+
|
|
94
102
|
class SessionStatusLedger {
|
|
95
103
|
constructor({
|
|
96
104
|
sessionId = '', providerId = '',
|
|
@@ -158,7 +166,18 @@ class SessionStatusLedger {
|
|
|
158
166
|
if (now < e.expiresAt) continue;
|
|
159
167
|
this.background.delete(id);
|
|
160
168
|
if (!e.leased) {
|
|
161
|
-
|
|
169
|
+
// WP-4 (M1/M5): the expired row carries the terminal spell (quietForMs) and the declared deadline, so the
|
|
170
|
+
// census can count quiet hours and deadline coverage without re-deriving either from the entry.
|
|
171
|
+
const firstSeenAt = Number(e.firstSeenAt) || 0;
|
|
172
|
+
this._bgPending.push({
|
|
173
|
+
kind: 'status-background-expired',
|
|
174
|
+
payload: {
|
|
175
|
+
id: e.id, type: e.type,
|
|
176
|
+
ageMs: Math.max(0, now - firstSeenAt),
|
|
177
|
+
quietForMs: Math.max(0, now - (Number(e.activeAt) || firstSeenAt)),
|
|
178
|
+
deadlineMs: bgDeadlineMsOf(e.label),
|
|
179
|
+
},
|
|
180
|
+
});
|
|
162
181
|
}
|
|
163
182
|
}
|
|
164
183
|
if (!this.background.size) this._bgEvent = null;
|
|
@@ -169,6 +188,8 @@ class SessionStatusLedger {
|
|
|
169
188
|
this.backgroundObservedAt = at;
|
|
170
189
|
if (!Array.isArray(tasks)) { // D-6: absent is not empty
|
|
171
190
|
const hadDeclared = this.backgroundDeclared;
|
|
191
|
+
// R4/WP-1: an absent set is NOT an empty one (D-6) -- the CLI did not report the set, so the owner
|
|
192
|
+
// retired nothing and no entry here reports a retirement. Only a set that omits an entry does that.
|
|
172
193
|
this.background.clear();
|
|
173
194
|
this.backgroundDeclared = false;
|
|
174
195
|
// Noisy on older Claude Code (every Stop is null): only a declared->absent flip, or the ledger's first null, is a row.
|
|
@@ -191,8 +212,11 @@ class SessionStatusLedger {
|
|
|
191
212
|
...n, firstSeenAt, expiresAt: keep ? Math.max(Number(prev.expiresAt) || 0, cap) : cap, source: 'hook', count: 1,
|
|
192
213
|
renewedAt: keep ? (prev.renewedAt || 0) : 0, renewals: keep ? (prev.renewals || 0) : 0, capCrossedAt: keep ? (prev.capCrossedAt || 0) : 0,
|
|
193
214
|
activeAt: keep ? (prev.activeAt || firstSeenAt) : firstSeenAt, outSize: keep ? (prev.outSize || 0) : 0, outMtimeMs: keep ? (prev.outMtimeMs || 0) : 0,
|
|
215
|
+
nudgedAt: keep ? (prev.nudgedAt || 0) : 0, nudgeCount: keep ? (prev.nudgeCount || 0) : 0, firstNudgedAt: keep ? (prev.firstNudgedAt || 0) : 0,
|
|
194
216
|
});
|
|
195
217
|
}
|
|
218
|
+
// R4/WP-1: an entry the new set no longer names has left the set (M2 evidence), whether it ended or was omitted.
|
|
219
|
+
for (const [id, e] of this.background) if (!next.has(id)) this._bgRetire(e, 'snapshot', at);
|
|
196
220
|
let changed = next.size !== before || !this.backgroundDeclared;
|
|
197
221
|
if (!changed) for (const [id, e] of next) { const o = this.background.get(id); if (!o || o.type !== e.type || o.label !== e.label) { changed = true; break; } }
|
|
198
222
|
this.background = next;
|
|
@@ -200,6 +224,28 @@ class SessionStatusLedger {
|
|
|
200
224
|
if (changed) this._bgNote('snapshot', source || 'hook', before);
|
|
201
225
|
}
|
|
202
226
|
|
|
227
|
+
// R4/WP-1 (M2) + WP-4 (M1/M5): one row for every entry that leaves the set and is either a hook-declared shell
|
|
228
|
+
// (the class M1/M3/M5 measure) or was ever nudged. The payload carries the whole terminal spell — the age, the
|
|
229
|
+
// quiet spell, the declared deadline and the nudge tally — so a census computes M1/M2/M5 from diagnostics alone.
|
|
230
|
+
// Expiry is not a retirement (_bgExpire keeps its own row); only the three callers that delete an entry report here.
|
|
231
|
+
_bgRetire(entry, via, now) {
|
|
232
|
+
const firstSeenAt = Number(entry.firstSeenAt) || 0;
|
|
233
|
+
const firstNudgedAt = Number(entry.firstNudgedAt) || 0;
|
|
234
|
+
const hookShell = entry.type === 'shell' && entry.source === 'hook';
|
|
235
|
+
if (!hookShell && !(firstNudgedAt > 0)) return;
|
|
236
|
+
this._bgPending.push({
|
|
237
|
+
kind: 'status-background-retired',
|
|
238
|
+
payload: {
|
|
239
|
+
id: entry.id, type: entry.type, via,
|
|
240
|
+
ageMs: Math.max(0, now - firstSeenAt),
|
|
241
|
+
quietForMs: Math.max(0, now - (Number(entry.activeAt) || firstSeenAt)),
|
|
242
|
+
deadlineMs: bgDeadlineMsOf(entry.label),
|
|
243
|
+
nudgeCount: entry.nudgeCount || 0,
|
|
244
|
+
sinceFirstNudgeMs: firstNudgedAt > 0 ? Math.max(0, now - firstNudgedAt) : null,
|
|
245
|
+
},
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
203
249
|
_bgTask(meta, at, source) {
|
|
204
250
|
const op = meta.op;
|
|
205
251
|
const n = normalizeBackgroundTask(meta);
|
|
@@ -207,7 +253,10 @@ class SessionStatusLedger {
|
|
|
207
253
|
const before = this.background.size;
|
|
208
254
|
const src = meta.source || source || 'hook';
|
|
209
255
|
if (op === 'remove') {
|
|
210
|
-
|
|
256
|
+
const gone = this.background.get(n.id);
|
|
257
|
+
if (!gone || !this.background.delete(n.id)) return;
|
|
258
|
+
// R4/WP-1 (M2): a removed entry that was ever nudged reports its retirement.
|
|
259
|
+
this._bgRetire(gone, 'remove', at);
|
|
211
260
|
this.backgroundObservedAt = at;
|
|
212
261
|
this._bgNote('remove', src, before);
|
|
213
262
|
return;
|
|
@@ -225,6 +274,7 @@ class SessionStatusLedger {
|
|
|
225
274
|
source: src, count,
|
|
226
275
|
renewedAt: keep ? (prev.renewedAt || 0) : 0, renewals: keep ? (prev.renewals || 0) : 0, capCrossedAt: keep ? (prev.capCrossedAt || 0) : 0,
|
|
227
276
|
activeAt: keep ? (prev.activeAt || firstSeenAt) : firstSeenAt, outSize: keep ? (prev.outSize || 0) : 0, outMtimeMs: keep ? (prev.outMtimeMs || 0) : 0,
|
|
277
|
+
nudgedAt: keep ? (prev.nudgedAt || 0) : 0, nudgeCount: keep ? (prev.nudgeCount || 0) : 0, firstNudgedAt: keep ? (prev.firstNudgedAt || 0) : 0,
|
|
228
278
|
};
|
|
229
279
|
if (leaseMs) entry.leased = true;
|
|
230
280
|
this.background.set(n.id, entry);
|
|
@@ -247,6 +297,51 @@ class SessionStatusLedger {
|
|
|
247
297
|
return [...this.background.values()].some(e => !this.backgroundQuiet(e, now));
|
|
248
298
|
}
|
|
249
299
|
|
|
300
|
+
// R4 (WP-1): the entries the owner should be asked about at this Stop, oldest quiet spell first. Pure reader:
|
|
301
|
+
// it signals nothing, schedules nothing and mutates nothing (noteBackgroundNudged records the emission).
|
|
302
|
+
// Nudge n for one task fires once its quiet spell reaches BG_QUIET_MS * 2**n (30 min, 1 h, 2 h, 4 h ...), and
|
|
303
|
+
// activity after a nudge starts a fresh spell whose count is 0 again.
|
|
304
|
+
backgroundNudges(now = Date.now(), { max = 3 } = {}) {
|
|
305
|
+
this._bgExpire(now);
|
|
306
|
+
const due = [];
|
|
307
|
+
for (const e of this.background.values()) {
|
|
308
|
+
if (e.leased || !this.backgroundQuiet(e, now)) continue;
|
|
309
|
+
const activeAt = e.activeAt || 0;
|
|
310
|
+
const quietForMs = Math.max(0, now - activeAt);
|
|
311
|
+
// a fresh quiet spell (activity after the last nudge) resets the ladder
|
|
312
|
+
const count = (e.nudgedAt && activeAt > e.nudgedAt) ? 0 : (e.nudgeCount || 0);
|
|
313
|
+
if (quietForMs < BG_QUIET_MS * 2 ** count) continue;
|
|
314
|
+
if (count > 0 && now - e.nudgedAt < BG_QUIET_MS * 2 ** (count - 1)) continue;
|
|
315
|
+
due.push({
|
|
316
|
+
id: e.id, label: e.label, type: e.type, firstSeenAt: e.firstSeenAt, activeAt,
|
|
317
|
+
quietForMs, outSize: e.outSize || 0, outMtimeMs: e.outMtimeMs || 0, nudgeCount: count,
|
|
318
|
+
deadlineMs: bgDeadlineMsOf(e.label),
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
due.sort((a, b) => a.activeAt - b.activeAt);
|
|
322
|
+
return due.slice(0, Math.max(0, Number(max) || 0));
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// R4 (WP-1): record that the owner was asked about these ids. Bookkeeping only — no timer, no signal, no
|
|
326
|
+
// onBackgroundChange (the set itself did not move). Returns how many ids were noted.
|
|
327
|
+
noteBackgroundNudged(ids, now = Date.now()) {
|
|
328
|
+
let noted = 0;
|
|
329
|
+
for (const id of Array.isArray(ids) ? ids : []) {
|
|
330
|
+
const e = this.background.get(id);
|
|
331
|
+
if (!e) continue;
|
|
332
|
+
if (e.nudgedAt && (e.activeAt || 0) > e.nudgedAt) e.nudgeCount = 0; // a fresh spell restarts the ladder
|
|
333
|
+
e.nudgeCount = (e.nudgeCount || 0) + 1;
|
|
334
|
+
e.nudgedAt = now;
|
|
335
|
+
e.firstNudgedAt ||= now;
|
|
336
|
+
this._bgPending.push({
|
|
337
|
+
kind: 'status-background-nudged',
|
|
338
|
+
payload: { id: e.id, nudgeCount: e.nudgeCount, quietForMs: Math.max(0, now - (e.activeAt || 0)), ageMs: Math.max(0, now - e.firstSeenAt) },
|
|
339
|
+
});
|
|
340
|
+
noted += 1;
|
|
341
|
+
}
|
|
342
|
+
return noted;
|
|
343
|
+
}
|
|
344
|
+
|
|
250
345
|
backgroundView(now = Date.now()) {
|
|
251
346
|
this._bgExpire(now);
|
|
252
347
|
if (!this.background.size) return null;
|
|
@@ -273,6 +368,7 @@ class SessionStatusLedger {
|
|
|
273
368
|
id: e.id, type: e.type, label: e.label, firstSeenAt: e.firstSeenAt, expiresAt: e.expiresAt, source: e.source, count: e.count,
|
|
274
369
|
renewedAt: e.renewedAt || 0, renewals: e.renewals || 0, capCrossedAt: e.capCrossedAt || 0,
|
|
275
370
|
activeAt: e.activeAt || 0, outSize: e.outSize || 0, outMtimeMs: e.outMtimeMs || 0,
|
|
371
|
+
nudgedAt: Number(e.nudgedAt) || 0, nudgeCount: Number(e.nudgeCount) || 0, firstNudgedAt: Number(e.firstNudgedAt) || 0,
|
|
276
372
|
});
|
|
277
373
|
}
|
|
278
374
|
return { declared: this.backgroundDeclared, observedAt: this.backgroundObservedAt, entries };
|
|
@@ -296,6 +392,7 @@ class SessionStatusLedger {
|
|
|
296
392
|
source: String(raw.source || 'hook'), count: Math.max(1, Number(raw.count) || 1),
|
|
297
393
|
renewedAt: Number(raw.renewedAt) || 0, renewals: Math.max(0, Number(raw.renewals) || 0), capCrossedAt: Number(raw.capCrossedAt) || 0,
|
|
298
394
|
activeAt: Number(raw.activeAt) > 0 ? Number(raw.activeAt) : now, outSize: Number(raw.outSize) || 0, outMtimeMs: Number(raw.outMtimeMs) || 0,
|
|
395
|
+
nudgedAt: Number(raw.nudgedAt) || 0, nudgeCount: Number(raw.nudgeCount) || 0, firstNudgedAt: Number(raw.firstNudgedAt) || 0,
|
|
299
396
|
});
|
|
300
397
|
n += 1;
|
|
301
398
|
}
|
|
@@ -311,7 +408,12 @@ class SessionStatusLedger {
|
|
|
311
408
|
dropBackground(predicate, now = Date.now(), reason = '') {
|
|
312
409
|
this._bgExpire(now);
|
|
313
410
|
const before = this.background.size;
|
|
314
|
-
for (const [id, e] of this.background)
|
|
411
|
+
for (const [id, e] of this.background) {
|
|
412
|
+
if (!predicate(e)) continue;
|
|
413
|
+
this.background.delete(id);
|
|
414
|
+
// R4/WP-1 (M2): a dropped entry that was ever nudged reports its retirement.
|
|
415
|
+
this._bgRetire(e, 'drop', now);
|
|
416
|
+
}
|
|
315
417
|
const removed = before - this.background.size;
|
|
316
418
|
if (removed) {
|
|
317
419
|
this.backgroundObservedAt = now;
|
|
@@ -618,4 +720,4 @@ class SessionStatusLedger {
|
|
|
618
720
|
}
|
|
619
721
|
}
|
|
620
722
|
|
|
621
|
-
module.exports = { SessionStatusLedger, EVIDENCE, AGENT_OUTPUT, PROVIDER_STATUS_POLICY, BG_END_STATUSES, BG_SHELL_MAX_MS, BG_QUIET_MS, BG_DECLARED_MAX_MS, BG_LABEL_MAX, normalizeBackgroundTask };
|
|
723
|
+
module.exports = { SessionStatusLedger, EVIDENCE, AGENT_OUTPUT, PROVIDER_STATUS_POLICY, BG_END_STATUSES, BG_SHELL_MAX_MS, BG_QUIET_MS, BG_DECLARED_MAX_MS, BG_LABEL_MAX, normalizeBackgroundTask, bgDeadlineMsOf };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// WP-4 of docs/deepseek-harness-acp-agent-2026-10-28.html (§5 D-4).
|
|
4
|
+
//
|
|
5
|
+
// The one predicate for "this session speaks the Agent Client Protocol on pipes and has no PTY"
|
|
6
|
+
// (today the DeepSeek Harness). Required from the prompt lane (lib/acp/dsh-prompt-lane.js), the
|
|
7
|
+
// snapshot redirect (lib/ws/state-sync.js, S-39) and server.js's approval-executor seam.
|
|
8
|
+
//
|
|
9
|
+
// PTY-shaped status code (lib/session-status-runtime.js) deliberately does NOT skip ACP sessions:
|
|
10
|
+
// they own a status ledger fed by turn evidence, which is how their chip reads running/needs-you/idle.
|
|
11
|
+
|
|
12
|
+
/** A session driven over the Agent Client Protocol (child on pipes, no terminal). */
|
|
13
|
+
function isAcpSession(session) {
|
|
14
|
+
return session?.transport === 'acp';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
module.exports = { isAcpSession };
|
|
@@ -2,6 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
const { parseWorkspaceIncarnation } = require('../../shared/storage/session-workspace-activity');
|
|
4
4
|
|
|
5
|
+
// Sorted-key stable JSON — the same 6-line function as lib/standup-incremental.js:5-10 (copied,
|
|
6
|
+
// not imported: that module is unrelated). Keys are sorted recursively so two structurally equal
|
|
7
|
+
// values compare equal as strings; arrays keep their order.
|
|
8
|
+
function stableStringify(value) {
|
|
9
|
+
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
10
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
|
|
11
|
+
const keys = Object.keys(value).sort();
|
|
12
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(',')}}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// WP-1 (§5 D-1/D-2 of docs/typing-delay-session-list-broadcast-storm-2026-11-01.html): the git
|
|
16
|
+
// FACTS of an observation, excluding exactly the two per-probe fields — `snapshotVersion` (the
|
|
17
|
+
// ordering counter incremented on every probe, runtime.js:137) and `collectedAt` (the probe
|
|
18
|
+
// time). Every other field stays in, because projectSessionWorkspaceTracking spreads the whole
|
|
19
|
+
// snapshot into the row (`git = { ...gitSnapshot, … }`), so any other field is visible on the
|
|
20
|
+
// wire and must count as a change. Null/non-object input has the empty signature, which is what
|
|
21
|
+
// makes the first installation of a session's snapshot a change.
|
|
22
|
+
function gitFactsSignature(git) {
|
|
23
|
+
if (!git || typeof git !== 'object') return '';
|
|
24
|
+
const { snapshotVersion, collectedAt, ...facts } = git;
|
|
25
|
+
return stableStringify(facts);
|
|
26
|
+
}
|
|
27
|
+
|
|
5
28
|
const GIT_STATES = ['checking', 'known', 'unknown', 'unavailable'];
|
|
6
29
|
const PROVENANCE = ['current-snapshot', 'last-observed', 'configured', 'unavailable'];
|
|
7
30
|
const UNAVAILABLE_CODES = ['WORKSPACE_IDENTITY_CHANGED', 'WORKSPACE_IDENTITY_UNAVAILABLE'];
|
|
@@ -41,7 +64,13 @@ function scopedErrorCode(gitSnapshot, tracking, selection) {
|
|
|
41
64
|
// so no consumer can read it as "still checking" or join it to another workspace.
|
|
42
65
|
// D-6: `checkoutProvenance` scopes the branch that may be shown; counts are only ever joined to
|
|
43
66
|
// a matching fresh snapshot, so one workspace's numbers can never accompany another selection.
|
|
44
|
-
|
|
67
|
+
//
|
|
68
|
+
// WP-3 (§6.3): the third argument is this session's landing-queue view, already computed by the
|
|
69
|
+
// caller (lib/landing/ctm-view.js is pure; the runtime owns the per-repo probe). It is gated on
|
|
70
|
+
// `git.freshness === 'fresh'` for the same reason every other count is: a stale or refused
|
|
71
|
+
// snapshot describes a workspace we can no longer prove this session is on, and a landing view
|
|
72
|
+
// quoting a branch from an unproven workspace is worse than no view at all.
|
|
73
|
+
function projectSessionWorkspaceTracking(tracking, gitSnapshot, landing = null) {
|
|
45
74
|
const selection = tracking?.selection || null;
|
|
46
75
|
if (!selection) return null;
|
|
47
76
|
const sameWorkspace = !!gitSnapshot && gitSnapshot.workspaceKey === selection.workspaceKey
|
|
@@ -94,14 +123,34 @@ function projectSessionWorkspaceTracking(tracking, gitSnapshot) {
|
|
|
94
123
|
workspaceIncarnation: selection.workspaceIncarnation,
|
|
95
124
|
selection,
|
|
96
125
|
git,
|
|
126
|
+
landing: git.freshness === 'fresh' && landing ? landing : null,
|
|
97
127
|
recentWorkspaceCount: (tracking.history || []).filter(item => item.actorRole === 'foreground').length,
|
|
98
128
|
history: (tracking.history || []).filter(item => item.actorRole === 'foreground').slice(0, 12),
|
|
99
129
|
};
|
|
100
130
|
}
|
|
101
|
-
|
|
131
|
+
|
|
132
|
+
// WP-3 (census 1e): the broadcast memo key. A landing-only change (the queue drained, a new pair
|
|
133
|
+
// appeared) moves nothing else in the row, so without this tuple the sessions frame would keep
|
|
134
|
+
// serving the previous memo and the chip would freeze. The view is built by a pure function with a
|
|
135
|
+
// fixed key order and a sorted `with` list, so this derived tuple — never the object itself — is
|
|
136
|
+
// what gets compared; it is stable across two runs over equal data.
|
|
137
|
+
function landingSignature(landing) {
|
|
138
|
+
if (!landing || typeof landing !== 'object') return null;
|
|
139
|
+
const radar = landing.radar && typeof landing.radar === 'object' ? landing.radar : {};
|
|
140
|
+
const with_ = Array.isArray(radar.with) ? radar.with : [];
|
|
141
|
+
return [landing.jobId ?? null, landing.state ?? null, landing.position ?? null, landing.total ?? null,
|
|
142
|
+
landing.eta_s ?? null, radar.max ?? null,
|
|
143
|
+
with_.map(entry => [entry?.session ?? null, entry?.branch ?? null, entry?.level ?? null])];
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// WP-1 (§5 D-2): the git part of the key is the FACTS signature, not `snapshotVersion`. With the
|
|
147
|
+
// per-probe counter in the key, every 30 s probe of an unchanged worktree missed the memo and
|
|
148
|
+
// rebuilt all 105 rows; the same signature the change test uses is what lets an unchanged row
|
|
149
|
+
// reuse its memo (the 1 s memo TTL still bounds clock-only fields).
|
|
150
|
+
function workspaceTrackingMemoSignature(tracking, git, landing) {
|
|
102
151
|
return tracking?.selection ? JSON.stringify([tracking.sessionEpoch, tracking.selectionVersion,
|
|
103
|
-
tracking.selection.workspaceKey, tracking.selection.workspaceIncarnation, git
|
|
104
|
-
git?.freshness, git?.state, git?.errorCode, git?.checkoutProvenance]) : '';
|
|
152
|
+
tracking.selection.workspaceKey, tracking.selection.workspaceIncarnation, gitFactsSignature(git),
|
|
153
|
+
git?.freshness, git?.state, git?.errorCode, git?.checkoutProvenance, landingSignature(landing)]) : '';
|
|
105
154
|
}
|
|
106
155
|
|
|
107
156
|
// D-6/AC-3c: a checkout is presented as CURRENT only from a matching fresh snapshot. A
|
|
@@ -175,4 +224,4 @@ function workspaceTrackingCompatibility(tracking) {
|
|
|
175
224
|
};
|
|
176
225
|
}
|
|
177
226
|
|
|
178
|
-
module.exports = { projectSessionWorkspaceTracking, workspaceTrackingMemoSignature, workspaceTrackingCompatibility };
|
|
227
|
+
module.exports = { projectSessionWorkspaceTracking, workspaceTrackingMemoSignature, workspaceTrackingCompatibility, landingSignature, gitFactsSignature };
|
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { parseWorkspaceIncarnation } = require('../../shared/storage/session-workspace-activity');
|
|
5
|
+
// WP-1 (§5 D-1): the change test compares git FACTS, not the per-probe ordering counter.
|
|
6
|
+
// No require cycle: session-workspace-projection.js requires only ../../shared/storage/*.
|
|
7
|
+
const { gitFactsSignature } = require('./session-workspace-projection');
|
|
8
|
+
// WP-3 (§6.3): the per-session landing view is a PURE function over the `land` CLI's own JSON —
|
|
9
|
+
// this runtime owns the probe that produces that JSON, the view module owns nothing but the shape.
|
|
10
|
+
const { landingForSession, landingStatusForRepo } = require('./landing/ctm-view');
|
|
5
11
|
|
|
6
12
|
// Activity moves only after the durable owner commits. Git reconciliation has its own version
|
|
7
13
|
// and never changes last-used activity.
|
|
@@ -56,6 +62,26 @@ function createSessionWorkspaceTrackingRuntime(deps = {}) {
|
|
|
56
62
|
const nativeDrain = new WeakMap();
|
|
57
63
|
const probes = new Map();
|
|
58
64
|
let snapshotVersion = 0;
|
|
65
|
+
// WP-3 (§5 D-1/D-2, §6.3, §8 WP-3): ONE landing probe per repository. The landing queue is
|
|
66
|
+
// per-repository state that only `bin/land.js` can read, so the runtime does not parse it — it
|
|
67
|
+
// hands a `{ repo, branches }` request to the injected probe (server.js wires the real async CLI
|
|
68
|
+
// child) and keeps the last payload. A probe for a repository that is already running is never
|
|
69
|
+
// started again: the in-flight promise is returned, so the request path and the sweep share one
|
|
70
|
+
// child per repository instead of racing two.
|
|
71
|
+
const runLandingProbe = typeof deps.runLandingProbe === 'function' ? deps.runLandingProbe : null;
|
|
72
|
+
const onLandingChanged = typeof deps.onLandingChanged === 'function' ? deps.onLandingChanged : null;
|
|
73
|
+
const landingSlots = new Map();
|
|
74
|
+
// A worktree path -> the key its probe actually resolved to (the git common dir the CLI printed).
|
|
75
|
+
// Sessions whose snapshot has no common dir (an unvalidated hydration) key on their worktree path
|
|
76
|
+
// until the first probe resolves it, and this alias is what keeps that first probe from becoming a
|
|
77
|
+
// second, parallel cache entry for the same repository.
|
|
78
|
+
const landingAliases = new Map();
|
|
79
|
+
const LANDING_PROBE_CAP = 64;
|
|
80
|
+
let landingSessions = [];
|
|
81
|
+
// repo key -> { branch -> [session id] }, rebuilt once per sweep so the per-session view is a map
|
|
82
|
+
// lookup instead of a scan of every live session (the payload projection runs once per session per
|
|
83
|
+
// broadcast, and a scan there would be quadratic in the session count).
|
|
84
|
+
let landingBranchIndex = new Map();
|
|
59
85
|
|
|
60
86
|
// §6: `git.errorCode` is a closed set. Raw Git/worker codes are mapped onto it so the
|
|
61
87
|
// projection never publishes an unbounded string.
|
|
@@ -132,7 +158,13 @@ function createSessionWorkspaceTrackingRuntime(deps = {}) {
|
|
|
132
158
|
const installed = { ...observation };
|
|
133
159
|
delete installed.refusedSelection;
|
|
134
160
|
state.gitSnapshot = installed;
|
|
135
|
-
|
|
161
|
+
// WP-1 (§5 D-1/D-3): `snapshotVersion` is the ORDERING key (the fence above), never the change
|
|
162
|
+
// key — it advances on every probe, so comparing it made every 30 s probe of an unchanged
|
|
163
|
+
// worktree a change and re-broadcast the whole session list. Compare the git facts instead
|
|
164
|
+
// (everything but `snapshotVersion`/`collectedAt`), plus `freshness`, which is probe-local
|
|
165
|
+
// state rather than an observation fact. The observation itself is still installed above.
|
|
166
|
+
const changed = gitFactsSignature(previous) !== gitFactsSignature(installed)
|
|
167
|
+
|| previous?.freshness !== observation.freshness;
|
|
136
168
|
if (changed) onChange(session);
|
|
137
169
|
return changed;
|
|
138
170
|
}
|
|
@@ -523,8 +555,188 @@ function createSessionWorkspaceTrackingRuntime(deps = {}) {
|
|
|
523
555
|
return reconcile(session, state, options);
|
|
524
556
|
}
|
|
525
557
|
|
|
558
|
+
// --- WP-3 (§5 D-1/D-2, §6.3, §8 WP-3): the landing probe cache --------------------------------
|
|
559
|
+
|
|
560
|
+
/**
|
|
561
|
+
* What CTM knows about this session's repository, or `null` when it cannot be PROVEN. A session
|
|
562
|
+
* whose git freshness is not `fresh` contributes nothing — the same gate the projection applies to
|
|
563
|
+
* the view itself, so the probe and the chip can never disagree about whose branch this is. No I/O:
|
|
564
|
+
* every field comes from the snapshot the sweep already installed.
|
|
565
|
+
*/
|
|
566
|
+
function describeSession(session) {
|
|
567
|
+
const state = session && typeof session === 'object' ? contexts.get(session) : null;
|
|
568
|
+
const git = state ? state.gitSnapshot : null;
|
|
569
|
+
if (!git || git.freshness !== 'fresh') return null;
|
|
570
|
+
const checkout = git.checkout || null;
|
|
571
|
+
const branch = checkout && typeof checkout.branch === 'string' ? checkout.branch : '';
|
|
572
|
+
if (!branch) return null;
|
|
573
|
+
const workspace = git.workspace || {};
|
|
574
|
+
const root = (typeof workspace.root === 'string' && workspace.root)
|
|
575
|
+
|| (typeof session.worktree_path === 'string' && session.worktree_path)
|
|
576
|
+
|| (typeof session.cwd === 'string' && session.cwd)
|
|
577
|
+
|| null;
|
|
578
|
+
if (!root) return null;
|
|
579
|
+
const commonDir = typeof workspace.commonDir === 'string' && workspace.commonDir ? workspace.commonDir : null;
|
|
580
|
+
return { id: String(session.id), branch, repo: commonDir || landingAliases.get(root) || root, path: root };
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const landingKeyFor = (repo) => landingAliases.get(repo) || repo;
|
|
584
|
+
|
|
585
|
+
function landingSlotFor(key) {
|
|
586
|
+
const existing = landingSlots.get(key);
|
|
587
|
+
if (existing) return existing;
|
|
588
|
+
if (landingSlots.size >= LANDING_PROBE_CAP) {
|
|
589
|
+
const idle = [...landingSlots].find(([, entry]) => !entry.pending);
|
|
590
|
+
// Every slot is in flight: keep the results already held and start nothing new. A probe that
|
|
591
|
+
// cannot be started is not a failure — the last payload stays, exactly like a failed probe's.
|
|
592
|
+
if (!idle) return null;
|
|
593
|
+
landingSlots.delete(idle[0]);
|
|
594
|
+
}
|
|
595
|
+
const slot = { path: key, root: null, branches: [], payload: null, error: null, at: 0, pending: null };
|
|
596
|
+
landingSlots.set(key, slot);
|
|
597
|
+
return slot;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function startLandingProbe(key, repoPath, branches) {
|
|
601
|
+
const slot = landingSlotFor(key);
|
|
602
|
+
if (!slot) {
|
|
603
|
+
return Promise.resolve({ ok: false, payload: null, error: { code: 'landing_probe_capacity', message: 'every landing probe slot is in flight' } });
|
|
604
|
+
}
|
|
605
|
+
// `key` is the repository IDENTITY (the git common dir, so every worktree of a repository shares
|
|
606
|
+
// one slot) while `repoPath` is the WORK TREE the CLI is run in. They are deliberately different
|
|
607
|
+
// values: the CLI refuses a bare git directory (`git rev-parse --show-toplevel` inside `.git`
|
|
608
|
+
// exits non-zero => `not_a_repo`), so probing the common dir would leave every chip permanently
|
|
609
|
+
// dark. Any work tree of the repository answers identically — the queue state lives under the
|
|
610
|
+
// common dir and the radar policy is read from the target branch tip.
|
|
611
|
+
slot.path = repoPath;
|
|
612
|
+
slot.root = repoPath;
|
|
613
|
+
slot.branches = branches;
|
|
614
|
+
// SINGLE-FLIGHT: a repository whose probe is still running is never probed again — the request
|
|
615
|
+
// path and the sweep join the child already in flight, and its result is the one that lands.
|
|
616
|
+
if (slot.pending) return slot.pending;
|
|
617
|
+
const pending = Promise.resolve()
|
|
618
|
+
.then(() => runLandingProbe({ repo: repoPath, branches }))
|
|
619
|
+
.then((result) => {
|
|
620
|
+
slot.at = now();
|
|
621
|
+
if (result && result.ok === true) {
|
|
622
|
+
const changed = JSON.stringify(slot.payload) !== JSON.stringify(result.payload || null);
|
|
623
|
+
slot.payload = result.payload || null;
|
|
624
|
+
// The sweep does not await the probe, so its broadcast went out with the previous payload;
|
|
625
|
+
// a changed payload must ask for one more (the memo still filters unchanged sessions).
|
|
626
|
+
if (changed && onLandingChanged) onLandingChanged(key);
|
|
627
|
+
slot.error = null;
|
|
628
|
+
const resolved = result.payload && typeof result.payload.repo === 'string' ? result.payload.repo : null;
|
|
629
|
+
// The CLI prints the repository's REAL common dir, which may differ from the key we probed
|
|
630
|
+
// with (a worktree path, or a snapshot that had no common dir yet): alias both directions so
|
|
631
|
+
// the next sweep keys on the same slot instead of starting a second cache entry.
|
|
632
|
+
if (resolved && resolved !== key) landingAliases.set(resolved, key);
|
|
633
|
+
if (repoPath !== key) landingAliases.set(repoPath, key);
|
|
634
|
+
} else {
|
|
635
|
+
// A failed probe says nothing new about the queue and keeps the last payload: an empty chip
|
|
636
|
+
// is worse than a slightly old one. The error is retained for the status route to report.
|
|
637
|
+
slot.error = (result && result.error) || { code: 'landing_probe_failed', message: 'the landing probe returned no result' };
|
|
638
|
+
}
|
|
639
|
+
return { ok: !!slot.payload, payload: slot.payload, error: slot.error };
|
|
640
|
+
})
|
|
641
|
+
.catch((error) => {
|
|
642
|
+
// The probe is injected; a rejection here is a wiring bug, not a CLI refusal. Record it and
|
|
643
|
+
// keep the previous payload rather than dropping the repository out of the cache.
|
|
644
|
+
slot.error = { code: (error && error.code) || 'landing_probe_failed', message: (error && error.message) || String(error) };
|
|
645
|
+
return { ok: !!slot.payload, payload: slot.payload, error: slot.error };
|
|
646
|
+
})
|
|
647
|
+
.finally(() => { if (slot.pending === pending) slot.pending = null; });
|
|
648
|
+
slot.pending = pending;
|
|
649
|
+
return pending;
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* The WORK TREE to run the CLI in for a repository keyed by `key`. A sweep always supplies the
|
|
654
|
+
* live session's own worktree root; the request path may be handed the git common dir instead
|
|
655
|
+
* (`?repo=<commonDir>`), and `dirname(<repo>/.git)` is that repository's primary work tree — the
|
|
656
|
+
* same rule lib/review-repo-admission.js uses to name a repository's root from its administrative
|
|
657
|
+
* directory. No such directory means the CLI refuses and the route answers `repo_unknown`.
|
|
658
|
+
*/
|
|
659
|
+
function landingRepoFor(key, repo) {
|
|
660
|
+
const slot = landingSlots.get(key);
|
|
661
|
+
if (slot && slot.root) return slot.root;
|
|
662
|
+
return path.basename(repo) === '.git' ? path.dirname(repo) : repo;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function landingBranchesFor(key) {
|
|
666
|
+
const branches = new Set();
|
|
667
|
+
for (const entry of landingSessions) {
|
|
668
|
+
if (landingKeyFor(entry.repo) === key) branches.add(entry.branch);
|
|
669
|
+
}
|
|
670
|
+
return [...branches];
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* One probe per repository, driven by the SAME sweep that just refreshed the per-session git
|
|
675
|
+
* snapshots — no new timer and no idle loop, so a repository with no live session on any branch is
|
|
676
|
+
* never probed at all. Errors are handled inside `startLandingProbe`; nothing here is awaited.
|
|
677
|
+
*/
|
|
678
|
+
function refreshLanding(sessions) {
|
|
679
|
+
const described = [];
|
|
680
|
+
for (const session of Array.isArray(sessions) ? sessions : []) {
|
|
681
|
+
const entry = describeSession(session);
|
|
682
|
+
if (entry) described.push(entry);
|
|
683
|
+
}
|
|
684
|
+
landingSessions = described;
|
|
685
|
+
const index = new Map();
|
|
686
|
+
const groups = new Map();
|
|
687
|
+
for (const entry of described) {
|
|
688
|
+
const key = landingKeyFor(entry.repo);
|
|
689
|
+
let byBranch = index.get(key);
|
|
690
|
+
if (!byBranch) { byBranch = {}; index.set(key, byBranch); }
|
|
691
|
+
if (!byBranch[entry.branch]) byBranch[entry.branch] = [];
|
|
692
|
+
byBranch[entry.branch].push(entry.id);
|
|
693
|
+
let group = groups.get(key);
|
|
694
|
+
if (!group) {
|
|
695
|
+
group = { key, repo: entry.path || entry.repo, branches: new Set() };
|
|
696
|
+
groups.set(key, group);
|
|
697
|
+
}
|
|
698
|
+
group.branches.add(entry.branch);
|
|
699
|
+
}
|
|
700
|
+
landingBranchIndex = index;
|
|
701
|
+
if (!runLandingProbe) return;
|
|
702
|
+
for (const group of groups.values()) startLandingProbe(group.key, group.repo, [...group.branches]);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/**
|
|
706
|
+
* The request path's probe: awaited, and single-flight with the sweep (same slot). `sessions`
|
|
707
|
+
* (optional) re-describes the live sessions first, so a route call cannot answer with a `with` list
|
|
708
|
+
* from a stale session set.
|
|
709
|
+
*/
|
|
710
|
+
function probeLanding(args = {}) {
|
|
711
|
+
if (Array.isArray(args.sessions)) refreshLanding(args.sessions);
|
|
712
|
+
const repo = typeof args.repo === 'string' && args.repo ? args.repo : null;
|
|
713
|
+
if (!repo) return Promise.resolve({ ok: false, payload: null, error: { code: 'repo_required', message: 'probeLanding needs a repository path' } });
|
|
714
|
+
if (!runLandingProbe) return Promise.resolve({ ok: false, payload: null, error: { code: 'landing_unavailable', message: 'no landing probe is wired on this instance' } });
|
|
715
|
+
const key = landingKeyFor(repo);
|
|
716
|
+
return startLandingProbe(key, landingRepoFor(key, repo), landingBranchesFor(key));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* The per-session view (§6.3) over the last payload for this session's repository. `null` when the
|
|
721
|
+
* repository has nothing to say OR its probe has not landed yet — the projection turns that into an
|
|
722
|
+
* absent chip, which is the honest state for "we have not read this queue".
|
|
723
|
+
*/
|
|
724
|
+
function landingView(session) {
|
|
725
|
+
const described = describeSession(session);
|
|
726
|
+
if (!described) return null;
|
|
727
|
+
const key = landingKeyFor(described.repo);
|
|
728
|
+
const slot = landingSlots.get(key);
|
|
729
|
+
if (!slot || !slot.payload) return null;
|
|
730
|
+
return landingForSession(slot.payload, {
|
|
731
|
+
sessionId: described.id,
|
|
732
|
+
branch: described.branch,
|
|
733
|
+
sessionsByBranch: landingBranchIndex.get(key) || {},
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
|
|
526
737
|
return { observe, observeNativeActivity, refresh, stateFor, snapshot: session => contexts.get(session)?.tracking || null,
|
|
527
738
|
gitSnapshot: session => contexts.get(session)?.gitSnapshot || null,
|
|
739
|
+
describeSession, refreshLanding, probeLanding, landingView,
|
|
528
740
|
bindSource: bindSource ? (input, options) => bindSource(input, options) : null };
|
|
529
741
|
}
|
|
530
742
|
|