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
|
@@ -85,6 +85,18 @@ function claudeProjectDirForCwd(cwd, opts = {}) {
|
|
|
85
85
|
return path.join(configDir, 'projects', encodeClaudeProjectEntry(cwd, opts));
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
// WP-2 (worker-transcript-link-derived-not-discovered-2026-10-25.html §5 D-4/D-7): the path
|
|
89
|
+
// Claude Code writes a session's transcript to, derived from (cwd, session id) alone -- no fs
|
|
90
|
+
// read, no directory scan. A resume preflight that finds an empty agent_sessions.jsonl_path
|
|
91
|
+
// uses this to look where the file deterministically is before refusing, then verifies it
|
|
92
|
+
// through lib/transcript-source.js (lib/transcript-link.js) rather than trusting the guess.
|
|
93
|
+
function deriveClaudeTranscriptPath({ cwd, agentSessionId, homeDir = process.env.HOME, claudeConfigDir = '' } = {}) {
|
|
94
|
+
const dir = String(cwd || '').trim();
|
|
95
|
+
const id = String(agentSessionId || '').trim();
|
|
96
|
+
if (!dir || !id) throw new TypeError('deriveClaudeTranscriptPath requires cwd and agentSessionId');
|
|
97
|
+
return path.join(claudeProjectDirForCwd(dir, { homeDir, claudeConfigDir }), `${id}.jsonl`);
|
|
98
|
+
}
|
|
99
|
+
|
|
88
100
|
// D-3 (main-loop-sync-io doc §5 D-3, §8 WP-0): decodeClaudeProjectEntry used to duplicate
|
|
89
101
|
// session-utils.js's own backtracking search AND add the authoritative-transcript-cwd fallback
|
|
90
102
|
// session-utils.js lacked, with no memo at all (R-2's 439,403-calls-in-88-min hot path). The full
|
|
@@ -166,6 +178,7 @@ module.exports = {
|
|
|
166
178
|
canonicalizeClaudeProjectPath,
|
|
167
179
|
resetCanonicalizeClaudeProjectPathMemo,
|
|
168
180
|
claudeProjectDirForCwd,
|
|
181
|
+
deriveClaudeTranscriptPath,
|
|
169
182
|
encodeClaudeProjectEntry,
|
|
170
183
|
encodeClaudeProjectSegment,
|
|
171
184
|
decodeClaudeProjectEntry,
|
|
@@ -372,7 +372,18 @@ function resolveResumeRung(input = {}, probes = {}) {
|
|
|
372
372
|
if (_RESTORE_STATES.has(state)) return done('restore');
|
|
373
373
|
const fileName = plainPath ? plainPath.split(/[\\/]/).pop() : '';
|
|
374
374
|
const trashed = fileName ? probes.trashEntry(fileName) : null;
|
|
375
|
-
|
|
375
|
+
// WP-3 (D-13): the trash rung rides the SAME two recorded facts as the preflight path, so the
|
|
376
|
+
// card's "why" sentence is identical whichever route found the copy.
|
|
377
|
+
if (trashed) {
|
|
378
|
+
return done('trash', {
|
|
379
|
+
detail: {
|
|
380
|
+
trashPath: trashed.trashPath,
|
|
381
|
+
originalPath: trashed.originalPath,
|
|
382
|
+
trashReason: trashed.reason || '',
|
|
383
|
+
trashedAt: trashed.trashedAt || '',
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
}
|
|
376
387
|
if (probes.storeHasHuman(ctmSessionId)) return done('store');
|
|
377
388
|
return done('fresh');
|
|
378
389
|
}
|
|
@@ -43,7 +43,7 @@ function _peerInstanceIds(syncPeers) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
function resumeOriginFacts(db, ctmSessionId, options = {}) {
|
|
46
|
-
const facts = { origin: 'unknown', originHostname: '', originIsPeer: false };
|
|
46
|
+
const facts = { origin: 'unknown', originHostname: '', originInstanceId: '', originIsPeer: false };
|
|
47
47
|
try {
|
|
48
48
|
const id = String(ctmSessionId || '').trim();
|
|
49
49
|
if (!id) return facts;
|
|
@@ -57,13 +57,46 @@ function resumeOriginFacts(db, ctmSessionId, options = {}) {
|
|
|
57
57
|
const originId = _originIdOf(handle.prepare('SELECT origin_instance_id FROM ctm_sessions WHERE id = ?').get(id));
|
|
58
58
|
facts.origin = resumeOriginFor({ origin_instance_id: originId }, identity);
|
|
59
59
|
facts.originHostname = String(birth?.hostname || '').trim();
|
|
60
|
+
// WP-6 (S-8/S-25, AC-6d): the raw MINTED origin id travels too, because it — never the hostname
|
|
61
|
+
// (S-5) — is the key a machine registration is pinned by (lib/machine-registry.js, D-3). It is
|
|
62
|
+
// what lets the resume-blocked card offer "open on <machine>" and what routes the resume itself.
|
|
63
|
+
facts.originInstanceId = originId;
|
|
60
64
|
facts.originIsPeer = !!originId && _peerInstanceIds(options.syncPeers).has(originId);
|
|
65
|
+
// WP-6 (S-8, AC-6d): when the origin is a REGISTERED, REACHABLE machine, the card can act on it
|
|
66
|
+
// instead of dead-ending at `on_other_machine`. Additive, and absent (not null) otherwise, so
|
|
67
|
+
// every existing payload keeps its exact shape. `displayName` is the registry's user label; a
|
|
68
|
+
// hostname is never used here.
|
|
69
|
+
const openOnMachine = _openOnMachineFor(originId, facts.origin);
|
|
70
|
+
if (openOnMachine) facts.openOnMachine = openOnMachine;
|
|
61
71
|
return facts;
|
|
62
72
|
} catch {
|
|
63
73
|
return facts; // expected: pre-boot / a test harness with no database -> the card says what it knows, which is nothing
|
|
64
74
|
}
|
|
65
75
|
}
|
|
66
76
|
|
|
77
|
+
// A machine that is not registered, is disabled, or is not reachable is NOT an actionable origin:
|
|
78
|
+
// the 60 s health walk (P3) means a reachable verdict is the only one worth offering an action on.
|
|
79
|
+
function _openOnMachineFor(originId, origin) {
|
|
80
|
+
const id = String(originId || '').trim();
|
|
81
|
+
if (!id || origin !== 'other_instance') return null;
|
|
82
|
+
try {
|
|
83
|
+
const registry = require('./machine-registry');
|
|
84
|
+
const db = require('../db-owner-client');
|
|
85
|
+
const { machines } = registry.listMachines({ db }) || {};
|
|
86
|
+
return _openOnMachineIn(Array.isArray(machines) ? machines : [], id);
|
|
87
|
+
} catch {
|
|
88
|
+
return null; // expected: pre-boot / no machines table -> today's card copy, byte-for-byte
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _openOnMachineIn(machines, originId) {
|
|
93
|
+
const row = (Array.isArray(machines) ? machines : [])
|
|
94
|
+
.find((machine) => machine && String(machine.instanceId || '') === originId);
|
|
95
|
+
if (!row || row.enabled === false) return null;
|
|
96
|
+
if (String(row.health || '') !== 'reachable') return null;
|
|
97
|
+
return { instanceId: String(row.instanceId || ''), displayName: String(row.displayName || '') };
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
// WP-3 (§6.4): the three sync-table facts the resume-blocked card needs to distinguish "the copy is
|
|
68
101
|
// here and gone", "the copy is on its way" and "everything is on the other machine". Every read is
|
|
69
102
|
// best-effort: a missing sync_transcript_replicas table (a pre-WP-3 database) or a pre-boot harness
|
|
@@ -152,7 +185,11 @@ function resumeOriginRecordFields(sessionId, info = {}) {
|
|
|
152
185
|
return {
|
|
153
186
|
origin,
|
|
154
187
|
originHostname: String(i.originHostname || facts.originHostname || ''),
|
|
188
|
+
originInstanceId: String(i.originInstanceId || facts.originInstanceId || ''),
|
|
155
189
|
originIsPeer: i.originIsPeer === true || facts.originIsPeer === true,
|
|
190
|
+
// WP-6 (S-8/AC-6d): the actionable-origin fact survives the record round-trip, so a caller that
|
|
191
|
+
// re-derives the wire object from the record still offers "open on <machine>".
|
|
192
|
+
...(i.openOnMachine || facts.openOnMachine ? { openOnMachine: i.openOnMachine || facts.openOnMachine } : {}),
|
|
156
193
|
...syncFacts,
|
|
157
194
|
transcriptState: i.transcriptState || deriveTranscriptState({
|
|
158
195
|
origin, localHistory: normalizeLocalHistory(i), ...syncFacts,
|
|
@@ -122,26 +122,21 @@ async function recheckResumeBlockedRecord(record, deps = {}) {
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
// Builds the human-readable "removed" sentence shared by both the Claude and Codex missing-
|
|
125
|
-
// transcript messages (R10): says the file was removed from this machine (with size +
|
|
126
|
-
// date)
|
|
127
|
-
// and
|
|
125
|
+
// transcript messages (R10, D-15): says the file was removed from this machine (with size +
|
|
126
|
+
// last-seen date) and whether CTM kept a copy it can put back. No path, no "move it back", no
|
|
127
|
+
// "Retry will work" — the user never handles a file, and the card's Details row carries paths.
|
|
128
128
|
function _removedTranscriptSentence(fields) {
|
|
129
129
|
const dateText = fields.lastSeenAt ? fields.lastSeenAt.slice(0, 10) : 'an earlier session';
|
|
130
|
-
|
|
131
|
-
+
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
+ `${fields.originalPath || 'its original location'} and Retry will work.`;
|
|
135
|
-
} else {
|
|
136
|
-
sentence += ' Start a new session from the cached history, or restore the missing transcript from backup.';
|
|
137
|
-
}
|
|
138
|
-
return sentence;
|
|
130
|
+
return `was removed from this machine (${_formatBytes(fields.lastSeenBytes)}, last seen ${dateText}). `
|
|
131
|
+
+ (fields.trashPath
|
|
132
|
+
? 'CTM kept a copy and can put it back.'
|
|
133
|
+
: 'CTM still has the cached conversation history.');
|
|
139
134
|
}
|
|
140
135
|
|
|
141
136
|
// Pure (no fs/DB): given a `localHistory === 'removed'` preflight result and a WP-1
|
|
142
|
-
// findTrashedTranscript() lookup result (`{ trashPath, originalPath, trashedAt } | null`,
|
|
143
|
-
// the CALLER -- this module never runs that async fs read itself), returns a new result
|
|
144
|
-
// `trashPath`/`originalPath` set and `message` rebuilt
|
|
137
|
+
// findTrashedTranscript() lookup result (`{ trashPath, originalPath, trashedAt, reason } | null`,
|
|
138
|
+
// found by the CALLER -- this module never runs that async fs read itself), returns a new result
|
|
139
|
+
// with `trashPath`/`originalPath`/`trashReason`/`trashedAt` set and `message` rebuilt. `trashed ===
|
|
145
140
|
// null` (nothing found, or no trashRoot configured) returns `result` unchanged -- same message as
|
|
146
141
|
// before. Never mutates its input.
|
|
147
142
|
function withTrashLocation(result, trashed) {
|
|
@@ -151,7 +146,14 @@ function withTrashLocation(result, trashed) {
|
|
|
151
146
|
const isCodex = result.code === 'missing_codex_rollout';
|
|
152
147
|
const message = `Cannot resume ${isCodex ? 'Codex' : 'Claude'} session ${shortId}: `
|
|
153
148
|
+ `${isCodex ? 'the local Codex rollout file' : 'the local Claude transcript file'} ${_removedTranscriptSentence(fields)}`;
|
|
154
|
-
return {
|
|
149
|
+
return {
|
|
150
|
+
...result,
|
|
151
|
+
trashPath: trashed.trashPath,
|
|
152
|
+
originalPath: trashed.originalPath,
|
|
153
|
+
trashReason: trashed.reason || '',
|
|
154
|
+
trashedAt: trashed.trashedAt || '',
|
|
155
|
+
message,
|
|
156
|
+
};
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
// A Claude transcript is "real" only if it carries at least one conversation
|
|
@@ -676,6 +678,9 @@ module.exports = {
|
|
|
676
678
|
claudeLiveTranscriptShouldRestore,
|
|
677
679
|
restoreClaudeLiveTranscriptFromBak,
|
|
678
680
|
withTrashLocation,
|
|
681
|
+
// WP-3 (D-15, AC-3c): exported so the exact sentence is pinned by a unit test instead of only by the
|
|
682
|
+
// message it is embedded in.
|
|
683
|
+
_removedTranscriptSentence,
|
|
679
684
|
withResumeRung,
|
|
680
685
|
recheckResumeBlockedRecord,
|
|
681
686
|
MISSING_TRANSCRIPT_CODES,
|
|
@@ -14,9 +14,19 @@ const os = require('os');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
|
|
16
16
|
const REJECT_SEGMENTS = new Set(['node_modules', '.cache']);
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
|
|
17
|
+
// Every scratch checkout the repo's own tooling mktemps under `<repo>/.worktrees/` and deletes
|
|
18
|
+
// when it is done. Admitting one leaves a registry row (or a /api/projects picker row, WP-2 of
|
|
19
|
+
// docs/remote-hub-followups-credential-projects-rows-idempotency-2026-10-23.html) whose cwd
|
|
20
|
+
// vanishes (`spawn git ENOENT`) or that the user never asked for. Widened from the single
|
|
21
|
+
// pre-commit template (S-10) to all four; each one is pinned by name in
|
|
22
|
+
// tests-integration/api-projects-rows.test.js (AC-2e), which reads the templates back out of the
|
|
23
|
+
// gate scripts themselves.
|
|
24
|
+
const GATE_SCRATCH_BASENAME_PATTERNS = Object.freeze([
|
|
25
|
+
/^test-gate-merged\./, // bin/test-gate.sh (merged-tree gate worktree)
|
|
26
|
+
/^precommit-staged\./, // bin/pre-commit-hook.sh
|
|
27
|
+
/^nightly-gate\./, // bin/nightly-gate.sh
|
|
28
|
+
/^deflake-snapshot-/, // bin/deflake-source-snapshot.js
|
|
29
|
+
]);
|
|
20
30
|
const WORKTREE_PARENT_SEGMENTS = ['.claude/worktrees', '.walle/worktrees', '.worktrees'];
|
|
21
31
|
|
|
22
32
|
function _segments(p) {
|
|
@@ -44,12 +54,28 @@ function _rejectReason(candidatePath, env) {
|
|
|
44
54
|
if (REJECT_SEGMENTS.has(seg)) return 'dependency-dir';
|
|
45
55
|
}
|
|
46
56
|
for (const seg of _segments(candidatePath)) {
|
|
47
|
-
if (
|
|
57
|
+
if (GATE_SCRATCH_BASENAME_PATTERNS.some((re) => re.test(seg))) return 'temp';
|
|
48
58
|
}
|
|
49
59
|
if (env.hidden && env.hidden.has(candidatePath)) return 'hidden';
|
|
50
60
|
return null;
|
|
51
61
|
}
|
|
52
62
|
|
|
63
|
+
/**
|
|
64
|
+
* True iff `p` is a gate scratch checkout: its PARENT directory is named `.worktrees` and its
|
|
65
|
+
* basename matches one of the gate's own mktemp templates (GATE_SCRATCH_BASENAME_PATTERNS).
|
|
66
|
+
* A real worktree the user created (e.g. `<repo>/.worktrees/feature-x`) is NOT gate scratch and
|
|
67
|
+
* stays visible; the four tooling templates never do. Used by the worktree worker's `projectRows`
|
|
68
|
+
* op (WP-2) — the Review Hub admission filter below keeps its own segment scan over the same list.
|
|
69
|
+
* @param {string} p
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
function isGateScratchPath(p) {
|
|
73
|
+
const value = String(p || '');
|
|
74
|
+
const name = path.basename(value);
|
|
75
|
+
if (path.basename(path.dirname(value)) !== '.worktrees') return false;
|
|
76
|
+
return GATE_SCRATCH_BASENAME_PATTERNS.some((re) => re.test(name));
|
|
77
|
+
}
|
|
78
|
+
|
|
53
79
|
/**
|
|
54
80
|
* @returns {{ok:true, realpath, kind:'primary'|'worktree', repoRoot, gitDir, viaSubdir?:true}
|
|
55
81
|
* | {ok:false, reason:'missing'|'outside-home'|'temp'|'scratch'|'dependency-dir'|'not-git'|'hidden'}}
|
|
@@ -97,7 +123,7 @@ function admitReviewRepo(candidatePath, env) {
|
|
|
97
123
|
}
|
|
98
124
|
|
|
99
125
|
for (const seg of _segments(realpath)) {
|
|
100
|
-
if (
|
|
126
|
+
if (GATE_SCRATCH_BASENAME_PATTERNS.some((re) => re.test(seg))) return { ok: false, reason: 'temp' };
|
|
101
127
|
}
|
|
102
128
|
|
|
103
129
|
if (env.hidden && env.hidden.has(realpath)) return { ok: false, reason: 'hidden' };
|
|
@@ -145,4 +171,11 @@ function defaultAdmissionEnv({ home = os.homedir(), scratchRoot = null } = {}) {
|
|
|
145
171
|
};
|
|
146
172
|
}
|
|
147
173
|
|
|
148
|
-
module.exports = {
|
|
174
|
+
module.exports = {
|
|
175
|
+
admitReviewRepo,
|
|
176
|
+
defaultAdmissionEnv,
|
|
177
|
+
isGateScratchPath,
|
|
178
|
+
GATE_SCRATCH_BASENAME_PATTERNS,
|
|
179
|
+
REJECT_SEGMENTS,
|
|
180
|
+
WORKTREE_PARENT_SEGMENTS,
|
|
181
|
+
};
|
|
@@ -665,6 +665,23 @@ function createReviewRepoRegistry({ db, gitRead, sessionFeed, readSources, broad
|
|
|
665
665
|
next.watcher = watchFn(dir, { persistent: false }, () => {
|
|
666
666
|
for (const p of next.repoPaths) _scheduleWatchRefresh(p);
|
|
667
667
|
});
|
|
668
|
+
// F-2 (clock-free follow-ups r3): a synchronous try/catch is NOT an 'error' listener.
|
|
669
|
+
// On macOS libuv broadcasts one FSEventStreamStart failure (UV_EMFILE) to EVERY
|
|
670
|
+
// directory watcher in the loop (src/unix/fsevents.c), asynchronously — so without
|
|
671
|
+
// this listener the EventEmitter throws and the whole CTM process exits (observed:
|
|
672
|
+
// "EMFILE: too many open files, watch" 13 s after a dev restart).
|
|
673
|
+
next.watcher.on('error', (error) => {
|
|
674
|
+
// REPORT once per dir; the next discover() pass (boot, a new transcript, or the client
|
|
675
|
+
// Refresh) re-watches it via _watchRepo. Until then the 60 s sweep keeps status correct.
|
|
676
|
+
// No timer and no immediate re-attach: a re-attach rebuilds the one shared FSEvents
|
|
677
|
+
// stream, which would just re-fail every handle (D-2).
|
|
678
|
+
if (watchedDirs.get(dir) === next) watchedDirs.delete(dir);
|
|
679
|
+
try { next.watcher.close(); } catch { /* expected: an errored FSWatcher may throw on close */ }
|
|
680
|
+
if (!warnedWatchDirs.has(dir)) {
|
|
681
|
+
warnedWatchDirs.add(dir);
|
|
682
|
+
console.warn('[review-registry] watch error', dir, error && error.code);
|
|
683
|
+
}
|
|
684
|
+
});
|
|
668
685
|
} catch (error) {
|
|
669
686
|
// REPORT once per dir: the 60s sweep is the fallback for a dir this process can't
|
|
670
687
|
// watch (e.g. an fs.watch fd/inotify-limit failure), not a hard failure.
|
|
@@ -51,6 +51,7 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
51
51
|
const {
|
|
52
52
|
coordinator, readLatestDelivery, readJobText, preflight, buildWritePayload,
|
|
53
53
|
deliverTerminal, isDeliveryCurrent = () => true, now = Date.now,
|
|
54
|
+
reportDiagnostic = () => {},
|
|
54
55
|
} = deps;
|
|
55
56
|
const required = { 'coordinator.execute': coordinator?.execute, readLatestDelivery, readJobText,
|
|
56
57
|
preflight, buildWritePayload, deliverTerminal };
|
|
@@ -58,10 +59,23 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
58
59
|
if (typeof callback !== 'function') throw new TypeError(`Root delivery runtime requires ${name}`);
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
// WP-0: one bounded diagnostic per rejected handoff stage. `phase` is the exact
|
|
63
|
+
// stage the driver gave up at and `code` is a static, non-parameterized token --
|
|
64
|
+
// no prompt text, attachment, auth fact, path, receipt id, or raw exception text
|
|
65
|
+
// ever rides along, and a throwing sink can never change an outcome.
|
|
66
|
+
const report = (phase, code) => {
|
|
67
|
+
if (typeof reportDiagnostic !== 'function') return;
|
|
68
|
+
try { reportDiagnostic(Object.freeze({ phase, code })); return true; }
|
|
69
|
+
catch { return false; } // Diagnostics are advisory; their failure cannot change delivery.
|
|
70
|
+
};
|
|
71
|
+
|
|
61
72
|
async function drive(admissionHandle, { automatic = false, nowMs = now() } = {}) {
|
|
62
73
|
const committed = getCommittedRootAdmission(admissionHandle);
|
|
63
74
|
if (!committed || typeof committed.isCurrent !== 'function' || !committed.selector
|
|
64
|
-
|| !committed.metadata || !committed.target || typeof committed.target.isCurrent !== 'function')
|
|
75
|
+
|| !committed.metadata || !committed.target || typeof committed.target.isCurrent !== 'function') {
|
|
76
|
+
report('admission_metadata', 'root_admission_metadata_unavailable');
|
|
77
|
+
return unavailable();
|
|
78
|
+
}
|
|
65
79
|
const intent = rootPromptIntent.getCapturedRootPromptIntent(committed.intentHandle);
|
|
66
80
|
const parent = committed.metadata;
|
|
67
81
|
const deliveryCurrent = () => {
|
|
@@ -70,6 +84,7 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
70
84
|
if (!intent || intent.sessionId !== parent.sessionId || intent.source !== parent.source
|
|
71
85
|
|| !committed.isCurrent()) {
|
|
72
86
|
retiredJobs.add(admissionHandle);
|
|
87
|
+
report('admission_metadata', 'root_admission_metadata_unavailable');
|
|
73
88
|
return unavailable();
|
|
74
89
|
}
|
|
75
90
|
if (!deliveryCurrent()) return unavailable('root_delivery_expired');
|
|
@@ -98,22 +113,37 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
98
113
|
};
|
|
99
114
|
const selector = committed.selector;
|
|
100
115
|
let snapshot;
|
|
101
|
-
try { snapshot = await readLatestDelivery(selector); } catch {
|
|
116
|
+
try { snapshot = await readLatestDelivery(selector); } catch {
|
|
117
|
+
report('delivery_snapshot', 'root_delivery_snapshot_unavailable');
|
|
118
|
+
return unavailable();
|
|
119
|
+
}
|
|
102
120
|
if (!stillJobCurrent()) return unavailable();
|
|
103
121
|
if (!deliveryCurrent()) return unavailable('root_delivery_expired');
|
|
104
|
-
if (!snapshot || !snapshot.parent || !sameMetadata(snapshot.parent, parent))
|
|
122
|
+
if (!snapshot || !snapshot.parent || !sameMetadata(snapshot.parent, parent)) {
|
|
123
|
+
report('delivery_snapshot', 'root_delivery_snapshot_unavailable');
|
|
124
|
+
return unavailable();
|
|
125
|
+
}
|
|
105
126
|
const selected = selectRootDeliveryAttempt(snapshot, { automatic, nowMs });
|
|
106
|
-
if (!selected?.ok)
|
|
127
|
+
if (!selected?.ok) {
|
|
128
|
+
report('delivery_selection', 'root_delivery_selection_refused');
|
|
129
|
+
return selected || unavailable();
|
|
130
|
+
}
|
|
107
131
|
|
|
108
132
|
let workerResult;
|
|
109
|
-
try { workerResult = await readJobText(selector, parent.textBytes); } catch {
|
|
133
|
+
try { workerResult = await readJobText(selector, parent.textBytes); } catch {
|
|
134
|
+
report('delivery_text', 'root_delivery_text_unavailable');
|
|
135
|
+
return unavailable();
|
|
136
|
+
}
|
|
110
137
|
if (!stillJobCurrent()) return unavailable();
|
|
111
138
|
if (!deliveryCurrent()) return unavailable('root_delivery_expired');
|
|
112
139
|
const buffer = workerResult?.__resultBuffer;
|
|
113
140
|
const metadata = workerResult?.__resultMetadata;
|
|
114
141
|
if (!Buffer.isBuffer(buffer) || !sameMetadata(metadata, parent) || buffer.byteLength !== parent.textBytes
|
|
115
142
|
|| crypto.createHash('sha256').update(buffer).digest('hex') !== parent.textHash
|
|
116
|
-
|| !buffer.equals(Buffer.from(intent.text, 'utf8')))
|
|
143
|
+
|| !buffer.equals(Buffer.from(intent.text, 'utf8'))) {
|
|
144
|
+
report('delivery_text', 'root_delivery_text_unavailable');
|
|
145
|
+
return unavailable();
|
|
146
|
+
}
|
|
117
147
|
const text = buffer.toString('utf8');
|
|
118
148
|
|
|
119
149
|
const expectedChildId = remoteCommandStore.remoteCommandReceiptId(selector.namespaceHash, selected.commandId);
|
|
@@ -128,18 +158,26 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
128
158
|
authorize,
|
|
129
159
|
dispatch: async (message) => {
|
|
130
160
|
if (message?.id !== expectedChildId || message?.type !== ROOT_DELIVERY_ACTION || message?.version !== 1
|
|
131
|
-
|| !message.body || JSON.stringify(message.body) !== JSON.stringify(selected.body)
|
|
132
|
-
|
|
161
|
+
|| !message.body || JSON.stringify(message.body) !== JSON.stringify(selected.body)) {
|
|
162
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
163
|
+
return unavailable();
|
|
164
|
+
}
|
|
165
|
+
if (!stillJobCurrent()) return unavailable();
|
|
133
166
|
let blocker;
|
|
134
|
-
try { blocker = preflight(parent.sessionId, committed.target.session, text); } catch {
|
|
167
|
+
try { blocker = preflight(parent.sessionId, committed.target.session, text); } catch {
|
|
168
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
169
|
+
return unavailable('root_delivery_preflight_unavailable');
|
|
170
|
+
}
|
|
135
171
|
if (blocker && typeof blocker?.then === 'function') {
|
|
136
172
|
// A promise violates the synchronous preflight contract; consume either settlement without exposing it.
|
|
137
173
|
void Promise.resolve(blocker).then(() => false, () => false);
|
|
174
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
138
175
|
return unavailable('root_delivery_preflight_unavailable');
|
|
139
176
|
}
|
|
140
177
|
if (blocker !== null) {
|
|
141
178
|
if (!blocker || typeof blocker !== 'object' || Array.isArray(blocker)
|
|
142
179
|
|| blocker.ok !== false || !PREFLIGHT.has(blocker.error)) {
|
|
180
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
143
181
|
return unavailable('root_delivery_preflight_unavailable');
|
|
144
182
|
}
|
|
145
183
|
const blockerCode = blocker.error;
|
|
@@ -150,16 +188,29 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
150
188
|
} };
|
|
151
189
|
}
|
|
152
190
|
if (!deliveryCurrent()) return unavailable('root_delivery_expired');
|
|
153
|
-
if (!stillTargetCurrent())
|
|
191
|
+
if (!stillTargetCurrent()) {
|
|
192
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
193
|
+
return unavailable('terminal_target_replaced');
|
|
194
|
+
}
|
|
154
195
|
let payload;
|
|
155
|
-
try { payload = buildWritePayload(committed.target.session, text); } catch {
|
|
196
|
+
try { payload = buildWritePayload(committed.target.session, text); } catch {
|
|
197
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
198
|
+
return unavailable('root_delivery_payload_unavailable');
|
|
199
|
+
}
|
|
156
200
|
if (payload && typeof payload?.then === 'function') {
|
|
157
201
|
// Payload construction is synchronous by contract; consume async contract violations safely.
|
|
158
202
|
void Promise.resolve(payload).then(() => false, () => false);
|
|
203
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
204
|
+
return unavailable('root_delivery_payload_unavailable');
|
|
205
|
+
}
|
|
206
|
+
if (typeof payload !== 'string' || !payload || !stillJobCurrent()) {
|
|
207
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
159
208
|
return unavailable('root_delivery_payload_unavailable');
|
|
160
209
|
}
|
|
161
|
-
if (
|
|
162
|
-
|
|
210
|
+
if (!stillTargetCurrent()) {
|
|
211
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
212
|
+
return unavailable('terminal_target_replaced');
|
|
213
|
+
}
|
|
163
214
|
const permit = Object.freeze({});
|
|
164
215
|
const permitState = { admissionHandle, committed, target: committed.target, parent, deliveryCurrent,
|
|
165
216
|
text, payload, active: true, phase: 'text', pending: null };
|
|
@@ -174,12 +225,18 @@ function createRootDeliveryRuntime(deps = {}) {
|
|
|
174
225
|
delivered = await deliverTerminal({ permit, target: committed.target, sessionId: parent.sessionId,
|
|
175
226
|
text, submissionId: parent.receiptId, source: intent.source,
|
|
176
227
|
attachments: intent.attachments });
|
|
177
|
-
} catch {
|
|
228
|
+
} catch {
|
|
229
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
230
|
+
return { ok: false, error: 'root_delivery_failed', retryable: false };
|
|
231
|
+
}
|
|
178
232
|
if (!deliveryCurrent()) return unavailable('root_delivery_expired');
|
|
179
233
|
return cleanDeliveryResult(delivered);
|
|
180
234
|
},
|
|
181
235
|
});
|
|
182
|
-
} catch {
|
|
236
|
+
} catch {
|
|
237
|
+
report('delivery_dispatch', 'root_delivery_dispatch_failed');
|
|
238
|
+
result = unavailable('remote_command_unresolved');
|
|
239
|
+
}
|
|
183
240
|
finally {
|
|
184
241
|
if (issuedPermit) retirePermit(issuedPermit);
|
|
185
242
|
}
|
|
@@ -16,6 +16,21 @@ const AUTOMATIC_BLOCKERS = new Set([
|
|
|
16
16
|
]);
|
|
17
17
|
const AUTO_LIMIT = 5;
|
|
18
18
|
const AUTO_AGE_MS = 30 * 60 * 1000;
|
|
19
|
+
// A parent row that reached one of these statuses is itself the recorded proof
|
|
20
|
+
// that the prompt reached the terminal (see prompt-submission-contract's
|
|
21
|
+
// TERMINAL_STATUS_MAP). Nothing to recover: the client is told "sent".
|
|
22
|
+
const SENT_PARENT_STATUSES = new Set([
|
|
23
|
+
'sent', 'accepted_by_terminal', 'durable_user_message_seen', 'response_started', 'queued_by_agent',
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
// WP-0 (queue-delivery-recovery): the ONLY three verdicts the recovery classifier
|
|
27
|
+
// may return. `recoveryAction` is the action a client should offer; `deliveryState`
|
|
28
|
+
// is what CTM can prove about the prompt. Each is a frozen singleton so a caller can
|
|
29
|
+
// never mutate a verdict, and none carries a permit, proof, or receipt id -- this
|
|
30
|
+
// classifier never grants delivery authority.
|
|
31
|
+
const RECOVERY_SENT = Object.freeze({ recoveryAction: 'none', deliveryState: 'sent' });
|
|
32
|
+
const RECOVERY_NOT_STARTED = Object.freeze({ recoveryAction: 'resend', deliveryState: 'not_started' });
|
|
33
|
+
const RECOVERY_UNKNOWN = Object.freeze({ recoveryAction: 'review', deliveryState: 'unknown' });
|
|
19
34
|
|
|
20
35
|
function unavailable() {
|
|
21
36
|
return Object.freeze({ ok: false, code: 'root_job_unavailable', retryable: false });
|
|
@@ -119,4 +134,25 @@ function selectRootDeliveryAttempt(snapshot, { automatic = false, nowMs = Date.n
|
|
|
119
134
|
});
|
|
120
135
|
}
|
|
121
136
|
|
|
122
|
-
|
|
137
|
+
// Truthful recovery classification for a parent whose local delivery job is gone
|
|
138
|
+
// (process restart, replayed id, lost registry). It answers one question only:
|
|
139
|
+
// may the user resend the SAME text, or must a human review first?
|
|
140
|
+
//
|
|
141
|
+
// confirmed sent parent status -> sent / none
|
|
142
|
+
// valid no-child or valid certified no-effect child -> not_started / resend
|
|
143
|
+
// everything else (invalid, read-failed, reserved,
|
|
144
|
+
// unknown, ambiguous, exhausted) -> unknown / review
|
|
145
|
+
//
|
|
146
|
+
// `sent` requires the durable parent row itself to say so. A proven no-child
|
|
147
|
+
// snapshot (`latest: null`) and a certified preflight_no_effect child both permit
|
|
148
|
+
// `resend`; a snapshot whose `latest` field is absent or malformed proves nothing
|
|
149
|
+
// and stays `review`.
|
|
150
|
+
function selectRootDeliveryRecovery(snapshot) {
|
|
151
|
+
const parent = validParent(snapshot);
|
|
152
|
+
if (!parent) return RECOVERY_UNKNOWN;
|
|
153
|
+
if (SENT_PARENT_STATUSES.has(parent.status)) return RECOVERY_SENT;
|
|
154
|
+
const selected = selectRootDeliveryAttempt(snapshot, { automatic: false });
|
|
155
|
+
return selected?.ok === true ? RECOVERY_NOT_STARTED : RECOVERY_UNKNOWN;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
module.exports = { selectRootDeliveryAttempt, selectRootDeliveryRecovery };
|
|
@@ -315,6 +315,7 @@ function createRootPromptAdmission(deps = {}) {
|
|
|
315
315
|
});
|
|
316
316
|
const expected = Object.freeze({ ...expectedJob });
|
|
317
317
|
let metadata = null;
|
|
318
|
+
let metadataReadDiagnostic = null;
|
|
318
319
|
try {
|
|
319
320
|
if (currentIdentity(getDb, getInstanceId, originalDb, instanceId)) {
|
|
320
321
|
const candidate = await promptAdmissionTrace.time('metadata.read', () => readAcceptedMetadata(selector));
|
|
@@ -322,8 +323,38 @@ function createRootPromptAdmission(deps = {}) {
|
|
|
322
323
|
metadata = validateMetadata(candidate, selector, expected);
|
|
323
324
|
}
|
|
324
325
|
}
|
|
325
|
-
} catch {
|
|
326
|
-
|
|
326
|
+
} catch (error) {
|
|
327
|
+
metadata = null;
|
|
328
|
+
metadataReadDiagnostic = canonicalReadError(error, originalDb);
|
|
329
|
+
}
|
|
330
|
+
if (!metadata) {
|
|
331
|
+
// WP-0 criterion 5: this admission dispatched a FRESH parent and confirmed
|
|
332
|
+
// its durable record, but the bounded metadata read did not reproduce the
|
|
333
|
+
// committed descriptor. That is a typed handoff failure -- the caller must
|
|
334
|
+
// never see the cached acceptance without a committed in-memory handle. The
|
|
335
|
+
// committed historical record is left untouched: no permission is recreated
|
|
336
|
+
// from the database, and nothing is written back. A plain cached replay
|
|
337
|
+
// never reaches here (it does not dispatch), so it is not mislabelled.
|
|
338
|
+
if (didDispatch && confirmed && expectedJob) {
|
|
339
|
+
reportDiagnosticSafely(reportDiagnostic, {
|
|
340
|
+
phase: 'admission_metadata',
|
|
341
|
+
code: 'root_admission_metadata_unavailable',
|
|
342
|
+
...(metadataReadDiagnostic ? {
|
|
343
|
+
errorKind: metadataReadDiagnostic.errorKind,
|
|
344
|
+
nativeHandleOpen: metadataReadDiagnostic.nativeHandleOpen,
|
|
345
|
+
nativeHandleClosed: metadataReadDiagnostic.nativeHandleClosed,
|
|
346
|
+
...(metadataReadDiagnostic.failureClass ? { failureClass: metadataReadDiagnostic.failureClass } : {}),
|
|
347
|
+
...(metadataReadDiagnostic.sourceFile ? { sourceFile: metadataReadDiagnostic.sourceFile } : {}),
|
|
348
|
+
...(metadataReadDiagnostic.sourceLine ? { sourceLine: metadataReadDiagnostic.sourceLine } : {}),
|
|
349
|
+
} : {}),
|
|
350
|
+
});
|
|
351
|
+
return { response: { ...blocked('root_admission_metadata_unavailable', 503),
|
|
352
|
+
recoveryAction: 'resend',
|
|
353
|
+
message: 'CTM saved your prompt but could not start delivery. Resend to try again.',
|
|
354
|
+
}, admission: null };
|
|
355
|
+
}
|
|
356
|
+
return { response, admission: null };
|
|
357
|
+
}
|
|
327
358
|
const handle = Object.freeze({});
|
|
328
359
|
const record = Object.freeze({
|
|
329
360
|
selector, metadata, intentHandle, target: target || null,
|