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,105 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* WP-3 watch worker — design doc typing-delay-session-list-broadcast-storm-2026-11-01.html
|
|
4
|
+
* §5 D-4, §6 (WP-3), §8 WP-3, §9 S-3..S-8.
|
|
5
|
+
*
|
|
6
|
+
* Every `fs.watch()` directory handle CTM owns used to live on the main thread's libuv loop.
|
|
7
|
+
* On macOS libuv serves all of them from ONE FSEventStream per `uv_loop_t`, and
|
|
8
|
+
* `uv__fsevents_close` blocks the calling thread on `uv_sem_wait` until libuv's CF thread has
|
|
9
|
+
* destroyed + recreated + `FSEventStreamStart`ed that stream with every remaining path
|
|
10
|
+
* (src/unix/fsevents.c). With ~690 directory watchers open, one `close()` costs 0.4-1.6 s of
|
|
11
|
+
* RPC to fseventsd, on the thread that also has to echo keystrokes and paint frames — the
|
|
12
|
+
* 2026-11-01 23:52Z incident sample has 1,330/1,339 main-thread samples in
|
|
13
|
+
* `HandleWrap::Close -> uv__fsevents_close -> uv_sem_wait`.
|
|
14
|
+
*
|
|
15
|
+
* libuv keeps that CF state PER LOOP, so a `worker_threads` Worker gets its own stream and its
|
|
16
|
+
* own semaphore. This file runs inside that worker and owns every directory watcher; a blocked
|
|
17
|
+
* close then stalls only this thread.
|
|
18
|
+
*
|
|
19
|
+
* Protocol over `parentPort` (main → worker):
|
|
20
|
+
* { op: 'watch', id, dir } create one non-recursive watcher (persistent:false — the same
|
|
21
|
+
* contract the main thread's `fs.watch` callers used)
|
|
22
|
+
* { op: 'unwatch', id } close + forget the handle, then ack (see below)
|
|
23
|
+
* { op: 'shutdown' } stop processing; the main side terminates the worker, so the
|
|
24
|
+
* per-handle closes are NOT required at exit (§9 S-8: closes at
|
|
25
|
+
* exit are the ones the process reclaims for free)
|
|
26
|
+
* worker → main:
|
|
27
|
+
* { op: 'event', id, eventType, filename|null } one raw fs.watch callback
|
|
28
|
+
* { op: 'error', id, code, message, sync?: true } sync:true = `fs.watch` threw synchronously
|
|
29
|
+
* (ENOENT and friends) — the owner must NOT schedule a reattach
|
|
30
|
+
* timer for those, matching its old synchronous try/catch
|
|
31
|
+
* { op: 'unwatched', id } the handle is closed; the main thread's close() already returned
|
|
32
|
+
*
|
|
33
|
+
* The filename is stringified (or null) so it survives structured clone.
|
|
34
|
+
*/
|
|
35
|
+
const fs = require('node:fs');
|
|
36
|
+
const { parentPort } = require('node:worker_threads');
|
|
37
|
+
|
|
38
|
+
const handles = new Map(); // id -> fs.FSWatcher
|
|
39
|
+
let shuttingDown = false;
|
|
40
|
+
|
|
41
|
+
function post(message) {
|
|
42
|
+
if (!parentPort) return;
|
|
43
|
+
try {
|
|
44
|
+
parentPort.postMessage(message);
|
|
45
|
+
} catch {
|
|
46
|
+
/* expected: the port is already closed when the main thread tears this worker down */
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function errorFields(error) {
|
|
51
|
+
return {
|
|
52
|
+
code: (error && error.code) || '',
|
|
53
|
+
message: (error && error.message) || String(error),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function attach(id, dir) {
|
|
58
|
+
if (handles.has(id)) return;
|
|
59
|
+
let handle;
|
|
60
|
+
try {
|
|
61
|
+
handle = fs.watch(dir, { persistent: false }, (eventType, filename) => {
|
|
62
|
+
post({ op: 'event', id, eventType, filename: filename == null ? null : String(filename) });
|
|
63
|
+
});
|
|
64
|
+
} catch (error) {
|
|
65
|
+
post({ op: 'error', id, sync: true, ...errorFields(error) });
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// F-2 (clock-free tests r3): a synchronous try/catch is NOT an 'error' listener. libuv
|
|
69
|
+
// broadcasts one FSEventStreamStart failure (UV_EMFILE) to EVERY watcher in the loop, on the
|
|
70
|
+
// async path — without this listener the EventEmitter would throw out of the callback.
|
|
71
|
+
handle.on('error', (error) => {
|
|
72
|
+
handles.delete(id);
|
|
73
|
+
try {
|
|
74
|
+
handle.close();
|
|
75
|
+
} catch {
|
|
76
|
+
/* expected: an already-errored FSWatcher may throw again on close */
|
|
77
|
+
}
|
|
78
|
+
post({ op: 'error', id, ...errorFields(error) });
|
|
79
|
+
});
|
|
80
|
+
handles.set(id, handle);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function detach(id) {
|
|
84
|
+
const handle = handles.get(id);
|
|
85
|
+
if (handle) {
|
|
86
|
+
handles.delete(id);
|
|
87
|
+
try {
|
|
88
|
+
handle.close();
|
|
89
|
+
} catch {
|
|
90
|
+
/* expected: a handle whose 'error' listener already closed it may throw on the second close */
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
post({ op: 'unwatched', id });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function onMessage(message) {
|
|
97
|
+
if (shuttingDown || !message || typeof message !== 'object') return;
|
|
98
|
+
if (message.op === 'watch') attach(Number(message.id), String(message.dir));
|
|
99
|
+
else if (message.op === 'unwatch') detach(Number(message.id));
|
|
100
|
+
else if (message.op === 'shutdown') shuttingDown = true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (parentPort) parentPort.on('message', onMessage);
|
|
104
|
+
|
|
105
|
+
module.exports = { onMessage };
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { parentPort } = require('node:worker_threads');
|
|
3
|
+
const { parentPort, isMainThread } = require('node:worker_threads');
|
|
4
4
|
const fs = require('node:fs');
|
|
5
5
|
const path = require('node:path');
|
|
6
|
+
const { setTimeout: sleepPromise } = require('node:timers/promises');
|
|
7
|
+
const { boolFlag, registerFlag } = require('../lib/env-flags');
|
|
6
8
|
const gitUtils = require('../git-utils');
|
|
7
9
|
const createWorktreeGitObservation = require('../lib/worktree-git-observation');
|
|
8
10
|
const branchInventory = require('../lib/branch-inventory');
|
|
@@ -23,7 +25,39 @@ let closing = false;
|
|
|
23
25
|
let enqueued = 0;
|
|
24
26
|
let completed = 0;
|
|
25
27
|
let failed = 0;
|
|
26
|
-
|
|
28
|
+
let dropped = 0;
|
|
29
|
+
|
|
30
|
+
// D-1 (docs/session-create-box-restart-and-git-lane-starvation-2026-10-30.html §5, §6.1):
|
|
31
|
+
// the serial git lane serves the waiting human first. An op a human is blocked on is
|
|
32
|
+
// "interactive"; everything else is "background". The class is decided HERE, from a static
|
|
33
|
+
// table, never by the caller — callers already disagree about budgets (the review registry
|
|
34
|
+
// allows 10 s per statusV2, the create flow 10 min + 30 s), so a caller-set priority drifts.
|
|
35
|
+
const INTERACTIVE_OPS = new Set([
|
|
36
|
+
'createWorktree', 'removeWorktree', 'finishWorktree', 'mergeWorktree', 'deleteBranch',
|
|
37
|
+
'recoverWorktree', 'syncWorktree', 'pushAndCreatePR', 'projectRows', 'scanRoots',
|
|
38
|
+
'resolveFileReference', 'lsTree', 'resolveRef', 'getBranch',
|
|
39
|
+
]);
|
|
40
|
+
// Every other op in runOp's switch, explicitly acknowledged as background. The coverage test
|
|
41
|
+
// (tests/worktree-worker-queue.test.js, AC-1c) parses runOp's `case` labels and fails on a
|
|
42
|
+
// label that is in neither set (or in both), so a new op cannot be added unclassified.
|
|
43
|
+
const BACKGROUND_OPS_ACK = new Set([
|
|
44
|
+
'listRichWorktrees', 'mainSha', 'resolveSyncTarget', 'repoFacts', 'statusV2', 'observeWorktree',
|
|
45
|
+
'listWorktrees', 'gitRemoteKeys', 'richWorktreeStatusForPath', 'gatherGitFacts',
|
|
46
|
+
'listReviewDocuments', 'findHtmlInWorktrees', 'getUnmergedCommitList', 'pruneGhosts',
|
|
47
|
+
'syncAllWorktrees', 'resolveMainBranch', 'getDefaultReviewBase', 'getDefaultDiffStat',
|
|
48
|
+
'getDiffStat', 'getDiffStatVsRef', 'bestOfDiff', 'untrackedDiff', 'untrackedCount',
|
|
49
|
+
'__testSleep', // test-only op — see runOp's case; unreachable outside the test env gate
|
|
50
|
+
]);
|
|
51
|
+
|
|
52
|
+
function opClass(op, payload = {}) {
|
|
53
|
+
// Test-only: the queue tests need both classes out of one op name (`__testSleep`).
|
|
54
|
+
if (op === '__testSleep' && payload?.interactive === true) return 'interactive';
|
|
55
|
+
return INTERACTIVE_OPS.has(op) ? 'interactive' : 'background';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// D-1: two FIFO queues, drained interactive-first. One lane, so there is no preemption and the
|
|
59
|
+
// worst case for a human op is the one op already running (P-3).
|
|
60
|
+
const queues = { interactive: [], background: [] };
|
|
27
61
|
const { observeWorktree } = createWorktreeGitObservation({
|
|
28
62
|
git: (cwd, args, options = {}) => gitUtils.git(cwd, args, { ...options, env: gitUtils._gitProbeEnv() }),
|
|
29
63
|
gitStrict: (cwd, args, options = {}) => gitUtils.gitStrict(cwd, args, { ...options, env: gitUtils._gitProbeEnv() }),
|
|
@@ -47,13 +81,60 @@ function serializeError(error) {
|
|
|
47
81
|
};
|
|
48
82
|
}
|
|
49
83
|
|
|
84
|
+
function unsupportedOpError(op) {
|
|
85
|
+
const error = new Error(`Unsupported worktree worker op: ${op}`);
|
|
86
|
+
error.code = 'CTM_WORKTREE_OP_UNSUPPORTED';
|
|
87
|
+
return error;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// D-2a: the one shape a task that will never run is answered with. The client deletes its
|
|
91
|
+
// pending entry BEFORE it posts `cancel` (lib/worktree-worker-client.js:99-104,117), so this
|
|
92
|
+
// answer is simply ignored there; it exists for a caller that is still waiting.
|
|
93
|
+
function cancelledError() {
|
|
94
|
+
return Object.assign(new Error('Worktree worker request cancelled'), {
|
|
95
|
+
code: 'CTM_WORKTREE_WORKER_CANCELLED',
|
|
96
|
+
cancelled: true,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Test-only op body (see runOp's `__testSleep` case): a sleep that honours the task's signal.
|
|
101
|
+
async function sleepHonoringSignal(ms, signal) {
|
|
102
|
+
if (signal?.aborted) throw cancelledError();
|
|
103
|
+
try {
|
|
104
|
+
await sleepPromise(Math.max(0, Number(ms) || 0), undefined, { signal });
|
|
105
|
+
} catch (err) {
|
|
106
|
+
if (err?.name === 'AbortError') throw cancelledError();
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
registerFlag('CTM_WORKTREE_WORKER_TEST_OPS', {
|
|
112
|
+
kind: 'bool',
|
|
113
|
+
default: false,
|
|
114
|
+
desc: 'Test-only: enable the __testSleep fake op in the worktree worker so the queue tests '
|
|
115
|
+
+ 'drive workers/worktree-worker.js without git. Requires NODE_ENV=test as well; never set outside tests.',
|
|
116
|
+
expires: null,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
function testOpsEnabled() {
|
|
120
|
+
return process.env.NODE_ENV === 'test' && boolFlag('CTM_WORKTREE_WORKER_TEST_OPS', false);
|
|
121
|
+
}
|
|
122
|
+
|
|
50
123
|
function status() {
|
|
124
|
+
const heads = [queues.interactive[0], queues.background[0]].filter(Boolean);
|
|
51
125
|
return {
|
|
52
126
|
active: activeTask?.op || '',
|
|
53
|
-
|
|
127
|
+
activeClass: activeTask?.taskClass || '',
|
|
128
|
+
queued: queues.interactive.length + queues.background.length,
|
|
129
|
+
queuedInteractive: queues.interactive.length,
|
|
130
|
+
queuedBackground: queues.background.length,
|
|
131
|
+
oldestQueuedMs: heads.length
|
|
132
|
+
? Math.max(0, Date.now() - Math.min(...heads.map(task => task.enqueuedAt)))
|
|
133
|
+
: 0,
|
|
54
134
|
enqueued,
|
|
55
135
|
completed,
|
|
56
136
|
failed,
|
|
137
|
+
dropped,
|
|
57
138
|
errors: errorSink.snapshot(),
|
|
58
139
|
...gitUtils._getGitProbeMetrics(),
|
|
59
140
|
};
|
|
@@ -358,18 +439,32 @@ async function runOp(op, payload = {}, context = {}) {
|
|
|
358
439
|
return untrackedDiffer.untrackedDiff(cwd, payload || {});
|
|
359
440
|
case 'untrackedCount':
|
|
360
441
|
return { total: (await untrackedDiffer.listUntracked(cwd)).length };
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
442
|
+
case '__testSleep': {
|
|
443
|
+
// Test-only (tests/worktree-worker-queue.test.js AC-0a..c/AC-1a/AC-1b/meta): drives the
|
|
444
|
+
// real queue of THIS file without touching git. Off unless NODE_ENV=test and
|
|
445
|
+
// CTM_WORKTREE_WORKER_TEST_OPS=1, where the op is otherwise unsupported.
|
|
446
|
+
if (!testOpsEnabled()) throw unsupportedOpError(op);
|
|
447
|
+
context.emitProgress?.({ stage: 'test-start', tag: payload.tag });
|
|
448
|
+
await sleepHonoringSignal(payload.ms, context.signal);
|
|
449
|
+
return { tag: payload.tag };
|
|
365
450
|
}
|
|
451
|
+
default:
|
|
452
|
+
throw unsupportedOpError(op);
|
|
366
453
|
}
|
|
367
454
|
}
|
|
368
455
|
|
|
369
456
|
function pump() {
|
|
370
457
|
if (activeTask || closing) return;
|
|
371
|
-
|
|
372
|
-
|
|
458
|
+
// D-1: the oldest interactive task first, otherwise the oldest background one (FIFO within a
|
|
459
|
+
// class). D-2b: a task whose signal already aborted never starts — it is answered like a
|
|
460
|
+
// cancel and skipped, so a dropped caller never costs the lane real work.
|
|
461
|
+
let task;
|
|
462
|
+
for (;;) {
|
|
463
|
+
task = queues.interactive.shift() || queues.background.shift();
|
|
464
|
+
if (!task) return;
|
|
465
|
+
if (!task.controller.signal.aborted) break;
|
|
466
|
+
answerCancelled(task);
|
|
467
|
+
}
|
|
373
468
|
activeTask = task;
|
|
374
469
|
try { parentPort.postMessage({ type: 'status', status: status() }); } catch (_) {}
|
|
375
470
|
const emitProgress = (event) => {
|
|
@@ -391,9 +486,32 @@ function pump() {
|
|
|
391
486
|
});
|
|
392
487
|
}
|
|
393
488
|
|
|
489
|
+
// D-2a: a queued task leaves its queue the moment its caller stops waiting (splice by index,
|
|
490
|
+
// O(queue)); the caller is answered now instead of at the front of a lane it no longer wants.
|
|
491
|
+
function findQueued(requestId) {
|
|
492
|
+
for (const name of ['interactive', 'background']) {
|
|
493
|
+
const index = queues[name].findIndex(candidate => candidate.requestId === requestId);
|
|
494
|
+
if (index !== -1) return { name, index };
|
|
495
|
+
}
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function answerCancelled(task) {
|
|
500
|
+
dropped += 1;
|
|
501
|
+
try {
|
|
502
|
+
parentPort.postMessage({
|
|
503
|
+
type: 'response',
|
|
504
|
+
requestId: task.requestId,
|
|
505
|
+
error: serializeError(cancelledError()),
|
|
506
|
+
status: status(),
|
|
507
|
+
});
|
|
508
|
+
} catch (_) { /* expected: the parent port is already closed during shutdown */ }
|
|
509
|
+
}
|
|
510
|
+
|
|
394
511
|
function closeWhenIdle() {
|
|
395
512
|
closing = true;
|
|
396
|
-
|
|
513
|
+
queues.interactive.length = 0;
|
|
514
|
+
queues.background.length = 0;
|
|
397
515
|
if (activeTask) {
|
|
398
516
|
setTimeout(closeWhenIdle, 10).unref?.();
|
|
399
517
|
return;
|
|
@@ -402,27 +520,44 @@ function closeWhenIdle() {
|
|
|
402
520
|
setImmediate(() => parentPort.close());
|
|
403
521
|
}
|
|
404
522
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
}
|
|
523
|
+
if (!isMainThread) {
|
|
524
|
+
parentPort.on('message', (message) => {
|
|
525
|
+
if (!message || typeof message !== 'object') return;
|
|
526
|
+
if (message.type === 'request') {
|
|
527
|
+
enqueued += 1;
|
|
528
|
+
const task = {
|
|
529
|
+
requestId: message.requestId,
|
|
530
|
+
op: message.op,
|
|
531
|
+
payload: message.payload || {},
|
|
532
|
+
controller: new AbortController(),
|
|
533
|
+
enqueuedAt: Date.now(),
|
|
534
|
+
};
|
|
535
|
+
task.taskClass = opClass(task.op, task.payload);
|
|
536
|
+
queues[task.taskClass].push(task);
|
|
537
|
+
pump();
|
|
538
|
+
} else if (message.type === 'cancel') {
|
|
539
|
+
// D-2c: cancelling the RUNNING task only aborts its controller. The lane is not freed
|
|
540
|
+
// early — the next task starts when this one settles.
|
|
541
|
+
if (activeTask?.requestId === message.requestId) {
|
|
542
|
+
try { activeTask.controller.abort(); } catch (_) {}
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
// D-2a: a QUEUED task is removed and answered at once.
|
|
546
|
+
const queued = findQueued(message.requestId);
|
|
547
|
+
if (!queued) return;
|
|
548
|
+
const [task] = queues[queued.name].splice(queued.index, 1);
|
|
549
|
+
answerCancelled(task);
|
|
550
|
+
} else if (message.type === 'status') {
|
|
551
|
+
parentPort.postMessage({ type: 'status', status: status() });
|
|
552
|
+
} else if (message.type === 'close') {
|
|
553
|
+
closeWhenIdle();
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
parentPort.postMessage({ type: 'ready', status: status() });
|
|
558
|
+
}
|
|
427
559
|
|
|
428
|
-
|
|
560
|
+
// The queue tests (tests/worktree-worker-queue.test.js, AC-1c) require this module from the
|
|
561
|
+
// main thread to check runOp's `case` labels against the classification tables; loaded as a
|
|
562
|
+
// worker thread this export is inert.
|
|
563
|
+
module.exports = { INTERACTIVE_OPS, BACKGROUND_OPS_ACK, opClass };
|
|
@@ -11,7 +11,13 @@ const { EventEmitter } = require('node:events');
|
|
|
11
11
|
const TYPE = Object.freeze({
|
|
12
12
|
HELLO: 0x01, // client->relay once after upgrade; payload JSON {v:1, client:'cli', version, caps:{enc:[]}}
|
|
13
13
|
READY: 0x02, // relay->client; payload JSON {tunnel_id, slug, hostname, quota:{bytes_30d, used_30d, reset_at}}
|
|
14
|
-
OPEN
|
|
14
|
+
// OPEN 0x03 is relay->client; payload JSON. Rev 2 has one kind:
|
|
15
|
+
// { kind: 'tls', sni: 'mac.r.walle.sh', alpn: ['h2','http/1.1'] | [], remote: '203.0.113.7', remote_port: 51234 }
|
|
16
|
+
// The first DATA frames carry the buffered ClientHello bytes verbatim; then the
|
|
17
|
+
// rest of the TCP stream. Rev 1's 'http' and 'upgrade' kinds are removed by
|
|
18
|
+
// WP-11 (relay edge) / WP-14 (box terminator); a receiver RSTs any kind outside
|
|
19
|
+
// OPEN_KINDS with {code:'client_error'}.
|
|
20
|
+
OPEN: 0x03,
|
|
15
21
|
HEAD: 0x04, // client->relay; payload JSON {status, headers:{...}} (first frame of every response)
|
|
16
22
|
DATA: 0x05, // either direction; payload = raw bytes
|
|
17
23
|
END: 0x06, // either direction; half-close (no more DATA from the sender)
|
|
@@ -26,6 +32,12 @@ const TYPE = Object.freeze({
|
|
|
26
32
|
// the frame and count it in stats().reservedFrames.
|
|
27
33
|
});
|
|
28
34
|
|
|
35
|
+
// Kinds an OPEN frame may carry. Rev 2 has exactly one ('tls'); WP-11 (relay
|
|
36
|
+
// edge) and WP-14 (box terminator) delete rev 1's 'http'/'upgrade' paths and
|
|
37
|
+
// reduce this list to ['tls'] in the same pass. Until then every kind here is
|
|
38
|
+
// accepted, and a receiver RSTs anything else with {code:'client_error'}.
|
|
39
|
+
const OPEN_KINDS = Object.freeze(['tls', 'http', 'upgrade']);
|
|
40
|
+
|
|
29
41
|
const INITIAL_WINDOW = 256 * 1024; // DATA bytes in flight per stream before WINDOW is required
|
|
30
42
|
const MAX_FRAME = 64 * 1024; // max DATA payload per frame; the encoder splits larger writes
|
|
31
43
|
const MAX_STREAMS_PER_TUNNEL = 512; // the 513th concurrent OPEN is RST {code:'client_error'} and NOT created
|
|
@@ -496,6 +508,7 @@ function createMux(transport, options = {}) {
|
|
|
496
508
|
|
|
497
509
|
module.exports = {
|
|
498
510
|
TYPE,
|
|
511
|
+
OPEN_KINDS,
|
|
499
512
|
INITIAL_WINDOW,
|
|
500
513
|
MAX_FRAME,
|
|
501
514
|
MAX_STREAMS_PER_TUNNEL,
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "walle",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "walle",
|
|
9
|
-
"version": "0.10.
|
|
9
|
+
"version": "0.10.3",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"pg": "^8.22.0"
|
package/template/package.json
CHANGED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Backup retention policy shared by CTM (db.js cleanOldBackups) and Wall-E (brain.js
|
|
4
|
+
// cleanOldBackups). Design: coding-task-manager/docs/backup-retention-days-setting-2026-10-31.html
|
|
5
|
+
// §5 R-1..R-6, §6.
|
|
6
|
+
//
|
|
7
|
+
// Policy (R-1): group backups by the UTC date of `createdAt` and keep EVERY backup whose day is
|
|
8
|
+
// among the N most recent days that have a backup. Days without a backup do not count (Borg /
|
|
9
|
+
// Proxmox keep-daily semantics), so downtime never empties the window and no count floor is
|
|
10
|
+
// needed. The UTC date matches the day key both daily schedulers already use
|
|
11
|
+
// (`new Date().toISOString().slice(0, 10)`).
|
|
12
|
+
//
|
|
13
|
+
// Pure: no fs, no clock. Callers list their own backups and delete the expired rows as a set.
|
|
14
|
+
|
|
15
|
+
const DEFAULT_BACKUP_RETENTION_DAYS = 3;
|
|
16
|
+
const MIN_BACKUP_RETENTION_DAYS = 1;
|
|
17
|
+
const MAX_BACKUP_RETENTION_DAYS = 365;
|
|
18
|
+
const BACKUP_RETENTION_SETTING_KEY = 'backup_retention_days';
|
|
19
|
+
|
|
20
|
+
function _inRange(n) {
|
|
21
|
+
return Number.isInteger(n) && n >= MIN_BACKUP_RETENTION_DAYS && n <= MAX_BACKUP_RETENTION_DAYS;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Read path (R-5): any stored value -> integer in [1,365]; anything invalid -> the default. Never throws. */
|
|
25
|
+
function normalizeRetentionDays(value) {
|
|
26
|
+
let n = NaN;
|
|
27
|
+
if (typeof value === 'number') n = value;
|
|
28
|
+
else if (typeof value === 'string' && /^\d+$/.test(value.trim())) n = Number(value.trim());
|
|
29
|
+
return _inRange(n) ? n : DEFAULT_BACKUP_RETENTION_DAYS;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Write path (R-6): strict. A missing, fractional, padded or out-of-range value is rejected, never defaulted. */
|
|
33
|
+
function parseRetentionDaysInput(value) {
|
|
34
|
+
let n = NaN;
|
|
35
|
+
if (typeof value === 'number') n = value;
|
|
36
|
+
else if (typeof value === 'string' && /^\d+$/.test(value)) n = Number(value);
|
|
37
|
+
if (_inRange(n)) return { ok: true, days: n };
|
|
38
|
+
return {
|
|
39
|
+
ok: false,
|
|
40
|
+
error: 'retention_days_invalid',
|
|
41
|
+
min: MIN_BACKUP_RETENTION_DAYS,
|
|
42
|
+
max: MAX_BACKUP_RETENTION_DAYS,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _utcDayKey(createdAt) {
|
|
47
|
+
if (createdAt === null || createdAt === undefined || createdAt === '') return null;
|
|
48
|
+
const ms = createdAt instanceof Date ? createdAt.getTime() : new Date(createdAt).getTime();
|
|
49
|
+
if (!Number.isFinite(ms)) return null;
|
|
50
|
+
return new Date(ms).toISOString().slice(0, 10);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* R-1..R-3. Returns the EXPIRED rows (same object references), newest first. Rows whose
|
|
55
|
+
* createdAt does not parse are never expired and do not consume a day slot. The input array
|
|
56
|
+
* is not mutated.
|
|
57
|
+
* @param {Array<{name: string, createdAt: string|number|Date}>} backups
|
|
58
|
+
* @param {*} days retention days (normalized; invalid -> default)
|
|
59
|
+
*/
|
|
60
|
+
function selectExpiredBackups(backups, days) {
|
|
61
|
+
const keepDays = normalizeRetentionDays(days);
|
|
62
|
+
const dated = [];
|
|
63
|
+
for (const row of Array.isArray(backups) ? backups : []) {
|
|
64
|
+
const day = _utcDayKey(row && row.createdAt);
|
|
65
|
+
if (day) dated.push({ row, day, ms: new Date(row.createdAt instanceof Date ? row.createdAt.getTime() : row.createdAt).getTime() });
|
|
66
|
+
}
|
|
67
|
+
const keptDays = new Set([...new Set(dated.map((d) => d.day))].sort().reverse().slice(0, keepDays));
|
|
68
|
+
return dated
|
|
69
|
+
.filter((d) => !keptDays.has(d.day))
|
|
70
|
+
.sort((a, b) => b.ms - a.ms)
|
|
71
|
+
.map((d) => d.row);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
DEFAULT_BACKUP_RETENTION_DAYS,
|
|
76
|
+
MIN_BACKUP_RETENTION_DAYS,
|
|
77
|
+
MAX_BACKUP_RETENTION_DAYS,
|
|
78
|
+
BACKUP_RETENTION_SETTING_KEY,
|
|
79
|
+
normalizeRetentionDays,
|
|
80
|
+
parseRetentionDaysInput,
|
|
81
|
+
selectExpiredBackups,
|
|
82
|
+
};
|
|
@@ -59,6 +59,8 @@ const PROVIDER_ALIASES = Object.freeze({
|
|
|
59
59
|
anthropic: 'claude',
|
|
60
60
|
claude: 'claude',
|
|
61
61
|
'claude-desktop': 'claude',
|
|
62
|
+
// DeepSeek Harness: CTM writes its transcript as Claude-shaped rows (deepseek-harness-acp-agent-2026-10-28.html D-8).
|
|
63
|
+
dsh: 'claude',
|
|
62
64
|
openai: 'openai',
|
|
63
65
|
codex: 'openai',
|
|
64
66
|
gemini: 'gemini',
|
|
@@ -80,12 +80,8 @@ const PEER_RELAY_PREFIX_RE = /^(Another Claude session sent a message:|<agent-me
|
|
|
80
80
|
// tool tags, task-notification summaries, system-reminder blocks wherever
|
|
81
81
|
// they appear -- user text AND tool_result previews).
|
|
82
82
|
const SYSTEM_REMINDER_RE = /<system-reminder>[\s\S]*?<\/system-reminder>/g;
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
// this regex required (verified against a real bracketed-paste capture; the closing tag never
|
|
86
|
-
// matched, so the whole envelope leaked through unstripped). `(?:\s[^>]*)?` accepts that
|
|
87
|
-
// attribute (or any other) on the closing tag without requiring it.
|
|
88
|
-
const PASTED_CONTENT_RE = /<pasted_content id="[^"]*">([\s\S]*?)<\/pasted_content(?:\s[^>]*)?>/g;
|
|
83
|
+
// Paste-wrapper recognition moved to coding-task-manager/public/js/pasted-content.js, Claude
|
|
84
|
+
// Code's strict rule (docs/hub-viewed-box-conv-freshness-and-paste-render-2026-10-31.html D-5).
|
|
89
85
|
const IDE_SELECTION_RE = /<ide_selection>[\s\S]*?<\/ide_selection>/g;
|
|
90
86
|
const TASK_NOTIFICATION_RE = /^<task-notification>([\s\S]*?)<\/task-notification>/i;
|
|
91
87
|
const TASK_NOTIFICATION_SUMMARY_RE = /<summary>([\s\S]*?)<\/summary>/i;
|
|
@@ -291,7 +287,7 @@ module.exports = {
|
|
|
291
287
|
claudeQueuedPromptEntry, userRowKind,
|
|
292
288
|
PROVENANCE, classifyUserProvenance, ENVELOPES, BUILTIN_LOCAL_COMMANDS, parseCommandEcho, UNKNOWN_TAG_RE,
|
|
293
289
|
COMMAND_ENVELOPE_RE, LOCAL_COMMAND_STDOUT_RE, PEER_RELAY_PREFIX_RE,
|
|
294
|
-
SYSTEM_REMINDER_RE,
|
|
290
|
+
SYSTEM_REMINDER_RE, IDE_SELECTION_RE,
|
|
295
291
|
TASK_NOTIFICATION_RE, TASK_NOTIFICATION_SUMMARY_RE,
|
|
296
292
|
BASH_INPUT_RE, BASH_STDOUT_RE, BASH_STDERR_RE,
|
|
297
293
|
};
|
|
@@ -96,6 +96,11 @@ const CTM_KNOWN_CONVERSATION_TABLES = Object.freeze(new Set([
|
|
|
96
96
|
'conversation_projection_outbox_v2',
|
|
97
97
|
'conversation_projection_deletes_v2', // v36 change-feed delete log: session_id/stable_key/timestamps, no text
|
|
98
98
|
'conversation_store_epoch_v2', // v36 change-feed store epoch: one row, random token
|
|
99
|
+
// WP-5 replica clock (key + origin/seq + timestamps, no text), declared by migration v61; not
|
|
100
|
+
// content-bearing. conversation_vv/conversation_local_seq are the vector and seq allocator.
|
|
101
|
+
'conversation_item_clock',
|
|
102
|
+
'conversation_vv',
|
|
103
|
+
'conversation_local_seq',
|
|
99
104
|
'conversation_storage_metrics_v2',
|
|
100
105
|
'conversation_text_fts_v2',
|
|
101
106
|
'conversation_events',
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Is this code running from a LINKED git worktree (`git worktree add`, e.g. .claude/worktrees/<name>)?
|
|
4
|
+
//
|
|
5
|
+
// Why it exists (2026-10-31 incident): a delegated worker in a feature-branch worktree ran
|
|
6
|
+
// `node -e "require('./server.js')"` with no CTM_DATA_DIR override. server.js booted against the
|
|
7
|
+
// default data dir (~/.walle/data, the user's PRIMARY), and the branch's unmerged schema migration
|
|
8
|
+
// (v59) ran on the live DB. main was at v58, so every primary respawn then died with SchemaAheadError.
|
|
9
|
+
//
|
|
10
|
+
// A primary install never runs from a linked worktree: the primary checkout is a plain clone (`.git` is
|
|
11
|
+
// a directory) and an npm install has no `.git` at all. So "linked worktree" is a precise marker for
|
|
12
|
+
// "unmerged branch code". The DB guards in coding-task-manager/db.js and wall-e/brain.js refuse the
|
|
13
|
+
// primary data dir in that case, so a branch's migrations can only reach a live DB after they land on
|
|
14
|
+
// main and ship, where every install applies them automatically at boot.
|
|
15
|
+
//
|
|
16
|
+
// Detection: walk up from `startDir` to the first `.git`. A linked worktree's `.git` is a FILE whose
|
|
17
|
+
// `gitdir:` line points into `<repo>/.git/worktrees/<name>`. Memoized per start dir; sync fs, called
|
|
18
|
+
// only on the (already sync) DB-open path.
|
|
19
|
+
|
|
20
|
+
const fs = require('fs');
|
|
21
|
+
const path = require('path');
|
|
22
|
+
|
|
23
|
+
const _memo = new Map();
|
|
24
|
+
|
|
25
|
+
function isLinkedWorktreeCheckout(startDir = __dirname) {
|
|
26
|
+
const key = path.resolve(startDir);
|
|
27
|
+
if (_memo.has(key)) return _memo.get(key);
|
|
28
|
+
let answer = false;
|
|
29
|
+
let dir = key;
|
|
30
|
+
for (;;) {
|
|
31
|
+
const gitPath = path.join(dir, '.git');
|
|
32
|
+
let stat = null;
|
|
33
|
+
try { stat = fs.statSync(gitPath); } catch { /* expected: no .git at this level -- keep walking up */ }
|
|
34
|
+
if (stat) {
|
|
35
|
+
if (stat.isFile()) {
|
|
36
|
+
let text = '';
|
|
37
|
+
try { text = fs.readFileSync(gitPath, 'utf8'); } catch { /* expected: unreadable .git file -- treat as not linked */ }
|
|
38
|
+
const match = /^gitdir:\s*(.+)$/m.exec(text);
|
|
39
|
+
answer = !!(match && /[\\/]worktrees[\\/][^\\/]+[\\/]?$/.test(match[1].trim()));
|
|
40
|
+
}
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
const parent = path.dirname(dir);
|
|
44
|
+
if (parent === dir) break;
|
|
45
|
+
dir = parent;
|
|
46
|
+
}
|
|
47
|
+
_memo.set(key, answer);
|
|
48
|
+
return answer;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
module.exports = { isLinkedWorktreeCheckout };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// macOS Full Disk Access (FDA) target identity — WHICH process identity the grant must name.
|
|
4
|
+
//
|
|
5
|
+
// macOS keys a TCC/FDA grant to the RESPONSIBLE process: for a launchd job that is the exec
|
|
6
|
+
// launchd started, and its children inherit the grant. Neither CTM nor Wall-E runs out of an
|
|
7
|
+
// app bundle on every host (the measured box runs both as
|
|
8
|
+
// `~/.walle/notarized-node/bin/node`), so a hint that always names
|
|
9
|
+
// `~/.walle/bundles/Coding Task Manager.app` points the user at a path that does not exist —
|
|
10
|
+
// and a Homebrew `node` the user grants instead (System Settings lists it as "node" too) has a
|
|
11
|
+
// different, versioned Cellar identity that never matches the running binary.
|
|
12
|
+
//
|
|
13
|
+
// So every FDA hint/reveal is computed from the RUNNING exec path. Pure + DI so the mapping is
|
|
14
|
+
// unit-testable without a real macOS.
|
|
15
|
+
//
|
|
16
|
+
// Design: docs/signin-flow-restart-settle-and-fda-target-identity-2026-10-26.html §5 D-4..D-7.
|
|
17
|
+
|
|
18
|
+
const fs = require('fs');
|
|
19
|
+
const path = require('path');
|
|
20
|
+
|
|
21
|
+
// `<X>.app/Contents/MacOS/<name>` — an app bundle's exec. Anything else is a plain binary.
|
|
22
|
+
const APP_EXEC_RE = /^(.*\.app)\/Contents\/MacOS\/[^/]+$/;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Map a running exec path to the identity an FDA grant must name.
|
|
26
|
+
*
|
|
27
|
+
* @param {string} execPath the running executable (process.execPath, or the Wall-E daemon exec)
|
|
28
|
+
* @param {{ realpathSync?: (p: string) => string, existsSync?: (p: string) => boolean }} [deps]
|
|
29
|
+
* @returns {{ kind: 'app'|'binary', path: string, displayName: string, exists: boolean }|null}
|
|
30
|
+
* null when execPath is empty / not a string. An app exec maps to its `.app` directory; any
|
|
31
|
+
* other exec maps to its realpath (the UNRESOLVED path when realpath throws). `exists` follows
|
|
32
|
+
* existsSync on the resolved path (a throw reads as false).
|
|
33
|
+
*/
|
|
34
|
+
function fdaTargetForExec(execPath, { realpathSync = fs.realpathSync, existsSync = fs.existsSync } = {}) {
|
|
35
|
+
if (typeof execPath !== 'string' || execPath === '') return null;
|
|
36
|
+
const appMatch = APP_EXEC_RE.exec(execPath);
|
|
37
|
+
let kind;
|
|
38
|
+
let targetPath;
|
|
39
|
+
let displayName;
|
|
40
|
+
if (appMatch) {
|
|
41
|
+
kind = 'app';
|
|
42
|
+
targetPath = appMatch[1];
|
|
43
|
+
displayName = path.basename(targetPath, '.app');
|
|
44
|
+
} else {
|
|
45
|
+
kind = 'binary';
|
|
46
|
+
targetPath = execPath;
|
|
47
|
+
try { targetPath = realpathSync(targetPath); }
|
|
48
|
+
catch { /* expected: broken symlink / unreadable parent — the unresolved exec path is still the identity to name */ }
|
|
49
|
+
displayName = path.basename(targetPath);
|
|
50
|
+
}
|
|
51
|
+
let exists = false;
|
|
52
|
+
try { exists = !!existsSync(targetPath); }
|
|
53
|
+
catch { exists = false; /* expected: an unreadable parent is not "exists" — never reveal what we cannot confirm */ }
|
|
54
|
+
return { kind, path: targetPath, displayName, exists };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { fdaTargetForExec };
|