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,232 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unified phone/remote-access connection health.
|
|
5
|
+
*
|
|
6
|
+
* Produces ONE normalized health record per method (Microsoft Dev Tunnel, Walle Remote
|
|
7
|
+
* relay, Tailscale) so the Access page can show a single status truth, detect
|
|
8
|
+
* expiry/offline, fire an action-required notification on the TRANSITION to broken, and
|
|
9
|
+
* auto-recover when the user fixes it.
|
|
10
|
+
*
|
|
11
|
+
* Probes are READ-ONLY (no recovery side-effects) — the existing Microsoft watchdog owns
|
|
12
|
+
* actually fixing the tunnel; this module reports state and offers `action` endpoints the
|
|
13
|
+
* client triggers. Cheap enough to poll (~20s) — no deep public-reachability probe here.
|
|
14
|
+
*
|
|
15
|
+
* Record shape:
|
|
16
|
+
* { method, state, detail, origin, action, since, checked_at }
|
|
17
|
+
* state: 'ok' | 'warning' | 'expired' | 'offline' | 'error' | 'not_configured'
|
|
18
|
+
* action: null | { kind, endpoint, method } // a one-click fix the client can POST/GET
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fs = require('fs');
|
|
22
|
+
const { X509Certificate } = require('crypto');
|
|
23
|
+
|
|
24
|
+
const microsoft = require('./microsoft-dev-tunnel-setup');
|
|
25
|
+
const tailscale = require('./tailscale-setup');
|
|
26
|
+
const remoteRelay = require('./remote-relay-store');
|
|
27
|
+
|
|
28
|
+
// Walle relay heartbeat: if not connected and last handshake older than this, treat as offline.
|
|
29
|
+
const WALLE_STALE_MS = Number(process.env.CTM_WALLE_RELAY_STALE_MS) || 5 * 60 * 1000;
|
|
30
|
+
const CERT_SOON_MS = 24 * 60 * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
// States that count as "broken" (drive the Reconnect badge + action-required notification).
|
|
33
|
+
const BROKEN = new Set(['expired', 'offline', 'error']);
|
|
34
|
+
|
|
35
|
+
// method -> { state, since } — transition cache (also keeps `since` accurate between polls).
|
|
36
|
+
const _stateCache = new Map();
|
|
37
|
+
|
|
38
|
+
function _rec(method, { state, detail, origin = '', action = null }) {
|
|
39
|
+
return { method, state, detail, origin, action };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// How far past its scheduled renewal the managed tunnel is (ms; 0 = on track).
|
|
43
|
+
// Considers both the tunnel renewal and the anonymous-access renewal.
|
|
44
|
+
function _renewalOverdueMs(managed, now = Date.now()) {
|
|
45
|
+
let worst = 0;
|
|
46
|
+
for (const key of ['next_tunnel_renewal_at', 'next_access_renewal_at']) {
|
|
47
|
+
const at = Date.parse(managed && managed[key] || '');
|
|
48
|
+
if (Number.isFinite(at) && now > at) worst = Math.max(worst, now - at);
|
|
49
|
+
}
|
|
50
|
+
return worst;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function probeMicrosoftHealth(opts) {
|
|
54
|
+
try {
|
|
55
|
+
const d = await microsoft.detectMicrosoftDevTunnelSetup(opts);
|
|
56
|
+
const managed = d.managed_tunnel || {};
|
|
57
|
+
const configured = !!(managed.configured || managed.desired_running || managed.tunnel_id);
|
|
58
|
+
if (!d.installed) {
|
|
59
|
+
return _rec('microsoft', { state: 'not_configured', detail: 'devtunnel CLI is not installed.',
|
|
60
|
+
action: { kind: 'install', endpoint: '/api/setup/network/microsoft-dev-tunnel/install', method: 'POST' } });
|
|
61
|
+
}
|
|
62
|
+
if (!d.signed_in) {
|
|
63
|
+
// Signed out: if a tunnel was previously configured this is almost always an EXPIRED login.
|
|
64
|
+
return _rec('microsoft', configured
|
|
65
|
+
? { state: 'expired', detail: 'Microsoft sign-in expired — reconnect.',
|
|
66
|
+
action: { kind: 'login', endpoint: '/api/setup/network/microsoft-dev-tunnel/login', method: 'POST' } }
|
|
67
|
+
: { state: 'not_configured', detail: 'Not signed in.',
|
|
68
|
+
action: { kind: 'login', endpoint: '/api/setup/network/microsoft-dev-tunnel/login', method: 'POST' } });
|
|
69
|
+
}
|
|
70
|
+
if (managed.desired_running && !managed.running) {
|
|
71
|
+
return _rec('microsoft', { state: 'offline', detail: 'Tunnel should be running but is not — reconnect.', origin: d.mobile_url,
|
|
72
|
+
action: { kind: 'recover', endpoint: '/api/setup/network/microsoft-dev-tunnel/recover', method: 'POST' } });
|
|
73
|
+
}
|
|
74
|
+
if (!configured || !managed.running) {
|
|
75
|
+
return _rec('microsoft', { state: 'not_configured', detail: 'Tunnel not started yet.', origin: d.mobile_url,
|
|
76
|
+
action: { kind: 'start', endpoint: '/api/setup/network/microsoft-dev-tunnel/start', method: 'POST' } });
|
|
77
|
+
}
|
|
78
|
+
if (managed.access && managed.access.status === 'error') {
|
|
79
|
+
return _rec('microsoft', { state: 'warning', detail: managed.access_error || 'Tunnel access needs attention.', origin: d.mobile_url,
|
|
80
|
+
action: { kind: 'recover', endpoint: '/api/setup/network/microsoft-dev-tunnel/recover', method: 'POST' } });
|
|
81
|
+
}
|
|
82
|
+
// Renewal runs at day 25 of the 30-day Dev Tunnels expiration. If it is
|
|
83
|
+
// overdue the tunnel is burning its ~5-day runway toward expiry — and an
|
|
84
|
+
// expired tunnel gets RECREATED with a NEW URL, which invalidates every
|
|
85
|
+
// paired phone (cookies + passkeys are hostname-bound). Escalate before that:
|
|
86
|
+
// >24h overdue = warning, >3 days = broken (fires the action-required alert).
|
|
87
|
+
const overdue = _renewalOverdueMs(managed);
|
|
88
|
+
if (overdue > 3 * 24 * 60 * 60 * 1000) {
|
|
89
|
+
return _rec('microsoft', { state: 'error', detail: 'Tunnel renewal has been failing for days — the URL will expire and rotate. Reconnect.', origin: d.mobile_url,
|
|
90
|
+
action: { kind: 'recover', endpoint: '/api/setup/network/microsoft-dev-tunnel/recover', method: 'POST' } });
|
|
91
|
+
}
|
|
92
|
+
if (overdue > 24 * 60 * 60 * 1000) {
|
|
93
|
+
return _rec('microsoft', { state: 'warning', detail: 'Tunnel renewal is overdue — reconnect before the URL expires and rotates.', origin: d.mobile_url,
|
|
94
|
+
action: { kind: 'recover', endpoint: '/api/setup/network/microsoft-dev-tunnel/recover', method: 'POST' } });
|
|
95
|
+
}
|
|
96
|
+
return _rec('microsoft', { state: 'ok', detail: 'Connected.', origin: d.mobile_url });
|
|
97
|
+
} catch (err) {
|
|
98
|
+
return _rec('microsoft', { state: 'error', detail: String(err?.message || err).slice(0, 200) });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async function probeWalleHealth(db) {
|
|
103
|
+
try {
|
|
104
|
+
if (!db) return _rec('walle', { state: 'not_configured', detail: 'Relay store unavailable.' });
|
|
105
|
+
remoteRelay.ensureRemoteRelaySchema(db);
|
|
106
|
+
const s = remoteRelay.buildRemoteStatus(db);
|
|
107
|
+
if (!s.configured) return _rec('walle', { state: 'not_configured', detail: 'Walle Remote is not set up.' });
|
|
108
|
+
if (s.connected) return _rec('walle', { state: 'ok', detail: 'Relay connected.', origin: s.relay && s.relay.mobile_url });
|
|
109
|
+
// Configured but not connected. Only treat as a BREAK if it was connected before
|
|
110
|
+
// (last_connected_at exists) — otherwise it's a default instance that was never used,
|
|
111
|
+
// which must NOT raise a false "offline — reconnect" alert.
|
|
112
|
+
const last = Number(s.instance && s.instance.last_connected_at) || 0;
|
|
113
|
+
if (!last) return _rec('walle', { state: 'not_configured', detail: 'Set up Walle Remote to pair a phone.', origin: s.relay && s.relay.mobile_url });
|
|
114
|
+
const stale = (Date.now() - last) > WALLE_STALE_MS;
|
|
115
|
+
return _rec('walle', {
|
|
116
|
+
state: 'offline',
|
|
117
|
+
detail: stale ? 'Relay is not connected.' : 'Relay reconnecting…',
|
|
118
|
+
origin: s.relay && s.relay.mobile_url,
|
|
119
|
+
action: null, // no programmatic re-dial yet (relay client auto-reconnects); surface guidance
|
|
120
|
+
});
|
|
121
|
+
} catch (err) {
|
|
122
|
+
return _rec('walle', { state: 'error', detail: String(err?.message || err).slice(0, 200) });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Returns { expired, soon, notAfter } or null if cert can't be read/parsed.
|
|
127
|
+
function _certStatus(certPath) {
|
|
128
|
+
try {
|
|
129
|
+
if (!certPath || !fs.existsSync(certPath)) return null;
|
|
130
|
+
const x = new X509Certificate(fs.readFileSync(certPath));
|
|
131
|
+
const notAfter = new Date(x.validTo).getTime();
|
|
132
|
+
if (!Number.isFinite(notAfter)) return null;
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
return { expired: now > notAfter, soon: notAfter - now < CERT_SOON_MS, notAfter };
|
|
135
|
+
} catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async function probeTailscaleHealth(opts) {
|
|
141
|
+
try {
|
|
142
|
+
const t = await tailscale.detectTailscaleSetup(opts);
|
|
143
|
+
const tailscaleAuto = { kind: 'recover', endpoint: '/api/setup/network/tailscale-auto', method: 'POST' };
|
|
144
|
+
if (!t.installed) return _rec('tailscale', { state: 'not_configured', detail: 'Tailscale is not installed.' });
|
|
145
|
+
// "Set up for CTM phone access" = an HTTPS cert was generated. Without it, Tailscale
|
|
146
|
+
// may be installed/used for other things but isn't a CTM phone path — so a stopped/
|
|
147
|
+
// logged-out node must NOT raise a false "offline — reconnect" alert.
|
|
148
|
+
if (!t.cert_exists) {
|
|
149
|
+
return _rec('tailscale', {
|
|
150
|
+
state: 'not_configured',
|
|
151
|
+
detail: t.available ? 'Run auto setup to enable HTTPS for phones.' : 'Tailscale is not set up for phone access.',
|
|
152
|
+
origin: t.mobile_url, action: tailscaleAuto,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
// Cert exists → the user set this up. Now report real liveness (authoritative
|
|
156
|
+
// BackendState + cert validity), so a genuine break surfaces as offline/expired.
|
|
157
|
+
const cert = _certStatus(t.cert_path);
|
|
158
|
+
if (cert && cert.expired) {
|
|
159
|
+
return _rec('tailscale', { state: 'expired', detail: 'HTTPS certificate expired — regenerate.', origin: t.mobile_url, action: tailscaleAuto });
|
|
160
|
+
}
|
|
161
|
+
const backend = String(t.backend_state || '');
|
|
162
|
+
if (backend === 'NeedsLogin') {
|
|
163
|
+
return _rec('tailscale', { state: 'expired', detail: 'Tailscale needs sign-in — run “tailscale up”.', origin: t.mobile_url, action: tailscaleAuto });
|
|
164
|
+
}
|
|
165
|
+
if (!t.available || (backend && backend !== 'Running')) {
|
|
166
|
+
const detail = backend === 'Stopped' ? 'Tailscale is stopped — run “tailscale up”.'
|
|
167
|
+
: backend === 'Starting' ? 'Tailscale is starting…'
|
|
168
|
+
: 'Tailscale is not connected — run “tailscale up”.';
|
|
169
|
+
return _rec('tailscale', { state: backend === 'Starting' ? 'warning' : 'offline', detail, origin: t.mobile_url, action: tailscaleAuto });
|
|
170
|
+
}
|
|
171
|
+
if (cert && cert.soon) {
|
|
172
|
+
return _rec('tailscale', { state: 'warning', detail: 'HTTPS certificate expires soon.', origin: t.mobile_url, action: tailscaleAuto });
|
|
173
|
+
}
|
|
174
|
+
return _rec('tailscale', { state: 'ok', detail: 'Reachable on your tailnet.', origin: t.mobile_url });
|
|
175
|
+
} catch (err) {
|
|
176
|
+
return _rec('tailscale', { state: 'error', detail: String(err?.message || err).slice(0, 200) });
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Update each record's `since`/`checked_at` from the cache and return broken-edge transitions.
|
|
181
|
+
function _applyTransitions(records, nowIso) {
|
|
182
|
+
const transitions = [];
|
|
183
|
+
for (const r of records) {
|
|
184
|
+
const prev = _stateCache.get(r.method);
|
|
185
|
+
if (!prev || prev.state !== r.state) {
|
|
186
|
+
r.since = nowIso;
|
|
187
|
+
if (prev && !BROKEN.has(prev.state) && BROKEN.has(r.state)) {
|
|
188
|
+
transitions.push({ method: r.method, from: prev.state, to: r.state });
|
|
189
|
+
}
|
|
190
|
+
_stateCache.set(r.method, { state: r.state, since: nowIso });
|
|
191
|
+
} else {
|
|
192
|
+
r.since = prev.since;
|
|
193
|
+
}
|
|
194
|
+
r.checked_at = nowIso;
|
|
195
|
+
}
|
|
196
|
+
return transitions;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const _ORDER = { microsoft: 0, walle: 1, tailscale: 2 };
|
|
200
|
+
function _pickRecommended(records) {
|
|
201
|
+
const oks = records.filter((r) => r.state === 'ok');
|
|
202
|
+
if (oks.length >= 1) {
|
|
203
|
+
return oks.sort((a, b) => _ORDER[a.method] - _ORDER[b.method])[0].method;
|
|
204
|
+
}
|
|
205
|
+
return 'microsoft'; // simplest to set up (no VPN app, no custom domain)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function getConnectionHealth(deps = {}) {
|
|
209
|
+
const opts = { configDir: deps.configDir, port: deps.port };
|
|
210
|
+
const nowIso = new Date().toISOString();
|
|
211
|
+
const order = ['microsoft', 'walle', 'tailscale'];
|
|
212
|
+
const settled = await Promise.allSettled([
|
|
213
|
+
probeMicrosoftHealth(opts),
|
|
214
|
+
probeWalleHealth(deps.db),
|
|
215
|
+
probeTailscaleHealth(opts),
|
|
216
|
+
]);
|
|
217
|
+
const records = settled.map((s, i) => (s.status === 'fulfilled'
|
|
218
|
+
? s.value
|
|
219
|
+
: _rec(order[i], { state: 'error', detail: String(s.reason && s.reason.message || s.reason).slice(0, 200) })));
|
|
220
|
+
const transitions = _applyTransitions(records, nowIso);
|
|
221
|
+
return { ok: true, checked_at: nowIso, recommended: _pickRecommended(records), methods: records, transitions };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
module.exports = {
|
|
225
|
+
getConnectionHealth,
|
|
226
|
+
probeMicrosoftHealth,
|
|
227
|
+
probeWalleHealth,
|
|
228
|
+
probeTailscaleHealth,
|
|
229
|
+
BROKEN,
|
|
230
|
+
_stateCache,
|
|
231
|
+
_internal: { _certStatus, _applyTransitions, _pickRecommended, _renewalOverdueMs },
|
|
232
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Shared parse of a session_conversations cache blob into a sorted message array.
|
|
4
|
+
//
|
|
5
|
+
// This is the heavy work behind `session-messages:parseCacheBlob`: JSON.parse of a
|
|
6
|
+
// 0.5–2MB messages string (real Claude/Codex messages are deeply nested, so the parse
|
|
7
|
+
// is 150–360ms on the main thread per the [freeze-probe] samples), a codex-synthetic
|
|
8
|
+
// scan, and an in-place timeline sort. Extracted so it runs IDENTICALLY on the main
|
|
9
|
+
// thread (cold/first-load + pool-failure fallback) and on a read-pool worker (the
|
|
10
|
+
// off-thread re-parse for growing sessions) — parity by construction (same function).
|
|
11
|
+
//
|
|
12
|
+
// Deliberately dependency-light so a worker can require it without pulling in server.js:
|
|
13
|
+
// only lib/timeline-order + lib/session-history (both already worker-safe).
|
|
14
|
+
|
|
15
|
+
const { sortTimelineMessagesInPlace } = require('./timeline-order');
|
|
16
|
+
|
|
17
|
+
// Lazy + defensive: if session-history can't load in a worker context, treat nothing as
|
|
18
|
+
// synthetic (the main-thread path uses the real predicate; worst case a worker keeps a
|
|
19
|
+
// synthetic codex context row that main would have dropped — corrected on the next
|
|
20
|
+
// main-served parse). Mirrors lib/session-prompt-index.js's resolution.
|
|
21
|
+
let _isCodexSyntheticUserText = null;
|
|
22
|
+
function _codexSynthetic() {
|
|
23
|
+
if (_isCodexSyntheticUserText) return _isCodexSyntheticUserText;
|
|
24
|
+
try { _isCodexSyntheticUserText = require('./session-history').isCodexSyntheticUserText; }
|
|
25
|
+
catch { _isCodexSyntheticUserText = () => false; }
|
|
26
|
+
return _isCodexSyntheticUserText;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Returns the parsed+sorted message array, or null when the blob is empty/invalid or the
|
|
30
|
+
// conversation is entirely injected codex context (matches the server.js inline behavior
|
|
31
|
+
// that this replaces). Throws only on malformed JSON — callers catch and fall back.
|
|
32
|
+
function parseConversationBlob(messagesString) {
|
|
33
|
+
if (typeof messagesString !== 'string' || messagesString.length === 0) return null;
|
|
34
|
+
const messages = JSON.parse(messagesString);
|
|
35
|
+
if (!Array.isArray(messages) || messages.length === 0) return null;
|
|
36
|
+
const isSynthetic = _codexSynthetic();
|
|
37
|
+
if (messages.some((m) => m && m.role === 'user' && isSynthetic(m.text))) return null;
|
|
38
|
+
sortTimelineMessagesInPlace(messages);
|
|
39
|
+
return messages;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = { parseConversationBlob };
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
|
+
const { sortTimelineMessages, timelineTimestampMs } = require('./timeline-order');
|
|
4
5
|
|
|
5
6
|
const MESSAGE_ROLES = new Set(['user', 'assistant']);
|
|
6
7
|
|
|
8
|
+
// Below this length, identical text is too likely to be a legitimately-repeated short
|
|
9
|
+
// turn (e.g. a user typing "yes" twice) to safely use as a cross-timestamp dedup key.
|
|
10
|
+
const CONTENT_IDENTITY_MIN_LEN = 200;
|
|
11
|
+
|
|
7
12
|
function textHash(text) {
|
|
8
13
|
return crypto.createHash('sha1').update(String(text || '')).digest('hex').slice(0, 16);
|
|
9
14
|
}
|
|
@@ -14,32 +19,65 @@ function messageText(message) {
|
|
|
14
19
|
|
|
15
20
|
function timestampValue(timestamp) {
|
|
16
21
|
if (timestamp == null || timestamp === '') return '';
|
|
17
|
-
if (typeof timestamp === 'number' && Number.isFinite(timestamp)) {
|
|
18
|
-
try { return new Date(timestamp).toISOString(); } catch { return String(timestamp); }
|
|
19
|
-
}
|
|
20
22
|
const raw = String(timestamp || '');
|
|
21
|
-
const parsed =
|
|
22
|
-
if (
|
|
23
|
+
const parsed = timelineTimestampMs(timestamp);
|
|
24
|
+
if (parsed != null) {
|
|
25
|
+
try { return new Date(parsed).toISOString(); } catch {}
|
|
26
|
+
}
|
|
23
27
|
return raw;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
|
-
function
|
|
27
|
-
if (typeof timestamp === 'number' && Number.isFinite(timestamp)) return timestamp;
|
|
28
|
-
const parsed = Date.parse(String(timestamp || ''));
|
|
29
|
-
return Number.isFinite(parsed) ? parsed : null;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function messageKey(message) {
|
|
30
|
+
function messageKey(message, textHashValue) {
|
|
33
31
|
const role = String(message?.role || '').trim();
|
|
34
32
|
const text = messageText(message);
|
|
35
33
|
if (!role || !text) return '';
|
|
36
34
|
return [
|
|
37
35
|
role,
|
|
38
36
|
timestampValue(message?.timestamp),
|
|
39
|
-
textHash(text),
|
|
37
|
+
textHashValue || textHash(text),
|
|
40
38
|
].join(':');
|
|
41
39
|
}
|
|
42
40
|
|
|
41
|
+
function messageIdentityKeys(message, textHashValue) {
|
|
42
|
+
const role = String(message?.role || '').trim();
|
|
43
|
+
if (!MESSAGE_ROLES.has(role)) return [];
|
|
44
|
+
const keys = [];
|
|
45
|
+
const parentUuid = String(
|
|
46
|
+
message?.parentUuid
|
|
47
|
+
|| message?._parent
|
|
48
|
+
|| message?.metadata?.parentUuid
|
|
49
|
+
|| message?.metadata?.uuid
|
|
50
|
+
|| ''
|
|
51
|
+
).trim();
|
|
52
|
+
const ts = timestampValue(message?.timestamp);
|
|
53
|
+
if (parentUuid) keys.push(['provider', role, parentUuid].join(':'));
|
|
54
|
+
if (ts) keys.push(['timestamp', role, ts].join(':'));
|
|
55
|
+
// Content identity: collapses an exact-duplicate long turn that shares neither a
|
|
56
|
+
// parentUuid nor a timestamp with its twin — the provider-side hazards (stream-json
|
|
57
|
+
// duplicate rows, a non-streaming fallback re-bundling blocks, or a session resume
|
|
58
|
+
// rewriting history with fresh uuids/timestamps). Gated to long text so genuinely
|
|
59
|
+
// repeated short turns are never collapsed.
|
|
60
|
+
const text = messageText(message);
|
|
61
|
+
if (text.length >= CONTENT_IDENTITY_MIN_LEN) {
|
|
62
|
+
keys.push(['content', role, textHashValue || textHash(text)].join(':'));
|
|
63
|
+
}
|
|
64
|
+
return keys;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function messagesRepresentSameEvent(a, b) {
|
|
68
|
+
const aText = messageText(a);
|
|
69
|
+
const bText = messageText(b);
|
|
70
|
+
if (!aText || !bText) return false;
|
|
71
|
+
if (aText === bText) return true;
|
|
72
|
+
const shorter = aText.length <= bText.length ? aText : bText;
|
|
73
|
+
const longer = aText.length <= bText.length ? bText : aText;
|
|
74
|
+
// The conversation cache intentionally stores long rows as bounded visible
|
|
75
|
+
// text, while the live stream tail can still have the full JSONL row. Treat
|
|
76
|
+
// a long cached prefix and the full stream event as the same turn. Keep this
|
|
77
|
+
// bounded to long text so two short same-timestamp messages are not merged.
|
|
78
|
+
return shorter.length >= 512 && longer.startsWith(shorter);
|
|
79
|
+
}
|
|
80
|
+
|
|
43
81
|
function eventRole(event) {
|
|
44
82
|
const type = String(event?.type || '').trim();
|
|
45
83
|
if (MESSAGE_ROLES.has(type)) return type;
|
|
@@ -64,33 +102,32 @@ function streamEventToMessage(event) {
|
|
|
64
102
|
};
|
|
65
103
|
if (event?.eventId) message.streamEventId = event.eventId;
|
|
66
104
|
if (Array.isArray(event?.data?.contentBlocks)) message.contentBlocks = event.data.contentBlocks;
|
|
105
|
+
if (event?.data?.parentUuid) message.parentUuid = event.data.parentUuid;
|
|
67
106
|
return message;
|
|
68
107
|
}
|
|
69
108
|
|
|
70
109
|
function sortMessagesChronologically(messages) {
|
|
71
|
-
return messages
|
|
72
|
-
.slice()
|
|
73
|
-
.sort((a, b) => {
|
|
74
|
-
const ams = timestampMs(a.timestamp);
|
|
75
|
-
const bms = timestampMs(b.timestamp);
|
|
76
|
-
if (ams != null && bms != null && ams !== bms) return ams - bms;
|
|
77
|
-
if (ams != null && bms == null) return -1;
|
|
78
|
-
if (ams == null && bms != null) return 1;
|
|
79
|
-
if (a._mergeSourceOrder !== b._mergeSourceOrder) return a._mergeSourceOrder - b._mergeSourceOrder;
|
|
80
|
-
return a._mergeIndex - b._mergeIndex;
|
|
81
|
-
})
|
|
110
|
+
return sortTimelineMessages(messages)
|
|
82
111
|
.map(({ _mergeSourceOrder, _mergeIndex, ...message }) => message);
|
|
83
112
|
}
|
|
84
113
|
|
|
85
114
|
function mergeMessagesWithStreamEvents(messages, events) {
|
|
86
115
|
const merged = [];
|
|
87
116
|
const seen = new Set();
|
|
117
|
+
const byIdentity = new Map();
|
|
88
118
|
let added = 0;
|
|
119
|
+
let replaced = 0;
|
|
89
120
|
|
|
90
121
|
const addMessage = (message, sourceOrder, index, fromStream = false) => {
|
|
91
122
|
const text = messageText(message);
|
|
92
123
|
const role = String(message?.role || '').trim();
|
|
93
|
-
if (!
|
|
124
|
+
if (!role || !text) return;
|
|
125
|
+
// Only user/assistant turns ever arrive via the live stream ring; durable
|
|
126
|
+
// base messages with other roles (tool-result rows, structured-capture
|
|
127
|
+
// system rows) must pass through, or a hot stream tail hides them. They
|
|
128
|
+
// get exact-key dedup below but no identity-key merging (identity keys
|
|
129
|
+
// are user/assistant-only).
|
|
130
|
+
if (fromStream && !MESSAGE_ROLES.has(role)) return;
|
|
94
131
|
const normalized = {
|
|
95
132
|
...message,
|
|
96
133
|
role,
|
|
@@ -99,9 +136,34 @@ function mergeMessagesWithStreamEvents(messages, events) {
|
|
|
99
136
|
_mergeSourceOrder: sourceOrder,
|
|
100
137
|
_mergeIndex: index,
|
|
101
138
|
};
|
|
102
|
-
const
|
|
139
|
+
const th = textHash(text);
|
|
140
|
+
const key = messageKey(normalized, th);
|
|
103
141
|
if (!key || seen.has(key)) return;
|
|
142
|
+
const identityKeys = messageIdentityKeys(normalized, th);
|
|
143
|
+
for (const identityKey of identityKeys) {
|
|
144
|
+
if (!byIdentity.has(identityKey)) continue;
|
|
145
|
+
const existing = byIdentity.get(identityKey);
|
|
146
|
+
const existingMessage = merged[existing.index];
|
|
147
|
+
// A shared provider key (parentUuid) means the SAME provider row: a still-streaming
|
|
148
|
+
// partial in the live ring and its finalized copy in the cache. Collapse it
|
|
149
|
+
// unconditionally (keep the longer text) — the partial is not always a clean >=512
|
|
150
|
+
// prefix of the final, so messagesRepresentSameEvent's prefix gate can miss it.
|
|
151
|
+
// Weaker keys (timestamp / content-hash) still require a text match so two genuinely
|
|
152
|
+
// different turns that merely collide on one weak key are never merged.
|
|
153
|
+
const sameEvent = identityKey.startsWith('provider:')
|
|
154
|
+
|| messagesRepresentSameEvent(existingMessage, normalized);
|
|
155
|
+
if (sameEvent) {
|
|
156
|
+
seen.add(key);
|
|
157
|
+
if (text.length > messageText(existingMessage).length) {
|
|
158
|
+
merged[existing.index] = normalized;
|
|
159
|
+
replaced++;
|
|
160
|
+
}
|
|
161
|
+
identityKeys.forEach(k => byIdentity.set(k, existing));
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
104
165
|
seen.add(key);
|
|
166
|
+
identityKeys.forEach(identityKey => byIdentity.set(identityKey, { index: merged.length, fromStream }));
|
|
105
167
|
merged.push(normalized);
|
|
106
168
|
if (fromStream) added++;
|
|
107
169
|
};
|
|
@@ -117,6 +179,7 @@ function mergeMessagesWithStreamEvents(messages, events) {
|
|
|
117
179
|
return {
|
|
118
180
|
messages: sortMessagesChronologically(merged),
|
|
119
181
|
added,
|
|
182
|
+
replaced,
|
|
120
183
|
};
|
|
121
184
|
}
|
|
122
185
|
|
|
@@ -13,7 +13,7 @@ function loadCtmSessionContext() {
|
|
|
13
13
|
return ctmSessionContext;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
function handleCtmSessionContextApi(req, res, url, { db, dbModule } = {}) {
|
|
16
|
+
function handleCtmSessionContextApi(req, res, url, { db, dbModule, runHeavy } = {}) {
|
|
17
17
|
if (!url.pathname.startsWith('/api/ctm/session-memory')) return false;
|
|
18
18
|
|
|
19
19
|
const context = loadCtmSessionContext();
|
|
@@ -26,19 +26,46 @@ function handleCtmSessionContextApi(req, res, url, { db, dbModule } = {}) {
|
|
|
26
26
|
});
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
// The search / context / context-pack reads are the heavy ones (FTS + LIKE scans over
|
|
30
|
+
// 220k+ message rows and a tens-of-MB conversation-blob column, plus full JSON.parse of
|
|
31
|
+
// matched conversations) — a single search was measured blocking the main loop 7.74s.
|
|
32
|
+
// When the server wires an off-thread runner, run them on the read-pool worker and reply
|
|
33
|
+
// when it resolves; `args` is the plain (clone-safe) input the worker re-runs with its own
|
|
34
|
+
// read-only connection, and `computeSync` is the main-thread fallback used when no runner
|
|
35
|
+
// is wired (tests / direct callers) or the pool is unavailable.
|
|
36
|
+
const serveHeavy = (op, args, computeSync) => {
|
|
37
|
+
if (typeof runHeavy !== 'function') {
|
|
38
|
+
return respondJson(res, 200, asApiResult(computeSync()));
|
|
39
|
+
}
|
|
40
|
+
Promise.resolve()
|
|
41
|
+
.then(() => runHeavy(op, args, computeSync))
|
|
42
|
+
.then((result) => respondJson(res, 200, asApiResult(result)))
|
|
43
|
+
.catch((err) => respondJson(res, 500, {
|
|
44
|
+
ok: false,
|
|
45
|
+
source: 'ctm-api',
|
|
46
|
+
authority: 'ctm',
|
|
47
|
+
reason: 'ctm_session_context_failed',
|
|
48
|
+
error: err.message,
|
|
49
|
+
}));
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
29
53
|
try {
|
|
30
54
|
if (url.pathname === '/api/ctm/session-memory/health' && req.method === 'GET') {
|
|
31
55
|
return respondJson(res, 200, asApiResult(context.getCtmDbHealth({ db })));
|
|
32
56
|
}
|
|
33
57
|
|
|
34
58
|
if (url.pathname === '/api/ctm/session-memory/search' && req.method === 'GET') {
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
59
|
+
const args = {
|
|
60
|
+
query: url.searchParams.get('q') || url.searchParams.get('query') || '',
|
|
61
|
+
limit: url.searchParams.get('limit') || undefined,
|
|
62
|
+
};
|
|
63
|
+
return serveHeavy('ctmSessionMemorySearch', args,
|
|
64
|
+
() => context.searchCtmSessions({ ...args, db }));
|
|
38
65
|
}
|
|
39
66
|
|
|
40
67
|
if (url.pathname === '/api/ctm/session-memory/context' && req.method === 'GET') {
|
|
41
|
-
|
|
68
|
+
const args = {
|
|
42
69
|
session_id: url.searchParams.get('session_id') || url.searchParams.get('id') || '',
|
|
43
70
|
session_ids: parseListParams(url.searchParams, 'session_id', 'session_ids', 'ids'),
|
|
44
71
|
limit: url.searchParams.get('limit') || undefined,
|
|
@@ -46,8 +73,9 @@ function handleCtmSessionContextApi(req, res, url, { db, dbModule } = {}) {
|
|
|
46
73
|
include_raw: parseBool(url.searchParams.get('include_raw')),
|
|
47
74
|
dedupe: !parseBool(url.searchParams.get('no_dedupe')),
|
|
48
75
|
format: url.searchParams.get('format') || 'messages',
|
|
49
|
-
|
|
50
|
-
|
|
76
|
+
};
|
|
77
|
+
return serveHeavy('ctmSessionMemoryContext', args,
|
|
78
|
+
() => context.getCtmSessionContext({ ...args, db }));
|
|
51
79
|
}
|
|
52
80
|
|
|
53
81
|
if (
|
|
@@ -76,7 +104,7 @@ function handleCtmSessionContextApi(req, res, url, { db, dbModule } = {}) {
|
|
|
76
104
|
if (url.pathname === '/api/ctm/session-memory/context-pack' && req.method === 'POST') {
|
|
77
105
|
readJsonBody(req, 128 * 1024)
|
|
78
106
|
.then((body) => {
|
|
79
|
-
|
|
107
|
+
const args = {
|
|
80
108
|
task: body.task || '',
|
|
81
109
|
query: body.query || '',
|
|
82
110
|
session_ids: body.session_ids || body.ids,
|
|
@@ -84,8 +112,9 @@ function handleCtmSessionContextApi(req, res, url, { db, dbModule } = {}) {
|
|
|
84
112
|
token_budget: body.token_budget,
|
|
85
113
|
include_raw: Boolean(body.include_raw),
|
|
86
114
|
mode: body.mode || 'auto',
|
|
87
|
-
|
|
88
|
-
|
|
115
|
+
};
|
|
116
|
+
serveHeavy('ctmSessionMemoryContextPack', args,
|
|
117
|
+
() => context.buildContextPack({ ...args, db }));
|
|
89
118
|
})
|
|
90
119
|
.catch((err) => respondJson(res, 400, {
|
|
91
120
|
ok: false,
|