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
|
@@ -105,7 +105,7 @@ function buildRawMessage(err) {
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function classifyType(status, lower) {
|
|
108
|
-
if (/insufficient[_ -]?quota|quota exceeded|quota_exceeded|resource[_ -]?exhausted|billing|credit|prepayment credits are depleted/i.test(lower)) return 'quota_exceeded';
|
|
108
|
+
if (/insufficient[_ -]?(quota|balance|funds)|quota exceeded|quota_exceeded|resource[_ -]?exhausted|billing|credit|prepayment credits are depleted/i.test(lower)) return 'quota_exceeded';
|
|
109
109
|
if (status === 429 || /rate[_ -]?limit|too many requests|too_many_requests|rate_limit/i.test(lower)) return 'rate_limited';
|
|
110
110
|
if (
|
|
111
111
|
status === 401
|
|
@@ -186,6 +186,10 @@ function classifyProviderError(err, context = {}) {
|
|
|
186
186
|
const copy = messageForType(type);
|
|
187
187
|
const provider = context.provider || err?.provider || err?.providerType || null;
|
|
188
188
|
const model = context.model || err?.model || null;
|
|
189
|
+
const providerId = context.providerId || err?.providerId || err?.provider_id || null;
|
|
190
|
+
const registryId = context.registryId || err?.registryId || err?.registry_id || null;
|
|
191
|
+
const routeLabel = context.routeLabel || err?.routeLabel || err?.route_label || null;
|
|
192
|
+
const connectionKind = context.connectionKind || err?.connectionKind || err?.connection_kind || null;
|
|
189
193
|
const providerName = providerLabel(provider);
|
|
190
194
|
const modelSuffix = model ? ` (${model})` : '';
|
|
191
195
|
const retryAfter = extractRetryAfter(err);
|
|
@@ -200,6 +204,10 @@ function classifyProviderError(err, context = {}) {
|
|
|
200
204
|
rawMessage,
|
|
201
205
|
status,
|
|
202
206
|
provider,
|
|
207
|
+
providerId,
|
|
208
|
+
registryId,
|
|
209
|
+
routeLabel,
|
|
210
|
+
connectionKind,
|
|
203
211
|
model,
|
|
204
212
|
retryAfter,
|
|
205
213
|
actionLabel: 'Open Setup',
|
|
@@ -251,6 +259,9 @@ function recordProviderFailureAlert(providerError, brain) {
|
|
|
251
259
|
if (!providerError) return { alerted: false };
|
|
252
260
|
try {
|
|
253
261
|
const planner = require('../skills/skill-planner');
|
|
262
|
+
const actionUrl = providerError.type === 'request_invariant'
|
|
263
|
+
? null
|
|
264
|
+
: (providerError.actionUrl || '/setup.html');
|
|
254
265
|
planner.addServiceAlert({
|
|
255
266
|
service: 'ai_provider',
|
|
256
267
|
type: `ai_provider:${providerError.provider || 'default'}:${providerError.type}`,
|
|
@@ -259,7 +270,7 @@ function recordProviderFailureAlert(providerError, brain) {
|
|
|
259
270
|
provider: providerError.provider || null,
|
|
260
271
|
model: providerError.model || null,
|
|
261
272
|
message: `${providerError.title}: ${providerError.userMessage}`,
|
|
262
|
-
action_url:
|
|
273
|
+
action_url: actionUrl,
|
|
263
274
|
provider_error: providerError,
|
|
264
275
|
});
|
|
265
276
|
return { alerted: true };
|
|
@@ -268,9 +279,26 @@ function recordProviderFailureAlert(providerError, brain) {
|
|
|
268
279
|
}
|
|
269
280
|
}
|
|
270
281
|
|
|
282
|
+
// One-line, secret-redacted human detail of a failed-provider error — for the
|
|
283
|
+
// routing-notice banner, so a degraded default surfaces WHY ("HTTP 404: anthropic
|
|
284
|
+
// error: model deepseek-v4-flash") instead of a bare "unreachable". Never throws.
|
|
285
|
+
function fallbackErrorDetail(err) {
|
|
286
|
+
if (!err) return '';
|
|
287
|
+
let raw = '';
|
|
288
|
+
try { raw = buildRawMessage(err); }
|
|
289
|
+
catch { try { raw = redactSecrets(String((err && err.message) || err || '')); } catch { raw = ''; } }
|
|
290
|
+
raw = String(raw || '').replace(/\s+/g, ' ').trim();
|
|
291
|
+
if (!raw) return '';
|
|
292
|
+
let status = 0;
|
|
293
|
+
try { status = extractStatus(err); } catch { status = 0; }
|
|
294
|
+
if (status && !new RegExp('\\b' + status + '\\b').test(raw)) raw = `HTTP ${status}: ${raw}`;
|
|
295
|
+
return raw.slice(0, 200);
|
|
296
|
+
}
|
|
297
|
+
|
|
271
298
|
module.exports = {
|
|
272
299
|
classifyProviderError,
|
|
273
300
|
decorateProviderError,
|
|
301
|
+
fallbackErrorDetail,
|
|
274
302
|
providerLabel,
|
|
275
303
|
recordProviderFailureAlert,
|
|
276
304
|
redactSecrets,
|
|
@@ -132,7 +132,11 @@ function modelMatchesProvider(model, providerId) {
|
|
|
132
132
|
if (!model || !providerId) return true;
|
|
133
133
|
const detected = detectProviderForModel(model);
|
|
134
134
|
if (!detected) return true; // No claim — accept.
|
|
135
|
-
|
|
135
|
+
const provider = String(providerId || '').toLowerCase();
|
|
136
|
+
if (provider === 'openai-compatible' || provider.startsWith('openai-compatible-')) {
|
|
137
|
+
return detected === 'openai';
|
|
138
|
+
}
|
|
139
|
+
return detected === provider;
|
|
136
140
|
}
|
|
137
141
|
|
|
138
142
|
/** Test helper. */
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ANTHROPIC_SAMPLING_PARAMS = Object.freeze([
|
|
4
|
+
'temperature',
|
|
5
|
+
'top_p',
|
|
6
|
+
'topP',
|
|
7
|
+
'top_k',
|
|
8
|
+
'topK',
|
|
9
|
+
]);
|
|
10
|
+
|
|
11
|
+
function normalizeModelId(model) {
|
|
12
|
+
if (!model) return '';
|
|
13
|
+
let id = typeof model === 'string' ? model : String(model.id || model.model || model.name || '');
|
|
14
|
+
id = id.trim();
|
|
15
|
+
id = id.replace(/^@anthropic\//i, '');
|
|
16
|
+
id = id.replace(/^anthropic[.:/]/i, '');
|
|
17
|
+
id = id.replace(/@(default|[a-z0-9_.-]+)$/i, '');
|
|
18
|
+
return id.toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function anthropicModelRejectsSamplingParams(model) {
|
|
22
|
+
const id = normalizeModelId(model);
|
|
23
|
+
const match = id.match(/^claude-(?:opus|sonnet|haiku)-4-(\d+)(?:-|$)/);
|
|
24
|
+
return Boolean(match && Number(match[1]) >= 7);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function stripParams(request, keys, reason) {
|
|
28
|
+
const stripped = [];
|
|
29
|
+
for (const key of keys) {
|
|
30
|
+
if (Object.prototype.hasOwnProperty.call(request, key) && request[key] !== undefined) {
|
|
31
|
+
delete request[key];
|
|
32
|
+
stripped.push(key);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return stripped.length > 0 ? [{ reason, keys: stripped }] : [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function stripAnthropicUnsupportedParams(request = {}, { model = request.model, force = false } = {}) {
|
|
39
|
+
const next = { ...request };
|
|
40
|
+
const shouldStrip = force || anthropicModelRejectsSamplingParams(model);
|
|
41
|
+
if (!shouldStrip) return { request: next, warnings: [] };
|
|
42
|
+
const warnings = stripParams(
|
|
43
|
+
next,
|
|
44
|
+
ANTHROPIC_SAMPLING_PARAMS,
|
|
45
|
+
`Removed Anthropic sampling parameter(s) unsupported by ${normalizeModelId(model) || 'selected model'}.`
|
|
46
|
+
);
|
|
47
|
+
return { request: next, warnings };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isAnthropicSamplingParamError(error) {
|
|
51
|
+
const text = [
|
|
52
|
+
error?.message,
|
|
53
|
+
error?.responseBody,
|
|
54
|
+
error?.body,
|
|
55
|
+
JSON.stringify(error?.error || ''),
|
|
56
|
+
].filter(Boolean).join('\n').toLowerCase();
|
|
57
|
+
return /(?:temperature|top[_ ]?p|top[_ ]?k).*(?:deprecated|not supported|unsupported|invalid)/i.test(text)
|
|
58
|
+
|| /(?:deprecated|not supported|unsupported|invalid).*(?:temperature|top[_ ]?p|top[_ ]?k)/i.test(text);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
ANTHROPIC_SAMPLING_PARAMS,
|
|
63
|
+
anthropicModelRejectsSamplingParams,
|
|
64
|
+
isAnthropicSamplingParamError,
|
|
65
|
+
normalizeModelId,
|
|
66
|
+
stripAnthropicUnsupportedParams,
|
|
67
|
+
};
|
|
@@ -38,6 +38,32 @@ const KV_BACKFILL_DONE = 'backfill:done';
|
|
|
38
38
|
|
|
39
39
|
const VALID_DOMAINS = ['work', 'personal', 'social', 'technical', 'admin'];
|
|
40
40
|
|
|
41
|
+
// ── Cheap "work remaining" signals ──────────────────────────────────────────
|
|
42
|
+
// This loop runs every 30s ON THE MAIN THREAD, and better-sqlite3 is synchronous,
|
|
43
|
+
// so any unbounded query here blocks Wall-E's entire event loop — HTTP included —
|
|
44
|
+
// for the full duration of the scan. On a multi-GB brain that means the daemon
|
|
45
|
+
// stops answering /api/* (e.g. the setup page's provider list) for tens of seconds
|
|
46
|
+
// at a time. So every per-tick "is there work left?" check MUST stay bounded — no
|
|
47
|
+
// full-table content scans.
|
|
48
|
+
//
|
|
49
|
+
// `length(content) > 10` forces reading each candidate row's (often overflow-paged)
|
|
50
|
+
// content, so counting embeddable memories is the one unavoidable full scan. It
|
|
51
|
+
// only moves as memories are ingested, so cache it briefly instead of paying it
|
|
52
|
+
// every tick.
|
|
53
|
+
let _embeddableCache = { value: null, at: 0 };
|
|
54
|
+
const EMBEDDABLE_TTL_MS = 10 * 60 * 1000;
|
|
55
|
+
function embeddableMemoryTotal() {
|
|
56
|
+
const now = Date.now();
|
|
57
|
+
if (_embeddableCache.value != null && now - _embeddableCache.at < EMBEDDABLE_TTL_MS) {
|
|
58
|
+
return _embeddableCache.value;
|
|
59
|
+
}
|
|
60
|
+
const c = brain.getDb().prepare(
|
|
61
|
+
"SELECT count(*) AS c FROM memories WHERE content IS NOT NULL AND length(content) > 10"
|
|
62
|
+
).get().c;
|
|
63
|
+
_embeddableCache = { value: c, at: now };
|
|
64
|
+
return c;
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
/**
|
|
42
68
|
* Run one backfill iteration. Call periodically from the daemon.
|
|
43
69
|
* Returns { classified, embedded, done } counts.
|
|
@@ -79,7 +105,10 @@ async function runOnce(opts = {}) {
|
|
|
79
105
|
// Phase 2: Embed memories missing embeddings — run multiple batches per tick
|
|
80
106
|
// Quick Ollama readiness check — after laptop sleep, Ollama needs a few seconds
|
|
81
107
|
// to reload. Skip this tick rather than wasting it on failed/slow batches.
|
|
82
|
-
|
|
108
|
+
// Gate on the cheap remaining-count BEFORE the readiness probe: when embedding is
|
|
109
|
+
// already complete, embedBatch's anti-join would otherwise SCAN the whole memories
|
|
110
|
+
// table just to return 0 every tick. countUnembedded() is bounded (indexed counts).
|
|
111
|
+
if (embeddings && embeddings.isAvailable() && countUnembedded() > 0 && await _isEmbeddingProviderReady()) {
|
|
83
112
|
const MAX_BATCHES_PER_TICK = 5; // Up to 5 x EMBED_BATCH per tick for faster backfill
|
|
84
113
|
for (let b = 0; b < MAX_BATCHES_PER_TICK; b++) {
|
|
85
114
|
try {
|
|
@@ -147,12 +176,19 @@ async function runOnce(opts = {}) {
|
|
|
147
176
|
async function classifyBatch(opts = {}) {
|
|
148
177
|
const db = brain.getDb();
|
|
149
178
|
|
|
150
|
-
|
|
179
|
+
// Self-draining: select straight from the unclassified set (domain IS NULL) with a
|
|
180
|
+
// plain LIMIT — no `id > cursor` / `ORDER BY id ASC`. The old cursor+ORDER BY form
|
|
181
|
+
// made SQLite drive off idx_memories_domain and build a TEMP B-TREE over EVERY
|
|
182
|
+
// unclassified row (reading each one's content via a table seek) before applying
|
|
183
|
+
// LIMIT — so a single call read 80k+ rows' content and blocked the event loop for
|
|
184
|
+
// tens of seconds. classifyMemory always assigns a non-null domain, so classified
|
|
185
|
+
// rows leave the set and the next LIMIT picks up where this one left off. This is
|
|
186
|
+
// bounded to CLASSIFY_BATCH content reads per call and can't strand rows.
|
|
151
187
|
const batch = db.prepare(`
|
|
152
188
|
SELECT id, content, source, source_channel FROM memories
|
|
153
|
-
WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10
|
|
154
|
-
|
|
155
|
-
`).all(
|
|
189
|
+
WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10
|
|
190
|
+
LIMIT ?
|
|
191
|
+
`).all(CLASSIFY_BATCH);
|
|
156
192
|
|
|
157
193
|
if (batch.length === 0) return 0;
|
|
158
194
|
|
|
@@ -165,7 +201,6 @@ async function classifyBatch(opts = {}) {
|
|
|
165
201
|
});
|
|
166
202
|
classify();
|
|
167
203
|
|
|
168
|
-
brain.setKv(KV_CLASSIFY_CURSOR, batch[batch.length - 1].id);
|
|
169
204
|
return batch.length;
|
|
170
205
|
}
|
|
171
206
|
|
|
@@ -358,24 +393,33 @@ async function indexBatch() {
|
|
|
358
393
|
}
|
|
359
394
|
|
|
360
395
|
const NOISE_BATCH = 500;
|
|
396
|
+
const KV_NOISE_DONE = 'backfill:noise:done';
|
|
361
397
|
|
|
362
398
|
async function noiseStripBatch() {
|
|
399
|
+
// One-time legacy sweep: new content is stripped at ingest, so once we've swept the
|
|
400
|
+
// whole table there's nothing left to do — skip entirely instead of re-scanning.
|
|
401
|
+
if (brain.getKv(KV_NOISE_DONE) === '1') return 0;
|
|
402
|
+
|
|
363
403
|
const db = brain.getDb();
|
|
364
404
|
const cursor = brain.getKv(KV_NOISE_CURSOR) || '0';
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
405
|
+
// Scan a bounded id-window (LIMIT applies to SCANNED rows) and match the legacy
|
|
406
|
+
// <system-reminder> tag in JS. The old query put `content LIKE '%…%'` in SQL, so with
|
|
407
|
+
// only a handful of tagged rows it never filled the LIMIT and the cursor — advanced to
|
|
408
|
+
// the last MATCHED id — barely moved, making every 30s tick re-scan the rest of the
|
|
409
|
+
// table reading content. Advancing past the last SCANNED row guarantees forward
|
|
410
|
+
// progress and lets the sweep terminate.
|
|
411
|
+
const batch = db.prepare(
|
|
412
|
+
'SELECT id, content FROM memories WHERE id > ? ORDER BY id ASC LIMIT ?'
|
|
413
|
+
).all(cursor, NOISE_BATCH);
|
|
414
|
+
if (batch.length === 0) { brain.setKv(KV_NOISE_DONE, '1'); return 0; }
|
|
415
|
+
|
|
374
416
|
let stripped = 0;
|
|
375
417
|
const update = db.transaction(() => {
|
|
376
418
|
for (const mem of batch) {
|
|
377
|
-
const
|
|
378
|
-
if (
|
|
419
|
+
const content = mem.content;
|
|
420
|
+
if (!content || content.length <= 50 || !content.includes('<system-reminder>')) continue;
|
|
421
|
+
const cleaned = stripNoise(content);
|
|
422
|
+
if (cleaned !== content) {
|
|
379
423
|
db.prepare('UPDATE memories SET content_raw = content, content = ? WHERE id = ?').run(cleaned, mem.id);
|
|
380
424
|
stripped++;
|
|
381
425
|
}
|
|
@@ -383,26 +427,37 @@ async function noiseStripBatch() {
|
|
|
383
427
|
});
|
|
384
428
|
update();
|
|
385
429
|
brain.setKv(KV_NOISE_CURSOR, batch[batch.length - 1].id);
|
|
430
|
+
// Short batch ⇒ reached the end of the table ⇒ sweep complete.
|
|
431
|
+
if (batch.length < NOISE_BATCH) brain.setKv(KV_NOISE_DONE, '1');
|
|
386
432
|
return stripped;
|
|
387
433
|
}
|
|
388
434
|
|
|
435
|
+
// Capped at 21: the only consumers are the done-gate (`<= 20`) and a diagnostic log
|
|
436
|
+
// line. The exact count would read content for every one of 80k+ unclassified rows
|
|
437
|
+
// each tick (full scan); the LIMIT lets SQLite stop after the first 21 matches.
|
|
389
438
|
function countUnclassified() {
|
|
390
439
|
try {
|
|
391
440
|
return brain.getDb().prepare(
|
|
392
|
-
"SELECT count(*)
|
|
441
|
+
"SELECT count(*) AS c FROM (SELECT 1 FROM memories WHERE domain IS NULL AND content IS NOT NULL AND length(content) > 10 LIMIT 21)"
|
|
393
442
|
).get().c;
|
|
394
443
|
} catch { return 0; }
|
|
395
444
|
}
|
|
396
445
|
|
|
446
|
+
// Cheap: the accurate anti-join (memories LEFT JOIN embedding_map ... rowid IS NULL)
|
|
447
|
+
// is a full SCAN of memories reading content — tens of seconds cold, every tick.
|
|
448
|
+
// Instead derive remaining work from two bounded counts: the (cached) embeddable
|
|
449
|
+
// total minus the indexed count of memory-type embeddings for the active model.
|
|
450
|
+
// Stale embeddings can make this slightly under-count, which is acceptable: it only
|
|
451
|
+
// gates "is embedding done?" and mirrors the existing `unclassified <= 20` tolerance
|
|
452
|
+
// — a handful of unembedded rows won't meaningfully hurt semantic search.
|
|
397
453
|
function countUnembedded() {
|
|
398
454
|
try {
|
|
399
455
|
const activeModel = embeddings && embeddings.getEmbeddingModel();
|
|
400
456
|
if (!activeModel) return 0;
|
|
401
|
-
|
|
402
|
-
SELECT count(*)
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
`).get(activeModel).c;
|
|
457
|
+
const embedded = brain.getDb().prepare(
|
|
458
|
+
"SELECT count(*) AS c FROM embedding_map WHERE model = ? AND entity_type = 'memory'"
|
|
459
|
+
).get(activeModel).c;
|
|
460
|
+
return Math.max(0, embeddableMemoryTotal() - embedded);
|
|
406
461
|
} catch { return 0; }
|
|
407
462
|
}
|
|
408
463
|
|
|
@@ -417,6 +472,7 @@ function reset() {
|
|
|
417
472
|
brain.setKv(KV_ENTITY_CURSOR, null);
|
|
418
473
|
brain.setKv(KV_INDEX_CURSOR, null);
|
|
419
474
|
brain.setKv(KV_NOISE_CURSOR, null);
|
|
475
|
+
brain.setKv(KV_NOISE_DONE, null);
|
|
420
476
|
}
|
|
421
477
|
|
|
422
478
|
/**
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Deferred, one-shot brain-DB space optimization for users upgrading from a pre-int8 build.
|
|
3
|
+
// Runs OFF the boot path (registered with a multi-minute startDelay) so it never adds to the
|
|
4
|
+
// boot storm, and self-gates to run at most once via a kv flag.
|
|
5
|
+
//
|
|
6
|
+
// Safety model:
|
|
7
|
+
// - SIZE-GATED: the int8 rebuild + FTS rebuild + VACUUM block the (single) event loop for
|
|
8
|
+
// their duration. Tiny for a few-thousand-vector store, ~tens of seconds for hundreds of
|
|
9
|
+
// thousands. So only auto-run when the embedding store is small; otherwise log a one-time
|
|
10
|
+
// notice pointing at the supervised CLI (`scripts/db-optimize/migrate.js`, run with Wall-E
|
|
11
|
+
// stopped) and never nag again.
|
|
12
|
+
// - NEVER drops lost_and_found automatically: that is a per-install SQLite .recover artifact
|
|
13
|
+
// and dropping a recovery table needs explicit human confirmation.
|
|
14
|
+
// - Idempotent + crash-safe: each phase checks if already applied and is transactional, so a
|
|
15
|
+
// restart mid-run simply retries; float32 vectors are never lost (rolled back on crash).
|
|
16
|
+
|
|
17
|
+
const brain = require('../brain');
|
|
18
|
+
|
|
19
|
+
const KV_STATE = 'brain:optimize:v1'; // unset | 'done' | 'notified'
|
|
20
|
+
const AUTO_MAX = parseInt(process.env.BRAIN_OPTIMIZE_AUTO_MAX || '20000', 10);
|
|
21
|
+
|
|
22
|
+
async function runOnce() {
|
|
23
|
+
const state = brain.getKv(KV_STATE);
|
|
24
|
+
if (state === 'done' || state === 'notified') return { done: true, _done: true, skipped: state };
|
|
25
|
+
|
|
26
|
+
let db;
|
|
27
|
+
try { db = brain.getDb(); } catch { return { done: false, reason: 'no-db' }; }
|
|
28
|
+
|
|
29
|
+
// sqlite-vec must be loaded on this connection for the int8 quantization functions.
|
|
30
|
+
try { require('../embeddings').ensureVecLoaded(); } catch {}
|
|
31
|
+
|
|
32
|
+
let mig;
|
|
33
|
+
try { mig = require('../scripts/db-optimize/migrate'); } catch (e) {
|
|
34
|
+
console.error('[brain-optimize] migration module unavailable:', e.message);
|
|
35
|
+
brain.setKv(KV_STATE, 'notified');
|
|
36
|
+
return { done: true, _done: true, skipped: 'module-missing' };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (!mig.needsOptimization(db)) {
|
|
40
|
+
brain.setKv(KV_STATE, 'done');
|
|
41
|
+
return { done: true, _done: true, alreadyOptimal: true };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const n = mig.embeddingCount(db);
|
|
45
|
+
if (n > AUTO_MAX) {
|
|
46
|
+
console.warn(
|
|
47
|
+
`[brain-optimize] Brain has ${n} float32 embeddings (> ${AUTO_MAX} auto cap). ` +
|
|
48
|
+
`To reclaim disk, stop Wall-E and run: node wall-e/scripts/db-optimize/migrate.js "${brain.getDbPath()}". ` +
|
|
49
|
+
`Set BRAIN_OPTIMIZE_AUTO_MAX higher to auto-migrate anyway (briefly blocks the event loop).`
|
|
50
|
+
);
|
|
51
|
+
brain.setKv(KV_STATE, 'notified');
|
|
52
|
+
return { done: true, _done: true, skipped: 'large', embeddingCount: n };
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const t0 = Date.now();
|
|
56
|
+
mig.optimizeBrain(db, {
|
|
57
|
+
vacuum: true,
|
|
58
|
+
dropLostAndFound: false, // recovery artifact — never auto-drop
|
|
59
|
+
log: (...a) => console.log('[brain-optimize]', ...a),
|
|
60
|
+
});
|
|
61
|
+
brain.setKv(KV_STATE, 'done');
|
|
62
|
+
const ms = Date.now() - t0;
|
|
63
|
+
console.log(`[brain-optimize] upgrade migration complete in ${(ms / 1000).toFixed(1)}s (${n} vectors → int8)`);
|
|
64
|
+
return { done: true, _done: true, migrated: true, embeddingCount: n, ms };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
module.exports = { runOnce, KV_STATE, AUTO_MAX };
|
|
@@ -27,17 +27,32 @@ async function runOnce(adapters) {
|
|
|
27
27
|
|
|
28
28
|
try {
|
|
29
29
|
const memories = await adapter.poll(since);
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
30
|
+
// Insert in chunked transactions instead of N individually-locked INSERTs. One
|
|
31
|
+
// INSERT per message means N cross-process write-lock acquisitions per poll;
|
|
32
|
+
// wrapping ALL of them in a single transaction would swing the other way and hold
|
|
33
|
+
// the lock for the whole batch (the harvest lock-hold class). Chunking bounds the
|
|
34
|
+
// hold while still collapsing N lock acquisitions into N/CHUNK. High-importance
|
|
35
|
+
// events are emitted AFTER each chunk commits, never from inside the transaction.
|
|
36
|
+
const INSERT_CHUNK = 200;
|
|
37
|
+
for (let i = 0; i < memories.length; i += INSERT_CHUNK) {
|
|
38
|
+
const slice = memories.slice(i, i + INSERT_CHUNK);
|
|
39
|
+
const highImportance = [];
|
|
40
|
+
const insertChunk = brain.getDb().transaction(() => {
|
|
41
|
+
for (const mem of slice) {
|
|
42
|
+
adapterLatestTimestamp = newerTimestamp(adapterLatestTimestamp, mem.timestamp);
|
|
43
|
+
latestTimestamp = newerTimestamp(latestTimestamp, mem.timestamp);
|
|
44
|
+
const result = brain.insertMemory(mem);
|
|
45
|
+
if (result) {
|
|
46
|
+
memoriesIngested++;
|
|
47
|
+
adapterIngested++;
|
|
48
|
+
if (mem.importance >= 0.8 || mem.memory_type === 'message_received') {
|
|
49
|
+
highImportance.push({ id: result?.id, content: (mem.content || '').slice(0, 200) });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
39
52
|
}
|
|
40
|
-
}
|
|
53
|
+
});
|
|
54
|
+
insertChunk();
|
|
55
|
+
for (const h of highImportance) eventBus.emitHighImportance(h.id, h.content);
|
|
41
56
|
}
|
|
42
57
|
// After inserting individual memories, create exchange pairs for conversational sources
|
|
43
58
|
if (memories.length >= 2) {
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Daily question digest: select the few questions worth the owner's input, render them as a
|
|
4
|
+
// numbered message, and resolve them from the owner's reply. Channel fan-out (Slack/email/
|
|
5
|
+
// CTM tab) and the once-a-day scheduling wrap these pure helpers so they stay testable.
|
|
6
|
+
//
|
|
7
|
+
// Design: docs/superpowers/specs/2026-05-31-question-digest-and-brain-retention-design.md
|
|
8
|
+
// (daily-digest-only; deliver to Slack + email + CTM tab; answer via Slack reply or CTM).
|
|
9
|
+
|
|
10
|
+
const brain = require('../brain');
|
|
11
|
+
|
|
12
|
+
const DEFAULT_MAX = Number(process.env.WALL_E_QUESTION_DIGEST_MAX || 10);
|
|
13
|
+
|
|
14
|
+
function _ownerName() {
|
|
15
|
+
try { return brain.getOwnerName() || 'there'; } catch { return 'there'; }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Render a numbered, priority-tagged digest the owner can answer by replying "1: ... 2: ...".
|
|
19
|
+
function renderDigestText(questions, ownerName = _ownerName()) {
|
|
20
|
+
if (!questions || questions.length === 0) return '';
|
|
21
|
+
const n = questions.length;
|
|
22
|
+
const lines = [
|
|
23
|
+
`Hi ${ownerName} — Wall-E has ${n} question${n === 1 ? '' : 's'} for you:`,
|
|
24
|
+
'',
|
|
25
|
+
];
|
|
26
|
+
questions.forEach((q, i) => {
|
|
27
|
+
const tag = q.priority && q.priority !== 'normal' ? `[${q.priority}] ` : '';
|
|
28
|
+
lines.push(`${i + 1}. ${tag}${q.question}`);
|
|
29
|
+
});
|
|
30
|
+
lines.push('');
|
|
31
|
+
lines.push('Reply like "1: your answer 2: your answer" — or answer in the Wall-E tab.');
|
|
32
|
+
return lines.join('\n');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Build today's digest from undelivered pending questions. Does NOT mark delivered — the
|
|
36
|
+
// caller marks only after a successful send (so a send failure re-tries tomorrow).
|
|
37
|
+
function buildDigest({ max = DEFAULT_MAX } = {}) {
|
|
38
|
+
const questions = brain.listUndeliveredDigestQuestions({ max });
|
|
39
|
+
return {
|
|
40
|
+
count: questions.length,
|
|
41
|
+
questions,
|
|
42
|
+
text: renderDigestText(questions),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Parse an owner reply that answers numbered questions, e.g.:
|
|
47
|
+
// "1: yes 2 - it's the Dropbox path\n3. no"
|
|
48
|
+
// Maps each leading number to the question at that 1-based position in `questions`.
|
|
49
|
+
// Returns [{ id, answer }]. Unparseable lines are ignored.
|
|
50
|
+
function parseDigestReply(text, questions) {
|
|
51
|
+
if (!text || !Array.isArray(questions) || questions.length === 0) return [];
|
|
52
|
+
const out = [];
|
|
53
|
+
const seen = new Set();
|
|
54
|
+
const re = /(?:^|\n)\s*(\d{1,2})\s*[:.\)\-]\s*(.+?)(?=\n\s*\d{1,2}\s*[:.\)\-]|$)/gs;
|
|
55
|
+
let m;
|
|
56
|
+
while ((m = re.exec(text)) !== null) {
|
|
57
|
+
const idx = parseInt(m[1], 10) - 1;
|
|
58
|
+
const answer = m[2].trim();
|
|
59
|
+
if (idx < 0 || idx >= questions.length || !answer) continue;
|
|
60
|
+
const q = questions[idx];
|
|
61
|
+
if (!q || seen.has(q.id)) continue;
|
|
62
|
+
seen.add(q.id);
|
|
63
|
+
out.push({ id: q.id, answer });
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Resolve questions from parsed replies. Returns the count resolved.
|
|
69
|
+
function applyDigestReplies(parsed, { resolutionEvidence = 'digest reply' } = {}) {
|
|
70
|
+
if (!Array.isArray(parsed)) return 0;
|
|
71
|
+
let resolved = 0;
|
|
72
|
+
for (const { id, answer } of parsed) {
|
|
73
|
+
try {
|
|
74
|
+
brain.answerQuestion(id, { answer, resolution_type: 'answered', resolution_evidence: resolutionEvidence });
|
|
75
|
+
resolved += 1;
|
|
76
|
+
} catch { /* question may have been resolved/expired already */ }
|
|
77
|
+
}
|
|
78
|
+
return resolved;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ---- Channel fan-out -------------------------------------------------------------------
|
|
82
|
+
// The CTM Wall-E tab is the always-on surface (it lists pending questions + Answer/Dismiss
|
|
83
|
+
// buttons, no opt-in). Slack DM and email are OPT-IN push notifications, off by default,
|
|
84
|
+
// enabled via kv flags (set from the Wall-E settings UI). "Delivered" only gates the push
|
|
85
|
+
// channels — the CTM tab always shows pending questions regardless.
|
|
86
|
+
const KV = {
|
|
87
|
+
slackEnabled: 'digest:slack_enabled',
|
|
88
|
+
emailEnabled: 'digest:email_enabled',
|
|
89
|
+
slackChannel: 'digest:slack_channel',
|
|
90
|
+
emailTo: 'digest:email_to',
|
|
91
|
+
lastSent: 'digest:last_sent_date',
|
|
92
|
+
};
|
|
93
|
+
function _kv(key) { try { return brain.getKv(key) || ''; } catch { return ''; } }
|
|
94
|
+
function _kvBool(key) { return String(_kv(key)).toLowerCase() === 'true'; }
|
|
95
|
+
|
|
96
|
+
function getDigestChannelConfig() {
|
|
97
|
+
return {
|
|
98
|
+
ctm: true, // always on
|
|
99
|
+
slack: _kvBool(KV.slackEnabled),
|
|
100
|
+
slackChannel: _kv(KV.slackChannel),
|
|
101
|
+
email: _kvBool(KV.emailEnabled),
|
|
102
|
+
emailTo: _kv(KV.emailTo),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function _defaultSendSlack(channel, text) {
|
|
107
|
+
const { callSlackMcp } = require('../tools/slack-mcp');
|
|
108
|
+
return callSlackMcp('slack_send_message', { channel, text });
|
|
109
|
+
}
|
|
110
|
+
async function _defaultSendEmail(to, subject, body) {
|
|
111
|
+
const { sendMail } = require('../tools/local-tools');
|
|
112
|
+
return sendMail({ to, subject, body, source: 'wall-e-digest' });
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Once-a-day digest job (registered in agent.js). Builds the undelivered digest and pushes
|
|
116
|
+
// it to the OPT-IN channels (Slack/email); marks delivered only if a push actually sent, so
|
|
117
|
+
// enabling a channel later still pushes the backlog once. The CTM tab needs no push.
|
|
118
|
+
// `opts` injects { date, force, sendSlack, sendEmail } for tests.
|
|
119
|
+
async function runDigestJob(opts = {}) {
|
|
120
|
+
const date = opts.date || new Date();
|
|
121
|
+
const todayStr = date.toISOString().slice(0, 10);
|
|
122
|
+
const targetHour = Number(process.env.WALL_E_DIGEST_HOUR || 8);
|
|
123
|
+
if (!opts.force) {
|
|
124
|
+
if (date.getHours() < targetHour) return { sent: false, reason: 'before-target-hour' };
|
|
125
|
+
if (_kv(KV.lastSent) === todayStr) return { sent: false, reason: 'already-sent-today' };
|
|
126
|
+
}
|
|
127
|
+
const cfg = getDigestChannelConfig();
|
|
128
|
+
if (!cfg.slack && !cfg.email) return { sent: false, reason: 'no-push-channels-enabled' };
|
|
129
|
+
|
|
130
|
+
const digest = buildDigest({ max: opts.max || DEFAULT_MAX });
|
|
131
|
+
if (digest.count === 0) return { sent: false, reason: 'no-questions' };
|
|
132
|
+
|
|
133
|
+
const sendSlack = opts.sendSlack || _defaultSendSlack;
|
|
134
|
+
const sendEmail = opts.sendEmail || _defaultSendEmail;
|
|
135
|
+
const results = {};
|
|
136
|
+
let anySent = false;
|
|
137
|
+
if (cfg.slack && cfg.slackChannel) {
|
|
138
|
+
try { await sendSlack(cfg.slackChannel, digest.text); results.slack = 'sent'; anySent = true; }
|
|
139
|
+
catch (e) { results.slack = `err:${e.message}`; }
|
|
140
|
+
} else if (cfg.slack) {
|
|
141
|
+
results.slack = 'skipped:no-channel-configured';
|
|
142
|
+
}
|
|
143
|
+
if (cfg.email && cfg.emailTo) {
|
|
144
|
+
try { await sendEmail(cfg.emailTo, `Wall-E: ${digest.count} question${digest.count === 1 ? '' : 's'} for you`, digest.text); results.email = 'sent'; anySent = true; }
|
|
145
|
+
catch (e) { results.email = `err:${e.message}`; }
|
|
146
|
+
} else if (cfg.email) {
|
|
147
|
+
results.email = 'skipped:no-recipient-configured';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (anySent) {
|
|
151
|
+
brain.markQuestionsDelivered(digest.questions.map((q) => q.id));
|
|
152
|
+
try { brain.setKv(KV.lastSent, todayStr); } catch {}
|
|
153
|
+
}
|
|
154
|
+
return { sent: anySent, count: digest.count, results };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
module.exports = {
|
|
158
|
+
buildDigest, renderDigestText, parseDigestReply, applyDigestReplies,
|
|
159
|
+
getDigestChannelConfig, runDigestJob, DEFAULT_MAX, KV,
|
|
160
|
+
};
|
|
@@ -45,12 +45,14 @@ async function runOnce(opts = {}) {
|
|
|
45
45
|
const checkpoint = brain.getCheckpoint('reflect');
|
|
46
46
|
const lastRunAt = checkpoint ? checkpoint.last_run_at : null;
|
|
47
47
|
|
|
48
|
-
// Count new memories since last reflect
|
|
48
|
+
// Count new memories since last reflect. Use a DB COUNT — listMemories(...).length
|
|
49
|
+
// pulled every matching row (content blobs and all) just to count them, a full
|
|
50
|
+
// materialization on a 350k-row brain.
|
|
49
51
|
const sinceOpts = lastRunAt ? { since: lastRunAt } : {};
|
|
50
|
-
const memoriesSinceLastReflect = brain.
|
|
52
|
+
const memoriesSinceLastReflect = brain.countMemories(sinceOpts);
|
|
51
53
|
|
|
52
|
-
// Count pending questions
|
|
53
|
-
const pendingQuestions = brain.
|
|
54
|
+
// Count pending questions (DB COUNT, not a full materialization)
|
|
55
|
+
const pendingQuestions = brain.countQuestions({ status: 'pending' });
|
|
54
56
|
|
|
55
57
|
// Update checkpoint
|
|
56
58
|
brain.upsertCheckpoint('reflect', {
|