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
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const { reduceModelObservation, validateModelState } = require('../coding-session-model-state');
|
|
4
|
+
// WP-8 (deepseek-harness-acp-agent §9b M-9): the owner agent-type list is the ONE declaration in
|
|
5
|
+
// lib/coding-session-model-owner.js — this module keeps no private copy of it.
|
|
6
|
+
const { OWNER_AGENT_TYPES } = require('../coding-session-model-owner');
|
|
4
7
|
const { compatibilityModelFact } = require('../coding-session-model-projection');
|
|
5
8
|
const { readLaunchGenerationRow, applyGenerationAdoption } = require('./coding-session-generation-store');
|
|
6
9
|
|
|
@@ -27,7 +30,7 @@ function createCodingSessionModelStore({ getCoreDb, tableExists, projectCompatib
|
|
|
27
30
|
return decode(select(db, String(ctmSessionId || '').trim()));
|
|
28
31
|
};
|
|
29
32
|
const ownerValid = (owner) => owner && typeof owner.ctmSessionId === 'string' && owner.ctmSessionId.trim() === owner.ctmSessionId && Buffer.byteLength(owner.ctmSessionId, 'utf8') > 0 && Buffer.byteLength(owner.ctmSessionId, 'utf8') <= 256 &&
|
|
30
|
-
|
|
33
|
+
OWNER_AGENT_TYPES.includes(owner.agentType) &&
|
|
31
34
|
typeof owner.providerSessionId === 'string' && owner.providerSessionId.trim() === owner.providerSessionId && Buffer.byteLength(owner.providerSessionId, 'utf8') > 0 && Buffer.byteLength(owner.providerSessionId, 'utf8') <= 256 &&
|
|
32
35
|
Number.isSafeInteger(owner.processGeneration) && owner.processGeneration >= 0;
|
|
33
36
|
const ownerRow = (db, owner) => db.prepare(`
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
const { isValidEffort } = require('../launch-model-override');
|
|
18
18
|
|
|
19
19
|
const MAX_FIELD_LENGTH = 200;
|
|
20
|
-
const AGENTS = new Set(['claude', 'codex']);
|
|
20
|
+
const AGENTS = new Set(['claude', 'codex', 'opencode']);
|
|
21
21
|
const LANES = new Set(['native', 'routed', 'inherit']);
|
|
22
22
|
const LAUNCH_KINDS = new Set(['none', 'native', 'inherit', 'routed', 'unservable']);
|
|
23
23
|
const MIRROR_TABLE = 'delegate_pref_mirror';
|
|
@@ -44,6 +44,19 @@ function _normalizeEffort(effort) {
|
|
|
44
44
|
throw new TypeError("effort must be null, '' or a bounded effort identifier");
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
// v58 (WP-1, codex-native-subagent-pick-enforcement-2026-10-28.html §5 D-4, §6): the launch row also
|
|
48
|
+
// records what the spawn DID about Codex native sub-agents. Exactly two known states plus unknown:
|
|
49
|
+
// null = unknown (no fact recorded, i.e. every pre-v58 row)
|
|
50
|
+
// '' = this launch did NOT deny native sub-agents
|
|
51
|
+
// 'disabled' = this launch carried CODEX_NATIVE_SUBAGENT_DENY_ARGS
|
|
52
|
+
// A non-string or any other token is refused here rather than stored, so a read can never report a
|
|
53
|
+
// malformed value as the deny fact.
|
|
54
|
+
function _normalizeNativeSubagents(value) {
|
|
55
|
+
if (value === null || value === undefined) return null;
|
|
56
|
+
if (value === '' || value === 'disabled') return value;
|
|
57
|
+
throw new TypeError("nativeSubagents must be null, '' or 'disabled'");
|
|
58
|
+
}
|
|
59
|
+
|
|
47
60
|
function createDelegatePrefMirrorStore({ getCoreDb, tableExists, flushWal = () => {}, now = () => new Date().toISOString() } = {}) {
|
|
48
61
|
if (typeof getCoreDb !== 'function' || typeof tableExists !== 'function') throw new TypeError('getCoreDb and tableExists are required');
|
|
49
62
|
|
|
@@ -161,6 +174,9 @@ function createDelegatePrefMirrorStore({ getCoreDb, tableExists, flushWal = () =
|
|
|
161
174
|
// Absent (pre-v50) is NULL = unknown; a stored '' is the separate "known NO role effort
|
|
162
175
|
// override" state. Read verbatim -- the projection decides what a kind/agent may claim.
|
|
163
176
|
effort: row.effort == null ? null : row.effort,
|
|
177
|
+
// v58 (WP-1): what this launch DID about Codex native sub-agents. Absent (pre-v58) is NULL =
|
|
178
|
+
// unknown; read verbatim, so a malformed stored value never becomes a claim.
|
|
179
|
+
native_subagents: row.native_subagents == null ? null : row.native_subagents,
|
|
164
180
|
};
|
|
165
181
|
}
|
|
166
182
|
|
|
@@ -168,18 +184,24 @@ function createDelegatePrefMirrorStore({ getCoreDb, tableExists, flushWal = () =
|
|
|
168
184
|
const sessionId = _str(ctmSessionId);
|
|
169
185
|
if (!sessionId || !tableExists(LAUNCH_TABLE)) return null;
|
|
170
186
|
return toLaunchRow(getCoreDb().prepare(
|
|
171
|
-
'SELECT ctm_session_id, agent, kind, model, source, origin, reason, route_id, launched_at, process_generation, harness, provider_thread_id, effort FROM session_subagent_pick_launch WHERE ctm_session_id = ?'
|
|
187
|
+
'SELECT ctm_session_id, agent, kind, model, source, origin, reason, route_id, launched_at, process_generation, harness, provider_thread_id, effort, native_subagents FROM session_subagent_pick_launch WHERE ctm_session_id = ?'
|
|
172
188
|
).get(sessionId));
|
|
173
189
|
}
|
|
174
190
|
|
|
175
191
|
function setLaunch({ ctmSessionId, agent, kind, model = '', source = '', origin = '', reason = '', routeId = '',
|
|
176
|
-
processGeneration, harness, providerThreadId, launchedAt, effort = null } = {}) {
|
|
192
|
+
processGeneration, harness, providerThreadId, launchedAt, effort = null, nativeSubagents = null } = {}) {
|
|
177
193
|
const sessionId = _str(ctmSessionId).trim();
|
|
178
194
|
if (!sessionId) throw new TypeError('ctmSessionId is required');
|
|
179
|
-
if (!AGENTS.has(agent)) throw new TypeError("agent must be 'claude' or '
|
|
195
|
+
if (!AGENTS.has(agent)) throw new TypeError("agent must be 'claude', 'codex' or 'opencode'");
|
|
180
196
|
if (!LAUNCH_KINDS.has(kind)) throw new TypeError('kind must be none, native, inherit, routed or unservable');
|
|
181
197
|
const effortValue = _normalizeEffort(effort);
|
|
182
|
-
const
|
|
198
|
+
const nativeSubagentsValue = _normalizeNativeSubagents(nativeSubagents);
|
|
199
|
+
// Only Codex has its own harness label. An 'opencode' lead owns its launch row under the same
|
|
200
|
+
// non-Codex label Claude uses: CTM's owner tuple (generation/harness/thread, see
|
|
201
|
+
// subagent-pick-table._launchOwnerContext and deleteOwnedLaunch) has exactly two values, and an
|
|
202
|
+
// OpenCode lead has no lead-route token to conflict with either. Per-launch model projection for
|
|
203
|
+
// OpenCode is env-only (OPENCODE_CONFIG_CONTENT), so there is no third harness to add here.
|
|
204
|
+
const expectedHarness = agent === 'codex' ? 'codex-cli' : 'claude-code';
|
|
183
205
|
if (!Number.isSafeInteger(processGeneration) || processGeneration < 1) {
|
|
184
206
|
throw new TypeError('processGeneration must be a positive integer');
|
|
185
207
|
}
|
|
@@ -213,15 +235,16 @@ function createDelegatePrefMirrorStore({ getCoreDb, tableExists, flushWal = () =
|
|
|
213
235
|
db.prepare(
|
|
214
236
|
`INSERT INTO session_subagent_pick_launch
|
|
215
237
|
(ctm_session_id, agent, kind, model, source, origin, reason, route_id, launched_at,
|
|
216
|
-
process_generation, harness, provider_thread_id, effort)
|
|
217
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
238
|
+
process_generation, harness, provider_thread_id, effort, native_subagents)
|
|
239
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
218
240
|
ON CONFLICT(ctm_session_id) DO UPDATE SET agent = excluded.agent, kind = excluded.kind,
|
|
219
241
|
model = excluded.model, source = excluded.source, origin = excluded.origin,
|
|
220
242
|
reason = excluded.reason, route_id = excluded.route_id, launched_at = excluded.launched_at,
|
|
221
243
|
process_generation = excluded.process_generation, harness = excluded.harness,
|
|
222
|
-
provider_thread_id = excluded.provider_thread_id, effort = excluded.effort
|
|
244
|
+
provider_thread_id = excluded.provider_thread_id, effort = excluded.effort,
|
|
245
|
+
native_subagents = excluded.native_subagents`
|
|
223
246
|
).run(sessionId, agent, kind, fields.model, fields.source, fields.origin, fields.reason,
|
|
224
|
-
fields.routeId, launched, processGeneration, harness, threadId, effortValue);
|
|
247
|
+
fields.routeId, launched, processGeneration, harness, threadId, effortValue, nativeSubagentsValue);
|
|
225
248
|
return getLaunch(sessionId);
|
|
226
249
|
});
|
|
227
250
|
const result = run();
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Boot sweep for CTM's non-PTY ("structured") sessions. Extracted verbatim from server.js (design
|
|
4
|
+
// doc docs/deepseek-harness-acp-agent-2026-10-28.html §7 offset (3), §9 S-42) so server.js keeps its
|
|
5
|
+
// zero-headroom line count while the same sweep can later also close out an interrupted DeepSeek
|
|
6
|
+
// Harness turn (WP-5 adds that strategy next to the Wall-E one below).
|
|
7
|
+
//
|
|
8
|
+
// WP-V2 A4 — boot sweep. Incident class B: a Wall-E turn that died mid-retry across a restart writes
|
|
9
|
+
// NO terminal record, so the durable transcript permanently looks "running"
|
|
10
|
+
// (walle-interrupted-visibility-resume-design-2026-07-17.html §2.2 V6). Closes those out at the next
|
|
11
|
+
// boot: bounded to walle sessions active in the last 7 days, off the main thread for both the recency
|
|
12
|
+
// check and the jsonl read (reuses the SAME read-pool ops the restore-interruption detector already
|
|
13
|
+
// uses), never a synchronous DB query, and scheduled by server.js via the existing
|
|
14
|
+
// _scheduleAfterStartupMaintenanceQuiet deferral so it never competes with the restore storm on the
|
|
15
|
+
// boot critical path.
|
|
16
|
+
//
|
|
17
|
+
// Every server global the two functions use arrives as a dep; the two limit values (one of them
|
|
18
|
+
// env-derived) are read in server.js because the flag lint forbids new raw process.env reads here.
|
|
19
|
+
//
|
|
20
|
+
// WP-5 (§5 D-9, §9 S-42) adds the DeepSeek Harness strategy beside the Wall-E one. A dsh turn that was
|
|
21
|
+
// in flight when CTM went down wrote its user row and nothing after it, so the durable transcript
|
|
22
|
+
// permanently looks "running". At the next boot the dsh strategy re-reads that transcript off-thread,
|
|
23
|
+
// and — when the last row is a `user` row — appends exactly ONE `dsh_turn_interrupted` row through the
|
|
24
|
+
// SAME writer the live path uses (append-only, async, ordered). Since WP-7d that row is
|
|
25
|
+
// `assistant`-shaped (an API-error row) so Conv renders it. It is naturally
|
|
26
|
+
// idempotent: the row it writes is itself terminal, so a second sweep finds nothing open.
|
|
27
|
+
|
|
28
|
+
const { createDshTranscriptWriter, dshTranscriptDir, findOpenDshTurn } = require('./acp/dsh-transcript');
|
|
29
|
+
const path = require('node:path');
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {object} deps
|
|
33
|
+
* @param {Map} deps.sessions
|
|
34
|
+
* @param {Function} deps.offthreadRead // server.js _offthreadRead
|
|
35
|
+
* @param {object} deps.restoreInterruptionBreaker
|
|
36
|
+
* @param {Function} deps.findLastWalleTurnTerminal
|
|
37
|
+
* @param {Function} deps.detectOpenWalleTurn
|
|
38
|
+
* @param {Function} deps.appendWalleTurnTerminal // server.js _appendWalleTurnTerminal
|
|
39
|
+
* @param {object} deps.runtimeWorkRegistry
|
|
40
|
+
* @param {Function} deps.statFilesOffThread // server.js _statFilesOffThread
|
|
41
|
+
* @param {number} deps.maxSessions
|
|
42
|
+
* @param {number} deps.maxAgeMs
|
|
43
|
+
* @param {Function} [deps.getDataDir] // WP-5: lib/data-dir.js's getDataDir()
|
|
44
|
+
* @param {Function} [deps.log]
|
|
45
|
+
* @param {Function} [deps.warn]
|
|
46
|
+
* @param {Function} [deps.error]
|
|
47
|
+
*/
|
|
48
|
+
function createStructuredSessionBootSweep(deps = {}) {
|
|
49
|
+
const {
|
|
50
|
+
sessions,
|
|
51
|
+
offthreadRead,
|
|
52
|
+
restoreInterruptionBreaker,
|
|
53
|
+
findLastWalleTurnTerminal,
|
|
54
|
+
detectOpenWalleTurn,
|
|
55
|
+
appendWalleTurnTerminal,
|
|
56
|
+
runtimeWorkRegistry,
|
|
57
|
+
statFilesOffThread,
|
|
58
|
+
maxSessions,
|
|
59
|
+
maxAgeMs,
|
|
60
|
+
getDataDir,
|
|
61
|
+
log = (...args) => console.log(...args),
|
|
62
|
+
warn = (...args) => console.warn(...args),
|
|
63
|
+
error = (...args) => console.error(...args),
|
|
64
|
+
} = deps;
|
|
65
|
+
|
|
66
|
+
async function sweepWalleSessionOpenTurn(session) {
|
|
67
|
+
if (!session || !session.jsonlPath || session.abortController) return null;
|
|
68
|
+
const tail = await offthreadRead({
|
|
69
|
+
breaker: restoreInterruptionBreaker,
|
|
70
|
+
op: 'restoreInterruptionTail',
|
|
71
|
+
payload: { path: session.jsonlPath },
|
|
72
|
+
timeoutMs: 15000,
|
|
73
|
+
onSuccess: (res) => res || null,
|
|
74
|
+
onFail: () => null,
|
|
75
|
+
});
|
|
76
|
+
const entries = tail && Array.isArray(tail.entries) ? tail.entries : null;
|
|
77
|
+
if (!entries || !entries.length) return null;
|
|
78
|
+
const lastTerminal = findLastWalleTurnTerminal(entries);
|
|
79
|
+
if (lastTerminal) {
|
|
80
|
+
session._walleLastTurnTerminal = lastTerminal;
|
|
81
|
+
session._walleTurnTerminalWrittenFor = lastTerminal.turnId || '';
|
|
82
|
+
}
|
|
83
|
+
const openTurn = detectOpenWalleTurn(entries);
|
|
84
|
+
if (!openTurn.open) return null;
|
|
85
|
+
// Re-check liveness right before writing — this session's off-thread tail read may
|
|
86
|
+
// have raced a fresh turn starting (auto-resume, a queued send) while it was in
|
|
87
|
+
// flight. Never slam the door on a turn that's actually running.
|
|
88
|
+
if (session.abortController) return null;
|
|
89
|
+
const appended = appendWalleTurnTerminal(session, openTurn.turnId, {
|
|
90
|
+
status: 'interrupted_infra',
|
|
91
|
+
reason: 'boot_sweep_open_turn',
|
|
92
|
+
source: 'boot_sweep',
|
|
93
|
+
detectedBy: 'boot_sweep',
|
|
94
|
+
message: 'Wall-E restarted before this turn finished — nothing is running now.',
|
|
95
|
+
lastActivityAt: openTurn.lastActivityAt || '',
|
|
96
|
+
});
|
|
97
|
+
if (!appended) return null;
|
|
98
|
+
const retired = runtimeWorkRegistry.retireTurnWithoutHydration({
|
|
99
|
+
sessionId: session.id,
|
|
100
|
+
turnId: openTurn.turnId,
|
|
101
|
+
status: 'stopped',
|
|
102
|
+
summary: 'Interrupted by CTM restart',
|
|
103
|
+
});
|
|
104
|
+
if (retired?.persisted && typeof retired.persisted.then === 'function') {
|
|
105
|
+
await retired.persisted;
|
|
106
|
+
}
|
|
107
|
+
return appended;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async function runWalleBootTurnSweep() {
|
|
111
|
+
try {
|
|
112
|
+
const candidates = [];
|
|
113
|
+
for (const session of sessions.values()) {
|
|
114
|
+
if (session && session.type === 'walle' && session.jsonlPath) candidates.push(session);
|
|
115
|
+
if (candidates.length >= maxSessions) break;
|
|
116
|
+
}
|
|
117
|
+
if (!candidates.length) return;
|
|
118
|
+
const stats = await statFilesOffThread(candidates.map((s) => s.jsonlPath), 20000);
|
|
119
|
+
if (!stats) {
|
|
120
|
+
warn('[walle-boot-sweep] off-thread stat unavailable — skipping this boot (next boot retries).');
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
124
|
+
const recent = candidates.filter((session) => {
|
|
125
|
+
const st = stats.get(session.jsonlPath);
|
|
126
|
+
return st && st.exists && Number(st.mtimeMs || 0) >= cutoff;
|
|
127
|
+
});
|
|
128
|
+
let closed = 0;
|
|
129
|
+
for (const session of recent) {
|
|
130
|
+
try {
|
|
131
|
+
const result = await sweepWalleSessionOpenTurn(session);
|
|
132
|
+
if (result) closed += 1;
|
|
133
|
+
} catch (e) {
|
|
134
|
+
error('[walle-boot-sweep] session sweep failed:', session.id, e && e.message);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (recent.length) {
|
|
138
|
+
log(` [walle-boot-sweep] checked ${recent.length} recent Wall-E session(s); closed ${closed} silently-open turn(s).`);
|
|
139
|
+
}
|
|
140
|
+
} catch (e) {
|
|
141
|
+
error('[walle-boot-sweep] failed:', e && e.message);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// The transcript a dsh session's rows live in: the placeholder's own `jsonlPath` when the restore
|
|
146
|
+
// loop set one, else the D-8 path derived from the stored ACP session id.
|
|
147
|
+
function transcriptPathForDsh(session) {
|
|
148
|
+
if (!session) return '';
|
|
149
|
+
if (session.jsonlPath) return session.jsonlPath;
|
|
150
|
+
const acpSessionId = String(session.agentSessionId || (session._restoreTask && session._restoreTask.chat_session_id) || '');
|
|
151
|
+
if (!acpSessionId || typeof getDataDir !== 'function') return '';
|
|
152
|
+
return path.join(dshTranscriptDir(getDataDir()), `${acpSessionId}.jsonl`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// One dsh session: read the transcript tail, and close a silently-open turn with ONE interrupted row.
|
|
156
|
+
async function sweepDshSessionOpenTurn(session) {
|
|
157
|
+
const jsonlPath = transcriptPathForDsh(session);
|
|
158
|
+
if (!jsonlPath) return null;
|
|
159
|
+
// A session whose child is LIVE right now owns its own writer and an actually-running turn; the
|
|
160
|
+
// sweep only ever closes a turn that died with the CTM process.
|
|
161
|
+
if (session._dshTranscript) return null;
|
|
162
|
+
const tail = await offthreadRead({
|
|
163
|
+
breaker: restoreInterruptionBreaker,
|
|
164
|
+
op: 'restoreInterruptionTail',
|
|
165
|
+
payload: { path: jsonlPath },
|
|
166
|
+
timeoutMs: 15000,
|
|
167
|
+
onSuccess: (res) => res || null,
|
|
168
|
+
onFail: () => null,
|
|
169
|
+
});
|
|
170
|
+
const entries = tail && Array.isArray(tail.entries) ? tail.entries : null;
|
|
171
|
+
if (!entries || !entries.length) return null;
|
|
172
|
+
if (!findOpenDshTurn(entries)) return null;
|
|
173
|
+
// Re-check liveness right before writing: this session's off-thread tail read may have raced a
|
|
174
|
+
// fresh wake (attach, queued Send) while it was in flight. Never slam a running turn shut.
|
|
175
|
+
if (session._dshTranscript) return null;
|
|
176
|
+
const acpSessionId = String(session.agentSessionId || (session._restoreTask && session._restoreTask.chat_session_id) || '');
|
|
177
|
+
if (!acpSessionId) return null;
|
|
178
|
+
// Reopen the SAME file append-only. This writer is throwaway: it exists only to land the one row,
|
|
179
|
+
// and it re-seeds its parentUuid from the file's own tail so the chain stays unbroken.
|
|
180
|
+
const writer = createDshTranscriptWriter({
|
|
181
|
+
dataDir: typeof getDataDir === 'function' ? getDataDir() : '',
|
|
182
|
+
acpSessionId,
|
|
183
|
+
cwd: session.cwd || '',
|
|
184
|
+
});
|
|
185
|
+
if (typeof writer.appendTurnInterrupted !== 'function') return null;
|
|
186
|
+
const row = writer.appendTurnInterrupted();
|
|
187
|
+
if (typeof writer.whenIdle === 'function') await writer.whenIdle();
|
|
188
|
+
return row || null;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async function runDshBootTurnSweep() {
|
|
192
|
+
try {
|
|
193
|
+
const candidates = [];
|
|
194
|
+
for (const session of sessions.values()) {
|
|
195
|
+
if (session && session.type === 'dsh' && transcriptPathForDsh(session)) candidates.push(session);
|
|
196
|
+
if (candidates.length >= maxSessions) break;
|
|
197
|
+
}
|
|
198
|
+
if (!candidates.length) return;
|
|
199
|
+
const stats = await statFilesOffThread(candidates.map((s) => transcriptPathForDsh(s)), 20000);
|
|
200
|
+
if (!stats) {
|
|
201
|
+
warn('[dsh-boot-sweep] off-thread stat unavailable — skipping this boot (next boot retries).');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const cutoff = Date.now() - maxAgeMs;
|
|
205
|
+
const recent = candidates.filter((session) => {
|
|
206
|
+
const st = stats.get(transcriptPathForDsh(session));
|
|
207
|
+
return st && st.exists && Number(st.mtimeMs || 0) >= cutoff;
|
|
208
|
+
});
|
|
209
|
+
let closed = 0;
|
|
210
|
+
for (const session of recent) {
|
|
211
|
+
try {
|
|
212
|
+
const result = await sweepDshSessionOpenTurn(session);
|
|
213
|
+
if (result) closed += 1;
|
|
214
|
+
} catch (e) {
|
|
215
|
+
error('[dsh-boot-sweep] session sweep failed:', session.id, e && e.message);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (recent.length) {
|
|
219
|
+
log(` [dsh-boot-sweep] checked ${recent.length} recent DeepSeek Harness session(s); closed ${closed} silently-open turn(s).`);
|
|
220
|
+
}
|
|
221
|
+
} catch (e) {
|
|
222
|
+
error('[dsh-boot-sweep] failed:', e && e.message);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// The one call server.js makes at boot: both non-PTY strategies, Wall-E first (unchanged order).
|
|
227
|
+
async function runStructuredBootTurnSweep() {
|
|
228
|
+
await runWalleBootTurnSweep();
|
|
229
|
+
await runDshBootTurnSweep();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return { runStructuredBootTurnSweep, runWalleBootTurnSweep, sweepWalleSessionOpenTurn, runDshBootTurnSweep, sweepDshSessionOpenTurn };
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
module.exports = { createStructuredSessionBootSweep };
|
|
@@ -101,6 +101,85 @@ function queuedWalleRuntimeInputId(item, fallbackInputId = '') {
|
|
|
101
101
|
return String(fallbackInputId || '');
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
+
// IR2 (design doc walle-inflight-readiness-and-replay-2026-10-27.html §5): a caller
|
|
105
|
+
// already joined to another in-process owner must not re-enter admission just because
|
|
106
|
+
// the stored decision still says proceed. Process readiness decides whether re-entry
|
|
107
|
+
// is permitted; the recorded phase decides the reply. Neither outcome certifies a SQL
|
|
108
|
+
// commit, a provider no-post boundary or cancellation authority.
|
|
109
|
+
function classifyInFlightSubmissionDecision(storedDecision, readiness) {
|
|
110
|
+
// Sent/conflict/read-error decisions are strictly further along than any local
|
|
111
|
+
// readiness observation and are returned untouched.
|
|
112
|
+
if (!storedDecision || storedDecision.action !== 'proceed') return storedDecision;
|
|
113
|
+
// Only a claim given up before its readiness checkpoint preserves the ordinary retry policy.
|
|
114
|
+
if (readiness === 'released_unready') return storedDecision;
|
|
115
|
+
// Unknown readiness supplies neither acceptance nor release authority.
|
|
116
|
+
if (readiness !== 'owner_ready') return { action: 'error', reason: 'dedup_unavailable' };
|
|
117
|
+
// Exact machine codes from the classifier's own inputs; never a display message,
|
|
118
|
+
// a suffix parse or a blanket proceed->accepted. Any other phase (pending,
|
|
119
|
+
// received, orphaned, missing, future) is a reconciliation-unavailable error.
|
|
120
|
+
switch (storedDecision.reason) {
|
|
121
|
+
case 'accepted_retry':
|
|
122
|
+
return { action: 'replay', replayStatus: 'accepted', reason: 'duplicate_replay' };
|
|
123
|
+
case 'rejected_retry':
|
|
124
|
+
case 'cancelled_retry':
|
|
125
|
+
return { action: 'replay', replayStatus: 'failed', reason: 'duplicate_replay' };
|
|
126
|
+
case 'deferred_retry':
|
|
127
|
+
return { action: 'replay', replayStatus: 'blocked', reason: 'duplicate_replay', retryable: true };
|
|
128
|
+
default:
|
|
129
|
+
return { action: 'error', reason: 'dedup_unavailable' };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// IR5: the pure stored-response presentation lives here (the frame module), not in
|
|
134
|
+
// server.js -- server.js is at its line ceiling and must not be compressed. None of
|
|
135
|
+
// these messages claim rollback, undo, provider liveness or a safe effect repetition.
|
|
136
|
+
const STORED_SUBMISSION_REPLAY_MESSAGES = Object.freeze({
|
|
137
|
+
sent: 'Sent (already delivered)',
|
|
138
|
+
accepted: 'Already accepted - still confirming...',
|
|
139
|
+
failed: 'The original submission has a recorded unsuccessful outcome. This replay did not start another attempt.',
|
|
140
|
+
blocked: 'The original submission is recorded as deferred. This replay did not start another attempt.',
|
|
141
|
+
});
|
|
142
|
+
const STORED_SUBMISSION_UNAVAILABLE_MESSAGE =
|
|
143
|
+
'CTM cannot yet reconcile the original request. This replay has not started an additional attempt.';
|
|
144
|
+
const STORED_SUBMISSION_CONFLICT_MESSAGE =
|
|
145
|
+
'This submission id was already used for different prompt text.';
|
|
146
|
+
|
|
147
|
+
function walleStoredSubmissionResponse(decision, source) {
|
|
148
|
+
const action = decision && typeof decision === 'object' ? decision.action : '';
|
|
149
|
+
if (
|
|
150
|
+
action === 'replay'
|
|
151
|
+
&& Object.prototype.hasOwnProperty.call(STORED_SUBMISSION_REPLAY_MESSAGES, String(decision.replayStatus || ''))
|
|
152
|
+
) {
|
|
153
|
+
const status = String(decision.replayStatus || '');
|
|
154
|
+
return {
|
|
155
|
+
status,
|
|
156
|
+
details: {
|
|
157
|
+
reason: decision.reason,
|
|
158
|
+
message: STORED_SUBMISSION_REPLAY_MESSAGES[status],
|
|
159
|
+
signal: status === 'sent' ? 'durable_row' : '',
|
|
160
|
+
source,
|
|
161
|
+
retryable: status === 'blocked',
|
|
162
|
+
},
|
|
163
|
+
result: { ok: true, deduplicated: true },
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Conflict keeps its own message; everything else (read error, unreadable phase,
|
|
167
|
+
// an unknown replay status) is reconciliation-unavailable and stays retryable.
|
|
168
|
+
const conflict = action === 'conflict';
|
|
169
|
+
const reason = conflict ? decision.reason : 'dedup_unavailable';
|
|
170
|
+
return {
|
|
171
|
+
status: 'blocked',
|
|
172
|
+
details: {
|
|
173
|
+
reason,
|
|
174
|
+
message: conflict ? STORED_SUBMISSION_CONFLICT_MESSAGE : STORED_SUBMISSION_UNAVAILABLE_MESSAGE,
|
|
175
|
+
signal: '',
|
|
176
|
+
source,
|
|
177
|
+
retryable: !conflict,
|
|
178
|
+
},
|
|
179
|
+
result: { ok: false, code: reason },
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
104
183
|
function createInFlightSubmissionRegistry() {
|
|
105
184
|
const records = new Map();
|
|
106
185
|
|
|
@@ -123,7 +202,7 @@ function createInFlightSubmissionRegistry() {
|
|
|
123
202
|
const record = records.get(String(key || ''));
|
|
124
203
|
if (!record || record.token !== token || record.resolved) return false;
|
|
125
204
|
record.resolved = true;
|
|
126
|
-
record.resolveReady();
|
|
205
|
+
record.resolveReady('owner_ready');
|
|
127
206
|
return true;
|
|
128
207
|
}
|
|
129
208
|
|
|
@@ -131,7 +210,14 @@ function createInFlightSubmissionRegistry() {
|
|
|
131
210
|
const cleanKey = String(key || '');
|
|
132
211
|
const record = records.get(cleanKey);
|
|
133
212
|
if (!record || record.token !== token) return false;
|
|
134
|
-
|
|
213
|
+
// A claim released before its readiness checkpoint is the ONLY readiness that
|
|
214
|
+
// permits the caller's ordinary retry policy. A claim already settled as
|
|
215
|
+
// owner_ready keeps that first settlement (Promise single-assignment); this
|
|
216
|
+
// deletes its own record and nothing else.
|
|
217
|
+
if (!record.resolved) {
|
|
218
|
+
record.resolved = true;
|
|
219
|
+
record.resolveReady('released_unready');
|
|
220
|
+
}
|
|
135
221
|
records.delete(cleanKey);
|
|
136
222
|
return true;
|
|
137
223
|
}
|
|
@@ -172,11 +258,13 @@ module.exports = {
|
|
|
172
258
|
CTM_SUBMIT_ACK_TRUTH,
|
|
173
259
|
REMOTE_SUBMISSION_OBSERVE_DELAYS_MS,
|
|
174
260
|
classifyDuplicateSubmission,
|
|
261
|
+
classifyInFlightSubmissionDecision,
|
|
175
262
|
createInFlightSubmissionRegistry,
|
|
176
263
|
markerEvidence,
|
|
177
264
|
queuedWalleRuntimeInputId,
|
|
178
265
|
redactSubmissionPreview,
|
|
179
266
|
replayStatusForEnvelopeResult,
|
|
180
267
|
walleSubmissionStatusFrame,
|
|
268
|
+
walleStoredSubmissionResponse,
|
|
181
269
|
walleStoredTurnIdForPrompt,
|
|
182
270
|
};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Session hosts survive a systemd CTM restart (docs/session-host-cgroup-survival-and-durable-jobs-2026-10-31.html
|
|
4
|
+
// §5 D-1..D-6, §6.1). Under ctm.service (KillMode=control-group) a `detached: true` child is still in the
|
|
5
|
+
// unit's cgroup and is reaped when the unit stops. tmux/podman fix this by moving the child into its own
|
|
6
|
+
// transient .scope right after fork (StartTransientUnit with PIDs). We issue the same call through busctl
|
|
7
|
+
// (no Node D-Bus binding is installed) with an argv array -- no shell.
|
|
8
|
+
|
|
9
|
+
const fs = require('node:fs');
|
|
10
|
+
const { execFile } = require('node:child_process');
|
|
11
|
+
const { registerFlag, boolFlag, createEnvFlags } = require('./env-flags');
|
|
12
|
+
|
|
13
|
+
registerFlag('CTM_HOST_OWN_SCOPE', {
|
|
14
|
+
kind: 'bool',
|
|
15
|
+
default: true,
|
|
16
|
+
desc: 'On a systemd service host, move each session-host/pool/proxy process into its own transient scope so a CTM restart does not reap it.',
|
|
17
|
+
expires: null,
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// The process-wide singleton reads process.env; a caller-injected env (tests) gets a throwaway reader.
|
|
21
|
+
function _ownScopeFlag(env) {
|
|
22
|
+
if (env === process.env || !env) return boolFlag('CTM_HOST_OWN_SCOPE', true);
|
|
23
|
+
return createEnvFlags({ env, warn: () => {} }).boolFlag('CTM_HOST_OWN_SCOPE', true);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const SCOPE_MOVE_TIMEOUT_MS = 5000; // busctl is a local bus call; the probe measured 6 ms
|
|
27
|
+
const SCOPE_PREFIX = 'ctm-host-'; // unit = `${SCOPE_PREFIX}${sanitize(label)}-${pid}.scope`
|
|
28
|
+
|
|
29
|
+
/** True iff this process is a systemd service (INVOCATION_ID is set by systemd for a unit's own processes). */
|
|
30
|
+
function isSystemdService({ platform = process.platform, env = process.env } = {}) {
|
|
31
|
+
return platform === 'linux' && !!(env && env.INVOCATION_ID);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** True iff the scope move is enabled: a systemd service and CTM_HOST_OWN_SCOPE not explicitly falsy. */
|
|
35
|
+
function scopeMoveEnabled({ platform = process.platform, env = process.env } = {}) {
|
|
36
|
+
return isSystemdService({ platform, env }) && _ownScopeFlag(env);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _sanitizeLabel(label) {
|
|
40
|
+
const cleaned = String(label || 'host').replace(/[^A-Za-z0-9_.:-]/g, '-').replace(/^-+|-+$/g, '').slice(0, 60);
|
|
41
|
+
return cleaned || 'host';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function _scopeArgv(unit, pid, label) {
|
|
45
|
+
return ['--user', 'call', 'org.freedesktop.systemd1', '/org/freedesktop/systemd1',
|
|
46
|
+
'org.freedesktop.systemd1.Manager', 'StartTransientUnit', 'ssa(sv)a(sa(sv))',
|
|
47
|
+
unit, 'fail', '3', 'PIDs', 'au', '1', String(pid), 'CollectMode', 's', 'inactive-or-failed',
|
|
48
|
+
'Description', 's', `CTM survivor: ${label}`, '0'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Move `pid` into a new transient user scope. Never throws.
|
|
53
|
+
* @returns {Promise<{ moved: boolean, unit: string|null, reason: string|null, ms: number }>}
|
|
54
|
+
* reason: 'disabled' | 'not-systemd' | 'bad-pid' | 'timeout' | `exit-${code}: ${stderr}` | 'ENOENT' | `error: ...`
|
|
55
|
+
*/
|
|
56
|
+
async function moveToOwnScope(pid, {
|
|
57
|
+
label = 'host', platform = process.platform, env = process.env,
|
|
58
|
+
execFileImpl = execFile, timeoutMs = SCOPE_MOVE_TIMEOUT_MS,
|
|
59
|
+
} = {}) {
|
|
60
|
+
const started = Date.now();
|
|
61
|
+
const fail = (reason) => ({ moved: false, unit: null, reason, ms: Date.now() - started });
|
|
62
|
+
try {
|
|
63
|
+
if (!isSystemdService({ platform, env })) return fail('not-systemd');
|
|
64
|
+
if (!_ownScopeFlag(env)) return fail('disabled');
|
|
65
|
+
const n = Number(pid);
|
|
66
|
+
if (!Number.isInteger(n) || n <= 1) return fail('bad-pid');
|
|
67
|
+
const safeLabel = _sanitizeLabel(label);
|
|
68
|
+
const unit = `${SCOPE_PREFIX}${safeLabel}-${n}.scope`;
|
|
69
|
+
return await new Promise((resolve) => {
|
|
70
|
+
let settled = false;
|
|
71
|
+
const done = (r) => { if (!settled) { settled = true; resolve(r); } };
|
|
72
|
+
try {
|
|
73
|
+
execFileImpl('busctl', _scopeArgv(unit, n, safeLabel), { timeout: timeoutMs }, (error, _stdout, stderr) => {
|
|
74
|
+
if (!error) return done({ moved: true, unit, reason: null, ms: Date.now() - started });
|
|
75
|
+
if (error.code === 'ENOENT') return done(fail('ENOENT'));
|
|
76
|
+
if (error.killed || error.signal === 'SIGTERM' || error.code === 'ETIMEDOUT') return done(fail('timeout'));
|
|
77
|
+
if (Number.isInteger(error.code)) return done(fail(`exit-${error.code}: ${String(stderr || error.message || '').trim().slice(0, 200)}`));
|
|
78
|
+
return done(fail(`error: ${String(error.message || error).slice(0, 200)}`));
|
|
79
|
+
});
|
|
80
|
+
} catch (err) {
|
|
81
|
+
// HANDLE: a synchronous spawn failure is classified like an async one; the caller reports it.
|
|
82
|
+
done(fail(err && err.code === 'ENOENT' ? 'ENOENT' : `error: ${String(err && err.message || err).slice(0, 200)}`));
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
} catch (err) {
|
|
86
|
+
// HANDLE: moveToOwnScope never throws; the classified reason is REPORTED by the caller.
|
|
87
|
+
return fail(`error: ${String(err && err.message || err).slice(0, 200)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function _cgroupLine(text) {
|
|
92
|
+
const line = String(text || '').split('\n').find((l) => l.startsWith('0::'));
|
|
93
|
+
return line ? line.trim() : null;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Synchronous truth for the restart guard (D-5). True when not a systemd service (launchd semantics: proven),
|
|
98
|
+
* or when /proc/<pid>/cgroup's '0::' line differs from /proc/self/cgroup's. False on any read error.
|
|
99
|
+
*/
|
|
100
|
+
function pidSurvivesServiceStop(pid, { platform = process.platform, env = process.env, readFileSync = fs.readFileSync } = {}) {
|
|
101
|
+
if (!isSystemdService({ platform, env })) return true;
|
|
102
|
+
const n = Number(pid);
|
|
103
|
+
if (!Number.isInteger(n) || n <= 0) return false;
|
|
104
|
+
try {
|
|
105
|
+
const mine = _cgroupLine(readFileSync('/proc/self/cgroup', 'utf8'));
|
|
106
|
+
const theirs = _cgroupLine(readFileSync(`/proc/${n}/cgroup`, 'utf8'));
|
|
107
|
+
if (!mine || !theirs) return false;
|
|
108
|
+
return mine !== theirs;
|
|
109
|
+
} catch {
|
|
110
|
+
// HANDLE: a dead/unreadable pid cannot survive anything -- the guard treats it as not surviving.
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
module.exports = { isSystemdService, scopeMoveEnabled, moveToOwnScope, pidSurvivesServiceStop, SCOPE_MOVE_TIMEOUT_MS, SCOPE_PREFIX };
|
|
@@ -213,6 +213,29 @@ function markAuthoritative(id, source) {
|
|
|
213
213
|
authoritative.set(id, source);
|
|
214
214
|
}
|
|
215
215
|
|
|
216
|
+
// R4 (WP-1): a duration in the owner's own units -- "<h> h <m> m" from an hour up, "<m> m" below it.
|
|
217
|
+
// Floored to whole minutes and never negative, so the line reads the same at 30 m as at 29.9 m.
|
|
218
|
+
function bgNudgeDuration(ms) {
|
|
219
|
+
const minutes = Math.max(0, Math.floor((Number(ms) || 0) / 60000));
|
|
220
|
+
return minutes >= 60 ? `${Math.floor(minutes / 60)} h ${minutes % 60} m` : `${minutes} m`;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/*
|
|
224
|
+
* R4 (WP-1): one line of evidence per quiet declared task, delivered as a Stop notice. Pure: it reads the
|
|
225
|
+
* entry a ledger reader produced and names the owner's own remedy (TaskStop) -- CTM itself never signals.
|
|
226
|
+
* The entry's label is already capped at BG_LABEL_MAX; its newlines are folded so the notice stays one line.
|
|
227
|
+
*/
|
|
228
|
+
function renderBackgroundNudge(d, now = Date.now()) {
|
|
229
|
+
const label = String(d.label || '').replace(/\s*[\r\n]+\s*/g, ' ').trim();
|
|
230
|
+
const deadlineMs = Number(d.deadlineMs) > 0 ? Number(d.deadlineMs) : 0;
|
|
231
|
+
const firstSeenAt = Number(d.firstSeenAt) || 0;
|
|
232
|
+
const written = Number(d.outMtimeMs) > 0 ? `${bgNudgeDuration(now - Number(d.outMtimeMs))} ago` : 'never';
|
|
233
|
+
const deadlinePart = deadlineMs && firstSeenAt + deadlineMs > now
|
|
234
|
+
? `Its own deadline ends it in ${bgNudgeDuration(firstSeenAt + deadlineMs - now)}.`
|
|
235
|
+
: 'It has no declared deadline.';
|
|
236
|
+
return `Background task ${d.id} has been quiet for ${bgNudgeDuration(d.quietForMs)} (started ${bgNudgeDuration(now - firstSeenAt)} ago; output ${Number(d.outSize) || 0} bytes, last written ${written}; no CPU activity seen since): ${label}. ${deadlinePart} If it is no longer needed, run TaskStop ${d.id}. If it must keep running, say why in one line.`;
|
|
237
|
+
}
|
|
238
|
+
|
|
216
239
|
// `ledger` = { kind, meta } is the typed status-ledger evidence for this broadcast (session-status-truth
|
|
217
240
|
// D-3/D-12). It rides the in-process message as a NON-enumerable property so the WS wire shape is unchanged.
|
|
218
241
|
function broadcastStatus(id, working, source, ledger) {
|
|
@@ -660,6 +683,21 @@ function hookClaude(route, payload) {
|
|
|
660
683
|
// After approval menu, Claude can already be idle before Stop hook arrives.
|
|
661
684
|
// Force a terminal.capture so the UI gets a fresh snapshot of post-menu state.
|
|
662
685
|
setTimeout(() => broadcastFn?.({ type: 'terminal.capture', id }), 500);
|
|
686
|
+
/*
|
|
687
|
+
* R4 (WP-1): the owner is asked, never killed. Once the Stop's declared set is on the ledger, list the
|
|
688
|
+
* entries backgroundQuiet already calls quiet and hand one evidence line per task back as a notice --
|
|
689
|
+
* except while the CLI itself says this Stop is already inside a continuation (stop_hook_active), where
|
|
690
|
+
* a nudge would feed the platform's own loop. No timer, no signal, no process inspection: it is a read.
|
|
691
|
+
*/
|
|
692
|
+
const nudgedAt = Date.now();
|
|
693
|
+
const ledger = sessForTranscript?._statusLedger;
|
|
694
|
+
if (ledger && hookJson(payload, 'stop_hook_active') !== true) {
|
|
695
|
+
const due = ledger.backgroundNudges(nudgedAt);
|
|
696
|
+
if (due.length) {
|
|
697
|
+
ledger.noteBackgroundNudged(due.map((d) => d.id), nudgedAt);
|
|
698
|
+
return { matched: true, backgroundNudges: due.map((d) => ({ id: d.id, text: renderBackgroundNudge(d, nudgedAt) })) };
|
|
699
|
+
}
|
|
700
|
+
}
|
|
663
701
|
return { matched: true };
|
|
664
702
|
}
|
|
665
703
|
if (route === 'idle' || route === 'waiting') {
|
|
@@ -862,6 +900,7 @@ module.exports = {
|
|
|
862
900
|
getAuthoritativeSource,
|
|
863
901
|
getReceiptStats,
|
|
864
902
|
// exposed for tests
|
|
903
|
+
renderBackgroundNudge,
|
|
865
904
|
_test: {
|
|
866
905
|
parseAttrs, resolveSessionId, codexPendingStop, codexPendingTools,
|
|
867
906
|
resolveSessionIdFromAgentToken, resolveSessionIdForRecord,
|