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
|
@@ -1,60 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
// Runs session-integrity scans off the main event loop.
|
|
1
|
+
'use strict';
|
|
3
2
|
|
|
4
|
-
const { parentPort
|
|
5
|
-
const path = require('path');
|
|
6
|
-
|
|
7
|
-
const dbModule = require(path.join(workerData.ctmDir, 'db'));
|
|
8
|
-
dbModule.connectDb(workerData.dbPath);
|
|
9
|
-
|
|
10
|
-
const sessionIntegrity = require(path.join(workerData.ctmDir, 'session-integrity'));
|
|
11
|
-
const { getAllSessionFiles } = require(path.join(workerData.ctmDir, 'session-utils'));
|
|
12
|
-
|
|
13
|
-
let running = false;
|
|
14
|
-
let closing = false;
|
|
15
|
-
let closed = false;
|
|
16
|
-
|
|
17
|
-
function closeDbAndAck() {
|
|
18
|
-
if (closed) return;
|
|
19
|
-
closed = true;
|
|
20
|
-
try { dbModule.closeDb({ checkpointMode: 'PASSIVE' }); } catch {}
|
|
21
|
-
parentPort.postMessage({ type: 'closed' });
|
|
22
|
-
setImmediate(() => process.exit(0));
|
|
23
|
-
}
|
|
3
|
+
const { parentPort } = require('node:worker_threads');
|
|
24
4
|
|
|
25
5
|
parentPort.on('message', (msg) => {
|
|
26
|
-
if (msg
|
|
27
|
-
|
|
28
|
-
if (!running) closeDbAndAck();
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
if (msg.type !== 'run') return;
|
|
32
|
-
if (closing) {
|
|
33
|
-
parentPort.postMessage({ type: 'error', requestId: msg.requestId, dur: 0, error: 'worker closing' });
|
|
6
|
+
if (msg?.type === 'close') {
|
|
7
|
+
parentPort.postMessage({ type: 'closed' });
|
|
34
8
|
return;
|
|
35
9
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
try {
|
|
43
|
-
const result = sessionIntegrity.runIntegrityCheck({
|
|
44
|
-
db: dbModule.getDb(),
|
|
45
|
-
getAllSessionFiles,
|
|
46
|
-
telemetry: null,
|
|
47
|
-
autoRecover: true,
|
|
48
|
-
});
|
|
49
|
-
parentPort.postMessage({ type: 'done', requestId: msg.requestId, dur: Date.now() - t0, result: result || { checked: true } });
|
|
50
|
-
} catch (e) {
|
|
51
|
-
parentPort.postMessage({ type: 'error', requestId: msg.requestId, dur: Date.now() - t0, error: e.message });
|
|
52
|
-
} finally {
|
|
53
|
-
running = false;
|
|
54
|
-
if (closing) closeDbAndAck();
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
process.on('exit', () => {
|
|
59
|
-
try { dbModule.closeDb({ checkpointMode: 'NONE' }); } catch {}
|
|
10
|
+
parentPort.postMessage({
|
|
11
|
+
type: 'error',
|
|
12
|
+
requestId: msg?.requestId,
|
|
13
|
+
dur: 0,
|
|
14
|
+
error: 'session integrity DB work is owned by the CTM DB owner worker',
|
|
15
|
+
});
|
|
60
16
|
});
|
|
@@ -16,6 +16,13 @@
|
|
|
16
16
|
// A detector MAY also opt into custom waiting-input detection by providing
|
|
17
17
|
// a `detectWaitingInput(text)` hook, but most providers get that for free
|
|
18
18
|
// via providers/<id>.detect() which is already wired into approval-agent.
|
|
19
|
+
//
|
|
20
|
+
// A detector MAY also provide `detectTurnFinished(text)`: positive evidence the
|
|
21
|
+
// agent returned to its ready prompt (e.g. a TUI repainting its idle composer
|
|
22
|
+
// with no in-progress status line). Distinct from detectWaitingInput — finished
|
|
23
|
+
// means Idle (nothing needed), not Needs-You. Hookless TUIs like Codex rely on
|
|
24
|
+
// this to close their active turn; without it a finished session would ride the
|
|
25
|
+
// long open-turn "running" hold. server.js _idlePromptDetections consumes it.
|
|
19
26
|
|
|
20
27
|
const DEFAULT_IDLE_DEBOUNCE_MS = 1500;
|
|
21
28
|
|
|
@@ -26,7 +33,17 @@ const TRIVIAL_CHUNK_RE = /^[\s\x1b\[\]\d;A-Za-z⠁⠂⠄⠈⠐⠠⡀⢀\\|/\-─
|
|
|
26
33
|
|
|
27
34
|
function stripAnsi(data) {
|
|
28
35
|
return String(data || '')
|
|
29
|
-
|
|
36
|
+
// `<=>` covers private-parameter CSI like modifyOtherKeys "\x1b[>4;0m"
|
|
37
|
+
// and kitty keyboard queries "\x1b[>7u" — without them the prefix byte
|
|
38
|
+
// survives and "[>4;0m[>7u" reads as printable content.
|
|
39
|
+
.replace(/\x1b\[[0-9;?<=>]*[a-zA-Z]/g, '')
|
|
40
|
+
// CSI sequences with intermediate bytes (0x20-0x2F) before the final byte —
|
|
41
|
+
// e.g. DECSCUSR cursor-shape "\x1b[0 q" (note the space). The rule above
|
|
42
|
+
// requires the final byte immediately after the params, so it misses these
|
|
43
|
+
// and leaves a stray "q"/"0 q" that reads as printable content (which made
|
|
44
|
+
// composer-cursor repaints score as activity). Additive: only matches the
|
|
45
|
+
// intermediate form the rule above cannot.
|
|
46
|
+
.replace(/\x1b\[[0-9;?]*[ -/]+[@-~]/g, '')
|
|
30
47
|
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
|
|
31
48
|
.replace(/[\x00-\x1f\x7f]/g, '');
|
|
32
49
|
}
|
|
@@ -9,7 +9,10 @@
|
|
|
9
9
|
const { baseDetector, stripAnsi } = require('./base');
|
|
10
10
|
|
|
11
11
|
const CLAUDE_STATUS_FRAGMENT_RE = /^(?:[✻✳✢✶◐◓◑◒⏺●○■□✔✓]+|Run|Runn|Runni|Runnin|Running\.{0,3}|unning|Thinking\.{0,3}|\(?ctrl\+o\s*to\s*expand\)?|>?\s*esc\s+to\s+interrupt|[⏵>]+\s*(?:accept edits|auto mode)\s+on\s+\(shift\+tab\s+to\s+cycle\)(?:\s*·\s*esc\s+to\s+interrupt)?(?:\s*·\s*ctrl\+t\s+to\s+(?:show|hide)\s+task)?|ctrl\+t\s+to\s+show\s+tasks?|\?\s+for\s+shortcuts|Native installation exists but .*? is not in your PATH\. Run:|echo\s+['"]export\s+PATH=.*)$/i;
|
|
12
|
-
const
|
|
12
|
+
const CLAUDE_PROMPT_LINE_RE = /^(?:›|❯|>)\s*(?:\S.*)?$/;
|
|
13
|
+
const CLAUDE_IDLE_FOOTER_LINE_RE = /(?:accept edits|auto mode)\s+on\s+\(shift\+tab\s+to\s+cycle\)|\besc\s+to\s+interrupt\b|\bctrl\+t\s+to\s+(?:show|hide)\s+tasks?\b|\bctrl\+o\s+to\s+expand\b|\?\s+for\s+shortcuts/i;
|
|
14
|
+
const CLAUDE_WORKED_LINE_RE = /^[*•]?\s*Worked for\s+\d/i;
|
|
15
|
+
const CLAUDE_PASSIVE_BORDER_LINE_RE = /^[\s─━═│┃┌┐└┘├┤┬┴┼╭╮╰╯]+$/u;
|
|
13
16
|
|
|
14
17
|
function isClaudeRedraw(data) {
|
|
15
18
|
const s = String(data || '');
|
|
@@ -17,6 +20,169 @@ function isClaudeRedraw(data) {
|
|
|
17
20
|
/\x1b\[[0-9]+A/.test(s) && /\x1b\[[0-9;?]*J/.test(s)
|
|
18
21
|
) || (
|
|
19
22
|
/\x1b\[[0-9]+;[0-9]+H/.test(s) && /\x1b\[[0-9;?]*[KJ]/.test(s)
|
|
23
|
+
) || /\x1b\[\?2026h/.test(s);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function stripAnsiPreserveLines(data) {
|
|
27
|
+
return String(data || '')
|
|
28
|
+
.replace(/\x1b\[[0-9]+;[0-9]+[Hf]/g, '\n')
|
|
29
|
+
.replace(/\x1b\[[0-9]+[ABCD]/g, '\n')
|
|
30
|
+
.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '')
|
|
31
|
+
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
|
|
32
|
+
.replace(/\r/g, '\n')
|
|
33
|
+
.replace(/[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, '');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function printableLines(data) {
|
|
37
|
+
return stripAnsiPreserveLines(data)
|
|
38
|
+
.replace(/\u00a0/g, ' ')
|
|
39
|
+
.split(/\n+/)
|
|
40
|
+
.map(line => line.trim())
|
|
41
|
+
.filter(Boolean);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function isClaudePassivePromptLine(line) {
|
|
45
|
+
const text = String(line || '').trim();
|
|
46
|
+
if (!text) return true;
|
|
47
|
+
return CLAUDE_PROMPT_LINE_RE.test(text) ||
|
|
48
|
+
CLAUDE_IDLE_FOOTER_LINE_RE.test(text) ||
|
|
49
|
+
CLAUDE_WORKED_LINE_RE.test(text) ||
|
|
50
|
+
CLAUDE_STATUS_FRAGMENT_RE.test(text) ||
|
|
51
|
+
CLAUDE_PASSIVE_BORDER_LINE_RE.test(text);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function isClaudeIdlePromptRedraw(data) {
|
|
55
|
+
if (!isClaudeRedraw(data)) return false;
|
|
56
|
+
const lines = printableLines(data);
|
|
57
|
+
if (!lines.length) return false;
|
|
58
|
+
const hasPrompt = lines.some(line => CLAUDE_PROMPT_LINE_RE.test(line));
|
|
59
|
+
const hasFooter = lines.some(line => CLAUDE_IDLE_FOOTER_LINE_RE.test(line));
|
|
60
|
+
if (!hasPrompt || !hasFooter) return false;
|
|
61
|
+
return lines.every(isClaudePassivePromptLine);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Live Claude approval/choice widget shapes. A real prompt highlights a
|
|
65
|
+
// selection row (❯ 1. Yes / ❯ Allow / ❯ Run) and/or asks an approval question
|
|
66
|
+
// with a confirm footer. These parallel approval-agent.js's LIVE_SELECTION_CURSOR_RE
|
|
67
|
+
// / LIVE_PROMPT_FOOTER_RE (which serve the auto-approver); kept here so the banner
|
|
68
|
+
// path's "at a live prompt?" answer comes from the provider detector, not from
|
|
69
|
+
// global content regexes tested over the whole tail.
|
|
70
|
+
// Leading box-frame border ("│ ❯ 1. …") — a menu rendered inside a bordered widget
|
|
71
|
+
// prefixes each row with a vertical rule. It is allowed (optional) before the cursor
|
|
72
|
+
// so a boxed AskUserQuestion reads like a borderless one; without it the menu rows
|
|
73
|
+
// fail to match and the whole widget classifies as `none`, stranding a stale banner.
|
|
74
|
+
const CLAUDE_BOX_BORDER = '(?:[│┃║▏▕|]\\s*)?';
|
|
75
|
+
const CLAUDE_SELECTION_CURSOR_RE = new RegExp('^\\s*' + CLAUDE_BOX_BORDER + '[❯▸›▶➤◆→]\\s*(?:\\d+[.)]\\s*)?(?:yes\\b|allow\\b|approve\\b|proceed\\b|accept\\b|run\\b|make edits|don\'?t ask|always)', 'i');
|
|
76
|
+
const CLAUDE_APPROVAL_QUESTION_RE = /\bDo you want to (?:proceed|make this edit|create|run|allow)\b/i;
|
|
77
|
+
const CLAUDE_CONFIRM_FOOTER_RE = /\benter to confirm\b|\besc to (?:cancel|reject|go back)\b|press enter to (?:confirm|continue)/i;
|
|
78
|
+
// Any numbered option row (cursor optional): "❯ 1. X", " 2. Y". Loose on its own —
|
|
79
|
+
// it also matches enumerated PROSE ("2. Double-stat (low): ..."), so it only counts
|
|
80
|
+
// toward a real menu alongside a highlight cursor or a menu nav footer (below).
|
|
81
|
+
const CLAUDE_NUMBERED_OPTION_RE = new RegExp('^\\s*' + CLAUDE_BOX_BORDER + '[❯▸›▶➤◆→]?\\s*\\d+[.)]\\s+\\S');
|
|
82
|
+
// A HIGHLIGHTED numbered option (selection cursor REQUIRED) — present in a real
|
|
83
|
+
// interactive menu ("❯ 1. Investigate"), absent from a prose findings list.
|
|
84
|
+
const CLAUDE_MENU_CURSOR_OPTION_RE = new RegExp('^\\s*' + CLAUDE_BOX_BORDER + '[❯▸▶➤◆→]\\s*\\d+[.)]\\s+\\S');
|
|
85
|
+
// A menu navigation footer ("Enter to select · ↑/↓ to navigate"): the other positive
|
|
86
|
+
// tell that the numbered rows are an interactive selection menu, not prose.
|
|
87
|
+
const CLAUDE_MENU_NAV_FOOTER_RE = /\bto\s+(?:select|navigate)\b|↑\s*\/\s*↓|↑\/↓/i;
|
|
88
|
+
// A yes/no-flavored option — the hallmark of a real yes/no APPROVAL menu (Yes / No /
|
|
89
|
+
// Allow / Deny / …) as opposed to an arbitrary multi-option CHOICE menu. The menu's
|
|
90
|
+
// OPTIONS, not its question text, decide approval-vs-choice: an AskUserQuestion-style
|
|
91
|
+
// menu can ask "...do you want to proceed?" (which matches the approval question regex)
|
|
92
|
+
// yet offer several non-yes/no answers, and it must read as a choice — otherwise the
|
|
93
|
+
// banner renders Approve/Deny (inject '1'/Esc) on it and the approval label path,
|
|
94
|
+
// which hunts for a command, comes back blank.
|
|
95
|
+
const CLAUDE_YESNO_OPTION_RE = /^\s*[❯▸›▶➤◆→]?\s*(?:\d+[.)]\s*)?(?:yes\b|no\b|allow\b|deny\b|approve\b|reject\b|proceed\b|cancel\b|accept\b|always\b|don'?t\b|keep planning\b)/i;
|
|
96
|
+
const CLAUDE_YN_RE = /\(Y\/n\)\s*$/i;
|
|
97
|
+
// The agent's free-text composer: a bare prompt line (›/❯/> with nothing typed).
|
|
98
|
+
// Its presence BELOW an approval/choice anchor is the real "idle, awaiting text"
|
|
99
|
+
// signal — the live approval widget REPLACES this composer. (The idle/working
|
|
100
|
+
// FOOTER — "esc to interrupt", "? for shortcuts" — can still be painted beneath a
|
|
101
|
+
// live approval, so footer presence alone must NOT veto. That blanket veto was the
|
|
102
|
+
// cause of approval cards never appearing live until a restart re-rendered a frame
|
|
103
|
+
// that happened to drop the footer from the captured tail.)
|
|
104
|
+
const CLAUDE_EMPTY_COMPOSER_RE = /^(?:›|❯|>)\s*$/;
|
|
105
|
+
|
|
106
|
+
// Negative gate: the idle/working composer footer (esc to interrupt, ? for
|
|
107
|
+
// shortcuts, accept edits on …) is on screen, which a live approval widget would
|
|
108
|
+
// have REPLACED — so the agent is at its ready composer, not blocked at a prompt.
|
|
109
|
+
function detectReadyComposer(text) {
|
|
110
|
+
const lines = printableLines(text);
|
|
111
|
+
if (!lines.length) return false;
|
|
112
|
+
return lines.slice(-12).some(line => CLAUDE_IDLE_FOOTER_LINE_RE.test(line));
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// "esc to interrupt" is the ONE footer phrase Claude shows exclusively while a
|
|
116
|
+
// turn is generating (or a tool/shell it spawned is still running) — the genuinely
|
|
117
|
+
// idle composer shows "? for shortcuts" / "auto mode on (shift+tab to cycle)" but
|
|
118
|
+
// never "esc to interrupt". So its presence in the tail is decisive evidence the
|
|
119
|
+
// agent is actively working, NOT idle awaiting input. server.js _idlePromptDetections
|
|
120
|
+
// uses this to suppress the bare-`❯`-composer 'input' idle-notify during a long,
|
|
121
|
+
// output-quiet tool turn (which otherwise mislabels the session "Needs You").
|
|
122
|
+
const CLAUDE_GENERATING_FOOTER_RE = /\besc\s+to\s+interrupt\b/i;
|
|
123
|
+
function detectActivelyGenerating(text) {
|
|
124
|
+
const lines = printableLines(text);
|
|
125
|
+
if (!lines.length) return false;
|
|
126
|
+
return lines.slice(-12).some(line => CLAUDE_GENERATING_FOOTER_RE.test(line));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Positive "blocked at a live approval/choice widget" signal. Returns
|
|
130
|
+
// {reason:'approval'|'choice'} only when a real widget is anchored in the active
|
|
131
|
+
// prompt region and the ready-composer footer is absent; null otherwise.
|
|
132
|
+
function detectWaitingInput(text) {
|
|
133
|
+
const raw = String(text || '');
|
|
134
|
+
if (!raw.trim()) return null;
|
|
135
|
+
const lines = printableLines(raw);
|
|
136
|
+
if (!lines.length) return null;
|
|
137
|
+
const tail = lines.slice(-14);
|
|
138
|
+
// Locate the anchors and the idle free-text composer by POSITION (closest to the
|
|
139
|
+
// bottom = the active element). A widget is live only when it is not sitting above
|
|
140
|
+
// a bare composer prompt (which would mean the agent already moved on and the
|
|
141
|
+
// widget is stale scrollback). Footer text is intentionally NOT consulted here.
|
|
142
|
+
let selIdx = -1, qIdx = -1, confIdx = -1, ynIdx = -1, composerIdx = -1;
|
|
143
|
+
let numberedOptCount = 0, lastNumberedOptIdx = -1, hasYesNoOption = false;
|
|
144
|
+
let hasMenuCursor = false, hasMenuNavFooter = false;
|
|
145
|
+
for (let i = 0; i < tail.length; i++) {
|
|
146
|
+
const line = tail[i];
|
|
147
|
+
if (CLAUDE_SELECTION_CURSOR_RE.test(line)) selIdx = i;
|
|
148
|
+
if (CLAUDE_APPROVAL_QUESTION_RE.test(line)) qIdx = i;
|
|
149
|
+
if (CLAUDE_CONFIRM_FOOTER_RE.test(line)) confIdx = i;
|
|
150
|
+
if (CLAUDE_YN_RE.test(line)) ynIdx = i;
|
|
151
|
+
if (CLAUDE_EMPTY_COMPOSER_RE.test(line)) composerIdx = i;
|
|
152
|
+
if (CLAUDE_MENU_NAV_FOOTER_RE.test(line)) hasMenuNavFooter = true;
|
|
153
|
+
if (CLAUDE_NUMBERED_OPTION_RE.test(line)) {
|
|
154
|
+
numberedOptCount += 1; lastNumberedOptIdx = i;
|
|
155
|
+
if (CLAUDE_YESNO_OPTION_RE.test(line)) hasYesNoOption = true;
|
|
156
|
+
if (CLAUDE_MENU_CURSOR_OPTION_RE.test(line)) hasMenuCursor = true;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// A multi-option numbered menu whose options are NOT yes/no-flavored is a CHOICE,
|
|
160
|
+
// even when its question matches the approval phrasing. A genuine yes/no approval
|
|
161
|
+
// (Yes/No options) keeps hasYesNoOption true and stays an approval below. Require a
|
|
162
|
+
// positive menu tell — a highlight cursor or a nav footer — so an enumerated PROSE
|
|
163
|
+
// findings list ("2. ... 3. ... 4. ...") is NOT mistaken for an interactive menu.
|
|
164
|
+
const multiChoiceMenu = numberedOptCount >= 2 && !hasYesNoOption &&
|
|
165
|
+
(hasMenuCursor || hasMenuNavFooter);
|
|
166
|
+
let approvalIdx = -1;
|
|
167
|
+
if (selIdx >= 0 && (qIdx >= 0 || confIdx >= 0)) approvalIdx = Math.max(selIdx, qIdx, confIdx);
|
|
168
|
+
else if (qIdx >= 0 && confIdx >= 0 && !multiChoiceMenu) approvalIdx = Math.max(qIdx, confIdx);
|
|
169
|
+
else if (ynIdx >= 0 && !multiChoiceMenu) approvalIdx = ynIdx;
|
|
170
|
+
// A bare composer BELOW the anchor ⇒ the widget is stale; the agent is idle at its
|
|
171
|
+
// composer. No composer-below ⇒ the widget is the live, active prompt.
|
|
172
|
+
if (approvalIdx >= 0 && !(composerIdx > approvalIdx)) return { reason: 'approval' };
|
|
173
|
+
// Choice: a highlighted yes/no-style selection cursor (selIdx) OR a non-yes/no
|
|
174
|
+
// numbered menu (anchored at its last option). Same composer-below staleness gate.
|
|
175
|
+
const choiceIdx = selIdx >= 0 ? selIdx : (multiChoiceMenu ? lastNumberedOptIdx : -1);
|
|
176
|
+
if (choiceIdx >= 0 && !(composerIdx > choiceIdx)) return { reason: 'choice' };
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function isClaudeStatusRedraw(data) {
|
|
181
|
+
const stripped = stripAnsi(data).trim();
|
|
182
|
+
return (
|
|
183
|
+
stripped.length <= 160 &&
|
|
184
|
+
CLAUDE_STATUS_FRAGMENT_RE.test(stripped) &&
|
|
185
|
+
isClaudeRedraw(data)
|
|
20
186
|
);
|
|
21
187
|
}
|
|
22
188
|
|
|
@@ -28,14 +194,21 @@ module.exports = {
|
|
|
28
194
|
// Base detector's TRIVIAL_CHUNK_RE already covers these.
|
|
29
195
|
isActiveChunk(data) {
|
|
30
196
|
if (!baseDetector.isActiveChunk(data)) return false;
|
|
31
|
-
|
|
32
|
-
if (
|
|
33
|
-
stripped.length <= 160 &&
|
|
34
|
-
isClaudeRedraw(data) &&
|
|
35
|
-
CLAUDE_STATUS_FRAGMENT_RE.test(stripped)
|
|
36
|
-
) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
197
|
+
if (isClaudeIdlePromptRedraw(data) || isClaudeStatusRedraw(data)) return false;
|
|
39
198
|
return true;
|
|
40
199
|
},
|
|
200
|
+
isStatusOnlyChunk(data) {
|
|
201
|
+
return isClaudeIdlePromptRedraw(data) || isClaudeStatusRedraw(data);
|
|
202
|
+
},
|
|
203
|
+
isBusyStatusChunk() {
|
|
204
|
+
return false;
|
|
205
|
+
},
|
|
206
|
+
isClaudeIdlePromptRedraw,
|
|
207
|
+
detectWaitingInput,
|
|
208
|
+
detectReadyComposer,
|
|
209
|
+
detectActivelyGenerating,
|
|
210
|
+
_test: {
|
|
211
|
+
printableLines,
|
|
212
|
+
isClaudePassivePromptLine,
|
|
213
|
+
},
|
|
41
214
|
};
|
|
@@ -11,12 +11,20 @@
|
|
|
11
11
|
const { baseDetector, stripAnsi } = require('./base');
|
|
12
12
|
|
|
13
13
|
const CODEX_STATUS_FRAGMENT_RE = /^[\s\d•◦·∙●○WwOoRrKkIiNnGg]+$/u;
|
|
14
|
-
const CODEX_BUSY_STATUS_LINE_RE = /^(?:working(?:\s*\([^)]*\))?(?:\s*[•◦·∙●○]\s*esc\s+to\s+interrupt)?|(?:working\s*)?esc\s+to\s+interrupt)$/iu;
|
|
14
|
+
const CODEX_BUSY_STATUS_LINE_RE = /^(?:(?:waiting\s+for\s+background\s+terminal\b.*)|working(?:\s*\([^)]*\))?(?:\s*[•◦·∙●○]\s*esc\s+to\s+interrupt)?|(?:working\s*)?esc\s+to\s+interrupt)$/iu;
|
|
15
15
|
const CODEX_BUSY_COMPACT_STATUS_LINE_RE = /^working\s+(?:\d+(?::\d+){0,2}|\d+(?:\.\d+)?\s*(?:ms|s|sec|secs|m|min|mins|h|hr|hrs))$/iu;
|
|
16
16
|
const CODEX_BUSY_WORD = 'working';
|
|
17
17
|
const CODEX_BUSY_HINT_RE = /esc\s+to\s+interrupt/i;
|
|
18
|
+
const CODEX_PROMPT_LINE_RE = /^(?:›|❯|>)\s*(?:\S.*)?$/;
|
|
19
|
+
const CODEX_FOOTER_LINE_RE = /^(?:gpt|claude|codex|gemini|deepseek|kimi|moonshot|qwen|llama|mistral)[\w.+-]*(?:\s+[\w.+-]+){0,8}\s+[·-]\s+(?:~|\/|\.\.?\/)/i;
|
|
20
|
+
const CODEX_SKILL_WARNING_RE = /^(?:⚠\s*)?Skipped loading\s+\d+\s+skill\(s\)\s+due to invalid SKILL\.md files\./i;
|
|
21
|
+
const CODEX_SKILL_DESCRIPTION_RE = /^\/.*\/SKILL\.md:\s+invalid description:\s+exceeds maximum length of\s+\d+\s+characters$/i;
|
|
22
|
+
// An approval/choice widget is pending — NOT a finished turn, even when the
|
|
23
|
+
// model footer is still painted at the bottom of the screen.
|
|
24
|
+
const CODEX_PENDING_PROMPT_RE = /Would you like to run the following command\?|Press enter to confirm or esc to cancel|tell Codex what to do differently|\b\d+\.\s*Yes\b/i;
|
|
18
25
|
|
|
19
26
|
function hasCodexBusyStatusFragment(text) {
|
|
27
|
+
if (/waiting\s+for\s+background\s+terminal/i.test(String(text || ''))) return true;
|
|
20
28
|
if (CODEX_BUSY_HINT_RE.test(String(text || ''))) return true;
|
|
21
29
|
const letters = String(text || '').toLowerCase().replace(/[^a-z]/g, '');
|
|
22
30
|
if (letters.length < 3) return false;
|
|
@@ -36,6 +44,105 @@ function isCursorAddressedRedraw(data) {
|
|
|
36
44
|
) || /\x1b\[\?2026h/.test(s);
|
|
37
45
|
}
|
|
38
46
|
|
|
47
|
+
function stripAnsiPreserveLines(data) {
|
|
48
|
+
return String(data || '')
|
|
49
|
+
.replace(/\x1b\[[0-9]+;[0-9]+[Hf]/g, '\n')
|
|
50
|
+
.replace(/\x1b\[[0-9]+[ABCD]/g, '\n')
|
|
51
|
+
.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '')
|
|
52
|
+
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
|
|
53
|
+
.replace(/\r/g, '\n')
|
|
54
|
+
.replace(/[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, '');
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function printableLines(data) {
|
|
58
|
+
return stripAnsiPreserveLines(data)
|
|
59
|
+
.replace(/\u00a0/g, ' ')
|
|
60
|
+
.split(/\n+/)
|
|
61
|
+
.map(line => line.trim())
|
|
62
|
+
.filter(Boolean);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isCodexPassiveDiagnosticChunk(data) {
|
|
66
|
+
const lines = printableLines(data);
|
|
67
|
+
return !!(lines.length && lines.every(line => (
|
|
68
|
+
CODEX_SKILL_WARNING_RE.test(line) ||
|
|
69
|
+
CODEX_SKILL_DESCRIPTION_RE.test(line)
|
|
70
|
+
)));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isCodexIdlePromptRedraw(data) {
|
|
74
|
+
if (!isCursorAddressedRedraw(data)) return false;
|
|
75
|
+
const lines = printableLines(data);
|
|
76
|
+
if (!lines.length) return false;
|
|
77
|
+
const compact = lines.join(' ');
|
|
78
|
+
// A live busy status line ("Working (5s) • esc to interrupt") means still
|
|
79
|
+
// working — not an idle composer. Use the PRECISE per-line busy-status check
|
|
80
|
+
// (same as detectTurnFinished), NOT the loose 3-letter fragment matcher: this
|
|
81
|
+
// classifier already requires a full composer frame (prompt + footer), so a
|
|
82
|
+
// coalesced full-screen repaint carries the conversation history above the
|
|
83
|
+
// composer, and the loose matcher trips on ordinary words (coding / going /
|
|
84
|
+
// rendering / work → wor/ork/rki/ing). That false-busy match mis-scored the
|
|
85
|
+
// idle repaint as active and flipped idle Codex sessions to "Running".
|
|
86
|
+
if (lines.some(line => textHasCodexBusyStatus(line))) return false;
|
|
87
|
+
const hasPrompt = lines.some(line => CODEX_PROMPT_LINE_RE.test(line)) ||
|
|
88
|
+
/(?:^|\s)(?:›|❯|>)\s*\S/.test(compact);
|
|
89
|
+
if (!hasPrompt) return false;
|
|
90
|
+
const hasFooter = lines.some(line => CODEX_FOOTER_LINE_RE.test(line)) ||
|
|
91
|
+
/\b(?:gpt-[\w.-]+|claude|sonnet|opus|haiku|deepseek|gemini|qwen|llama|mistral|kimi|moonshot)\b/i.test(compact) &&
|
|
92
|
+
/(?:\b(?:x?high|medium|low|fast)\b|\s[-·]\s|[~/])/.test(compact);
|
|
93
|
+
return hasFooter;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Positive "the turn is over" signal: Codex has repainted its ready composer
|
|
97
|
+
// (prompt line + model footer) with NO live "Working… esc to interrupt" status
|
|
98
|
+
// line. Unlike detectWaitingInput (which means Needs-You), this means the agent
|
|
99
|
+
// finished and is idle. Server uses it to close the active turn so a finished
|
|
100
|
+
// Codex session settles to Idle instead of riding the long open-turn hold.
|
|
101
|
+
// Operates on viewport text (already stripped) or a raw ANSI tail.
|
|
102
|
+
function detectTurnFinished(text) {
|
|
103
|
+
const raw = String(text || '');
|
|
104
|
+
if (!raw.trim()) return false;
|
|
105
|
+
if (CODEX_PENDING_PROMPT_RE.test(raw)) return false;
|
|
106
|
+
// printableLines preserves line structure (base.stripAnsi strips newlines, so
|
|
107
|
+
// a whole-buffer busy check would miss the status line). Check busy per line.
|
|
108
|
+
const lines = printableLines(raw);
|
|
109
|
+
if (!lines.length) return false;
|
|
110
|
+
// A live busy status line ("Working (5s) • esc to interrupt") means still
|
|
111
|
+
// working, even though the composer is also on screen.
|
|
112
|
+
if (lines.some(line => textHasCodexBusyStatus(line))) return false;
|
|
113
|
+
const hasPrompt = lines.some(line => CODEX_PROMPT_LINE_RE.test(line));
|
|
114
|
+
if (!hasPrompt) return false;
|
|
115
|
+
return lines.some(line => CODEX_FOOTER_LINE_RE.test(line));
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// The unambiguous live Codex approval/confirm widgets — the actual interactive
|
|
119
|
+
// prompt text, distinct from prose the agent may print. Kept narrow on purpose
|
|
120
|
+
// (hide-unless-confident): the run-command question and the confirm footer are
|
|
121
|
+
// real widget chrome; a numbered Yes/No PAIR is a menu. We deliberately do NOT
|
|
122
|
+
// fire on "tell Codex/the model what to do differently" alone, since that line
|
|
123
|
+
// also appears in the passive "Conversation interrupted" message after an Esc.
|
|
124
|
+
const CODEX_RUN_APPROVAL_RE = /Would you like to run the following command\?/i;
|
|
125
|
+
const CODEX_CONFIRM_WIDGET_RE = /Press enter to confirm or esc to cancel/i;
|
|
126
|
+
const CODEX_YESNO_MENU_RE = /\b1\.\s*Yes\b[\s\S]{0,80}\b2\.\s*No\b/i;
|
|
127
|
+
|
|
128
|
+
// Positive "the agent is blocked at a live approval/choice widget" signal.
|
|
129
|
+
// Returns {reason:'approval'|'choice'} ONLY when a real widget is in the active
|
|
130
|
+
// prompt region (bottom of the viewport) and the agent is not mid-work. Returns
|
|
131
|
+
// null otherwise — an idle/finished composer is reported by detectTurnFinished,
|
|
132
|
+
// not here. Operates on viewport text or a raw ANSI tail.
|
|
133
|
+
function detectWaitingInput(text) {
|
|
134
|
+
const raw = String(text || '');
|
|
135
|
+
if (!raw.trim()) return null;
|
|
136
|
+
const lines = printableLines(raw);
|
|
137
|
+
if (!lines.length) return null;
|
|
138
|
+
// Still working ("Working (5s) • esc to interrupt") → not waiting on the user.
|
|
139
|
+
if (lines.some(line => textHasCodexBusyStatus(line))) return null;
|
|
140
|
+
const tail = lines.slice(-10).join('\n');
|
|
141
|
+
if (CODEX_RUN_APPROVAL_RE.test(tail)) return { reason: 'approval' };
|
|
142
|
+
if (CODEX_CONFIRM_WIDGET_RE.test(tail) || CODEX_YESNO_MENU_RE.test(tail)) return { reason: 'choice' };
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
|
|
39
146
|
function isCodexStatusRedraw(data) {
|
|
40
147
|
const stripped = stripAnsi(data).trim();
|
|
41
148
|
return (
|
|
@@ -61,6 +168,17 @@ function textHasCodexBusyStatus(text) {
|
|
|
61
168
|
return clean.split('\n').some((line) => isCodexBusyStatusLine(line));
|
|
62
169
|
}
|
|
63
170
|
|
|
171
|
+
// The agent is mid-turn when its "Working… • esc to interrupt" status line (or a
|
|
172
|
+
// background-terminal wait) is on screen. server.js's generating-gate uses this to
|
|
173
|
+
// suppress the idle-notify so a long, output-quiet Codex turn whose composer matches
|
|
174
|
+
// a global prompt pattern is not mislabeled "Needs You". Same signal Codex's own
|
|
175
|
+
// detectWaitingInput/detectTurnFinished already trust.
|
|
176
|
+
function detectActivelyGenerating(text) {
|
|
177
|
+
// Per line: base.stripAnsi (inside textHasCodexBusyStatus) drops newlines, so a
|
|
178
|
+
// whole-buffer call would miss the status line — mirror detectWaitingInput.
|
|
179
|
+
return printableLines(text).some((line) => textHasCodexBusyStatus(line));
|
|
180
|
+
}
|
|
181
|
+
|
|
64
182
|
module.exports = {
|
|
65
183
|
...baseDetector,
|
|
66
184
|
id: 'codex',
|
|
@@ -73,17 +191,47 @@ module.exports = {
|
|
|
73
191
|
|
|
74
192
|
isActiveChunk(data) {
|
|
75
193
|
if (!baseDetector.isActiveChunk(data)) return false;
|
|
194
|
+
if (isCodexIdlePromptRedraw(data) || isCodexPassiveDiagnosticChunk(data)) return false;
|
|
76
195
|
if (isCodexStatusRedraw(data)) {
|
|
196
|
+
// Tiny status-region redraws (≤160 chars of WORKING-alphabet content):
|
|
197
|
+
// keep the LOOSE fragment matcher — "Working" routinely splits across
|
|
198
|
+
// chunk boundaries ("king", "Wo7") and these fragments are the only
|
|
199
|
+
// busy signal while the tab is hidden. The alphabet pre-filter in
|
|
200
|
+
// isCodexStatusRedraw bounds the false-positive surface.
|
|
77
201
|
return hasCodexBusyStatusFragment(stripAnsi(data).trim());
|
|
78
202
|
}
|
|
203
|
+
if (isCursorAddressedRedraw(data)) {
|
|
204
|
+
// Positive-evidence rule: a cursor-addressed frame/region repaint is
|
|
205
|
+
// agent work ONLY when a live busy-status line is in it (strict
|
|
206
|
+
// per-line match — printableLines preserves the row structure that
|
|
207
|
+
// stripAnsi would flatten). Everything else — MCP startup status,
|
|
208
|
+
// welcome animation, update nudges, the history half of a split
|
|
209
|
+
// full-frame repaint — is TUI chrome an IDLE codex paints too, and one
|
|
210
|
+
// mis-scored chunk holds the sidebar on "Running" for the whole 15s
|
|
211
|
+
// codex hold. Genuine work keeps its busy row on screen and
|
|
212
|
+
// repainting, so per-chunk strictness costs nothing (split fragments
|
|
213
|
+
// are reassembled by the server's rolling busy-evidence buffer).
|
|
214
|
+
return detectActivelyGenerating(data);
|
|
215
|
+
}
|
|
79
216
|
return true;
|
|
80
217
|
},
|
|
81
218
|
isStatusOnlyChunk(data) {
|
|
82
|
-
return isCodexStatusRedraw(data)
|
|
219
|
+
return isCodexStatusRedraw(data) ||
|
|
220
|
+
isCodexIdlePromptRedraw(data) ||
|
|
221
|
+
isCodexPassiveDiagnosticChunk(data) ||
|
|
222
|
+
(isCursorAddressedRedraw(data) && !detectActivelyGenerating(data));
|
|
83
223
|
},
|
|
84
224
|
isBusyStatusChunk(data) {
|
|
85
225
|
return isCodexStatusRedraw(data) && textHasCodexBusyStatus(data);
|
|
86
226
|
},
|
|
87
227
|
hasCodexBusyStatusFragment,
|
|
88
228
|
textHasCodexBusyStatus,
|
|
229
|
+
isCodexIdlePromptRedraw,
|
|
230
|
+
detectTurnFinished,
|
|
231
|
+
detectWaitingInput,
|
|
232
|
+
detectActivelyGenerating,
|
|
233
|
+
_test: {
|
|
234
|
+
printableLines,
|
|
235
|
+
isCodexPassiveDiagnosticChunk,
|
|
236
|
+
},
|
|
89
237
|
};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
// Cursor (cursor-agent) TUI state detector.
|
|
3
|
+
//
|
|
4
|
+
// cursor-agent renders an interactive TUI: a prompt/composer region, a status
|
|
5
|
+
// footer (model + workspace), and a spinner/status line while the model works.
|
|
6
|
+
// Like OpenCode, it redraws those regions while sitting idle at the prompt, so
|
|
7
|
+
// those frames must paint but must NOT be treated as model activity. Real tool
|
|
8
|
+
// output, approval widgets, and "generating" status keep the session busy.
|
|
9
|
+
//
|
|
10
|
+
// The footer / prompt / busy regexes below are a grounded first cut; they are
|
|
11
|
+
// tuned against live cursor-agent output during the dev verification pass.
|
|
12
|
+
// Anything we don't recognize falls through to baseDetector, so an unmatched
|
|
13
|
+
// frame is treated as activity (fail-safe: never wrongly idle a working agent).
|
|
14
|
+
|
|
15
|
+
const { baseDetector } = require('./base');
|
|
16
|
+
|
|
17
|
+
// Footer: model id + workspace path (e.g. "composer-2.5 · ~/ws/tools").
|
|
18
|
+
const CURSOR_FOOTER_LINE_RE = /^(?:composer|gpt|claude|sonnet|opus|haiku|gemini|o\d|auto)[\w.+-]*(?:\s+[\w.+-]+){0,8}\s+[·|-]\s+(?:~|\/|\.\.?\/)/i;
|
|
19
|
+
// Newer Cursor Agent builds render an idle welcome/composer frame with title,
|
|
20
|
+
// version, help, model, and workspace lines split across rows. These are UI
|
|
21
|
+
// chrome, not agent output, and selecting an idle tab can replay exactly this
|
|
22
|
+
// frame.
|
|
23
|
+
const CURSOR_STATIC_HEADER_LINE_RE = /^Cursor Agent$/i;
|
|
24
|
+
const CURSOR_VERSION_LINE_RE = /^v\d{4}\.\d{2}\.\d{2}[-\w.]*$/i;
|
|
25
|
+
const CURSOR_IDLE_HELP_LINE_RE = /^Use\s+\/plan\b.*\bbefore code changes\.?$/i;
|
|
26
|
+
const CURSOR_IDLE_PLACEHOLDER_RE = /^(?:Plan,\s*)?search,\s*build anything$/i;
|
|
27
|
+
const CURSOR_MODEL_LINE_RE = /^Composer\s+\d+(?:\.\d+)*(?:[-\w.]*)?(?:\s+\w+){0,3}$/i;
|
|
28
|
+
const CURSOR_WORKSPACE_LINE_RE = /^(?:~|\/|\.\.?\/).+\s+[·|-]\s+[\w.+-]+$/;
|
|
29
|
+
// Composer prompt glyphs.
|
|
30
|
+
const CURSOR_PROMPT_LINE_RE = /^(?:>|›|❯|→|▌|\|)\s*(?:\S.*)?$/;
|
|
31
|
+
// Spinner/status while the model is producing output. cursor-agent shows a
|
|
32
|
+
// running verb plus an interrupt hint (esc/ctrl-c) while busy.
|
|
33
|
+
const CURSOR_BUSY_LINE_RE = /^(?:[•●◦▪·*⠿⠷⠯⠟⠻⠽⠾]\s*)?(?:(?:Waiting\s+for\s+background\s+terminal\b.*)|(?:Composing|Generating|Thinking|Working|Reading|Writing|Editing|Running|Searching|Planning|Analyzing|Calling|Using|Processing|Reasoning|Applying|Streaming)\b.*\b(?:esc|interrupt|cancel|stop)\b)/i;
|
|
34
|
+
// Passive status lines that should not hold the session busy.
|
|
35
|
+
const CURSOR_PASSIVE_STATUS_LINE_RE = /^(?:[•●◦▪·*]\s*)?(?:Ready|Waiting|Idle|Stopped|Done|Complete|Completed|Accept|Reject|Run everything|Allowlist)\b/i;
|
|
36
|
+
const CURSOR_CONTROL_HINT_RE = /^(?:esc|ctrl-c|ctrl\+c|tab|enter|press\s+enter)\b.*(?:interrupt|cancel|confirm|send|accept|reject|toggle)/i;
|
|
37
|
+
|
|
38
|
+
function stripAnsiPreserveLines(data) {
|
|
39
|
+
return String(data || '')
|
|
40
|
+
// Cursor-addressed redraws otherwise collapse rows together after stripping
|
|
41
|
+
// CSI. Preserve row boundaries before removing control codes.
|
|
42
|
+
.replace(/\x1b\[[0-9]+;[0-9]+[Hf]/g, '\n')
|
|
43
|
+
.replace(/\x1b\[[0-9]+[ABCD]/g, '\n')
|
|
44
|
+
.replace(/\x1b\[[0-9;?]*[a-zA-Z]/g, '')
|
|
45
|
+
.replace(/\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)/g, '')
|
|
46
|
+
.replace(/\r/g, '\n')
|
|
47
|
+
.replace(/[\x00-\x09\x0b\x0c\x0e-\x1f\x7f]/g, '');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function printableLines(data) {
|
|
51
|
+
return stripAnsiPreserveLines(data)
|
|
52
|
+
.replace(/\u00a0/g, ' ')
|
|
53
|
+
.split(/\n+/)
|
|
54
|
+
.map(line => line.trim())
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isCursorPassiveLine(line) {
|
|
59
|
+
const text = String(line || '').trim();
|
|
60
|
+
if (!text) return true;
|
|
61
|
+
return CURSOR_FOOTER_LINE_RE.test(text) ||
|
|
62
|
+
CURSOR_STATIC_HEADER_LINE_RE.test(text) ||
|
|
63
|
+
CURSOR_VERSION_LINE_RE.test(text) ||
|
|
64
|
+
CURSOR_IDLE_HELP_LINE_RE.test(text) ||
|
|
65
|
+
CURSOR_IDLE_PLACEHOLDER_RE.test(text) ||
|
|
66
|
+
CURSOR_MODEL_LINE_RE.test(text) ||
|
|
67
|
+
CURSOR_WORKSPACE_LINE_RE.test(text) ||
|
|
68
|
+
CURSOR_PROMPT_LINE_RE.test(text) ||
|
|
69
|
+
CURSOR_PASSIVE_STATUS_LINE_RE.test(text) ||
|
|
70
|
+
CURSOR_CONTROL_HINT_RE.test(text);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function isCursorBusyLine(line) {
|
|
74
|
+
return CURSOR_BUSY_LINE_RE.test(String(line || '').trim());
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function detectWaitingInput(text) {
|
|
78
|
+
const lines = printableLines(text).slice(-10);
|
|
79
|
+
if (!lines.length) return null;
|
|
80
|
+
if (lines.some(isCursorBusyLine)) return null;
|
|
81
|
+
if (!lines.some(line => CURSOR_PROMPT_LINE_RE.test(line))) return null;
|
|
82
|
+
return { reason: 'input' };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// The agent is mid-turn when its busy/spinner line ("Generating… esc to interrupt")
|
|
86
|
+
// is on screen. server.js's generating-gate uses this to suppress the idle-notify so
|
|
87
|
+
// a long, output-quiet Cursor turn whose composer matches a global prompt pattern is
|
|
88
|
+
// not mislabeled "Needs You". Tail-scoped so a stale spinner up in scrollback doesn't
|
|
89
|
+
// count; same busy signal detectWaitingInput already gates on.
|
|
90
|
+
function detectActivelyGenerating(text) {
|
|
91
|
+
return printableLines(text).slice(-12).some(isCursorBusyLine);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
module.exports = {
|
|
95
|
+
...baseDetector,
|
|
96
|
+
id: 'cursor',
|
|
97
|
+
// Cursor redraws prompt/footer regions periodically; hold the provider only
|
|
98
|
+
// for meaningful activity frames, mirroring the OpenCode debounce.
|
|
99
|
+
idleDebounceMs: 10000,
|
|
100
|
+
|
|
101
|
+
isActiveChunk(data) {
|
|
102
|
+
if (!baseDetector.isActiveChunk(data)) return false;
|
|
103
|
+
const lines = printableLines(data);
|
|
104
|
+
if (lines.length && lines.every(line => isCursorPassiveLine(line) && !isCursorBusyLine(line))) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
return true;
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
isStatusOnlyChunk(data) {
|
|
111
|
+
const lines = printableLines(data);
|
|
112
|
+
return !!(lines.length && lines.every(line => isCursorPassiveLine(line) || isCursorBusyLine(line)));
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
isBusyStatusChunk(data) {
|
|
116
|
+
const lines = printableLines(data);
|
|
117
|
+
return lines.some(isCursorBusyLine);
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
detectWaitingInput,
|
|
121
|
+
detectActivelyGenerating,
|
|
122
|
+
_test: {
|
|
123
|
+
printableLines,
|
|
124
|
+
isCursorPassiveLine,
|
|
125
|
+
isCursorBusyLine,
|
|
126
|
+
},
|
|
127
|
+
};
|