create-walle 0.9.21 → 0.9.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -5
- package/package.json +2 -2
- package/template/CLAUDE.md +2 -2
- package/template/LICENSE +1 -1
- package/template/bin/ctm-dev-cleanup.js +24 -3
- package/template/bin/ctm-launch.sh +13 -0
- package/template/bin/dev.sh +156 -18
- package/template/bin/node-bin.sh +84 -0
- package/template/bin/pin-node.sh +51 -0
- package/template/claude-task-manager/api-prompts.js +1203 -182
- package/template/claude-task-manager/api-reviews.js +109 -15
- package/template/claude-task-manager/approval-agent.js +1360 -280
- package/template/claude-task-manager/bin/restart-ctm.sh +64 -23
- package/template/claude-task-manager/bin/storage-migration-supervisor.js +338 -0
- package/template/claude-task-manager/db.js +4417 -295
- package/template/claude-task-manager/docs/app-update-refresh-protocol.md +69 -0
- package/template/claude-task-manager/docs/approval-ai-refinement.md +138 -0
- package/template/claude-task-manager/docs/approval-rescue-loop.md +74 -0
- package/template/claude-task-manager/docs/codex-operational-warning-health.md +107 -0
- package/template/claude-task-manager/docs/codex-resume-state-guard-design.md +17 -12
- package/template/claude-task-manager/docs/codex-terminal-render-controller-handoff.md +311 -0
- package/template/claude-task-manager/docs/coding-agent-hooks-architecture.md +418 -0
- package/template/claude-task-manager/docs/conversation-import-freshness.md +20 -0
- package/template/claude-task-manager/docs/google-workspace-auth-health.md +77 -0
- package/template/claude-task-manager/docs/image-paste-ux.md +13 -0
- package/template/claude-task-manager/docs/ipad-web-preview.md +88 -0
- package/template/claude-task-manager/docs/main-loop-offload-architecture.md +66 -0
- package/template/claude-task-manager/docs/microsoft-dev-tunnel-phone-access-design.md +274 -519
- package/template/claude-task-manager/docs/mobile-live-streaming.md +27 -5
- package/template/claude-task-manager/docs/mobile-remote-submission-lifecycle.md +69 -0
- package/template/claude-task-manager/docs/phone-access-design.md +53 -15
- package/template/claude-task-manager/docs/phone-passkey-identity.md +122 -0
- package/template/claude-task-manager/docs/phone-setup.md +3 -0
- package/template/claude-task-manager/docs/prompt-editing-tree-design.md +25 -1
- package/template/claude-task-manager/docs/remote-desktop-access-design.md +268 -0
- package/template/claude-task-manager/docs/restart-lifecycle-architecture.md +95 -0
- package/template/claude-task-manager/docs/runtime-work-control-plane.md +53 -0
- package/template/claude-task-manager/docs/session-interactive-wait-surfaces.md +38 -0
- package/template/claude-task-manager/docs/session-needs-you-dismissal.md +84 -0
- package/template/claude-task-manager/docs/session-render-state-management-design.md +91 -3
- package/template/claude-task-manager/docs/session-standup-command-center-design.md +25 -1
- package/template/claude-task-manager/docs/session-title-authority.md +32 -0
- package/template/claude-task-manager/docs/session-workspace-binding.md +33 -0
- package/template/claude-task-manager/docs/skill-intent-resolution-design.md +72 -0
- package/template/claude-task-manager/docs/walle-mcp-supervisor-health.md +86 -0
- package/template/claude-task-manager/docs/walle-relay-phone-access-design.md +24 -15
- package/template/claude-task-manager/docs/walle-session-history-hydration.md +114 -0
- package/template/claude-task-manager/docs/walle-session-input-queue.md +104 -0
- package/template/claude-task-manager/docs/walle-session-model-catalog.md +90 -0
- package/template/claude-task-manager/docs/walle-session-model-preferences.md +15 -6
- package/template/claude-task-manager/git-utils.js +897 -27
- package/template/claude-task-manager/lib/agent-capabilities.js +33 -0
- package/template/claude-task-manager/lib/agent-cli-cache.js +37 -7
- package/template/claude-task-manager/lib/agent-hooks-installer.js +26 -2
- package/template/claude-task-manager/lib/agent-presets.js +17 -1
- package/template/claude-task-manager/lib/all-sessions-query.js +108 -0
- package/template/claude-task-manager/lib/approval-ai-refinement.js +488 -0
- package/template/claude-task-manager/lib/approval-self-adapt.js +168 -0
- package/template/claude-task-manager/lib/async-semaphore.js +44 -0
- package/template/claude-task-manager/lib/auth-context.js +5 -0
- package/template/claude-task-manager/lib/auth-rate-limit.js +47 -4
- package/template/claude-task-manager/lib/auth-rules.js +29 -2
- package/template/claude-task-manager/lib/auto-approval-verifier.js +129 -16
- package/template/claude-task-manager/lib/background-llm.js +144 -17
- package/template/claude-task-manager/lib/branch-inventory.js +212 -0
- package/template/claude-task-manager/lib/claude-desktop-sessions.js +15 -3
- package/template/claude-task-manager/lib/coalesce-sync-frames.js +151 -0
- package/template/claude-task-manager/lib/codex-launch-health.js +762 -0
- package/template/claude-task-manager/lib/codex-transcript-pager.js +51 -0
- package/template/claude-task-manager/lib/codex-zst.js +124 -0
- package/template/claude-task-manager/lib/coding-agent-models.js +233 -30
- package/template/claude-task-manager/lib/connection-health.js +232 -0
- package/template/claude-task-manager/lib/conversation-blob-parser.js +42 -0
- package/template/claude-task-manager/lib/conversation-tail-merge.js +89 -26
- package/template/claude-task-manager/lib/ctm-session-context-api.js +39 -10
- package/template/claude-task-manager/lib/cursor-conversation-store.js +354 -0
- package/template/claude-task-manager/lib/db-owner-worker-client.js +315 -0
- package/template/claude-task-manager/lib/document-review.js +141 -6
- package/template/claude-task-manager/lib/escalation-review.js +152 -0
- package/template/claude-task-manager/lib/graceful-shutdown.js +159 -0
- package/template/claude-task-manager/lib/headless-term-service.js +678 -0
- package/template/claude-task-manager/lib/heavy-worker-fallback.js +38 -0
- package/template/claude-task-manager/lib/jsonl-conversation-parser.js +542 -0
- package/template/claude-task-manager/lib/jsonl-range-reader.js +112 -0
- package/template/claude-task-manager/lib/main-db-census.js +216 -0
- package/template/claude-task-manager/lib/message-pagination.js +106 -4
- package/template/claude-task-manager/lib/microsoft-dev-tunnel-setup.js +750 -26
- package/template/claude-task-manager/lib/mobile-auth-api.js +274 -7
- package/template/claude-task-manager/lib/mobile-auth-store.js +592 -10
- package/template/claude-task-manager/lib/mobile-notification-dispatcher.js +15 -0
- package/template/claude-task-manager/lib/model-overview-brain-fallback.js +311 -0
- package/template/claude-task-manager/lib/model-overview-cache.js +141 -0
- package/template/claude-task-manager/lib/models-health-routing-notice.js +126 -0
- package/template/claude-task-manager/lib/node-pin-guard.js +93 -0
- package/template/claude-task-manager/lib/perf-tracker.js +242 -6
- package/template/claude-task-manager/lib/permission-match.js +76 -0
- package/template/claude-task-manager/lib/permission-sync.js +133 -20
- package/template/claude-task-manager/lib/process-title.js +35 -0
- package/template/claude-task-manager/lib/prompt-executions-query.js +25 -0
- package/template/claude-task-manager/lib/prompt-index-disk-cache.js +44 -0
- package/template/claude-task-manager/lib/prompt-intent.js +132 -0
- package/template/claude-task-manager/lib/provider-user-context.js +34 -0
- package/template/claude-task-manager/lib/read-pool-client.js +313 -0
- package/template/claude-task-manager/lib/readpool-breaker.js +31 -0
- package/template/claude-task-manager/lib/recent-sessions-breaker.js +12 -0
- package/template/claude-task-manager/lib/remote-feedback-client.js +72 -0
- package/template/claude-task-manager/lib/remote-relay-protocol.js +37 -4
- package/template/claude-task-manager/lib/remote-relay-store.js +159 -0
- package/template/claude-task-manager/lib/remote-submission-observer.js +278 -0
- package/template/claude-task-manager/lib/restart-guard.js +109 -0
- package/template/claude-task-manager/lib/restore-interruption-detector.js +439 -0
- package/template/claude-task-manager/lib/restore-policy.js +13 -0
- package/template/claude-task-manager/lib/restore-resume-batch.js +74 -0
- package/template/claude-task-manager/lib/restore-runtime.js +68 -0
- package/template/claude-task-manager/lib/restore-storm.js +34 -0
- package/template/claude-task-manager/lib/resume-cwd.js +36 -0
- package/template/claude-task-manager/lib/resume-preflight.js +313 -0
- package/template/claude-task-manager/lib/runtime-work-registry.js +444 -0
- package/template/claude-task-manager/lib/sanitize-openai-auth.js +31 -0
- package/template/claude-task-manager/lib/scheduler.js +21 -1
- package/template/claude-task-manager/lib/scrollback-snapshot-store.js +159 -0
- package/template/claude-task-manager/lib/serial-task-queue.js +64 -0
- package/template/claude-task-manager/lib/server-listeners.js +239 -0
- package/template/claude-task-manager/lib/session-capture.js +42 -7
- package/template/claude-task-manager/lib/session-content-backfill.js +131 -0
- package/template/claude-task-manager/lib/session-history.js +388 -43
- package/template/claude-task-manager/lib/session-host-manager.js +287 -0
- package/template/claude-task-manager/lib/session-image-refs.js +209 -0
- package/template/claude-task-manager/lib/session-jobs.js +399 -59
- package/template/claude-task-manager/lib/session-prompt-index.js +137 -0
- package/template/claude-task-manager/lib/session-restore.js +53 -0
- package/template/claude-task-manager/lib/session-standup.js +123 -23
- package/template/claude-task-manager/lib/session-state-bus.js +14 -0
- package/template/claude-task-manager/lib/session-stream.js +64 -16
- package/template/claude-task-manager/lib/session-timeline-summary.js +260 -0
- package/template/claude-task-manager/lib/session-token-usage.js +494 -0
- package/template/claude-task-manager/lib/session-workspace-binding.js +356 -0
- package/template/claude-task-manager/lib/setup-network-config.js +9 -0
- package/template/claude-task-manager/lib/size-cap.js +45 -0
- package/template/claude-task-manager/lib/size-cap.test.js +62 -0
- package/template/claude-task-manager/lib/skill-autocomplete.js +180 -1
- package/template/claude-task-manager/lib/skill-intent-resolver.js +304 -0
- package/template/claude-task-manager/lib/sqlite-driver.js +19 -3
- package/template/claude-task-manager/lib/standup-attention.js +7 -3
- package/template/claude-task-manager/lib/status-authority.js +39 -0
- package/template/claude-task-manager/lib/status-hooks.js +4 -0
- package/template/claude-task-manager/lib/storage-migration.js +235 -0
- package/template/claude-task-manager/lib/structured-capture.js +298 -0
- package/template/claude-task-manager/lib/sync-io-census.js +163 -0
- package/template/claude-task-manager/lib/tailscale-setup.js +6 -0
- package/template/claude-task-manager/lib/terminal-activity-evidence.js +33 -0
- package/template/claude-task-manager/lib/terminal-choice.js +364 -0
- package/template/claude-task-manager/lib/terminal-control-sanitize.js +17 -0
- package/template/claude-task-manager/lib/terminal-fingerprint.js +48 -0
- package/template/claude-task-manager/lib/terminal-output-flush.js +84 -0
- package/template/claude-task-manager/lib/timeline-order.js +122 -0
- package/template/claude-task-manager/lib/transcript-store.js +348 -43
- package/template/claude-task-manager/lib/transport-security.js +84 -1
- package/template/claude-task-manager/lib/wait-state.js +184 -0
- package/template/claude-task-manager/lib/walle-client.js +47 -5
- package/template/claude-task-manager/lib/walle-ctm-history.js +564 -4
- package/template/claude-task-manager/lib/walle-external-actions.js +135 -16
- package/template/claude-task-manager/lib/walle-history-hydration.js +46 -0
- package/template/claude-task-manager/lib/walle-native-health.js +403 -0
- package/template/claude-task-manager/lib/walle-repair.js +701 -0
- package/template/claude-task-manager/lib/walle-session-cache.js +109 -0
- package/template/claude-task-manager/lib/walle-session-context.js +57 -21
- package/template/claude-task-manager/lib/walle-session-model-catalog.js +34 -0
- package/template/claude-task-manager/lib/walle-supervisor.js +539 -63
- package/template/claude-task-manager/lib/walle-transcript.js +52 -0
- package/template/claude-task-manager/lib/worktree-active-sync.js +11 -7
- package/template/claude-task-manager/lib/worktree-cwd.js +32 -1
- package/template/claude-task-manager/package.json +1 -1
- package/template/claude-task-manager/prompt-harvest.js +89 -66
- package/template/claude-task-manager/providers/claude-code.js +51 -3
- package/template/claude-task-manager/providers/cursor.js +140 -45
- package/template/claude-task-manager/public/css/reviews.css +551 -61
- package/template/claude-task-manager/public/css/setup.css +191 -0
- package/template/claude-task-manager/public/css/walle-session.css +865 -10
- package/template/claude-task-manager/public/css/walle.css +154 -0
- package/template/claude-task-manager/public/designs/ai-providers-consolidation-v2.html +830 -0
- package/template/claude-task-manager/public/index.html +18516 -2058
- package/template/claude-task-manager/public/ipad.html +363 -0
- package/template/claude-task-manager/public/js/document-review-links.js +301 -0
- package/template/claude-task-manager/public/js/image-normalize.js +69 -36
- package/template/claude-task-manager/public/js/message-renderer.js +1265 -77
- package/template/claude-task-manager/public/js/prompts.js +66 -29
- package/template/claude-task-manager/public/js/reviews.js +901 -133
- package/template/claude-task-manager/public/js/session-activity-utils.js +11 -1
- package/template/claude-task-manager/public/js/session-search-utils.js +94 -10
- package/template/claude-task-manager/public/js/session-status-precedence.js +23 -5
- package/template/claude-task-manager/public/js/setup.js +1273 -176
- package/template/claude-task-manager/public/js/stream-view.js +691 -73
- package/template/claude-task-manager/public/js/terminal-reconciler.js +210 -0
- package/template/claude-task-manager/public/js/walle-session.js +2455 -158
- package/template/claude-task-manager/public/js/walle.js +455 -28
- package/template/claude-task-manager/public/m/app.css +2909 -262
- package/template/claude-task-manager/public/m/app.js +6601 -398
- package/template/claude-task-manager/public/m/claim.html +224 -17
- package/template/claude-task-manager/public/m/index.html +117 -21
- package/template/claude-task-manager/public/m/sw.js +3 -1
- package/template/claude-task-manager/public/manifest.json +2 -2
- package/template/claude-task-manager/public/prompts.html +30 -14
- package/template/claude-task-manager/queue-engine.js +507 -28
- package/template/claude-task-manager/scripts/repair-claude-session-images.js +27 -8
- package/template/claude-task-manager/server.js +14341 -2197
- package/template/claude-task-manager/session-integrity.js +160 -18
- package/template/claude-task-manager/session-search-ranking.js +1 -0
- package/template/claude-task-manager/session-utils.js +25 -5
- package/template/claude-task-manager/workers/approval-blocklist.js +96 -6
- package/template/claude-task-manager/workers/approval-widget-validator.js +14 -8
- package/template/claude-task-manager/workers/conversation-import-worker.js +11 -50
- package/template/claude-task-manager/workers/db-owner-worker.js +386 -0
- package/template/claude-task-manager/workers/harvest-worker.js +9 -55
- package/template/claude-task-manager/workers/headless-term-worker.js +9 -530
- package/template/claude-task-manager/workers/read-pool-worker.js +387 -0
- package/template/claude-task-manager/workers/scrollback-worker.js +11 -72
- package/template/claude-task-manager/workers/session-host-process.js +146 -0
- package/template/claude-task-manager/workers/session-integrity-worker.js +10 -54
- package/template/claude-task-manager/workers/state-detectors/base.js +18 -1
- package/template/claude-task-manager/workers/state-detectors/claude-code.js +182 -9
- package/template/claude-task-manager/workers/state-detectors/codex.js +150 -2
- package/template/claude-task-manager/workers/state-detectors/cursor.js +127 -0
- package/template/claude-task-manager/workers/state-detectors/gemini.js +21 -0
- package/template/claude-task-manager/workers/state-detectors/index.js +29 -0
- package/template/claude-task-manager/workers/state-detectors/opencode.js +103 -0
- package/template/docs/design/markdown-review-pane.md +206 -0
- package/template/docs/designs/2026-05-17-portkey-gateway-provider-ux.md +129 -38
- package/template/docs/designs/2026-05-20-mobile-worktree-finish-command.md +27 -0
- package/template/docs/designs/2026-05-22-ai-configuration-consolidation.md +248 -0
- package/template/docs/designs/ai-configuration-consolidation-mock.html +812 -0
- package/template/docs/private-memory-and-pii-policy.md +69 -0
- package/template/package.json +2 -1
- package/template/scripts/check-private-data.js +201 -0
- package/template/shared/sqlite-owner-guard.js +30 -0
- package/template/shared/sqlite-owner-write-queue.js +225 -0
- package/template/shared/sqlite-storage-policy.js +111 -0
- package/template/shared/sqlite-write-lock.js +428 -0
- package/template/wall-e/agent-runners/claude-code.js +5 -0
- package/template/wall-e/agent.js +166 -22
- package/template/wall-e/api-walle.js +524 -70
- package/template/wall-e/auth/provider-flows.js +11 -1
- package/template/wall-e/bin/walle-mcp-stdio.js +341 -17
- package/template/wall-e/brain.js +1614 -141
- package/template/wall-e/chat/attachment-blocks.js +96 -0
- package/template/wall-e/chat/attachments.js +2 -1
- package/template/wall-e/chat/capability-resolver.js +7 -7
- package/template/wall-e/chat/context-messages.js +28 -0
- package/template/wall-e/chat/conversation-frame.js +630 -0
- package/template/wall-e/chat/provider-messages.js +125 -0
- package/template/wall-e/chat.js +1002 -233
- package/template/wall-e/coding/acceptance-contract.js +170 -0
- package/template/wall-e/coding/acp-adapter.js +1 -1
- package/template/wall-e/coding/agent-catalog.js +3 -0
- package/template/wall-e/coding/artifact-store.js +93 -0
- package/template/wall-e/coding/capability-router.js +120 -0
- package/template/wall-e/coding/coding-run-controller.js +423 -0
- package/template/wall-e/coding/compaction-service.js +157 -12
- package/template/wall-e/coding/frontend-verification.js +258 -0
- package/template/wall-e/coding/lifecycle-hooks.js +75 -0
- package/template/wall-e/coding/local-preview-contract.js +157 -0
- package/template/wall-e/coding/permission-service.js +57 -13
- package/template/wall-e/coding/prompt-bundle.js +19 -1
- package/template/wall-e/coding/prompt-section-registry.js +227 -0
- package/template/wall-e/coding/provider-compat.js +15 -0
- package/template/wall-e/coding/runtime-events.js +224 -0
- package/template/wall-e/coding/runtime-mode.js +3 -0
- package/template/wall-e/coding/side-git-snapshot.js +160 -4
- package/template/wall-e/coding/snapshot-service.js +143 -1
- package/template/wall-e/coding/stream-processor.js +388 -34
- package/template/wall-e/coding/task-tool.js +141 -4
- package/template/wall-e/coding/tool-execution-controller.js +365 -0
- package/template/wall-e/coding/tool-registry.js +43 -5
- package/template/wall-e/coding/user-hooks.js +217 -0
- package/template/wall-e/coding-orchestrator.js +1330 -221
- package/template/wall-e/coding-prompts.js +20 -4
- package/template/wall-e/context/context-builder.js +15 -2
- package/template/wall-e/decision/confidence.js +1 -1
- package/template/wall-e/docs/coding-acceptance-contract.md +41 -0
- package/template/wall-e/docs/external-action-controller.md +26 -6
- package/template/wall-e/docs/telemetry-lifecycle.md +8 -2
- package/template/wall-e/embeddings.js +591 -53
- package/template/wall-e/external-action-controller.js +12 -0
- package/template/wall-e/http/auth.js +1 -0
- package/template/wall-e/http/chat-api.js +46 -11
- package/template/wall-e/http/model-admin.js +836 -34
- package/template/wall-e/lib/boot-profile.js +88 -0
- package/template/wall-e/lib/event-loop-monitor.js +93 -0
- package/template/wall-e/lib/service-health.js +194 -0
- package/template/wall-e/llm/anthropic.js +130 -5
- package/template/wall-e/llm/client.js +266 -63
- package/template/wall-e/llm/default-fallback.js +382 -0
- package/template/wall-e/llm/health.js +19 -0
- package/template/wall-e/llm/message-guard.js +78 -0
- package/template/wall-e/llm/model-catalog.js +252 -1
- package/template/wall-e/llm/openai.js +26 -4
- package/template/wall-e/llm/portkey-sync.js +654 -0
- package/template/wall-e/llm/provider-error.js +30 -2
- package/template/wall-e/llm/registry.js +5 -1
- package/template/wall-e/llm/request-compat.js +67 -0
- package/template/wall-e/loops/backfill.js +79 -23
- package/template/wall-e/loops/brain-optimize.js +67 -0
- package/template/wall-e/loops/ingest.js +25 -10
- package/template/wall-e/loops/question-digest.js +160 -0
- package/template/wall-e/loops/reflect.js +6 -4
- package/template/wall-e/loops/think.js +39 -12
- package/template/wall-e/mcp-server.js +318 -36
- package/template/wall-e/memory/ctm-context-client.js +52 -14
- package/template/wall-e/memory/ctm-operational-context.js +237 -0
- package/template/wall-e/memory/ctm-prompt-executions-client.js +128 -0
- package/template/wall-e/memory/ctm-session-context.js +111 -63
- package/template/wall-e/prompts/coding/deepseek.txt +3 -0
- package/template/wall-e/prompts/coding/gemini.txt +6 -0
- package/template/wall-e/prompts/coding/gpt.txt +6 -0
- package/template/wall-e/prompts/coding/local.txt +7 -0
- package/template/wall-e/runtime/decision-hooks.js +115 -0
- package/template/wall-e/runtime/devbox-gateway.js +82 -8
- package/template/wall-e/runtime/prompt-manifest.js +86 -0
- package/template/wall-e/runtime/tool-executor.js +269 -0
- package/template/wall-e/runtime/tool-result-envelope.js +138 -0
- package/template/wall-e/runtime/transcript-projection.js +60 -0
- package/template/wall-e/runtime/walle-runtime.js +224 -0
- package/template/wall-e/scripts/db-optimize/migrate.js +162 -0
- package/template/wall-e/scripts/db-optimize/recall-eval.js +117 -0
- package/template/wall-e/server.js +15 -0
- package/template/wall-e/session-files.js +9 -0
- package/template/wall-e/skills/_bundled/google-calendar/run.js +1 -1
- package/template/wall-e/skills/_bundled/gws-workspace/run.js +1 -1
- package/template/wall-e/skills/_bundled/slack-mentions/run.js +76 -6
- package/template/wall-e/skills/claude-code-reader.js +7 -3
- package/template/wall-e/skills/script-skill-runner.js +10 -0
- package/template/wall-e/skills/skill-planner.js +38 -0
- package/template/wall-e/tools/builtin-middleware.js +19 -9
- package/template/wall-e/tools/local-tools.js +1428 -16
- package/template/wall-e/tools/permission-checker.js +73 -5
- package/template/wall-e/tools/question-manager.js +117 -7
- package/template/wall-e/training/harvester.js +12 -28
- package/template/wall-e/training/replay.js +25 -80
- package/template/website/index.html +10 -10
- package/template/wall-e/eval/ab-test.js +0 -203
- package/template/wall-e/eval/agent-runner.js +0 -772
- package/template/wall-e/eval/agent-scorer.js +0 -461
- package/template/wall-e/eval/aggregator.js +0 -414
- package/template/wall-e/eval/allowed-test-commands.js +0 -34
- package/template/wall-e/eval/benchmark-generator.js +0 -113
- package/template/wall-e/eval/benchmarks/chat-eval.json +0 -1662
- package/template/wall-e/eval/benchmarks/chat.json +0 -82
- package/template/wall-e/eval/benchmarks/coding-agent-real.json +0 -1
- package/template/wall-e/eval/benchmarks/coding-agent.json +0 -1581
- package/template/wall-e/eval/benchmarks/coding.json +0 -122
- package/template/wall-e/eval/benchmarks/memory-retrieval.json +0 -234
- package/template/wall-e/eval/benchmarks/reasoning.json +0 -82
- package/template/wall-e/eval/benchmarks/swebench-lite-30.json +0 -212
- package/template/wall-e/eval/benchmarks.js +0 -669
- package/template/wall-e/eval/cc-replay.js +0 -719
- package/template/wall-e/eval/chat-eval.js +0 -525
- package/template/wall-e/eval/check-keys.js +0 -15
- package/template/wall-e/eval/check-providers.js +0 -42
- package/template/wall-e/eval/codex-cli-baseline.js +0 -669
- package/template/wall-e/eval/coding-agent-real.js +0 -570
- package/template/wall-e/eval/context-compactor.js +0 -251
- package/template/wall-e/eval/debug-agent003.js +0 -68
- package/template/wall-e/eval/diagnostics.js +0 -216
- package/template/wall-e/eval/eval-orchestrator.js +0 -642
- package/template/wall-e/eval/evaluate.js +0 -202
- package/template/wall-e/eval/evaluator.js +0 -373
- package/template/wall-e/eval/exporter.js +0 -212
- package/template/wall-e/eval/fixtures/express-basic/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-basic/server.js +0 -115
- package/template/wall-e/eval/fixtures/express-basic/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy/server.js +0 -113
- package/template/wall-e/eval/fixtures/express-buggy/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy-items/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy-items/server.js +0 -112
- package/template/wall-e/eval/fixtures/express-buggy-items/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-buggy-search/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-buggy-search/server.js +0 -121
- package/template/wall-e/eval/fixtures/express-buggy-search/test.js +0 -83
- package/template/wall-e/eval/fixtures/express-rename-data/data.js +0 -34
- package/template/wall-e/eval/fixtures/express-rename-data/package.json +0 -9
- package/template/wall-e/eval/fixtures/express-rename-data/server.js +0 -97
- package/template/wall-e/eval/fixtures/express-rename-data/test.js +0 -88
- package/template/wall-e/eval/fixtures/express-xss/package.json +0 -12
- package/template/wall-e/eval/fixtures/express-xss/server.js +0 -90
- package/template/wall-e/eval/fixtures/express-xss/test.js +0 -67
- package/template/wall-e/eval/fixtures/express-xss/views/profile.ejs +0 -9
- package/template/wall-e/eval/fixtures/fullstack-app/config/default.js +0 -9
- package/template/wall-e/eval/fixtures/fullstack-app/config/test.js +0 -13
- package/template/wall-e/eval/fixtures/fullstack-app/package.json +0 -11
- package/template/wall-e/eval/fixtures/fullstack-app/public/css/style.css +0 -137
- package/template/wall-e/eval/fixtures/fullstack-app/public/index.html +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/app.js +0 -121
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/auth.js +0 -71
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/items.js +0 -80
- package/template/wall-e/eval/fixtures/fullstack-app/public/js/users.js +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/public/login.html +0 -45
- package/template/wall-e/eval/fixtures/fullstack-app/public/register.html +0 -38
- package/template/wall-e/eval/fixtures/fullstack-app/scripts/migrate.js +0 -23
- package/template/wall-e/eval/fixtures/fullstack-app/scripts/seed.js +0 -46
- package/template/wall-e/eval/fixtures/fullstack-app/server/db.js +0 -99
- package/template/wall-e/eval/fixtures/fullstack-app/server/index.js +0 -94
- package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/auth.js +0 -19
- package/template/wall-e/eval/fixtures/fullstack-app/server/middleware/logger.js +0 -19
- package/template/wall-e/eval/fixtures/fullstack-app/server/router.js +0 -50
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/auth.js +0 -69
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/health.js +0 -23
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/items.js +0 -88
- package/template/wall-e/eval/fixtures/fullstack-app/server/routes/users.js +0 -75
- package/template/wall-e/eval/fixtures/fullstack-app/server/test.js +0 -198
- package/template/wall-e/eval/fixtures/fullstack-app/server/utils/response.js +0 -34
- package/template/wall-e/eval/fixtures/fullstack-app/server/utils/validate.js +0 -26
- package/template/wall-e/eval/fixtures/fullstack-app/server.js +0 -8
- package/template/wall-e/eval/fixtures/fullstack-app/test.js +0 -12
- package/template/wall-e/eval/fixtures/monorepo-basic/package.json +0 -8
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/data.js +0 -58
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/middleware.js +0 -46
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/package.json +0 -8
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/routes.js +0 -64
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/server.js +0 -56
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/api/test.js +0 -116
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/commands.js +0 -61
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/index.js +0 -62
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/output.js +0 -43
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/package.json +0 -11
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/cli/test.js +0 -44
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/formatters.js +0 -43
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/index.js +0 -12
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/package.json +0 -5
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/test.js +0 -55
- package/template/wall-e/eval/fixtures/monorepo-basic/packages/shared/validators.js +0 -29
- package/template/wall-e/eval/fixtures/monorepo-basic/test.js +0 -46
- package/template/wall-e/eval/fixtures/node-cli/index.js +0 -78
- package/template/wall-e/eval/fixtures/node-cli/package.json +0 -10
- package/template/wall-e/eval/fixtures/node-cli/test.js +0 -57
- package/template/wall-e/eval/fixtures/node-typed/package.json +0 -8
- package/template/wall-e/eval/fixtures/node-typed/src/handlers.js +0 -31
- package/template/wall-e/eval/fixtures/node-typed/src/utils.js +0 -33
- package/template/wall-e/eval/fixtures/node-typed/test.js +0 -36
- package/template/wall-e/eval/fixtures/python-flask/app.py +0 -14
- package/template/wall-e/eval/fixtures/python-flask/requirements.txt +0 -2
- package/template/wall-e/eval/fixtures/python-flask/test_app.py +0 -25
- package/template/wall-e/eval/fixtures/wall-e-subset/brain.js +0 -105
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/aggregator.js +0 -101
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/chat.json +0 -20
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks/coding.json +0 -32
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/benchmarks.js +0 -64
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/package.json +0 -6
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/server.js +0 -31
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/test.js +0 -18
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/fixtures/simple-project/utils.js +0 -34
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/runner.js +0 -104
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/scorer.js +0 -73
- package/template/wall-e/eval/fixtures/wall-e-subset/eval/test.js +0 -134
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/client.js +0 -99
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/providers.js +0 -63
- package/template/wall-e/eval/fixtures/wall-e-subset/llm/test.js +0 -70
- package/template/wall-e/eval/fixtures/wall-e-subset/package.json +0 -10
- package/template/wall-e/eval/fixtures/wall-e-subset/test.js +0 -86
- package/template/wall-e/eval/harvester.js +0 -685
- package/template/wall-e/eval/head-to-head.js +0 -388
- package/template/wall-e/eval/humaneval-adapter.js +0 -321
- package/template/wall-e/eval/list-models.js +0 -31
- package/template/wall-e/eval/livecodebench-adapter.js +0 -291
- package/template/wall-e/eval/mail-integration.js +0 -443
- package/template/wall-e/eval/manifest.js +0 -186
- package/template/wall-e/eval/meta-harness/adapters/coding-agent.js +0 -57
- package/template/wall-e/eval/meta-harness/bootstrap-snapshot.js +0 -149
- package/template/wall-e/eval/meta-harness/candidate-store.js +0 -117
- package/template/wall-e/eval/meta-harness/cli.js +0 -86
- package/template/wall-e/eval/meta-harness/domain-spec.js +0 -154
- package/template/wall-e/eval/meta-harness/domains/coding-agent.domain.json +0 -84
- package/template/wall-e/eval/meta-harness/examples/env-bootstrap-candidate.js +0 -29
- package/template/wall-e/eval/meta-harness/experience-store.js +0 -174
- package/template/wall-e/eval/meta-harness/frontier.js +0 -96
- package/template/wall-e/eval/meta-harness/harness-interface.js +0 -90
- package/template/wall-e/eval/meta-harness/leakage-guard.js +0 -80
- package/template/wall-e/eval/meta-harness/optimizer.js +0 -207
- package/template/wall-e/eval/meta-harness/proposer-runner.js +0 -110
- package/template/wall-e/eval/meta-harness/reporting.js +0 -58
- package/template/wall-e/eval/meta-harness/telemetry.js +0 -27
- package/template/wall-e/eval/meta-harness/validation.js +0 -81
- package/template/wall-e/eval/promoter.js +0 -228
- package/template/wall-e/eval/provider-normalizer.js +0 -33
- package/template/wall-e/eval/replay.js +0 -395
- package/template/wall-e/eval/run-agent-benchmarks.js +0 -386
- package/template/wall-e/eval/run-codex-cli-baseline.js +0 -177
- package/template/wall-e/eval/run-coding-agent-real.js +0 -187
- package/template/wall-e/eval/run-eval.js +0 -435
- package/template/wall-e/eval/run-model-comparison.js +0 -142
- package/template/wall-e/eval/session-evaluator.js +0 -187
- package/template/wall-e/eval/session-miner.js +0 -207
- package/template/wall-e/eval/session-retrieval-benchmark.js +0 -150
- package/template/wall-e/eval/session-transcripts.js +0 -509
- package/template/wall-e/eval/shadow.js +0 -161
- package/template/wall-e/eval/swebench-adapter.js +0 -345
- package/template/wall-e/eval/swebench-docker.js +0 -192
- package/template/wall-e/eval/train.py +0 -320
- package/template/wall-e/eval/trainer.js +0 -232
- package/template/wall-e/eval/weekly-eval-loop.js +0 -241
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Fallback policy for a PERIODIC, HEAVY, idempotent db-owner-worker job (a backfill or
|
|
4
|
+
// import sweep) when its worker op fails. The hard rule this enforces: a slow/degraded/
|
|
5
|
+
// timed-out worker must NEVER cause the heavy sweep to run on the main event loop — that
|
|
6
|
+
// turns transient worker saturation into a multi-second UI freeze (a giant transcript
|
|
7
|
+
// ingest holds the SQLite write lock → the backfill op times out → its .catch() ran the
|
|
8
|
+
// whole JSON.parse + row-rewrite on main). These jobs are idempotent and re-run on the
|
|
9
|
+
// next scheduler tick once the worker recovers, so "skip this tick" is correct.
|
|
10
|
+
//
|
|
11
|
+
// The ONE case where the in-process owner queue IS the intended path is a genuinely
|
|
12
|
+
// DISABLED worker (feature off by config, CTM_DB_OWNER_WORKER_DISABLED): then there is no
|
|
13
|
+
// worker to contend with and in-process is how writes are meant to run.
|
|
14
|
+
//
|
|
15
|
+
// Pure + dependency-injected (runInProcess, logDropped) so it is unit-testable in isolation
|
|
16
|
+
// from server.js. Mirrors the _runBestEffortWorkerOpOrDrop policy but returns a skip result
|
|
17
|
+
// the scheduler tolerates instead of a fire-and-forget drop.
|
|
18
|
+
|
|
19
|
+
const WORKER_DISABLED_CODE = 'CTM_DB_OWNER_WORKER_DISABLED';
|
|
20
|
+
|
|
21
|
+
function makeSkipHeavyWorkerJobOnDegraded({ runInProcess, logDropped } = {}) {
|
|
22
|
+
if (typeof runInProcess !== 'function') {
|
|
23
|
+
throw new Error('makeSkipHeavyWorkerJobOnDegraded requires a runInProcess function');
|
|
24
|
+
}
|
|
25
|
+
// Returns a factory: (label, skipResult, inProcessFn) → a .catch() handler.
|
|
26
|
+
// skipResult may be a value or a thunk (evaluated lazily on the skip path).
|
|
27
|
+
return function skipHeavyWorkerJobOnDegraded(label, skipResult, inProcessFn) {
|
|
28
|
+
return (workerError) => {
|
|
29
|
+
if (workerError && workerError.code === WORKER_DISABLED_CODE) {
|
|
30
|
+
return runInProcess(label, inProcessFn);
|
|
31
|
+
}
|
|
32
|
+
if (typeof logDropped === 'function') logDropped(label, workerError);
|
|
33
|
+
return Promise.resolve(typeof skipResult === 'function' ? skipResult() : skipResult);
|
|
34
|
+
};
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { makeSkipHeavyWorkerJobOnDegraded, WORKER_DISABLED_CODE };
|
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Shared JSONL → conversation-message parser.
|
|
4
|
+
//
|
|
5
|
+
// Extracted VERBATIM from server.js so the same parser can run on the main
|
|
6
|
+
// thread AND inside the read-pool worker (workers/read-pool-worker.js) without
|
|
7
|
+
// forking behavior. The Conversation tab and the #review= page both render the
|
|
8
|
+
// output of this parser via public/js/message-renderer.js, so any divergence
|
|
9
|
+
// here would break Conversation/Review render parity — the functions below must
|
|
10
|
+
// stay byte-for-byte identical to what server.js used. server.js now re-exports
|
|
11
|
+
// these (alias) instead of defining its own copies.
|
|
12
|
+
//
|
|
13
|
+
// Dependencies are already standalone modules (no server.js coupling):
|
|
14
|
+
// - parseCodexJsonlIntoMessages / parseCodexJsonlFileIntoMessages /
|
|
15
|
+
// createCodexUserDeduper (lib/session-history) — Codex rollout format
|
|
16
|
+
// - isProviderGeneratedUserContextText (lib/provider-user-context)
|
|
17
|
+
// - sortTimelineMessagesInPlace (lib/timeline-order)
|
|
18
|
+
// - node:fs, node:path
|
|
19
|
+
|
|
20
|
+
const fs = require('node:fs');
|
|
21
|
+
const path = require('node:path');
|
|
22
|
+
const {
|
|
23
|
+
parseCodexJsonlIntoMessages,
|
|
24
|
+
parseCodexJsonlFileIntoMessages,
|
|
25
|
+
createCodexUserDeduper,
|
|
26
|
+
} = require('./session-history');
|
|
27
|
+
const { isZstRolloutFileName, materializeCodexRollout } = require('./codex-zst');
|
|
28
|
+
const { isProviderGeneratedUserContextText } = require('./provider-user-context');
|
|
29
|
+
const { sortTimelineMessagesInPlace } = require('./timeline-order');
|
|
30
|
+
const {
|
|
31
|
+
structuredCaptureEnabled,
|
|
32
|
+
compactBoundaryMessage,
|
|
33
|
+
compactSummaryMessage,
|
|
34
|
+
reasoningMessage,
|
|
35
|
+
toolCallMessage,
|
|
36
|
+
toolResultMessage,
|
|
37
|
+
} = require('./structured-capture');
|
|
38
|
+
|
|
39
|
+
// Usage fields worth persisting on assistant messages (token chips).
|
|
40
|
+
function _usageMetadata(usage) {
|
|
41
|
+
if (!usage || typeof usage !== 'object') return null;
|
|
42
|
+
const out = {};
|
|
43
|
+
for (const key of ['input_tokens', 'output_tokens', 'cache_creation_input_tokens', 'cache_read_input_tokens']) {
|
|
44
|
+
const value = Number(usage[key]);
|
|
45
|
+
if (Number.isFinite(value) && value >= 0) out[key] = value;
|
|
46
|
+
}
|
|
47
|
+
return Object.keys(out).length ? out : null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const _SESSION_FILE_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
51
|
+
|
|
52
|
+
// Session id a Claude JSONL file OWNS, derived from its filename stem
|
|
53
|
+
// (`<uuid>.jsonl` / `<uuid>.jsonl.bak`). '' when the stem isn't a UUID
|
|
54
|
+
// (codex rollouts, walle history files).
|
|
55
|
+
function claudeFileSessionId(filePath) {
|
|
56
|
+
const stem = path.basename(String(filePath || '')).replace(/\.jsonl(\.bak)?$/i, '');
|
|
57
|
+
return _SESSION_FILE_UUID_RE.test(stem) ? stem : '';
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Byte budgets for the multi-file parse. Kept here (not server.js) so the read
|
|
61
|
+
// pool applies the identical thresholds. Values match the originals in server.js.
|
|
62
|
+
const LARGE_SESSION_TAIL_THRESHOLD_BYTES = 50 * 1024 * 1024;
|
|
63
|
+
const LARGE_SESSION_TAIL_READ_BYTES = 10 * 1024 * 1024;
|
|
64
|
+
const LATENCY_SENSITIVE_SYNC_BUDGET_BYTES = 12 * 1024 * 1024;
|
|
65
|
+
const PARSE_MAX_SYNC_SIZE = 10 * 1024 * 1024; // 10MB total budget for non-latency parse
|
|
66
|
+
const PARSE_SMALL_FILE_THRESHOLD = 1024 * 1024; // always read small (subagent) files
|
|
67
|
+
|
|
68
|
+
function _jsonlContentText(content) {
|
|
69
|
+
if (typeof content === 'string') return content;
|
|
70
|
+
if (!Array.isArray(content)) return '';
|
|
71
|
+
const parts = [];
|
|
72
|
+
let imageCount = 0;
|
|
73
|
+
for (const block of content) {
|
|
74
|
+
if (!block || typeof block !== 'object') continue;
|
|
75
|
+
if ((block.type === 'text' || block.type === 'input_text') && block.text) {
|
|
76
|
+
parts.push(block.text);
|
|
77
|
+
} else if (block.type === 'image' || block.type === 'input_image' || block.type === 'image_ref') {
|
|
78
|
+
parts.push(`[Image #${++imageCount}]`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return parts.join('\n');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function _isClaudeSubagentJsonlPath(filePath) {
|
|
85
|
+
return String(filePath || '').replace(/\\/g, '/').includes('/subagents/');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Subagent linkage: `subagents/agent-<id>.jsonl` has a sibling
|
|
89
|
+
// `agent-<id>.meta.json` carrying {agentType, description, toolUseId} —
|
|
90
|
+
// toolUseId equals the spawning Task tool_use's id (the tool_call row's
|
|
91
|
+
// callId), so nesting needs no prompt-matching. Cached per file path.
|
|
92
|
+
const _subagentFileInfoCache = new Map();
|
|
93
|
+
function _readSubagentFileInfo(filePath) {
|
|
94
|
+
const key = String(filePath || '');
|
|
95
|
+
if (_subagentFileInfoCache.has(key)) return _subagentFileInfoCache.get(key);
|
|
96
|
+
const idMatch = path.basename(key).match(/^agent-(.+)\.jsonl$/i);
|
|
97
|
+
const info = { id: idMatch ? idMatch[1] : '', agentType: '', description: '', parentToolUseId: '' };
|
|
98
|
+
if (idMatch) {
|
|
99
|
+
try {
|
|
100
|
+
const meta = JSON.parse(fs.readFileSync(key.replace(/\.jsonl$/i, '.meta.json'), 'utf8'));
|
|
101
|
+
if (meta && typeof meta === 'object') {
|
|
102
|
+
info.agentType = typeof meta.agentType === 'string' ? meta.agentType : '';
|
|
103
|
+
info.description = typeof meta.description === 'string' ? meta.description.slice(0, 200) : '';
|
|
104
|
+
info.parentToolUseId = typeof meta.toolUseId === 'string' ? meta.toolUseId : '';
|
|
105
|
+
}
|
|
106
|
+
} catch { /* meta.json missing — linkage falls back to render-time matching */ }
|
|
107
|
+
}
|
|
108
|
+
if (_subagentFileInfoCache.size > 512) _subagentFileInfoCache.clear();
|
|
109
|
+
_subagentFileInfoCache.set(key, info);
|
|
110
|
+
return info;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function _parseJsonlIntoMessages(content, messages, opts = {}) {
|
|
114
|
+
if (content.includes('"type":"session_meta"') && content.includes('"originator":"codex-tui"')) {
|
|
115
|
+
parseCodexJsonlIntoMessages(content, messages);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const sourceFile = opts && opts.sourceFile ? path.resolve(opts.sourceFile) : '';
|
|
119
|
+
const sourceIsSubagent = _isClaudeSubagentJsonlPath(sourceFile);
|
|
120
|
+
const subagentFileInfo = sourceIsSubagent ? _readSubagentFileInfo(sourceFile) : null;
|
|
121
|
+
const summaries = opts && Array.isArray(opts.summaries) ? opts.summaries : null;
|
|
122
|
+
// Line-level sessionId of the entry currently being parsed. Stamped on each
|
|
123
|
+
// message so parseSessionFiles can drop inherited post-compact prefixes
|
|
124
|
+
// (a new Claude session file starts with the PARENT file's lines, old
|
|
125
|
+
// sessionId at the top, switching mid-file).
|
|
126
|
+
let entrySessionId = '';
|
|
127
|
+
// Per-line sidechain identity (inline Task-subagent turns carry
|
|
128
|
+
// isSidechain/agentId/slug on the entry itself).
|
|
129
|
+
let entrySidechain = null;
|
|
130
|
+
let entrySlug = '';
|
|
131
|
+
// Last contiguous run of messages emitted for one assistant parentUuid
|
|
132
|
+
// (structured emission replaces the whole run when the row re-streams).
|
|
133
|
+
let lastAssistantRun = null;
|
|
134
|
+
const pushMessage = (msg, lineIdx) => {
|
|
135
|
+
if (sourceFile) {
|
|
136
|
+
msg._sourceFile = sourceFile;
|
|
137
|
+
msg._sourceLine = lineIdx;
|
|
138
|
+
}
|
|
139
|
+
if (entrySessionId) msg._entrySessionId = entrySessionId;
|
|
140
|
+
if (sourceIsSubagent || entrySidechain) {
|
|
141
|
+
const metadata = msg.metadata && typeof msg.metadata === 'object' ? msg.metadata : {};
|
|
142
|
+
msg.metadata = { ...metadata, sourceKind: 'subagent' };
|
|
143
|
+
// Nesting contract: which subagent this row belongs to and which Task
|
|
144
|
+
// tool_use spawned it (parentToolUseId == the Task tool_call's callId).
|
|
145
|
+
msg.metadata.subagent = {
|
|
146
|
+
id: (subagentFileInfo && subagentFileInfo.id) || (entrySidechain && entrySidechain.id) || '',
|
|
147
|
+
slug: entrySlug || '',
|
|
148
|
+
agentType: (subagentFileInfo && subagentFileInfo.agentType) || '',
|
|
149
|
+
description: (subagentFileInfo && subagentFileInfo.description) || '',
|
|
150
|
+
parentToolUseId: (subagentFileInfo && subagentFileInfo.parentToolUseId) || '',
|
|
151
|
+
};
|
|
152
|
+
if (msg.role === 'user') {
|
|
153
|
+
msg.metadata.originalRole = msg.metadata.originalRole || 'user';
|
|
154
|
+
msg.metadata.promptKind = msg.metadata.promptKind || 'subagent';
|
|
155
|
+
msg.role = 'system';
|
|
156
|
+
msg.roleLabel = msg.roleLabel || 'Subagent prompt';
|
|
157
|
+
msg.agentLabel = msg.agentLabel || 'Subagent prompt';
|
|
158
|
+
} else if (msg.role === 'assistant') {
|
|
159
|
+
msg.roleLabel = msg.roleLabel || 'Subagent';
|
|
160
|
+
msg.agentLabel = msg.agentLabel || 'Subagent';
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
messages.push(msg);
|
|
164
|
+
return msg;
|
|
165
|
+
};
|
|
166
|
+
const lines = content.split('\n');
|
|
167
|
+
|
|
168
|
+
for (let lineIdx = 0; lineIdx < lines.length; lineIdx++) {
|
|
169
|
+
const line = lines[lineIdx];
|
|
170
|
+
if (!line.trim()) continue;
|
|
171
|
+
try {
|
|
172
|
+
const entry = JSON.parse(line);
|
|
173
|
+
entrySessionId = typeof entry.sessionId === 'string' ? entry.sessionId : '';
|
|
174
|
+
// Inline sidechain rows (Task subagents written into the SAME file by
|
|
175
|
+
// older Claude Code versions). Gated: relabeling user→system changes
|
|
176
|
+
// turn starts, so only new imports get it.
|
|
177
|
+
entrySlug = typeof entry.slug === 'string' ? entry.slug : '';
|
|
178
|
+
entrySidechain = !sourceIsSubagent && entry.isSidechain === true && structuredCaptureEnabled()
|
|
179
|
+
? { id: typeof entry.agentId === 'string' ? entry.agentId : '' }
|
|
180
|
+
: null;
|
|
181
|
+
if (entry.type === 'summary' && typeof entry.summary === 'string') {
|
|
182
|
+
// Async-generated session title ({summary, leafUuid}); not a turn.
|
|
183
|
+
if (summaries && entry.summary.trim()) {
|
|
184
|
+
summaries.push({ summary: entry.summary.trim(), leafUuid: entry.leafUuid || '' });
|
|
185
|
+
}
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
if (entry.type === 'system' && entry.subtype === 'compact_boundary') {
|
|
189
|
+
if (structuredCaptureEnabled()) {
|
|
190
|
+
const compactMeta = entry.compactMetadata || {};
|
|
191
|
+
pushMessage(compactBoundaryMessage({
|
|
192
|
+
provider: 'claude',
|
|
193
|
+
trigger: compactMeta.trigger,
|
|
194
|
+
preTokens: Number(compactMeta.preTokens),
|
|
195
|
+
logicalParentUuid: entry.logicalParentUuid,
|
|
196
|
+
timestamp: entry.timestamp,
|
|
197
|
+
}), lineIdx);
|
|
198
|
+
}
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (entry.type === 'user' && entry.message?.role === 'user') {
|
|
202
|
+
const c = entry.message.content;
|
|
203
|
+
let text = _jsonlContentText(c);
|
|
204
|
+
// Detect system/tool messages masquerading as user messages
|
|
205
|
+
const isToolResult = Array.isArray(c) && c.some(b => b.type === 'tool_result');
|
|
206
|
+
// _jsonlContentText only reads text/image blocks, so a pure tool_result
|
|
207
|
+
// entry has empty text — structured capture still wants its blocks.
|
|
208
|
+
if (!text && !(isToolResult && structuredCaptureEnabled())) continue;
|
|
209
|
+
// Post-compact synthetic context summary: render as a collapsed
|
|
210
|
+
// structured row under the compact divider, never as a user prompt.
|
|
211
|
+
if (structuredCaptureEnabled() && (entry.isCompactSummary || entry.isVisibleInTranscriptOnly)) {
|
|
212
|
+
pushMessage(compactSummaryMessage({ provider: 'claude', text, timestamp: entry.timestamp }), lineIdx);
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
const isTaskNotification = text.includes('<task-notification>') || (text.includes('toolu_') && text.includes('.output'));
|
|
216
|
+
const isSystemReminder = text.includes('<system-reminder>') && !text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '').trim();
|
|
217
|
+
if (!isToolResult && !isTaskNotification && isProviderGeneratedUserContextText(text)) {
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (isToolResult && structuredCaptureEnabled()) {
|
|
221
|
+
// One structured tool_result message per block, paired to its
|
|
222
|
+
// tool_use at render time via callId. The top-level toolUseResult
|
|
223
|
+
// carries the rich result (structuredPatch for Edit, filePath,
|
|
224
|
+
// durationMs) — attach it when the entry has a single result block.
|
|
225
|
+
const resultBlocks = c.filter(b => b && b.type === 'tool_result');
|
|
226
|
+
const tur = resultBlocks.length === 1 && entry.toolUseResult && typeof entry.toolUseResult === 'object'
|
|
227
|
+
? entry.toolUseResult : null;
|
|
228
|
+
for (const block of resultBlocks) {
|
|
229
|
+
const blockText = typeof block.content === 'string' ? block.content : _jsonlContentText(block.content);
|
|
230
|
+
pushMessage(toolResultMessage({
|
|
231
|
+
provider: 'claude',
|
|
232
|
+
callId: block.tool_use_id,
|
|
233
|
+
output: blockText || (tur && typeof tur.stdout === 'string' ? tur.stdout : ''),
|
|
234
|
+
isError: block.is_error === true,
|
|
235
|
+
filePath: tur ? (tur.filePath || tur.file_path) : undefined,
|
|
236
|
+
durationMs: tur ? Number(tur.durationMs) : undefined,
|
|
237
|
+
structuredPatch: tur ? tur.structuredPatch : undefined,
|
|
238
|
+
timestamp: entry.timestamp,
|
|
239
|
+
}), lineIdx);
|
|
240
|
+
}
|
|
241
|
+
} else if (isToolResult || isTaskNotification || isSystemReminder) {
|
|
242
|
+
pushMessage({ role: 'system', text, timestamp: entry.timestamp }, lineIdx);
|
|
243
|
+
} else {
|
|
244
|
+
// Strip system-reminder tags from real user messages
|
|
245
|
+
const cleaned = text.replace(/<system-reminder>[\s\S]*?<\/system-reminder>/g, '').trim();
|
|
246
|
+
pushMessage({ role: 'user', text: cleaned || text, timestamp: entry.timestamp }, lineIdx);
|
|
247
|
+
}
|
|
248
|
+
} else if (entry.type === 'user' && entry.provider === 'walle' && typeof entry.content === 'string') {
|
|
249
|
+
pushMessage({ role: 'user', text: entry.content, timestamp: entry.timestamp }, lineIdx);
|
|
250
|
+
} else if (entry.type === 'assistant' && entry.message?.role === 'assistant') {
|
|
251
|
+
const c = entry.message.content;
|
|
252
|
+
if (!Array.isArray(c)) continue;
|
|
253
|
+
if (structuredCaptureEnabled()) {
|
|
254
|
+
// Structured emission: thinking → reasoning rows, tool_use → tool_call
|
|
255
|
+
// rows (text stays exactly `[Tool: name]`, no longer inlined into the
|
|
256
|
+
// assistant text), text blocks → ONE assistant message carrying
|
|
257
|
+
// parentUuid + usage/model metadata.
|
|
258
|
+
const run = [];
|
|
259
|
+
const textParts = [];
|
|
260
|
+
let textInsertIndex = -1; // keep block order: thinking → text → tool_use
|
|
261
|
+
for (const block of c) {
|
|
262
|
+
if (!block || typeof block !== 'object') continue;
|
|
263
|
+
if (block.type === 'text' && block.text) {
|
|
264
|
+
if (textInsertIndex === -1) textInsertIndex = run.length;
|
|
265
|
+
textParts.push(block.text);
|
|
266
|
+
} else if (block.type === 'thinking' && (block.thinking || block.text)) {
|
|
267
|
+
run.push(reasoningMessage({ provider: 'claude', text: block.thinking || block.text, timestamp: entry.timestamp }));
|
|
268
|
+
} else if (block.type === 'tool_use') {
|
|
269
|
+
const callMsg = toolCallMessage({
|
|
270
|
+
provider: 'claude', tool: block.name, callId: block.id, args: block.input, timestamp: entry.timestamp,
|
|
271
|
+
});
|
|
272
|
+
// Task spawns: surface the subagent identity on the tool_call
|
|
273
|
+
// row so subagent rows (metadata.subagent.parentToolUseId ==
|
|
274
|
+
// this callId) can nest under it at render time.
|
|
275
|
+
if (block.name === 'Task' && block.input && typeof block.input === 'object') {
|
|
276
|
+
if (typeof block.input.subagent_type === 'string') callMsg.metadata.taskAgentType = block.input.subagent_type;
|
|
277
|
+
if (typeof block.input.description === 'string') callMsg.metadata.taskDescription = block.input.description.slice(0, 200);
|
|
278
|
+
}
|
|
279
|
+
run.push(callMsg);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
if (textParts.length) {
|
|
283
|
+
// parentUuid rides ONLY on the assistant text message — it is the
|
|
284
|
+
// stream-tail merge's dedup identity; putting it on tool/reasoning
|
|
285
|
+
// rows would collapse them into this message.
|
|
286
|
+
const msg = { role: 'assistant', text: textParts.join('\n'), timestamp: entry.timestamp, parentUuid: entry.parentUuid };
|
|
287
|
+
const usage = _usageMetadata(entry.message.usage);
|
|
288
|
+
const model = typeof entry.message.model === 'string' ? entry.message.model : '';
|
|
289
|
+
if (usage || model) {
|
|
290
|
+
msg.metadata = {};
|
|
291
|
+
if (usage) msg.metadata.usage = usage;
|
|
292
|
+
if (model) msg.metadata.model = model;
|
|
293
|
+
}
|
|
294
|
+
run.splice(textInsertIndex === -1 ? run.length : textInsertIndex, 0, msg);
|
|
295
|
+
}
|
|
296
|
+
if (run.length) {
|
|
297
|
+
// Streaming increments re-emit the SAME provider row (same
|
|
298
|
+
// parentUuid) with more content. Replace the previous contiguous
|
|
299
|
+
// run for that parentUuid — but only when nothing else landed in
|
|
300
|
+
// between, so interleaved entries are never spliced away.
|
|
301
|
+
if (entry.parentUuid && lastAssistantRun
|
|
302
|
+
&& lastAssistantRun.parentUuid === entry.parentUuid
|
|
303
|
+
&& lastAssistantRun.end === messages.length) {
|
|
304
|
+
messages.splice(lastAssistantRun.start);
|
|
305
|
+
}
|
|
306
|
+
const start = messages.length;
|
|
307
|
+
for (const m of run) pushMessage(m, lineIdx);
|
|
308
|
+
lastAssistantRun = { parentUuid: entry.parentUuid, start, end: messages.length };
|
|
309
|
+
}
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
const parts = [];
|
|
313
|
+
for (const block of c) {
|
|
314
|
+
if (block.type === 'text' && block.text) {
|
|
315
|
+
parts.push(block.text);
|
|
316
|
+
} else if (block.type === 'tool_use') {
|
|
317
|
+
parts.push(`[Tool: ${block.name}]`);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
if (parts.length > 0) {
|
|
321
|
+
// Deduplicate: assistant messages arrive incrementally, keep only the last one with same parentUuid.
|
|
322
|
+
// We surface parentUuid on the stored message (not the internal _parent) so the stream-tail merge
|
|
323
|
+
// (lib/conversation-tail-merge.js) and the client renderer can dedup a still-streaming partial in the
|
|
324
|
+
// live ring against the finalized copy in the conversation cache — the strong, timestamp-independent key.
|
|
325
|
+
const lastMsg = messages[messages.length - 1];
|
|
326
|
+
if (lastMsg && lastMsg.role === 'assistant' && lastMsg.parentUuid === entry.parentUuid) {
|
|
327
|
+
lastMsg.text = parts.join('\n');
|
|
328
|
+
if (sourceFile) {
|
|
329
|
+
lastMsg._sourceFile = sourceFile;
|
|
330
|
+
lastMsg._sourceLine = lineIdx;
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
pushMessage({ role: 'assistant', text: parts.join('\n'), timestamp: entry.timestamp, parentUuid: entry.parentUuid }, lineIdx);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
} else if (entry.type === 'assistant' && entry.provider === 'walle' && typeof entry.content === 'string') {
|
|
337
|
+
if (entry.content) pushMessage({ role: 'assistant', text: entry.content, timestamp: entry.timestamp }, lineIdx);
|
|
338
|
+
} else if (entry.type === 'walle_part') {
|
|
339
|
+
const data = entry.data || {};
|
|
340
|
+
let text = '';
|
|
341
|
+
if (entry.partType === 'tool_call') {
|
|
342
|
+
text = `[Tool: ${data.name || 'tool'}]`;
|
|
343
|
+
} else if (entry.partType === 'tool_result') {
|
|
344
|
+
const result = typeof data.result === 'string' ? data.result : '';
|
|
345
|
+
text = `[Tool result: ${data.name || 'tool'}]${result ? '\n' + result : ''}`;
|
|
346
|
+
} else if (entry.partType === 'error') {
|
|
347
|
+
text = `[Wall-E error] ${data.message || ''}`.trim();
|
|
348
|
+
} else if (entry.partType === 'cancelled') {
|
|
349
|
+
text = '[Wall-E cancelled]';
|
|
350
|
+
}
|
|
351
|
+
if (text) pushMessage({ role: 'system', text, timestamp: entry.timestamp }, lineIdx);
|
|
352
|
+
}
|
|
353
|
+
} catch { /* skip malformed line */ }
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Read only the last `maxBytes` of a large file and parse the (line-aligned)
|
|
358
|
+
// tail. Moved VERBATIM from server.js so the pool tails identically.
|
|
359
|
+
function _parseJsonlTailIntoMessages(filePath, size, messages, opts = {}) {
|
|
360
|
+
const readSize = Math.min(
|
|
361
|
+
Math.max(1, Number(opts.maxBytes) || LARGE_SESSION_TAIL_READ_BYTES),
|
|
362
|
+
Math.max(0, Number(size) || 0)
|
|
363
|
+
);
|
|
364
|
+
if (!readSize) return { bytesRead: 0, partial: true };
|
|
365
|
+
const fd = fs.openSync(filePath, 'r');
|
|
366
|
+
try {
|
|
367
|
+
const buf = Buffer.alloc(readSize);
|
|
368
|
+
fs.readSync(fd, buf, 0, readSize, Math.max(0, size - readSize));
|
|
369
|
+
let content = buf.toString('utf8');
|
|
370
|
+
const nlIdx = content.indexOf('\n');
|
|
371
|
+
if (nlIdx > 0) content = content.slice(nlIdx + 1);
|
|
372
|
+
if (opts.isCodexRollout) {
|
|
373
|
+
const parsed = parseCodexJsonlIntoMessages(content, messages, { codexUserDeduper: opts.codexUserDeduper || opts.seenUsers });
|
|
374
|
+
return { ...parsed, bytesRead: readSize, partial: true };
|
|
375
|
+
}
|
|
376
|
+
_parseJsonlIntoMessages(content, messages, { sourceFile: filePath, summaries: opts.summaries });
|
|
377
|
+
return { bytesRead: readSize, partial: true };
|
|
378
|
+
} finally {
|
|
379
|
+
fs.closeSync(fd);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Read + parse a session's JSONL file set into a combined, chronologically
|
|
385
|
+
* sorted message list. This is the body of apiSessionMessages' cache-miss path,
|
|
386
|
+
* extracted VERBATIM so it can run EITHER on the main thread or inside the read
|
|
387
|
+
* pool with byte-identical behavior (parity by construction — same code, not a
|
|
388
|
+
* fork). DB-free: it returns `codexParsedFiles` so the caller can write the
|
|
389
|
+
* Codex conversation cache on the main thread.
|
|
390
|
+
*
|
|
391
|
+
* @param {string[]} filePaths
|
|
392
|
+
* @param {Object} opts
|
|
393
|
+
* @param {boolean} [opts.latencySensitiveLargeParse] — tail-read large files under a budget
|
|
394
|
+
* @returns {{ messages, partialLoad, bytesRead, totalFiles, codexParsedFiles }}
|
|
395
|
+
*/
|
|
396
|
+
function parseSessionFiles(filePaths, opts = {}) {
|
|
397
|
+
const latencySensitiveLargeParse = !!opts.latencySensitiveLargeParse;
|
|
398
|
+
const messages = [];
|
|
399
|
+
const summaries = [];
|
|
400
|
+
let partialLoad = false;
|
|
401
|
+
|
|
402
|
+
// Sort files by creation time (earliest first) so messages are chronological.
|
|
403
|
+
// `.jsonl.zst` archives (cold Codex rollouts) are materialized to the disk
|
|
404
|
+
// cache first so size budgets and reads apply to the decompressed bytes;
|
|
405
|
+
// a failed materialization (no zstd / too large) marks a partial load.
|
|
406
|
+
let zstUnavailable = false;
|
|
407
|
+
const filesWithTime = (Array.isArray(filePaths) ? filePaths : []).map(fp => {
|
|
408
|
+
let readPath = fp;
|
|
409
|
+
if (isZstRolloutFileName(fp)) {
|
|
410
|
+
const mat = materializeCodexRollout(fp);
|
|
411
|
+
if (!mat.ok) { zstUnavailable = true; return null; }
|
|
412
|
+
readPath = mat.path;
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
const st = fs.statSync(readPath);
|
|
416
|
+
return { fp: readPath, originalFp: fp, birthtimeMs: st.birthtimeMs, size: st.size };
|
|
417
|
+
} catch { return null; }
|
|
418
|
+
}).filter(Boolean).sort((a, b) => a.birthtimeMs - b.birthtimeMs);
|
|
419
|
+
if (zstUnavailable) partialLoad = true;
|
|
420
|
+
const filesToParse = latencySensitiveLargeParse
|
|
421
|
+
? [...filesWithTime].sort((a, b) => b.birthtimeMs - a.birthtimeMs)
|
|
422
|
+
: filesWithTime;
|
|
423
|
+
|
|
424
|
+
let bytesRead = 0;
|
|
425
|
+
let latencySensitiveBytesRead = 0;
|
|
426
|
+
const codexUserDeduper = createCodexUserDeduper();
|
|
427
|
+
const codexParsedFiles = [];
|
|
428
|
+
for (const { fp, size, originalFp } of filesToParse) {
|
|
429
|
+
// A materialized .zst lives in the cache dir — detect codex-ness from the
|
|
430
|
+
// ORIGINAL path.
|
|
431
|
+
const isCodexRollout = (originalFp || fp).includes(`${path.sep}.codex${path.sep}sessions${path.sep}`)
|
|
432
|
+
|| (originalFp ? isZstRolloutFileName(originalFp) : false);
|
|
433
|
+
if (!latencySensitiveLargeParse && !isCodexRollout && bytesRead > 0 && bytesRead + size > PARSE_MAX_SYNC_SIZE && size > PARSE_SMALL_FILE_THRESHOLD) {
|
|
434
|
+
// Budget exceeded — skip large files, but keep going for small subagent files
|
|
435
|
+
partialLoad = true;
|
|
436
|
+
continue; // continue instead of break — small files after this will still be read
|
|
437
|
+
}
|
|
438
|
+
try {
|
|
439
|
+
let fileBytesRead = 0;
|
|
440
|
+
const shouldTailForLatency = latencySensitiveLargeParse && size > PARSE_SMALL_FILE_THRESHOLD;
|
|
441
|
+
if (shouldTailForLatency) {
|
|
442
|
+
const remainingBudget = LATENCY_SENSITIVE_SYNC_BUDGET_BYTES - latencySensitiveBytesRead;
|
|
443
|
+
if (remainingBudget <= 0) {
|
|
444
|
+
partialLoad = true;
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
447
|
+
const parsed = _parseJsonlTailIntoMessages(fp, size, messages, {
|
|
448
|
+
isCodexRollout,
|
|
449
|
+
codexUserDeduper,
|
|
450
|
+
summaries,
|
|
451
|
+
maxBytes: Math.min(LARGE_SESSION_TAIL_READ_BYTES, remainingBudget),
|
|
452
|
+
});
|
|
453
|
+
fileBytesRead = parsed.bytesRead || 0;
|
|
454
|
+
latencySensitiveBytesRead += fileBytesRead;
|
|
455
|
+
partialLoad = true;
|
|
456
|
+
} else if (isCodexRollout) {
|
|
457
|
+
// Codex rollouts are large because they include tool/event payloads.
|
|
458
|
+
// The semantic user/assistant transcript is much smaller, so stream
|
|
459
|
+
// the whole JSONL file and keep old eval output visible after restart.
|
|
460
|
+
const before = messages.length;
|
|
461
|
+
const parsed = parseCodexJsonlFileIntoMessages(fp, messages, { codexUserDeduper });
|
|
462
|
+
fileBytesRead = parsed.bytesRead || size;
|
|
463
|
+
codexParsedFiles.push({
|
|
464
|
+
fp,
|
|
465
|
+
size,
|
|
466
|
+
parsed,
|
|
467
|
+
messages: messages.slice(before),
|
|
468
|
+
});
|
|
469
|
+
} else if (size > LARGE_SESSION_TAIL_THRESHOLD_BYTES) {
|
|
470
|
+
// For very large individual files (>50MB), read only the last 10MB
|
|
471
|
+
// to show the most recent conversation without blocking the event loop
|
|
472
|
+
const parsed = _parseJsonlTailIntoMessages(fp, size, messages, { summaries });
|
|
473
|
+
fileBytesRead = parsed.bytesRead || 0;
|
|
474
|
+
partialLoad = true; // Signal that we didn't load the full file
|
|
475
|
+
} else {
|
|
476
|
+
const content = fs.readFileSync(fp, 'utf8');
|
|
477
|
+
fileBytesRead = size;
|
|
478
|
+
_parseJsonlIntoMessages(content, messages, { sourceFile: fp, summaries });
|
|
479
|
+
}
|
|
480
|
+
bytesRead += fileBytesRead;
|
|
481
|
+
} catch (e) { console.error(`[session-messages] error reading ${path.basename(fp).slice(0, 8)}:`, e.message); }
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Cross-file stitching: after /compact, Claude Code starts a NEW session
|
|
485
|
+
// file that INHERITS the parent file's lines (old sessionId at the top,
|
|
486
|
+
// switching mid-file). When the owning file is also in this set — or still
|
|
487
|
+
// on disk next to the child — keep only the owner's copy so merged project
|
|
488
|
+
// dirs don't render the inherited prefix twice. Orphan prefixes (owner file
|
|
489
|
+
// deleted) are kept: they are the only surviving copy.
|
|
490
|
+
const ownedIds = new Set();
|
|
491
|
+
for (const { fp } of filesWithTime) {
|
|
492
|
+
const owned = claudeFileSessionId(fp);
|
|
493
|
+
if (owned) ownedIds.add(owned);
|
|
494
|
+
}
|
|
495
|
+
const ownerOnDiskCache = new Map();
|
|
496
|
+
const ownerExistsOnDisk = (sourceFile, entryId) => {
|
|
497
|
+
const dir = path.dirname(sourceFile);
|
|
498
|
+
const key = `${dir}|${entryId}`;
|
|
499
|
+
if (!ownerOnDiskCache.has(key)) {
|
|
500
|
+
let exists = false;
|
|
501
|
+
try { exists = fs.existsSync(path.join(dir, `${entryId}.jsonl`)); } catch {}
|
|
502
|
+
ownerOnDiskCache.set(key, exists);
|
|
503
|
+
}
|
|
504
|
+
return ownerOnDiskCache.get(key);
|
|
505
|
+
};
|
|
506
|
+
const stitched = messages.filter(m => {
|
|
507
|
+
const entryId = m._entrySessionId;
|
|
508
|
+
if (!entryId || !m._sourceFile) return true;
|
|
509
|
+
const fileId = claudeFileSessionId(m._sourceFile);
|
|
510
|
+
if (!fileId || entryId === fileId) return true;
|
|
511
|
+
if (!_SESSION_FILE_UUID_RE.test(entryId)) return true;
|
|
512
|
+
return !(ownedIds.has(entryId) || ownerExistsOnDisk(m._sourceFile, entryId));
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
// Clean internal fields. NOTE: parentUuid is intentionally retained — it is the
|
|
516
|
+
// stream-tail merge's strongest dedup key (collapses a live-ring streaming partial
|
|
517
|
+
// against the cached finalized turn), and the client renderer reads it for data-parent-uuid.
|
|
518
|
+
stitched.forEach(m => {
|
|
519
|
+
delete m._sourceFile;
|
|
520
|
+
delete m._sourceLine;
|
|
521
|
+
delete m._entrySessionId;
|
|
522
|
+
});
|
|
523
|
+
|
|
524
|
+
// Sort all messages by timestamp across files
|
|
525
|
+
sortTimelineMessagesInPlace(stitched);
|
|
526
|
+
|
|
527
|
+
return { messages: stitched, partialLoad, bytesRead, totalFiles: filesWithTime.length, codexParsedFiles, summaries };
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
module.exports = {
|
|
531
|
+
_jsonlContentText,
|
|
532
|
+
_isClaudeSubagentJsonlPath,
|
|
533
|
+
_parseJsonlIntoMessages,
|
|
534
|
+
_parseJsonlTailIntoMessages,
|
|
535
|
+
parseSessionFiles,
|
|
536
|
+
claudeFileSessionId,
|
|
537
|
+
_usageMetadata,
|
|
538
|
+
// thresholds exported so server.js can keep its own MAX_SYNC_SIZE checks aligned
|
|
539
|
+
LARGE_SESSION_TAIL_THRESHOLD_BYTES,
|
|
540
|
+
LARGE_SESSION_TAIL_READ_BYTES,
|
|
541
|
+
LATENCY_SENSITIVE_SYNC_BUDGET_BYTES,
|
|
542
|
+
};
|