create-walle 0.9.21 → 0.9.23
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 +27 -5
- package/package.json +2 -2
- package/template/CLAUDE.md +2 -2
- package/template/LICENSE +1 -1
- package/template/bin/ctm-dev-cleanup.js +24 -3
- package/template/bin/ctm-launch.sh +13 -0
- package/template/bin/dev.sh +156 -18
- package/template/bin/node-bin.sh +84 -0
- package/template/bin/pin-node.sh +51 -0
- package/template/claude-task-manager/api-prompts.js +1203 -182
- package/template/claude-task-manager/api-reviews.js +109 -15
- package/template/claude-task-manager/approval-agent.js +1360 -280
- package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
- package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
- package/template/claude-task-manager/db.js +4417 -295
- package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
- package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
- package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
- package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
- package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
- package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
- package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
- package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
- package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
- package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
- package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
- package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
- package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
- package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
- package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
- package/template/claude-task-manager/docs/phone-access-design.md +53 -15
- package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
- package/template/claude-task-manager/docs/phone-setup.md +3 -0
- package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
- package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
- package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
- package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
- package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
- package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
- package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
- package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
- package/template/claude-task-manager/docs/session-title-authority.md +32 -0
- package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
- package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
- package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
- package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
- package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
- package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
- package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
- package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
- package/template/claude-task-manager/git-utils.js +897 -27
- package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
- package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
- package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
- package/template/claude-task-manager/lib/agent-presets.js +17 -1
- package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
- package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
- package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
- package/template/claude-task-manager/lib/async-semaphore.js +44 -0
- package/template/claude-task-manager/lib/auth-context.js +5 -0
- package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
- package/template/claude-task-manager/lib/auth-rules.js +29 -2
- package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
- package/template/claude-task-manager/lib/background-llm.js +144 -17
- package/template/claude-task-manager/lib/branch-inventory.js +212 -0
- package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
- package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
- package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
- package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
- package/template/claude-task-manager/lib/codex-zst.js +124 -0
- package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
- package/template/claude-task-manager/lib/connection-health.js +232 -0
- package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
- package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
- package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
- package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
- package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
- package/template/claude-task-manager/lib/document-review.js +141 -6
- package/template/claude-task-manager/lib/escalation-review.js +152 -0
- package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
- package/template/claude-task-manager/lib/headless-term-service.js +678 -0
- package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
- package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
- package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
- package/template/claude-task-manager/lib/main-db-census.js +216 -0
- package/template/claude-task-manager/lib/message-pagination.js +106 -4
- package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
- package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
- package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
- package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
- package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
- package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
- package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
- package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
- package/template/claude-task-manager/lib/perf-tracker.js +242 -6
- package/template/claude-task-manager/lib/permission-match.js +76 -0
- package/template/claude-task-manager/lib/permission-sync.js +133 -20
- package/template/claude-task-manager/lib/process-title.js +35 -0
- package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
- package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
- package/template/claude-task-manager/lib/prompt-intent.js +132 -0
- package/template/claude-task-manager/lib/provider-user-context.js +34 -0
- package/template/claude-task-manager/lib/read-pool-client.js +313 -0
- package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
- package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
- package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
- package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
- package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
- package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
- package/template/claude-task-manager/lib/restart-guard.js +109 -0
- package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
- package/template/claude-task-manager/lib/restore-policy.js +13 -0
- package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
- package/template/claude-task-manager/lib/restore-runtime.js +68 -0
- package/template/claude-task-manager/lib/restore-storm.js +34 -0
- package/template/claude-task-manager/lib/resume-cwd.js +36 -0
- package/template/claude-task-manager/lib/resume-preflight.js +313 -0
- package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
- package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
- package/template/claude-task-manager/lib/scheduler.js +21 -1
- package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
- package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
- package/template/claude-task-manager/lib/server-listeners.js +239 -0
- package/template/claude-task-manager/lib/session-capture.js +42 -7
- package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
- package/template/claude-task-manager/lib/session-history.js +388 -43
- package/template/claude-task-manager/lib/session-host-manager.js +287 -0
- package/template/claude-task-manager/lib/session-image-refs.js +209 -0
- package/template/claude-task-manager/lib/session-jobs.js +399 -59
- package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
- package/template/claude-task-manager/lib/session-restore.js +53 -0
- package/template/claude-task-manager/lib/session-standup.js +123 -23
- package/template/claude-task-manager/lib/session-state-bus.js +14 -0
- package/template/claude-task-manager/lib/session-stream.js +64 -16
- package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
- package/template/claude-task-manager/lib/session-token-usage.js +494 -0
- package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
- package/template/claude-task-manager/lib/setup-network-config.js +9 -0
- package/template/claude-task-manager/lib/size-cap.js +45 -0
- package/template/claude-task-manager/lib/size-cap.test.js +62 -0
- package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
- package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
- package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
- package/template/claude-task-manager/lib/standup-attention.js +7 -3
- package/template/claude-task-manager/lib/status-authority.js +39 -0
- package/template/claude-task-manager/lib/status-hooks.js +4 -0
- package/template/claude-task-manager/lib/storage-migration.js +235 -0
- package/template/claude-task-manager/lib/structured-capture.js +298 -0
- package/template/claude-task-manager/lib/sync-io-census.js +163 -0
- package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
- package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
- package/template/claude-task-manager/lib/terminal-choice.js +364 -0
- package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
- package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
- package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
- package/template/claude-task-manager/lib/timeline-order.js +122 -0
- package/template/claude-task-manager/lib/transcript-store.js +348 -43
- package/template/claude-task-manager/lib/transport-security.js +84 -1
- package/template/claude-task-manager/lib/wait-state.js +184 -0
- package/template/claude-task-manager/lib/walle-client.js +47 -5
- package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
- package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
- package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
- package/template/claude-task-manager/lib/walle-native-health.js +403 -0
- package/template/claude-task-manager/lib/walle-repair.js +701 -0
- package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
- package/template/claude-task-manager/lib/walle-session-context.js +57 -21
- package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
- package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
- package/template/claude-task-manager/lib/walle-transcript.js +52 -0
- package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
- package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
- package/template/claude-task-manager/package.json +1 -1
- package/template/claude-task-manager/prompt-harvest.js +89 -66
- package/template/claude-task-manager/providers/claude-code.js +51 -3
- package/template/claude-task-manager/providers/cursor.js +140 -45
- package/template/claude-task-manager/public/css/reviews.css +551 -61
- package/template/claude-task-manager/public/css/setup.css +191 -0
- package/template/claude-task-manager/public/css/walle-session.css +865 -10
- package/template/claude-task-manager/public/css/walle.css +154 -0
- package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
- package/template/claude-task-manager/public/index.html +18516 -2058
- package/template/claude-task-manager/public/ipad.html +363 -0
- package/template/claude-task-manager/public/js/document-review-links.js +301 -0
- package/template/claude-task-manager/public/js/image-normalize.js +69 -36
- package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
- package/template/claude-task-manager/public/js/prompts.js +66 -29
- package/template/claude-task-manager/public/js/reviews.js +901 -133
- package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
- package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
- package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
- package/template/claude-task-manager/public/js/setup.js +1273 -176
- package/template/claude-task-manager/public/js/stream-view.js +691 -73
- package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
- package/template/claude-task-manager/public/js/walle-session.js +2455 -158
- package/template/claude-task-manager/public/js/walle.js +455 -28
- package/template/claude-task-manager/public/m/app.css +2909 -262
- package/template/claude-task-manager/public/m/app.js +6601 -398
- package/template/claude-task-manager/public/m/claim.html +224 -17
- package/template/claude-task-manager/public/m/index.html +117 -21
- package/template/claude-task-manager/public/m/sw.js +3 -1
- package/template/claude-task-manager/public/manifest.json +2 -2
- package/template/claude-task-manager/public/prompts.html +30 -14
- package/template/claude-task-manager/queue-engine.js +507 -28
- package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
- package/template/claude-task-manager/server.js +14341 -2197
- package/template/claude-task-manager/session-integrity.js +160 -18
- package/template/claude-task-manager/session-search-ranking.js +1 -0
- package/template/claude-task-manager/session-utils.js +25 -5
- package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
- package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
- package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
- package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
- package/template/claude-task-manager/workers/harvest-worker.js +9 -55
- package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
- package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
- package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
- package/template/claude-task-manager/workers/session-host-process.js +146 -0
- package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
- package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
- package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
- package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
- package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
- package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
- package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
- package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
- package/template/docs/design/markdown-review-pane.md +206 -0
- package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
- package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
- package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
- package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
- package/template/docs/private-memory-and-pii-policy.md +69 -0
- package/template/package.json +2 -1
- package/template/scripts/check-private-data.js +201 -0
- package/template/shared/sqlite-owner-guard.js +30 -0
- package/template/shared/sqlite-owner-write-queue.js +225 -0
- package/template/shared/sqlite-storage-policy.js +111 -0
- package/template/shared/sqlite-write-lock.js +428 -0
- package/template/wall-e/agent-runners/claude-code.js +5 -0
- package/template/wall-e/agent.js +166 -22
- package/template/wall-e/api-walle.js +524 -70
- package/template/wall-e/auth/provider-flows.js +11 -1
- package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
- package/template/wall-e/brain.js +1614 -141
- package/template/wall-e/chat/attachment-blocks.js +96 -0
- package/template/wall-e/chat/attachments.js +2 -1
- package/template/wall-e/chat/capability-resolver.js +7 -7
- package/template/wall-e/chat/context-messages.js +28 -0
- package/template/wall-e/chat/conversation-frame.js +630 -0
- package/template/wall-e/chat/provider-messages.js +125 -0
- package/template/wall-e/chat.js +1002 -233
- package/template/wall-e/coding/acceptance-contract.js +170 -0
- package/template/wall-e/coding/acp-adapter.js +1 -1
- package/template/wall-e/coding/agent-catalog.js +3 -0
- package/template/wall-e/coding/artifact-store.js +93 -0
- package/template/wall-e/coding/capability-router.js +120 -0
- package/template/wall-e/coding/coding-run-controller.js +423 -0
- package/template/wall-e/coding/compaction-service.js +157 -12
- package/template/wall-e/coding/frontend-verification.js +258 -0
- package/template/wall-e/coding/lifecycle-hooks.js +75 -0
- package/template/wall-e/coding/local-preview-contract.js +157 -0
- package/template/wall-e/coding/permission-service.js +57 -13
- package/template/wall-e/coding/prompt-bundle.js +19 -1
- package/template/wall-e/coding/prompt-section-registry.js +227 -0
- package/template/wall-e/coding/provider-compat.js +15 -0
- package/template/wall-e/coding/runtime-events.js +224 -0
- package/template/wall-e/coding/runtime-mode.js +3 -0
- package/template/wall-e/coding/side-git-snapshot.js +160 -4
- package/template/wall-e/coding/snapshot-service.js +143 -1
- package/template/wall-e/coding/stream-processor.js +388 -34
- package/template/wall-e/coding/task-tool.js +141 -4
- package/template/wall-e/coding/tool-execution-controller.js +365 -0
- package/template/wall-e/coding/tool-registry.js +43 -5
- package/template/wall-e/coding/user-hooks.js +217 -0
- package/template/wall-e/coding-orchestrator.js +1330 -221
- package/template/wall-e/coding-prompts.js +20 -4
- package/template/wall-e/context/context-builder.js +15 -2
- package/template/wall-e/decision/confidence.js +1 -1
- package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
- package/template/wall-e/docs/external-action-controller.md +26 -6
- package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
- package/template/wall-e/embeddings.js +591 -53
- package/template/wall-e/external-action-controller.js +12 -0
- package/template/wall-e/http/auth.js +1 -0
- package/template/wall-e/http/chat-api.js +46 -11
- package/template/wall-e/http/model-admin.js +836 -34
- package/template/wall-e/lib/boot-profile.js +88 -0
- package/template/wall-e/lib/event-loop-monitor.js +93 -0
- package/template/wall-e/lib/service-health.js +194 -0
- package/template/wall-e/llm/anthropic.js +130 -5
- package/template/wall-e/llm/client.js +266 -63
- package/template/wall-e/llm/default-fallback.js +382 -0
- package/template/wall-e/llm/health.js +19 -0
- package/template/wall-e/llm/message-guard.js +78 -0
- package/template/wall-e/llm/model-catalog.js +252 -1
- package/template/wall-e/llm/openai.js +26 -4
- package/template/wall-e/llm/portkey-sync.js +654 -0
- package/template/wall-e/llm/provider-error.js +30 -2
- package/template/wall-e/llm/registry.js +5 -1
- package/template/wall-e/llm/request-compat.js +67 -0
- package/template/wall-e/loops/backfill.js +79 -23
- package/template/wall-e/loops/brain-optimize.js +67 -0
- package/template/wall-e/loops/ingest.js +25 -10
- package/template/wall-e/loops/question-digest.js +160 -0
- package/template/wall-e/loops/reflect.js +6 -4
- package/template/wall-e/loops/think.js +39 -12
- package/template/wall-e/mcp-server.js +318 -36
- package/template/wall-e/memory/ctm-context-client.js +52 -14
- package/template/wall-e/memory/ctm-operational-context.js +237 -0
- package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
- package/template/wall-e/memory/ctm-session-context.js +111 -63
- package/template/wall-e/prompts/coding/deepseek.txt +3 -0
- package/template/wall-e/prompts/coding/gemini.txt +6 -0
- package/template/wall-e/prompts/coding/gpt.txt +6 -0
- package/template/wall-e/prompts/coding/local.txt +7 -0
- package/template/wall-e/runtime/decision-hooks.js +115 -0
- package/template/wall-e/runtime/devbox-gateway.js +82 -8
- package/template/wall-e/runtime/prompt-manifest.js +86 -0
- package/template/wall-e/runtime/tool-executor.js +269 -0
- package/template/wall-e/runtime/tool-result-envelope.js +138 -0
- package/template/wall-e/runtime/transcript-projection.js +60 -0
- package/template/wall-e/runtime/walle-runtime.js +224 -0
- package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
- package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
- package/template/wall-e/server.js +15 -0
- package/template/wall-e/session-files.js +9 -0
- package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
- package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
- package/template/wall-e/skills/claude-code-reader.js +7 -3
- package/template/wall-e/skills/script-skill-runner.js +10 -0
- package/template/wall-e/skills/skill-planner.js +38 -0
- package/template/wall-e/tools/builtin-middleware.js +19 -9
- package/template/wall-e/tools/local-tools.js +1428 -16
- package/template/wall-e/tools/permission-checker.js +73 -5
- package/template/wall-e/tools/question-manager.js +117 -7
- package/template/wall-e/training/harvester.js +12 -28
- package/template/wall-e/training/replay.js +25 -80
- package/template/website/index.html +10 -10
- package/template/wall-e/eval/ab-test.js +0 -203
- package/template/wall-e/eval/agent-runner.js +0 -772
- package/template/wall-e/eval/agent-scorer.js +0 -461
- package/template/wall-e/eval/aggregator.js +0 -414
- package/template/wall-e/eval/allowed-test-commands.js +0 -34
- package/template/wall-e/eval/benchmark-generator.js +0 -113
- package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
- package/template/wall-e/eval/benchmarks/chat.json +0 -82
- package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
- package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
- package/template/wall-e/eval/benchmarks/coding.json +0 -122
- package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
- package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
- package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
- package/template/wall-e/eval/benchmarks.js +0 -669
- package/template/wall-e/eval/cc-replay.js +0 -719
- package/template/wall-e/eval/chat-eval.js +0 -525
- package/template/wall-e/eval/check-keys.js +0 -15
- package/template/wall-e/eval/check-providers.js +0 -42
- package/template/wall-e/eval/codex-cli-baseline.js +0 -669
- package/template/wall-e/eval/coding-agent-real.js +0 -570
- package/template/wall-e/eval/context-compactor.js +0 -251
- package/template/wall-e/eval/debug-agent003.js +0 -68
- package/template/wall-e/eval/diagnostics.js +0 -216
- package/template/wall-e/eval/eval-orchestrator.js +0 -642
- package/template/wall-e/eval/evaluate.js +0 -202
- package/template/wall-e/eval/evaluator.js +0 -373
- package/template/wall-e/eval/exporter.js +0 -212
- package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
- package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
- package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
- package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
- package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
- package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
- package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
- package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
- package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
- package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
- package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
- package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
- package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
- package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
- package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
- package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
- package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
- package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
- package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
- package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
- package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
- package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
- package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
- package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
- package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
- package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
- package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
- package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
- package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
- package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
- package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
- package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
- package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
- package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
- package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
- package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
- package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
- package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
- package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
- package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
- package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
- package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
- package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
- package/template/wall-e/eval/harvester.js +0 -685
- package/template/wall-e/eval/head-to-head.js +0 -388
- package/template/wall-e/eval/humaneval-adapter.js +0 -321
- package/template/wall-e/eval/list-models.js +0 -31
- package/template/wall-e/eval/livecodebench-adapter.js +0 -291
- package/template/wall-e/eval/mail-integration.js +0 -443
- package/template/wall-e/eval/manifest.js +0 -186
- package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
- package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
- package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
- package/template/wall-e/eval/meta-harness/cli.js +0 -86
- package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
- package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
- package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
- package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
- package/template/wall-e/eval/meta-harness/frontier.js +0 -96
- package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
- package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
- package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
- package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
- package/template/wall-e/eval/meta-harness/reporting.js +0 -58
- package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
- package/template/wall-e/eval/meta-harness/validation.js +0 -81
- package/template/wall-e/eval/promoter.js +0 -228
- package/template/wall-e/eval/provider-normalizer.js +0 -33
- package/template/wall-e/eval/replay.js +0 -395
- package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
- package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
- package/template/wall-e/eval/run-coding-agent-real.js +0 -187
- package/template/wall-e/eval/run-eval.js +0 -435
- package/template/wall-e/eval/run-model-comparison.js +0 -142
- package/template/wall-e/eval/session-evaluator.js +0 -187
- package/template/wall-e/eval/session-miner.js +0 -207
- package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
- package/template/wall-e/eval/session-transcripts.js +0 -509
- package/template/wall-e/eval/shadow.js +0 -161
- package/template/wall-e/eval/swebench-adapter.js +0 -345
- package/template/wall-e/eval/swebench-docker.js +0 -192
- package/template/wall-e/eval/train.py +0 -320
- package/template/wall-e/eval/trainer.js +0 -232
- package/template/wall-e/eval/weekly-eval-loop.js +0 -241
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
class SerialTaskQueue {
|
|
4
|
+
constructor(name = 'serial-task-queue') {
|
|
5
|
+
this.name = name;
|
|
6
|
+
this._tail = Promise.resolve();
|
|
7
|
+
this._active = null;
|
|
8
|
+
this._pending = 0;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
run(label, fn) {
|
|
12
|
+
if (typeof fn !== 'function') throw new TypeError('SerialTaskQueue.run requires a function');
|
|
13
|
+
const taskLabel = label || 'task';
|
|
14
|
+
this._pending += 1;
|
|
15
|
+
const runTask = async () => {
|
|
16
|
+
this._pending -= 1;
|
|
17
|
+
this._active = taskLabel;
|
|
18
|
+
try {
|
|
19
|
+
return await fn();
|
|
20
|
+
} finally {
|
|
21
|
+
this._active = null;
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const job = this._tail.then(runTask, runTask);
|
|
25
|
+
this._tail = job.catch(() => {});
|
|
26
|
+
return job;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async drain(timeoutMs = 5000) {
|
|
30
|
+
const timeout = Math.max(0, Number(timeoutMs) || 0);
|
|
31
|
+
if (timeout === 0) {
|
|
32
|
+
await this._tail;
|
|
33
|
+
return { ok: true, timedOut: false };
|
|
34
|
+
}
|
|
35
|
+
let timer = null;
|
|
36
|
+
try {
|
|
37
|
+
return await Promise.race([
|
|
38
|
+
this._tail.then(() => ({ ok: true, timedOut: false })),
|
|
39
|
+
new Promise((resolve) => {
|
|
40
|
+
timer = setTimeout(() => resolve({
|
|
41
|
+
ok: false,
|
|
42
|
+
timedOut: true,
|
|
43
|
+
active: this._active,
|
|
44
|
+
pending: this._pending,
|
|
45
|
+
}), timeout);
|
|
46
|
+
if (typeof timer.unref === 'function') timer.unref();
|
|
47
|
+
}),
|
|
48
|
+
]);
|
|
49
|
+
} finally {
|
|
50
|
+
if (timer) clearTimeout(timer);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getState() {
|
|
55
|
+
return {
|
|
56
|
+
name: this.name,
|
|
57
|
+
active: this._active,
|
|
58
|
+
pending: this._pending,
|
|
59
|
+
idle: !this._active && this._pending === 0,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
module.exports = { SerialTaskQueue };
|
|
@@ -37,8 +37,247 @@ function listenerUrl(listener, hostOverride = '') {
|
|
|
37
37
|
return `${listener.scheme}://${host}:${listener.port}/`;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
// Errors that mean "the bind address is not currently assigned to any local
|
|
41
|
+
// interface" — typically because Tailscale logged out, a NIC was unplugged, or
|
|
42
|
+
// an IPv6-only address vanished. These are not permanent: a slow background
|
|
43
|
+
// retry recovers automatically when the interface reappears.
|
|
44
|
+
const INTERFACE_MISSING_CODES = new Set(['EADDRNOTAVAIL', 'EINVAL']);
|
|
45
|
+
|
|
46
|
+
const DEFAULT_FAST_RETRY_BASE_MS = 1000;
|
|
47
|
+
const DEFAULT_MAX_FAST_ATTEMPTS = 5;
|
|
48
|
+
const DEFAULT_SLOW_RETRY_MS = 30_000;
|
|
49
|
+
|
|
50
|
+
function classifyListenerError(err) {
|
|
51
|
+
const code = err && err.code ? String(err.code) : null;
|
|
52
|
+
const message = (err && err.message) ? String(err.message) : String(err || '');
|
|
53
|
+
if (code === 'EADDRINUSE') return { kind: 'transient-port', code, message };
|
|
54
|
+
if (code && INTERFACE_MISSING_CODES.has(code)) return { kind: 'interface-missing', code, message };
|
|
55
|
+
return { kind: 'permanent', code, message };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Coordinates `listen()` retries across a set of listener entries.
|
|
59
|
+
//
|
|
60
|
+
// Soft-degrade policy: if the primary bind fails because its interface is
|
|
61
|
+
// missing (e.g. Tailscale IP not currently assigned), the loopback fallback
|
|
62
|
+
// listener — already in the same listeners array via buildCtmListenerConfigs —
|
|
63
|
+
// keeps serving. The missing-interface listener is retried in the background
|
|
64
|
+
// at slowRetryMs cadence so remote access is restored automatically when the
|
|
65
|
+
// interface returns, without a CTM restart.
|
|
66
|
+
//
|
|
67
|
+
// Fatal exit (via onFatal) is only triggered when every listener has failed
|
|
68
|
+
// AND no retry is pending — i.e. nothing will ever serve.
|
|
69
|
+
//
|
|
70
|
+
// Dependency injection (timers, logger, onFatal) makes the coordinator
|
|
71
|
+
// testable without binding real ports or calling process.exit.
|
|
72
|
+
function createListenerRetryCoordinator({
|
|
73
|
+
listeners,
|
|
74
|
+
logger = console,
|
|
75
|
+
onFatal = (msg) => { try { console.error(msg); } catch {} process.exit(1); },
|
|
76
|
+
setTimeoutFn = (fn, delay) => {
|
|
77
|
+
const t = setTimeout(fn, delay);
|
|
78
|
+
if (t && typeof t.unref === 'function') t.unref();
|
|
79
|
+
return t;
|
|
80
|
+
},
|
|
81
|
+
clearTimeoutFn = clearTimeout,
|
|
82
|
+
fastRetryBaseMs = DEFAULT_FAST_RETRY_BASE_MS,
|
|
83
|
+
maxFastAttempts = DEFAULT_MAX_FAST_ATTEMPTS,
|
|
84
|
+
slowRetryMs = DEFAULT_SLOW_RETRY_MS,
|
|
85
|
+
} = {}) {
|
|
86
|
+
if (!Array.isArray(listeners) || listeners.length === 0) {
|
|
87
|
+
throw new Error('createListenerRetryCoordinator requires a non-empty listeners array');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const entries = listeners.map((listener) => ({
|
|
91
|
+
listener,
|
|
92
|
+
state: 'idle',
|
|
93
|
+
slowRetryTimer: null,
|
|
94
|
+
everListening: false,
|
|
95
|
+
everFailed: false,
|
|
96
|
+
// True once we've logged the current "interface missing" outage. Gates the
|
|
97
|
+
// slow-retry warning to ONE line per outage episode instead of every
|
|
98
|
+
// slowRetryMs tick (otherwise a logged-out Tailscale floods the log forever).
|
|
99
|
+
interfaceMissingLogged: false,
|
|
100
|
+
}));
|
|
101
|
+
let fastRetryAttempts = 0;
|
|
102
|
+
let fastRetryTimer = null;
|
|
103
|
+
let started = false;
|
|
104
|
+
let stopped = false;
|
|
105
|
+
|
|
106
|
+
const fmtAddr = (l) => `${l.host}:${l.port}`;
|
|
107
|
+
|
|
108
|
+
const anyListening = () => entries.some((e) => e.state === 'listening');
|
|
109
|
+
|
|
110
|
+
const anyRetryPending = () => {
|
|
111
|
+
if (fastRetryTimer) return true;
|
|
112
|
+
for (const e of entries) {
|
|
113
|
+
if (e.state === 'pending') return true;
|
|
114
|
+
if (e.slowRetryTimer) return true;
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
function clearSlowRetry(entry) {
|
|
120
|
+
if (entry.slowRetryTimer) {
|
|
121
|
+
clearTimeoutFn(entry.slowRetryTimer);
|
|
122
|
+
entry.slowRetryTimer = null;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function startListen(entry) {
|
|
127
|
+
if (stopped) return;
|
|
128
|
+
entry.state = 'pending';
|
|
129
|
+
try {
|
|
130
|
+
entry.listener.server.listen(entry.listener.port, entry.listener.host);
|
|
131
|
+
} catch (err) {
|
|
132
|
+
// Some listen() failures throw synchronously instead of emitting 'error'.
|
|
133
|
+
// Route through the same handler so policy stays consistent.
|
|
134
|
+
handleError(entry, err);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function scheduleFastRetry() {
|
|
139
|
+
if (stopped || fastRetryTimer) return null;
|
|
140
|
+
fastRetryAttempts += 1;
|
|
141
|
+
const delay = fastRetryBaseMs * fastRetryAttempts;
|
|
142
|
+
fastRetryTimer = setTimeoutFn(() => {
|
|
143
|
+
fastRetryTimer = null;
|
|
144
|
+
if (stopped) return;
|
|
145
|
+
for (const e of entries) {
|
|
146
|
+
if (e.state === 'pending' || e.state === 'listening') continue;
|
|
147
|
+
if (e.slowRetryTimer) continue;
|
|
148
|
+
startListen(e);
|
|
149
|
+
}
|
|
150
|
+
}, delay);
|
|
151
|
+
return delay;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function scheduleSlowRetry(entry) {
|
|
155
|
+
if (stopped || entry.slowRetryTimer) return;
|
|
156
|
+
entry.slowRetryTimer = setTimeoutFn(() => {
|
|
157
|
+
entry.slowRetryTimer = null;
|
|
158
|
+
if (stopped || entry.state === 'listening') return;
|
|
159
|
+
startListen(entry);
|
|
160
|
+
}, slowRetryMs);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function maybeFatal(entry, message) {
|
|
164
|
+
if (stopped) return;
|
|
165
|
+
if (anyListening() || anyRetryPending()) {
|
|
166
|
+
logger.error(
|
|
167
|
+
`[startup] ${entry.listener.id} listener ${fmtAddr(entry.listener)} failed: ${message} — ` +
|
|
168
|
+
(anyListening() ? 'continuing on other listener(s).' : 'waiting on other listener(s).')
|
|
169
|
+
);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
onFatal(`[FATAL] ${entry.listener.id} listener ${fmtAddr(entry.listener)}: ${message}`);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function handleListening(entry) {
|
|
176
|
+
const isRecovery = entry.everFailed;
|
|
177
|
+
entry.state = 'listening';
|
|
178
|
+
clearSlowRetry(entry);
|
|
179
|
+
if (isRecovery) {
|
|
180
|
+
logger.log(
|
|
181
|
+
`[startup] ${entry.listener.id} listener re-bound at ${fmtAddr(entry.listener)} ` +
|
|
182
|
+
'(recovered after earlier bind failure).'
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
entry.everListening = true;
|
|
186
|
+
entry.everFailed = false;
|
|
187
|
+
// A successful (re)bind ends any outage — re-arm the one-shot warning so a
|
|
188
|
+
// future down→up→down cycle logs again.
|
|
189
|
+
entry.interfaceMissingLogged = false;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function handleError(entry, err) {
|
|
193
|
+
if (stopped) return;
|
|
194
|
+
entry.state = 'failed';
|
|
195
|
+
entry.everFailed = true;
|
|
196
|
+
const decision = classifyListenerError(err);
|
|
197
|
+
|
|
198
|
+
if (decision.kind === 'transient-port') {
|
|
199
|
+
if (fastRetryTimer) {
|
|
200
|
+
logger.error(
|
|
201
|
+
`[startup] ${entry.listener.id} ${fmtAddr(entry.listener)} EADDRINUSE; ` +
|
|
202
|
+
'covered by pending retry.'
|
|
203
|
+
);
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
if (fastRetryAttempts >= maxFastAttempts) {
|
|
207
|
+
maybeFatal(entry, `EADDRINUSE after ${maxFastAttempts} attempts`);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const delay = scheduleFastRetry();
|
|
211
|
+
logger.error(
|
|
212
|
+
`[startup] ${entry.listener.id} ${fmtAddr(entry.listener)} EADDRINUSE ` +
|
|
213
|
+
`(attempt ${fastRetryAttempts}/${maxFastAttempts}), retrying in ${delay}ms...`
|
|
214
|
+
);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (decision.kind === 'interface-missing') {
|
|
219
|
+
scheduleSlowRetry(entry);
|
|
220
|
+
// Log ONCE per outage episode, not on every slowRetryMs tick. The slow
|
|
221
|
+
// retry keeps running silently; handleListening() re-arms this flag when
|
|
222
|
+
// the interface returns, so a new outage logs again.
|
|
223
|
+
if (!entry.interfaceMissingLogged) {
|
|
224
|
+
entry.interfaceMissingLogged = true;
|
|
225
|
+
const tail = anyListening()
|
|
226
|
+
? 'Other listener(s) still serving in the meantime.'
|
|
227
|
+
: 'No other listener bound yet; CTM will become reachable when the interface returns or another listener binds.';
|
|
228
|
+
logger.warn(
|
|
229
|
+
`[startup] ${entry.listener.id} listener ${fmtAddr(entry.listener)} ${decision.code}: ` +
|
|
230
|
+
`bind interface not available — retrying every ${Math.round(slowRetryMs / 1000)}s (further attempts logged once on recovery). ${tail}`
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
// Safety invariant only: never exits while this entry's slow retry is
|
|
234
|
+
// pending (anyRetryPending() is true). Checked inline so repeat failures
|
|
235
|
+
// don't re-log the "continuing on other listener(s)" line.
|
|
236
|
+
if (!anyListening() && !anyRetryPending()) {
|
|
237
|
+
onFatal(`[FATAL] ${entry.listener.id} listener ${fmtAddr(entry.listener)}: ${decision.code}: ${decision.message}`);
|
|
238
|
+
}
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
maybeFatal(entry, decision.message);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function start() {
|
|
246
|
+
if (started) return;
|
|
247
|
+
started = true;
|
|
248
|
+
for (const entry of entries) {
|
|
249
|
+
entry.listener.server.on('listening', () => handleListening(entry));
|
|
250
|
+
entry.listener.server.on('error', (err) => handleError(entry, err));
|
|
251
|
+
}
|
|
252
|
+
for (const entry of entries) startListen(entry);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function stop() {
|
|
256
|
+
stopped = true;
|
|
257
|
+
if (fastRetryTimer) { clearTimeoutFn(fastRetryTimer); fastRetryTimer = null; }
|
|
258
|
+
for (const entry of entries) clearSlowRetry(entry);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function snapshot() {
|
|
262
|
+
return entries.map((e) => ({
|
|
263
|
+
id: e.listener.id,
|
|
264
|
+
state: e.state,
|
|
265
|
+
slowRetryScheduled: !!e.slowRetryTimer,
|
|
266
|
+
everListening: e.everListening,
|
|
267
|
+
}));
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return { start, stop, snapshot };
|
|
271
|
+
}
|
|
272
|
+
|
|
40
273
|
module.exports = {
|
|
41
274
|
buildCtmListenerConfigs,
|
|
42
275
|
hostForUrl,
|
|
43
276
|
listenerUrl,
|
|
277
|
+
classifyListenerError,
|
|
278
|
+
createListenerRetryCoordinator,
|
|
279
|
+
// Exposed for tests / callers that want to tune defaults via env:
|
|
280
|
+
DEFAULT_FAST_RETRY_BASE_MS,
|
|
281
|
+
DEFAULT_MAX_FAST_ATTEMPTS,
|
|
282
|
+
DEFAULT_SLOW_RETRY_MS,
|
|
44
283
|
};
|
|
@@ -13,10 +13,39 @@ function toMs(value) {
|
|
|
13
13
|
return Number.isFinite(n) && n > 0 ? n : nowMs();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function explicitActivityMs(input = {}) {
|
|
17
|
+
const candidates = [
|
|
18
|
+
input.lastActivity,
|
|
19
|
+
input.lastAgentOutputAt,
|
|
20
|
+
input.agentOutputAt,
|
|
21
|
+
input.lastPtyActivity,
|
|
22
|
+
input.lastJsonlActivity,
|
|
23
|
+
input.outputAt,
|
|
24
|
+
];
|
|
25
|
+
for (const value of candidates) {
|
|
26
|
+
const n = Number(value);
|
|
27
|
+
if (Number.isFinite(n) && n > 0) return n;
|
|
28
|
+
}
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isActivityBearingStatus(input = {}) {
|
|
33
|
+
const status = normalizeStatus(input.status);
|
|
34
|
+
const source = String(input.source || '').toLowerCase();
|
|
35
|
+
const reason = String(input.reason || '').toLowerCase();
|
|
36
|
+
if (reason === 'snapshot') return false;
|
|
37
|
+
if (status === 'waiting' && /waiting-for-input|terminal-prompt/.test(`${source} ${reason}`)) return false;
|
|
38
|
+
if (/pty-activity|jsonl-event|codex-jsonl-event|claude-jsonl|terminal-output|session-resumed/.test(`${source} ${reason}`)) return true;
|
|
39
|
+
if (status === 'running' && /authoritative:hook|hook|worker/.test(source)) return true;
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
16
43
|
function normalizeStatus(status) {
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
if (
|
|
44
|
+
const text = String(status || '').trim().toLowerCase().replace(/[-\s]+/g, '_');
|
|
45
|
+
if (text === 'busy' || text === 'active' || text === 'working' || text === 'thinking') return 'running';
|
|
46
|
+
if (text === 'restoring' || text === 'resuming' || text === 'restore_starting' || text === 'restarting') return 'resuming';
|
|
47
|
+
if (text === 'waiting_input' || text === 'waiting_for_input') return 'waiting';
|
|
48
|
+
if (text === 'running' || text === 'resuming' || text === 'waiting' || text === 'idle' || text === 'exited') return text;
|
|
20
49
|
return 'unknown';
|
|
21
50
|
}
|
|
22
51
|
|
|
@@ -33,6 +62,8 @@ function statusFromServerMessage(msg) {
|
|
|
33
62
|
reason: msg.reason || 'stream-status',
|
|
34
63
|
timestamp: msg.timestamp,
|
|
35
64
|
lastActivity: msg.lastActivity,
|
|
65
|
+
lastPtyActivity: msg.lastPtyActivity,
|
|
66
|
+
lastJsonlActivity: msg.lastJsonlActivity,
|
|
36
67
|
};
|
|
37
68
|
}
|
|
38
69
|
|
|
@@ -254,6 +285,8 @@ class SessionCapture extends EventEmitter {
|
|
|
254
285
|
reason: statusEvt.reason || 'stream-status',
|
|
255
286
|
timestamp: statusEvt.timestamp,
|
|
256
287
|
lastActivity: statusEvt.lastActivity,
|
|
288
|
+
lastPtyActivity: statusEvt.lastPtyActivity,
|
|
289
|
+
lastJsonlActivity: statusEvt.lastJsonlActivity,
|
|
257
290
|
eventCount: statusEvt.eventCount,
|
|
258
291
|
});
|
|
259
292
|
}
|
|
@@ -285,8 +318,10 @@ class SessionCapture extends EventEmitter {
|
|
|
285
318
|
decisionId: input.decisionId,
|
|
286
319
|
};
|
|
287
320
|
record.lastUpdated = Math.max(record.lastUpdated || 0, timestamp);
|
|
288
|
-
|
|
289
|
-
|
|
321
|
+
const activityMs = explicitActivityMs(input) || (isActivityBearingStatus(input) ? timestamp : 0);
|
|
322
|
+
if (activityMs) {
|
|
323
|
+
record.lastActivity = Math.max(record.lastActivity || 0, activityMs);
|
|
324
|
+
}
|
|
290
325
|
if (input.eventCount != null) record.eventCount = input.eventCount;
|
|
291
326
|
|
|
292
327
|
const projected = this._project(record);
|
|
@@ -335,7 +370,7 @@ class SessionCapture extends EventEmitter {
|
|
|
335
370
|
ctmSessionId: record.ctmSessionId || null,
|
|
336
371
|
status: projected.status,
|
|
337
372
|
captureStatus: projected.status,
|
|
338
|
-
lastActivity: record.lastActivity ||
|
|
373
|
+
lastActivity: record.lastActivity || 0,
|
|
339
374
|
eventCount: record.eventCount || 0,
|
|
340
375
|
statusEvidence: projected.evidence,
|
|
341
376
|
});
|
|
@@ -403,7 +438,7 @@ class SessionCapture extends EventEmitter {
|
|
|
403
438
|
ctmSessionId: record.ctmSessionId || null,
|
|
404
439
|
agentSessionId: record.agentSessionId || null,
|
|
405
440
|
status: normalizeStatus(status),
|
|
406
|
-
lastActivity: record.lastActivity ||
|
|
441
|
+
lastActivity: record.lastActivity || 0,
|
|
407
442
|
evidence: evidence.map((ev) => ({
|
|
408
443
|
source: ev.source,
|
|
409
444
|
status: ev.status,
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Background migration: backfill session_message_rows for EXISTING sessions whose rows are
|
|
4
|
+
// not yet complete. Source = the existing (small, ≤24 MB) blob; over-cap sessions ('[]')
|
|
5
|
+
// stay on the JSONL render path (the gate keeps them off rows) until re-imported. Idempotent,
|
|
6
|
+
// bounded, resumable — and the render gate (db.sessionContentRowsAvailable) keeps a
|
|
7
|
+
// half-migrated session on the blob path, so a sweep in progress never breaks reads.
|
|
8
|
+
//
|
|
9
|
+
// `db` is the better-sqlite3 handle (dbModule.getDb()); `replaceSessionMessageRows` is the
|
|
10
|
+
// db.js writer (operates on the same handle). Passed in so this stays unit-testable in
|
|
11
|
+
// isolation.
|
|
12
|
+
|
|
13
|
+
// Sessions with a real blob whose row count is below the conversation's message count.
|
|
14
|
+
function findUnbackfilledSessions(db, limit = 50) {
|
|
15
|
+
const lim = Math.max(1, Math.min(100000, Number(limit) || 50));
|
|
16
|
+
return db.prepare(`
|
|
17
|
+
SELECT sc.ctm_session_id AS id
|
|
18
|
+
FROM session_conversations sc
|
|
19
|
+
WHERE sc.extracted_source_len > 0
|
|
20
|
+
AND sc.messages IS NOT NULL AND sc.messages != '[]' AND length(sc.messages) > 2
|
|
21
|
+
AND (SELECT COUNT(*) FROM session_message_rows r WHERE r.ctm_session_id = sc.ctm_session_id) < sc.extracted_source_len
|
|
22
|
+
LIMIT ?
|
|
23
|
+
`).all(lim).map(r => r.id);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Backfill one session's rows from its blob. Returns rows written (0 if no parseable blob).
|
|
27
|
+
function backfillSessionContentRows(db, sessionId, replaceSessionMessageRows) {
|
|
28
|
+
const row = db.prepare('SELECT messages FROM session_conversations WHERE ctm_session_id = ?').get(sessionId);
|
|
29
|
+
if (!row || !row.messages) return 0;
|
|
30
|
+
let msgs;
|
|
31
|
+
try { msgs = JSON.parse(row.messages); } catch { return 0; }
|
|
32
|
+
if (!Array.isArray(msgs) || msgs.length === 0) return 0;
|
|
33
|
+
return replaceSessionMessageRows(sessionId, msgs);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Disk reclaim: sessions whose faithful rows are COMPLETE (count >= extracted_source_len > 0 — the
|
|
37
|
+
// same gate as sessionContentRowsAvailable) but whose blob is still present. Once rows serve the
|
|
38
|
+
// render, the blob is dead weight; nulling it reclaims disk. Reversible: re-import from the JSONL
|
|
39
|
+
// rebuilds it. Excludes already-empty/over-cap blobs.
|
|
40
|
+
function findReclaimableSessions(db, limit = 50) {
|
|
41
|
+
const lim = Math.max(1, Math.min(100000, Number(limit) || 50));
|
|
42
|
+
return db.prepare(`
|
|
43
|
+
SELECT sc.ctm_session_id AS id
|
|
44
|
+
FROM session_conversations sc
|
|
45
|
+
WHERE sc.messages IS NOT NULL AND sc.messages != '[]' AND length(sc.messages) > 2
|
|
46
|
+
AND sc.extracted_source_len > 0
|
|
47
|
+
AND (SELECT COUNT(*) FROM session_message_rows r WHERE r.ctm_session_id = sc.ctm_session_id) >= sc.extracted_source_len
|
|
48
|
+
LIMIT ?
|
|
49
|
+
`).all(lim).map(r => r.id);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Null the blob (messages='[]') for complete-rows sessions. Bounded + idempotent. The caller only
|
|
53
|
+
// runs this when blob retirement is active (CTM_DUAL_WRITE_BLOB=0) — otherwise the next import would
|
|
54
|
+
// just rewrite the blob, so reclaiming would be pointless churn.
|
|
55
|
+
function reclaimRetiredBlobs(db, { limit = 50 } = {}) {
|
|
56
|
+
const ids = findReclaimableSessions(db, limit);
|
|
57
|
+
let reclaimed = 0;
|
|
58
|
+
const upd = db.prepare("UPDATE session_conversations SET messages = '[]' WHERE ctm_session_id = ? AND messages != '[]'");
|
|
59
|
+
for (const id of ids) {
|
|
60
|
+
try { if (upd.run(id).changes > 0) reclaimed++; }
|
|
61
|
+
catch (e) { console.error('[blob-reclaim] session', String(id).slice(0, 8), 'failed:', e.message); }
|
|
62
|
+
}
|
|
63
|
+
return { swept: ids.length, reclaimed, remaining: findReclaimableSessions(db, 1).length };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// One bounded sweep. Safe to call repeatedly (idempotent); converges as sessions complete. When blob
|
|
67
|
+
// retirement is active, the same sweep also reclaims disk from complete-rows sessions (one off-thread
|
|
68
|
+
// pass does both — no extra scheduler job).
|
|
69
|
+
function runContentRowsBackfillSweep(db, replaceSessionMessageRows, { limit = 50 } = {}) {
|
|
70
|
+
const ids = findUnbackfilledSessions(db, limit);
|
|
71
|
+
let migrated = 0, rows = 0;
|
|
72
|
+
for (const id of ids) {
|
|
73
|
+
try {
|
|
74
|
+
const n = backfillSessionContentRows(db, id, replaceSessionMessageRows);
|
|
75
|
+
if (n > 0) { migrated++; rows += n; }
|
|
76
|
+
} catch (e) {
|
|
77
|
+
console.error('[content-backfill] session', String(id).slice(0, 8), 'failed:', e.message);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
let reclaimed = 0;
|
|
81
|
+
if (process.env.CTM_DUAL_WRITE_BLOB === '0') {
|
|
82
|
+
try { reclaimed = reclaimRetiredBlobs(db, { limit }).reclaimed; }
|
|
83
|
+
catch (e) { console.error('[blob-reclaim] sweep failed:', e.message); }
|
|
84
|
+
}
|
|
85
|
+
let droppedLegacy = null;
|
|
86
|
+
if (process.env.CTM_RETIRE_LEGACY_STORES === '1') {
|
|
87
|
+
try { const r = retireLegacyStores(db); if (r.dropped.length) droppedLegacy = r.dropped; }
|
|
88
|
+
catch (e) { console.error('[retire-legacy] sweep failed:', e.message); }
|
|
89
|
+
}
|
|
90
|
+
return { swept: ids.length, migrated, rows, reclaimed, droppedLegacy, remaining: findUnbackfilledSessions(db, 1).length };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Drop the fully-retired legacy message stores once migration is COMPLETE (every eligible session
|
|
94
|
+
// has rows, so the search/freshness/reference readers never need the legacy fallback). The readers
|
|
95
|
+
// are independently guarded against a missing table, so a drop mid-process can't blank results.
|
|
96
|
+
// Gated by the caller on CTM_RETIRE_LEGACY_STORES=1. Reversible: set CTM_DUAL_WRITE_SESSION_MESSAGES=1
|
|
97
|
+
// + re-run the legacy backfill, or re-import from the JSONL.
|
|
98
|
+
function retireLegacyStores(db) {
|
|
99
|
+
const status = getSessionRowsStatus(db);
|
|
100
|
+
if (!status.complete) return { dropped: [], reason: 'migration_incomplete', remaining: status.remaining };
|
|
101
|
+
const dropped = [];
|
|
102
|
+
for (const t of ['session_messages_fts', 'session_messages']) {
|
|
103
|
+
try {
|
|
104
|
+
if (db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name=?").get(t)) {
|
|
105
|
+
db.exec(`DROP TABLE IF EXISTS ${t}`);
|
|
106
|
+
dropped.push(t);
|
|
107
|
+
}
|
|
108
|
+
} catch (e) { console.error('[retire-legacy] drop', t, 'failed:', e.message); }
|
|
109
|
+
}
|
|
110
|
+
return { dropped };
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Migration completeness (for /api/ctm/session-rows-status + the cutover gate).
|
|
114
|
+
function getSessionRowsStatus(db) {
|
|
115
|
+
const total = Number(db.prepare(
|
|
116
|
+
"SELECT COUNT(*) AS c FROM session_conversations WHERE extracted_source_len > 0 AND messages != '[]' AND length(messages) > 2"
|
|
117
|
+
).get().c);
|
|
118
|
+
const remaining = findUnbackfilledSessions(db, 100000).length;
|
|
119
|
+
const reclaimable = findReclaimableSessions(db, 100000).length;
|
|
120
|
+
return { total, migrated: total - remaining, remaining, complete: remaining === 0, reclaimable };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
module.exports = {
|
|
124
|
+
findUnbackfilledSessions,
|
|
125
|
+
backfillSessionContentRows,
|
|
126
|
+
runContentRowsBackfillSweep,
|
|
127
|
+
findReclaimableSessions,
|
|
128
|
+
reclaimRetiredBlobs,
|
|
129
|
+
retireLegacyStores,
|
|
130
|
+
getSessionRowsStatus,
|
|
131
|
+
};
|