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
|
@@ -15,12 +15,14 @@
|
|
|
15
15
|
|
|
16
16
|
const fs = require('fs');
|
|
17
17
|
const path = require('path');
|
|
18
|
+
const crypto = require('crypto');
|
|
18
19
|
const claudeDesktopSessions = require('./lib/claude-desktop-sessions');
|
|
19
20
|
const {
|
|
20
21
|
codexRolloutIdFromPath,
|
|
21
22
|
findCodexSessionFiles,
|
|
22
23
|
findCodexSessionFilesByFilename,
|
|
23
24
|
findCodexStateRolloutPath,
|
|
25
|
+
readCodexRolloutMetadata,
|
|
24
26
|
} = require('./lib/session-history');
|
|
25
27
|
|
|
26
28
|
const CLAUDE_PROJECTS_DIR = path.join(process.env.HOME, '.claude', 'projects');
|
|
@@ -64,6 +66,42 @@ function isCodexSessionRow(row) {
|
|
|
64
66
|
|| String(row?.jsonl_path || '').includes(`${path.sep}.codex${path.sep}sessions${path.sep}`);
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
function providerChildThreadFields(row = {}) {
|
|
70
|
+
const threadSource = String(row.thread_source || row.threadSource || '').trim().toLowerCase();
|
|
71
|
+
const parentAgentSessionId = String(
|
|
72
|
+
row.parent_agent_session_id
|
|
73
|
+
|| row.parentAgentSessionId
|
|
74
|
+
|| row.parent_thread_id
|
|
75
|
+
|| row.parentThreadId
|
|
76
|
+
|| ''
|
|
77
|
+
).trim();
|
|
78
|
+
return { threadSource, parentAgentSessionId };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function isProviderChildAgentRow(row = {}) {
|
|
82
|
+
const provider = String(row.provider || '').trim().toLowerCase();
|
|
83
|
+
const { threadSource, parentAgentSessionId } = providerChildThreadFields(row);
|
|
84
|
+
return provider === 'codex' && threadSource === 'subagent' && !!parentAgentSessionId;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function addAgentRowFileIds(dbFileIds, row = {}) {
|
|
88
|
+
if (row.id) dbFileIds.add(row.id);
|
|
89
|
+
if (row.agent_session_id) dbFileIds.add(row.agent_session_id);
|
|
90
|
+
const jsonlPath = String(row.jsonl_path || '').trim();
|
|
91
|
+
if (jsonlPath) {
|
|
92
|
+
const fileId = sessionFileIdFromPath(jsonlPath);
|
|
93
|
+
if (fileId) dbFileIds.add(fileId);
|
|
94
|
+
const rolloutId = codexRolloutIdFromPath(jsonlPath);
|
|
95
|
+
if (rolloutId) dbFileIds.add(rolloutId);
|
|
96
|
+
if (isCodexSessionRow(row)) {
|
|
97
|
+
try {
|
|
98
|
+
const meta = readCodexRolloutMetadata(jsonlPath) || {};
|
|
99
|
+
if (meta.id) dbFileIds.add(String(meta.id));
|
|
100
|
+
} catch {}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
67
105
|
function resolveCodexCanonicalFile(expectedFileId, fileIndex) {
|
|
68
106
|
if (!expectedFileId) return null;
|
|
69
107
|
if (fileIndex[expectedFileId]) return fileIndex[expectedFileId];
|
|
@@ -291,18 +329,31 @@ function detectMismatches(db, getAllSessionFiles) {
|
|
|
291
329
|
// Check first and select accordingly.
|
|
292
330
|
let hasSlugColumn = false;
|
|
293
331
|
let hasProviderResumeColumn = false;
|
|
332
|
+
let hasThreadSourceColumn = false;
|
|
333
|
+
let hasParentAgentSessionColumn = false;
|
|
334
|
+
let hasAgentNicknameColumn = false;
|
|
335
|
+
let hasAgentRoleColumn = false;
|
|
294
336
|
try {
|
|
295
337
|
const cols = db.prepare('PRAGMA table_info(agent_sessions)').all();
|
|
296
338
|
hasSlugColumn = cols.some((c) => c.name === 'slug');
|
|
297
339
|
hasProviderResumeColumn = cols.some((c) => c.name === 'provider_resume_id');
|
|
340
|
+
hasThreadSourceColumn = cols.some((c) => c.name === 'thread_source');
|
|
341
|
+
hasParentAgentSessionColumn = cols.some((c) => c.name === 'parent_agent_session_id');
|
|
342
|
+
hasAgentNicknameColumn = cols.some((c) => c.name === 'agent_nickname');
|
|
343
|
+
hasAgentRoleColumn = cols.some((c) => c.name === 'agent_role');
|
|
298
344
|
} catch {}
|
|
299
345
|
const slugCol = hasSlugColumn ? 'a.slug' : "'' AS slug";
|
|
300
346
|
const providerResumeCol = hasProviderResumeColumn ? 'a.provider_resume_id' : "'' AS provider_resume_id";
|
|
347
|
+
const threadSourceCol = hasThreadSourceColumn ? 'a.thread_source' : "'' AS thread_source";
|
|
348
|
+
const parentAgentSessionCol = hasParentAgentSessionColumn ? 'a.parent_agent_session_id' : "'' AS parent_agent_session_id";
|
|
349
|
+
const agentNicknameCol = hasAgentNicknameColumn ? 'a.agent_nickname' : "'' AS agent_nickname";
|
|
350
|
+
const agentRoleCol = hasAgentRoleColumn ? 'a.agent_role' : "'' AS agent_role";
|
|
301
351
|
allSessions = db.prepare(`
|
|
302
352
|
SELECT c.id, c.provider, c.title, c.user_renamed, c.starred, c.project_path, c.cwd,
|
|
303
353
|
c.created_at, c.updated_at,
|
|
304
354
|
a.agent_session_id, ${providerResumeCol}, a.jsonl_path, a.file_size, a.first_message,
|
|
305
355
|
a.modified_at, a.hostname, a.model, a.git_branch, a.user_msg_count,
|
|
356
|
+
${threadSourceCol}, ${parentAgentSessionCol}, ${agentNicknameCol}, ${agentRoleCol},
|
|
306
357
|
${slugCol}
|
|
307
358
|
FROM ctm_sessions c
|
|
308
359
|
LEFT JOIN agent_sessions a ON a.ctm_session_id = c.id
|
|
@@ -600,8 +651,7 @@ function detectMismatches(db, getAllSessionFiles) {
|
|
|
600
651
|
// Check 6: Orphan files (on disk but not in DB)
|
|
601
652
|
const dbFileIds = new Set();
|
|
602
653
|
for (const row of allSessions) {
|
|
603
|
-
dbFileIds
|
|
604
|
-
if (row.agent_session_id) dbFileIds.add(row.agent_session_id);
|
|
654
|
+
addAgentRowFileIds(dbFileIds, row);
|
|
605
655
|
if (isCodexSessionRow(row) && row.agent_session_id) {
|
|
606
656
|
try {
|
|
607
657
|
for (const filePath of findCodexSessionFiles(row.agent_session_id)) {
|
|
@@ -611,6 +661,27 @@ function detectMismatches(db, getAllSessionFiles) {
|
|
|
611
661
|
} catch {}
|
|
612
662
|
}
|
|
613
663
|
}
|
|
664
|
+
try {
|
|
665
|
+
const agentCols = new Set(db.prepare('PRAGMA table_info(agent_sessions)').all().map(c => c.name));
|
|
666
|
+
const threadSourceCol = agentCols.has('thread_source') ? 'thread_source' : "'' AS thread_source";
|
|
667
|
+
const parentAgentSessionCol = agentCols.has('parent_agent_session_id') ? 'parent_agent_session_id' : "'' AS parent_agent_session_id";
|
|
668
|
+
const allAgentRows = db.prepare(`
|
|
669
|
+
SELECT agent_session_id, ctm_session_id, provider, jsonl_path,
|
|
670
|
+
${threadSourceCol}, ${parentAgentSessionCol}
|
|
671
|
+
FROM agent_sessions
|
|
672
|
+
`).all();
|
|
673
|
+
for (const row of allAgentRows) {
|
|
674
|
+
addAgentRowFileIds(dbFileIds, row);
|
|
675
|
+
if (isCodexSessionRow(row) && row.agent_session_id) {
|
|
676
|
+
try {
|
|
677
|
+
for (const filePath of findCodexSessionFiles(row.agent_session_id)) {
|
|
678
|
+
const rolloutFileId = sessionFileIdFromPath(filePath);
|
|
679
|
+
if (rolloutFileId) dbFileIds.add(rolloutFileId);
|
|
680
|
+
}
|
|
681
|
+
} catch {}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
} catch {}
|
|
614
685
|
for (const issue of issues) {
|
|
615
686
|
if (issue.type === 'codex_agent_jsonl_mismatch' && issue.details?.file_agent_session_id) {
|
|
616
687
|
dbFileIds.add(issue.details.file_agent_session_id);
|
|
@@ -706,25 +777,39 @@ function detectMismatches(db, getAllSessionFiles) {
|
|
|
706
777
|
});
|
|
707
778
|
}
|
|
708
779
|
|
|
709
|
-
// Check 7: Orphan agent_sessions (no valid ctm_session parent)
|
|
710
|
-
//
|
|
711
|
-
//
|
|
780
|
+
// Check 7: Orphan top-level agent_sessions (no valid ctm_session parent).
|
|
781
|
+
// Provider-native child threads (Codex subagents, etc.) are deliberately
|
|
782
|
+
// indexed as agent identities without top-level CTM tabs. Promoting them here
|
|
783
|
+
// creates the one-message "ghost sessions" that pollute Recent Sessions.
|
|
712
784
|
try {
|
|
785
|
+
const agentCols = new Set(db.prepare('PRAGMA table_info(agent_sessions)').all().map(c => c.name));
|
|
786
|
+
const threadSourceCol = agentCols.has('thread_source') ? 'a.thread_source' : "'' AS thread_source";
|
|
787
|
+
const parentAgentSessionCol = agentCols.has('parent_agent_session_id') ? 'a.parent_agent_session_id' : "'' AS parent_agent_session_id";
|
|
788
|
+
const agentNicknameCol = agentCols.has('agent_nickname') ? 'a.agent_nickname' : "'' AS agent_nickname";
|
|
789
|
+
const agentRoleCol = agentCols.has('agent_role') ? 'a.agent_role' : "'' AS agent_role";
|
|
713
790
|
const orphanAgents = db.prepare(`
|
|
714
|
-
SELECT a.agent_session_id, a.ctm_session_id, a.
|
|
791
|
+
SELECT a.agent_session_id, a.ctm_session_id, a.provider, a.project_path,
|
|
792
|
+
a.jsonl_path, a.file_size,
|
|
793
|
+
${threadSourceCol}, ${parentAgentSessionCol}, ${agentNicknameCol}, ${agentRoleCol}
|
|
715
794
|
FROM agent_sessions a
|
|
716
795
|
LEFT JOIN ctm_sessions c ON c.id = a.ctm_session_id
|
|
717
796
|
WHERE a.ctm_session_id IS NULL OR c.id IS NULL
|
|
718
797
|
`).all();
|
|
719
798
|
for (const row of orphanAgents) {
|
|
799
|
+
if (isProviderChildAgentRow(row)) continue;
|
|
720
800
|
issues.push({
|
|
721
801
|
type: 'orphan_agent_session', severity: 'warning', sessionId: row.agent_session_id,
|
|
722
802
|
details: {
|
|
723
803
|
agent_session_id: row.agent_session_id,
|
|
724
804
|
stale_ctm_id: row.ctm_session_id || null,
|
|
805
|
+
provider: row.provider || '',
|
|
725
806
|
project_path: row.project_path || '',
|
|
726
807
|
jsonl_path: row.jsonl_path || '',
|
|
727
808
|
file_size: row.file_size || 0,
|
|
809
|
+
thread_source: row.thread_source || '',
|
|
810
|
+
parent_agent_session_id: row.parent_agent_session_id || '',
|
|
811
|
+
agent_nickname: row.agent_nickname || '',
|
|
812
|
+
agent_role: row.agent_role || '',
|
|
728
813
|
},
|
|
729
814
|
suggestion: 'agent_session has no valid ctm_session parent. Will auto-create parent.',
|
|
730
815
|
});
|
|
@@ -1082,9 +1167,15 @@ function recoverMismatches(db, issues, getAllSessionFiles) {
|
|
|
1082
1167
|
|
|
1083
1168
|
case 'orphan_agent_session': {
|
|
1084
1169
|
// Auto-create a ctm_session parent for orphaned agent_sessions.
|
|
1085
|
-
// This restores the invariant
|
|
1170
|
+
// This restores the invariant for top-level agent_sessions. Provider
|
|
1171
|
+
// child threads are intentionally parentless from CTM's tab model.
|
|
1086
1172
|
const agentId = issue.details.agent_session_id;
|
|
1087
1173
|
if (!agentId) { result.skipped++; break; }
|
|
1174
|
+
if (isProviderChildAgentRow(issue.details || {})) {
|
|
1175
|
+
result.skipped++;
|
|
1176
|
+
result.actions.push(`Skipped provider child agent ${agentId.slice(0, 8)}; child threads are not CTM tabs`);
|
|
1177
|
+
break;
|
|
1178
|
+
}
|
|
1088
1179
|
try {
|
|
1089
1180
|
const txn = db.transaction(() => {
|
|
1090
1181
|
// Create a new ctm_session using the agent_session_id as the CTM id
|
|
@@ -1277,12 +1368,16 @@ function repairSafeRelinkSwaps(db, auditResult = null) {
|
|
|
1277
1368
|
const projectA = rowA.project_path || stA.cwd || ctmRowA.cwd || '';
|
|
1278
1369
|
const projectB = rowB.project_path || stB.cwd || ctmRowB.cwd || '';
|
|
1279
1370
|
_updateColumns(db, 'startup_tasks', 'ctm_session_id', ctmA, {
|
|
1371
|
+
agent_session_id: desiredA,
|
|
1372
|
+
claude_session_id: desiredA,
|
|
1280
1373
|
agent_project_dir: _dirForJsonl(rowA.jsonl_path),
|
|
1281
1374
|
claude_project_dir: _dirForJsonl(rowA.jsonl_path),
|
|
1282
1375
|
cwd: projectA,
|
|
1283
1376
|
worktree_path: /\/\.(?:claude|walle)\/worktrees\//.test(projectA) ? projectA : null,
|
|
1284
1377
|
});
|
|
1285
1378
|
_updateColumns(db, 'startup_tasks', 'ctm_session_id', ctmB, {
|
|
1379
|
+
agent_session_id: desiredB,
|
|
1380
|
+
claude_session_id: desiredB,
|
|
1286
1381
|
agent_project_dir: _dirForJsonl(rowB.jsonl_path),
|
|
1287
1382
|
claude_project_dir: _dirForJsonl(rowB.jsonl_path),
|
|
1288
1383
|
cwd: projectB,
|
|
@@ -1320,6 +1415,11 @@ const INTEGRITY_ISSUE_TELEMETRY_TTL_MS = 6 * 60 * 60 * 1000;
|
|
|
1320
1415
|
const INTEGRITY_ISSUE_TELEMETRY_DETAIL_CAP = 5;
|
|
1321
1416
|
const _lastIntegrityIssueTelemetry = new Map();
|
|
1322
1417
|
|
|
1418
|
+
function integrityDetailTelemetryEnabled() {
|
|
1419
|
+
const value = String(process.env.CTM_SESSION_INTEGRITY_DETAIL_TELEMETRY || '').toLowerCase();
|
|
1420
|
+
return value === '1' || value === 'true' || value === 'yes';
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1323
1423
|
function integrityIssueTelemetryKey(issue) {
|
|
1324
1424
|
const details = issue && issue.details && typeof issue.details === 'object'
|
|
1325
1425
|
? Object.keys(issue.details).sort().join('|')
|
|
@@ -1349,7 +1449,7 @@ function integrityIssueTelemetryDetails(issue) {
|
|
|
1349
1449
|
? issue.details
|
|
1350
1450
|
: {};
|
|
1351
1451
|
const detailKeys = Object.keys(details).sort();
|
|
1352
|
-
const detailsHash =
|
|
1452
|
+
const detailsHash = crypto
|
|
1353
1453
|
.createHash('sha256')
|
|
1354
1454
|
.update(JSON.stringify(details))
|
|
1355
1455
|
.digest('hex')
|
|
@@ -1360,8 +1460,40 @@ function integrityIssueTelemetryDetails(issue) {
|
|
|
1360
1460
|
};
|
|
1361
1461
|
}
|
|
1362
1462
|
|
|
1463
|
+
function integrityIssueTelemetrySummary(issues) {
|
|
1464
|
+
const byType = {};
|
|
1465
|
+
const bySeverity = {};
|
|
1466
|
+
const sampleHashes = [];
|
|
1467
|
+
const seenHashes = new Set();
|
|
1468
|
+
for (const issue of issues || []) {
|
|
1469
|
+
const type = issue?.type || 'unknown';
|
|
1470
|
+
const severity = issue?.severity || 'unknown';
|
|
1471
|
+
byType[type] = (byType[type] || 0) + 1;
|
|
1472
|
+
bySeverity[severity] = (bySeverity[severity] || 0) + 1;
|
|
1473
|
+
if (sampleHashes.length < 10) {
|
|
1474
|
+
const details = integrityIssueTelemetryDetails(issue);
|
|
1475
|
+
const key = `${type}:${severity}:${details.details_hash}`;
|
|
1476
|
+
if (!seenHashes.has(key)) {
|
|
1477
|
+
seenHashes.add(key);
|
|
1478
|
+
sampleHashes.push({
|
|
1479
|
+
type,
|
|
1480
|
+
severity,
|
|
1481
|
+
detail_keys: details.detail_keys,
|
|
1482
|
+
details_hash: details.details_hash,
|
|
1483
|
+
});
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
return {
|
|
1488
|
+
issue_count: (issues || []).length,
|
|
1489
|
+
by_type: JSON.stringify(byType),
|
|
1490
|
+
by_severity: JSON.stringify(bySeverity),
|
|
1491
|
+
sample_hashes: JSON.stringify(sampleHashes),
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1363
1495
|
/**
|
|
1364
|
-
* Report integrity check results to telemetry with
|
|
1496
|
+
* Report integrity check results to telemetry with bounded debug context.
|
|
1365
1497
|
*/
|
|
1366
1498
|
function reportToTelemetry(telemetry, issues, recovery) {
|
|
1367
1499
|
if (!telemetry || typeof telemetry.track !== 'function') return;
|
|
@@ -1382,16 +1514,24 @@ function reportToTelemetry(telemetry, issues, recovery) {
|
|
|
1382
1514
|
recovery_skipped: recovery ? recovery.skipped : 0,
|
|
1383
1515
|
});
|
|
1384
1516
|
|
|
1385
|
-
//
|
|
1517
|
+
// One low-cardinality issue summary keeps fleet visibility without emitting
|
|
1518
|
+
// one telemetry row per mismatched session. Detailed issue rows are opt-in for
|
|
1519
|
+
// local debugging because old fleets made this event type very noisy.
|
|
1386
1520
|
const serious = issues.filter(i => i.severity === 'error' || i.severity === 'critical');
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1521
|
+
if (serious.length > 0) {
|
|
1522
|
+
telemetry.track('session_integrity_issue_summary', integrityIssueTelemetrySummary(serious));
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (integrityDetailTelemetryEnabled()) {
|
|
1526
|
+
for (const issue of serious.filter(issue => shouldReportIntegrityIssue(issue)).slice(0, INTEGRITY_ISSUE_TELEMETRY_DETAIL_CAP)) {
|
|
1527
|
+
const detailMeta = integrityIssueTelemetryDetails(issue);
|
|
1528
|
+
telemetry.track('session_integrity_issue', {
|
|
1529
|
+
type: issue.type,
|
|
1530
|
+
severity: issue.severity,
|
|
1531
|
+
session_id: issue.sessionId ? issue.sessionId.slice(0, 8) : null,
|
|
1532
|
+
...detailMeta,
|
|
1533
|
+
});
|
|
1534
|
+
}
|
|
1395
1535
|
}
|
|
1396
1536
|
|
|
1397
1537
|
// Recovery actions
|
|
@@ -1521,6 +1661,8 @@ module.exports = {
|
|
|
1521
1661
|
integrityIssueTelemetryKey,
|
|
1522
1662
|
shouldReportIntegrityIssue,
|
|
1523
1663
|
integrityIssueTelemetryDetails,
|
|
1664
|
+
integrityIssueTelemetrySummary,
|
|
1665
|
+
integrityDetailTelemetryEnabled,
|
|
1524
1666
|
resetIntegrityIssueTelemetry: () => _lastIntegrityIssueTelemetry.clear(),
|
|
1525
1667
|
},
|
|
1526
1668
|
};
|
|
@@ -59,6 +59,7 @@ function searchMatchFieldPriority(hit) {
|
|
|
59
59
|
const field = String(hit?._matchField || '');
|
|
60
60
|
if (field === 'session_id') return 100;
|
|
61
61
|
if (hit?._isExactTitle) return 95;
|
|
62
|
+
if (field === 'session_reference') return 93;
|
|
62
63
|
if (field === 'message' && hit?._messageStrong) return 92;
|
|
63
64
|
if (field === 'title') return 90;
|
|
64
65
|
if (field === 'message') return 80;
|
|
@@ -61,6 +61,12 @@ function messageText(messageOrContent) {
|
|
|
61
61
|
return walleTranscript.extractText(content);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
function shouldSkipLegacyWalleSessionFile(file, fileSet) {
|
|
65
|
+
const sessionId = String(file || '').replace(/\.jsonl(\.bak)?$/, '');
|
|
66
|
+
if (!sessionId || sessionId.startsWith('walle-')) return false;
|
|
67
|
+
return fileSet.has(`walle-${sessionId}.jsonl`) || fileSet.has(`walle-${sessionId}.jsonl.bak`);
|
|
68
|
+
}
|
|
69
|
+
|
|
64
70
|
function pushWalleSessionFiles(results) {
|
|
65
71
|
const sessionsDir = walleTranscript.defaultSessionsDir(process.env);
|
|
66
72
|
let files;
|
|
@@ -69,6 +75,7 @@ function pushWalleSessionFiles(results) {
|
|
|
69
75
|
for (const file of files) {
|
|
70
76
|
if (!file.endsWith('.jsonl') && !file.endsWith('.jsonl.bak')) continue;
|
|
71
77
|
if (file.endsWith('.jsonl.bak') && fileSet.has(file.replace(/\.bak$/, ''))) continue;
|
|
78
|
+
if (shouldSkipLegacyWalleSessionFile(file, fileSet)) continue;
|
|
72
79
|
const filePath = path.join(sessionsDir, file);
|
|
73
80
|
let stat;
|
|
74
81
|
try { stat = fs.statSync(filePath); } catch { continue; }
|
|
@@ -123,6 +130,7 @@ async function pushWalleSessionFilesAsync(results) {
|
|
|
123
130
|
for (const file of files) {
|
|
124
131
|
if (!file.endsWith('.jsonl') && !file.endsWith('.jsonl.bak')) continue;
|
|
125
132
|
if (file.endsWith('.jsonl.bak') && fileSet.has(file.replace(/\.bak$/, ''))) continue;
|
|
133
|
+
if (shouldSkipLegacyWalleSessionFile(file, fileSet)) continue;
|
|
126
134
|
const filePath = path.join(sessionsDir, file);
|
|
127
135
|
let stat;
|
|
128
136
|
try { stat = await fsp.stat(filePath); } catch { continue; }
|
|
@@ -175,20 +183,27 @@ function parseSessionFile(filePath, projectPath, projectEntry) {
|
|
|
175
183
|
const meta = readCodexRolloutMetadata(filePath) || {};
|
|
176
184
|
const cwd = meta.cwd || projectPath || path.dirname(filePath);
|
|
177
185
|
const modelId = meta.model || '';
|
|
186
|
+
const firstMessage = meta.first_user_message || meta.title || '';
|
|
187
|
+
const title = firstMessage
|
|
188
|
+
? firstMessage.split('\n')[0].replace(/^#+\s*/, '').replace(/[*_`]/g, '').trim().slice(0, 80)
|
|
189
|
+
: '';
|
|
190
|
+
const createdAt = meta.created_at
|
|
191
|
+
? new Date(Number(meta.created_at) * 1000).toISOString()
|
|
192
|
+
: modifiedAt;
|
|
178
193
|
return {
|
|
179
194
|
sessionId: meta.id || codexSessionId,
|
|
180
195
|
project: cwd,
|
|
181
196
|
projectEntry: 'codex',
|
|
182
197
|
cwd,
|
|
183
|
-
firstMessage
|
|
184
|
-
lastUserContent:
|
|
198
|
+
firstMessage,
|
|
199
|
+
lastUserContent: firstMessage,
|
|
185
200
|
firstAssistantText: '',
|
|
186
201
|
renameName: '',
|
|
187
|
-
title
|
|
202
|
+
title,
|
|
188
203
|
isEmpty: false,
|
|
189
|
-
userMsgCount: 0,
|
|
204
|
+
userMsgCount: firstMessage ? 1 : 0,
|
|
190
205
|
modifiedAt,
|
|
191
|
-
timestamp:
|
|
206
|
+
timestamp: createdAt,
|
|
192
207
|
version: '',
|
|
193
208
|
gitBranch: meta.git_branch || '',
|
|
194
209
|
slug: '',
|
|
@@ -199,6 +214,11 @@ function parseSessionFile(filePath, projectPath, projectEntry) {
|
|
|
199
214
|
model: modelId,
|
|
200
215
|
agent: 'codex',
|
|
201
216
|
jsonlPath: filePath,
|
|
217
|
+
threadSource: meta.thread_source || '',
|
|
218
|
+
parentAgentSessionId: meta.parent_agent_session_id || meta.parent_thread_id || '',
|
|
219
|
+
parentThreadId: meta.parent_thread_id || meta.parent_agent_session_id || '',
|
|
220
|
+
agentNickname: meta.agent_nickname || '',
|
|
221
|
+
agentRole: meta.agent_role || '',
|
|
202
222
|
};
|
|
203
223
|
}
|
|
204
224
|
|
|
@@ -46,6 +46,14 @@ const PATTERNS = [
|
|
|
46
46
|
{ re: />\s*\/dev\/[sh]d[a-z]/, reason: 'direct write to block device', category: 'destruction' },
|
|
47
47
|
{ re: />\s*\/etc\//, reason: 'direct overwrite of /etc/', category: 'destruction' },
|
|
48
48
|
{ re: />\s*\/boot\//, reason: 'direct overwrite of /boot/', category: 'destruction' },
|
|
49
|
+
{ re: />\s*\/usr\//, reason: 'direct overwrite of /usr/', category: 'destruction' },
|
|
50
|
+
{ re: />\s*\/var\//, reason: 'direct overwrite of /var/', category: 'destruction' },
|
|
51
|
+
|
|
52
|
+
// --- Irreversible data / history loss (seeded so allow-by-default can't run them) ---
|
|
53
|
+
{ re: /\bdrop\s+(table|database|schema)\b/i, reason: 'SQL drop table/database', category: 'destruction' },
|
|
54
|
+
{ re: /\btruncate\s+table\b/i, reason: 'SQL truncate table', category: 'destruction' },
|
|
55
|
+
{ re: /\bgit\s+push\b[^\n]*\s(--force|-f)\b/, reason: 'git push --force (history overwrite)', category: 'destruction' },
|
|
56
|
+
{ re: /\bchmod\s+[0-7]*777\b[^\n]*(\/(?:etc|usr|var|boot|bin|sbin|lib)\b|\s\/(?:\s|$)|~)/, reason: 'chmod 777 on a system path', category: 'perms' },
|
|
49
57
|
|
|
50
58
|
// --- Credential exfiltration / secret leakage ---
|
|
51
59
|
{ re: /\b(curl|wget|nc|ncat|netcat)\b.*\.(ssh|gnupg|aws|kube|config)\b/i, reason: 'credential exfil via network tool', category: 'exfiltration' },
|
|
@@ -58,6 +66,7 @@ const PATTERNS = [
|
|
|
58
66
|
{ re: /\beval\s+.*`/, reason: 'eval with backtick substitution', category: 'exec' },
|
|
59
67
|
{ re: /\b(curl|wget)\s+.*\|\s*(bash|sh|zsh|source)\b/, reason: 'curl|bash (remote code exec)', category: 'exec' },
|
|
60
68
|
{ re: /\b(bash|sh|zsh)\s+<\s*\(.*\b(curl|wget)\b/, reason: 'process substitution remote exec', category: 'exec' },
|
|
69
|
+
{ re: /\bfind\b[\s\S]*\s-exec\s+(rm|rmdir|unlink|shred|dd|mkfs|sh|bash|zsh|chmod|chown|mv|truncate)\b/, reason: 'find -exec running a destructive command', category: 'exec' },
|
|
61
70
|
|
|
62
71
|
// --- Recursive permission / ownership on sensitive paths ---
|
|
63
72
|
{ re: /\bchmod\s+-[a-zA-Z]*R[a-zA-Z]*\s+\S+\s+(\/(?:\s|$)|~\/|\/etc(?:\s|\/|$)|\/var(?:\s|\/|$)|\/home(?:\s|\/|$))/, reason: 'recursive chmod on sensitive path', category: 'perms' },
|
|
@@ -101,29 +110,108 @@ function _normalizeCommand(input) {
|
|
|
101
110
|
try { return String(input); } catch { return ''; }
|
|
102
111
|
}
|
|
103
112
|
|
|
113
|
+
// --- User-configurable patterns (Permission tab) -------------------------
|
|
114
|
+
// The built-in PATTERNS above are sensible DEFAULTS shipped with CTM. The user
|
|
115
|
+
// can, in the Permission tab: (a) DISABLE specific defaults (by id), and (b) ADD
|
|
116
|
+
// their own custom patterns. Both live in the `approval_blocklist_config` setting
|
|
117
|
+
// and are passed into checkBlocklist() by the approval agent. Custom regexes are
|
|
118
|
+
// user-authored on their own machine — still, we validate + ReDoS-guard them so a
|
|
119
|
+
// fat-fingered pattern can't wedge the approval path.
|
|
120
|
+
const CUSTOM_FLAGS_ALLOWED = 'imsu';
|
|
121
|
+
const CUSTOM_SOURCE_MAX = 200;
|
|
122
|
+
const CUSTOM_PATTERNS_MAX = 200;
|
|
123
|
+
|
|
124
|
+
// ReDoS guard — same shape as approval-agent's isSafeRegex (nested quantifiers
|
|
125
|
+
// are the main catastrophic-backtracking vector). Kept local so the worker has
|
|
126
|
+
// no cross-module dependency.
|
|
127
|
+
function _isSafeRegexSource(source) {
|
|
128
|
+
if (/(\+|\*|\{)\)?(\+|\*|\?)/.test(source)) return false; // nested quantifiers
|
|
129
|
+
if ((source.match(/\|/g) || []).length > 20) return false; // excessive alternation
|
|
130
|
+
if (/\|\||^\||\|$/.test(source)) return false; // empty alternation → matches all
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Validate one user pattern. Returns { ok, re?, normalized?, error? }. Pure — the
|
|
135
|
+
// API layer uses it to reject bad input with a message; checkBlocklist uses it to
|
|
136
|
+
// skip (not throw on) a stored pattern that somehow became invalid.
|
|
137
|
+
function validateUserPattern(p) {
|
|
138
|
+
if (!p || typeof p !== 'object') return { ok: false, error: 'pattern must be an object' };
|
|
139
|
+
const source = typeof p.source === 'string' ? p.source.trim() : '';
|
|
140
|
+
if (!source) return { ok: false, error: 'regex is required' };
|
|
141
|
+
if (source.length > CUSTOM_SOURCE_MAX) return { ok: false, error: `regex too long (max ${CUSTOM_SOURCE_MAX} chars)` };
|
|
142
|
+
const flags = typeof p.flags === 'string' ? p.flags : '';
|
|
143
|
+
for (const f of flags) if (!CUSTOM_FLAGS_ALLOWED.includes(f)) return { ok: false, error: `unsupported flag "${f}" (allowed: ${CUSTOM_FLAGS_ALLOWED})` };
|
|
144
|
+
if (new Set(flags).size !== flags.length) return { ok: false, error: 'duplicate regex flag' };
|
|
145
|
+
if (!_isSafeRegexSource(source)) return { ok: false, error: 'regex rejected (nested quantifiers / excessive alternation — possible ReDoS)' };
|
|
146
|
+
let re;
|
|
147
|
+
try { re = new RegExp(source, flags); } catch (e) { return { ok: false, error: `invalid regex: ${e.message}` }; }
|
|
148
|
+
return {
|
|
149
|
+
ok: true,
|
|
150
|
+
re,
|
|
151
|
+
normalized: {
|
|
152
|
+
source,
|
|
153
|
+
flags,
|
|
154
|
+
reason: String(p.reason || '').slice(0, 200).trim() || 'Custom blocklist pattern',
|
|
155
|
+
category: (String(p.category || 'custom').slice(0, 40).trim() || 'custom'),
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Coerce a raw config object into the shape checkBlocklist expects, dropping
|
|
161
|
+
// anything malformed. Returns { disabledIds: Set<number>, customPatterns: [{re, reason, category, id}] }.
|
|
162
|
+
function _resolveConfig(config) {
|
|
163
|
+
const disabledIds = new Set();
|
|
164
|
+
const customPatterns = [];
|
|
165
|
+
if (config && typeof config === 'object') {
|
|
166
|
+
const raw = Array.isArray(config.disabledIds) ? config.disabledIds : [];
|
|
167
|
+
for (const id of raw) { const n = Number(id); if (Number.isInteger(n) && n >= 0 && n < PATTERNS.length) disabledIds.add(n); }
|
|
168
|
+
const customs = Array.isArray(config.customPatterns) ? config.customPatterns.slice(0, CUSTOM_PATTERNS_MAX) : [];
|
|
169
|
+
customs.forEach((p, i) => {
|
|
170
|
+
const v = validateUserPattern(p);
|
|
171
|
+
if (v.ok) customPatterns.push({ re: v.re, reason: v.normalized.reason, category: v.normalized.category, id: (p && p.id != null) ? p.id : `c${i}` });
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return { disabledIds, customPatterns };
|
|
175
|
+
}
|
|
176
|
+
|
|
104
177
|
// Check a command against the blocklist.
|
|
105
178
|
// Returns { blocked: true, reason, category, patternId } on first match,
|
|
106
179
|
// or { blocked: false } otherwise.
|
|
107
180
|
//
|
|
108
|
-
//
|
|
109
|
-
//
|
|
110
|
-
//
|
|
111
|
-
|
|
181
|
+
// Optional `config` ({ disabledIds, customPatterns }) layers the user's Permission
|
|
182
|
+
// tab edits on top of the defaults: disabled default ids are skipped, and custom
|
|
183
|
+
// patterns are checked after the defaults. Omitting `config` evaluates the defaults
|
|
184
|
+
// only (back-compat). Default matches carry a numeric `patternId`; custom matches
|
|
185
|
+
// carry `custom: true` + `customId` and `patternId: null`.
|
|
186
|
+
//
|
|
187
|
+
// This is synchronous and O(N patterns). The built-in patterns are anchored on
|
|
188
|
+
// literal keywords (\brm, \bmkfs, etc.) and custom patterns are ReDoS-guarded, so
|
|
189
|
+
// there's no catastrophic backtracking concern even on adversarial input.
|
|
190
|
+
function checkBlocklist(command, config) {
|
|
112
191
|
const cmd = _normalizeCommand(command);
|
|
113
192
|
if (!cmd) return { blocked: false };
|
|
193
|
+
const { disabledIds, customPatterns } = _resolveConfig(config);
|
|
194
|
+
// 1) Built-in defaults (skip user-disabled ids).
|
|
114
195
|
for (let i = 0; i < PATTERNS.length; i++) {
|
|
196
|
+
if (disabledIds.has(i)) continue;
|
|
115
197
|
const p = PATTERNS[i];
|
|
116
198
|
if (p.re.test(cmd)) {
|
|
117
199
|
return { blocked: true, reason: p.reason, category: p.category, patternId: i };
|
|
118
200
|
}
|
|
119
201
|
}
|
|
202
|
+
// 2) User custom patterns.
|
|
203
|
+
for (const c of customPatterns) {
|
|
204
|
+
if (c.re.test(cmd)) {
|
|
205
|
+
return { blocked: true, reason: c.reason, category: c.category, patternId: null, custom: true, customId: c.id };
|
|
206
|
+
}
|
|
207
|
+
}
|
|
120
208
|
return { blocked: false };
|
|
121
209
|
}
|
|
122
210
|
|
|
123
211
|
// Legacy helper — check multiple strings at once (e.g. tool name + command).
|
|
124
|
-
function checkBlocklistMulti(fields) {
|
|
212
|
+
function checkBlocklistMulti(fields, config) {
|
|
125
213
|
for (const v of fields) {
|
|
126
|
-
const res = checkBlocklist(v);
|
|
214
|
+
const res = checkBlocklist(v, config);
|
|
127
215
|
if (res.blocked) return res;
|
|
128
216
|
}
|
|
129
217
|
return { blocked: false };
|
|
@@ -132,7 +220,9 @@ function checkBlocklistMulti(fields) {
|
|
|
132
220
|
module.exports = {
|
|
133
221
|
checkBlocklist,
|
|
134
222
|
checkBlocklistMulti,
|
|
223
|
+
validateUserPattern,
|
|
135
224
|
PATTERN_META,
|
|
225
|
+
DEFAULT_PATTERN_COUNT: PATTERNS.length,
|
|
136
226
|
// Exposed for tests — callers should not mutate.
|
|
137
227
|
_PATTERNS: PATTERNS,
|
|
138
228
|
};
|
|
@@ -26,7 +26,8 @@ const ABOVE_ANCHOR_DEPTH = 40;
|
|
|
26
26
|
|
|
27
27
|
// Footer anchor that real widgets render at the bottom of the prompt.
|
|
28
28
|
// Claude Code: "Esc to cancel". Codex: "Press enter to confirm or esc to cancel".
|
|
29
|
-
|
|
29
|
+
// Cursor Agent: "Skip (esc or n)".
|
|
30
|
+
const ANCHOR_RE = /Esc to cancel|esc to cancel|Press enter to confirm|\(esc or n\)/;
|
|
30
31
|
|
|
31
32
|
// Approval-option pattern. Accepts an optional selection-arrow prefix in any of
|
|
32
33
|
// the forms different CLIs use: ❯ (Claude Code), ›/▶/▸ (Cursor/others), or
|
|
@@ -35,6 +36,11 @@ const ANCHOR_RE = /Esc to cancel|esc to cancel|Press enter to confirm/;
|
|
|
35
36
|
// — which is unstyled in Codex's renderer and trips no-widget-formatting.
|
|
36
37
|
const YES_RE = /^\s*(?:[❯›▶▸>]\s*)?\d+\.\s*(?:Yes|Allow)\b/i;
|
|
37
38
|
|
|
39
|
+
// Cursor Agent's "Run this command?" widget has no numbered options — the
|
|
40
|
+
// confirm row is a hotkey: "→ Run (once) (y)". Recognize it as the structural
|
|
41
|
+
// "Yes" row so the validator accepts Cursor prompts.
|
|
42
|
+
const CURSOR_RUN_RE = /^\s*(?:[▸❯›▶→>-]+\s*)?Run\s*\(once\)\s*\(y\)/i;
|
|
43
|
+
|
|
38
44
|
/**
|
|
39
45
|
* Check if the terminal is currently displaying an active approval widget.
|
|
40
46
|
*
|
|
@@ -108,7 +114,7 @@ function validateWidget(term, opts = {}) {
|
|
|
108
114
|
const line = buf.getLine(buf.viewportY + i);
|
|
109
115
|
if (!line) continue;
|
|
110
116
|
const text = line.translateToString(true);
|
|
111
|
-
if (YES_RE.test(text)) { yesRow = i; break; }
|
|
117
|
+
if (YES_RE.test(text) || CURSOR_RUN_RE.test(text)) { yesRow = i; break; }
|
|
112
118
|
}
|
|
113
119
|
if (yesRow < 0) {
|
|
114
120
|
return { valid: false, reason: 'yes-not-at-bottom' };
|
|
@@ -140,7 +146,7 @@ function _hasWidgetFormatting(buf, yesRow, totalRows) {
|
|
|
140
146
|
const yesLine = buf.getLine(buf.viewportY + yesRow);
|
|
141
147
|
if (!yesLine) return false;
|
|
142
148
|
const yesText = yesLine.translateToString(true);
|
|
143
|
-
if (/[
|
|
149
|
+
if (/[❯›▶▸→]/.test(yesText)) return true;
|
|
144
150
|
|
|
145
151
|
// Check for a selection marker near the approval options. Codex MCP forms can
|
|
146
152
|
// select option 2 ("Allow for this session"), while option 1 is the first
|
|
@@ -149,17 +155,17 @@ function _hasWidgetFormatting(buf, yesRow, totalRows) {
|
|
|
149
155
|
const line = buf.getLine(buf.viewportY + row);
|
|
150
156
|
if (!line) continue;
|
|
151
157
|
const text = line.translateToString(true);
|
|
152
|
-
// "❯" (U+276F), "›" (U+203A), "▶" (U+25B6), "
|
|
153
|
-
if (/[
|
|
158
|
+
// "❯" (U+276F), "›" (U+203A), "▶" (U+25B6), "→" (U+2192, Cursor), ">" spaced
|
|
159
|
+
if (/[❯›▶▸→]/.test(text)) return true;
|
|
154
160
|
}
|
|
155
161
|
|
|
156
|
-
// Check for
|
|
157
|
-
// block is pushed down by long wrapped content.
|
|
162
|
+
// Check for a selection marker anywhere in bottom 5 rows for prompts whose
|
|
163
|
+
// option block is pushed down by long wrapped content.
|
|
158
164
|
for (let row = Math.max(0, totalRows - 5); row < totalRows; row++) {
|
|
159
165
|
const line = buf.getLine(buf.viewportY + row);
|
|
160
166
|
if (!line) continue;
|
|
161
167
|
const text = line.translateToString(true);
|
|
162
|
-
if (/[
|
|
168
|
+
if (/[❯›▶▸→]/.test(text)) return true;
|
|
163
169
|
}
|
|
164
170
|
|
|
165
171
|
// Check for ANSI foreground color on the Yes-option line.
|