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
|
@@ -14,18 +14,60 @@ const MODEL_TIER_ENV = {
|
|
|
14
14
|
quality: 'WALLE_MODEL_QUALITY',
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// The starred default provider/model live in Wall-E's brain (set from the AI
|
|
18
|
+
// Providers page) and are served by GET /api/wall-e/status. CTM's process env
|
|
19
|
+
// (WALLE_PROVIDER/WALLE_MODEL from the launchd plist) goes stale the moment the
|
|
20
|
+
// user re-stars a provider, so background calls must ask Wall-E first and only
|
|
21
|
+
// fall back to env when Wall-E is unreachable. Cached briefly to keep the
|
|
22
|
+
// per-call overhead at one HTTP roundtrip per TTL, with a shorter negative TTL
|
|
23
|
+
// so a Wall-E restart doesn't pin us to env for long.
|
|
24
|
+
const WALLE_DEFAULTS_TTL_MS = 30000;
|
|
25
|
+
const WALLE_DEFAULTS_NEGATIVE_TTL_MS = 5000;
|
|
26
|
+
let _walleDefaultsCache = null; // { at, value: {provider, model} | null }
|
|
27
|
+
|
|
28
|
+
function _resetWalleDefaultsCache() {
|
|
29
|
+
_walleDefaultsCache = null;
|
|
19
30
|
}
|
|
20
31
|
|
|
21
|
-
function
|
|
32
|
+
async function fetchWalleStatusDefaults(env) {
|
|
33
|
+
const port = Number(env?.WALLE_PORT) || 3457;
|
|
34
|
+
const response = await fetch(`http://127.0.0.1:${port}/api/wall-e/status`, {
|
|
35
|
+
signal: AbortSignal.timeout(1500),
|
|
36
|
+
});
|
|
37
|
+
if (!response.ok) throw new Error(`Wall-E status returned ${response.status}`);
|
|
38
|
+
const json = await response.json();
|
|
39
|
+
return json?.data || json || {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function getWalleDefaultSelection(options = {}) {
|
|
43
|
+
const env = options.env || process.env;
|
|
44
|
+
if (String(env.CTM_BACKGROUND_LLM_WALLE_DEFAULTS || '').trim() === '0') return null;
|
|
45
|
+
const now = Date.now();
|
|
46
|
+
if (_walleDefaultsCache) {
|
|
47
|
+
const ttl = _walleDefaultsCache.value ? WALLE_DEFAULTS_TTL_MS : WALLE_DEFAULTS_NEGATIVE_TTL_MS;
|
|
48
|
+
if (now - _walleDefaultsCache.at < ttl) return _walleDefaultsCache.value;
|
|
49
|
+
}
|
|
50
|
+
let value = null;
|
|
22
51
|
try {
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
52
|
+
const fetchStatus = options.fetchWalleStatus || fetchWalleStatusDefaults;
|
|
53
|
+
const status = await fetchStatus(env);
|
|
54
|
+
const provider = sanitizeSetupProviderType(status?.walle_provider || '');
|
|
55
|
+
if (provider) {
|
|
56
|
+
value = { provider, model: typeof status?.walle_model === 'string' ? status.walle_model : '' };
|
|
57
|
+
}
|
|
26
58
|
} catch {
|
|
27
|
-
|
|
59
|
+
value = null;
|
|
28
60
|
}
|
|
61
|
+
_walleDefaultsCache = { at: now, value };
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function defaultClientModule() {
|
|
66
|
+
return require(path.resolve(__dirname, '..', '..', 'wall-e', 'llm', 'client'));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function defaultBrain() {
|
|
70
|
+
return null;
|
|
29
71
|
}
|
|
30
72
|
|
|
31
73
|
function getBrainKv(brain, key) {
|
|
@@ -112,10 +154,14 @@ function resolveBackgroundLlmSelection(options = {}) {
|
|
|
112
154
|
|| 'anthropic'
|
|
113
155
|
) || 'anthropic';
|
|
114
156
|
const runtime = providerRuntimeState({ brain, env, provider });
|
|
157
|
+
// options.defaultModel is the starred default model from Wall-E status — the
|
|
158
|
+
// live value of the brain's walle_model KV, so it slots in at that priority,
|
|
159
|
+
// ahead of the potentially stale env.WALLE_MODEL.
|
|
115
160
|
const requestedModel = options.model
|
|
116
161
|
|| selectTierModel(options.modelTier, provider, { brain, env, clientModule })
|
|
117
162
|
|| getBrainKv(brain, 'walle_model_' + provider)
|
|
118
163
|
|| getBrainKv(brain, 'walle_model')
|
|
164
|
+
|| options.defaultModel
|
|
119
165
|
|| env.WALLE_MODEL
|
|
120
166
|
|| '';
|
|
121
167
|
const model = clientModule.resolveCompatibleModel
|
|
@@ -182,15 +228,50 @@ function extractBackgroundLlmText(response) {
|
|
|
182
228
|
return '';
|
|
183
229
|
}
|
|
184
230
|
|
|
231
|
+
// Failure classes (wall-e/llm/provider-error.js) that mean "this provider can't
|
|
232
|
+
// serve background work right now" — worth one hop to the local fallback. This
|
|
233
|
+
// deliberately includes quota_exceeded and provider_rejected (a depleted
|
|
234
|
+
// account, e.g. DeepSeek "402 Insufficient Balance", classifies as the former;
|
|
235
|
+
// other 4xx rejections as the latter) — background work is best-effort, so any
|
|
236
|
+
// provider-side refusal is better served locally than dropped.
|
|
237
|
+
const FALLBACK_ELIGIBLE_CLASSES = new Set([
|
|
238
|
+
'quota_exceeded', 'auth_error', 'rate_limited', 'provider_unavailable',
|
|
239
|
+
'network', 'timeout', 'provider_rejected',
|
|
240
|
+
]);
|
|
241
|
+
|
|
242
|
+
function classifyBackgroundLlmError(err, options = {}) {
|
|
243
|
+
if (err?.reason === 'no_provider_credentials') return 'auth_error';
|
|
244
|
+
try {
|
|
245
|
+
const classify = options.classifyError
|
|
246
|
+
|| require(path.resolve(__dirname, '..', '..', 'wall-e', 'llm', 'provider-error')).classifyProviderError;
|
|
247
|
+
return classify(err)?.type || 'unknown';
|
|
248
|
+
} catch {
|
|
249
|
+
return 'unknown';
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function backgroundLlmFallbackProvider(env) {
|
|
254
|
+
const raw = String(env?.CTM_BACKGROUND_LLM_FALLBACK_PROVIDER ?? 'ollama').trim().toLowerCase();
|
|
255
|
+
if (!raw || raw === '0' || raw === 'none' || raw === 'off') return '';
|
|
256
|
+
return sanitizeSetupProviderType(raw);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let _lastFallbackLogAt = 0;
|
|
260
|
+
|
|
185
261
|
async function callBackgroundLlm(prompt, options = {}) {
|
|
186
262
|
const clientModule = options.clientModule || defaultClientModule();
|
|
187
|
-
const
|
|
188
|
-
const createClient = options.createClient || clientModule.createClient;
|
|
189
|
-
const client = createClient(selection.clientType, selection.config);
|
|
190
|
-
const timeoutMs = options.timeoutMs || 60000;
|
|
191
|
-
const signal = options.signal || AbortSignal.timeout(timeoutMs);
|
|
263
|
+
const env = options.env || process.env;
|
|
192
264
|
|
|
193
|
-
|
|
265
|
+
// Respect the starred default from the AI Providers page (Wall-E brain) over
|
|
266
|
+
// the process env, which only reflects the launchd plist at boot.
|
|
267
|
+
let walleDefaults = null;
|
|
268
|
+
if (!options.provider) walleDefaults = await getWalleDefaultSelection(options);
|
|
269
|
+
|
|
270
|
+
const attempt = async (selection) => {
|
|
271
|
+
const createClient = options.createClient || clientModule.createClient;
|
|
272
|
+
const client = createClient(selection.clientType, selection.config);
|
|
273
|
+
const timeoutMs = options.timeoutMs || 60000;
|
|
274
|
+
const signal = options.signal || AbortSignal.timeout(timeoutMs);
|
|
194
275
|
const response = await client.chat({
|
|
195
276
|
model: selection.model || undefined,
|
|
196
277
|
system: options.system,
|
|
@@ -209,17 +290,63 @@ async function callBackgroundLlm(prompt, options = {}) {
|
|
|
209
290
|
requestedModel: selection.requestedModel,
|
|
210
291
|
modelAdjusted: selection.modelAdjusted,
|
|
211
292
|
};
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const resolveOptions = {
|
|
296
|
+
...options,
|
|
297
|
+
clientModule,
|
|
298
|
+
provider: options.provider || walleDefaults?.provider || undefined,
|
|
299
|
+
defaultModel: options.defaultModel
|
|
300
|
+
|| (walleDefaults?.provider && !options.provider ? walleDefaults.model : ''),
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
let selection = null;
|
|
304
|
+
let primaryError = null;
|
|
305
|
+
try {
|
|
306
|
+
selection = resolveBackgroundLlmSelection(resolveOptions);
|
|
307
|
+
return await attempt(selection);
|
|
212
308
|
} catch (err) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
309
|
+
primaryError = err;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
const failedProvider = selection?.provider || primaryError.provider || resolveOptions.provider || '';
|
|
313
|
+
primaryError.provider = primaryError.provider || failedProvider;
|
|
314
|
+
primaryError.model = primaryError.model || selection?.model || selection?.requestedModel || '';
|
|
315
|
+
primaryError.clientType = primaryError.clientType || selection?.clientType || '';
|
|
316
|
+
|
|
317
|
+
// One hop to the local fallback provider (free, no surprise spend) when the
|
|
318
|
+
// configured provider can't serve — never on caller-initiated aborts.
|
|
319
|
+
const fallbackProvider = backgroundLlmFallbackProvider(env);
|
|
320
|
+
const callerAborted = !!(options.signal && options.signal.aborted);
|
|
321
|
+
if (!fallbackProvider || fallbackProvider === failedProvider || callerAborted) throw primaryError;
|
|
322
|
+
const errorClass = classifyBackgroundLlmError(primaryError, options);
|
|
323
|
+
if (!FALLBACK_ELIGIBLE_CLASSES.has(errorClass)) throw primaryError;
|
|
324
|
+
|
|
325
|
+
try {
|
|
326
|
+
const fallbackSelection = resolveBackgroundLlmSelection({
|
|
327
|
+
...resolveOptions,
|
|
328
|
+
provider: fallbackProvider,
|
|
329
|
+
model: undefined, // primary's model hints don't apply to the fallback
|
|
330
|
+
defaultModel: walleDefaults?.provider === fallbackProvider ? walleDefaults.model : '',
|
|
331
|
+
});
|
|
332
|
+
const result = await attempt(fallbackSelection);
|
|
333
|
+
const now = Date.now();
|
|
334
|
+
if (now - _lastFallbackLogAt > 60000) {
|
|
335
|
+
_lastFallbackLogAt = now;
|
|
336
|
+
console.warn(`[background-llm] ${failedProvider} failed (${errorClass}: ${String(primaryError.message || '').slice(0, 120)}); served by fallback provider ${fallbackProvider}`);
|
|
337
|
+
}
|
|
338
|
+
return { ...result, fallbackFrom: failedProvider, fallbackReason: errorClass };
|
|
339
|
+
} catch (fallbackErr) {
|
|
340
|
+
primaryError.fallbackError = String(fallbackErr?.message || fallbackErr || '');
|
|
341
|
+
throw primaryError;
|
|
217
342
|
}
|
|
218
343
|
}
|
|
219
344
|
|
|
220
345
|
module.exports = {
|
|
221
346
|
callBackgroundLlm,
|
|
222
347
|
extractBackgroundLlmText,
|
|
348
|
+
getWalleDefaultSelection,
|
|
223
349
|
providerRuntimeState,
|
|
224
350
|
resolveBackgroundLlmSelection,
|
|
351
|
+
_resetWalleDefaultsCache,
|
|
225
352
|
};
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Branch + worktree inventory: classify every local branch and worktree against
|
|
4
|
+
// the set of refs that any CTM session ever touched (live sessions, startup_tasks,
|
|
5
|
+
// agent_sessions). A ref is "orphaned" when NO session — live or in the DB — ever
|
|
6
|
+
// referenced it. Cleanup safety is conservative: only merged orphans are auto-safe.
|
|
7
|
+
//
|
|
8
|
+
// `classifyInventory` is PURE (no git, no DB) so it is fully unit-testable; the
|
|
9
|
+
// caller supplies git facts (branches/worktrees/mergedSet) and the linked-ref set.
|
|
10
|
+
// `gatherGitFacts` is the thin async git layer.
|
|
11
|
+
|
|
12
|
+
function normalizeBranch(value) {
|
|
13
|
+
return String(value || '').trim().replace(/^refs\/heads\//, '');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function isMainBranch(ref) {
|
|
17
|
+
const b = normalizeBranch(ref);
|
|
18
|
+
return b === 'main' || b === 'master';
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// CTM-internal refs that should never be swept by the bulk "Clean orphaned (safe)"
|
|
22
|
+
// action even when merged + session-less: auto-snapshot/sync branches CTM creates
|
|
23
|
+
// (`ctm/pre-sync/*`) and underscore-prefixed bookkeeping refs (`_refs/...`,
|
|
24
|
+
// `_remote_main`). They still classify as orphaned (visible) — just not auto-safe.
|
|
25
|
+
function isInternalRef(ref) {
|
|
26
|
+
const b = normalizeBranch(ref);
|
|
27
|
+
if (!b) return false;
|
|
28
|
+
return b.startsWith('_') || b.startsWith('ctm/pre-sync/');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Normalize a path for set membership compares. Callers should pass realpath-
|
|
32
|
+
// resolved paths when possible; this only strips trailing separators.
|
|
33
|
+
function normalizePathKey(p) {
|
|
34
|
+
const s = String(p || '').trim();
|
|
35
|
+
if (!s) return '';
|
|
36
|
+
return s.replace(/[\\/]+$/, '');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function ageDaysFrom(iso, now) {
|
|
40
|
+
if (!iso) return null;
|
|
41
|
+
const t = new Date(iso).getTime();
|
|
42
|
+
if (!Number.isFinite(t)) return null;
|
|
43
|
+
return Math.max(0, Math.floor((now - t) / 86400000));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @param {object} input
|
|
48
|
+
* @param {Array<{ref,head,committerDate}>} input.branches all local branches
|
|
49
|
+
* @param {Array<object>} input.worktrees rich worktrees (live)
|
|
50
|
+
* @param {Set<string>} input.mergedSet branch names merged into main
|
|
51
|
+
* @param {object} input.linked { branches:[{ref,source,sessionId,label,status}],
|
|
52
|
+
* worktreePaths:[{path,source,sessionId,label,status}] }
|
|
53
|
+
* @param {Set<string>} input.liveBranches branch names used by live sessions now
|
|
54
|
+
* @param {Set<string>} input.liveWorktreePaths worktree paths used by live sessions now
|
|
55
|
+
* @param {number} input.now ms epoch (caller-supplied; deterministic)
|
|
56
|
+
*/
|
|
57
|
+
function classifyInventory(input = {}) {
|
|
58
|
+
const {
|
|
59
|
+
branches = [],
|
|
60
|
+
worktrees = [],
|
|
61
|
+
mergedSet = new Set(),
|
|
62
|
+
linked = {},
|
|
63
|
+
liveBranches = new Set(),
|
|
64
|
+
liveWorktreePaths = new Set(),
|
|
65
|
+
now = 0,
|
|
66
|
+
} = input;
|
|
67
|
+
|
|
68
|
+
// Index the linked-ref set.
|
|
69
|
+
const linkedBranch = new Map(); // ref -> {source, sessionId, label, status}
|
|
70
|
+
for (const b of (linked.branches || [])) {
|
|
71
|
+
const ref = normalizeBranch(b.ref);
|
|
72
|
+
if (ref && !linkedBranch.has(ref)) linkedBranch.set(ref, b);
|
|
73
|
+
}
|
|
74
|
+
const linkedWtByPath = new Map();
|
|
75
|
+
for (const w of (linked.worktreePaths || [])) {
|
|
76
|
+
const key = normalizePathKey(w.path);
|
|
77
|
+
if (key && !linkedWtByPath.has(key)) linkedWtByPath.set(key, w);
|
|
78
|
+
}
|
|
79
|
+
const liveBranchSet = new Set([...liveBranches].map(normalizeBranch).filter(Boolean));
|
|
80
|
+
const liveWtSet = new Set([...liveWorktreePaths].map(normalizePathKey).filter(Boolean));
|
|
81
|
+
|
|
82
|
+
// Live worktree truth: branch -> worktree.
|
|
83
|
+
const wtByBranch = new Map();
|
|
84
|
+
for (const wt of worktrees) {
|
|
85
|
+
const ref = normalizeBranch(wt && wt.branch);
|
|
86
|
+
if (ref) wtByBranch.set(ref, wt);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function linkInfoForBranch(ref, wtPathKey) {
|
|
90
|
+
const hasLive = liveBranchSet.has(ref) || (!!wtPathKey && liveWtSet.has(wtPathKey));
|
|
91
|
+
const rec = linkedBranch.get(ref)
|
|
92
|
+
|| (wtPathKey ? linkedWtByPath.get(wtPathKey) : null)
|
|
93
|
+
|| null;
|
|
94
|
+
const isLinked = hasLive || !!rec;
|
|
95
|
+
let source = '';
|
|
96
|
+
if (hasLive) source = 'live_session';
|
|
97
|
+
else if (rec) source = rec.source || 'session';
|
|
98
|
+
return {
|
|
99
|
+
linked: isLinked,
|
|
100
|
+
hasLiveSession: hasLive,
|
|
101
|
+
linkedSource: source,
|
|
102
|
+
sessionId: rec ? (rec.sessionId || '') : '',
|
|
103
|
+
sessionLabel: rec ? (rec.label || '') : '',
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---- Classify worktrees (decorate, don't replace existing rich fields) ----
|
|
108
|
+
const classifiedWorktrees = worktrees.map(wt => {
|
|
109
|
+
const ref = normalizeBranch(wt && wt.branch);
|
|
110
|
+
const wtPathKey = normalizePathKey(wt && wt.path);
|
|
111
|
+
const info = linkInfoForBranch(ref, wtPathKey);
|
|
112
|
+
const isMain = !!(wt && (wt.isMain === true || isMainBranch(ref)));
|
|
113
|
+
const merged = ref ? mergedSet.has(ref) : false;
|
|
114
|
+
const isGhost = !!(wt && wt.isGhost);
|
|
115
|
+
const isOrphan = !isMain && !info.linked && !isGhost;
|
|
116
|
+
const dirty = Number((wt && wt.dirtyFiles) || 0);
|
|
117
|
+
const internal = isInternalRef(ref);
|
|
118
|
+
return {
|
|
119
|
+
...wt,
|
|
120
|
+
orphan: isOrphan,
|
|
121
|
+
orphanMerged: merged,
|
|
122
|
+
internal,
|
|
123
|
+
linked: info.linked,
|
|
124
|
+
hasLiveSession: info.hasLiveSession,
|
|
125
|
+
linkedSource: info.linkedSource,
|
|
126
|
+
orphanSessionId: info.sessionId,
|
|
127
|
+
orphanSessionLabel: info.sessionLabel,
|
|
128
|
+
safeToClean: isOrphan && merged && dirty === 0 && !internal,
|
|
129
|
+
};
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// ---- Classify branches (all local branches, incl. those with worktrees) ----
|
|
133
|
+
const classifiedBranches = branches.map(b => {
|
|
134
|
+
const ref = normalizeBranch(b.ref);
|
|
135
|
+
const wt = wtByBranch.get(ref) || null;
|
|
136
|
+
const wtPathKey = wt ? normalizePathKey(wt.path) : '';
|
|
137
|
+
const info = linkInfoForBranch(ref, wtPathKey);
|
|
138
|
+
const isMain = isMainBranch(ref);
|
|
139
|
+
const merged = mergedSet.has(ref);
|
|
140
|
+
const hasWorktree = !!wt;
|
|
141
|
+
const isOrphan = !isMain && !info.linked;
|
|
142
|
+
const internal = isInternalRef(ref);
|
|
143
|
+
return {
|
|
144
|
+
ref,
|
|
145
|
+
head: b.head || '',
|
|
146
|
+
ageDays: ageDaysFrom(b.committerDate, now),
|
|
147
|
+
committerDate: b.committerDate || '',
|
|
148
|
+
hasWorktree,
|
|
149
|
+
worktreePath: wt ? wt.path : '',
|
|
150
|
+
merged,
|
|
151
|
+
isMain,
|
|
152
|
+
internal,
|
|
153
|
+
linked: info.linked,
|
|
154
|
+
hasLiveSession: info.hasLiveSession,
|
|
155
|
+
linkedSource: info.linkedSource,
|
|
156
|
+
sessionId: info.sessionId,
|
|
157
|
+
sessionLabel: info.sessionLabel,
|
|
158
|
+
isOrphan,
|
|
159
|
+
// Bare-branch deletion is auto-safe only when merged + orphaned + no
|
|
160
|
+
// worktree, and not a CTM-internal snapshot/bookkeeping ref.
|
|
161
|
+
safeToClean: isOrphan && merged && !hasWorktree && !internal,
|
|
162
|
+
};
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
const orphanWorktreeRefs = new Set(
|
|
166
|
+
classifiedWorktrees.filter(w => w.orphan && w.branch).map(w => normalizeBranch(w.branch))
|
|
167
|
+
);
|
|
168
|
+
const counts = {
|
|
169
|
+
totalBranches: classifiedBranches.length,
|
|
170
|
+
bareBranches: classifiedBranches.filter(b => !b.hasWorktree && !b.isMain).length,
|
|
171
|
+
orphanBranches: classifiedBranches.filter(b => b.isOrphan).length,
|
|
172
|
+
safeBranches: classifiedBranches.filter(b => b.safeToClean).length,
|
|
173
|
+
orphanWorktrees: classifiedWorktrees.filter(w => w.orphan).length,
|
|
174
|
+
safeWorktrees: classifiedWorktrees.filter(w => w.safeToClean).length,
|
|
175
|
+
};
|
|
176
|
+
// orphanTotal counts distinct refs (a branch + its worktree are one thing).
|
|
177
|
+
counts.orphanTotal = counts.orphanWorktrees
|
|
178
|
+
+ classifiedBranches.filter(b => b.isOrphan && !orphanWorktreeRefs.has(b.ref)).length;
|
|
179
|
+
counts.safeTotal = counts.safeBranches + counts.safeWorktrees;
|
|
180
|
+
|
|
181
|
+
return { branches: classifiedBranches, worktrees: classifiedWorktrees, counts };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Gather raw git facts needed for classification. `git` is git-utils' `git(cwd,args)`.
|
|
186
|
+
* Returns { branches:[{ref,head,committerDate}], mergedSet:Set }.
|
|
187
|
+
*/
|
|
188
|
+
async function gatherGitFacts(cwd, git, mainBranch = 'main') {
|
|
189
|
+
const SEP = String.fromCharCode(31); // unit separator (iso8601 dates contain spaces)
|
|
190
|
+
const fmt = `%(refname:short)${SEP}%(objectname:short)${SEP}%(committerdate:iso8601)`;
|
|
191
|
+
const [refsOut, mergedOut] = await Promise.all([
|
|
192
|
+
git(cwd, ['for-each-ref', `--format=${fmt}`, 'refs/heads']).catch(() => ''),
|
|
193
|
+
git(cwd, ['branch', '--merged', mainBranch, '--format=%(refname:short)']).catch(() => ''),
|
|
194
|
+
]);
|
|
195
|
+
const branches = String(refsOut || '').split('\n').filter(Boolean).map(line => {
|
|
196
|
+
const [ref, head, committerDate] = line.split(SEP);
|
|
197
|
+
return { ref: normalizeBranch(ref), head: head || '', committerDate: committerDate || '' };
|
|
198
|
+
});
|
|
199
|
+
const mergedSet = new Set(
|
|
200
|
+
String(mergedOut || '').split('\n').map(s => normalizeBranch(s)).filter(Boolean)
|
|
201
|
+
);
|
|
202
|
+
return { branches, mergedSet };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
module.exports = {
|
|
206
|
+
classifyInventory,
|
|
207
|
+
gatherGitFacts,
|
|
208
|
+
normalizeBranch,
|
|
209
|
+
isMainBranch,
|
|
210
|
+
isInternalRef,
|
|
211
|
+
normalizePathKey,
|
|
212
|
+
};
|
|
@@ -245,10 +245,22 @@ function readCacheConversations(appDir) {
|
|
|
245
245
|
try { stat = fs.statSync(filePath); } catch { continue; }
|
|
246
246
|
if (!stat.isFile() || stat.size <= 0 || stat.size > MAX_CACHE_FILE_SIZE) continue;
|
|
247
247
|
|
|
248
|
+
// Probe only the 4096-byte HEAD first: the Electron HTTP cache holds hundreds of files
|
|
249
|
+
// and almost none are chat conversations. Reading every file IN FULL just to test the
|
|
250
|
+
// head was ~15s of synchronous readFileSync at boot (named by the fs-census:
|
|
251
|
+
// readFileSync@claude-desktop-sessions.js — 408 full reads). Read the head via one
|
|
252
|
+
// bounded readSync, and only fall back to a full read when the marker actually matches.
|
|
248
253
|
let buf;
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
254
|
+
let fd = -1;
|
|
255
|
+
try {
|
|
256
|
+
fd = fs.openSync(filePath, 'r');
|
|
257
|
+
const headLen = Math.min(stat.size, 4096);
|
|
258
|
+
const headBuf = Buffer.allocUnsafe(headLen);
|
|
259
|
+
const n = fs.readSync(fd, headBuf, 0, headLen, 0); // only inspect the bytes actually read
|
|
260
|
+
if (!/chat_conversations/i.test(headBuf.subarray(0, n).toString('latin1'))) { fs.closeSync(fd); continue; }
|
|
261
|
+
buf = fs.readFileSync(filePath); // marker present → now read the whole file
|
|
262
|
+
} catch { if (fd >= 0) { try { fs.closeSync(fd); } catch {} } continue; }
|
|
263
|
+
try { fs.closeSync(fd); } catch {}
|
|
252
264
|
|
|
253
265
|
const json = extractJsonFromCacheBuffer(buf);
|
|
254
266
|
if (!json) continue;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Safe coalescing of obsolete synchronized TUI frames.
|
|
3
|
+
//
|
|
4
|
+
// A synchronized frame is the byte span from `\x1b[?2026h` (begin synchronized
|
|
5
|
+
// update) to the matching `\x1b[?2026l` (end synchronized update). Within a
|
|
6
|
+
// single output batch a TUI may emit many such frames.
|
|
7
|
+
//
|
|
8
|
+
// THE ONLY SAFE COALESCING RULE (provably correct, no information loss):
|
|
9
|
+
// A synchronized frame that BEGINS with a full-screen erase (`\x1b[2J`,
|
|
10
|
+
// optionally accompanied by `\x1b[3J` scrollback-erase and/or a cursor-home
|
|
11
|
+
// `\x1b[H` / `\x1b[1;1H`) WIPES the entire screen. Therefore every COMPLETE
|
|
12
|
+
// synchronized frame that PRECEDES the LAST such full-clear frame in the
|
|
13
|
+
// batch is fully superseded and can be dropped without information loss.
|
|
14
|
+
//
|
|
15
|
+
// Partial-update frames (no leading full clear) are NEVER dropped unless a
|
|
16
|
+
// LATER full-clear supersedes them. This preserves the picker-clear /
|
|
17
|
+
// styling-restore / prompt-repair behavior that "latest frame wins" would
|
|
18
|
+
// destroy. Frames AFTER the last full-clear are always preserved (they paint
|
|
19
|
+
// on top of the cleared screen).
|
|
20
|
+
//
|
|
21
|
+
// Parsing is index-scan based (no catastrophic-backtracking regex) so huge
|
|
22
|
+
// batches with hundreds of frames stay cheap.
|
|
23
|
+
|
|
24
|
+
const SYNC_ON = '\x1b[?2026h';
|
|
25
|
+
const SYNC_OFF = '\x1b[?2026l';
|
|
26
|
+
|
|
27
|
+
// Matches a leading full-screen erase region: any ordering/combination of
|
|
28
|
+
// cursor-home + `\x1b[2J` (+ optional `\x1b[3J`), with optional surrounding
|
|
29
|
+
// home moves. We require at least one `\x1b[2J`. Bounded, linear-time regex
|
|
30
|
+
// anchored at start of the post-SYNC_ON content.
|
|
31
|
+
const CURSOR_HOME_RE = /^(?:\x1b\[H|\x1b\[1;1H)/;
|
|
32
|
+
const ERASE_DISPLAY_2J_RE = /^\x1b\[2J/;
|
|
33
|
+
const ERASE_SCROLLBACK_3J_RE = /^\x1b\[3J/;
|
|
34
|
+
|
|
35
|
+
// Returns true if `content` (the bytes immediately following `\x1b[?2026h`)
|
|
36
|
+
// begins with a full-screen clear, tolerating leading/interleaved cursor-home
|
|
37
|
+
// and scrollback-erase sequences around the mandatory `\x1b[2J`.
|
|
38
|
+
function beginsWithFullScreenClear(content) {
|
|
39
|
+
let i = 0;
|
|
40
|
+
let saw2J = false;
|
|
41
|
+
// Consume any leading run of cursor-home / 2J / 3J sequences. We only treat
|
|
42
|
+
// the frame as a full clear if a `\x1b[2J` appears in that leading run.
|
|
43
|
+
// Allowing 3J/home before or after 2J covers `\x1b[2J\x1b[3J\x1b[H`,
|
|
44
|
+
// `\x1b[3J\x1b[2J`, `\x1b[H\x1b[2J`, etc.
|
|
45
|
+
for (;;) {
|
|
46
|
+
const rest = content.slice(i);
|
|
47
|
+
let m;
|
|
48
|
+
if ((m = ERASE_DISPLAY_2J_RE.exec(rest))) {
|
|
49
|
+
saw2J = true;
|
|
50
|
+
i += m[0].length;
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
if ((m = ERASE_SCROLLBACK_3J_RE.exec(rest))) {
|
|
54
|
+
i += m[0].length;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if ((m = CURSOR_HOME_RE.exec(rest))) {
|
|
58
|
+
i += m[0].length;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
break;
|
|
62
|
+
}
|
|
63
|
+
return saw2J;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Tokenize `batch` into ordered segments. Each segment is one of:
|
|
67
|
+
// { type: 'frame', text, fullClear } — a COMPLETE synchronized frame
|
|
68
|
+
// { type: 'raw', text } — inter-frame bytes, OR a trailing
|
|
69
|
+
// INCOMPLETE frame (SYNC_ON with no
|
|
70
|
+
// following SYNC_OFF)
|
|
71
|
+
function tokenize(batch) {
|
|
72
|
+
const segments = [];
|
|
73
|
+
let pos = 0;
|
|
74
|
+
const len = batch.length;
|
|
75
|
+
while (pos < len) {
|
|
76
|
+
const onIdx = batch.indexOf(SYNC_ON, pos);
|
|
77
|
+
if (onIdx === -1) {
|
|
78
|
+
// No more frames — remaining bytes are raw.
|
|
79
|
+
segments.push({ type: 'raw', text: batch.slice(pos) });
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
// Raw bytes before this frame's SYNC_ON.
|
|
83
|
+
if (onIdx > pos) {
|
|
84
|
+
segments.push({ type: 'raw', text: batch.slice(pos, onIdx) });
|
|
85
|
+
}
|
|
86
|
+
const contentStart = onIdx + SYNC_ON.length;
|
|
87
|
+
const offIdx = batch.indexOf(SYNC_OFF, contentStart);
|
|
88
|
+
if (offIdx === -1) {
|
|
89
|
+
// Trailing INCOMPLETE frame — keep as raw, never drop.
|
|
90
|
+
segments.push({ type: 'raw', text: batch.slice(onIdx) });
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
const frameEnd = offIdx + SYNC_OFF.length;
|
|
94
|
+
const content = batch.slice(contentStart, offIdx);
|
|
95
|
+
segments.push({
|
|
96
|
+
type: 'frame',
|
|
97
|
+
text: batch.slice(onIdx, frameEnd),
|
|
98
|
+
fullClear: beginsWithFullScreenClear(content),
|
|
99
|
+
});
|
|
100
|
+
pos = frameEnd;
|
|
101
|
+
}
|
|
102
|
+
return segments;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Coalesce obsolete synchronized frames in `batch`.
|
|
106
|
+
// Returns a (possibly shorter) string. Never throws; returns the original
|
|
107
|
+
// batch unchanged when there is nothing safe to drop.
|
|
108
|
+
function coalesceSyncFrames(batch) {
|
|
109
|
+
const input = String(batch == null ? '' : batch);
|
|
110
|
+
if (!input) return input;
|
|
111
|
+
// Cheap bail-out: need at least two SYNC_ON markers AND a full clear marker
|
|
112
|
+
// for any coalescing to be possible.
|
|
113
|
+
if (input.indexOf('\x1b[2J') === -1) return input;
|
|
114
|
+
|
|
115
|
+
const segments = tokenize(input);
|
|
116
|
+
|
|
117
|
+
// Find index of the LAST complete frame that begins with a full-screen clear.
|
|
118
|
+
let lastFullClearIdx = -1;
|
|
119
|
+
for (let i = 0; i < segments.length; i++) {
|
|
120
|
+
const seg = segments[i];
|
|
121
|
+
if (seg.type === 'frame' && seg.fullClear) lastFullClearIdx = i;
|
|
122
|
+
}
|
|
123
|
+
if (lastFullClearIdx === -1) return input; // no full-clear frame → unchanged.
|
|
124
|
+
|
|
125
|
+
// Are there any COMPLETE frames before the full-clear frame to drop?
|
|
126
|
+
let droppable = 0;
|
|
127
|
+
for (let i = 0; i < lastFullClearIdx; i++) {
|
|
128
|
+
if (segments[i].type === 'frame') droppable++;
|
|
129
|
+
}
|
|
130
|
+
if (droppable === 0) return input; // nothing superseded → unchanged.
|
|
131
|
+
|
|
132
|
+
// Rebuild: drop COMPLETE frames before the last full-clear frame. Keep
|
|
133
|
+
// everything else (raw segments, the full-clear frame, and all segments
|
|
134
|
+
// after it) in place.
|
|
135
|
+
let out = '';
|
|
136
|
+
for (let i = 0; i < segments.length; i++) {
|
|
137
|
+
const seg = segments[i];
|
|
138
|
+
if (i < lastFullClearIdx && seg.type === 'frame') continue; // superseded
|
|
139
|
+
out += seg.text;
|
|
140
|
+
}
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
module.exports = {
|
|
145
|
+
coalesceSyncFrames,
|
|
146
|
+
// exported for unit testing of the internals
|
|
147
|
+
_tokenize: tokenize,
|
|
148
|
+
_beginsWithFullScreenClear: beginsWithFullScreenClear,
|
|
149
|
+
SYNC_ON,
|
|
150
|
+
SYNC_OFF,
|
|
151
|
+
};
|