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,738 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Landing-throughput WP-6 shard S12 (design doc §5 D-10 "move-only", §6 "Shard module
|
|
4
|
+
// seam"): the session-park cluster (parkSession + its five private helpers) moves out of
|
|
5
|
+
// server.js VERBATIM into this module behind the same configure(deps) seam as
|
|
6
|
+
// lib/api/session-attach.js and lib/terminal-attach-geometry.js (S6), lib/walle-history-serve.js
|
|
7
|
+
// (S8), lib/codex-rollout-history.js (S9), lib/terminal-input-prepared.js (S10) and
|
|
8
|
+
// lib/idle-notify.js (S11). It registers NO route and NO socket handler: server.js keeps the
|
|
9
|
+
// pass-through call sites as sessionPark.parkSession(...) (plus sessionPark._lifecycleProcessGroupId
|
|
10
|
+
// in _signalLifecycleProcessGroup's default parameter), and this configure() seam.
|
|
11
|
+
// Bodies are identical to server.js declarations except for the `_deps.` prefix on every
|
|
12
|
+
// identifier that used to resolve to a server.js top-level binding (D-10 rule 1), proven by
|
|
13
|
+
// `node bin/move-only-check.js`. Four inline `require('./lib/...')` calls are rebased to this
|
|
14
|
+
// module's own `require('./...')` (S8 precedent: one move-only DIFF per rebased require -- the
|
|
15
|
+
// remainder is proven by re-running against a copy with
|
|
16
|
+
// `sed "s#require('\./#require('./lib/#g"` applied).
|
|
17
|
+
//
|
|
18
|
+
// The module deliberately lives at lib/session-park.js and NOT under lib/ws/: parkSession is
|
|
19
|
+
// the session-park transaction, not a ws-router handler.
|
|
20
|
+
//
|
|
21
|
+
// Injected deps, per function:
|
|
22
|
+
// _prepareLifecycleTranscript: _heartbeatTokenPath, _lifecycleResumeTarget, _mainTranscriptReads, detectAgentType, resumeTranscriptPaths, sessions
|
|
23
|
+
// _verifyLifecycleTranscriptPhysical: readRestoreFileFingerprint, resumeTranscriptPaths
|
|
24
|
+
// _lifecycleTranscriptStat: _mainTranscriptReads, resumeTranscriptPaths
|
|
25
|
+
// _auditParkedProcessGroup: _lifecycleOrphanEvents, _lifecycleOrphanGroups, _sessionResourceSampler, recordSessionDiagnostic
|
|
26
|
+
// _lifecycleProcessGroupId: (none)
|
|
27
|
+
// parkSession: SESSION_TRANSCRIPT_TRASH_ROOT, _RESTORE_STORM_GRACE_MS, _applyPersistedSnapshotToPlaceholder, _broadcastLifecyclePressureStatus, _codingSessionModelRuntime, _connectedLifecycleClientCount, _createDeferredRestoreSession, _destroySession, _dropDeferredRuntimeInputs, _getHeadlessRows, _lastLifecycleRelief, _lifecycleConfig, _lifecycleDiagnostics, _lifecycleJournalGate, _lifecycleJournalHost, _lifecyclePressure, _lifecycleResumeTarget, _lifecycleVisibilityGrace, _liveLifecycleSessions, _promoteDeferredRuntimeInputs, _readScrollbackSnapshotForRestoreAsync, _restoreCompletedAt, _restoreInProgress, _sampleLifecycleResources, _scheduleDeferredRestoreStart, _sessionLifecycleView, _sessionResourceSampler, _signalLifecycleProcessGroup, _snapshotSessionScrollbackToFile, _waitForLifecycleExit, _writeLifecyclePty, _wsSend, agentSessionKernel, armParkFollowup, broadcastSessionList, broadcastToSession, canResumeAgent, cleanAutoApprovalBuffer, cleanIdleNotify, copyWorkerMarkers, createParkAttemptRecorder, dbModule, detectAgentType, drainDeferredInputs, errorSink, evaluateManualPark, evaluatePark, getResumeSpec, getSession, matchesSessionModelParkPrecondition, parkedSessionMessage, readComposerDraft, recordSessionDiagnostic, restoreRuntime, sendDeferredRestoreStatus, sessionDiagnostics, sessionInputAuthority, sessionPolicy, sessions
|
|
28
|
+
//
|
|
29
|
+
// Mutable module state (D-10 rule 2 / S9 lesson 7): _restoreInProgress and _restoreCompletedAt are
|
|
30
|
+
// server.js `let`s reassigned during boot/restore, so they are injected as GETTERS -- never
|
|
31
|
+
// copies. TWO moved bodies WRITE a server.js binding: parkSession assigns _lastLifecycleRelief
|
|
32
|
+
// (inside the park-followup onFollowup callback) and _auditParkedProcessGroup increments
|
|
33
|
+
// _lifecycleOrphanEvents, so both are injected as a GET/SET pair. Every other dep is a by-value
|
|
34
|
+
// reference. Calls between the six moved functions stay direct (no `_deps.`).
|
|
35
|
+
|
|
36
|
+
let _deps = null;
|
|
37
|
+
|
|
38
|
+
function configure(deps = {}) {
|
|
39
|
+
_deps = deps;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function _prepareLifecycleTranscript(session) {
|
|
43
|
+
const unavailable = () => Object.assign(new Error('Lifecycle transcript preparation is unavailable'), {
|
|
44
|
+
code: 'transcript_preparation_unavailable', statusCode: 503,
|
|
45
|
+
});
|
|
46
|
+
const agentType = _deps.detectAgentType(session?.cmd || '');
|
|
47
|
+
const runtimePath = String(
|
|
48
|
+
session?.jsonlPath || _deps._heartbeatTokenPath(session, agentType) || '',
|
|
49
|
+
).trim();
|
|
50
|
+
let candidates = _deps.resumeTranscriptPaths([
|
|
51
|
+
session?._resumeTranscriptPath,
|
|
52
|
+
runtimePath,
|
|
53
|
+
]);
|
|
54
|
+
const resumeId = _deps._lifecycleResumeTarget(session, agentType);
|
|
55
|
+
let codexRolloutPath = '';
|
|
56
|
+
let codexStateUnavailable = false;
|
|
57
|
+
if (agentType === 'codex' && resumeId) {
|
|
58
|
+
const codex = await _deps._mainTranscriptReads.prepareCodexLink({
|
|
59
|
+
threadId: resumeId, ctmSessionId: session.id, liveAgentSessionId: session._claudeSessionId || '',
|
|
60
|
+
cwd: session.cwd || '', createdAtMs: session._ctmOriginalCreatedAt || session.createdAt || '',
|
|
61
|
+
liveCtmSessionIds: [..._deps.sessions.keys()],
|
|
62
|
+
});
|
|
63
|
+
if (!codex) throw unavailable();
|
|
64
|
+
codexRolloutPath = String(codex.resolved?.rolloutPath || '').trim();
|
|
65
|
+
codexStateUnavailable = codex.status === 'state_db_unavailable' && !codexRolloutPath;
|
|
66
|
+
candidates = _deps.resumeTranscriptPaths([candidates, codexRolloutPath]);
|
|
67
|
+
}
|
|
68
|
+
if (candidates.length > 3) throw unavailable();
|
|
69
|
+
const rows = await _deps._mainTranscriptReads.statPaths(candidates, { requireAvailable: true });
|
|
70
|
+
if (!(rows instanceof Map)) throw unavailable();
|
|
71
|
+
for (const p of candidates) {
|
|
72
|
+
const row = rows.get(p);
|
|
73
|
+
if (!row || !['plain', 'archived', 'trashed', 'missing'].includes(row.state)
|
|
74
|
+
|| typeof row.exists !== 'boolean' || row.exists !== (row.state !== 'missing')
|
|
75
|
+
|| row.size < 0 || !['size', 'mtimeMs', 'birthtimeMs'].every((key) => Number.isFinite(row[key]))) throw unavailable();
|
|
76
|
+
}
|
|
77
|
+
const found = candidates.find((p) => rows.get(p).exists);
|
|
78
|
+
if (!found && codexStateUnavailable) throw unavailable();
|
|
79
|
+
const row = found ? rows.get(found) : null;
|
|
80
|
+
const transcript = { filePath: found || candidates[0] || '', size: row?.size || 0,
|
|
81
|
+
mtimeMs: row?.mtimeMs || 0, modifiedAt: row ? new Date(Math.round(row.mtimeMs)).toISOString() : '',
|
|
82
|
+
missing: !found, unknown: false, candidates };
|
|
83
|
+
return { rows, candidates, transcript, codexRolloutPath };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function _verifyLifecycleTranscriptPhysical(prepared) {
|
|
87
|
+
// Narrow mutation verification only: the existing writer-owned physical fingerprint API
|
|
88
|
+
// never waits for a worker, consults manifests or parses/decodes transcript content.
|
|
89
|
+
const candidates = _deps.resumeTranscriptPaths(prepared.candidates);
|
|
90
|
+
let physical = null;
|
|
91
|
+
let retainedArchive = null;
|
|
92
|
+
let diagnostic = prepared.transcript;
|
|
93
|
+
let unknown = false;
|
|
94
|
+
for (const p of candidates) {
|
|
95
|
+
const prior = prepared.rows.get(p);
|
|
96
|
+
let observed;
|
|
97
|
+
try { observed = await _deps.readRestoreFileFingerprint(p); }
|
|
98
|
+
catch { observed = { state: 'unavailable' }; /* expected: unavailable physical evidence cannot authorize fresh */ }
|
|
99
|
+
if (observed.state === 'file') {
|
|
100
|
+
if (!physical) physical = { filePath: p, size: observed.size, mtimeMs: observed.mtimeMs,
|
|
101
|
+
modifiedAt: new Date(Math.round(observed.mtimeMs)).toISOString() };
|
|
102
|
+
} else if (observed.state === 'missing') {
|
|
103
|
+
if (!retainedArchive && prior?.exists && ['archived', 'trashed'].includes(prior.state)) {
|
|
104
|
+
retainedArchive = { filePath: p, size: prior.size, mtimeMs: prior.mtimeMs,
|
|
105
|
+
modifiedAt: new Date(Math.round(prior.mtimeMs)).toISOString() };
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
unknown = true;
|
|
109
|
+
}
|
|
110
|
+
if (prior?.exists && (!diagnostic || diagnostic.missing)) {
|
|
111
|
+
diagnostic = { filePath: p, size: prior.size, mtimeMs: prior.mtimeMs,
|
|
112
|
+
modifiedAt: new Date(Math.round(prior.mtimeMs)).toISOString() };
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const found = physical || retainedArchive;
|
|
116
|
+
if (unknown) return { ...(found || diagnostic), missing: false, unknown: true, candidates };
|
|
117
|
+
if (found) return { ...found, missing: false, unknown: false, candidates };
|
|
118
|
+
return { filePath: candidates[0] || '', size: 0, mtimeMs: 0, modifiedAt: '', missing: true, unknown: false, candidates };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function _lifecycleTranscriptStat(prepared) {
|
|
122
|
+
const candidates = _deps.resumeTranscriptPaths(prepared?.candidates || []);
|
|
123
|
+
const unavailable = () => ({ filePath: prepared?.transcript?.filePath || candidates[0] || '',
|
|
124
|
+
size: Number(prepared?.transcript?.size || 0), mtimeMs: 0, missing: false, unknown: true, candidates });
|
|
125
|
+
if (!candidates.length) {
|
|
126
|
+
// No candidate path exists at all, so there is nothing the at-signal re-check could observe
|
|
127
|
+
// appearing or mutating. An adopted session can also lack a known transcript path:
|
|
128
|
+
// `_resumeTranscriptPath` comes from create-path preflight, which host adoption skips, and
|
|
129
|
+
// its other runtime/durable path fields can be empty. This is a verdict about the fixed
|
|
130
|
+
// candidate set; the native resume identity and normal snapshot/signal guards still apply.
|
|
131
|
+
// `_verifyLifecycleTranscriptPhysical` already
|
|
132
|
+
// reached this same verdict (`missing: true, unknown: false`) for the identical empty set, so
|
|
133
|
+
// reporting `unknown` here would refuse a park that the physical check just authorized.
|
|
134
|
+
return { filePath: '', size: 0, mtimeMs: 0, missing: true, unknown: false, candidates: [] };
|
|
135
|
+
}
|
|
136
|
+
if (candidates.length > 3) return unavailable();
|
|
137
|
+
let rows;
|
|
138
|
+
try { rows = await _deps._mainTranscriptReads.statPaths(candidates, { requireAvailable: true }); }
|
|
139
|
+
catch { return unavailable(); }
|
|
140
|
+
if (!(rows instanceof Map)) return unavailable();
|
|
141
|
+
for (const candidate of candidates) {
|
|
142
|
+
const row = rows.get(candidate);
|
|
143
|
+
if (!row || !['plain', 'archived', 'trashed', 'missing'].includes(row.state)
|
|
144
|
+
|| typeof row.exists !== 'boolean' || row.exists !== (row.state !== 'missing')
|
|
145
|
+
|| !Number.isFinite(row.size) || row.size < 0
|
|
146
|
+
|| !['mtimeMs', 'birthtimeMs'].every((key) => Number.isFinite(row[key]))) return unavailable();
|
|
147
|
+
}
|
|
148
|
+
const filePath = candidates.find((candidate) => rows.get(candidate).exists) || '';
|
|
149
|
+
const row = filePath ? rows.get(filePath) : null;
|
|
150
|
+
return { filePath: filePath || candidates[0], size: row?.size || 0,
|
|
151
|
+
mtimeMs: row?.mtimeMs || 0, missing: !filePath, unknown: false, candidates };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function _auditParkedProcessGroup(sessionId, pgid, rssBeforeMB = 0, descendantPidsBefore = []) {
|
|
155
|
+
if (!pgid) return { pgid: 0, rssMB: 0, procCount: 0, pids: [] };
|
|
156
|
+
let group;
|
|
157
|
+
try {
|
|
158
|
+
group = await _deps._sessionResourceSampler.sampleProcessGroup(pgid, descendantPidsBefore);
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.warn(`[lifecycle] park-audit-failed id=${String(sessionId).slice(0, 8)} pgid=${pgid} error=${error.message}`);
|
|
161
|
+
return { pgid, rssMB: 0, procCount: 0, pids: [], auditFailed: true };
|
|
162
|
+
}
|
|
163
|
+
const orphanMB = Number(group.rssMB || 0);
|
|
164
|
+
const victimRssMB = Math.max(0, Number(rssBeforeMB || 0));
|
|
165
|
+
// D-5: rows captured by the pre-signal descendant census that are still alive but do NOT share the
|
|
166
|
+
// agent's pgid — the ones the pgid rollup is structurally blind to.
|
|
167
|
+
const survivorPids = Array.isArray(group.survivorPids) ? group.survivorPids : [];
|
|
168
|
+
if (group.procCount > 0) {
|
|
169
|
+
_deps._lifecycleOrphanGroups.set(pgid, group);
|
|
170
|
+
_deps._lifecycleOrphanEvents += 1;
|
|
171
|
+
} else {
|
|
172
|
+
_deps._lifecycleOrphanGroups.delete(pgid);
|
|
173
|
+
}
|
|
174
|
+
console.log(`[lifecycle] park-victimRssMB=${victimRssMB.toFixed(1)} park-orphanMB=${orphanMB.toFixed(1)} id=${String(sessionId).slice(0, 8)} pgid=${pgid}`);
|
|
175
|
+
if (group.procCount > 0) {
|
|
176
|
+
console.warn(`[lifecycle] park-orphan id=${String(sessionId).slice(0, 8)} pgid=${pgid} pids=${group.pids.join(',')}`);
|
|
177
|
+
_deps.recordSessionDiagnostic(sessionId, 'park-orphan', { pgid, pids: group.pids, orphanMB, victimRssMB, survivorPids });
|
|
178
|
+
} else {
|
|
179
|
+
_deps.recordSessionDiagnostic(sessionId, 'park-process-group-gone', { pgid, orphanMB: 0, victimRssMB });
|
|
180
|
+
}
|
|
181
|
+
return { ...group, victimRssMB, orphanMB };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function _lifecycleProcessGroupId(session) {
|
|
185
|
+
const ptyProcess = session?.ptyProcess;
|
|
186
|
+
if (ptyProcess && Object.prototype.hasOwnProperty.call(ptyProcess, 'started') && ptyProcess.started !== true) return 0;
|
|
187
|
+
const pgid = Number(ptyProcess?.pid || session?.pid || 0);
|
|
188
|
+
return Number.isInteger(pgid) && pgid > 1 ? pgid : 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function parkSession(sessionId, options = {}) {
|
|
192
|
+
const session = _deps.getSession(String(sessionId || ''));
|
|
193
|
+
const reason = String(options.reason || 'manual').slice(0, 32);
|
|
194
|
+
// WP-2 (S-15, design doc §5 D-3): warn-tier parks now select victims with the plain idle
|
|
195
|
+
// predicate (config), not the stripped pressure-relief predicate -- labeled 'idle' here,
|
|
196
|
+
// same as the idle/freeze-expired lanes, so a journal reader sees which predicate actually
|
|
197
|
+
// ran rather than the generic 'pressure' label critical still carries.
|
|
198
|
+
const configName = options.manual === true
|
|
199
|
+
? 'manual'
|
|
200
|
+
: (reason === 'cap' ? 'cap' : (reason === 'idle' || reason === 'freeze-expired' || reason === 'warn' ? 'idle' : 'pressure'));
|
|
201
|
+
const journalHost = _deps._lifecycleJournalHost();
|
|
202
|
+
const recordParkAttempt = _deps.createParkAttemptRecorder({ enabled: _deps._lifecycleConfig.parkJournal });
|
|
203
|
+
const finishParkAttempt = (result, decision = null, event = null) => {
|
|
204
|
+
const eventName = event || (result?.ok && result?.alreadyParked !== true ? 'park' : 'park-refused');
|
|
205
|
+
const recorded = recordParkAttempt({
|
|
206
|
+
event: eventName,
|
|
207
|
+
id: String(session?.id || sessionId || ''),
|
|
208
|
+
reason,
|
|
209
|
+
code: result?.code || null,
|
|
210
|
+
gate: _deps._lifecycleJournalGate(session, decision, configName),
|
|
211
|
+
host: journalHost,
|
|
212
|
+
victimRssMB: Number(session?.rssMB) || 0,
|
|
213
|
+
// WP-2 (S-14 item 5): the preceding trim's freedMB, so a warn-tier park record shows
|
|
214
|
+
// whether trim had already tried and failed. Absent (null) for every other reason.
|
|
215
|
+
trim: options.trim || null,
|
|
216
|
+
});
|
|
217
|
+
if (recorded && eventName === 'park' && result?.ok && result?.alreadyParked !== true) {
|
|
218
|
+
_deps.armParkFollowup({
|
|
219
|
+
id: String(session?.id || sessionId || ''),
|
|
220
|
+
reason,
|
|
221
|
+
beforeHost: journalHost,
|
|
222
|
+
sampleHost: () => _deps._sampleLifecycleResources({ force: true }),
|
|
223
|
+
runtime: () => {
|
|
224
|
+
const liveSessions = _deps._liveLifecycleSessions();
|
|
225
|
+
return {
|
|
226
|
+
pressureLevel: _deps._lifecyclePressure.level,
|
|
227
|
+
liveCount: liveSessions.length,
|
|
228
|
+
liveRssMB: liveSessions.reduce((sum, item) => sum + Number(item.rssMB || 0), 0),
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
onFollowup: (record) => {
|
|
232
|
+
_deps._lastLifecycleRelief = {
|
|
233
|
+
at: record.at || new Date().toISOString(),
|
|
234
|
+
sessionId: String(session?.id || sessionId || ''),
|
|
235
|
+
deltaFreeMB: record.deltaFreeMB,
|
|
236
|
+
deltaSwapPct: record.deltaSwapPct,
|
|
237
|
+
};
|
|
238
|
+
_deps._broadcastLifecyclePressureStatus(true);
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
return result;
|
|
243
|
+
};
|
|
244
|
+
if (!session) return finishParkAttempt({ ok: false, code: 'session_not_found', statusCode: 404 });
|
|
245
|
+
// WP-2A: an OMITTED precondition keeps the legacy park behaviour; a PRESENT one (even an explicit
|
|
246
|
+
// null/undefined) is validated against the SAME canonical runtime's owner generation AND thread.
|
|
247
|
+
const expectedThreadId = String(options.sourceProviderThreadId || '');
|
|
248
|
+
const parkOwnerMatches = () => {
|
|
249
|
+
if (_deps.sessions.get(String(sessionId || '')) !== session || options.signal?.aborted) return false;
|
|
250
|
+
if (!_deps.matchesSessionModelParkPrecondition({ provided: Object.hasOwn(options, 'sourceGeneration'),
|
|
251
|
+
expectedGeneration: options.sourceGeneration, expectedThreadId,
|
|
252
|
+
owner: _deps._codingSessionModelRuntime?.publicFields?.(session)?.model_owner || null })) return false;
|
|
253
|
+
if (expectedThreadId) {
|
|
254
|
+
const recipeThread = session._modelExecutionRecipe?.providerThreadId || '';
|
|
255
|
+
const liveThread = _deps._lifecycleResumeTarget(session, _deps.detectAgentType(session.cmd || '')) || '';
|
|
256
|
+
if (String(recipeThread || liveThread) !== expectedThreadId) return false;
|
|
257
|
+
}
|
|
258
|
+
return true;
|
|
259
|
+
};
|
|
260
|
+
if (!parkOwnerMatches()) return finishParkAttempt({ ok: false, code: 'park_owner_changed', statusCode: 409 });
|
|
261
|
+
if (session._restoreDeferred) return finishParkAttempt({ ok: true, alreadyParked: true, sessionId: session.id });
|
|
262
|
+
if (!session.ptyProcess || session._parking) return finishParkAttempt({ ok: false, code: 'session_not_live', statusCode: 409 });
|
|
263
|
+
if (!_deps.sessionPolicy(session).parkable) return finishParkAttempt({ ok: false, code: 'session_not_parkable', statusCode: 409 });
|
|
264
|
+
|
|
265
|
+
const now = Date.now();
|
|
266
|
+
const policyConfig = options.prewarmExpiry === true
|
|
267
|
+
? {
|
|
268
|
+
..._deps._lifecycleConfig,
|
|
269
|
+
idleParkAfterMs: 0,
|
|
270
|
+
disconnectCollapseMs: 0,
|
|
271
|
+
parkCooldownMs: 0,
|
|
272
|
+
}
|
|
273
|
+
: (options.policyConfig || _deps._lifecycleConfig);
|
|
274
|
+
const parkDecision = (at) => {
|
|
275
|
+
const view = _deps._sessionLifecycleView(session, { now: at });
|
|
276
|
+
if (view.state === 'parking') view.state = 'live';
|
|
277
|
+
if (options.manual === true) {
|
|
278
|
+
return _deps.evaluateManualPark(
|
|
279
|
+
view,
|
|
280
|
+
{ now: at, connectedClients: _deps._connectedLifecycleClientCount() },
|
|
281
|
+
_deps._lifecycleConfig,
|
|
282
|
+
);
|
|
283
|
+
} else if (options.prewarmExpiry === true) {
|
|
284
|
+
// Speculative activation must not protect itself. User intent still does.
|
|
285
|
+
view.isMru = false;
|
|
286
|
+
view.recentNonStatusActivityAt = 0;
|
|
287
|
+
view.lastActivatedAt = 0;
|
|
288
|
+
}
|
|
289
|
+
return _deps.evaluatePark(view, { now: at, connectedClients: _deps._connectedLifecycleClientCount() }, policyConfig);
|
|
290
|
+
};
|
|
291
|
+
// D-3: read the agent's own composer row immediately before the decision -- flush the mirror
|
|
292
|
+
// feed first, then the bounded read. `session._parkComposerDraft` is the verdict the decision
|
|
293
|
+
// (and the sweep's views + /api/diagnostics/lifecycle) reads.
|
|
294
|
+
session._parkComposerDraft = await _deps.readComposerDraft(session, { getRows: _deps._getHeadlessRows });
|
|
295
|
+
const decision = parkDecision(now);
|
|
296
|
+
if (!decision.eligible) {
|
|
297
|
+
const composerVerdict = session._parkComposerDraft || { state: 'unread', reason: 'none' };
|
|
298
|
+
console.log(`[lifecycle] park-refused id=${String(session.id).slice(0, 8)} reason=${reason} composer=${composerVerdict.state}/${composerVerdict.reason} protections=${decision.protections.join(',')}`);
|
|
299
|
+
return finishParkAttempt(
|
|
300
|
+
{ ok: false, code: 'session_protected', statusCode: 409, protections: decision.protections, retryAt: decision.protections.includes('restore') ? _deps.restoreRuntime.restoreProtectionUntil(session, { restoreInProgress: _deps._restoreInProgress, restoreCompletedAt: _deps._restoreCompletedAt, graceMs: _deps._RESTORE_STORM_GRACE_MS }) : null },
|
|
301
|
+
decision,
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const agentType = _deps.detectAgentType(session.cmd || '');
|
|
306
|
+
const resumeId = _deps._lifecycleResumeTarget(session, agentType);
|
|
307
|
+
const resumeSpec = _deps.canResumeAgent(agentType) ? _deps.getResumeSpec(agentType, resumeId) : null;
|
|
308
|
+
const deferredLaunchOwnership = require('./deferred-restore-attempt').captureDeferredRestoreLaunchOwnership(session);
|
|
309
|
+
if (session._modelExecutionRecipe && !deferredLaunchOwnership) {
|
|
310
|
+
return finishParkAttempt({ ok: false, code: 'launch_ownership_unavailable', statusCode: 409 }, decision);
|
|
311
|
+
}
|
|
312
|
+
const lifetime = { ptyProcess: session.ptyProcess, provider: session._providerId || '',
|
|
313
|
+
agentId: session._claudeSessionId || '', resumeId: session._providerResumeId || '',
|
|
314
|
+
generation: Number(session._launchPlanGeneration || 0), cmd: session.cmd, cwd: session.cwd };
|
|
315
|
+
const lifetimeMatches = (allowExitedPty = false) => _deps.getSession(session.id) === session
|
|
316
|
+
&& (session.ptyProcess === lifetime.ptyProcess || (allowExitedPty && session.ptyProcess === null))
|
|
317
|
+
&& (session._providerId || '') === lifetime.provider
|
|
318
|
+
&& (session._claudeSessionId || '') === lifetime.agentId && (session._providerResumeId || '') === lifetime.resumeId
|
|
319
|
+
&& Number(session._launchPlanGeneration || 0) === lifetime.generation && session.cmd === lifetime.cmd && session.cwd === lifetime.cwd;
|
|
320
|
+
const lifetimeCurrent = () => !session._parking && lifetimeMatches();
|
|
321
|
+
let prepared;
|
|
322
|
+
let freshSpec;
|
|
323
|
+
try {
|
|
324
|
+
prepared = await _prepareLifecycleTranscript(session);
|
|
325
|
+
freshSpec = await require('./park-fresh-relaunch').planFreshRelaunch({ session, agentType, resumeId,
|
|
326
|
+
transcript: prepared.transcript, codexRolloutPath: prepared.codexRolloutPath,
|
|
327
|
+
storedTask: _deps.dbModule.getStartupTask(session.id, { includeParked: true }),
|
|
328
|
+
resolveRung: (input) => require('./resume-from-store-client').resolveResumeRungOffThread({ ...input,
|
|
329
|
+
dbPath: _deps.dbModule.getDbPath(), trashRoot: _deps.SESSION_TRANSCRIPT_TRASH_ROOT }), diagnostic: _deps.recordSessionDiagnostic });
|
|
330
|
+
} catch (error) {
|
|
331
|
+
return finishParkAttempt({ ok: false, code: 'transcript_preparation_unavailable', statusCode: 503 }, decision);
|
|
332
|
+
}
|
|
333
|
+
if (!lifetimeCurrent()) return finishParkAttempt({ ok: false, code: 'park_lifetime_changed', statusCode: 409 }, decision);
|
|
334
|
+
if (!resumeSpec && !freshSpec) return finishParkAttempt({ ok: false, code: 'session_not_resumable', statusCode: 409 }, decision);
|
|
335
|
+
const transcriptBefore = await _verifyLifecycleTranscriptPhysical(prepared);
|
|
336
|
+
if (!lifetimeCurrent()) return finishParkAttempt({ ok: false, code: 'park_lifetime_changed', statusCode: 409 }, decision);
|
|
337
|
+
const preparedDecision = parkDecision(Date.now());
|
|
338
|
+
if (!preparedDecision.eligible) return finishParkAttempt({ ok: false, code: 'session_protected', statusCode: 409,
|
|
339
|
+
protections: preparedDecision.protections }, preparedDecision);
|
|
340
|
+
if (transcriptBefore.unknown) return finishParkAttempt({ ok: false, code: 'transcript_verification_unavailable', statusCode: 503 }, preparedDecision);
|
|
341
|
+
if (!transcriptBefore.missing && !resumeSpec) return finishParkAttempt({ ok: false, code: 'session_not_resumable', statusCode: 409 }, preparedDecision);
|
|
342
|
+
// WP-2 (docs/composer-freeze-followups-load-sigstop-attribution-2026-10-20.html §5 D-B1..D-B4): a giant rollout used to divert
|
|
343
|
+
// here into the deleted SIGSTOP "frozen" state. There is no such state any more -- the park
|
|
344
|
+
// falls through to the one kill ladder below (SIGTERM -> parkTermGraceMs -> SIGKILL).
|
|
345
|
+
const rolloutSizeBytes = Math.max(
|
|
346
|
+
0,
|
|
347
|
+
Number(session._resumeLoadingFileSize || session.fileSize || session.file_size) || 0,
|
|
348
|
+
Number(prepared.transcript?.size || 0),
|
|
349
|
+
Number(transcriptBefore.size || 0),
|
|
350
|
+
);
|
|
351
|
+
|
|
352
|
+
if (options.prewarmExpiry === true && session._prewarmExpiryTimer) {
|
|
353
|
+
clearTimeout(session._prewarmExpiryTimer);
|
|
354
|
+
session._prewarmExpiryTimer = null;
|
|
355
|
+
}
|
|
356
|
+
const pgid = _lifecycleProcessGroupId(session);
|
|
357
|
+
if (!pgid) return finishParkAttempt({ ok: false, code: 'process_group_unavailable', statusCode: 409 }, decision);
|
|
358
|
+
const rssBeforeMB = Number(session.rssMB || 0);
|
|
359
|
+
const inputGeneration = Math.max(1, Number(session._terminalLifecycleCounter || 0) + 1);
|
|
360
|
+
let resolveParkingTransition;
|
|
361
|
+
const parkingTransition = new Promise(resolve => { resolveParkingTransition = resolve; });
|
|
362
|
+
session._parking = true;
|
|
363
|
+
session._parkingPromise = parkingTransition;
|
|
364
|
+
session._parkingSignalSent = false;
|
|
365
|
+
session._parkingAbortRequested = false;
|
|
366
|
+
session._parkingInputQueue = [];
|
|
367
|
+
session._parkingInputGeneration = inputGeneration;
|
|
368
|
+
session._parkExitedAt = 0;
|
|
369
|
+
session._lifecycleState = 'parking';
|
|
370
|
+
// Register exit observation before the first park await. A provider may exit
|
|
371
|
+
// while snapshots are flushing, and that exit must complete this transaction.
|
|
372
|
+
const exitPromise = _deps._waitForLifecycleExit(session);
|
|
373
|
+
// D-4 (session-park-busy-background-shell-truth-2026-10-14): ONE ps read, the agent's descendant pid
|
|
374
|
+
// set, taken BEFORE any signal, so the post-kill audit can name survivors that lead their own process
|
|
375
|
+
// group. Placed here rather than beside `rssBeforeMB` above: this is the transaction's first await, so
|
|
376
|
+
// it must come AFTER `session._parking = true` (otherwise a re-entrant parkSession would still observe
|
|
377
|
+
// `_parking === false` and start a second transaction) and after the exit observation is registered (a
|
|
378
|
+
// provider exit during this await must still complete this transaction). Best-effort: a failed census
|
|
379
|
+
// degrades to the pgid-only rollup this audit has always done.
|
|
380
|
+
let descendantPidsBefore = [];
|
|
381
|
+
try { descendantPidsBefore = (await _deps._sessionResourceSampler.sampleDescendants(pgid)).descendantPids; }
|
|
382
|
+
catch (error) { console.warn(`[lifecycle] park-descendant-sample-failed id=${String(session.id).slice(0, 8)} pgid=${pgid} error=${error.message}`); }
|
|
383
|
+
const finishTransition = (result) => {
|
|
384
|
+
if (resolveParkingTransition) resolveParkingTransition(result);
|
|
385
|
+
resolveParkingTransition = null;
|
|
386
|
+
return result;
|
|
387
|
+
};
|
|
388
|
+
const abortBeforeSignal = (code, protections = []) => {
|
|
389
|
+
const ownsFields = session._parkingPromise === parkingTransition;
|
|
390
|
+
const sameProcess = ownsFields && lifetimeMatches() && parkOwnerMatches();
|
|
391
|
+
const queued = _deps.drainDeferredInputs(ownsFields ? session._parkingInputQueue : [], inputGeneration);
|
|
392
|
+
if (ownsFields) {
|
|
393
|
+
session._parkingInputQueue = [];
|
|
394
|
+
session._parkingAbortRequested = false;
|
|
395
|
+
session._parking = false;
|
|
396
|
+
session._parkingPromise = null;
|
|
397
|
+
session._lifecycleInputInFlight = false;
|
|
398
|
+
session._lifecycleState = session._parkExitedAt ? 'exited' : 'live';
|
|
399
|
+
session._lastActivatedAt = Date.now();
|
|
400
|
+
}
|
|
401
|
+
if (sameProcess && !session._parkExitedAt && session.ptyProcess) {
|
|
402
|
+
// T-B5: the one byte retention D-7 leaves -- park aborted before the TERM signal, the PTY
|
|
403
|
+
// is still the SAME live process, so the parked composer bytes are written, not refused.
|
|
404
|
+
for (const data of queued.data) _deps._writeLifecyclePty(session, data, 'park-abort');
|
|
405
|
+
_deps._promoteDeferredRuntimeInputs(session, queued.entries, 'park_abort_flush');
|
|
406
|
+
} else if (queued.entries.length && sameProcess) {
|
|
407
|
+
// D-6: the PTY did not survive the abort after all -- tell the client, don't drop silently.
|
|
408
|
+
_deps._dropDeferredRuntimeInputs(session, queued.entries, 'park_abort_pty_missing');
|
|
409
|
+
} else if (queued.entries.length) {
|
|
410
|
+
_deps._dropDeferredRuntimeInputs(session, queued.entries, 'park_generation_mismatch');
|
|
411
|
+
}
|
|
412
|
+
_deps._dropDeferredRuntimeInputs(session, queued.stale, 'park_generation_mismatch');
|
|
413
|
+
if (ownsFields) _deps.broadcastSessionList(true, { reason: 'park-abort' });
|
|
414
|
+
return finishParkAttempt(
|
|
415
|
+
finishTransition({ ok: false, code, statusCode: 409, protections }),
|
|
416
|
+
{ ...decision, protections },
|
|
417
|
+
);
|
|
418
|
+
};
|
|
419
|
+
const postSignalCurrent = () => session._parkingPromise === parkingTransition
|
|
420
|
+
&& lifetimeMatches(true) && parkOwnerMatches();
|
|
421
|
+
const finishStaleAfterSignal = (queueBeforeAwait, code = 'park_lifetime_changed') => {
|
|
422
|
+
const ownsFields = session._parkingPromise === parkingTransition;
|
|
423
|
+
// Enqueue is copy-on-append: observe the current old-owned queue. If another
|
|
424
|
+
// promise owns the fields, only the pre-await snapshot belongs to this cleanup.
|
|
425
|
+
const queued = _deps.drainDeferredInputs(ownsFields ? session._parkingInputQueue : queueBeforeAwait, inputGeneration);
|
|
426
|
+
if (ownsFields) {
|
|
427
|
+
session._parkingInputQueue = [];
|
|
428
|
+
session._parking = false;
|
|
429
|
+
session._parkingPromise = null;
|
|
430
|
+
session._parkingAbortRequested = false;
|
|
431
|
+
session._lifecycleInputInFlight = false;
|
|
432
|
+
if (session._lifecycleState === 'parking') session._lifecycleState = session.ptyProcess ? 'live' : 'exited';
|
|
433
|
+
}
|
|
434
|
+
_deps._dropDeferredRuntimeInputs(session, queued.entries, 'park_generation_mismatch');
|
|
435
|
+
_deps._dropDeferredRuntimeInputs(session, queued.stale, 'park_generation_mismatch');
|
|
436
|
+
return finishParkAttempt(finishTransition({ ok: false, code, statusCode: 409 }), decision);
|
|
437
|
+
};
|
|
438
|
+
const abortAfterSignal = (code) => {
|
|
439
|
+
const queueBeforeAwait = Array.isArray(session._parkingInputQueue) ? session._parkingInputQueue.slice() : [];
|
|
440
|
+
if (!postSignalCurrent() || !parkOwnerMatches()) return finishStaleAfterSignal(queueBeforeAwait, code);
|
|
441
|
+
session._parking = false;
|
|
442
|
+
session._parkingPromise = null;
|
|
443
|
+
if (session._lifecycleState === 'parking') session._lifecycleState = session.ptyProcess ? 'live' : 'exited';
|
|
444
|
+
return finishParkAttempt(finishTransition({ ok: false, code, statusCode: 409 }));
|
|
445
|
+
};
|
|
446
|
+
// The descendant census is an await before these helpers are available. Revalidate the captured
|
|
447
|
+
// parking owner before publishing anything so a replacement, user abort, or PTY exit during ps
|
|
448
|
+
// cannot announce parking or proceed into snapshot/signal work for a stale lifetime.
|
|
449
|
+
if (session._parkingPromise !== parkingTransition || !session._parking || !lifetimeMatches()) {
|
|
450
|
+
return abortBeforeSignal('park_lifetime_changed');
|
|
451
|
+
}
|
|
452
|
+
if (!parkOwnerMatches()) return abortBeforeSignal('park_owner_changed');
|
|
453
|
+
if (session._parkExitedAt || session._exited || !session.ptyProcess) {
|
|
454
|
+
return abortBeforeSignal('park_lifetime_changed');
|
|
455
|
+
}
|
|
456
|
+
if (session._parkingAbortRequested) {
|
|
457
|
+
const censusDecision = parkDecision(Date.now());
|
|
458
|
+
return abortBeforeSignal('park_race_protected', censusDecision.protections || []);
|
|
459
|
+
}
|
|
460
|
+
_deps.broadcastToSession(session.id, session, { type: 'session-parking', id: session.id, reason });
|
|
461
|
+
_deps.broadcastSessionList(true, { reason: 'park-start' });
|
|
462
|
+
// Give input already in flight from the parking notification a deterministic
|
|
463
|
+
// chance to reach _handleInputPrepared before the irreversible TERM signal.
|
|
464
|
+
const preSignalGraceMs = Math.max(0, Number(_deps._lifecycleConfig.parkPreSignalGraceMs || 0));
|
|
465
|
+
if (preSignalGraceMs > 0) {
|
|
466
|
+
await new Promise(resolve => setTimeout(resolve, preSignalGraceMs));
|
|
467
|
+
}
|
|
468
|
+
if (session._parkingPromise !== parkingTransition) return abortBeforeSignal('park_lifetime_changed');
|
|
469
|
+
if (!lifetimeMatches()) return abortBeforeSignal('park_lifetime_changed');
|
|
470
|
+
if (!parkOwnerMatches()) return abortBeforeSignal('park_owner_changed');
|
|
471
|
+
const transcriptAtSignal = await _lifecycleTranscriptStat(prepared);
|
|
472
|
+
if (session._parkingPromise !== parkingTransition) return abortBeforeSignal('park_lifetime_changed');
|
|
473
|
+
if (!lifetimeMatches()) return abortBeforeSignal('park_lifetime_changed');
|
|
474
|
+
if (!parkOwnerMatches()) return abortBeforeSignal('park_owner_changed');
|
|
475
|
+
if (transcriptAtSignal.unknown) return abortBeforeSignal('transcript_verification_unavailable');
|
|
476
|
+
let snapshotWritten = false;
|
|
477
|
+
try {
|
|
478
|
+
if (typeof session._drainPtyData === 'function') session._drainPtyData(true);
|
|
479
|
+
if (typeof session._flushHeadlessFeed === 'function') session._flushHeadlessFeed();
|
|
480
|
+
if (typeof session._flushScrollbackToDb === 'function') session._flushScrollbackToDb();
|
|
481
|
+
// WP-0 (session-runtime-reset-regression-triage-2026-09-06.html) deleted the WP-4
|
|
482
|
+
// pre-park flush-tail step that used to run here (_flushSessionTailBeforeDeath) -- it was
|
|
483
|
+
// dangling (deleted by the runtime reset merge, 16a10a4b9, while this call site survived).
|
|
484
|
+
// Restoring the flush-tail behavior itself is out of WP-0's scope; the snapshot below is
|
|
485
|
+
// unaffected and still runs, just without that pre-park archive top-up.
|
|
486
|
+
snapshotWritten = await _deps._snapshotSessionScrollbackToFile(session.id, 5_000);
|
|
487
|
+
} catch (error) {
|
|
488
|
+
console.warn(`[lifecycle] pre-park snapshot failed id=${String(session.id).slice(0, 8)} error=${error.message}`);
|
|
489
|
+
return abortBeforeSignal('snapshot_failed');
|
|
490
|
+
}
|
|
491
|
+
if (!snapshotWritten) {
|
|
492
|
+
return abortBeforeSignal('snapshot_unavailable');
|
|
493
|
+
}
|
|
494
|
+
if (session._parkingPromise !== parkingTransition) return abortBeforeSignal('park_lifetime_changed');
|
|
495
|
+
if (!lifetimeMatches()) return abortBeforeSignal('park_lifetime_changed');
|
|
496
|
+
if (!parkOwnerMatches()) return abortBeforeSignal('park_owner_changed');
|
|
497
|
+
|
|
498
|
+
const finalSnapshot = await _deps._readScrollbackSnapshotForRestoreAsync(
|
|
499
|
+
session.id,
|
|
500
|
+
resumeId,
|
|
501
|
+
'lifecycle-park-placeholder',
|
|
502
|
+
);
|
|
503
|
+
if (session._parkingPromise !== parkingTransition) return abortBeforeSignal('park_lifetime_changed');
|
|
504
|
+
if (!lifetimeMatches()) return abortBeforeSignal('park_lifetime_changed');
|
|
505
|
+
if (!parkOwnerMatches()) return abortBeforeSignal('park_owner_changed');
|
|
506
|
+
// pre-signal: close the snapshot race. Approval, draft, input, queue, focus,
|
|
507
|
+
// or work that arrived while flushing aborts before any signal is delivered.
|
|
508
|
+
// D-3: re-read the composer here too, so text typed during the flush window still refuses.
|
|
509
|
+
session._parkComposerDraft = await _deps.readComposerDraft(session, { getRows: _deps._getHeadlessRows });
|
|
510
|
+
const preSignalDecision = parkDecision(Date.now());
|
|
511
|
+
if (session._parkingAbortRequested || !preSignalDecision.eligible) {
|
|
512
|
+
return abortBeforeSignal('park_race_protected', preSignalDecision.protections);
|
|
513
|
+
}
|
|
514
|
+
if (!lifetimeMatches()) return abortBeforeSignal('park_lifetime_changed');
|
|
515
|
+
if (!parkOwnerMatches()) return abortBeforeSignal('park_owner_changed');
|
|
516
|
+
session._parkingSignalSent = true;
|
|
517
|
+
if (!_deps._signalLifecycleProcessGroup(session, 'SIGTERM', pgid)) {
|
|
518
|
+
return abortBeforeSignal('term_signal_failed');
|
|
519
|
+
}
|
|
520
|
+
let exited = await Promise.race([
|
|
521
|
+
exitPromise.then(() => true),
|
|
522
|
+
new Promise(resolve => setTimeout(() => resolve(false), _deps._lifecycleConfig.parkTermGraceMs)),
|
|
523
|
+
]);
|
|
524
|
+
if (!exited) {
|
|
525
|
+
if (!parkOwnerMatches() || !lifetimeMatches(true)) return abortAfterSignal('park_owner_changed_after_term');
|
|
526
|
+
_deps._signalLifecycleProcessGroup(session, 'SIGKILL', pgid);
|
|
527
|
+
exited = await Promise.race([
|
|
528
|
+
exitPromise.then(() => true),
|
|
529
|
+
new Promise(resolve => setTimeout(() => resolve(false), 2_000)),
|
|
530
|
+
]);
|
|
531
|
+
}
|
|
532
|
+
if (!parkOwnerMatches() || !lifetimeMatches(true)) return abortAfterSignal('park_owner_changed_after_exit_wait');
|
|
533
|
+
if (!exited) {
|
|
534
|
+
const queueBeforeAudit = Array.isArray(session._parkingInputQueue) ? session._parkingInputQueue.slice() : [];
|
|
535
|
+
const audit = await _auditParkedProcessGroup(session.id, pgid, rssBeforeMB, descendantPidsBefore);
|
|
536
|
+
if (!postSignalCurrent()) return finishStaleAfterSignal(queueBeforeAudit);
|
|
537
|
+
// D-4: survivors outside the agent's pgid are reported, never a reason to refuse the park — this
|
|
538
|
+
// branch keeps asking only whether the agent's OWN process group is gone (pre-D-4 semantics).
|
|
539
|
+
const groupProcCount = Number(audit.procCount || 0) - (Array.isArray(audit.survivorPids) ? audit.survivorPids.length : 0);
|
|
540
|
+
if (audit.auditFailed === true || groupProcCount !== 0) {
|
|
541
|
+
session._parking = false;
|
|
542
|
+
session._parkingPromise = null;
|
|
543
|
+
session._lifecycleState = 'live';
|
|
544
|
+
return finishParkAttempt(
|
|
545
|
+
finishTransition({ ok: false, code: 'process_exit_timeout', statusCode: 500 }),
|
|
546
|
+
decision,
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
session._lifecycleState = 'exited';
|
|
550
|
+
session.ptyProcess = null;
|
|
551
|
+
_deps.sessionInputAuthority.observeProvider({ sessionId: session.id, status: 'exited' });
|
|
552
|
+
session._exited = true;
|
|
553
|
+
exited = true;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
const queueBeforeVerification = Array.isArray(session._parkingInputQueue) ? session._parkingInputQueue.slice() : [];
|
|
557
|
+
const observedTranscriptAfter = await _lifecycleTranscriptStat(prepared);
|
|
558
|
+
if (!postSignalCurrent() || !parkOwnerMatches()) return finishStaleAfterSignal(queueBeforeVerification);
|
|
559
|
+
if (observedTranscriptAfter.unknown) return abortAfterSignal('transcript_verification_unavailable');
|
|
560
|
+
const transcriptAfter = await _verifyLifecycleTranscriptPhysical(prepared);
|
|
561
|
+
if (!postSignalCurrent()) return finishStaleAfterSignal(queueBeforeVerification);
|
|
562
|
+
if (!resumeSpec && (transcriptAfter.unknown || transcriptAfter.missing !== true)) {
|
|
563
|
+
// Unknown or newly appeared history without a resume identity cannot use the earlier
|
|
564
|
+
// fresh plan. Preserve explicit exited state/startup identity and refuse deferred bytes.
|
|
565
|
+
const queued = _deps.drainDeferredInputs(session._parkingInputQueue, inputGeneration);
|
|
566
|
+
session._parkingInputQueue = [];
|
|
567
|
+
session._parking = false;
|
|
568
|
+
session._parkingPromise = null;
|
|
569
|
+
session._parkingAbortRequested = false;
|
|
570
|
+
session._lifecycleInputInFlight = false;
|
|
571
|
+
session._lifecycleState = 'exited';
|
|
572
|
+
session._exited = true;
|
|
573
|
+
session.ptyProcess = null;
|
|
574
|
+
session.fileSize = Math.max(Number(session.fileSize || 0), Number(transcriptBefore?.size || 0),
|
|
575
|
+
Number(transcriptAfter.size || 0), Number(observedTranscriptAfter?.size || 0), rolloutSizeBytes);
|
|
576
|
+
session._resumeLoadingFileSize = Math.max(Number(session._resumeLoadingFileSize || 0), session.fileSize);
|
|
577
|
+
_deps.sessionInputAuthority.observeProvider({ sessionId: session.id, status: 'exited' });
|
|
578
|
+
_deps._dropDeferredRuntimeInputs(session, queued.entries, 'pty-missing');
|
|
579
|
+
_deps._dropDeferredRuntimeInputs(session, queued.stale, 'park_generation_mismatch');
|
|
580
|
+
_deps.broadcastSessionList(true, { reason: 'park-verify-unavailable' });
|
|
581
|
+
return finishParkAttempt(finishTransition({ ok: false, code: 'transcript_verification_unavailable', statusCode: 503 }), decision);
|
|
582
|
+
}
|
|
583
|
+
const lastOutputAt = Number(session.lastPtyActivity || 0);
|
|
584
|
+
const verifiedTranscriptMtimeMs = Number(transcriptAfter?.mtimeMs || observedTranscriptAfter?.mtimeMs || 0);
|
|
585
|
+
if (verifiedTranscriptMtimeMs > 0 && (!transcriptAfter || !transcriptAfter.missing) && lastOutputAt > 0
|
|
586
|
+
&& verifiedTranscriptMtimeMs + 1_000 < lastOutputAt) {
|
|
587
|
+
console.warn(`[lifecycle] park-verify id=${String(session.id).slice(0, 8)} transcriptMtime=${Math.floor(verifiedTranscriptMtimeMs)} lastOutputAt=${lastOutputAt}`);
|
|
588
|
+
} else if ((transcriptAtSignal?.filePath || transcriptBefore?.filePath)
|
|
589
|
+
&& (!transcriptAfter || transcriptAfter.missing)) {
|
|
590
|
+
console.warn(`[lifecycle] park-verify id=${String(session.id).slice(0, 8)} transcript unavailable after TERM`);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const restoreSpec = freshSpec && transcriptAfter?.missing === true && !transcriptAfter.unknown ? freshSpec : resumeSpec;
|
|
594
|
+
const task = {
|
|
595
|
+
session_id: session.id,
|
|
596
|
+
label: session.title || session.label || '',
|
|
597
|
+
cmd: restoreSpec.cmd || session.cmd,
|
|
598
|
+
args: Array.isArray(restoreSpec.args) ? restoreSpec.args.slice() : [],
|
|
599
|
+
cwd: session.cwd,
|
|
600
|
+
created_at: new Date(Number(session._ctmOriginalCreatedAt || session.createdAt || now)).toISOString(),
|
|
601
|
+
model_id: session.model_id || null,
|
|
602
|
+
model_provider: session.model_provider || null,
|
|
603
|
+
model_registry_id: session.model_registry_id || '',
|
|
604
|
+
model_provider_id: session.model_provider_id || '',
|
|
605
|
+
type: session.type || 'pty',
|
|
606
|
+
branch: session.branch || null,
|
|
607
|
+
worktree_path: session.worktree_path || null,
|
|
608
|
+
attention_dismissed_at: session._attentionDismissedAt || 0,
|
|
609
|
+
attention_dismissed_input_at: session._attentionDismissedInputAt || 0,
|
|
610
|
+
last_user_prompt_input_at: session._lastUserPromptInputAt || 0,
|
|
611
|
+
last_meaningful_activity_at: session.lastMeaningfulActivityAt || now,
|
|
612
|
+
lifecycle_pinned: session._lifecyclePinned ? 1 : 0,
|
|
613
|
+
};
|
|
614
|
+
const clients = Array.isArray(session.clients) ? session.clients.slice() : [];
|
|
615
|
+
const queuedParkingInputs = Array.isArray(session._parkingInputQueue) ? session._parkingInputQueue.slice() : [];
|
|
616
|
+
// S-19 (design doc §5/§9 WP-1): persist the last footprint sample the sweep cache had
|
|
617
|
+
// for this session before it's torn down, so a park doesn't simply lose it. Nullable
|
|
618
|
+
// (no sample yet) writes through as NULL, same as the in-memory field's own contract.
|
|
619
|
+
try {
|
|
620
|
+
// `void`: the injected dbModule is db-owner-client, whose invokeOwnerMutation already attaches the
|
|
621
|
+
// detached-error reporter to the returned promise; the try/catch keeps the sync (pre-arm) path covered.
|
|
622
|
+
void _deps.dbModule.updateSessionLastFootprint(session.id, session.footprintMB == null ? null : session.footprintMB);
|
|
623
|
+
} catch (error) {
|
|
624
|
+
// REPORT: best-effort diagnostic write -- must never block the park teardown below.
|
|
625
|
+
_deps.errorSink.report('lifecycle:persist-last-footprint', error, { sessionId: session.id });
|
|
626
|
+
}
|
|
627
|
+
// C-4: parkSession replaces the live aggregate with a deferred-restore placeholder and
|
|
628
|
+
// carries the live sockets forward (`placeholder.clients = clients` below) -- the
|
|
629
|
+
// session-runtime aggregate must survive the teardown so those carried-forward viewers
|
|
630
|
+
// stay attached, or planFrameFanout has nobody to deliver to until the client
|
|
631
|
+
// reconnects from scratch.
|
|
632
|
+
await _deps._destroySession(session.id, 'process-teardown', { retireRuntime: false });
|
|
633
|
+
_deps._lifecycleVisibilityGrace.delete(session.id);
|
|
634
|
+
const placeholder = _deps._createDeferredRestoreSession(task, task.args, agentType, {
|
|
635
|
+
restoreCwd: session.cwd,
|
|
636
|
+
restoreSnapshot: finalSnapshot,
|
|
637
|
+
});
|
|
638
|
+
_deps.copyWorkerMarkers(session, placeholder); // D-2: a parked worker keeps its markers, DB-free
|
|
639
|
+
if (!placeholder) {
|
|
640
|
+
_deps.sessions.set(session.id, session);
|
|
641
|
+
session._parking = false;
|
|
642
|
+
session._parkingPromise = null;
|
|
643
|
+
session._lifecycleState = 'exited';
|
|
644
|
+
session._exited = true;
|
|
645
|
+
session.ptyProcess = null;
|
|
646
|
+
_deps.sessionInputAuthority.observeProvider({ sessionId: session.id, status: 'exited' });
|
|
647
|
+
return finishParkAttempt(
|
|
648
|
+
finishTransition({ ok: false, code: 'placeholder_create_failed', statusCode: 500 }),
|
|
649
|
+
decision,
|
|
650
|
+
);
|
|
651
|
+
}
|
|
652
|
+
if (deferredLaunchOwnership
|
|
653
|
+
&& !require('./deferred-restore-attempt').transferDeferredRestoreLaunchOwnership(
|
|
654
|
+
deferredLaunchOwnership, placeholder)) {
|
|
655
|
+
_deps.recordSessionDiagnostic(session.id, 'park-launch-ownership-transfer-failed', {
|
|
656
|
+
sourceGeneration: deferredLaunchOwnership.processGeneration,
|
|
657
|
+
});
|
|
658
|
+
// A normal placeholder remains recoverable, but cannot authorize a history move without
|
|
659
|
+
// this in-memory source receipt. The handoff controller will fail closed on the missing
|
|
660
|
+
// source recipe/context rather than guessing from the durable startup task.
|
|
661
|
+
}
|
|
662
|
+
placeholder.clients = clients;
|
|
663
|
+
placeholder._lifecycleState = 'parked';
|
|
664
|
+
placeholder._parkedAt = Date.now();
|
|
665
|
+
placeholder._parkReason = reason;
|
|
666
|
+
placeholder._lastActivatedAt = session._lastActivatedAt || 0;
|
|
667
|
+
placeholder._lastActivation = session._lastActivation || null;
|
|
668
|
+
placeholder._lastFocusedAt = session._lastFocusedAt || 0;
|
|
669
|
+
placeholder.lastMeaningfulActivityAt = session.lastMeaningfulActivityAt || now;
|
|
670
|
+
placeholder._lifecyclePinned = !!session._lifecyclePinned;
|
|
671
|
+
placeholder._lastClientDisconnectAt = Number(session._lastClientDisconnectAt || 0);
|
|
672
|
+
placeholder._deferredInputGeneration = inputGeneration;
|
|
673
|
+
// WP-2 (D-4/D-7): park completed -- the parked composer bytes are NOT carried into the
|
|
674
|
+
// placeholder for later replay (no server queue retains terminal bytes across a PTY
|
|
675
|
+
// lifecycle any more). They are refused, with a reason of 'parked', and the sender is told
|
|
676
|
+
// now that the outcome is final.
|
|
677
|
+
if (queuedParkingInputs.length) _deps._dropDeferredRuntimeInputs(placeholder, queuedParkingInputs, 'parked');
|
|
678
|
+
placeholder._lifecycleInputInFlight = false;
|
|
679
|
+
placeholder._resumeLoadingFileSize = Math.max(
|
|
680
|
+
0,
|
|
681
|
+
Number(transcriptAfter?.size || transcriptBefore?.size || rolloutSizeBytes || 0),
|
|
682
|
+
);
|
|
683
|
+
placeholder.fileSize = placeholder._resumeLoadingFileSize;
|
|
684
|
+
placeholder.rssMB = 0;
|
|
685
|
+
placeholder.cpuPct = 0;
|
|
686
|
+
placeholder.procCount = 0;
|
|
687
|
+
placeholder.resourceSampledAt = Date.now();
|
|
688
|
+
// WP-6 (D-7/S-15): the park-time content fingerprint at the SAME dims as the snapshot
|
|
689
|
+
// (the scrollback-snapshot-store writer computes this alongside the serialized blob --
|
|
690
|
+
// see lib/scrollback-snapshot-store.js `fp`). This is the strongest server-side content
|
|
691
|
+
// identity that survives a stop: the headless mirror is torn down at park
|
|
692
|
+
// (`_destroySession` posts `{type:'destroy'}` to the worker), so
|
|
693
|
+
// there is no live mirror to sample against for a `_restoreDeferred` session -- only this
|
|
694
|
+
// stored fp. Only comparable when the activation grid still equals cols/rows above.
|
|
695
|
+
_deps._applyPersistedSnapshotToPlaceholder(placeholder, finalSnapshot);
|
|
696
|
+
session._parking = false;
|
|
697
|
+
session._parkingPromise = null;
|
|
698
|
+
finishTransition({ ok: true, sessionId: session.id, reason, parkedAt: placeholder._parkedAt });
|
|
699
|
+
_deps.cleanAutoApprovalBuffer(session.id);
|
|
700
|
+
_deps.cleanIdleNotify(session.id);
|
|
701
|
+
_deps.agentSessionKernel.forgetSession(session.id);
|
|
702
|
+
_deps.sessionDiagnostics.clear(session.id); // WP-3/S2: the ring survives park unless swept here
|
|
703
|
+
_deps._lifecycleDiagnostics.recordPark({ id: session.id, reason, idleMs: decision.idleMs });
|
|
704
|
+
console.log(`[lifecycle] park id=${String(session.id).slice(0, 8)} reason=${reason} idleMin=${Math.floor(decision.idleMs / 60_000)} victimRssMB=${Math.round(Number(session.rssMB || 0))}`);
|
|
705
|
+
for (const client of clients) {
|
|
706
|
+
if (client?.readyState === 1) {
|
|
707
|
+
_deps.sendDeferredRestoreStatus(client, placeholder, session.id, placeholder._ptyCols, placeholder._ptyRows, 'park', {
|
|
708
|
+
reason,
|
|
709
|
+
message: _deps.parkedSessionMessage(reason, Math.floor(decision.idleMs / 60_000)),
|
|
710
|
+
});
|
|
711
|
+
try {
|
|
712
|
+
_deps._wsSend(client, JSON.stringify({
|
|
713
|
+
type: 'session-parked',
|
|
714
|
+
id: session.id,
|
|
715
|
+
reason,
|
|
716
|
+
parkedAt: placeholder._parkedAt,
|
|
717
|
+
idleMin: Math.floor(decision.idleMs / 60_000),
|
|
718
|
+
}));
|
|
719
|
+
} catch { /* expected: client disconnected during lifecycle broadcast */ }
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
_deps.broadcastSessionList(true, { reason: 'session-parked' });
|
|
723
|
+
await _auditParkedProcessGroup(session.id, pgid, rssBeforeMB, descendantPidsBefore);
|
|
724
|
+
if (queuedParkingInputs.length) {
|
|
725
|
+
_deps._scheduleDeferredRestoreStart(placeholder, { source: 'queued-input', clientKind: 'desktop' });
|
|
726
|
+
}
|
|
727
|
+
return finishParkAttempt({ ok: true, sessionId: session.id, reason, parkedAt: placeholder._parkedAt }, decision);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
module.exports = {
|
|
731
|
+
configure,
|
|
732
|
+
_prepareLifecycleTranscript,
|
|
733
|
+
_verifyLifecycleTranscriptPhysical,
|
|
734
|
+
_lifecycleTranscriptStat,
|
|
735
|
+
_auditParkedProcessGroup,
|
|
736
|
+
_lifecycleProcessGroupId,
|
|
737
|
+
parkSession,
|
|
738
|
+
};
|