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,382 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Resilient default-provider client.
|
|
5
|
+
*
|
|
6
|
+
* `getDefaultClient()` historically resolved a SINGLE provider (the user's
|
|
7
|
+
* `walle_provider`) and returned a bare client with no fallback — so if that
|
|
8
|
+
* one provider was unreachable (e.g. an off-VPN gateway returning "fetch
|
|
9
|
+
* failed"), every Wall-E surface that uses the default (chat, coding, skills,
|
|
10
|
+
* background loops) failed outright, even when the user had another working
|
|
11
|
+
* provider configured.
|
|
12
|
+
*
|
|
13
|
+
* This module wraps an ORDERED chain of provider clients:
|
|
14
|
+
* 1. the user's chosen default,
|
|
15
|
+
* 2. any other enabled+credentialed provider they've set up (e.g. deepseek),
|
|
16
|
+
* 3. the local coding-agent CLIs (claude-cli, then codex-cli) when installed.
|
|
17
|
+
*
|
|
18
|
+
* Per call it retries transient failures on the current provider, then falls
|
|
19
|
+
* through to the next candidate on connectivity/auth failures, and notifies
|
|
20
|
+
* when it had to fall back (or when nothing was reachable). Genuine request
|
|
21
|
+
* errors (bad request, content) are surfaced immediately rather than masked by
|
|
22
|
+
* trying a different provider.
|
|
23
|
+
*
|
|
24
|
+
* The module is dependency-injected (no direct brain/network/child_process
|
|
25
|
+
* coupling in the core logic) so it is unit-testable with fakes.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
const { spawnSync } = require('child_process');
|
|
29
|
+
const { classifyProviderError } = require('./provider-error');
|
|
30
|
+
|
|
31
|
+
// Failure classes (from provider-error.js classifyProviderError) that are worth
|
|
32
|
+
// retrying on the SAME provider — they're usually transient.
|
|
33
|
+
const TRANSIENT_CLASSES = new Set(['network', 'timeout', 'provider_unavailable', 'rate_limited']);
|
|
34
|
+
// Failure classes that mean "this provider can't serve the request right now" —
|
|
35
|
+
// fall through to the next candidate. auth_error is included (misconfigured /
|
|
36
|
+
// expired credential) but is NOT retried on the same provider; likewise
|
|
37
|
+
// quota_exceeded (depleted account/credits, e.g. DeepSeek "402 Insufficient
|
|
38
|
+
// Balance") — retrying the same provider can't help, but another candidate can
|
|
39
|
+
// serve. Anything else (invalid_request, content filter, unknown) is a real
|
|
40
|
+
// error we must surface.
|
|
41
|
+
const FALLTHROUGH_CLASSES = new Set(['network', 'timeout', 'provider_unavailable', 'rate_limited', 'auth_error', 'quota_exceeded']);
|
|
42
|
+
|
|
43
|
+
function classify(err) {
|
|
44
|
+
try {
|
|
45
|
+
return classifyProviderError(err)?.type || 'unknown';
|
|
46
|
+
} catch {
|
|
47
|
+
return 'unknown';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isTransientClass(cls) { return TRANSIENT_CLASSES.has(cls); }
|
|
52
|
+
function shouldFallThroughClass(cls) { return FALLTHROUGH_CLASSES.has(cls); }
|
|
53
|
+
function defaultDelay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }
|
|
54
|
+
|
|
55
|
+
// A request that was CANCELLED (caller aborted, or an AbortError) is not a provider-health
|
|
56
|
+
// signal — the caller no longer wants a result. classifyProviderError() lumps "aborted" into
|
|
57
|
+
// 'timeout' (transient), which would otherwise make us retry + fall through the whole chain
|
|
58
|
+
// (re-aborting on each, since they share the aborted signal) and falsely report "every
|
|
59
|
+
// provider unreachable". So aborts must propagate immediately: no retry, no fallthrough, no
|
|
60
|
+
// health-failure, no notice.
|
|
61
|
+
function isAbortError(err, opts) {
|
|
62
|
+
if (opts && opts.signal && opts.signal.aborted) return true;
|
|
63
|
+
if (!err) return false;
|
|
64
|
+
if (err.name === 'AbortError' || err.code === 'ABORT_ERR') return true;
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const _binCache = new Map();
|
|
69
|
+
// Is a CLI binary (claude / codex) on PATH? Memoized — presence doesn't change
|
|
70
|
+
// within a process. Injectable spawn/platform for tests.
|
|
71
|
+
function isCliBinaryAvailable(bin, { spawn = spawnSync, platform = process.platform, cache = _binCache } = {}) {
|
|
72
|
+
if (cache.has(bin)) return cache.get(bin);
|
|
73
|
+
let ok = false;
|
|
74
|
+
try {
|
|
75
|
+
const probe = platform === 'win32' ? 'where' : 'which';
|
|
76
|
+
const result = spawn(probe, [bin], { stdio: 'ignore', timeout: 3000 });
|
|
77
|
+
ok = !!result && result.status === 0;
|
|
78
|
+
} catch {
|
|
79
|
+
ok = false;
|
|
80
|
+
}
|
|
81
|
+
cache.set(bin, ok);
|
|
82
|
+
return ok;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Wrap an ordered chain of candidates into a single resilient client.
|
|
87
|
+
*
|
|
88
|
+
* @param {Array<{id:string,type:string,label?:string,client:object}>} chain
|
|
89
|
+
* Ordered candidates (index 0 = the user's default). Each `client` is a
|
|
90
|
+
* normal provider client (chat / chatStream / capabilities / type).
|
|
91
|
+
* @param {object} deps
|
|
92
|
+
* @param {object} [deps.health] ProviderHealth instance (trackCall used).
|
|
93
|
+
* @param {Function} [deps.isDown] (id) => bool — skip providers already down.
|
|
94
|
+
* @param {Function} [deps.onFallback] ({from,to,error}) => void — fired once when
|
|
95
|
+
* a non-primary candidate ends up serving the call.
|
|
96
|
+
* @param {Function} [deps.onExhausted] ({chain,error}) => void — every candidate failed.
|
|
97
|
+
* @param {number} [deps.maxRetries=2] Per-candidate transient retries.
|
|
98
|
+
* @param {number} [deps.baseDelayMs=400] Backoff base (exponential).
|
|
99
|
+
* @param {Function} [deps.sleep] Injectable delay (tests).
|
|
100
|
+
*/
|
|
101
|
+
function makeResilientClient(chain, deps = {}) {
|
|
102
|
+
if (!Array.isArray(chain) || chain.length === 0) {
|
|
103
|
+
throw new Error('makeResilientClient: chain must be a non-empty array');
|
|
104
|
+
}
|
|
105
|
+
const health = deps.health || null;
|
|
106
|
+
const isDown = typeof deps.isDown === 'function' ? deps.isDown : () => false;
|
|
107
|
+
const maxRetries = Number.isInteger(deps.maxRetries) ? deps.maxRetries : 2;
|
|
108
|
+
const baseDelayMs = Number.isFinite(deps.baseDelayMs) ? deps.baseDelayMs : 400;
|
|
109
|
+
const onFallback = typeof deps.onFallback === 'function' ? deps.onFallback : () => {};
|
|
110
|
+
const onPrimaryOk = typeof deps.onPrimaryOk === 'function' ? deps.onPrimaryOk : () => {};
|
|
111
|
+
const onExhausted = typeof deps.onExhausted === 'function' ? deps.onExhausted : () => {};
|
|
112
|
+
const sleep = typeof deps.sleep === 'function' ? deps.sleep : defaultDelay;
|
|
113
|
+
const primary = chain[0];
|
|
114
|
+
|
|
115
|
+
function track(id, ok, err) {
|
|
116
|
+
try { if (health && typeof health.trackCall === 'function') health.trackCall(id, ok, err); } catch {}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// The caller's opts.model belongs to the PRIMARY provider. When we fall through
|
|
120
|
+
// to a different provider it must use ITS own default model, not the primary's
|
|
121
|
+
// (e.g. don't hand "deepseek-chat" to the claude CLI). The primary keeps the
|
|
122
|
+
// caller's model verbatim.
|
|
123
|
+
function optsForCandidate(cand, opts) {
|
|
124
|
+
if (cand.id === primary.id) return opts;
|
|
125
|
+
const next = { ...opts };
|
|
126
|
+
if (cand.model) next.model = cand.model;
|
|
127
|
+
else delete next.model;
|
|
128
|
+
return next;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Active candidates for this call: drop providers health marked 'down', and —
|
|
132
|
+
// when the call needs tool calls — drop providers that can't do tools (e.g. the
|
|
133
|
+
// claude-cli tail, capabilities.tools === false), leaving those to the coding
|
|
134
|
+
// orchestrator's own CLI fallback. Never return empty: fall back to the full
|
|
135
|
+
// chain so we always attempt something.
|
|
136
|
+
function activeChain(opts = {}) {
|
|
137
|
+
const wantsTools = Array.isArray(opts.tools) && opts.tools.length > 0;
|
|
138
|
+
let cands = chain.filter((c) => !isDown(c.id));
|
|
139
|
+
if (wantsTools) {
|
|
140
|
+
const toolCapable = cands.filter((c) => c.client?.capabilities?.tools !== false);
|
|
141
|
+
if (toolCapable.length) cands = toolCapable;
|
|
142
|
+
}
|
|
143
|
+
return cands.length ? cands : chain.slice(-1);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Run one candidate with transient retries. Returns {ok, res} or {ok:false, err, cls}.
|
|
147
|
+
async function runCandidate(cand, callOpts, invoke) {
|
|
148
|
+
let lastErr;
|
|
149
|
+
let lastCls = 'unknown';
|
|
150
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
151
|
+
try {
|
|
152
|
+
const res = await invoke(cand.client, callOpts);
|
|
153
|
+
track(cand.id, true);
|
|
154
|
+
return { ok: true, res };
|
|
155
|
+
} catch (err) {
|
|
156
|
+
// Caller cancelled — propagate at once (no retry/fallthrough/health-failure).
|
|
157
|
+
if (isAbortError(err, callOpts)) throw err;
|
|
158
|
+
const cls = classify(err);
|
|
159
|
+
lastErr = err;
|
|
160
|
+
lastCls = cls;
|
|
161
|
+
track(cand.id, false, err);
|
|
162
|
+
if (isTransientClass(cls) && attempt < maxRetries) {
|
|
163
|
+
await sleep(baseDelayMs * (2 ** attempt));
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
return { ok: false, err, cls };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return { ok: false, err: lastErr, cls: lastCls };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async function callWithFallback(opts, invoke) {
|
|
173
|
+
const cands = activeChain(opts);
|
|
174
|
+
let lastErr;
|
|
175
|
+
for (let i = 0; i < cands.length; i++) {
|
|
176
|
+
const cand = cands[i];
|
|
177
|
+
const out = await runCandidate(cand, optsForCandidate(cand, opts), invoke);
|
|
178
|
+
if (out.ok) {
|
|
179
|
+
if (cand.id !== primary.id) onFallback({ from: primary, to: cand, error: lastErr });
|
|
180
|
+
else onPrimaryOk({ provider: cand });
|
|
181
|
+
return out.res;
|
|
182
|
+
}
|
|
183
|
+
lastErr = out.err;
|
|
184
|
+
// A genuine request error (not connectivity/auth) shouldn't be masked by
|
|
185
|
+
// retrying a different provider — surface it.
|
|
186
|
+
if (!shouldFallThroughClass(out.cls)) throw out.err;
|
|
187
|
+
}
|
|
188
|
+
onExhausted({ chain: cands, error: lastErr });
|
|
189
|
+
throw lastErr || new Error('All default providers were unavailable');
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function* streamWithFallback(opts) {
|
|
193
|
+
const cands = activeChain(opts);
|
|
194
|
+
let lastErr;
|
|
195
|
+
for (let i = 0; i < cands.length; i++) {
|
|
196
|
+
const cand = cands[i];
|
|
197
|
+
const callOpts = optsForCandidate(cand, opts);
|
|
198
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
199
|
+
let started = false;
|
|
200
|
+
try {
|
|
201
|
+
for await (const chunk of cand.client.chatStream(callOpts)) {
|
|
202
|
+
if (!started) {
|
|
203
|
+
started = true;
|
|
204
|
+
track(cand.id, true);
|
|
205
|
+
if (cand.id !== primary.id) onFallback({ from: primary, to: cand, error: lastErr });
|
|
206
|
+
else onPrimaryOk({ provider: cand });
|
|
207
|
+
}
|
|
208
|
+
yield chunk;
|
|
209
|
+
}
|
|
210
|
+
return; // stream completed
|
|
211
|
+
} catch (err) {
|
|
212
|
+
// Caller cancelled — propagate at once (no retry/fallthrough/health-failure).
|
|
213
|
+
if (isAbortError(err, callOpts)) throw err;
|
|
214
|
+
const cls = classify(err);
|
|
215
|
+
track(cand.id, false, err);
|
|
216
|
+
// Mid-stream we can't switch providers without re-emitting — propagate.
|
|
217
|
+
if (started) throw err;
|
|
218
|
+
lastErr = err;
|
|
219
|
+
if (isTransientClass(cls) && attempt < maxRetries) {
|
|
220
|
+
await sleep(baseDelayMs * (2 ** attempt));
|
|
221
|
+
continue; // retry same candidate
|
|
222
|
+
}
|
|
223
|
+
if (!shouldFallThroughClass(cls)) throw err;
|
|
224
|
+
break; // move to next candidate
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
onExhausted({ chain: cands, error: lastErr });
|
|
229
|
+
throw lastErr || new Error('All default providers were unavailable');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const wrapper = {
|
|
233
|
+
type: primary.client.type,
|
|
234
|
+
capabilities: primary.client.capabilities,
|
|
235
|
+
_resilientChain: chain,
|
|
236
|
+
};
|
|
237
|
+
if (typeof primary.client.chat === 'function') {
|
|
238
|
+
wrapper.chat = (opts = {}) => callWithFallback(opts, (client, callOpts) => client.chat(callOpts));
|
|
239
|
+
}
|
|
240
|
+
wrapper.chatStream = async function* chatStream(opts = {}) {
|
|
241
|
+
yield* streamWithFallback(opts);
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
// Pass through any other provider methods/props (token counting, model lists,
|
|
245
|
+
// etc.) to the primary client so the wrapper is a drop-in replacement.
|
|
246
|
+
return new Proxy(wrapper, {
|
|
247
|
+
get(target, prop) {
|
|
248
|
+
if (prop in target) return target[prop];
|
|
249
|
+
const value = primary.client[prop];
|
|
250
|
+
return typeof value === 'function' ? value.bind(primary.client) : value;
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function _parseHeaders(value) {
|
|
256
|
+
if (!value) return undefined;
|
|
257
|
+
if (typeof value === 'object' && !Array.isArray(value)) return value;
|
|
258
|
+
if (typeof value !== 'string') return undefined;
|
|
259
|
+
try {
|
|
260
|
+
const parsed = JSON.parse(value);
|
|
261
|
+
return parsed && typeof parsed === 'object' ? parsed : undefined;
|
|
262
|
+
} catch {
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function _configFromRow(row = {}) {
|
|
268
|
+
const config = {};
|
|
269
|
+
if (row.api_key_encrypted) config.apiKey = row.api_key_encrypted;
|
|
270
|
+
if (row.base_url) config.baseUrl = row.base_url;
|
|
271
|
+
const headers = _parseHeaders(row.custom_headers);
|
|
272
|
+
if (headers && Object.keys(headers).length > 0) config.customHeaders = headers;
|
|
273
|
+
return config;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const _ENV_KEY_BY_TYPE = {
|
|
277
|
+
anthropic: ['ANTHROPIC_API_KEY', 'ANTHROPIC_AUTH_TOKEN'],
|
|
278
|
+
openai: ['OPENAI_API_KEY'],
|
|
279
|
+
google: ['GOOGLE_API_KEY', 'GEMINI_API_KEY'],
|
|
280
|
+
deepseek: ['DEEPSEEK_API_KEY'],
|
|
281
|
+
moonshot: ['MOONSHOT_API_KEY'],
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
function _hasEnvKey(type, env = process.env) {
|
|
285
|
+
const names = _ENV_KEY_BY_TYPE[String(type || '').toLowerCase()] || [];
|
|
286
|
+
return names.some((name) => !!env[name]);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Build the ordered candidate chain for the default client.
|
|
291
|
+
*
|
|
292
|
+
* @param {object} deps
|
|
293
|
+
* @param {object} deps.brain Wall-E brain (listModelProviders / getModelProviderWithKey).
|
|
294
|
+
* @param {Function} deps.createClient (type, config) => client.
|
|
295
|
+
* @param {Function} deps.buildPrimary () => {id,type,client} | null — the user's default,
|
|
296
|
+
* built by client.js with its full auth_method logic.
|
|
297
|
+
* @param {Function} [deps.isCliAvailable] (bin) => bool.
|
|
298
|
+
* @param {object} [deps.env]
|
|
299
|
+
* @returns {Array<{id,type,label,client}>}
|
|
300
|
+
*/
|
|
301
|
+
function buildDefaultProviderChain(deps = {}) {
|
|
302
|
+
const { brain, createClient, buildPrimary } = deps;
|
|
303
|
+
const isCliAvailable = deps.isCliAvailable || isCliBinaryAvailable;
|
|
304
|
+
// (type) => default model id for that provider. Fallback providers use their OWN
|
|
305
|
+
// default model, never the primary's. Returns undefined for CLI types (let the CLI choose).
|
|
306
|
+
const resolveModelForType = typeof deps.resolveModelForType === 'function' ? deps.resolveModelForType : () => undefined;
|
|
307
|
+
const env = deps.env || process.env;
|
|
308
|
+
const chain = [];
|
|
309
|
+
const seen = new Set();
|
|
310
|
+
|
|
311
|
+
function add(cand) {
|
|
312
|
+
if (!cand || !cand.client || !cand.id || seen.has(cand.id)) return;
|
|
313
|
+
seen.add(cand.id);
|
|
314
|
+
chain.push(cand);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// 1. The user's chosen default (built upstream with full auth handling).
|
|
318
|
+
let primaryType = null;
|
|
319
|
+
try {
|
|
320
|
+
const primary = typeof buildPrimary === 'function' ? buildPrimary() : null;
|
|
321
|
+
if (primary && primary.client && primary.id) {
|
|
322
|
+
add(primary);
|
|
323
|
+
primaryType = primary.type || null;
|
|
324
|
+
}
|
|
325
|
+
} catch {}
|
|
326
|
+
|
|
327
|
+
// 2. Other enabled, credentialed providers the user has set up — most recently
|
|
328
|
+
// updated first. Only api_key-auth rows with a usable key qualify here; CLI-
|
|
329
|
+
// auth providers are covered by the tail below.
|
|
330
|
+
try {
|
|
331
|
+
const rows = (typeof brain.listModelProviders === 'function' ? brain.listModelProviders() : []) || [];
|
|
332
|
+
const enabled = rows.filter((r) => r && r.enabled && r.type);
|
|
333
|
+
const withKeys = enabled.map((r) => {
|
|
334
|
+
try {
|
|
335
|
+
return (typeof brain.getModelProviderWithKey === 'function' && brain.getModelProviderWithKey(r.id)) || r;
|
|
336
|
+
} catch {
|
|
337
|
+
return r;
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
withKeys.sort((a, b) => String(b.updated_at || '').localeCompare(String(a.updated_at || '')));
|
|
341
|
+
for (const row of withKeys) {
|
|
342
|
+
const id = row.id || `${row.type}-default`;
|
|
343
|
+
if (seen.has(id)) continue;
|
|
344
|
+
if (row.type === primaryType) continue; // primary already represents this type
|
|
345
|
+
const authMethod = row.auth_method || 'api_key';
|
|
346
|
+
if (authMethod !== 'api_key') continue; // CLI/oauth handled elsewhere
|
|
347
|
+
if (!row.api_key_encrypted && !_hasEnvKey(row.type, env)) continue;
|
|
348
|
+
let client;
|
|
349
|
+
try {
|
|
350
|
+
client = createClient(row.type, _configFromRow(row));
|
|
351
|
+
} catch {
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
add({ id, type: row.type, label: row.name || row.type, client, model: resolveModelForType(row.type) });
|
|
355
|
+
}
|
|
356
|
+
} catch {}
|
|
357
|
+
|
|
358
|
+
// 3. Coding-agent CLI tail (subscription billing, no key, works off-VPN).
|
|
359
|
+
if (!seen.has('claude-cli') && isCliAvailable('claude')) {
|
|
360
|
+
try { add({ id: 'claude-cli', type: 'claude-cli', label: 'Claude CLI', client: createClient('claude-cli', {}), model: resolveModelForType('claude-cli') }); } catch {}
|
|
361
|
+
}
|
|
362
|
+
if (!seen.has('codex-cli') && isCliAvailable('codex')) {
|
|
363
|
+
try { add({ id: 'codex-cli', type: 'codex-cli', label: 'Codex CLI', client: createClient('codex-cli', {}), model: resolveModelForType('codex-cli') }); } catch {}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return chain;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
module.exports = {
|
|
370
|
+
makeResilientClient,
|
|
371
|
+
buildDefaultProviderChain,
|
|
372
|
+
isCliBinaryAvailable,
|
|
373
|
+
_internals: {
|
|
374
|
+
classify,
|
|
375
|
+
isTransientClass,
|
|
376
|
+
shouldFallThroughClass,
|
|
377
|
+
_configFromRow,
|
|
378
|
+
_hasEnvKey,
|
|
379
|
+
TRANSIENT_CLASSES,
|
|
380
|
+
FALLTHROUGH_CLASSES,
|
|
381
|
+
},
|
|
382
|
+
};
|
|
@@ -38,6 +38,24 @@ class ProviderHealth extends EventEmitter {
|
|
|
38
38
|
this._providers = new Map();
|
|
39
39
|
this._intervalId = null;
|
|
40
40
|
this._allDown = false;
|
|
41
|
+
// Most recent default-routing notice (set when the live default falls back to
|
|
42
|
+
// another provider/CLI, cleared when the default recovers). Surfaced to the user
|
|
43
|
+
// via getSystemHealth() → /api/models/health.
|
|
44
|
+
this._routingNotice = null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Record (or clear) the latest default-provider routing notice for the UI. */
|
|
48
|
+
recordRoutingNotice(notice) {
|
|
49
|
+
this._routingNotice = notice ? { ...notice, at: new Date().toISOString() } : null;
|
|
50
|
+
return this._routingNotice;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getRoutingNotice() {
|
|
54
|
+
return this._routingNotice;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
clearRoutingNotice() {
|
|
58
|
+
this._routingNotice = null;
|
|
41
59
|
}
|
|
42
60
|
|
|
43
61
|
_getOrCreate(providerId) {
|
|
@@ -168,6 +186,7 @@ class ProviderHealth extends EventEmitter {
|
|
|
168
186
|
providers,
|
|
169
187
|
hasHealthyProvider: healthyProviders.length > 0,
|
|
170
188
|
healthyProviders,
|
|
189
|
+
routingNotice: this._routingNotice,
|
|
171
190
|
};
|
|
172
191
|
}
|
|
173
192
|
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function _messageHasPayload(message) {
|
|
4
|
+
if (!message || typeof message !== 'object') return false;
|
|
5
|
+
const role = String(message.role || '').trim();
|
|
6
|
+
if (role !== 'user' && role !== 'assistant') return false;
|
|
7
|
+
const content = message.content;
|
|
8
|
+
if (typeof content === 'string') return content.length > 0;
|
|
9
|
+
if (!Array.isArray(content)) return false;
|
|
10
|
+
return content.some((part) => {
|
|
11
|
+
if (!part || typeof part !== 'object') return false;
|
|
12
|
+
if (part.type === 'text') return typeof part.text === 'string' && part.text.length > 0;
|
|
13
|
+
return Boolean(part.type);
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function _emptyMessagesError({ provider, model } = {}) {
|
|
18
|
+
const providerLabel = provider || 'unknown';
|
|
19
|
+
const err = new Error('Provider request aborted: messages must contain at least one non-empty chat message.');
|
|
20
|
+
err.code = 'WALLE_EMPTY_PROVIDER_MESSAGES';
|
|
21
|
+
err.status = 400;
|
|
22
|
+
err.providerError = {
|
|
23
|
+
code: 'AI_PROVIDER_ERROR',
|
|
24
|
+
type: 'request_invariant',
|
|
25
|
+
severity: 'error',
|
|
26
|
+
title: 'Wall-E request invariant failed',
|
|
27
|
+
message: 'Wall-E refused to send an empty message list to the provider.',
|
|
28
|
+
userMessage: `${providerLabel}${model ? ` (${model})` : ''}: Wall-E refused to send an empty model request. No provider call was made.`,
|
|
29
|
+
rawMessage: 'empty provider messages',
|
|
30
|
+
status: 400,
|
|
31
|
+
provider: provider || null,
|
|
32
|
+
model: model || null,
|
|
33
|
+
providerId: null,
|
|
34
|
+
registryId: null,
|
|
35
|
+
routeLabel: null,
|
|
36
|
+
connectionKind: null,
|
|
37
|
+
actionLabel: null,
|
|
38
|
+
actionUrl: null,
|
|
39
|
+
createdAt: new Date().toISOString(),
|
|
40
|
+
};
|
|
41
|
+
return err;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function assertProviderMessages(messages, context = {}) {
|
|
45
|
+
if (!Array.isArray(messages) || !messages.some(_messageHasPayload)) {
|
|
46
|
+
throw _emptyMessagesError(context);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function withProviderMessageGuard(provider) {
|
|
51
|
+
if (!provider || provider.__walleMessageGuarded === true) return provider;
|
|
52
|
+
const guarded = { ...provider, __walleMessageGuarded: true };
|
|
53
|
+
if (typeof provider.chat === 'function') {
|
|
54
|
+
guarded.chat = async function guardedChat(opts = {}) {
|
|
55
|
+
assertProviderMessages(opts.messages, {
|
|
56
|
+
provider: opts.provider || provider.type,
|
|
57
|
+
model: opts.model,
|
|
58
|
+
});
|
|
59
|
+
return provider.chat.call(provider, opts);
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
if (typeof provider.chatStream === 'function') {
|
|
63
|
+
guarded.chatStream = async function* guardedChatStream(opts = {}) {
|
|
64
|
+
assertProviderMessages(opts.messages, {
|
|
65
|
+
provider: opts.provider || provider.type,
|
|
66
|
+
model: opts.model,
|
|
67
|
+
});
|
|
68
|
+
yield* provider.chatStream.call(provider, opts);
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return guarded;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
assertProviderMessages,
|
|
76
|
+
withProviderMessageGuard,
|
|
77
|
+
_private: { messageHasPayload: _messageHasPayload },
|
|
78
|
+
};
|