create-walle 0.10.1 → 0.10.2
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 +4 -0
- package/lib/owner-write-safety.js +135 -0
- package/lib/public-runtime.js +13 -5
- package/lib/release-schema.json +3 -2
- package/lib/runtime-closure-policy.json +6 -4
- package/package.json +1 -1
- package/template/bin/deps-doctor.js +1 -1
- package/template/bin/node-bin.sh +11 -0
- package/template/coding-task-manager/api-prompts.js +10 -22
- package/template/coding-task-manager/bin/claude-hook.js +7 -3
- package/template/coding-task-manager/bin/claude-table-parity.js +307 -0
- package/template/coding-task-manager/bin/conversation-storage-auto-migration-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-generation-evidence.js +9 -2
- package/template/coding-task-manager/bin/conversation-storage-service-entry-lib.js +16 -1
- package/template/coding-task-manager/bin/conversation-storage-walle-owner.js +247 -0
- package/template/coding-task-manager/bin/ctm-machine-pair.js +160 -0
- package/template/coding-task-manager/bin/lead-wait-census.js +1348 -0
- package/template/coding-task-manager/bin/repair-foreign-clone.js +564 -0
- package/template/coding-task-manager/bin/repair-resume-echo-titles.js +248 -0
- package/template/coding-task-manager/bin/write-lock-bench.js +111 -0
- package/template/coding-task-manager/db.js +705 -286
- package/template/coding-task-manager/git-utils.js +44 -0
- package/template/coding-task-manager/lib/adopted-host-redial.js +4 -0
- package/template/coding-task-manager/lib/agent-presets.js +15 -0
- package/template/coding-task-manager/lib/agent-title-apply.js +88 -0
- package/template/coding-task-manager/lib/agent-title-backfill-read.js +92 -0
- package/template/coding-task-manager/lib/api/host-stats.js +98 -0
- package/template/coding-task-manager/lib/api/ingress.js +35 -12
- package/template/coding-task-manager/lib/api/machine-pairing.js +285 -0
- package/template/coding-task-manager/lib/api/machine-pool.js +307 -0
- package/template/coding-task-manager/lib/api/machines.js +461 -0
- package/template/coding-task-manager/lib/api/misc-ops.js +20 -5
- package/template/coding-task-manager/lib/api/providers.js +53 -6
- package/template/coding-task-manager/lib/api/resume-from-store.js +25 -0
- package/template/coding-task-manager/lib/api/sessions.js +160 -8
- package/template/coding-task-manager/lib/api/transcript-history.js +17 -4
- package/template/coding-task-manager/lib/api/worktrees.js +38 -37
- package/template/coding-task-manager/lib/approval-drift.js +16 -1
- package/template/coding-task-manager/lib/approval-logical-frame.js +5 -1
- package/template/coding-task-manager/lib/approval-scope.js +68 -4
- package/template/coding-task-manager/lib/auth-context.js +32 -2
- package/template/coding-task-manager/lib/auth-rate-limit.js +41 -1
- package/template/coding-task-manager/lib/auth-rules.js +186 -45
- package/template/coding-task-manager/lib/broadcast-serialize.js +2 -1
- package/template/coding-task-manager/lib/browser-session-broker.js +9 -1
- package/template/coding-task-manager/lib/browser-use-adapter.js +33 -2
- package/template/coding-task-manager/lib/claude-markdown-ansi.js +27 -85
- package/template/coding-task-manager/lib/claude-session-title.js +6 -30
- package/template/coding-task-manager/lib/claude-table-layout.js +322 -0
- package/template/coding-task-manager/lib/client-diag-handler.js +5 -0
- package/template/coding-task-manager/lib/codex-config-guard.js +33 -3
- package/template/coding-task-manager/lib/codex-harness-qualification.js +12 -0
- package/template/coding-task-manager/lib/codex-launch-health.js +29 -2
- package/template/coding-task-manager/lib/codex-link-preparation.js +9 -8
- package/template/coding-task-manager/lib/codex-paths.js +2 -1
- package/template/coding-task-manager/lib/coding-agent-models.js +21 -30
- package/template/coding-task-manager/lib/coding-session-model-adapters.js +27 -2
- package/template/coding-task-manager/lib/coding-session-model-projection.js +5 -0
- package/template/coding-task-manager/lib/coding-session-model-runtime.js +1 -2
- package/template/coding-task-manager/lib/coding-session-model-state.js +10 -0
- package/template/coding-task-manager/lib/coding-session-model-sync-state.js +0 -1
- package/template/coding-task-manager/lib/conversation-import/cursor-guard-owner.js +10 -0
- package/template/coding-task-manager/lib/create/resolve-launch-params.js +8 -1
- package/template/coding-task-manager/lib/ctm-boot-lifecycle.js +17 -1
- package/template/coding-task-manager/lib/db-domain-tables.js +7 -0
- package/template/coding-task-manager/lib/db-domains.js +21 -2
- package/template/coding-task-manager/lib/db-owner-op-registry.js +7 -0
- package/template/coding-task-manager/lib/db-schema-source-files.js +1 -0
- package/template/coding-task-manager/lib/feature-manifest.js +1 -1
- package/template/coding-task-manager/lib/fs-watcher.js +3 -2
- package/template/coding-task-manager/lib/harness-history-transition.js +3 -3
- package/template/coding-task-manager/lib/headless-inflight-ops.js +203 -0
- package/template/coding-task-manager/lib/headless-missing-reply.js +42 -0
- package/template/coding-task-manager/lib/headless-term-service.js +55 -17
- package/template/coding-task-manager/lib/host-control-dial.js +167 -0
- package/template/coding-task-manager/lib/host-handoff-orchestrator.js +16 -2
- package/template/coding-task-manager/lib/host-handoff.js +51 -13
- package/template/coding-task-manager/lib/host-health-ledger.js +164 -12
- package/template/coding-task-manager/lib/host-self-truth-watchdog.js +4 -0
- package/template/coding-task-manager/lib/host-stats.js +328 -0
- package/template/coding-task-manager/lib/host-threadpool-evacuation.js +160 -0
- package/template/coding-task-manager/lib/host-threadpool-monitor.js +14 -1
- package/template/coding-task-manager/lib/hosted-input-dispatch.js +203 -0
- package/template/coding-task-manager/lib/hosted-sync-worker.js +113 -5
- package/template/coding-task-manager/lib/instance-id-ownership-manifest.js +4 -0
- package/template/coding-task-manager/lib/instance-manifest-runtime.js +3 -0
- package/template/coding-task-manager/lib/instance-manifest.js +13 -1
- package/template/coding-task-manager/lib/instance-sync-status.js +15 -0
- package/template/coding-task-manager/lib/launch-model-override.js +160 -1
- package/template/coding-task-manager/lib/launch-plan-worker.js +59 -2
- package/template/coding-task-manager/lib/lead-model-switch-runtime.js +57 -4
- package/template/coding-task-manager/lib/lead-model-switch.js +3 -2
- package/template/coding-task-manager/lib/lead-model-transition-store.js +61 -3
- package/template/coding-task-manager/lib/legacy-draft-cancellation-recovery.js +111 -0
- package/template/coding-task-manager/lib/login-tripwire.js +1 -0
- package/template/coding-task-manager/lib/machine-pairing.js +238 -0
- package/template/coding-task-manager/lib/machine-pool.js +409 -0
- package/template/coding-task-manager/lib/machine-registry.js +749 -0
- package/template/coding-task-manager/lib/machine-self.js +66 -0
- package/template/coding-task-manager/lib/main-thread-lint-exemptions.json +52 -4
- package/template/coding-task-manager/lib/media-journal-control-reader.js +473 -0
- package/template/coding-task-manager/lib/media-journal.js +84 -27
- package/template/coding-task-manager/lib/media-store-options.js +43 -15
- package/template/coding-task-manager/lib/migrations/v56-host-samples-and-machine-pool.js +76 -0
- package/template/coding-task-manager/lib/mirror-feed-sanitize.js +12 -3
- package/template/coding-task-manager/lib/model-proxy/anthropic-face.js +4 -0
- package/template/coding-task-manager/lib/model-proxy/chat-response-translators.js +3 -0
- package/template/coding-task-manager/lib/model-proxy/handoff.js +494 -0
- package/template/coding-task-manager/lib/model-proxy/lead-worker-picks.js +25 -0
- package/template/coding-task-manager/lib/model-proxy/native-lead-listener.js +61 -7
- package/template/coding-task-manager/lib/model-proxy/proxy-cli.js +200 -0
- package/template/coding-task-manager/lib/model-proxy/proxy-instance-record.js +257 -0
- package/template/coding-task-manager/lib/model-proxy/stream-accounting.js +205 -0
- package/template/coding-task-manager/lib/model-proxy/tokens.js +78 -0
- package/template/coding-task-manager/lib/model-proxy/upstream-outcome.js +9 -0
- package/template/coding-task-manager/lib/native-history-control.js +2 -2
- package/template/coding-task-manager/lib/native-lead-settings-client.js +2 -1
- package/template/coding-task-manager/lib/native-lead-tls.js +18 -4
- package/template/coding-task-manager/lib/oauth-proxy-supervisor.js +628 -64
- package/template/coding-task-manager/lib/owner-action-manifest-data.json +14 -0
- package/template/coding-task-manager/lib/painted-row-recorder.js +9 -1
- package/template/coding-task-manager/lib/prompt-submission-contract.js +21 -1
- package/template/coding-task-manager/lib/provider-login-broker.js +192 -9
- package/template/coding-task-manager/lib/provider-login-capture.js +153 -0
- package/template/coding-task-manager/lib/queue-draft-status-overlay.js +36 -0
- package/template/coding-task-manager/lib/raw-codex-history-window.js +5 -3
- package/template/coding-task-manager/lib/read-pool-client.js +5 -2
- package/template/coding-task-manager/lib/read-pool-op-registry.js +10 -1
- package/template/coding-task-manager/lib/recent-catalog-query.js +26 -0
- package/template/coding-task-manager/lib/recent-session-catalog-response.js +9 -0
- package/template/coding-task-manager/lib/remote-command-context.js +12 -0
- package/template/coding-task-manager/lib/remote-command-runtime.js +13 -1
- package/template/coding-task-manager/lib/remote-machine-catalog.js +1062 -0
- package/template/coding-task-manager/lib/remote-machine-create.js +693 -0
- package/template/coding-task-manager/lib/remote-machine-link.js +939 -0
- package/template/coding-task-manager/lib/remote-machine-proxy.js +520 -0
- package/template/coding-task-manager/lib/remote-session-forward.js +306 -0
- package/template/coding-task-manager/lib/remote-submission-status.js +6 -0
- package/template/coding-task-manager/lib/restart-outcome-truth.js +20 -3
- package/template/coding-task-manager/lib/restart-preflight.js +9 -2
- package/template/coding-task-manager/lib/restore-backoff-gate.js +10 -2
- package/template/coding-task-manager/lib/restore-prepare-retry.js +12 -5
- package/template/coding-task-manager/lib/restore-resume-batch.js +22 -1
- package/template/coding-task-manager/lib/resume-blocked-wire.js +62 -1
- package/template/coding-task-manager/lib/resume-cwd.js +13 -0
- package/template/coding-task-manager/lib/resume-from-store.js +12 -1
- package/template/coding-task-manager/lib/resume-origin-facts.js +38 -1
- package/template/coding-task-manager/lib/resume-preflight.js +21 -16
- package/template/coding-task-manager/lib/review-repo-admission.js +39 -6
- package/template/coding-task-manager/lib/review-repo-registry.js +17 -0
- package/template/coding-task-manager/lib/root-delivery-runtime.js +72 -15
- package/template/coding-task-manager/lib/root-delivery-selection.js +37 -1
- package/template/coding-task-manager/lib/root-prompt-admission.js +33 -2
- package/template/coding-task-manager/lib/root-prompt-flow.js +167 -33
- package/template/coding-task-manager/lib/scrollback-archive-store.js +7 -4
- package/template/coding-task-manager/lib/session-analysis-memory.js +2 -0
- package/template/coding-task-manager/lib/session-create/codex-native-provider-gate.js +218 -0
- package/template/coding-task-manager/lib/session-create/codex-resume-exit-classify.js +52 -0
- package/template/coding-task-manager/lib/session-create/delegate-worker.js +95 -6
- package/template/coding-task-manager/lib/session-create/identity.js +20 -1
- package/template/coding-task-manager/lib/session-create/prepared.js +69 -1
- package/template/coding-task-manager/lib/session-create/provider-launch-context.js +77 -0
- package/template/coding-task-manager/lib/session-create/provider-process.js +34 -37
- package/template/coding-task-manager/lib/session-create/response.js +30 -12
- package/template/coding-task-manager/lib/session-create/transcript.js +13 -4
- package/template/coding-task-manager/lib/session-create/worker-continuation.js +43 -1
- package/template/coding-task-manager/lib/session-create/worker-notices.js +94 -5
- package/template/coding-task-manager/lib/session-create/worker-observability.js +99 -7
- package/template/coding-task-manager/lib/session-create/worker-stop.js +11 -3
- package/template/coding-task-manager/lib/session-create/worker-wait-watcher.js +315 -0
- package/template/coding-task-manager/lib/session-geometry-authority-handler.js +10 -1
- package/template/coding-task-manager/lib/session-host-manager.js +152 -206
- package/template/coding-task-manager/lib/session-jobs.js +17 -9
- package/template/coding-task-manager/lib/session-lifecycle.js +18 -0
- package/template/coding-task-manager/lib/session-model-action-preconditions.js +9 -0
- package/template/coding-task-manager/lib/session-resource-sampler.js +19 -9
- package/template/coding-task-manager/lib/session-standup.js +67 -4
- package/template/coding-task-manager/lib/session-status-ledger.js +42 -3
- package/template/coding-task-manager/lib/session-status-runtime.js +45 -1
- package/template/coding-task-manager/lib/session-title-mirror.js +25 -57
- package/template/coding-task-manager/lib/session-transcript-trash.js +8 -1
- package/template/coding-task-manager/lib/startup-pointer-write.js +57 -0
- package/template/coding-task-manager/lib/state-sync/painted-frame-field.js +11 -0
- package/template/coding-task-manager/lib/storage/coding-session-model-store.js +21 -22
- package/template/coding-task-manager/lib/surviving-owner-bootstrap.js +7 -2
- package/template/coding-task-manager/lib/surviving-owner-recovery.js +2 -1
- package/template/coding-task-manager/lib/terminal-draft-http-status.js +25 -0
- package/template/coding-task-manager/lib/transcript-archiver.js +229 -229
- package/template/coding-task-manager/lib/transcript-frame-cache.js +12 -2
- package/template/coding-task-manager/lib/transcript-guard-job.js +55 -4
- package/template/coding-task-manager/lib/transcript-link.js +77 -0
- package/template/coding-task-manager/lib/transcript-manifest-store.js +8 -4
- package/template/coding-task-manager/lib/transcript-source.js +8 -3
- package/template/coding-task-manager/lib/transcript-store.js +40 -0
- package/template/coding-task-manager/lib/tunnel-lane-governor.js +6 -3
- package/template/coding-task-manager/lib/typing-interaction-trace.js +92 -0
- package/template/coding-task-manager/lib/typing-worker-stages.js +60 -0
- package/template/coding-task-manager/lib/visible-revision-watchdog.js +8 -12
- package/template/coding-task-manager/lib/wake-skip-reasons.js +46 -0
- package/template/coding-task-manager/lib/walle-supervisor.js +98 -14
- package/template/coding-task-manager/lib/worker-failure-classify.js +13 -1
- package/template/coding-task-manager/lib/ws/state-sync.js +19 -2
- package/template/coding-task-manager/lib/ws/terminal-attach.js +32 -1
- package/template/coding-task-manager/lib/ws-router.js +68 -1
- package/template/coding-task-manager/lib/ws-send-admission.js +22 -4
- package/template/coding-task-manager/oauth-proxy.js +99 -55
- package/template/coding-task-manager/package-lock.json +24 -0
- package/template/coding-task-manager/package.json +3 -0
- package/template/coding-task-manager/providers/codex-mcp-extract.js +121 -19
- package/template/coding-task-manager/providers/codex-mcp.js +14 -5
- package/template/coding-task-manager/public/css/app.css +279 -1
- package/template/coding-task-manager/public/css/reviews.css +4 -0
- package/template/coding-task-manager/public/index.html +294 -223
- package/template/coding-task-manager/public/js/app/machines-page.js +1252 -0
- package/template/coding-task-manager/public/js/app/models-tab.js +546 -36
- package/template/coding-task-manager/public/js/app/worktrees-panel.js +7 -5
- package/template/coding-task-manager/public/js/background-work.js +13 -7
- package/template/coding-task-manager/public/js/coding-session-model-presentation.js +44 -4
- package/template/coding-task-manager/public/js/core/session-commands.js +676 -0
- package/template/coding-task-manager/public/js/core/session-selectors.js +5 -1
- package/template/coding-task-manager/public/js/history-app-seam.js +26 -2
- package/template/coding-task-manager/public/js/history-painted-rows.js +17 -2
- package/template/coding-task-manager/public/js/history-reader-park.js +13 -1
- package/template/coding-task-manager/public/js/history-scroller.js +187 -9
- package/template/coding-task-manager/public/js/history-transcript-pages.js +5 -5
- package/template/coding-task-manager/public/js/machine-pool-shared.js +436 -0
- package/template/coding-task-manager/public/js/private-state-scope.js +31 -3
- package/template/coding-task-manager/public/js/prompt-submission-client.js +173 -8
- package/template/coding-task-manager/public/js/prompt-submission-tray.js +281 -0
- package/template/coding-task-manager/public/js/queue-panel-submission-reconcile.js +174 -1
- package/template/coding-task-manager/public/js/reviews.js +7 -3
- package/template/coding-task-manager/public/js/root-draft-lifetime.js +139 -2
- package/template/coding-task-manager/public/js/root-queue-builder-lifetime.js +16 -0
- package/template/coding-task-manager/public/js/row-join.js +4 -2
- package/template/coding-task-manager/public/js/session-model-picker.js +1 -1
- package/template/coding-task-manager/public/js/session-search-utils.js +46 -19
- package/template/coding-task-manager/public/js/state-sync-client.js +4 -0
- package/template/coding-task-manager/public/js/terminal/runtime-02.js +16 -7
- package/template/coding-task-manager/public/js/terminal/runtime-03.js +41 -0
- package/template/coding-task-manager/public/js/terminal/runtime-04.js +41 -4
- package/template/coding-task-manager/public/js/terminal/runtime-12.js +52 -2
- package/template/coding-task-manager/public/js/terminal/runtime-16.js +12 -12
- package/template/coding-task-manager/public/js/terminal/runtime-18.js +14 -0
- package/template/coding-task-manager/public/js/terminal/runtime-19.js +8 -4
- package/template/coding-task-manager/public/js/terminal/runtime-20.js +210 -73
- package/template/coding-task-manager/public/js/terminal/runtime-21.js +19 -6
- package/template/coding-task-manager/public/js/terminal/runtime-22.js +86 -0
- package/template/coding-task-manager/public/js/terminal-history-viewport.js +1 -1
- package/template/coding-task-manager/public/js/terminal-wheel.js +39 -2
- package/template/coding-task-manager/public/js/walle/chat-conversation.js +11 -3
- package/template/coding-task-manager/public/js/walle-session.js +30 -7
- package/template/coding-task-manager/public/m/app.css +226 -8
- package/template/coding-task-manager/public/m/app.js +502 -9
- package/template/coding-task-manager/public/m/index.html +38 -1
- package/template/coding-task-manager/public/m/machines.js +1313 -0
- package/template/coding-task-manager/public/m/pool-failover.js +391 -0
- package/template/coding-task-manager/public/m/private-queue-actions.js +26 -7
- package/template/coding-task-manager/public/m/sw.js +5 -0
- package/template/coding-task-manager/queue-engine.js +13 -0
- package/template/coding-task-manager/server/approvals.js +5 -6
- package/template/coding-task-manager/server/boot.js +136 -17
- package/template/coding-task-manager/server.js +902 -239
- package/template/coding-task-manager/session-integrity.js +7 -9
- package/template/coding-task-manager/workers/db-owner-worker.js +53 -7
- package/template/coding-task-manager/workers/read-pool-worker.js +17 -6
- package/template/coding-task-manager/workers/session-host-pool-process.js +18 -4
- package/template/coding-task-manager/workers/session-host-process.js +14 -3
- package/template/coding-task-manager/workers/transcript-archive-worker.js +1 -1
- package/template/coding-task-manager/workers/worktree-worker.js +34 -0
- package/template/package-lock.json +2 -2
- package/template/package.json +1 -1
- package/template/scripts/conversation-storage-v2-migration.js +78 -11
- package/template/shared/conversation-storage-v2-contract.js +42 -6
- package/template/shared/fixtures/worker-status-payloads.json +392 -0
- package/template/shared/git-remote-key.js +83 -0
- package/template/shared/host-identity.js +98 -2
- package/template/shared/owner-write-safety.js +135 -0
- package/template/shared/public-runtime.js +13 -5
- package/template/shared/release-schema.json +3 -2
- package/template/shared/report-citations.js +14 -0
- package/template/shared/sqlite-write-lock.js +361 -223
- package/template/shared/storage/contract.js +35 -3
- package/template/wall-e/agent.js +21 -0
- package/template/wall-e/api-walle.js +5 -0
- package/template/wall-e/bin/brain-purge-legacy-ctm.js +3 -2
- package/template/wall-e/bin/walle-mcp-stdio.js +12 -6
- package/template/wall-e/brain/helper-rules-schema.js +129 -0
- package/template/wall-e/brain/migrations.js +51 -22
- package/template/wall-e/brain/model-deployment-facts.js +31 -1
- package/template/wall-e/brain/owner-call-whitelist.js +16 -0
- package/template/wall-e/brain/schema-ensures.js +22 -0
- package/template/wall-e/brain.js +1 -1
- package/template/wall-e/coding/journal-dispatcher.js +51 -0
- package/template/wall-e/coding/session-journal.js +28 -40
- package/template/wall-e/coding-orchestrator.js +23 -26
- package/template/wall-e/http/auth.js +18 -1
- package/template/wall-e/http/blog-views-api.js +318 -0
- package/template/wall-e/http/helper-rules-api.js +321 -0
- package/template/wall-e/http/model-proxy-providers.js +3 -1
- package/template/wall-e/http/telemetry-api.js +12 -0
- package/template/wall-e/lib/blog-view-policy.js +88 -0
- package/template/wall-e/lib/blog-view-store.js +169 -0
- package/template/wall-e/lib/client-config-lock.js +2 -1
- package/template/wall-e/lib/coding-journal-writer-child-client.js +67 -18
- package/template/wall-e/lib/error-kinds.js +10 -1
- package/template/wall-e/lib/feature-manifest.js +1 -1
- package/template/wall-e/lib/helper-rules/cell-state.js +123 -0
- package/template/wall-e/lib/helper-rules/evaluate.js +357 -0
- package/template/wall-e/lib/helper-rules/explore-rubric.md +12 -0
- package/template/wall-e/lib/helper-rules/grade.js +277 -0
- package/template/wall-e/lib/helper-rules/label.js +247 -0
- package/template/wall-e/lib/helper-rules/lead-call-hook.js +522 -0
- package/template/wall-e/lib/helper-rules/store.js +667 -0
- package/template/wall-e/lib/mcp-integration.js +5 -6
- package/template/wall-e/lib/runtime-worker-pool.js +1 -0
- package/template/wall-e/lib/scheduler-worker-jobs.js +6 -0
- package/template/wall-e/lib/telemetry-db-schema.js +37 -0
- package/template/wall-e/lib/telemetry-process-client.js +76 -1
- package/template/wall-e/llm/default-fallback.js +4 -0
- package/template/wall-e/llm/model-catalog.js +13 -4
- package/template/wall-e/llm/model-roles.js +6 -0
- package/template/wall-e/llm/portkey-http.js +150 -0
- package/template/wall-e/llm/portkey-sync.js +15 -145
- package/template/wall-e/llm/worker-mcp-routing.js +61 -0
- package/template/wall-e/llm/worker-route.js +43 -1
- package/template/wall-e/loops/brain-custom-writes.js +45 -0
- package/template/wall-e/mcp-protocol-contract.js +41 -1
- package/template/wall-e/mcp-server.js +13 -2
- package/template/wall-e/mcp-worker-schemas.js +20 -2
- package/template/wall-e/memory/ctm-context-client.js +25 -5
- package/template/wall-e/shared/release-schema.json +3 -2
- package/template/wall-e/shared/sqlite-write-lock.js +361 -223
- package/template/wall-e/shared/storage/contract.js +35 -3
- package/template/wall-e/skills/_templates/delegate-worker/VERSION +1 -1
- package/template/wall-e/skills/_templates/delegate-worker/released-sha256.json +6 -0
- package/template/wall-e/skills/_templates/delegate-worker/templates/claude/SKILL.md +7 -2
- package/template/wall-e/skills/_templates/delegate-worker/templates/codex/SKILL.md +7 -2
- package/template/wall-e/tools/local-tools.js +6 -4
- package/template/wall-e/workers/brain-owner-worker.js +9 -0
- package/template/wall-e/workers/coding-journal-writer-child.js +8 -1
- package/template/wall-e/workers/telemetry-worker.js +8 -0
|
@@ -427,6 +427,15 @@ function reconstructSessionModelTransitionSource({ sessionId, source, sourceLaun
|
|
|
427
427
|
const rebound = require('./launch-model-override').ensureCodexResumeTarget(args, source.providerThreadId);
|
|
428
428
|
if (!rebound.ok) return null;
|
|
429
429
|
args = rebound.args;
|
|
430
|
+
} else if (agent === 'claude') {
|
|
431
|
+
// The SAME canonical normalization for a native Claude source: the recorded capture may name its
|
|
432
|
+
// own thread through an explicit `--session-id` (the identity CTM injected at startup) and may
|
|
433
|
+
// still carry that launch's original positional prompt. Only the canonical owner's own
|
|
434
|
+
// `--resume <source thread>` is the source recipe, so the existing extractor below reads a
|
|
435
|
+
// rebound argv instead of rejecting the capture outright.
|
|
436
|
+
const rebound = require('./launch-model-override').ensureClaudeResumeTarget(args, source.providerThreadId);
|
|
437
|
+
if (!rebound.ok) return null;
|
|
438
|
+
args = rebound.args;
|
|
430
439
|
}
|
|
431
440
|
if ((require('./session-history').extractResumeTarget(agent, args)?.sessionId || '') !== String(source.providerThreadId)) return null;
|
|
432
441
|
return { ...context, args };
|
|
@@ -79,14 +79,14 @@ function splitAgentDescendantRows(rows, agentPid) {
|
|
|
79
79
|
const agentRssMB = agentRow ? Math.round(((Number(agentRow.rssKB) || 0) / 1024) * 100) / 100 : 0;
|
|
80
80
|
const pending = [...(byParent.get(agentPid) || [])];
|
|
81
81
|
const seen = new Set();
|
|
82
|
-
let descendantRssKB = 0, descendantGroupLeaderCount = 0; // D-10: a descendant leading its own process group (a background shell's wrapper)
|
|
82
|
+
let descendantRssKB = 0, descendantCpuTimeMs = 0, descendantGroupLeaderCount = 0; // D-10: a descendant leading its own process group (a background shell's wrapper)
|
|
83
83
|
while (pending.length) {
|
|
84
84
|
const pid = pending.pop();
|
|
85
85
|
if (seen.has(pid)) continue;
|
|
86
86
|
seen.add(pid);
|
|
87
87
|
const row = byPid.get(pid);
|
|
88
88
|
if (!row) continue;
|
|
89
|
-
descendantRssKB += Number(row.rssKB) || 0; if (row.pgid === row.pid) descendantGroupLeaderCount += 1;
|
|
89
|
+
descendantRssKB += Number(row.rssKB) || 0; descendantCpuTimeMs += Number(row.cpuTimeMs) || 0; if (row.pgid === row.pid) descendantGroupLeaderCount += 1;
|
|
90
90
|
pending.push(...(byParent.get(pid) || []));
|
|
91
91
|
}
|
|
92
92
|
return {
|
|
@@ -99,7 +99,7 @@ function splitAgentDescendantRows(rows, agentPid) {
|
|
|
99
99
|
// invisible to a pgid-only rollup).
|
|
100
100
|
descendantPids: [...seen],
|
|
101
101
|
descendantGroupLeaderCount,
|
|
102
|
-
descendantRssMB: Math.round((descendantRssKB / 1024) * 100) / 100,
|
|
102
|
+
descendantRssMB: Math.round((descendantRssKB / 1024) * 100) / 100, descendantCpuTimeMs, // WP-0 (bg-shell-liveness): Σ cpuTimeMs of every visited descendant
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
|
|
@@ -468,18 +468,28 @@ class SessionResourceSampler {
|
|
|
468
468
|
return { agentPid: Number(agentPid), descendantPids: split.descendantPids, descendantGroupLeaderCount: split.descendantGroupLeaderCount };
|
|
469
469
|
}
|
|
470
470
|
|
|
471
|
+
// D-9 (WP-6, clock-free-tests-followups-2026-10-23 §5 D-9/§8 WP-6): ONE whole-table read a caller
|
|
472
|
+
// can share across every rollup in one sweep. Same command/args/limits as sampleProcessGroup's own
|
|
473
|
+
// read, and the same maxBuffer: `ps -axo …` prints ~1.37 MB on this host, above Node's 1 MiB default.
|
|
474
|
+
async sampleProcessTable() {
|
|
475
|
+
const psCommand = this.platform === 'darwin' ? '/bin/ps' : 'ps';
|
|
476
|
+
const { stdout } = await this.exec(psCommand, PS_ARGS, { timeout: 10_000, maxBuffer: 8 * 1024 * 1024 });
|
|
477
|
+
return parseProcessTable(stdout);
|
|
478
|
+
}
|
|
479
|
+
|
|
471
480
|
// D-4: the post-kill census. One ps read; the pgid rollup as before, plus every row whose pid is in
|
|
472
481
|
// `descendantPidsBefore` (and not already in the group). Survivors are merged into pids/procCount/rssMB/cpuPct.
|
|
473
482
|
// A call with no second argument (every pre-existing caller) behaves exactly as before.
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
const
|
|
483
|
+
// D-9: `{ rows }` lets a sweep hand in the ONE table it already read (sampleProcessTable above) —
|
|
484
|
+
// then this call execs nothing. The rollup/survivor logic below is shared verbatim by both paths,
|
|
485
|
+
// so a caller that supplies rows cannot diverge from one that does not.
|
|
486
|
+
async sampleProcessGroup(pgid, descendantPidsBefore = [], { rows = null } = {}) {
|
|
487
|
+
const table = Array.isArray(rows) ? rows : await this.sampleProcessTable();
|
|
488
|
+
const group = rollupProcessGroup(table, pgid);
|
|
479
489
|
const before = new Set((descendantPidsBefore || []).map(Number));
|
|
480
490
|
if (!before.size) return group;
|
|
481
491
|
const have = new Set(group.pids);
|
|
482
|
-
const survivors =
|
|
492
|
+
const survivors = table.filter(row => before.has(Number(row.pid)) && !have.has(row.pid)).sort((a, b) => a.pid - b.pid);
|
|
483
493
|
if (!survivors.length) return group;
|
|
484
494
|
const rssKB = survivors.reduce((sum, row) => sum + (Number(row.rssKB) || 0), 0);
|
|
485
495
|
const cpuPct = survivors.reduce((sum, row) => sum + (Number(row.cpuPct) || 0), 0);
|
|
@@ -882,6 +882,15 @@ function classifySessionStandup(session, signals = {}, now = Date.now()) {
|
|
|
882
882
|
resume: session?.resume?.schemaVersion === 2 ? session.resume : null,
|
|
883
883
|
priority: LANE_ORDER.get(lane) ?? 99, backgroundWork: session?.backgroundWork || null, statusReason: session?.statusReason || '',
|
|
884
884
|
verdictAt: Number(session?.verdictAt) || Number(session?.liveStatusAt) || 0,
|
|
885
|
+
// WP-3 (remote-hub-followups D-9): the machine tag rides the card IFF the input session carries
|
|
886
|
+
// it. `_buildStandupSnapshot` stamps local standup sessions EMPTY and remote (box) sessions with
|
|
887
|
+
// the registry values, but ONLY when at least one machine is registered — so a zero-machine hub
|
|
888
|
+
// adds no key here and its snapshot stays byte-identical to the pre-WP-3 output (AC-3a).
|
|
889
|
+
...(session && Object.prototype.hasOwnProperty.call(session, 'machine') ? {
|
|
890
|
+
machine: String(session.machine == null ? '' : session.machine).trim(),
|
|
891
|
+
machineName: String(session.machineName == null ? '' : session.machineName).trim(),
|
|
892
|
+
machineHealth: String(session.machineHealth == null ? '' : session.machineHealth).trim(),
|
|
893
|
+
} : {}),
|
|
885
894
|
};
|
|
886
895
|
// WP1C: the card's bounded own/null authority fields and its shared model label, applied to THIS card
|
|
887
896
|
// object after it is built (see applyStandupModelAuthority in ./coding-session-model-public).
|
|
@@ -970,13 +979,13 @@ function isStandupStartupTaskSeedEnabled(env = process.env) {
|
|
|
970
979
|
// existing classifier can run through unmodified, so a "seeded" card gets exactly the same
|
|
971
980
|
// lane/priority logic as a live one — just starting from persisted metadata instead of a
|
|
972
981
|
// live stream.
|
|
973
|
-
function startupTaskPlaceholderSession(task) {
|
|
982
|
+
function startupTaskPlaceholderSession(task, titleInfo) {
|
|
974
983
|
if (!task) return null;
|
|
975
984
|
const id = String(task.session_id || task.ctm_session_id || '').trim();
|
|
976
985
|
if (!id) return null;
|
|
977
986
|
const placeholder = {
|
|
978
987
|
id,
|
|
979
|
-
label: task.label || '',
|
|
988
|
+
label: String(titleInfo?.title || '').trim() || task.label || '', // D-13/S-26: durable ctm_sessions title; echo only if no row
|
|
980
989
|
cmd: task.cmd || '',
|
|
981
990
|
cwd: task.cwd || '',
|
|
982
991
|
project: task.cwd || '',
|
|
@@ -1015,15 +1024,16 @@ function startupTaskPlaceholderSession(task) {
|
|
|
1015
1024
|
// a startup task appearing more than once). Callers union the result onto their live session
|
|
1016
1025
|
// list; kept separate (rather than mutating/returning a merged array) so the server can push
|
|
1017
1026
|
// each placeholder through the same standupPairs/attention wiring live sessions use.
|
|
1018
|
-
function selectUnmatchedStartupTaskPlaceholders(existingSessions, startupTasks) {
|
|
1027
|
+
function selectUnmatchedStartupTaskPlaceholders(existingSessions, startupTasks, titlesForIds) {
|
|
1019
1028
|
const seen = new Set();
|
|
1020
1029
|
for (const session of existingSessions || []) {
|
|
1021
1030
|
const id = String(session?.id || session?.sessionId || '').trim();
|
|
1022
1031
|
if (id) seen.add(id);
|
|
1023
1032
|
}
|
|
1033
|
+
const titlesById = typeof titlesForIds === 'function' ? titlesForIds((startupTasks || []).map((t) => t?.session_id || t?.ctm_session_id).filter(Boolean)) : null; // D-13/S-26: ONE batched read, never per row
|
|
1024
1034
|
const placeholders = [];
|
|
1025
1035
|
for (const task of startupTasks || []) {
|
|
1026
|
-
const placeholder = startupTaskPlaceholderSession(task);
|
|
1036
|
+
const placeholder = startupTaskPlaceholderSession(task, titlesById?.get?.(task?.session_id || task?.ctm_session_id));
|
|
1027
1037
|
if (!placeholder || seen.has(placeholder.id)) continue;
|
|
1028
1038
|
seen.add(placeholder.id);
|
|
1029
1039
|
placeholders.push(placeholder);
|
|
@@ -1031,6 +1041,57 @@ function selectUnmatchedStartupTaskPlaceholders(existingSessions, startupTasks)
|
|
|
1031
1041
|
return placeholders;
|
|
1032
1042
|
}
|
|
1033
1043
|
|
|
1044
|
+
// WP-3 (D-9): the statuses a REMOTE (box) session row can carry that mean its PTY is live. A row
|
|
1045
|
+
// whose box reports a parked/exited/stopped look (the sync-mirror or last-good degradation) is not
|
|
1046
|
+
// live. The hub never consults a LOCAL PTY helper for these rows — they are not on this host.
|
|
1047
|
+
const REMOTE_ROW_LIVE_STATUSES = new Set([
|
|
1048
|
+
'running', 'busy', 'active', 'working', 'thinking',
|
|
1049
|
+
'waiting_input', 'waiting_for_input', 'waiting',
|
|
1050
|
+
'resuming', 'restoring', 'restarting',
|
|
1051
|
+
]);
|
|
1052
|
+
|
|
1053
|
+
/** The box's own liveness for a remote catalog row (never a local PTY check). */
|
|
1054
|
+
function remoteRowTerminalLive(row) {
|
|
1055
|
+
// The row's OWN status decides when it carries one (a live reachable box row, or the at-rest
|
|
1056
|
+
// mirror's 'parked'); the boolean flags are only a fallback for a row that has no status.
|
|
1057
|
+
const status = String(row?.standupStatus || row?.liveStatus || row?.status || row?.lifecycleState || '')
|
|
1058
|
+
.trim().toLowerCase().replace(/[-\s]+/g, '_');
|
|
1059
|
+
if (status) return REMOTE_ROW_LIVE_STATUSES.has(status);
|
|
1060
|
+
return row?.terminalLive === true || row?.terminal_live === true
|
|
1061
|
+
|| row?.activeCtmSession === true || row?.active_ctm_session === true
|
|
1062
|
+
|| row?.runtimeActive === true || row?.runtime_active === true;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* WP-3 (D-9): a session-shaped object for one row of `remoteMachineCatalog.remoteRows()`, ready for
|
|
1067
|
+
* the same `classifySessionStandup` the local sessions map goes through (`summary: null` — a box row
|
|
1068
|
+
* has no local stream summary). Liveness comes from the row's OWN status; no local PTY helper runs.
|
|
1069
|
+
*
|
|
1070
|
+
* @param {object} row a remote catalog row (already machine-tagged by remoteRows())
|
|
1071
|
+
* @param {{instanceId?: string, displayName?: string, health?: string}} [machineFacts] fallback tag
|
|
1072
|
+
*/
|
|
1073
|
+
function remoteStandupSession(row, machineFacts = {}) {
|
|
1074
|
+
const source = row && typeof row === 'object' ? row : {};
|
|
1075
|
+
const terminalLive = remoteRowTerminalLive(source);
|
|
1076
|
+
const rawStatus = String(source.standupStatus || source.liveStatus || source.status || '').trim();
|
|
1077
|
+
const standupStatus = rawStatus
|
|
1078
|
+
? rawStatus.toLowerCase().replace(/[-\s]+/g, '_')
|
|
1079
|
+
: (terminalLive ? 'running' : 'parked');
|
|
1080
|
+
return {
|
|
1081
|
+
...source,
|
|
1082
|
+
machine: String(source.machine || machineFacts.instanceId || '').trim(),
|
|
1083
|
+
machineName: String(source.machineName || machineFacts.displayName || '').trim(),
|
|
1084
|
+
machineHealth: String(source.machineHealth || machineFacts.health || '').trim(),
|
|
1085
|
+
terminalLive,
|
|
1086
|
+
terminal_live: terminalLive,
|
|
1087
|
+
activeCtmSession: terminalLive,
|
|
1088
|
+
active_ctm_session: terminalLive,
|
|
1089
|
+
runtimeActive: terminalLive,
|
|
1090
|
+
runtime_active: terminalLive,
|
|
1091
|
+
standupStatus,
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1034
1095
|
function buildSessionStandupSnapshot({ sessions = [], summaries = [], statuses = [], now = Date.now() } = {}) {
|
|
1035
1096
|
const summaryIndex = buildIndex(summaries);
|
|
1036
1097
|
const statusIndex = buildIndex(statuses);
|
|
@@ -1070,6 +1131,8 @@ module.exports = {
|
|
|
1070
1131
|
startupTaskPlaceholderSession,
|
|
1071
1132
|
selectUnmatchedStartupTaskPlaceholders,
|
|
1072
1133
|
isStandupStartupTaskSeedEnabled,
|
|
1134
|
+
remoteRowTerminalLive,
|
|
1135
|
+
remoteStandupSession,
|
|
1073
1136
|
_private: {
|
|
1074
1137
|
normalizeStatus,
|
|
1075
1138
|
normalizeProvider,
|
|
@@ -72,6 +72,7 @@ const BOUNDARY_CLOSERS = new Set(['end_turn', 'interrupt']);
|
|
|
72
72
|
const BG_END_STATUSES = new Set(['completed', 'failed', 'killed', 'stopped']);
|
|
73
73
|
// renewal span, and the backstop expiry when no process sample arrives (busy-background-shell design D-3); the CLI has no local cap since 2.1.288
|
|
74
74
|
const BG_SHELL_MAX_MS = 2 * 3600_000 + 5 * 60_000;
|
|
75
|
+
const BG_QUIET_MS = 30 * 60_000;
|
|
75
76
|
const BG_DECLARED_MAX_MS = 6 * 3600_000;
|
|
76
77
|
const BG_LABEL_MAX = 160;
|
|
77
78
|
const BG_VIEW_TASKS_MAX = 8;
|
|
@@ -189,6 +190,7 @@ class SessionStatusLedger {
|
|
|
189
190
|
next.set(n.id, {
|
|
190
191
|
...n, firstSeenAt, expiresAt: keep ? Math.max(Number(prev.expiresAt) || 0, cap) : cap, source: 'hook', count: 1,
|
|
191
192
|
renewedAt: keep ? (prev.renewedAt || 0) : 0, renewals: keep ? (prev.renewals || 0) : 0, capCrossedAt: keep ? (prev.capCrossedAt || 0) : 0,
|
|
193
|
+
activeAt: keep ? (prev.activeAt || firstSeenAt) : firstSeenAt, outSize: keep ? (prev.outSize || 0) : 0, outMtimeMs: keep ? (prev.outMtimeMs || 0) : 0,
|
|
192
194
|
});
|
|
193
195
|
}
|
|
194
196
|
let changed = next.size !== before || !this.backgroundDeclared;
|
|
@@ -222,6 +224,7 @@ class SessionStatusLedger {
|
|
|
222
224
|
expiresAt: keep ? Math.max(Number(prev.expiresAt) || 0, computed) : computed,
|
|
223
225
|
source: src, count,
|
|
224
226
|
renewedAt: keep ? (prev.renewedAt || 0) : 0, renewals: keep ? (prev.renewals || 0) : 0, capCrossedAt: keep ? (prev.capCrossedAt || 0) : 0,
|
|
227
|
+
activeAt: keep ? (prev.activeAt || firstSeenAt) : firstSeenAt, outSize: keep ? (prev.outSize || 0) : 0, outMtimeMs: keep ? (prev.outMtimeMs || 0) : 0,
|
|
225
228
|
};
|
|
226
229
|
if (leaseMs) entry.leased = true;
|
|
227
230
|
this.background.set(n.id, entry);
|
|
@@ -234,16 +237,28 @@ class SessionStatusLedger {
|
|
|
234
237
|
return this.background.size > 0;
|
|
235
238
|
}
|
|
236
239
|
|
|
240
|
+
// D-1: quiet = a hook-declared shell with no activity evidence for BG_QUIET_MS. Never kills; only drops park protection.
|
|
241
|
+
backgroundQuiet(entry, now) {
|
|
242
|
+
return entry.type === 'shell' && entry.source === 'hook' && now - (entry.activeAt || 0) >= BG_QUIET_MS;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
backgroundActive(now = Date.now()) {
|
|
246
|
+
this._bgExpire(now);
|
|
247
|
+
return [...this.background.values()].some(e => !this.backgroundQuiet(e, now));
|
|
248
|
+
}
|
|
249
|
+
|
|
237
250
|
backgroundView(now = Date.now()) {
|
|
238
251
|
this._bgExpire(now);
|
|
239
252
|
if (!this.background.size) return null;
|
|
240
253
|
const live = [...this.background.values()].sort((a, b) => a.firstSeenAt - b.firstSeenAt);
|
|
241
254
|
const types = {};
|
|
242
255
|
let count = 0;
|
|
256
|
+
let quiet = 0;
|
|
257
|
+
for (const e of live) { if (this.backgroundQuiet(e, now)) quiet += 1; }
|
|
243
258
|
for (const e of live) { types[e.type] = (types[e.type] || 0) + e.count; count += e.count; }
|
|
244
259
|
return {
|
|
245
|
-
count, types,
|
|
246
|
-
tasks: live.slice(0, BG_VIEW_TASKS_MAX).map(e => ({ id: e.id, type: e.type, label: e.label, since: e.firstSeenAt })),
|
|
260
|
+
count, types, quiet,
|
|
261
|
+
tasks: live.slice(0, BG_VIEW_TASKS_MAX).map(e => ({ id: e.id, type: e.type, label: e.label, since: e.firstSeenAt, activeAt: e.activeAt || 0, quiet: this.backgroundQuiet(e, now) })),
|
|
247
262
|
observedAt: this.backgroundObservedAt, declared: this.backgroundDeclared,
|
|
248
263
|
};
|
|
249
264
|
}
|
|
@@ -257,6 +272,7 @@ class SessionStatusLedger {
|
|
|
257
272
|
entries.push({
|
|
258
273
|
id: e.id, type: e.type, label: e.label, firstSeenAt: e.firstSeenAt, expiresAt: e.expiresAt, source: e.source, count: e.count,
|
|
259
274
|
renewedAt: e.renewedAt || 0, renewals: e.renewals || 0, capCrossedAt: e.capCrossedAt || 0,
|
|
275
|
+
activeAt: e.activeAt || 0, outSize: e.outSize || 0, outMtimeMs: e.outMtimeMs || 0,
|
|
260
276
|
});
|
|
261
277
|
}
|
|
262
278
|
return { declared: this.backgroundDeclared, observedAt: this.backgroundObservedAt, entries };
|
|
@@ -279,6 +295,7 @@ class SessionStatusLedger {
|
|
|
279
295
|
firstSeenAt, expiresAt: Math.min(Number(raw.expiresAt), now + bgCapFor(type)),
|
|
280
296
|
source: String(raw.source || 'hook'), count: Math.max(1, Number(raw.count) || 1),
|
|
281
297
|
renewedAt: Number(raw.renewedAt) || 0, renewals: Math.max(0, Number(raw.renewals) || 0), capCrossedAt: Number(raw.capCrossedAt) || 0,
|
|
298
|
+
activeAt: Number(raw.activeAt) > 0 ? Number(raw.activeAt) : now, outSize: Number(raw.outSize) || 0, outMtimeMs: Number(raw.outMtimeMs) || 0,
|
|
282
299
|
});
|
|
283
300
|
n += 1;
|
|
284
301
|
}
|
|
@@ -325,6 +342,28 @@ class SessionStatusLedger {
|
|
|
325
342
|
return renewed;
|
|
326
343
|
}
|
|
327
344
|
|
|
345
|
+
// D-4: silent activity evidence (a process-sample CPU delta). Returns the number of entries whose activeAt moved.
|
|
346
|
+
markBackgroundActive(predicate, at = Date.now()) {
|
|
347
|
+
let moved = 0;
|
|
348
|
+
for (const e of this.background.values()) {
|
|
349
|
+
if (!predicate(e)) continue;
|
|
350
|
+
if (at > (e.activeAt || 0)) { e.activeAt = at; moved += 1; }
|
|
351
|
+
}
|
|
352
|
+
return moved;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// D-4: output-file growth evidence; a never-seen entry counts as growth only when size > 0.
|
|
356
|
+
observeBackgroundOutput(id, { size = 0, mtimeMs = 0 } = {}, at = Date.now()) {
|
|
357
|
+
const e = this.background.get(id);
|
|
358
|
+
if (!e) return false;
|
|
359
|
+
const seen = (e.outSize || 0) > 0 || (e.outMtimeMs || 0) > 0;
|
|
360
|
+
const grew = seen ? (size > (e.outSize || 0) || mtimeMs > (e.outMtimeMs || 0)) : size > 0;
|
|
361
|
+
if (!grew) return false;
|
|
362
|
+
e.activeAt = Math.max(e.activeAt || 0, at);
|
|
363
|
+
e.outSize = size; e.outMtimeMs = mtimeMs;
|
|
364
|
+
return true;
|
|
365
|
+
}
|
|
366
|
+
|
|
328
367
|
record(kind, { at = Date.now(), source = '', meta = {} } = {}) {
|
|
329
368
|
const prev = this.verdict(at);
|
|
330
369
|
const bgBefore = this._bgPending.length;
|
|
@@ -579,4 +618,4 @@ class SessionStatusLedger {
|
|
|
579
618
|
}
|
|
580
619
|
}
|
|
581
620
|
|
|
582
|
-
module.exports = { SessionStatusLedger, EVIDENCE, AGENT_OUTPUT, PROVIDER_STATUS_POLICY, BG_END_STATUSES, BG_SHELL_MAX_MS, BG_DECLARED_MAX_MS, BG_LABEL_MAX, normalizeBackgroundTask };
|
|
621
|
+
module.exports = { SessionStatusLedger, EVIDENCE, AGENT_OUTPUT, PROVIDER_STATUS_POLICY, BG_END_STATUSES, BG_SHELL_MAX_MS, BG_QUIET_MS, BG_DECLARED_MAX_MS, BG_LABEL_MAX, normalizeBackgroundTask };
|
|
@@ -34,6 +34,7 @@ const UNKNOWN_IDLE_BACKOFF_MS = 60000;
|
|
|
34
34
|
const OPEN_TURN_IDLE_PROBE_AFTER_MS = 15000;
|
|
35
35
|
// D-6: a session tree's CPU-time delta is process evidence at or above this floor AND at or above 5 % of one core
|
|
36
36
|
// over the sample interval (the park predicate's cpuPct >= 5); the rate keeps a 60 s sample from firing on 0.8 %.
|
|
37
|
+
const BG_OUTPUT_UNREADABLE_DIAG_MS = 10 * 60 * 1000;
|
|
37
38
|
const CPU_ACTIVE_MIN_DELTA_MS = 500;
|
|
38
39
|
const CPU_ACTIVE_MIN_CORE_FRACTION = 0.05;
|
|
39
40
|
// The fast tick fires on a 10 s timer whose jitter must not skip a due sample.
|
|
@@ -886,6 +887,17 @@ function createBindings(d) {
|
|
|
886
887
|
shape.zeros = 0; // a newer snapshot (or no shell to check) restarts the count
|
|
887
888
|
shape.observedAt = ledger.backgroundObservedAt;
|
|
888
889
|
}
|
|
890
|
+
// D-2: a descendant CPU-time delta is silent-activity evidence; the baseline is independent of snapshots.
|
|
891
|
+
const cpuNow = Number(sample.descendantCpuTimeMs);
|
|
892
|
+
if (Number.isFinite(cpuNow)) {
|
|
893
|
+
if (Number.isFinite(shape.cpuTimeMs)) {
|
|
894
|
+
const delta = cpuNow - shape.cpuTimeMs;
|
|
895
|
+
const intervalMs = Number(sample.intervalMs) > 0 ? Number(sample.intervalMs) : (sampledAt - (Number(shape.cpuSampledAt) || 0));
|
|
896
|
+
if (delta >= Math.max(CPU_ACTIVE_MIN_DELTA_MS, CPU_ACTIVE_MIN_CORE_FRACTION * intervalMs)) ledger.markBackgroundActive(isHookShell, sampledAt);
|
|
897
|
+
}
|
|
898
|
+
shape.cpuTimeMs = cpuNow;
|
|
899
|
+
shape.cpuSampledAt = sampledAt;
|
|
900
|
+
}
|
|
889
901
|
const leaders = Number(sample.descendantGroupLeaderCount);
|
|
890
902
|
if (leaders > 0 && sampledAt > ledger.backgroundObservedAt && [...ledger.background.values()].some(isHookShell)) {
|
|
891
903
|
const renewed = ledger.renewBackground(isHookShell, sampledAt); // D-1
|
|
@@ -915,6 +927,29 @@ function createBindings(d) {
|
|
|
915
927
|
d.markHeartbeatDirty(session);
|
|
916
928
|
}
|
|
917
929
|
|
|
930
|
+
// D-3: the output file of each live hook shell is a second silent-activity signal. Never throws.
|
|
931
|
+
async function observeTaskOutputs(session, sampledAt) {
|
|
932
|
+
try {
|
|
933
|
+
const ledger = session && session._statusLedger;
|
|
934
|
+
if (!ledger || !d.fsp) return;
|
|
935
|
+
const entries = [...ledger.background.values()].filter((e) => e.type === 'shell' && e.source === 'hook');
|
|
936
|
+
for (const e of entries) {
|
|
937
|
+
const p = typeof d.taskOutputPathFor === 'function' ? d.taskOutputPathFor(session, e.id) : '';
|
|
938
|
+
if (!p) continue;
|
|
939
|
+
try {
|
|
940
|
+
const st = await d.fsp.stat(p);
|
|
941
|
+
ledger.observeBackgroundOutput(e.id, { size: st.size, mtimeMs: st.mtimeMs }, sampledAt);
|
|
942
|
+
} catch (err) {
|
|
943
|
+
const at = Number(sampledAt) || Date.now();
|
|
944
|
+
if (!(at - (Number(ledger._bgOutputUnreadableAt) || 0) < BG_OUTPUT_UNREADABLE_DIAG_MS)) {
|
|
945
|
+
ledger._bgOutputUnreadableAt = at;
|
|
946
|
+
d.recordDiagnostic(session.id, 'status-background-output-unreadable', { id: e.id, code: (err && err.code) || '' });
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
} catch { /* expected: evidence-only observer must never throw into the sampler */ }
|
|
951
|
+
}
|
|
952
|
+
|
|
918
953
|
// D-6 cadence: 60 s baseline is the lifecycle sweep's own ps; while any ledger is probable/unknown/near-stall
|
|
919
954
|
// (or a session was just CPU-active) this tick takes ONE cheap ps at STATUS_FAST_SAMPLE_MS. At baseline it is a no-op.
|
|
920
955
|
let cpuTickRunning = false;
|
|
@@ -979,7 +1014,7 @@ function createBindings(d) {
|
|
|
979
1014
|
}
|
|
980
1015
|
}
|
|
981
1016
|
|
|
982
|
-
return { timeDrivenTick, inputWaitIsIdle, markHookObserved, ledgerFor, verdictFor, recordEvidence, recordWaitCleared, syncWaitFields, composerMayClose, composerRetryMs, keepBlockingWait, cardGoneTwice, cardSeenTwice, cardKeyFor, scheduleWaitEvaluation, cancelWaitEvaluations, recordPtyChunk, fireStallBackstop, observeTranscriptBoundary, observeTranscriptGrowth, observeCodexRollout, liveStatus, tick, applyCpuSample, applyProcessShape, processSampleTick };
|
|
1017
|
+
return { timeDrivenTick, inputWaitIsIdle, markHookObserved, ledgerFor, verdictFor, recordEvidence, recordWaitCleared, syncWaitFields, composerMayClose, composerRetryMs, keepBlockingWait, cardGoneTwice, cardSeenTwice, cardKeyFor, scheduleWaitEvaluation, cancelWaitEvaluations, recordPtyChunk, fireStallBackstop, observeTranscriptBoundary, observeTranscriptGrowth, observeCodexRollout, liveStatus, tick, applyCpuSample, applyProcessShape, observeTaskOutputs, processSampleTick };
|
|
983
1018
|
}
|
|
984
1019
|
|
|
985
1020
|
// W-1: under host load shedding the wait-state sweep still owes the sessions whose status truth is at stake: wait fields set,
|
|
@@ -998,6 +1033,14 @@ function shedSweepIds(sessionMap, approverBuffers, cap = 4, now = Date.now()) {
|
|
|
998
1033
|
return due.slice(0, cap).map(({ id, s }) => { s._reconcileVisitedAt = now; return id; });
|
|
999
1034
|
}
|
|
1000
1035
|
|
|
1036
|
+
// D-3: Claude Code's background-shell output file. Pure; the server passes uid and its canonical project-entry encoder.
|
|
1037
|
+
function claudeTaskOutputPathFor(session, taskId, { uid, encodeClaudeProjectEntry } = {}) {
|
|
1038
|
+
const sid = String((session && session.agentSessionId) || '').trim();
|
|
1039
|
+
const cwd = String((session && session.cwd) || '');
|
|
1040
|
+
if (!sid || !cwd || !/^[\w.-]{1,80}$/.test(String(taskId)) || String(taskId) === '..' || String(taskId) === '.') return '';
|
|
1041
|
+
return require('path').join('/tmp', 'claude-' + uid, encodeClaudeProjectEntry(cwd), sid, 'tasks', taskId + '.output');
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1001
1044
|
// R-10: the constant-size empty keepalive frame, built on the main thread (never behind a read-pool op that can time out).
|
|
1002
1045
|
function keepaliveFrame(now) {
|
|
1003
1046
|
return JSON.stringify({ type: 'session-activity', sessions: [], keepalive: true, statusTimestamp: Number(now) || 0, serverNow: Number(now) || 0 });
|
|
@@ -1015,6 +1058,7 @@ function rowFields(v, reason) {
|
|
|
1015
1058
|
|
|
1016
1059
|
module.exports = {
|
|
1017
1060
|
createBindings,
|
|
1061
|
+
claudeTaskOutputPathFor,
|
|
1018
1062
|
movingCardKey,
|
|
1019
1063
|
shedSweepIds,
|
|
1020
1064
|
keepaliveFrame,
|
|
@@ -76,14 +76,14 @@ async function _mirrorSessionTitleAttempt(o) {
|
|
|
76
76
|
const message = (error && error.message) || String(error);
|
|
77
77
|
try {
|
|
78
78
|
if (db && typeof db.setSessionTitleMirror === 'function') {
|
|
79
|
-
db.setSessionTitleMirror(sessionId, { state: 'failed', error: message, value: '' });
|
|
79
|
+
db.setSessionTitleMirror(sessionId, { state: 'failed', reason: 'exception', error: message, value: '' });
|
|
80
80
|
}
|
|
81
81
|
} catch (persistError) {
|
|
82
82
|
// expected: a persist failure on this catch-all path must not turn a resolved
|
|
83
83
|
// outcome into a throw either.
|
|
84
84
|
void persistError;
|
|
85
85
|
}
|
|
86
|
-
return { state: 'failed', reason: '', error: message };
|
|
86
|
+
return { state: 'failed', reason: 'exception', error: message };
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
@@ -91,7 +91,10 @@ async function _mirrorSessionTitleInner({ db, sessionId, adapters, getTitleSpec
|
|
|
91
91
|
const logger = typeof log === 'function' ? log : () => {};
|
|
92
92
|
const persist = (state, reason, error, value) => {
|
|
93
93
|
try {
|
|
94
|
-
|
|
94
|
+
// D-12: the REASON travels with the outcome — db.setSessionTitleMirror composes
|
|
95
|
+
// title_mirror_error = reason + (error ? ': ' + error : ''), so an `unsupported` row can no
|
|
96
|
+
// longer sit there with an empty error and no recorded cause (S-24).
|
|
97
|
+
db.setSessionTitleMirror(sessionId, { state, reason: reason || '', error: error || '', value: value || '' });
|
|
95
98
|
} catch (persistError) {
|
|
96
99
|
// expected: a persist failure must not turn a resolved outcome into a throw —
|
|
97
100
|
// the caller still gets the real state/reason/error, just unrecorded in the DB.
|
|
@@ -147,10 +150,11 @@ async function _mirrorSessionTitleInner({ db, sessionId, adapters, getTitleSpec
|
|
|
147
150
|
outcome = { state: 'unsupported', reason: 'unknown_title_spec_kind', error: '' };
|
|
148
151
|
}
|
|
149
152
|
|
|
150
|
-
// Step 7: persist + return.
|
|
153
|
+
// Step 7: persist + return. D-12: a failure that reports no reason of its own still gets one, so
|
|
154
|
+
// `failed`/`unsupported` never lands with an empty reason to compose from.
|
|
151
155
|
return persist(
|
|
152
156
|
outcome.state,
|
|
153
|
-
outcome.reason || '',
|
|
157
|
+
outcome.reason || (outcome.state === 'failed' ? 'adapter_failed' : ''),
|
|
154
158
|
outcome.error || '',
|
|
155
159
|
outcome.state === 'mirrored' ? name : row.value
|
|
156
160
|
);
|
|
@@ -166,6 +170,14 @@ async function _mirrorSessionTitleInner({ db, sessionId, adapters, getTitleSpec
|
|
|
166
170
|
// Synchronous by design (setSessionTitleNew is a plain DB write, not provider I/O) — callers on
|
|
167
171
|
// a live-session sync tick (lib/coding-agent-models.js, workers/db-owner-worker.js) never await
|
|
168
172
|
// this; a rejection would have nothing to propagate to, so it's log-and-drop and retried next tick.
|
|
173
|
+
// D-9/AC-4c: the LAST agent name a re-assert was fired for, per CTM session. The observed name is
|
|
174
|
+
// re-read on every sync tick (D-10) while the agent's own name only changes when the AGENT renames
|
|
175
|
+
// itself, so a plain per-session memo is what turns "observed on every tick" into "mirrored once per
|
|
176
|
+
// observed name". Convergence clears the entry (the agent's name equals the CTM title again), so a
|
|
177
|
+
// later drift fires again. In-memory by design: it is a coalescing memo for a live process, never a
|
|
178
|
+
// durable state machine — a restart simply re-observes.
|
|
179
|
+
const _lastReassertedName = new Map();
|
|
180
|
+
|
|
169
181
|
function applyProviderTitle(o = {}) {
|
|
170
182
|
const { db, sessionId, provider, mirror = mirrorSessionTitle } = o;
|
|
171
183
|
const agentName = normalizeSessionTitle(o.agentName);
|
|
@@ -175,13 +187,20 @@ function applyProviderTitle(o = {}) {
|
|
|
175
187
|
if (agentName && agentName !== normalizeSessionTitle(row.title)) {
|
|
176
188
|
// Fire-and-forget re-assert: the agent has drifted from the user's CTM name: push the
|
|
177
189
|
// CTM name back out. NEVER write the agent's name over a user rename.
|
|
190
|
+
const key = String(sessionId || '');
|
|
191
|
+
if (_lastReassertedName.get(key) === agentName) return { applied: 'none', reason: 'already_reasserted' };
|
|
192
|
+
_lastReassertedName.set(key, agentName);
|
|
178
193
|
void mirror({ db, sessionId });
|
|
179
194
|
return { applied: 'reasserted' };
|
|
180
195
|
}
|
|
196
|
+
if (agentName) _lastReassertedName.delete(String(sessionId || ''));
|
|
181
197
|
return { applied: 'none' };
|
|
182
198
|
}
|
|
183
199
|
|
|
184
200
|
if (agentName) {
|
|
201
|
+
// No write churn: a row already carrying this exact name (tier 2 or a tier-3 title that
|
|
202
|
+
// happens to match) is left alone — no setSessionTitleNew, no updated_at bump.
|
|
203
|
+
if (row && normalizeSessionTitle(row.title) === agentName) return { applied: 'none', reason: 'unchanged' };
|
|
185
204
|
try {
|
|
186
205
|
// Explicitly detached: the write is best-effort off a live-session sync tick and this
|
|
187
206
|
// function is synchronous, so a rejection has no caller to propagate to — the next tick
|
|
@@ -202,57 +221,6 @@ function applyProviderTitle(o = {}) {
|
|
|
202
221
|
return { applied: 'none' };
|
|
203
222
|
}
|
|
204
223
|
|
|
205
|
-
// WP-3 (session-rename-mirror §5 D-1, D-4; AC-3d/AC-3e): resolves the Claude agent's OWN
|
|
206
|
-
// session name for feeding applyProviderTitle, on demand for ONE session — never call this from
|
|
207
|
-
// a full-catalog scan loop (see AC-3f's "no process spawn" sibling concern for Codex; this one's
|
|
208
|
-
// hazard is the SDK round-trip, not a spawn). Guarded by getTitleSpec('claude')?.readsUserName:
|
|
209
|
-
// an absent/false spec (the kill switch is `titleSpec: null`) falls straight back to the
|
|
210
|
-
// caller-supplied parser result (parsedTitle, e.g. parseSessionFile's tail/head-scan title)
|
|
211
|
-
// WITHOUT ever touching the SDK adapter (AC-3e). `customTitle` (info.userName) outranks the
|
|
212
|
-
// derived `aiTitle` (info.summary when it differs from the first prompt) — same precedence
|
|
213
|
-
// lib/claude-session-title.js's readClaudeSessionTitle already encodes into its own return shape.
|
|
214
|
-
async function resolveClaudeAgentName(o = {}) {
|
|
215
|
-
const { sessionId, dir, sdk, parsedTitle = '', getTitleSpec = defaultGetTitleSpec, adapter } = o;
|
|
216
|
-
const spec = getTitleSpec('claude');
|
|
217
|
-
if (!spec || !spec.readsUserName) return normalizeSessionTitle(parsedTitle);
|
|
218
|
-
const claude = adapter || _defaultClaudeAdapter();
|
|
219
|
-
const info = await claude.readClaudeSessionTitle({ sessionId, dir, sdk });
|
|
220
|
-
if (!info) return normalizeSessionTitle(parsedTitle);
|
|
221
|
-
const name = info.userName || info.aiTitle || '';
|
|
222
|
-
return normalizeSessionTitle(name || parsedTitle);
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Fix round 1 (Finding 1): production wiring for resolveClaudeAgentName — one call per Claude
|
|
226
|
-
// session becoming known/linked to CTM (never a scan/batch loop; the SDK round-trip is the
|
|
227
|
-
// per-session hazard, matching AC-3f's spawn concern for Codex's sibling). Callers use
|
|
228
|
-
// `void fillClaudeAgentTitle(...)` — async, fire-and-forget, never awaited on a hot sync path.
|
|
229
|
-
// Cheap guard BEFORE the SDK read: a tier-1 (user_renamed) row never triggers the round-trip.
|
|
230
|
-
// Never throws into the caller — any error (a throwing db read, a rejecting resolve/apply) is
|
|
231
|
-
// reported via recordSessionDiagnostic (HANDLE) and swallowed; `unsupported`/`sdk_absent` from
|
|
232
|
-
// resolveClaudeAgentName is a normal, silent `{applied:'none'}`, not an error.
|
|
233
|
-
async function fillClaudeAgentTitle(o = {}) {
|
|
234
|
-
const { db, sessionId, provider = 'claude', claudeSessionId, dir, sdk, getTitleSpec, adapter, recordSessionDiagnostic, log } = o;
|
|
235
|
-
try {
|
|
236
|
-
const row = db.getSessionTitleMirror(sessionId);
|
|
237
|
-
if (row && row.userRenamed && row.title) return { applied: 'none' };
|
|
238
|
-
const agentName = await resolveClaudeAgentName({ sessionId: claudeSessionId, dir, sdk, getTitleSpec, adapter });
|
|
239
|
-
if (!agentName) return { applied: 'none' };
|
|
240
|
-
return applyProviderTitle({ db, sessionId, provider, agentName });
|
|
241
|
-
} catch (error) {
|
|
242
|
-
const message = (error && error.message) || String(error);
|
|
243
|
-
try {
|
|
244
|
-
if (typeof recordSessionDiagnostic === 'function') {
|
|
245
|
-
recordSessionDiagnostic(sessionId, 'claude-agent-title-fill-error', { error: message });
|
|
246
|
-
}
|
|
247
|
-
} catch (diagError) {
|
|
248
|
-
// expected: diagnostics must never turn a swallowed error into a throw.
|
|
249
|
-
void diagError;
|
|
250
|
-
}
|
|
251
|
-
if (typeof log === 'function') log('fillClaudeAgentTitle failed', message);
|
|
252
|
-
return { applied: 'none' };
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
224
|
// F1 (final review, AC-3g): shared guard for the tier-3 "live label" writer — the ONLY
|
|
257
225
|
// question this answers is "does `existing` already carry a tier that a tier-3 write must
|
|
258
226
|
// never clobber", NEVER a db access itself (pure, so both an on-thread caller with a
|
|
@@ -269,4 +237,4 @@ function canWriteTier3Title(existing) {
|
|
|
269
237
|
return true;
|
|
270
238
|
}
|
|
271
239
|
|
|
272
|
-
module.exports = { mirrorSessionTitle, isUuidLike, applyProviderTitle,
|
|
240
|
+
module.exports = { mirrorSessionTitle, isUuidLike, applyProviderTitle, canWriteTier3Title };
|
|
@@ -196,7 +196,14 @@ async function findTrashedTranscript({ trashRoot, fileName, fs: fsImpl = fsPromi
|
|
|
196
196
|
} catch { continue; /* expected: missing/corrupt manifest in this trash dir */ }
|
|
197
197
|
for (const entry of manifest.entries || []) {
|
|
198
198
|
if (path.basename(entry.originalPath) === fileName) {
|
|
199
|
-
|
|
199
|
+
// WP-3 (D-13): the manifest already carries WHY it was trashed and when; the card's "why"
|
|
200
|
+
// sentence is built from these two facts alone, so they travel with the lookup result.
|
|
201
|
+
return {
|
|
202
|
+
trashPath: entry.trashPath,
|
|
203
|
+
originalPath: entry.originalPath,
|
|
204
|
+
trashedAt: manifest.trashedAt,
|
|
205
|
+
reason: String(manifest.reason || ''),
|
|
206
|
+
};
|
|
200
207
|
}
|
|
201
208
|
}
|
|
202
209
|
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// WP-3 (docs/codex-routed-fork-cold-restore-guard-2026-10-22.html §9 S-11, §10 AC-3a/3b): the
|
|
3
|
+
// startup pointer is written ONLY through db.updateStartupTaskAgentSession, whose
|
|
4
|
+
// `resumesCandidateFork` guard (lib/lead-model-transition-store.js, commit 77b9b3c0e3) keeps the
|
|
5
|
+
// source pointer while a lead-model transition is still active. session-integrity.js used to
|
|
6
|
+
// re-point it with a raw `UPDATE startup_tasks SET args …` that bypassed the guard: the 30-min pass
|
|
7
|
+
// picks the newest transcript row — exactly the uncommitted fork of a half-switched Codex session.
|
|
8
|
+
//
|
|
9
|
+
// Callers hand this the raw better-sqlite3 core handle (workers/db-owner-worker.js
|
|
10
|
+
// 'recoverSessionIntegrity' → db.getDomainDb('core')) or the db module; either way the write goes
|
|
11
|
+
// through the module's guarded writer against the same core table. The writer returns nothing, so
|
|
12
|
+
// `args` is compared before/after to keep the fixed/skipped accounting a raw UPDATE's `.changes` gave.
|
|
13
|
+
|
|
14
|
+
function readStartupPointerArgs(handle, sessionId) {
|
|
15
|
+
try {
|
|
16
|
+
return handle.prepare('SELECT args FROM startup_tasks WHERE ctm_session_id = ?').get(sessionId)?.args;
|
|
17
|
+
} catch {
|
|
18
|
+
/* expected: no startup_tasks/args column in a partial fixture schema — the write below reports the real failure */
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Lazy require: db.js is loaded by the owner process already; nothing in its graph requires this file.
|
|
24
|
+
function startupPointerWriter(handle) {
|
|
25
|
+
if (handle && typeof handle.updateStartupTaskAgentSession === 'function') {
|
|
26
|
+
return handle.updateStartupTaskAgentSession.bind(handle);
|
|
27
|
+
}
|
|
28
|
+
const dbModule = require('../db.js');
|
|
29
|
+
if (typeof dbModule.updateStartupTaskAgentSession !== 'function') {
|
|
30
|
+
throw new Error('db.updateStartupTaskAgentSession is unavailable; refusing a raw startup_tasks pointer write');
|
|
31
|
+
}
|
|
32
|
+
return dbModule.updateStartupTaskAgentSession;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// The `agent_project_dir`/`claude_project_dir` value currently on the row — the guarded writer
|
|
36
|
+
// rewrites BOTH columns from one argument, so a caller that only means to move `args` hands back
|
|
37
|
+
// what is already stored instead of clearing the dirs.
|
|
38
|
+
function readStartupTaskProjectDir(handle, sessionId) {
|
|
39
|
+
for (const column of ['agent_project_dir', 'claude_project_dir']) {
|
|
40
|
+
try {
|
|
41
|
+
return handle.prepare(`SELECT ${column} AS dir FROM startup_tasks WHERE ctm_session_id = ?`).get(sessionId)?.dir || '';
|
|
42
|
+
} catch {
|
|
43
|
+
/* expected: column absent on this schema — try the sibling column */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Move the startup pointer through the guarded writer; returns 1 when the row changed, else 0.
|
|
50
|
+
function writeStartupPointerGuarded(handle, sessionId, resumeArgs, projectDir) {
|
|
51
|
+
const write = startupPointerWriter(handle);
|
|
52
|
+
const before = readStartupPointerArgs(handle, sessionId);
|
|
53
|
+
write(sessionId, Array.isArray(resumeArgs) ? resumeArgs[1] : '', projectDir, resumeArgs);
|
|
54
|
+
return readStartupPointerArgs(handle, sessionId) === before ? 0 : 1;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
module.exports = { writeStartupPointerGuarded, readStartupTaskProjectDir };
|
|
@@ -27,12 +27,23 @@ function nearestBreaks(breaks, top) {
|
|
|
27
27
|
return list.slice().sort((a, b) => Math.abs(top - a.at) - Math.abs(top - b.at)).slice(0, BREAKS_MAX).sort((a, b) => a.at - b.at);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
function holdOf(h) {
|
|
31
|
+
if (!h || typeof h !== 'object') return null;
|
|
32
|
+
return { kind: typeof h.kind === 'string' ? h.kind : null, rows: isInt(h.rows) && h.rows >= 0 ? h.rows : 0, top: isInt(h.top) ? h.top : null };
|
|
33
|
+
}
|
|
34
|
+
|
|
30
35
|
// `deps` = { readPainted(id, epoch, from, count), paintedRevisedSince(id, seq) }.
|
|
31
36
|
// -> an object to spread into the frame: {} or { painted: { epoch, top, pinned, floor, revision, breaks, below[, revisedFrom][, from, rows] } }
|
|
32
37
|
function paintedFrameField(deps, id, keyframe, ackedGrid, grid) {
|
|
33
38
|
const now = grid && grid.painted;
|
|
34
39
|
if (!now || typeof now !== 'object') return {};
|
|
35
40
|
const state = { epoch: now.epoch, top: now.top, pinned: now.pinned, floor: now.floor, revision: isInt(now.revision) ? now.revision : 0, breaks: nearestBreaks(now.breaks, now.top), below: isInt(now.below) && now.below > 0 ? now.below : 0 }; // R-F2 (reader-seam-proof-followups): rows the app holds below its screen; a host that never measured it (or an at-rest app) sends 0
|
|
41
|
+
// Clock-free tests (WP-6): `samples` (the recorder's monotone observe count) and `hold` (the rejected, uncommitted samples) ride
|
|
42
|
+
// the field so a reader waits on "this write was sampled" instead of a window; neither one decides whether a frame is sent.
|
|
43
|
+
// Each is present only when the recorder published it, so an older recorder's field is unchanged.
|
|
44
|
+
if (isInt(now.samples) && now.samples >= 0) state.samples = now.samples;
|
|
45
|
+
const hold = holdOf(now.hold);
|
|
46
|
+
if (hold) state.hold = hold;
|
|
36
47
|
const before = ackedGrid && ackedGrid.painted;
|
|
37
48
|
if (keyframe || !before || before.epoch !== now.epoch) return { painted: state };
|
|
38
49
|
const beforeRevision = isInt(before.revision) ? before.revision : 0;
|