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,623 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Landing-throughput WP-5 shard S6 (design doc §5 D-10 "move-only", §6 "Shard
|
|
4
|
+
// module seam", §8 WP-6, §9 S-21..S-27): the HTTP attach handler
|
|
5
|
+
// (apiAttachSession, POST /api/sessions/attach) moves out of server.js VERBATIM
|
|
6
|
+
// into this module behind the same configure(deps) seam as
|
|
7
|
+
// lib/api/session-messages.js (S3). It registers NO routes -- the route stays in
|
|
8
|
+
// lib/api/sessions.js, whose configure() call in server.js now receives
|
|
9
|
+
// sessionAttachApi.apiAttachSession. The body is identical to server.js's
|
|
10
|
+
// declaration except for the `_deps.` prefix on every identifier that used to resolve
|
|
11
|
+
// to a server.js top-level binding (D-10 rule 1), proven by
|
|
12
|
+
// `node bin/move-only-check.js`. Four inline `require('./lib/...')` calls are rebased
|
|
13
|
+
// to this module's own `require('../...')` (S1/S3 precedent: injecting server.js's
|
|
14
|
+
// `require` is a loader-value transfer the public runtime closure guard refuses) -- the
|
|
15
|
+
// one deviation from a pure move-only diff.
|
|
16
|
+
//
|
|
17
|
+
// Injected deps, per function:
|
|
18
|
+
// apiAttachSession: CTM_FLUSH_BYTE_CAP, OUTPUT_FLUSH_BULK_INPUT_MAX_BYTES,
|
|
19
|
+
// OUTPUT_FLUSH_MAX_BYTES, PORT, PROJECT_ENTRY_RE, WALLE_PORT, WorkerTurnCounter,
|
|
20
|
+
// _answerPtyDeviceQueries, _appendScrollbackOnDbOwner, _clearRestoreInterruption,
|
|
21
|
+
// _clearScrollbackOnDbOwner, _codingSessionGenerationRuntime, _destroySession,
|
|
22
|
+
// _ensureSessionOutputGapWatchdog, _executeGeometryRevokeGraceForSession,
|
|
23
|
+
// _executionLocalityEnv, _forceFlowResume, _hasBufferedBusyStatusPtyEvidence,
|
|
24
|
+
// _isActivePtyChunk, _isBusyStatusPtyChunk, _isPtyUserInputEchoActivitySuppressed,
|
|
25
|
+
// _isServerWaitingForInput, _isShuttingDown, _isStatusOnlyPtyChunk, _mainTranscriptReads,
|
|
26
|
+
// _markHeartbeatDirty, _markProviderBusyStatusEvidence, _markServerSessionResumedFromPty,
|
|
27
|
+
// _markServerSessionTurnClosed, _markServerSessionTurnEvidence, _noteTerminalBell,
|
|
28
|
+
// _parseSessionMetaBatchOffThread, _perfWrap, _prependWalleBinToSessionPath,
|
|
29
|
+
// _providerIdFromCmd, _recordPtyChunkStatus, _retireStartupTaskOnDbOwner,
|
|
30
|
+
// _samplePtyDetectionData, _scheduleSecondaryPush, _sessionHostOwnsLiveHeadless,
|
|
31
|
+
// _spawnSessionPty, _stampSpawnAgentBundle, _stripPtyOutputReplies,
|
|
32
|
+
// _suppressCtmUiRefreshActivity, _useSessionHostProcessForPty, _wheelRttSampler, _wsSend,
|
|
33
|
+
// agentSessionKernel, applyAgentBundleToSpawn, applyClaudeRestoreBakDecision,
|
|
34
|
+
// applyLaunchEffortOverride, applyLaunchModelPin, broadcastSessionList,
|
|
35
|
+
// buildClaudeRendererLaunchEnv, buildTelemetryEnv, checkAutoApproval, checkIdleNotify,
|
|
36
|
+
// cleanAutoApprovalBuffer, cleanIdleNotify, createBoundedFlushScheduler,
|
|
37
|
+
// createSessionWithStartupTaskWithRetry, crypto, dbModule, delegateWorker,
|
|
38
|
+
// ensureWalleMcpForAgentSession, formatWorkerTerminalOutput, handleCreatePrepared,
|
|
39
|
+
// headlessWorker, oauthProxySupervisor, path, planCappedOutputAppend,
|
|
40
|
+
// ptyRuntimeActivityDecision, queueEngine, recordSessionDiagnostic, resolveAgentCliCommand,
|
|
41
|
+
// resolveSpawnBundleForSession, restoreRuntime, sanitizeAnthropicAuth,
|
|
42
|
+
// sanitizeInheritedAgentEnv, serverConfig, sessionDiagnostics, sessionEvents,
|
|
43
|
+
// sessionInputAuthority, sessionPolicy, sessions, setupQueueForSession,
|
|
44
|
+
// splitMirrorAndBrowserChunks, terminalOutputFlushDelay, treeHex
|
|
45
|
+
//
|
|
46
|
+
// Mutable module state is injected as a GETTER (D-10 rule 2): `_isShuttingDown` is a
|
|
47
|
+
// server.js `let` binding reassigned at runtime, so server.js injects
|
|
48
|
+
// `get _isShuttingDown() { return _isShuttingDown; }` rather than a frozen copy.
|
|
49
|
+
// No moved body assigns server.js state.
|
|
50
|
+
|
|
51
|
+
let _deps = null;
|
|
52
|
+
|
|
53
|
+
function configure(deps = {}) {
|
|
54
|
+
_deps = deps;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function apiAttachSession(req, res) {
|
|
58
|
+
let body = '';
|
|
59
|
+
req.on('data', chunk => { body += chunk; if (body.length > 1e6) req.destroy(); });
|
|
60
|
+
req.on('end', async () => {
|
|
61
|
+
try {
|
|
62
|
+
const { session_id, cwd: rawCwd } = JSON.parse(body);
|
|
63
|
+
if (!session_id) {
|
|
64
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
65
|
+
res.end(JSON.stringify({ error: 'session_id required' }));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (typeof session_id !== 'string' || !_deps.PROJECT_ENTRY_RE.test(session_id) || session_id === '.' || session_id === '..') {
|
|
70
|
+
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
71
|
+
res.end(JSON.stringify({ error: 'Invalid session_id format' }));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const tabId = _deps.crypto.randomUUID();
|
|
76
|
+
const cwdDir = (rawCwd || _deps.serverConfig.HOME).replace(/^~/, _deps.serverConfig.HOME);
|
|
77
|
+
const env = { ...process.env, PWD: cwdDir };
|
|
78
|
+
_deps._prependWalleBinToSessionPath(env);
|
|
79
|
+
_deps.sanitizeInheritedAgentEnv(env);
|
|
80
|
+
const claudeCmd = _deps.resolveAgentCliCommand('claude');
|
|
81
|
+
_deps.sanitizeAnthropicAuth(env, claudeCmd);
|
|
82
|
+
Object.assign(env, _deps._executionLocalityEnv());
|
|
83
|
+
// Resume path: inject CTM telemetry env the same way as the create path.
|
|
84
|
+
Object.assign(env, _deps.buildTelemetryEnv(tabId, claudeCmd, _deps.PORT, env));
|
|
85
|
+
Object.assign(env, _deps.buildClaudeRendererLaunchEnv(claudeCmd, env)); // restore-seed-alt-screen-trap R-4
|
|
86
|
+
_deps.ensureWalleMcpForAgentSession({
|
|
87
|
+
cmd: claudeCmd,
|
|
88
|
+
wallePort: _deps.WALLE_PORT,
|
|
89
|
+
homeDir: env.HOME || _deps.serverConfig.HOME,
|
|
90
|
+
env,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Restore .jsonl from .bak if needed (Claude Code migrates sessions to
|
|
94
|
+
// directory format, and an interrupted compaction can leave a live
|
|
95
|
+
// metadata-only stub while the conversation survives in .bak).
|
|
96
|
+
const claudeProjectsDir = _deps.path.join(_deps.serverConfig.HOME, '.claude', 'projects');
|
|
97
|
+
const unavailable = () => Object.assign(new Error('Session preparation is unavailable; retry shortly'), { code: 'READ_POOL_UNAVAILABLE', statusCode: 503 });
|
|
98
|
+
const entries = await _deps._mainTranscriptReads.listClaudeProjectDirectoryNames();
|
|
99
|
+
const candidates = entries.map((entry) => {
|
|
100
|
+
const livePath = _deps.path.join(claudeProjectsDir, entry, `${session_id}.jsonl`);
|
|
101
|
+
return { livePath, bakPath: `${livePath}.bak` };
|
|
102
|
+
});
|
|
103
|
+
const decisions = await _deps._mainTranscriptReads.prepareRestoreBackups(candidates, { requireAvailable: true });
|
|
104
|
+
const stats = await _deps._mainTranscriptReads.statPaths(candidates.flatMap((candidate) => [candidate.livePath, candidate.bakPath]), { requireAvailable: true });
|
|
105
|
+
if (!decisions || !stats || candidates.some((candidate) => [candidate.livePath, candidate.bakPath]
|
|
106
|
+
.some((p) => !stats.get(p) || stats.get(p).state === 'error'))) throw unavailable();
|
|
107
|
+
let chosen = null;
|
|
108
|
+
for (let i = 0; i < decisions.length; i++) {
|
|
109
|
+
const decision = decisions[i];
|
|
110
|
+
if (decision.reason === 'unavailable' || decision.live?.state === 'unavailable' || decision.bak?.state === 'unavailable') throw unavailable();
|
|
111
|
+
if (decision.eligible) {
|
|
112
|
+
const applied = await _deps.applyClaudeRestoreBakDecision(decision, { db: _deps.dbModule.getDb() });
|
|
113
|
+
// A failed apply can have renamed the stub already. Never reuse pre-apply exists=true.
|
|
114
|
+
if (!applied.restored) {
|
|
115
|
+
console.error('[attach] backup apply failed:', {
|
|
116
|
+
reason: applied.reason, errorCode: applied.errorCode || '', stubPath: applied.stubPath || '',
|
|
117
|
+
});
|
|
118
|
+
throw unavailable();
|
|
119
|
+
}
|
|
120
|
+
chosen = { filePath: decision.livePath, projectEntry: entries[i] };
|
|
121
|
+
} else if (stats.get(decision.livePath)?.exists) {
|
|
122
|
+
chosen = { filePath: decision.livePath, projectEntry: entries[i] };
|
|
123
|
+
}
|
|
124
|
+
if (chosen) break;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Guard: `claude --resume <id>` exits 1 if the JSONL doesn't exist, which
|
|
128
|
+
// triggers an auto-restart loop with the same missing ID. Surface a clear 404
|
|
129
|
+
// instead of letting Claude Code crash. The identity row is KEPT (design D-1):
|
|
130
|
+
// vendor retention deletes transcripts, the conversation may still be stored.
|
|
131
|
+
if (!chosen) {
|
|
132
|
+
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
133
|
+
res.end(JSON.stringify({ error: 'Session JSONL not found on disk', session_id, identityKept: true }));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// Prepare the existing DB/worker title before spawn: the new metadata await must not delay
|
|
138
|
+
// registration of the PTY's first output handler. Only the already-chosen path is parsed.
|
|
139
|
+
let label = '';
|
|
140
|
+
try {
|
|
141
|
+
const titleInfo = _deps.dbModule.getSessionTitleNew(session_id);
|
|
142
|
+
if (titleInfo && titleInfo.title) label = titleInfo.title;
|
|
143
|
+
} catch (e) { console.error('[attach] getSessionTitleNew error:', e.message); }
|
|
144
|
+
if (!label) {
|
|
145
|
+
try {
|
|
146
|
+
const results = await _deps._parseSessionMetaBatchOffThread([chosen], { metadataProfile: 'display' });
|
|
147
|
+
label = results?.find((row) => row?.filePath === chosen.filePath)?.parsed?.title || '';
|
|
148
|
+
} catch (e) { console.error('[attach] label preparation error:', e.message); }
|
|
149
|
+
}
|
|
150
|
+
if (!label) label = 'Attached: ' + session_id.slice(0, 8) + '...';
|
|
151
|
+
|
|
152
|
+
let ptyProcess;
|
|
153
|
+
const terminalOutputEpoch = _deps.crypto.randomUUID();
|
|
154
|
+
// WP-0b (design §9 S2, AC-0g): reserve BEFORE the PTY exists — a refused reservation leaves zero processes.
|
|
155
|
+
const attachLaunch = _deps._codingSessionGenerationRuntime.revalidateReservation(await _deps._codingSessionGenerationRuntime.prepareAttachedLaunch({ sessionId: tabId, providerThreadId: session_id, env }));
|
|
156
|
+
if (!attachLaunch.ok) { res.writeHead(503, { 'Content-Type': 'application/json' }); res.end(JSON.stringify({ error: 'Launch generation unavailable', session_id })); return; }
|
|
157
|
+
// WP-3 (docs/claude-resume-managed-model-pin-2026-10-16.html §6 D-7, §9 S-7): the attach path
|
|
158
|
+
// spawns outside createProviderSession, so the session's stored Claude lead-model pin is replayed
|
|
159
|
+
// here on THIS spawn's argv only. An adopted external session has no CTM row yet, so the read
|
|
160
|
+
// returns null and the argv is today's (design R-6). The `session.args` below and the startup
|
|
161
|
+
// task stay clean -- the pin is never stored.
|
|
162
|
+
// WP-2 (docs/claude-lead-model-pin-followups-2026-10-17.html §5.1 D-6b): the same spawn also
|
|
163
|
+
// replays the session's stored lead effort (`session_lead_effort`), because a plain `claude
|
|
164
|
+
// --resume` keeps neither the chosen model nor the chosen effort. The effort read has its OWN
|
|
165
|
+
// failure mode: an unreadable effort records lead-effort-read-failed and the spawn keeps the
|
|
166
|
+
// pinned argv. Neither value is persisted -- both reach this spawn's argv only.
|
|
167
|
+
// WP-4 (docs/agent-bundle-darwin-readonly-rename-2026-10-27.html §16 D-11/D-12, §17): the attach
|
|
168
|
+
// path spawns outside createProviderSession, so it applied no bundle and stamped no tree — an
|
|
169
|
+
// attached session silently ran without the hub's skills (census gap D-12). The SAME resolver,
|
|
170
|
+
// transform and diagnostic the create path uses (provider-process.js:687/:940), applied to THIS
|
|
171
|
+
// spawn's argv/env only: `session.args` below and the startup task stay clean, so a later restore
|
|
172
|
+
// re-resolves the hub's CURRENT pointer (D-7). No hub tree → null → today's argv/env byte for byte.
|
|
173
|
+
const attachBundle = await _deps.resolveSpawnBundleForSession(tabId, { report: (event, details) => _deps.recordSessionDiagnostic?.(tabId, event, details) });
|
|
174
|
+
let attachSpawnEnv = attachLaunch.env;
|
|
175
|
+
const attachSpawnArgs = (() => {
|
|
176
|
+
let args = ['--resume', session_id];
|
|
177
|
+
try {
|
|
178
|
+
if (typeof _deps.dbModule.getSessionLeadModel === 'function') {
|
|
179
|
+
args = _deps.applyLaunchModelPin({ agentType: 'claude', args: ['--resume', session_id],
|
|
180
|
+
pin: _deps.dbModule.getSessionLeadModel(tabId), env: attachLaunch.env }).args;
|
|
181
|
+
}
|
|
182
|
+
} catch (error) {
|
|
183
|
+
if (typeof _deps.recordSessionDiagnostic === 'function') _deps.recordSessionDiagnostic(tabId, 'lead-model-pin-read-failed', { message: String(error && error.message || error).slice(0, 200) });
|
|
184
|
+
/* REPORT: an unreadable pin never blocks an attach; the spawn keeps today's argv */
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const effort = _deps.dbModule.getSessionLeadEffort?.(tabId)?.effort;
|
|
188
|
+
if (typeof effort === 'string') args = _deps.applyLaunchEffortOverride(args, 'claude', effort).args;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
if (typeof _deps.recordSessionDiagnostic === 'function') _deps.recordSessionDiagnostic(tabId, 'lead-effort-read-failed', { message: String(error && error.message || error).slice(0, 200) });
|
|
191
|
+
/* REPORT: an unreadable effort never blocks an attach; the spawn keeps the pinned argv */
|
|
192
|
+
}
|
|
193
|
+
// WP-4: pinned argv + the bundle's --add-dir is the ONE argv this spawn receives (last, after pin and effort).
|
|
194
|
+
const bundled = _deps.applyAgentBundleToSpawn({ launchArgs: args, spawnEnv: attachLaunch.env, agentType: 'claude', bundle: attachBundle }); attachSpawnEnv = bundled.spawnEnv; args = bundled.launchArgs;
|
|
195
|
+
if (bundled.applied) _deps.recordSessionDiagnostic?.(tabId, 'agent-bundle-applied', { treeHash: attachBundle.treeHash, hubPeerId: attachBundle.hubPeerId });
|
|
196
|
+
return args;
|
|
197
|
+
})();
|
|
198
|
+
try {
|
|
199
|
+
ptyProcess = _deps._spawnSessionPty({
|
|
200
|
+
sessionId: tabId,
|
|
201
|
+
cmd: claudeCmd,
|
|
202
|
+
args: attachSpawnArgs,
|
|
203
|
+
cols: 120,
|
|
204
|
+
rows: 30,
|
|
205
|
+
cwd: cwdDir,
|
|
206
|
+
env: attachSpawnEnv,
|
|
207
|
+
outputEpoch: terminalOutputEpoch,
|
|
208
|
+
});
|
|
209
|
+
} catch (e) {
|
|
210
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
211
|
+
res.end(JSON.stringify({ error: 'Failed to spawn: ' + e.message }));
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const attachedSessionSpawnedAt = Date.now();
|
|
216
|
+
const session = {
|
|
217
|
+
id: tabId,
|
|
218
|
+
...attachLaunch.owner,
|
|
219
|
+
label,
|
|
220
|
+
cmd: 'claude',
|
|
221
|
+
args: ['--resume', session_id],
|
|
222
|
+
cwd: cwdDir,
|
|
223
|
+
pid: ptyProcess.pid,
|
|
224
|
+
ptyProcess,
|
|
225
|
+
// WI-3 (resume-grid-mismatch-render-churn): see the primary spawn site's comment.
|
|
226
|
+
_ptySpawnedAt: attachedSessionSpawnedAt,
|
|
227
|
+
clients: [],
|
|
228
|
+
// scrollback removed — headless terminal is source of truth (Phase 3D)
|
|
229
|
+
createdAt: attachedSessionSpawnedAt,
|
|
230
|
+
lastActivity: attachedSessionSpawnedAt,
|
|
231
|
+
lastPtyActivity: 0,
|
|
232
|
+
_ptyCols: 120,
|
|
233
|
+
_ptyRows: 30,
|
|
234
|
+
model_id: null,
|
|
235
|
+
model_provider: null,
|
|
236
|
+
_autoRestartCount: 0,
|
|
237
|
+
_terminalOutputEpoch: terminalOutputEpoch,
|
|
238
|
+
};
|
|
239
|
+
// WP-3/D-11, applied here by WP-4: the SAME spawn stamp the create path applies, so an attached
|
|
240
|
+
// session's bundle truth reaches the drift reconciler and `_sessionPayload.agentBundle` (and the
|
|
241
|
+
// store GC's live-set) exactly like a created one's. `treeHex` of a null bundle is null.
|
|
242
|
+
_deps._stampSpawnAgentBundle({ session, agentBundleTreeHash: _deps.treeHex(attachBundle?.treeHash) });
|
|
243
|
+
|
|
244
|
+
_deps.sessions.set(tabId, session);
|
|
245
|
+
_deps.sessionInputAuthority.observeProvider({ sessionId: tabId, status: 'ready' });
|
|
246
|
+
if (typeof ptyProcess.onStarted === 'function') {
|
|
247
|
+
ptyProcess.onStarted((started = {}) => {
|
|
248
|
+
session.pid = started.pid || session.pid || ptyProcess.pid;
|
|
249
|
+
session._hostPid = ptyProcess.hostPid || null;
|
|
250
|
+
_deps.broadcastSessionList(true, { reason: 'attach-started' });
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Create headless terminal for legacy PTYs. Hosted PTYs create their
|
|
255
|
+
// embedded headless terminal inside the child process before the PTY
|
|
256
|
+
// starts, so the primary process must not send a second create that could
|
|
257
|
+
// reset early output.
|
|
258
|
+
if (!_deps._sessionHostOwnsLiveHeadless(tabId)) {
|
|
259
|
+
_deps.headlessWorker.postMessage({ type: 'create', sessionId: tabId, cols: 120, rows: 30, outputEpoch: terminalOutputEpoch });
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Durable restore identity for crash-safe restore: the startup_tasks row AND the
|
|
263
|
+
// ctm_sessions/agent_sessions rows in ONE transaction, through the same helper the ordinary
|
|
264
|
+
// create path uses (lib/session-create/provider-process.js:1394). The attach route used to
|
|
265
|
+
// write ONLY the startup task, so an API-only attached tab had no agent_sessions owner row for
|
|
266
|
+
// the provider thread it actually resumed and every owner-keyed durable store refused it on its
|
|
267
|
+
// own read (lib/storage/coding-session-model-store.js's bind -> ownerRow requires the exact
|
|
268
|
+
// ctm_session_id + agent_session_id + provider tuple) -- the attached session could never
|
|
269
|
+
// publish durable model state.
|
|
270
|
+
// The identity is the REAL provider thread this child was launched with (`--resume session_id`),
|
|
271
|
+
// never the CTM tab UUID: the tab id is only the ctm_session_id. Fire-and-forget on purpose --
|
|
272
|
+
// the promise is exposed on the live session exactly like the create path's
|
|
273
|
+
// `session._startupPersistence`, and nothing is awaited between the spawn above and the
|
|
274
|
+
// onData/onExit registration below, which would risk losing early PTY bytes.
|
|
275
|
+
session._startupPersistence = _deps.createSessionWithStartupTaskWithRetry({
|
|
276
|
+
sessionId: tabId,
|
|
277
|
+
label,
|
|
278
|
+
cmd: 'claude',
|
|
279
|
+
args: ['--resume', session_id],
|
|
280
|
+
cwd: cwdDir,
|
|
281
|
+
sessionData: {
|
|
282
|
+
agentSessionId: session_id,
|
|
283
|
+
provider: 'claude',
|
|
284
|
+
cwd: cwdDir,
|
|
285
|
+
title: label,
|
|
286
|
+
projectEntry: chosen.projectEntry,
|
|
287
|
+
projectPath: cwdDir,
|
|
288
|
+
},
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Output batching (same as main session path)
|
|
292
|
+
let outputBuf2 = '';
|
|
293
|
+
let outputBufBytes2 = 0;
|
|
294
|
+
let outputFlushTimer2 = null;
|
|
295
|
+
let headlessFeedBuf2 = '';
|
|
296
|
+
let headlessFeedTimer2 = null;
|
|
297
|
+
let _sb2Seq = 0, _sb2Pending = [], _sb2Timer = null;
|
|
298
|
+
function _flushSb2() { _sb2Timer = null; if (!_sb2Pending.length) return; const w = _sb2Pending; _sb2Pending = []; _deps._appendScrollbackOnDbOwner(tabId, w); }
|
|
299
|
+
session._flushScrollbackToDb = _flushSb2;
|
|
300
|
+
function flushHeadlessFeed2() {
|
|
301
|
+
if (headlessFeedTimer2) {
|
|
302
|
+
clearTimeout(headlessFeedTimer2);
|
|
303
|
+
headlessFeedTimer2 = null;
|
|
304
|
+
}
|
|
305
|
+
if (!headlessFeedBuf2) return;
|
|
306
|
+
const batch = headlessFeedBuf2;
|
|
307
|
+
headlessFeedBuf2 = '';
|
|
308
|
+
if (!_deps.sessions.has(tabId) || _deps.sessions.get(tabId) !== session) return;
|
|
309
|
+
if (_deps._sessionHostOwnsLiveHeadless(tabId)) return;
|
|
310
|
+
_deps.headlessWorker.postMessage({ type: 'feed', sessionId: tabId, data: batch });
|
|
311
|
+
}
|
|
312
|
+
function enqueueHeadlessFeed2(data) {
|
|
313
|
+
if (!data) return;
|
|
314
|
+
headlessFeedBuf2 += data;
|
|
315
|
+
if (!headlessFeedTimer2) headlessFeedTimer2 = setTimeout(flushHeadlessFeed2, 16);
|
|
316
|
+
}
|
|
317
|
+
session._flushHeadlessFeed = flushHeadlessFeed2;
|
|
318
|
+
function flushOutput2() {
|
|
319
|
+
if (_deps.CTM_FLUSH_BYTE_CAP && outputFlushTimer2) clearTimeout(outputFlushTimer2);
|
|
320
|
+
outputFlushTimer2 = null;
|
|
321
|
+
if (!outputBuf2) return;
|
|
322
|
+
const batch = outputBuf2;
|
|
323
|
+
outputBuf2 = '';
|
|
324
|
+
outputBufBytes2 = 0;
|
|
325
|
+
_sb2Pending.push({ seq: _sb2Seq++, data: batch }); _deps.sessionEvents.emit('session:tail-dirty', { id: tabId });
|
|
326
|
+
if (!_sb2Timer) _sb2Timer = setTimeout(_flushSb2, 1000);
|
|
327
|
+
// State-sync delivers these bytes via the next 'frame' (the worker mirror, fed via
|
|
328
|
+
// enqueueHeadlessFeed2 / the session host, already holds them). Scrollback is persisted above;
|
|
329
|
+
// secondary viewers get a reflowed snapshot below.
|
|
330
|
+
// Render secondary viewers at their own width from a debounced reflowed
|
|
331
|
+
// snapshot (no-op + free when there are no secondaries).
|
|
332
|
+
if (_deps.serverConfig.CTM_DEBUG_SECPUSH) console.warn(`[flushout] ${tabId.slice(0,6)} hasSecondary=${!!session._hasSecondaryViewers} clients=${session.clients.length}`);
|
|
333
|
+
if (session._hasSecondaryViewers) _deps._scheduleSecondaryPush(session, tabId, 'output');
|
|
334
|
+
}
|
|
335
|
+
function flushForcedOutput2(batch) {
|
|
336
|
+
if (!batch) return;
|
|
337
|
+
const pending = outputBuf2;
|
|
338
|
+
const pendingBytes = outputBufBytes2;
|
|
339
|
+
const pendingTimer = outputFlushTimer2;
|
|
340
|
+
outputBuf2 = batch;
|
|
341
|
+
outputBufBytes2 = Buffer.byteLength(batch, 'utf8');
|
|
342
|
+
outputFlushTimer2 = null;
|
|
343
|
+
try {
|
|
344
|
+
flushOutput2();
|
|
345
|
+
} finally {
|
|
346
|
+
outputBuf2 = pending;
|
|
347
|
+
outputBufBytes2 = pendingBytes;
|
|
348
|
+
outputFlushTimer2 = pendingTimer;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const cappedFlushScheduler2 = _deps.createBoundedFlushScheduler({
|
|
352
|
+
flush: flushForcedOutput2,
|
|
353
|
+
onIdle: () => {
|
|
354
|
+
if (outputBuf2 && !outputFlushTimer2) {
|
|
355
|
+
outputFlushTimer2 = setTimeout(
|
|
356
|
+
flushOutput2,
|
|
357
|
+
_deps.terminalOutputFlushDelay(outputBuf2, session),
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
},
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const attachedPtyDataHandler = _deps._perfWrap(`pty:onData:${tabId.slice(0, 8)}`, (data) => {
|
|
364
|
+
_deps._wheelRttSampler.onOutput(tabId);
|
|
365
|
+
const now = Date.now();
|
|
366
|
+
// RAW output recency for the mid-stream resize guard (_ptyOutputInFlight) —
|
|
367
|
+
// unfiltered on purpose; see the matching stamp in the queued onData path.
|
|
368
|
+
session._lastRawPtyOutputAt = now;
|
|
369
|
+
_deps._ensureSessionOutputGapWatchdog(session, tabId)?.notePtyOutput(
|
|
370
|
+
now,
|
|
371
|
+
Buffer.byteLength(String(data || ''), 'utf8'),
|
|
372
|
+
);
|
|
373
|
+
_deps._markHeartbeatDirty(session);
|
|
374
|
+
_deps._answerPtyDeviceQueries(session, tabId, data, 'feed-attached');
|
|
375
|
+
const rawTerminalData = _deps._stripPtyOutputReplies(session, tabId, data, 'feed-attached');
|
|
376
|
+
// Count raw worker turns before projection, then schedule progress checkpoints
|
|
377
|
+
// (spark-worker-latency-prompt-diet §8 WP-3).
|
|
378
|
+
if (session.agentRole === 'worker') {
|
|
379
|
+
session._workerTurnCounter ||= new _deps.WorkerTurnCounter({ agent: session._workerAgent });
|
|
380
|
+
if (session._workerTurnCounter.feed(rawTerminalData)) session._workerTurnCount = session._workerTurnCounter.count;
|
|
381
|
+
_deps.delegateWorker.scheduleWorkerObservation(session.id, session);
|
|
382
|
+
}
|
|
383
|
+
const terminalData = _deps.formatWorkerTerminalOutput(session, rawTerminalData);
|
|
384
|
+
// A BEL that terminates a stripped color reply is not an approval signal.
|
|
385
|
+
_deps._noteTerminalBell(tabId, session, terminalData);
|
|
386
|
+
const detectionData = _deps._samplePtyDetectionData(terminalData);
|
|
387
|
+
const activeChunk = _deps._isActivePtyChunk(session, detectionData);
|
|
388
|
+
const statusOnlyChunk = _deps._isStatusOnlyPtyChunk(session, detectionData);
|
|
389
|
+
const busyStatusChunk = _deps._isBusyStatusPtyChunk(session, detectionData);
|
|
390
|
+
const busyStatusEvidenceChunk = busyStatusChunk || _deps._hasBufferedBusyStatusPtyEvidence(session, detectionData, now);
|
|
391
|
+
const waitingForInput = _deps._isServerWaitingForInput(tabId, session);
|
|
392
|
+
const suppressedUiRefreshChunk = _deps._suppressCtmUiRefreshActivity(session, statusOnlyChunk);
|
|
393
|
+
const restoreActivitySuppressed = _deps.restoreRuntime.restoreActivitySuppressed(session, now);
|
|
394
|
+
const suppressedInputEchoChunk = _deps._isPtyUserInputEchoActivitySuppressed(session, detectionData, {
|
|
395
|
+
statusOnlyChunk,
|
|
396
|
+
busyStatusChunk,
|
|
397
|
+
});
|
|
398
|
+
if (busyStatusEvidenceChunk && !restoreActivitySuppressed && !suppressedInputEchoChunk) {
|
|
399
|
+
_deps._markProviderBusyStatusEvidence(
|
|
400
|
+
tabId,
|
|
401
|
+
session,
|
|
402
|
+
now,
|
|
403
|
+
suppressedUiRefreshChunk ? 'attached-pty-ui-refresh' : 'attached-pty'
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
const { activityChunk, runtimeActivityChunk } = _deps.ptyRuntimeActivityDecision({
|
|
407
|
+
activeChunk,
|
|
408
|
+
statusOnlyChunk,
|
|
409
|
+
busyStatusChunk,
|
|
410
|
+
waitingForInput,
|
|
411
|
+
suppressedUiRefreshChunk,
|
|
412
|
+
suppressedInputEchoChunk,
|
|
413
|
+
restoreActivitySuppressed,
|
|
414
|
+
});
|
|
415
|
+
if (busyStatusChunk && waitingForInput && !restoreActivitySuppressed && !suppressedInputEchoChunk) {
|
|
416
|
+
const providerId = session?._providerId || _deps._providerIdFromCmd(session?.cmd || '') || 'provider';
|
|
417
|
+
_deps._markServerSessionResumedFromPty(tabId, session, `${providerId}-busy-status`);
|
|
418
|
+
}
|
|
419
|
+
_deps._recordPtyChunkStatus(session, detectionData, {
|
|
420
|
+
now, waitingForInput, suppressedUiRefreshChunk, suppressedInputEchoChunk, restoreActivitySuppressed,
|
|
421
|
+
});
|
|
422
|
+
if (runtimeActivityChunk) {
|
|
423
|
+
session.lastActivity = now;
|
|
424
|
+
session.lastPtyActivity = now;
|
|
425
|
+
if (session._activeTurnOpen) _deps._markServerSessionTurnEvidence(session, now, 'attached-pty-activity', { ledger: false });
|
|
426
|
+
_deps.restoreRuntime.clearRestoreState(session);
|
|
427
|
+
_deps._clearRestoreInterruption(session, now, 'attached-pty-activity');
|
|
428
|
+
}
|
|
429
|
+
// TWO feeds, one chunk -- the same shared split as the primary feed site in
|
|
430
|
+
// lib/session-create/response.js. `mirrorData` feeds the headless MIRROR, the authority for
|
|
431
|
+
// the app's mouse-tracking mode (R-2) and therefore the one copy that must keep the
|
|
432
|
+
// ?1000/?1002/?1003/?1006/?1015 enables; `cleanData` feeds the BROWSER and is byte-identical
|
|
433
|
+
// to the pre-change computation (R-10). See splitMirrorAndBrowserChunks for why the
|
|
434
|
+
// transform order matters.
|
|
435
|
+
const { mirror: mirrorData, browser: cleanData } = _deps.splitMirrorAndBrowserChunks(
|
|
436
|
+
terminalData,
|
|
437
|
+
session.cmd || '',
|
|
438
|
+
);
|
|
439
|
+
if (!_deps.CTM_FLUSH_BYTE_CAP) {
|
|
440
|
+
outputBuf2 += cleanData;
|
|
441
|
+
if (!outputFlushTimer2) {
|
|
442
|
+
outputFlushTimer2 = setTimeout(flushOutput2, _deps.terminalOutputFlushDelay(cleanData, session));
|
|
443
|
+
}
|
|
444
|
+
} else if (cleanData) {
|
|
445
|
+
const bulkInputActive = Number(session._codexComposerBulkInputUntil || 0) > now;
|
|
446
|
+
const byteCap = bulkInputActive
|
|
447
|
+
? _deps.OUTPUT_FLUSH_BULK_INPUT_MAX_BYTES
|
|
448
|
+
: _deps.OUTPUT_FLUSH_MAX_BYTES;
|
|
449
|
+
const appendPlan = _deps.planCappedOutputAppend(outputBuf2, cleanData, byteCap, outputBufBytes2);
|
|
450
|
+
if (appendPlan.flushes.length) {
|
|
451
|
+
if (outputFlushTimer2) clearTimeout(outputFlushTimer2);
|
|
452
|
+
outputFlushTimer2 = null;
|
|
453
|
+
cappedFlushScheduler2.enqueue(appendPlan.flushes);
|
|
454
|
+
}
|
|
455
|
+
outputBuf2 = appendPlan.pending;
|
|
456
|
+
outputBufBytes2 = appendPlan.pendingBytes;
|
|
457
|
+
if (outputBuf2 && !cappedFlushScheduler2.pending && !outputFlushTimer2) {
|
|
458
|
+
outputFlushTimer2 = setTimeout(flushOutput2, _deps.terminalOutputFlushDelay(cleanData, session));
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
if (!_deps._sessionHostOwnsLiveHeadless(tabId)) enqueueHeadlessFeed2(mirrorData);
|
|
463
|
+
// Mark cache stale on output (matches the primary feed site).
|
|
464
|
+
if (cleanData && cleanData.length) session._cachedSnapshotDirty = true;
|
|
465
|
+
|
|
466
|
+
// Feed output to queue engine for completion detection
|
|
467
|
+
_deps.queueEngine.feedOutput(tabId, detectionData);
|
|
468
|
+
|
|
469
|
+
// Feed output to auto-approval engine
|
|
470
|
+
_deps.checkAutoApproval(tabId, session, detectionData);
|
|
471
|
+
|
|
472
|
+
// Feed output to idle/waiting-for-input detection
|
|
473
|
+
_deps.checkIdleNotify(tabId, session, detectionData);
|
|
474
|
+
});
|
|
475
|
+
ptyProcess.onData(attachedPtyDataHandler);
|
|
476
|
+
|
|
477
|
+
// WP-1 §6.6 item 2: the owner + cleanup FACTORY are captured BEFORE this handler is installed
|
|
478
|
+
// (see lib/session-create/native-lead-server-wiring.js); the handler passes its FULL event through it.
|
|
479
|
+
const exitLeadOwner = require('../session-create/native-lead-server-wiring').captureExitLeadOwner(session);
|
|
480
|
+
const leadRouteCleanup = require('../session-create/native-lead-server-wiring').createLeadRouteCleanupForSession({ session, sessionId: session.id, ownership: session._leadRouteTokenOwnership, hosted: () => _deps._useSessionHostProcessForPty() === true });
|
|
481
|
+
ptyProcess.onExit(async (exitEvent) => {
|
|
482
|
+
const { exitCode, signal } = exitEvent || {};
|
|
483
|
+
_deps.sessionInputAuthority.observeProvider({ sessionId: tabId, status: 'exited' });
|
|
484
|
+
if (session.agentRole === 'worker') attachedPtyDataHandler('\n');
|
|
485
|
+
if (outputFlushTimer2) { clearTimeout(outputFlushTimer2); flushOutput2(); }
|
|
486
|
+
if (_sb2Timer) { clearTimeout(_sb2Timer); _flushSb2(); }
|
|
487
|
+
_deps._forceFlowResume(session, tabId, 'pty-exit');
|
|
488
|
+
// WP-2 (D-1, S-16): don't let a grace-deferred geometry revoke outlive the PTY it was
|
|
489
|
+
// guarding ownership of.
|
|
490
|
+
_deps._executeGeometryRevokeGraceForSession(tabId);
|
|
491
|
+
// WP-2 (delegated-implementation-workers-2026-09-24.html §5 D-7, §9 S-13): revoke a
|
|
492
|
+
// worker session's proxy token on PTY exit -- best-effort, never blocks teardown.
|
|
493
|
+
if (session.agentRole === 'worker' && session._workerToken) {
|
|
494
|
+
_deps.oauthProxySupervisor.revokeWorkerToken(session._workerToken).catch(() => { /* expected: best-effort token revoke on PTY exit -- an unreachable/already-down proxy must never block session teardown */ });
|
|
495
|
+
} else if (session.agentRole !== 'worker') {
|
|
496
|
+
// §6.6 item 2/§6.11: the FULL event goes through the captured factory BEFORE the park/shutdown
|
|
497
|
+
// early returns below; a hosted synthetic/unknown event revokes the exact owner and keeps files.
|
|
498
|
+
await require('../session-create/native-lead-lifetime').runLeadRouteLifetimeCleanup(leadRouteCleanup, exitEvent, { reason: 'pty-exit' });
|
|
499
|
+
// WP-4 D-8: drop the exited lead from the pick table (inline require: may run before the module-level const).
|
|
500
|
+
require('../session-create/subagent-pick-table').forget(session.id, exitLeadOwner)
|
|
501
|
+
.catch(() => { /* expected: subagent-pick-table forget never rejects -- it reports its own failures */ });
|
|
502
|
+
}
|
|
503
|
+
// Lifecycle WP-4 (delegated-worker-lifecycle-identity-observability-2026-09-27.html
|
|
504
|
+
// §5 D-7, §9 S-20): release this worker's admission slot -- and, on a 429 ending,
|
|
505
|
+
// record the throttle timestamp D-7 keys admission decisions off -- on every worker
|
|
506
|
+
// exit, attached-path or create-path (response.js has the twin call), regardless of
|
|
507
|
+
// `_isShuttingDown` below (a shutdown-time exit must still free the slot for the next
|
|
508
|
+
// process to admit correctly).
|
|
509
|
+
_deps.delegateWorker.releaseAdmissionOnExit(session, exitCode);
|
|
510
|
+
// Lifecycle WP-5 (delegated-worker-lifecycle-identity-observability-2026-09-27.html
|
|
511
|
+
// §5 D-10, §9 S-16): stamp terminal_at/terminal_status/failure_json (once) and arm
|
|
512
|
+
// the D-10 retirement timer -- right after the existing token revoke + admission
|
|
513
|
+
// release above, on every worker exit (attached path; response.js's create-path
|
|
514
|
+
// exit handler has the twin call), regardless of `_isShuttingDown` below (a
|
|
515
|
+
// shutdown-time exit must still schedule the hide so a worker that exited just
|
|
516
|
+
// before a restart isn't retained forever by a lost in-memory timer -- the boot
|
|
517
|
+
// re-arm below picks it back up from the persisted hide_at either way).
|
|
518
|
+
// getWorkerStatus derives its status off session._exitCode, not this closure's own
|
|
519
|
+
// `exitCode` param, so that has to be stamped here too -- harmless: the SAME
|
|
520
|
+
// assignment happens again, unconditionally, a few lines further down (line ~39135)
|
|
521
|
+
// once this early-return window has passed.
|
|
522
|
+
session._exitCode = exitCode; session._exitSignal = signal ?? null;
|
|
523
|
+
_deps.delegateWorker.retireWorkerOnExit(session);
|
|
524
|
+
if (_deps._isShuttingDown) return;
|
|
525
|
+
|
|
526
|
+
// A delegated worker's exit is terminal, never an interactive --resume auto-restart: a worker
|
|
527
|
+
// continuation does launch `-p --resume` / `exec resume`, but relaunching it here would start an
|
|
528
|
+
// INTERACTIVE session under the worker's identity, stuck at worker-status `starting`
|
|
529
|
+
// (lib/session-class.js: relaunchFromRestore=false). Stamp session._exitCode unconditionally
|
|
530
|
+
// (every exit path benefits) so delegate-worker.js's getWorkerStatus can derive 'failed' from it.
|
|
531
|
+
const isWorkerSession = session.agentRole === 'worker';
|
|
532
|
+
session._exitCode = exitCode; session._exitSignal = signal ?? null;
|
|
533
|
+
|
|
534
|
+
// Crash recovery for attached sessions (always Claude --resume)
|
|
535
|
+
const isCrash = !session._intentionalKill && exitCode !== 0;
|
|
536
|
+
const maxAutoRestarts = 3;
|
|
537
|
+
const restartCount = session._autoRestartCount || 0;
|
|
538
|
+
const shouldAutoRestart = _deps.sessionPolicy(session).relaunchFromRestore && isCrash && restartCount < maxAutoRestarts;
|
|
539
|
+
|
|
540
|
+
const payload = JSON.stringify({ type: shouldAutoRestart ? 'restarting' : 'exit', id: tabId, exitCode, signal });
|
|
541
|
+
for (const client of session.clients) {
|
|
542
|
+
if (client.readyState === 1) _deps._wsSend(client, payload);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (shouldAutoRestart) {
|
|
546
|
+
console.log(` Attached session ${tabId.slice(0, 8)} crashed (exit ${exitCode}) — auto-restarting (attempt ${restartCount + 1}/${maxAutoRestarts})...`);
|
|
547
|
+
const delay = Math.min(2000 * Math.pow(2, restartCount), 15000);
|
|
548
|
+
session._restartTimer = setTimeout(async () => {
|
|
549
|
+
// C-4: this teardown is immediately followed by a relaunch of the SAME
|
|
550
|
+
// tabId (handleCreatePrepared below) -- unlike the final-exit branch further
|
|
551
|
+
// down, the session-runtime aggregate must survive so the crashed session's
|
|
552
|
+
// attached viewers stay attached across the auto-restart instead of being
|
|
553
|
+
// dropped until they reconnect from scratch.
|
|
554
|
+
await _deps._destroySession(tabId, 'process-teardown', { retireRuntime: false });
|
|
555
|
+
_deps.queueEngine.onSessionExit(tabId);
|
|
556
|
+
_deps.cleanAutoApprovalBuffer(tabId);
|
|
557
|
+
_deps.cleanIdleNotify(tabId);
|
|
558
|
+
try {
|
|
559
|
+
// WP-2 (docs/claude-lead-model-pin-followups-2026-10-17.html §5.1 D-6a, §9 WP-2): this
|
|
560
|
+
// relaunch of an EXISTING session is a RESTORE, not a create -- the same shape the in-session
|
|
561
|
+
// auto-restart uses (lib/session-create/response.js:1180-1181). `_isRestore: true` makes
|
|
562
|
+
// provider-process replay the stored Claude lead-model pin AND the stored session lead effort
|
|
563
|
+
// on THIS spawn's argv (lib/session-create/provider-process.js:813, :826) and skips the
|
|
564
|
+
// startup_tasks re-insert whose created_at would otherwise be reset (provider-process.js:1373).
|
|
565
|
+
// Neither value is persisted: the ladder rebinds launchArgs only, never `args`.
|
|
566
|
+
await _deps.handleCreatePrepared(null, {
|
|
567
|
+
id: tabId,
|
|
568
|
+
title: session.title || session.label,
|
|
569
|
+
label: session.title || session.label,
|
|
570
|
+
cmd: 'claude',
|
|
571
|
+
args: ['--resume', session_id],
|
|
572
|
+
cwd: cwdDir,
|
|
573
|
+
_isRestore: true,
|
|
574
|
+
_ctmOriginalCreatedAt: session._ctmOriginalCreatedAt || session.createdAt,
|
|
575
|
+
_autoRestartCount: restartCount + 1,
|
|
576
|
+
});
|
|
577
|
+
console.log(` ✓ Restarted attached session ${tabId.slice(0, 8)}`);
|
|
578
|
+
} catch (e) {
|
|
579
|
+
console.log(` ✗ Restart failed: ${e.message}`);
|
|
580
|
+
_deps.broadcastSessionList(true, { reason: 'attach-restart-failed' });
|
|
581
|
+
}
|
|
582
|
+
}, delay);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
_deps._markServerSessionTurnClosed(session, Date.now(), 'attached-session-exit');
|
|
587
|
+
// A worker session keeps its in-memory row (deleteSession:false) so a caller
|
|
588
|
+
// polling worker-status right after this exit still finds session._exitCode --
|
|
589
|
+
// deleting it here would collapse getWorkerStatus's `!session` branch back to
|
|
590
|
+
// 'exited'/row-existence, which can't distinguish a genuine failure from a
|
|
591
|
+
// clean teardown. Cleanup of a stale worker session's memory footprint is the
|
|
592
|
+
// same tradeoff the crash-restart branch above already makes with
|
|
593
|
+
// retireRuntime:false; a worker session's lifetime is bounded (one trial run).
|
|
594
|
+
await _deps._destroySession(tabId, 'process-teardown', isWorkerSession ? { deleteSession: false } : undefined);
|
|
595
|
+
_deps.agentSessionKernel.forgetSession(tabId);
|
|
596
|
+
_deps.sessionDiagnostics.clear(tabId); // WP-3/S2: session fully retired -- ring must not outlive it
|
|
597
|
+
_deps._retireStartupTaskOnDbOwner(tabId, 'attached-session-exit').catch(() => {});
|
|
598
|
+
_deps._clearScrollbackOnDbOwner(tabId).catch(() => {});
|
|
599
|
+
_deps.queueEngine.onSessionExit(tabId);
|
|
600
|
+
_deps.cleanAutoApprovalBuffer(tabId);
|
|
601
|
+
_deps.cleanIdleNotify(tabId);
|
|
602
|
+
_deps.broadcastSessionList(true, { reason: 'attach-exit' });
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Wire up queue engine hooks if a queue exists for this session
|
|
606
|
+
_deps.setupQueueForSession(tabId);
|
|
607
|
+
|
|
608
|
+
_deps.broadcastSessionList(true, { reason: 'attach-created' });
|
|
609
|
+
|
|
610
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
611
|
+
res.end(JSON.stringify({ ok: true, tab_id: tabId, session_id }));
|
|
612
|
+
} catch (e) {
|
|
613
|
+
const status = e.statusCode || 500;
|
|
614
|
+
res.writeHead(status, { 'Content-Type': 'application/json', ...(status === 503 ? { 'Retry-After': '2' } : {}) });
|
|
615
|
+
res.end(JSON.stringify({ error: e.message, ...(status === 503 ? { retry: true } : {}) }));
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
module.exports = {
|
|
621
|
+
configure,
|
|
622
|
+
apiAttachSession,
|
|
623
|
+
};
|