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
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
const { execFile, spawn } = require('child_process');
|
|
3
3
|
const path = require('path');
|
|
4
4
|
const { createSemaphore } = require('./lib/async-semaphore');
|
|
5
|
+
// WP-4: one normal form for a git remote, shared with wall-e and the hub (never re-implemented).
|
|
6
|
+
const { normalizeGitRemote } = require('../shared/git-remote-key');
|
|
5
7
|
|
|
6
8
|
const DEFAULT_GIT_TIMEOUT_MS = _positiveInt(process.env.CTM_GIT_TIMEOUT_MS, 15000);
|
|
7
9
|
const DEFAULT_WORKTREE_CREATE_TIMEOUT_MS = Math.max(
|
|
@@ -628,6 +630,46 @@ function _invalidateGitFactCache() {
|
|
|
628
630
|
async function resolveMainBranch(cwd) {
|
|
629
631
|
return _mainBranchCache(cwd || '', () => _resolveMainBranchUncached(cwd));
|
|
630
632
|
}
|
|
633
|
+
|
|
634
|
+
// WP-4 (§6.5 step 2, §9 S-9): the box-side `gitRemoteKey` for /api/projects rows.
|
|
635
|
+
//
|
|
636
|
+
// The SAME cached-promise pattern as resolveMainBranch above — async (never a sync spawn on the
|
|
637
|
+
// main thread), one `git remote get-url origin` per checkout per TTL, and a rejection drops the
|
|
638
|
+
// cache entry instead of pinning an empty key for the whole window. Everything about WHICH string
|
|
639
|
+
// is the key lives in shared/git-remote-key.js (one implementation, both ends); this is only the
|
|
640
|
+
// git read. A non-repo / no-origin / detached cwd resolves to '' — the caller emits the field
|
|
641
|
+
// with an empty value rather than omitting it, so a client can tell "asked and there is no remote"
|
|
642
|
+
// from "this instance is too old to answer".
|
|
643
|
+
const _GIT_REMOTE_KEY_TTL_MS = 30000; // fixed on purpose: no new env var (WP-4 hard rule)
|
|
644
|
+
const _remoteKeyCache = _ttlPromiseCache(_GIT_REMOTE_KEY_TTL_MS);
|
|
645
|
+
|
|
646
|
+
async function gitRemoteKey(cwd) {
|
|
647
|
+
const repoPath = String(cwd || '');
|
|
648
|
+
if (!repoPath) return '';
|
|
649
|
+
return _remoteKeyCache(repoPath, async () => {
|
|
650
|
+
const url = await _gitSafe(repoPath, ['remote', 'get-url', 'origin']);
|
|
651
|
+
return normalizeGitRemote(url);
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
/**
|
|
656
|
+
* Attach `gitRemoteKey` to every project row (in place, returning the same array).
|
|
657
|
+
*
|
|
658
|
+
* Bounded concurrency: a projects list is normally < 100 rows and each miss costs one `git`
|
|
659
|
+
* process, but a shared box can hold hundreds — the pool keeps the fan-out flat instead of
|
|
660
|
+
* spawning one git per row at once.
|
|
661
|
+
*/
|
|
662
|
+
async function attachGitRemoteKeys(projects) {
|
|
663
|
+
const rows = Array.isArray(projects) ? projects : [];
|
|
664
|
+
const concurrency = 8;
|
|
665
|
+
for (let i = 0; i < rows.length; i += concurrency) {
|
|
666
|
+
await Promise.all(rows.slice(i, i + concurrency).map(async (row) => {
|
|
667
|
+
if (!row || typeof row !== 'object') return;
|
|
668
|
+
row.gitRemoteKey = await gitRemoteKey(row.path);
|
|
669
|
+
}));
|
|
670
|
+
}
|
|
671
|
+
return rows;
|
|
672
|
+
}
|
|
631
673
|
async function _resolveMainBranchUncached(cwd) {
|
|
632
674
|
const originHead = await _gitSafe(cwd, ['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD']);
|
|
633
675
|
if (originHead) {
|
|
@@ -2303,4 +2345,6 @@ module.exports = {
|
|
|
2303
2345
|
_invalidateGitFactCache,
|
|
2304
2346
|
_gitProbeEnv,
|
|
2305
2347
|
_mapWorktreesBounded, _getGitProbeMetrics, _resetGitProbeMetrics,
|
|
2348
|
+
// WP-4: gitRemoteKey on /api/projects rows (S-9) — the key itself is normalizeGitRemote.
|
|
2349
|
+
gitRemoteKey, attachGitRemoteKeys, normalizeGitRemote,
|
|
2306
2350
|
};
|
|
@@ -60,6 +60,10 @@ function _declareHostDead(manager, hostId, attemptNumber) {
|
|
|
60
60
|
adapter._emitExitOnce({ exitCode: 1, signal: null, hostClosed: true, reason: 'adopted-redial-exhausted' });
|
|
61
61
|
}
|
|
62
62
|
manager._removeHostRecordAsync(hostId);
|
|
63
|
+
// D-8 (stale-pty-host-evacuation design): a dead host leaves no retry behind --
|
|
64
|
+
// without its compat entry handoffStale() returns null and never re-arms a
|
|
65
|
+
// stale retry for a process that is already gone.
|
|
66
|
+
manager.hostCompatibility?.delete(hostId);
|
|
63
67
|
// I6 (host-readiness design): announce the death so the primary's readiness
|
|
64
68
|
// ledger forgets this hostId -- the ONE terminal path for an adopted host.
|
|
65
69
|
manager.emit?.('host-exit', { hostId, kind: 'adopted' });
|
|
@@ -188,11 +188,25 @@ const INHERITED_AGENT_ENV_KEYS = [
|
|
|
188
188
|
// lead, it never drives CTM; interactive sessions (explicitWorkerAgent undefined) keep all four.
|
|
189
189
|
const WORKER_STRIPPED_TOKEN_KEYS = Object.freeze(['WALLE_CTM_TOKEN', 'WALL_E_API_TOKEN', 'CTM_TOKEN', 'CTM_API_TOKEN']);
|
|
190
190
|
|
|
191
|
+
// D-7 (session-stops-followups-2026-10-24.html §1.3, §5 D-7, §8 WP-4): the same worker branch also
|
|
192
|
+
// must not hold a CTM API ADDRESS. On 2026-10-24 a delegated worker's script loaded Wall-E's CTM
|
|
193
|
+
// client with no base URL; resolveApiBaseUrl (wall-e/memory/ctm-context-client.js:12-17) fell back
|
|
194
|
+
// to the inherited CTM_PORT=3456 and reached PRIMARY. These three names are every alias that
|
|
195
|
+
// resolver reads, so a worker keeps none of them and carries the resolver's existing
|
|
196
|
+
// `WALLE_CTM_API='0'` switch instead: with no explicit URL it resolves '' and calls throw
|
|
197
|
+
// ctm_api_unavailable. buildTelemetryEnv still sets CTM_PORT (the shared session env; interactive
|
|
198
|
+
// hooks read it, a default worker runs none); the address is what must not travel. Least privilege
|
|
199
|
+
// by inheritance (P-5): the orchestrator's addressability is not a worker capability. Interactive
|
|
200
|
+
// sessions keep all of it.
|
|
201
|
+
const WORKER_STRIPPED_CTM_API_KEYS = Object.freeze(['CTM_SESSION_MEMORY_API_BASE_URL', 'CTM_API_BASE_URL', 'CTM_BASE_URL']);
|
|
202
|
+
|
|
191
203
|
function sanitizeInheritedAgentEnv(env, explicitWorkerAgent) {
|
|
192
204
|
if (!env || typeof env !== 'object') return env;
|
|
193
205
|
for (const key of INHERITED_AGENT_ENV_KEYS) delete env[key];
|
|
194
206
|
if (explicitWorkerAgent === 'claude' || explicitWorkerAgent === 'codex') {
|
|
195
207
|
for (const key of WORKER_STRIPPED_TOKEN_KEYS) delete env[key];
|
|
208
|
+
for (const key of WORKER_STRIPPED_CTM_API_KEYS) delete env[key];
|
|
209
|
+
env.WALLE_CTM_API = '0';
|
|
196
210
|
env.CTM_WORKER_AGENT = explicitWorkerAgent;
|
|
197
211
|
}
|
|
198
212
|
return env;
|
|
@@ -238,4 +252,5 @@ module.exports = {
|
|
|
238
252
|
buildTelemetryEnv,
|
|
239
253
|
sanitizeInheritedAgentEnv,
|
|
240
254
|
WORKER_STRIPPED_TOKEN_KEYS,
|
|
255
|
+
WORKER_STRIPPED_CTM_API_KEYS,
|
|
241
256
|
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// WP-2 (design doc session-name-agent-title-precedence §5 D-4/D-5/D-9, §6): the inbound applier
|
|
4
|
+
// for a name the CODING AGENT wrote into its own transcript — Claude's `ai-title` / `custom-title`
|
|
5
|
+
// records, captured during ingest (lib/transcript-store.js claudeMessagesFromEntry).
|
|
6
|
+
//
|
|
7
|
+
// Pure SQL on the caller's better-sqlite3 handle. This module is loaded on the ingest path
|
|
8
|
+
// (transcript-store → the db-owner worker, the read pool and the main thread), so it must never
|
|
9
|
+
// require lib/db.js — the domain router owns the handles and passes one in.
|
|
10
|
+
//
|
|
11
|
+
// Contract:
|
|
12
|
+
// * Tier 1 (`user_renamed=1`) is never rewritten. A drifted agent name only asks for ONE
|
|
13
|
+
// re-assert outward (`reassert: true`; the caller runs mirrorSessionTitle({force:true})).
|
|
14
|
+
// `ai-title` alone is never drift — Claude keeps writing ai-titles under a custom title (§1e).
|
|
15
|
+
// * `custom-title` equal to `title_mirror_value` is CTM's own echo of what it pushed into the
|
|
16
|
+
// agent, not the agent's name (D-5), so the `ai-title` wins instead.
|
|
17
|
+
// * A tier-2 write touches ctm_sessions only. The caller (server.js _runTranscriptDeltaPipeline)
|
|
18
|
+
// moves `startup_tasks.label` through db.updateStartupTaskLabel, which also enqueues the
|
|
19
|
+
// multi-machine sync outbox row this db.js-free module cannot reach (D-13).
|
|
20
|
+
|
|
21
|
+
const { normalizeSessionTitle } = require('./session-title-signals');
|
|
22
|
+
const { isUuidLike } = require('./session-title-mirror');
|
|
23
|
+
|
|
24
|
+
// D-9: these two states mean "a push was attempted and did not land" — a row already sitting at
|
|
25
|
+
// that outcome for the CURRENT title is not re-driven from inbound (exit and relink keep retrying).
|
|
26
|
+
const MIRROR_RETRY_STATES = new Set(['failed', 'unsupported']);
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {import('better-sqlite3').Database} coreDb handle carrying ctm_sessions + startup_tasks
|
|
30
|
+
* (the caller's CORE domain handle — `options.coreDb || db` at the ingest choke point).
|
|
31
|
+
* @param {{ ctmSessionId: string, provider?: string, customTitle?: string, aiTitle?: string }} o
|
|
32
|
+
* `provider` is accepted for call-site symmetry; the provider gate (titleSpec.readsAiTitle) is
|
|
33
|
+
* the caller's, because the reason string and the SQL are provider-independent.
|
|
34
|
+
* @returns {{ applied: 'agent'|'none', reason: string, title?: string, reassert: boolean }}
|
|
35
|
+
*/
|
|
36
|
+
function applyAgentTitleSignals(coreDb, { ctmSessionId, customTitle = '', aiTitle = '' } = {}) {
|
|
37
|
+
const id = String(ctmSessionId || '').trim();
|
|
38
|
+
if (!id) return { applied: 'none', reason: 'no_session', reassert: false };
|
|
39
|
+
const row = coreDb.prepare(
|
|
40
|
+
'SELECT title, user_renamed, title_mirror_state, title_mirror_value FROM ctm_sessions WHERE id = ?',
|
|
41
|
+
).get(id);
|
|
42
|
+
if (!row) return { applied: 'none', reason: 'no_row', reassert: false };
|
|
43
|
+
|
|
44
|
+
const title = normalizeSessionTitle(row.title);
|
|
45
|
+
const custom = normalizeSessionTitle(customTitle);
|
|
46
|
+
const ai = normalizeSessionTitle(aiTitle);
|
|
47
|
+
const mirrorValue = normalizeSessionTitle(row.title_mirror_value);
|
|
48
|
+
const mirrorState = String(row.title_mirror_state || '');
|
|
49
|
+
|
|
50
|
+
if (row.user_renamed) {
|
|
51
|
+
// C-5/D-9: a tier-1 row nobody has ever tried to push gets exactly one attempt on its first
|
|
52
|
+
// observation. Checked before the drift vocabulary below — nothing was pushed yet, so the
|
|
53
|
+
// agent cannot have drifted FROM anything, and naming the real condition is what a diagnosis
|
|
54
|
+
// should report. Either way the row is never rewritten.
|
|
55
|
+
if (mirrorState === '') return { applied: 'none', reason: 'tier1_unmirrored', reassert: true };
|
|
56
|
+
if (custom && custom !== title) {
|
|
57
|
+
if (MIRROR_RETRY_STATES.has(mirrorState) && mirrorValue === title) {
|
|
58
|
+
return { applied: 'none', reason: 'tier1_drift', reassert: false };
|
|
59
|
+
}
|
|
60
|
+
// Drift is recorded either way: a later batch carrying the CTM name back converges
|
|
61
|
+
// (`mirrorSessionTitle` persists state='mirrored') and the next observation is quiet.
|
|
62
|
+
coreDb.prepare('UPDATE ctm_sessions SET title_mirror_state = ? WHERE id = ?').run('drifted', id);
|
|
63
|
+
return { applied: 'none', reason: 'tier1_drift', reassert: true };
|
|
64
|
+
}
|
|
65
|
+
return { applied: 'none', reason: 'tier1', reassert: false };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// D-5: the agent's name wins, unless the custom-title is CTM's own echo of title_mirror_value.
|
|
69
|
+
const name = (custom && custom !== mirrorValue) ? custom : ai;
|
|
70
|
+
if (!name) return { applied: 'none', reason: 'unchanged', reassert: false };
|
|
71
|
+
// A provider name that is just the provider's opaque session id names nothing.
|
|
72
|
+
if (isUuidLike(name)) return { applied: 'none', reason: 'uuid_like', reassert: false };
|
|
73
|
+
if (name === title) return { applied: 'none', reason: 'unchanged', reassert: false };
|
|
74
|
+
|
|
75
|
+
const reason = name === custom ? 'provider_custom_title' : 'provider_ai_title';
|
|
76
|
+
const updatedAt = new Date().toISOString();
|
|
77
|
+
// The tier guard lives in the WHERE, so a concurrent rename is never overwritten.
|
|
78
|
+
const changes = coreDb.prepare(`
|
|
79
|
+
UPDATE ctm_sessions
|
|
80
|
+
SET title = ?, title_source = 'provider_title', title_status = 'imported',
|
|
81
|
+
title_status_reason = ?, title_updated_at = ?, updated_at = datetime('now')
|
|
82
|
+
WHERE id = ? AND COALESCE(user_renamed, 0) = 0
|
|
83
|
+
`).run(name, reason, updatedAt, id).changes;
|
|
84
|
+
if (!changes) return { applied: 'none', reason: 'tier1', reassert: false };
|
|
85
|
+
return { applied: 'agent', title: name, reason, reassert: false };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { applyAgentTitleSignals };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// WP-6 (design doc session-name-agent-title-precedence §§1e/6/8): the READ half of the one-shot
|
|
4
|
+
// agent-title backfill — one bounded page of Claude candidates CTM has never been given an agent
|
|
5
|
+
// name for, plus each row's transcript TAIL (last <=64 KiB, one positional range; never a
|
|
6
|
+
// whole-file read). The apply is the db-owner's (db.js applyAgentTitleBackfillRows ->
|
|
7
|
+
// lib/agent-title-apply.js), so this stays a pure read on the caller's read-only WAL handle.
|
|
8
|
+
// Called by the read-pool worker op 'backfillAgentTitles'; the caller (server/boot.js) owns the
|
|
9
|
+
// page loop and the once-per-install marker.
|
|
10
|
+
|
|
11
|
+
const transcriptSource = require('./transcript-source');
|
|
12
|
+
|
|
13
|
+
function readAgentTitleBackfillPage(conn, payload = {}) {
|
|
14
|
+
const { extractTitleSignalFromRecord } = require('./session-title-signals');
|
|
15
|
+
// §1e measured the EOF→last title-record distance at p50 3,293 B, p90 19,166 B, max 36,778 B
|
|
16
|
+
// over n=43; 64 KiB is that bound with headroom and is the hard per-row ceiling.
|
|
17
|
+
const MAX_ROWS = 20;
|
|
18
|
+
const TAIL_BYTES = 64 * 1024;
|
|
19
|
+
const explicitIds = (Array.isArray(payload.ids) ? payload.ids : [])
|
|
20
|
+
.map((id) => String(id || '').trim()).filter(Boolean).slice(0, MAX_ROWS);
|
|
21
|
+
const limit = Math.max(1, Math.min(MAX_ROWS, Number(payload.limit) || MAX_ROWS));
|
|
22
|
+
const tailBytes = Math.max(1024, Math.min(TAIL_BYTES, Number(payload.tailBytes) || TAIL_BYTES));
|
|
23
|
+
// An explicit id list (bin/repair-resume-echo-titles.js) has already re-stated the row's
|
|
24
|
+
// tier itself, so those pages drop the tier filters; the default page keeps them.
|
|
25
|
+
const tierFilter = explicitIds.length ? '' : `
|
|
26
|
+
AND COALESCE(c.user_renamed, 0) = 0
|
|
27
|
+
AND COALESCE(c.title_source, '') <> 'provider_title'`;
|
|
28
|
+
const idFilter = explicitIds.length ? ` AND c.id IN (${explicitIds.map(() => '?').join(',')})` : '';
|
|
29
|
+
// Run-scoped walk cursor: the caller accumulates the ids this page ALREADY read and hands
|
|
30
|
+
// them back, so a row whose transcript turns out to carry no title record cannot make the
|
|
31
|
+
// walk loop forever (rows that DO get a title also leave the candidate set on their own).
|
|
32
|
+
const skipIds = (Array.isArray(payload.skipIds) ? payload.skipIds : [])
|
|
33
|
+
.map((id) => String(id || '').trim()).filter(Boolean).slice(0, 4000);
|
|
34
|
+
const skipFilter = skipIds.length ? ` AND c.id NOT IN (${skipIds.map(() => '?').join(',')})` : '';
|
|
35
|
+
// "The row's transcript" is the LATEST agent_sessions row, the same rule the display path
|
|
36
|
+
// uses (db.js getSessionDisplayTitleInfo's ROW_NUMBER() OVER … rn = 1), not MAX(jsonl_path):
|
|
37
|
+
// a stitched compaction continuation adds a second row, and the freshest file is the one
|
|
38
|
+
// Claude is still appending ai-titles to.
|
|
39
|
+
const candidates = conn.prepare(`
|
|
40
|
+
SELECT c.id AS id, a.jsonl_path AS jsonl_path
|
|
41
|
+
FROM ctm_sessions c
|
|
42
|
+
JOIN (
|
|
43
|
+
SELECT ctm_session_id, jsonl_path, modified_at, updated_at, created_at,
|
|
44
|
+
ROW_NUMBER() OVER (
|
|
45
|
+
PARTITION BY ctm_session_id
|
|
46
|
+
ORDER BY COALESCE(NULLIF(modified_at, ''), updated_at, created_at) DESC,
|
|
47
|
+
updated_at DESC, created_at DESC
|
|
48
|
+
) AS rn
|
|
49
|
+
FROM agent_sessions
|
|
50
|
+
) a ON a.ctm_session_id = c.id AND a.rn = 1
|
|
51
|
+
WHERE lower(COALESCE(c.provider, '')) = 'claude'
|
|
52
|
+
AND COALESCE(a.jsonl_path, '') <> ''${tierFilter}${idFilter}${skipFilter}
|
|
53
|
+
ORDER BY COALESCE(NULLIF(a.modified_at, ''), a.updated_at, a.created_at, '') DESC
|
|
54
|
+
LIMIT ?
|
|
55
|
+
`).all(...explicitIds, ...skipIds, limit);
|
|
56
|
+
const rows = [];
|
|
57
|
+
const scannedIds = [];
|
|
58
|
+
for (const candidate of candidates) {
|
|
59
|
+
const id = String(candidate.id || '');
|
|
60
|
+
scannedIds.push(id);
|
|
61
|
+
const jsonlPath = String(candidate.jsonl_path || '');
|
|
62
|
+
let view = null;
|
|
63
|
+
try { view = transcriptSource.statTranscriptSync(jsonlPath, { db: conn }); } catch { view = null; /* expected: a transcript rotated away between the row read and the stat — skip the row, never fatal */ }
|
|
64
|
+
if (!view || view.state === 'missing' || !(Number(view.size) > 0)) continue;
|
|
65
|
+
const size = Number(view.size);
|
|
66
|
+
const from = Math.max(0, size - tailBytes);
|
|
67
|
+
let buf = null;
|
|
68
|
+
try { buf = transcriptSource.readTranscriptRangeSync(jsonlPath, from, size, { db: conn }); } catch { continue; /* expected: an unreadable/rotating transcript — skip the row */ }
|
|
69
|
+
// Both records are kept independently (D-4); which one wins is the applier's D-5 decision,
|
|
70
|
+
// because Claude keeps re-appending ai-titles under a custom title (§1e).
|
|
71
|
+
const lines = buf.toString('utf8').split('\n');
|
|
72
|
+
if (from > 0) lines.shift(); // the window started mid-record
|
|
73
|
+
let customTitle = '';
|
|
74
|
+
let aiTitle = '';
|
|
75
|
+
for (const line of lines) {
|
|
76
|
+
const text = line.trim();
|
|
77
|
+
if (!text) continue;
|
|
78
|
+
let entry = null;
|
|
79
|
+
try { entry = JSON.parse(text); } catch { continue; /* expected: a window-edge partial line or a non-record line */ }
|
|
80
|
+
const signal = extractTitleSignalFromRecord(entry);
|
|
81
|
+
if (signal.userTitle) customTitle = signal.userTitle;
|
|
82
|
+
else if (signal.providerTitle) aiTitle = signal.providerTitle;
|
|
83
|
+
}
|
|
84
|
+
if (!customTitle && !aiTitle) continue;
|
|
85
|
+
// `jsonlPath` is the file this page actually read — the repair script prints it instead of
|
|
86
|
+
// re-deriving "the row's transcript" and risking a different answer.
|
|
87
|
+
rows.push({ id, jsonlPath, customTitle, aiTitle, tailBytes: size - from });
|
|
88
|
+
}
|
|
89
|
+
return { rows, scanned: candidates.length, scannedIds, limit };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
module.exports = { readAgentTitleBackfillPage };
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// WP-1 (machine-pool-picker-pairing-dashboard-2026-10-24.html §6.1, D-11/D-13): the host-stats
|
|
4
|
+
// read surface. Every CTM samples its own host on the existing 60 s resource tick (no new timer —
|
|
5
|
+
// the record call rides the tick server.js already runs) and keeps 24 h of one-per-minute rows;
|
|
6
|
+
// this route serves them to its own picker and, through the ?machine= proxy, to a hub.
|
|
7
|
+
//
|
|
8
|
+
// GET /api/host/stats?latest=1 -> { ok:true, instanceId, latest: HostSample|null }
|
|
9
|
+
// GET /api/host/stats?since=<ms> -> { ok:true, instanceId, latest, samples: HostSample[] }
|
|
10
|
+
// `since` is clamped to now-24h (the retention window), so
|
|
11
|
+
// `?since=0` means "everything retained", never "everything
|
|
12
|
+
// ever" — a caller cannot ask for a row the purge dropped.
|
|
13
|
+
//
|
|
14
|
+
// Like lib/api/machines.js (and unlike the lib/api/*.js extraction slices) this is NEW surface
|
|
15
|
+
// added by WP-1: the handler body lives here, so there is no legacy handleApi branch to delete and
|
|
16
|
+
// tests-integration/api-router-parity.test.js's no-dual-dispatch rule is satisfied by construction.
|
|
17
|
+
// Auth is NOT evaluated here: lib/auth-rules.js carries the row — read, NOT loopbackOnly (a phone
|
|
18
|
+
// picker reads another machine through the hub), peerControl:true (D-13) — and a route with no rule
|
|
19
|
+
// is 403 auth_rule_missing by design, so the rule row and this registration must land together.
|
|
20
|
+
const { registerApiRoutes } = require('../api-router');
|
|
21
|
+
const remoteMachineProxy = require('../remote-machine-proxy');
|
|
22
|
+
|
|
23
|
+
// Mirrors lib/host-stats.js's DEFAULT_RETENTION_MS. It is the clamp floor here, so a caller asking
|
|
24
|
+
// for a wider window than the sampler keeps is answered with the rows that actually exist.
|
|
25
|
+
const RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
26
|
+
const ROUTE = 'GET /api/host/stats';
|
|
27
|
+
|
|
28
|
+
let _deps = null;
|
|
29
|
+
|
|
30
|
+
function configure({
|
|
31
|
+
hostStats,
|
|
32
|
+
sendJsonResponse,
|
|
33
|
+
} = {}) {
|
|
34
|
+
_deps = { hostStats, sendJsonResponse };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function _send(res, status, payload) {
|
|
38
|
+
if (_deps && typeof _deps.sendJsonResponse === 'function') return _deps.sendJsonResponse(res, status, payload);
|
|
39
|
+
const body = JSON.stringify(payload || {});
|
|
40
|
+
res.writeHead(status, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) });
|
|
41
|
+
res.end(body);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// This machine's own minted id — the instanceId a hub keys the response by. Read through the
|
|
45
|
+
// accessor (never re-derived here), and '' rather than a throw before boot has primed identity: a
|
|
46
|
+
// 200 with an empty id is a truthful "sampler is up, identity is not yet" answer.
|
|
47
|
+
function _instanceId() {
|
|
48
|
+
try {
|
|
49
|
+
const access = require('../instance-identity-access');
|
|
50
|
+
const identity = access.getInstanceIdentity({ required: false });
|
|
51
|
+
return (identity && identity.instanceId) || '';
|
|
52
|
+
} catch {
|
|
53
|
+
return ''; // expected: instance identity is not ready yet (early boot / pre-initDb)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Self-contained on purpose: no countTasks/lastCreateAt here. Those two are injected into
|
|
58
|
+
// lib/host-stats.js by server.js (it owns the live session map and the status bindings), so this
|
|
59
|
+
// handler only reads what the sampler already wrote.
|
|
60
|
+
async function handleHostStats(req, res, url) {
|
|
61
|
+
const stats = _deps && _deps.hostStats;
|
|
62
|
+
// ORDER MATTERS: the history read is the slow leg (a DB-owner round trip, ~1 ms alone but seconds
|
|
63
|
+
// under load), and `latest` is the in-memory sample. Reading `latest` FIRST lets a tick land
|
|
64
|
+
// mid-read, so the answer carries a row NEWER than the `latest` object beside it — which is exactly
|
|
65
|
+
// how this file's own assertion `latest === samples[last]` failed once on a saturated host.
|
|
66
|
+
// Reading history first makes `latest.at >= samples[last].at` hold for the whole answer.
|
|
67
|
+
const payload = { ok: true, instanceId: _instanceId(), latest: null };
|
|
68
|
+
|
|
69
|
+
const sinceRaw = url && url.searchParams ? url.searchParams.get('since') : null;
|
|
70
|
+
if (sinceRaw != null && sinceRaw !== '') {
|
|
71
|
+
const now = Date.now();
|
|
72
|
+
const floor = now - RETENTION_MS;
|
|
73
|
+
const requested = Number(sinceRaw);
|
|
74
|
+
const since = Math.min(Math.max(Number.isFinite(requested) ? Math.trunc(requested) : floor, floor), now);
|
|
75
|
+
payload.samples = stats && typeof stats.history === 'function' ? await stats.history({ sinceMs: since }) : [];
|
|
76
|
+
}
|
|
77
|
+
payload.latest = stats && typeof stats.latest === 'function' ? (stats.latest() || null) : null;
|
|
78
|
+
return _send(res, 200, payload);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Remote machine hub (§6.1 / S-5): the same wrapper lib/api/misc-ops.js:41 uses. A request naming
|
|
82
|
+
// another machine (?machine= or X-CTM-Machine) is forwarded to that box with the peer bearer and
|
|
83
|
+
// this handler never runs; every other request is answered locally. `url` is passed through so the
|
|
84
|
+
// proxied box sees the caller's own `since`.
|
|
85
|
+
function _machineRouted(route, local) {
|
|
86
|
+
return (req, res, url) => (remoteMachineProxy.isMachineRouted(req, url)
|
|
87
|
+
? remoteMachineProxy.maybeProxyMachineRequest(req, res, url, { route })
|
|
88
|
+
.then((handled) => (handled ? undefined : local(req, res, url)))
|
|
89
|
+
: local(req, res, url));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function register() {
|
|
93
|
+
registerApiRoutes('host-stats', [
|
|
94
|
+
{ method: 'GET', path: '/api/host/stats', handler: _machineRouted(ROUTE, (req, res, url) => handleHostStats(req, res, url)) },
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { configure, register, handleHostStats };
|
|
@@ -69,27 +69,50 @@ function collectHook(req, res, onPayload) {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
// A lead hears its delegated worker stopped on its next PostToolUse / UserPromptSubmit hook
|
|
72
|
+
// A lead hears its delegated worker stopped on its next PostToolUse / UserPromptSubmit hook, and --
|
|
73
|
+
// RC-1 (D-1..D-4/D-6) -- on its Stop hook, which is answered synchronously with the drained notices
|
|
74
|
+
// plus a nudge when the lead still owns a live worker and nothing pending will wake it.
|
|
73
75
|
// A notice counts as delivered only once the response carrying it finished (was handed to the
|
|
74
76
|
// socket); a response that closes or errors first re-queues it in memory for the next hook.
|
|
75
|
-
function workerNoticeBody(route, payload, res) {
|
|
77
|
+
function workerNoticeBody(route, payload, res, verdict) {
|
|
76
78
|
const isPrompt = route === 'start' && payload.hook_event_name === 'UserPromptSubmit';
|
|
79
|
+
const isToolUse = route === 'PostToolUse';
|
|
80
|
+
const isStop = route === 'stop';
|
|
77
81
|
const leadId = typeof payload.ctm_id === 'string' ? payload.ctm_id : '';
|
|
78
|
-
if ((
|
|
82
|
+
if ((!isToolUse && !isPrompt && !isStop) || !leadId) return undefined;
|
|
83
|
+
// D-4: the loop guard counts TOOL CALLS, so every PostToolUse advances the epoch even when the
|
|
84
|
+
// lead has no notice waiting.
|
|
85
|
+
if (isToolUse) workerNotices.noteLeadToolUse(leadId);
|
|
86
|
+
// D-6: a Stop that arrived after a newer UserPromptSubmit is stale -- no notices and no nudge.
|
|
87
|
+
if (isStop && verdict && verdict.stale) return undefined;
|
|
79
88
|
const drained = workerNotices.drainNotices(leadId, { now: Date.now(), dbModule: _deps.dbModule || null, delegateWorker: _deps.delegateWorker || null });
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
89
|
+
const nudge = isStop
|
|
90
|
+
? workerNotices.stopNudge(leadId, { backgroundTasks: payload.background_tasks, sessionCrons: payload.session_crons })
|
|
91
|
+
: null;
|
|
92
|
+
if (!drained.notices.length && !nudge) return undefined;
|
|
93
|
+
// §7: one session_diagnostics row per nudge, so the RC-1 census can count leads that were held.
|
|
94
|
+
if (nudge && typeof _deps.recordSessionDiagnostic === 'function') {
|
|
95
|
+
try { _deps.recordSessionDiagnostic(leadId, 'lead-stop-nudge', { workers: nudge.workers, background_running: false }); }
|
|
96
|
+
catch (err) { console.error('[worker-notices] lead-stop-nudge diagnostic failed:', err && err.message); }
|
|
97
|
+
}
|
|
98
|
+
if (drained.notices.length) {
|
|
99
|
+
if (res && typeof res.once === 'function') {
|
|
100
|
+
res.once('finish', () => drained.commit());
|
|
101
|
+
res.once('close', () => drained.abandon()); // after 'finish' this is a no-op
|
|
102
|
+
res.once('error', () => drained.abandon());
|
|
103
|
+
} else drained.commit();
|
|
104
|
+
}
|
|
105
|
+
const notices = drained.notices.map((n) => ({ receipt_id: n.receipt_id, text: n.text }));
|
|
106
|
+
// A nudge is not a receipt-backed notice, so it carries no receipt_id.
|
|
107
|
+
if (nudge) notices.push({ receipt_id: null, text: nudge.text });
|
|
108
|
+
return { notices };
|
|
87
109
|
}
|
|
88
110
|
|
|
89
111
|
function handleClaudeHook(req, res, url) {
|
|
90
112
|
const route = url.pathname.slice('/hook/claude/'.length);
|
|
91
113
|
collectHook(req, res, (payload) => {
|
|
92
|
-
|
|
114
|
+
// D-1/D-6: the Stop verdict says whether this Stop is stale; the notice body needs it.
|
|
115
|
+
const verdict = _deps.telemetryReceiver.hookClaude(route, payload);
|
|
93
116
|
// WP-1 (native-subagent-sidebar-rows-2026-10-12 §5 D-4, §9 S-8): feed the lead's native
|
|
94
117
|
// subagent lifecycle from the same hook stream. Best-effort -- a runtime failure never fails
|
|
95
118
|
// the hook (the telemetry receiver above already owns the status/background writes).
|
|
@@ -100,7 +123,7 @@ function handleClaudeHook(req, res, url) {
|
|
|
100
123
|
// go through the separate activity adapter and never rewrite resume cwd.
|
|
101
124
|
if (!['PostToolUse', 'PostToolUseFailure', 'CwdChanged'].includes(route)) _deps.observeHookCwd(payload, `claude.${route}`);
|
|
102
125
|
if (typeof _deps.observeWorkspaceActivity === 'function') _deps.observeWorkspaceActivity('claude', route, payload);
|
|
103
|
-
try { return workerNoticeBody(route, payload, res); } catch { return undefined; /* expected: a notice failure never fails the hook */ }
|
|
126
|
+
try { return workerNoticeBody(route, payload, res, verdict); } catch { return undefined; /* expected: a notice failure never fails the hook */ }
|
|
104
127
|
});
|
|
105
128
|
}
|
|
106
129
|
|