commandmate 0.33.3 → 0.34.0
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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +146 -146
- package/.next/app-path-routes-manifest.json +29 -29
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +9 -9
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agents/versions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/app/update-check/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js +1 -1
- package/.next/server/app/api/assistant/conversation/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/conversation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js +1 -1
- package/.next/server/app/api/assistant/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js +1 -1
- package/.next/server/app/api/assistant/messages/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route.js +1 -1
- package/.next/server/app/api/assistant/session/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route.js +2 -2
- package/.next/server/app/api/assistant/start/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route.js +3 -3
- package/.next/server/app/api/assistant/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js +1 -1
- package/.next/server/app/api/assistant/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/assistant/tools/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/login/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/logout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/auth/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/capabilities/route.js +1 -1
- package/.next/server/app/api/capabilities/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/daily-summary/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/external-apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/browse/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/recent-paths/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js +1 -1
- package/.next/server/app/api/hooks/agent-event/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/agent-event/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/claude-done/route.js +2 -2
- package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/claude-done/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js +1 -1
- package/.next/server/app/api/hooks/permission-request/route.js.nft.json +1 -1
- package/.next/server/app/api/hooks/permission-request/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/metrics/vibe/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/ollama/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/escalation/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/subscriptions/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/push/vapid/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/[relayId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/relays/route.js +1 -1
- package/.next/server/app/api/relays/route.js.nft.json +1 -1
- package/.next/server/app/api/relays/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/pair/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/[jobId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js +1 -1
- package/.next/server/app/api/repositories/clone/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/clone/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/excluded/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js +1 -1
- package/.next/server/app/api/repositories/restore/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/restore/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route.js +1 -1
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js +1 -1
- package/.next/server/app/api/repositories/scan/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js +1 -1
- package/.next/server/app/api/repositories/sync/route.js.nft.json +1 -1
- package/.next/server/app/api/repositories/sync/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/validate-path/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js +1 -1
- package/.next/server/app/api/settings/default-agents/route.js.nft.json +1 -1
- package/.next/server/app/api/settings/default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/settings/default-surface-mode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sidebar/group-order/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/installations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/operations/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/reindex/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/tasks/[taskId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/templates/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/verification/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/capture/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/clear-composer/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-reference/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/cmate/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/env/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/files/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/create/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branch/delete/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/branches/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/checkout/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/commit/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/fetch/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/log/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/pull/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/reset/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/revert/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/show/[commitHash]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/staged/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/[index]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/apply/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/pop/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/push/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/stash/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/status/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/unstage/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/git/working-diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/notes/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/instances/opencode/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/[filename]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/logs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/marp-render/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/[memoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/memos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/diff/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/opencode/share/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/resolve-target/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/active/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/search/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/git-workflow/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/uninstall-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/[skillId]/update-plan/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/special-keys/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tasks/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/tasks/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/terminal/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/terminal/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/history/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/timers/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/[todoId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/todos/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/tree/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/upload/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/cancel/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/[runId]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/verify/runs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/[id]/viewed/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/apply-default-agents/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route_client-reference-manifest.js +1 -1
- package/.next/server/app/chat/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/manifest.webmanifest/route_client-reference-manifest.js +1 -1
- package/.next/server/app/more/page_client-reference-manifest.js +1 -1
- package/.next/server/app/offline/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/repositories/page_client-reference-manifest.js +1 -1
- package/.next/server/app/review/page_client-reference-manifest.js +1 -1
- package/.next/server/app/sessions/page.js +1 -1
- package/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/[skillId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/installed/page_client-reference-manifest.js +1 -1
- package/.next/server/app/skills/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +8 -8
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +29 -29
- package/.next/server/chunks/1049.js +1 -0
- package/.next/server/chunks/1501.js +4 -4
- package/.next/server/chunks/1886.js +2 -2
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2404.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/3241.js +1 -1
- package/.next/server/chunks/3323.js +1 -1
- package/.next/server/chunks/3480.js +2 -2
- package/.next/server/chunks/4443.js +1 -1
- package/.next/server/chunks/4802.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/5571.js +1 -1
- package/.next/server/chunks/595.js +1 -1
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6285.js +1 -1
- package/.next/server/chunks/6286.js +7 -7
- package/.next/server/chunks/6337.js +1 -1
- package/.next/server/chunks/650.js +1 -1
- package/.next/server/chunks/663.js +1 -1
- package/.next/server/chunks/6677.js +25 -0
- package/.next/server/chunks/7075.js +1 -0
- package/.next/server/chunks/7905.js +1 -1
- package/.next/server/chunks/8583.js +1 -0
- package/.next/server/chunks/8918.js +4 -2
- package/.next/server/chunks/9080.js +1 -0
- package/.next/server/chunks/964.js +1 -0
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/2053-9ac860ab4e92ac89.js +1 -0
- package/.next/static/chunks/2652-18b8fea64413467e.js +1 -0
- package/.next/static/chunks/{4718-814f4ecfd7fcc513.js → 4718-2d9f0397a8f931c4.js} +1 -1
- package/.next/static/chunks/{8361-9bb887203c456edb.js → 8361-0142beb59be2abf3.js} +1 -1
- package/.next/static/chunks/app/{page-022090bf6f932818.js → page-26a42881c8648cd3.js} +1 -1
- package/.next/static/chunks/app/sessions/page-a3ab6f122ec60d5a.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-a39ec2a0732d11a6.js +1 -0
- package/.next/static/css/b09bede85744e1cf.css +1 -0
- package/.next/trace +13 -13
- package/dist/cli/commands/ask.d.ts.map +1 -1
- package/dist/cli/commands/ask.js +11 -2
- package/dist/cli/commands/respond.d.ts.map +1 -1
- package/dist/cli/commands/respond.js +8 -4
- package/dist/cli/commands/send.d.ts.map +1 -1
- package/dist/cli/commands/send.js +18 -0
- package/dist/cli/commands/wait.d.ts +18 -3
- package/dist/cli/commands/wait.d.ts.map +1 -1
- package/dist/cli/commands/wait.js +137 -2
- package/dist/cli/docs/agent-operations.d.ts +2 -2
- package/dist/cli/docs/agent-operations.d.ts.map +1 -1
- package/dist/cli/docs/agent-operations.js +41 -1
- package/dist/cli/utils/session-target.d.ts +23 -6
- package/dist/cli/utils/session-target.d.ts.map +1 -1
- package/dist/cli/utils/session-target.js +33 -5
- package/dist/server/locales/en/worktree.json +21 -2
- package/dist/server/locales/ja/worktree.json +21 -2
- package/dist/server/src/config/schedule-config.js +45 -3
- package/dist/server/src/lib/cli-tools/antigravity.js +95 -10
- package/dist/server/src/lib/cli-tools/submit-verified-sender.js +377 -16
- package/dist/server/src/lib/cmate-parser.js +8 -1
- package/dist/server/src/lib/detection/prompt-detect-multiple-choice.js +140 -19
- package/dist/server/src/lib/detection/tools/claude/detect.js +13 -2
- package/dist/server/src/lib/detection/tools/claude/picker-chrome.js +239 -0
- package/dist/server/src/lib/detection/tools/claude/prompt.js +74 -6
- package/dist/server/src/lib/hooks/sources/claude/history.js +391 -34
- package/dist/server/src/lib/hooks/sources/claude/transcript.js +91 -0
- package/dist/server/src/lib/polling/auto-yes-dialog-gate.js +114 -11
- package/dist/server/src/lib/polling/response-checker.js +82 -3
- package/dist/server/src/lib/schedule-manager.js +178 -19
- package/dist/server/src/lib/session/ask-user-question-prompt.js +4 -2
- package/dist/server/src/lib/session/claude-executor.js +33 -2
- package/dist/server/src/lib/session/resolve-session-target.js +37 -9
- package/dist/server/src/lib/verification/env-clean-gate.js +81 -4
- package/dist/server/src/types/agent-transcript.js +143 -1
- package/package.json +1 -1
- package/.next/server/chunks/1068.js +0 -1
- package/.next/server/chunks/6808.js +0 -1
- package/.next/server/chunks/7875.js +0 -25
- package/.next/server/chunks/9553.js +0 -1
- package/.next/static/chunks/2053-3f299a7a2c7d6767.js +0 -1
- package/.next/static/chunks/8772-9be53a530950d804.js +0 -1
- package/.next/static/chunks/app/sessions/page-584adf11ae51df10.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-3af707e74a75172b.js +0 -1
- package/.next/static/css/acd46e25427a2080.css +0 -1
- /package/.next/static/{9x6U1h52hH_r6pOsGwI_p → gniPBCKoXtbIOQey8kNcx}/_buildManifest.js +0 -0
- /package/.next/static/{9x6U1h52hH_r6pOsGwI_p → gniPBCKoXtbIOQey8kNcx}/_ssgManifest.js +0 -0
|
@@ -90,7 +90,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
90
90
|
};
|
|
91
91
|
})();
|
|
92
92
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
93
|
-
exports.CLAUDE_TURN_RECHECK_LIMIT = exports.CLAUDE_TRANSCRIPT_TAIL_BYTES = void 0;
|
|
93
|
+
exports.CLAUDE_TURN_RECHECK_LIMIT = exports.CLAUDE_TURN_HEAD_MISSING_MARKER = exports.CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES = exports.CLAUDE_TRANSCRIPT_TAIL_BYTES = void 0;
|
|
94
94
|
exports.resetClaudeTranscriptSessions = resetClaudeTranscriptSessions;
|
|
95
95
|
exports.resolveClaudeSessionId = resolveClaudeSessionId;
|
|
96
96
|
exports.claudeProjectsRoot = claudeProjectsRoot;
|
|
@@ -110,21 +110,62 @@ const agent_transcript_1 = require("../../../../types/agent-transcript");
|
|
|
110
110
|
const transcript_1 = require("./transcript");
|
|
111
111
|
const logger = (0, logger_1.createLogger)('lib/hooks/sources/claude/history');
|
|
112
112
|
/**
|
|
113
|
-
* How much of the transcript's tail is read.
|
|
113
|
+
* How much of the transcript's tail is read first.
|
|
114
114
|
*
|
|
115
115
|
* The file grows for the life of the session and a long one is tens of
|
|
116
116
|
* megabytes — the largest on this machine on 2026-08-31 was 23 MB — so reading
|
|
117
117
|
* it whole on every finished turn would be the most expensive thing the poller
|
|
118
|
-
* does.
|
|
119
|
-
*
|
|
118
|
+
* does. A turn is only ever written from a prompt record inside the window (see
|
|
119
|
+
* {@link captureClaudeTranscriptTurn}), which is also what stops a turn the
|
|
120
120
|
* window opened halfway through being written from its middle.
|
|
121
121
|
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
122
|
+
* #2121 sized this as "roughly two orders of magnitude above a single turn".
|
|
123
|
+
* Issue #2470 measured that and it does not hold at the top of the
|
|
124
|
+
* distribution. Over the 27 transcripts in this repository's project directory
|
|
125
|
+
* written in the 14 days to 2026-09-11 — 1,663 turns — the bytes one turn
|
|
126
|
+
* occupies in the file, from its prompt record to the next, were:
|
|
127
|
+
*
|
|
128
|
+
* | p50 | p90 | p99 | max | over 4 MiB |
|
|
129
|
+
* |---|---|---|---|---|
|
|
130
|
+
* | 0.04 MiB | 0.24 MiB | 4.71 MiB | 23.72 MiB | 19 turns (1.1%) |
|
|
131
|
+
*
|
|
132
|
+
* Every one of the 19 was a long, human-started orchestrate or UAT turn — the
|
|
133
|
+
* replies an operator most wants to read back — and before #2470 every one of
|
|
134
|
+
* them was reported as orphaned records and written by nobody. 4 MiB is kept as
|
|
135
|
+
* the size of the *first* read, which is the whole read for 99% of turns; a turn
|
|
136
|
+
* that does not fit is read further back instead of dropped. See
|
|
137
|
+
* {@link CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES}.
|
|
126
138
|
*/
|
|
127
139
|
exports.CLAUDE_TRANSCRIPT_TAIL_BYTES = 4 * 1024 * 1024;
|
|
140
|
+
/**
|
|
141
|
+
* How far back a finished turn's prompt record is looked for (Issue #2470).
|
|
142
|
+
*
|
|
143
|
+
* When the first window holds assistant records and no prompt record at all,
|
|
144
|
+
* {@link captureClaudeTranscriptTurn} doubles it — 8, 16, 32, 64 MiB — until a
|
|
145
|
+
* prompt record is inside, and stops there. 64 MiB is almost three times the
|
|
146
|
+
* largest turn measured (23.72 MiB), and it bounds the one cost this adds:
|
|
147
|
+
* reading up to 60 MiB more of the file, once per ask, for a turn that ran that
|
|
148
|
+
* long. Measured on 2026-09-11 with a synthetic transcript of 20 KiB tool
|
|
149
|
+
* results, reading and parsing a 64 MiB tail took 41 ms (4 MiB: 3 ms).
|
|
150
|
+
*
|
|
151
|
+
* A turn longer than this is still not dropped: what can be read of it is saved
|
|
152
|
+
* behind {@link CLAUDE_TURN_HEAD_MISSING_MARKER}. See
|
|
153
|
+
* {@link captureClaudeTranscriptTurn}.
|
|
154
|
+
*/
|
|
155
|
+
exports.CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES = 64 * 1024 * 1024;
|
|
156
|
+
/**
|
|
157
|
+
* The first line of a reply saved without its beginning (Issue #2470).
|
|
158
|
+
*
|
|
159
|
+
* Plain English Markdown, the way {@link CLAUDE_TURN_TRUNCATION_MARKER} marks
|
|
160
|
+
* the other end of a cut reply, rather than a translated string. `content` is
|
|
161
|
+
* fixed when the row is written and read back by every viewer in whatever
|
|
162
|
+
* language they use, and this layer has nobody to ask: the Stop hook that
|
|
163
|
+
* writes the row arrives with no request and no locale behind it. The
|
|
164
|
+
* `--- Output truncated (exceeded 100KB limit) ---` line
|
|
165
|
+
* `lib/session/claude-executor` appends to a scheduled run's output is the same
|
|
166
|
+
* choice for the same reason.
|
|
167
|
+
*/
|
|
168
|
+
exports.CLAUDE_TURN_HEAD_MISSING_MARKER = `_(The beginning of this reply is not shown: the turn runs further back than the ${exports.CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES / (1024 * 1024)} MiB of transcript that is read.)_`;
|
|
128
169
|
/** `.jsonl`; the only extension this reader will open. */
|
|
129
170
|
const CLAUDE_TRANSCRIPT_EXTENSION = '.jsonl';
|
|
130
171
|
/**
|
|
@@ -252,6 +293,36 @@ function acceptClaudeTranscriptHint(homeDir, hint) {
|
|
|
252
293
|
* where the earlier turns really are the scraper's, and there is no evidence in
|
|
253
294
|
* the window that says otherwise.
|
|
254
295
|
*
|
|
296
|
+
* ## A turn longer than the window (Issue #2470)
|
|
297
|
+
*
|
|
298
|
+
* A turn can be bigger than {@link CLAUDE_TRANSCRIPT_TAIL_BYTES} — 1.1% of the
|
|
299
|
+
* turns measured were, and they were the longest replies of all. Its window
|
|
300
|
+
* holds its assistant records and no prompt record, so `buildClaudeTurns` finds
|
|
301
|
+
* no turn, and before #2470 this answered false with nobody left to write the
|
|
302
|
+
* reply: the Stop receiver has no scrape to fall back on, and the poller that
|
|
303
|
+
* has one may have been restarted, or have given up at its 30-minute limit, long
|
|
304
|
+
* before a fifty-minute turn ends. The measured case wrote no row at all.
|
|
305
|
+
*
|
|
306
|
+
* Two steps now, and both only when the window holds orphaned assistant records
|
|
307
|
+
* and **no** prompt record. A window with a prompt record anywhere in it is read
|
|
308
|
+
* exactly as before, byte for byte.
|
|
309
|
+
*
|
|
310
|
+
* 1. **Read further back** ({@link extendClaudeTranscriptRead}). The window
|
|
311
|
+
* doubles, up to {@link CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES}, until a prompt
|
|
312
|
+
* record is in it, and the turn is then written like any other — user row,
|
|
313
|
+
* backfill anchor, #2264's open-turn gate and all.
|
|
314
|
+
* 2. **Save what can be read** ({@link captureHeadlessClaudeTurn}). A turn whose
|
|
315
|
+
* prompt is further back than even that is written as a reply behind
|
|
316
|
+
* {@link CLAUDE_TURN_HEAD_MISSING_MARKER}, with no user row, keyed on the
|
|
317
|
+
* record that closed it. Only when the read stopped at the limit rather than
|
|
318
|
+
* at the start of the file: a whole file with no prompt record in it is not a
|
|
319
|
+
* turn too long to reach but a shape this reader does not understand, and the
|
|
320
|
+
* scraper keeps it, as it did before.
|
|
321
|
+
*
|
|
322
|
+
* Either way the answer is still about the newest turn and means what it always
|
|
323
|
+
* meant: a headless row answers true because History now holds the turn, and an
|
|
324
|
+
* open one answers false with `not_yet_closed`.
|
|
325
|
+
*
|
|
255
326
|
* The return value is the poller's instruction, so the two failure directions
|
|
256
327
|
* are worth stating plainly. **True** means this path has recorded the turn and
|
|
257
328
|
* the scrape must be dropped. **False** means it has not, for any reason at all
|
|
@@ -296,20 +367,29 @@ async function captureClaudeTranscriptTurn(target, capture, report) {
|
|
|
296
367
|
});
|
|
297
368
|
return false;
|
|
298
369
|
}
|
|
299
|
-
const
|
|
300
|
-
if (
|
|
370
|
+
const read = await readClaudeTranscriptTurns(target, path, sessionId ?? '');
|
|
371
|
+
if (read === null)
|
|
301
372
|
return false;
|
|
302
|
-
const parsed =
|
|
303
|
-
const built = (0, transcript_1.buildClaudeTurns)(parsed.records, sessionId ?? '');
|
|
373
|
+
const { parsed, built } = read;
|
|
304
374
|
if (built.turns.length === 0) {
|
|
305
|
-
|
|
375
|
+
const orphaned = built.orphanedAssistantRecords;
|
|
376
|
+
// A warning since Issue #2470: when this line was `info`, the measured
|
|
377
|
+
// incident — a fifty-minute turn whose reply reached no writer at all —
|
|
378
|
+
// was three of them in a row and nothing else.
|
|
379
|
+
logger.warn('claude-transcript-no-turn', {
|
|
306
380
|
worktreeId: target.worktreeId,
|
|
307
381
|
instanceId,
|
|
308
382
|
path,
|
|
309
383
|
records: parsed.records.length,
|
|
310
384
|
malformedLines: parsed.malformedLines,
|
|
311
|
-
orphanedAssistantRecords:
|
|
385
|
+
orphanedAssistantRecords: orphaned,
|
|
386
|
+
// Orphans are the one reason the window can be to blame, so they are
|
|
387
|
+
// when how much of the file the read covered is worth reading.
|
|
388
|
+
...(orphaned > 0 ? { readBytes: read.size - read.startByte, size: read.size } : {}),
|
|
312
389
|
});
|
|
390
|
+
if (orphaned > 0 && read.reachedLimit) {
|
|
391
|
+
return await captureHeadlessClaudeTurn(target, parsed.records, sessionId ?? '', path, report);
|
|
392
|
+
}
|
|
313
393
|
return false;
|
|
314
394
|
}
|
|
315
395
|
if (parsed.malformedLines > 0 || built.orphanedAssistantRecords > 0) {
|
|
@@ -318,11 +398,12 @@ async function captureClaudeTranscriptTurn(target, capture, report) {
|
|
|
318
398
|
// both are the kind of thing that must be visible when it stops being
|
|
319
399
|
// small.
|
|
320
400
|
//
|
|
321
|
-
// `orphanedAssistantRecords`
|
|
322
|
-
// trap
|
|
323
|
-
// CLAUDE_TRANSCRIPT_TAIL_BYTES
|
|
324
|
-
//
|
|
325
|
-
//
|
|
401
|
+
// `orphanedAssistantRecords` used to be the shape the Issue #2196
|
|
402
|
+
// tail-window trap took as well: a turn whose prompt record fell outside
|
|
403
|
+
// CLAUDE_TRANSCRIPT_TAIL_BYTES. Since #2470 that shape never gets here —
|
|
404
|
+
// a window with no prompt record in it is read further back first — so
|
|
405
|
+
// what is counted now is the harmless half: the tail of an earlier turn,
|
|
406
|
+
// in front of the prompt that opened the newest one.
|
|
326
407
|
logger.info('claude-transcript-partial-read', {
|
|
327
408
|
worktreeId: target.worktreeId,
|
|
328
409
|
instanceId,
|
|
@@ -421,10 +502,14 @@ async function captureClaudeTranscriptTurn(target, capture, report) {
|
|
|
421
502
|
* after its user row (`resolveAssistantTimestampMs`), so the rows do not
|
|
422
503
|
* carry an ordering that could be trusted for this.
|
|
423
504
|
* - **The window bounds it.** `buildClaudeTurns` only opens a turn on a prompt
|
|
424
|
-
* record, so a turn whose prompt fell outside
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
505
|
+
* record, so a turn whose prompt fell outside the window is not in `turns` at
|
|
506
|
+
* all and cannot be backfilled from its middle. Its records are counted as
|
|
507
|
+
* `orphanedAssistantRecords` instead, which is what the caller reports. Since
|
|
508
|
+
* Issue #2470 the window can be wider than {@link CLAUDE_TRANSCRIPT_TAIL_BYTES}
|
|
509
|
+
* — widened only because the newest turn's prompt was not in the first one —
|
|
510
|
+
* and the rule is the same over it: an older turn the wider read happens to
|
|
511
|
+
* bring in is a candidate like any other, and one whose prompt is still
|
|
512
|
+
* outside is not.
|
|
428
513
|
*
|
|
429
514
|
* A window with no anchor answers with the newest turn alone. See
|
|
430
515
|
* {@link captureClaudeTranscriptTurn} for why that is #2121's behaviour rather
|
|
@@ -486,9 +571,15 @@ async function resolveClaudeTranscriptPath(target, capture) {
|
|
|
486
571
|
* different things to the client. A prompt-derived key is a promise that a row
|
|
487
572
|
* with the same id is coming; this one is a promise that no such row exists, so
|
|
488
573
|
* the bubble it draws is cleared by the session going idle rather than by a swap.
|
|
574
|
+
*
|
|
575
|
+
* Since Issue #2470 a row for the same turn usually does follow — under the
|
|
576
|
+
* prompt's key once the writer has read back far enough to find it, or under
|
|
577
|
+
* `claudeHeadlessTurnId` when it cannot — but never under this key, which is one
|
|
578
|
+
* per session and must not be written: the session's second long turn would
|
|
579
|
+
* find the first one's row and read "already saved".
|
|
489
580
|
*/
|
|
490
581
|
function headlessClaudeTurnKey(sessionId) {
|
|
491
|
-
return (0, agent_transcript_1.claudeTurnRequestId)(
|
|
582
|
+
return (0, agent_transcript_1.claudeTurnRequestId)(`${agent_transcript_1.CLAUDE_HEADLESS_TURN_ID_PREFIX}${sessionId}`);
|
|
492
583
|
}
|
|
493
584
|
/**
|
|
494
585
|
* Everything the window holds for a turn whose prompt record is outside it.
|
|
@@ -502,6 +593,12 @@ function headlessClaudeTurnKey(sessionId) {
|
|
|
502
593
|
*
|
|
503
594
|
* Only the records before the first prompt record are taken, which is exactly
|
|
504
595
|
* the set `buildClaudeTurns` counted as orphaned.
|
|
596
|
+
*
|
|
597
|
+
* The writer's counterpart is `buildHeadlessClaudeTurn` (Issue #2470), and the
|
|
598
|
+
* two are kept apart on purpose. That one is keyed on the record that closed
|
|
599
|
+
* the turn, reads `closed` off it, and keeps the turn's *end* when the block cap
|
|
600
|
+
* bites; this one keeps the first window's first blocks, as the live bubble
|
|
601
|
+
* always has, and its accumulator still never reaches a writer.
|
|
505
602
|
*/
|
|
506
603
|
function collectHeadlessClaudeTurn(records, sessionId) {
|
|
507
604
|
const blocks = [];
|
|
@@ -559,6 +656,19 @@ function collectHeadlessClaudeTurn(records, sessionId) {
|
|
|
559
656
|
* has just finished is harmless: the settled row carries the same
|
|
560
657
|
* {@link ClaudeTurnProgress.turnKey}, so the client replaces one with the other.
|
|
561
658
|
*
|
|
659
|
+
* **Not widened (Issue #2470).** The writer reads further back than
|
|
660
|
+
* {@link CLAUDE_TRANSCRIPT_TAIL_BYTES} when a window holds no prompt record; this
|
|
661
|
+
* reader deliberately does not, and keeps the one window and the `partial`
|
|
662
|
+
* bubble. The Issue left the choice to a measurement, and the measurement is
|
|
663
|
+
* about *when* the cost is paid rather than how big it is: a 64 MiB tail read
|
|
664
|
+
* and parsed in 41 ms on 2026-09-11 against 3 ms for 4 MiB, which is nothing
|
|
665
|
+
* once per finished turn and is a 10–40 ms stall of the server's event loop
|
|
666
|
+
* **every second** here — this runs on each generating tick with a subscriber
|
|
667
|
+
* (`CHAT_TURN_PROGRESS_MIN_INTERVAL_MS`), and the only turns that would widen are
|
|
668
|
+
* the ones that generate longest. What the wider read would buy is the prompt's
|
|
669
|
+
* key on the bubble instead of the partial marker; the reply itself is already
|
|
670
|
+
* on screen, and the row the writer saves when the turn ends replaces it.
|
|
671
|
+
*
|
|
562
672
|
* Never throws.
|
|
563
673
|
*
|
|
564
674
|
* @param target - The instance whose turn is in flight
|
|
@@ -576,14 +686,14 @@ async function readClaudeTurnProgress(target, capture) {
|
|
|
576
686
|
const path = await locateClaudeTranscript(homeDir, capture, sessionId);
|
|
577
687
|
if (!path)
|
|
578
688
|
return null;
|
|
579
|
-
const
|
|
580
|
-
if (
|
|
689
|
+
const window = await readClaudeTranscriptTail(path);
|
|
690
|
+
if (window === null)
|
|
581
691
|
return null;
|
|
582
692
|
// A fragment at the tail of a file being appended to is the normal case here
|
|
583
693
|
// — this reader runs *while* Claude is writing — and `parseClaudeTranscript`
|
|
584
694
|
// already counts it rather than throwing. Nothing extra is needed, and that
|
|
585
695
|
// is the property `claude-transcript-progress-2199` pins.
|
|
586
|
-
const parsed = (0, transcript_1.parseClaudeTranscript)(text);
|
|
696
|
+
const parsed = (0, transcript_1.parseClaudeTranscript)(window.text);
|
|
587
697
|
const built = (0, transcript_1.buildClaudeTurns)(parsed.records, sessionId ?? '');
|
|
588
698
|
const turn = built.turns.at(-1);
|
|
589
699
|
if (turn) {
|
|
@@ -785,6 +895,8 @@ async function isReadableFile(path) {
|
|
|
785
895
|
return false;
|
|
786
896
|
}
|
|
787
897
|
}
|
|
898
|
+
/** `\n`, which in UTF-8 is one byte and never part of another character. */
|
|
899
|
+
const LINE_FEED = 0x0a;
|
|
788
900
|
/**
|
|
789
901
|
* The last {@link CLAUDE_TRANSCRIPT_TAIL_BYTES} of the file, as UTF-8.
|
|
790
902
|
*
|
|
@@ -793,7 +905,13 @@ async function isReadableFile(path) {
|
|
|
793
905
|
* it would count as malformed anyway, and dropping it deliberately keeps that
|
|
794
906
|
* counter meaning "the writer was mid-append", which is the thing worth seeing.
|
|
795
907
|
*
|
|
796
|
-
*
|
|
908
|
+
* Since Issue #2470 the cut line is found by its byte rather than its
|
|
909
|
+
* character, because the window now says where its text starts and a read
|
|
910
|
+
* further back continues from there. The text is the same either way: the line
|
|
911
|
+
* feed is one byte that no other character contains, so decoding what follows
|
|
912
|
+
* it gives exactly what decoding the whole window and slicing after it did.
|
|
913
|
+
*
|
|
914
|
+
* @returns The window, or null when the file could not be read
|
|
797
915
|
*/
|
|
798
916
|
async function readClaudeTranscriptTail(path) {
|
|
799
917
|
let handle;
|
|
@@ -803,14 +921,20 @@ async function readClaudeTranscriptTail(path) {
|
|
|
803
921
|
const offset = Math.max(0, size - exports.CLAUDE_TRANSCRIPT_TAIL_BYTES);
|
|
804
922
|
const length = size - offset;
|
|
805
923
|
if (length <= 0)
|
|
806
|
-
return '';
|
|
924
|
+
return { text: '', size, startByte: 0 };
|
|
807
925
|
const buffer = Buffer.alloc(length);
|
|
808
926
|
const { bytesRead } = await handle.read(buffer, 0, length, offset);
|
|
809
|
-
const
|
|
927
|
+
const bytes = buffer.subarray(0, bytesRead);
|
|
810
928
|
if (offset === 0)
|
|
811
|
-
return text;
|
|
812
|
-
const firstBreak =
|
|
813
|
-
|
|
929
|
+
return { text: bytes.toString('utf8'), size, startByte: 0 };
|
|
930
|
+
const firstBreak = bytes.indexOf(LINE_FEED);
|
|
931
|
+
if (firstBreak === -1)
|
|
932
|
+
return { text: '', size, startByte: size };
|
|
933
|
+
return {
|
|
934
|
+
text: bytes.subarray(firstBreak + 1).toString('utf8'),
|
|
935
|
+
size,
|
|
936
|
+
startByte: offset + firstBreak + 1,
|
|
937
|
+
};
|
|
814
938
|
}
|
|
815
939
|
catch (error) {
|
|
816
940
|
logger.warn('claude-transcript-read-failed', {
|
|
@@ -823,6 +947,237 @@ async function readClaudeTranscriptTail(path) {
|
|
|
823
947
|
await handle?.close().catch(() => { });
|
|
824
948
|
}
|
|
825
949
|
}
|
|
950
|
+
/**
|
|
951
|
+
* The whole lines of `[from, to)`, for a read continuing in front of one whose
|
|
952
|
+
* text started at `to` (Issue #2470).
|
|
953
|
+
*
|
|
954
|
+
* `to` is always the start of a line — it is where the previous read's text
|
|
955
|
+
* began — so the only line that can be cut is the one at `from`, and it is
|
|
956
|
+
* dropped for the reason {@link readClaudeTranscriptTail} drops it.
|
|
957
|
+
*
|
|
958
|
+
* @returns The span, or null when fewer bytes came back than were asked for —
|
|
959
|
+
* the file is no longer the one the reads before this were taken from
|
|
960
|
+
*/
|
|
961
|
+
async function readClaudeTranscriptSpan(handle, from, to) {
|
|
962
|
+
const length = to - from;
|
|
963
|
+
const buffer = Buffer.alloc(length);
|
|
964
|
+
const { bytesRead } = await handle.read(buffer, 0, length, from);
|
|
965
|
+
if (bytesRead !== length)
|
|
966
|
+
return null;
|
|
967
|
+
if (from === 0)
|
|
968
|
+
return { text: buffer.toString('utf8'), startByte: 0 };
|
|
969
|
+
const firstBreak = buffer.indexOf(LINE_FEED);
|
|
970
|
+
if (firstBreak === -1)
|
|
971
|
+
return { text: '', startByte: to };
|
|
972
|
+
return { text: buffer.subarray(firstBreak + 1).toString('utf8'), startByte: from + firstBreak + 1 };
|
|
973
|
+
}
|
|
974
|
+
/**
|
|
975
|
+
* The transcript's last records, read back far enough to hold the newest turn's
|
|
976
|
+
* prompt record where that is possible (Issue #2470).
|
|
977
|
+
*
|
|
978
|
+
* The first read is {@link readClaudeTranscriptTail} and nothing else, and for a
|
|
979
|
+
* window holding a prompt record anywhere that is the whole of it: the records
|
|
980
|
+
* and the turns are exactly what they were before #2470. Only a window of
|
|
981
|
+
* orphaned assistant records and no prompt record is read further back — see
|
|
982
|
+
* {@link extendClaudeTranscriptRead}.
|
|
983
|
+
*
|
|
984
|
+
* @returns The records and turns, or null when the file could not be read
|
|
985
|
+
*/
|
|
986
|
+
async function readClaudeTranscriptTurns(target, path, sessionId) {
|
|
987
|
+
const window = await readClaudeTranscriptTail(path);
|
|
988
|
+
if (window === null)
|
|
989
|
+
return null;
|
|
990
|
+
const parsed = (0, transcript_1.parseClaudeTranscript)(window.text);
|
|
991
|
+
const built = (0, transcript_1.buildClaudeTurns)(parsed.records, sessionId);
|
|
992
|
+
const read = {
|
|
993
|
+
parsed,
|
|
994
|
+
built,
|
|
995
|
+
size: window.size,
|
|
996
|
+
startByte: window.startByte,
|
|
997
|
+
reachedLimit: false,
|
|
998
|
+
};
|
|
999
|
+
if (built.turns.length > 0 || built.orphanedAssistantRecords === 0 || window.startByte === 0) {
|
|
1000
|
+
return read;
|
|
1001
|
+
}
|
|
1002
|
+
// A read further back that fails leaves the first window's answer standing,
|
|
1003
|
+
// which is the pre-#2470 answer: no turn, and the scraper keeps it.
|
|
1004
|
+
return (await extendClaudeTranscriptRead(target, path, sessionId, read)) ?? read;
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Double the window until the newest turn's prompt record is in it (Issue #2470).
|
|
1008
|
+
*
|
|
1009
|
+
* 8, 16, 32, 64 MiB, stopping at the first size that brings a prompt record in,
|
|
1010
|
+
* at the start of the file, or at {@link CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES}. One
|
|
1011
|
+
* sequence per ask, and one `claude-transcript-window-extended` line for it.
|
|
1012
|
+
*
|
|
1013
|
+
* Each step reads only the bytes in front of the previous one and puts their
|
|
1014
|
+
* records in front of the records already parsed, so the cost of reaching a
|
|
1015
|
+
* prompt 20 MiB back is one read of 20-odd MiB, not four overlapping ones. That
|
|
1016
|
+
* is sound because the file is append-only and every read of one ask is bounded
|
|
1017
|
+
* by the size it was first opened at: the bytes already read do not change. It
|
|
1018
|
+
* also means the newest prompt record, once there is one, is in the span just
|
|
1019
|
+
* read — nothing behind it held one — which is where its offset is looked up.
|
|
1020
|
+
*
|
|
1021
|
+
* @returns The widened read, or null when a read failed part-way
|
|
1022
|
+
*/
|
|
1023
|
+
async function extendClaudeTranscriptRead(target, path, sessionId, first) {
|
|
1024
|
+
const { size } = first;
|
|
1025
|
+
let handle;
|
|
1026
|
+
try {
|
|
1027
|
+
handle = await (0, promises_1.open)(path, 'r');
|
|
1028
|
+
let records = first.parsed.records;
|
|
1029
|
+
let malformedLines = first.parsed.malformedLines;
|
|
1030
|
+
let built = first.built;
|
|
1031
|
+
let startByte = first.startByte;
|
|
1032
|
+
let windowBytes = exports.CLAUDE_TRANSCRIPT_TAIL_BYTES;
|
|
1033
|
+
let promptOffsetFromEnd = null;
|
|
1034
|
+
while (built.turns.length === 0 &&
|
|
1035
|
+
startByte > 0 &&
|
|
1036
|
+
windowBytes < exports.CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES) {
|
|
1037
|
+
windowBytes *= 2;
|
|
1038
|
+
const span = await readClaudeTranscriptSpan(handle, Math.max(0, size - windowBytes), startByte);
|
|
1039
|
+
if (span === null)
|
|
1040
|
+
return null;
|
|
1041
|
+
const prefix = (0, transcript_1.parseClaudeTranscript)(span.text);
|
|
1042
|
+
records = [...prefix.records, ...records];
|
|
1043
|
+
malformedLines += prefix.malformedLines;
|
|
1044
|
+
startByte = span.startByte;
|
|
1045
|
+
built = (0, transcript_1.buildClaudeTurns)(records, sessionId);
|
|
1046
|
+
const newest = built.turns.at(-1);
|
|
1047
|
+
if (newest)
|
|
1048
|
+
promptOffsetFromEnd = claudeRecordOffsetFromEnd(span, newest.promptUuid, size);
|
|
1049
|
+
}
|
|
1050
|
+
logger.info('claude-transcript-window-extended', {
|
|
1051
|
+
worktreeId: target.worktreeId,
|
|
1052
|
+
instanceId: target.instanceId ?? target.cliToolId,
|
|
1053
|
+
path,
|
|
1054
|
+
fromBytes: exports.CLAUDE_TRANSCRIPT_TAIL_BYTES,
|
|
1055
|
+
toBytes: windowBytes,
|
|
1056
|
+
size,
|
|
1057
|
+
// Null when no prompt record was found; see `reachedLimit`.
|
|
1058
|
+
promptOffsetFromEnd,
|
|
1059
|
+
records: records.length,
|
|
1060
|
+
});
|
|
1061
|
+
return {
|
|
1062
|
+
parsed: { records, malformedLines },
|
|
1063
|
+
built,
|
|
1064
|
+
size,
|
|
1065
|
+
startByte,
|
|
1066
|
+
reachedLimit: built.turns.length === 0 && startByte > 0,
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
catch (error) {
|
|
1070
|
+
logger.warn('claude-transcript-read-failed', {
|
|
1071
|
+
path,
|
|
1072
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1073
|
+
});
|
|
1074
|
+
return null;
|
|
1075
|
+
}
|
|
1076
|
+
finally {
|
|
1077
|
+
await handle?.close().catch(() => { });
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
/**
|
|
1081
|
+
* How far from the end of the file the line holding this record starts, or null.
|
|
1082
|
+
*
|
|
1083
|
+
* The number `claude-transcript-window-extended` reports, and nothing branches
|
|
1084
|
+
* on it: it says how much bigger than the first window the turn was. A line is
|
|
1085
|
+
* accepted only once it parses as the record with this `uuid`, because the same
|
|
1086
|
+
* string is also the next record's `parentUuid`.
|
|
1087
|
+
*/
|
|
1088
|
+
function claudeRecordOffsetFromEnd(span, uuid, size) {
|
|
1089
|
+
const { text } = span;
|
|
1090
|
+
for (let at = text.indexOf(uuid); at !== -1; at = text.indexOf(uuid, at + uuid.length)) {
|
|
1091
|
+
const lineStart = text.lastIndexOf('\n', at) + 1;
|
|
1092
|
+
const lineEnd = text.indexOf('\n', at);
|
|
1093
|
+
let record = null;
|
|
1094
|
+
try {
|
|
1095
|
+
record = (0, transcript_1.readClaudeTranscriptRecord)(JSON.parse(text.slice(lineStart, lineEnd === -1 ? text.length : lineEnd)));
|
|
1096
|
+
}
|
|
1097
|
+
catch {
|
|
1098
|
+
// Not a whole record; keep looking.
|
|
1099
|
+
}
|
|
1100
|
+
if (record?.uuid === uuid) {
|
|
1101
|
+
return size - span.startByte - Buffer.byteLength(text.slice(0, lineStart), 'utf8');
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return null;
|
|
1105
|
+
}
|
|
1106
|
+
/** A turn with no prompt record writes no user row; see {@link recordClaudeUserTurn}. */
|
|
1107
|
+
const NO_USER_ROW = { outcome: 'skipped', messageId: null, timestampMs: null };
|
|
1108
|
+
/**
|
|
1109
|
+
* Save what can be read of a turn whose prompt record is out of reach (Issue #2470).
|
|
1110
|
+
*
|
|
1111
|
+
* The safety net under {@link CLAUDE_TRANSCRIPT_MAX_TAIL_BYTES}: the window has
|
|
1112
|
+
* been widened as far as it goes and still holds no prompt record, so there is
|
|
1113
|
+
* no prompt `uuid` to key a row on and no user row to write. What the window
|
|
1114
|
+
* does hold is the turn's end — the part the operator most wants, and the part a
|
|
1115
|
+
* long turn's pane has scrolled furthest past — and this writes that rather than
|
|
1116
|
+
* leave the turn with no row at all. The body opens with
|
|
1117
|
+
* {@link CLAUDE_TURN_HEAD_MISSING_MARKER}, so it never reads as the whole reply.
|
|
1118
|
+
*
|
|
1119
|
+
* Everything that makes a prompt-opened row safe to write applies unchanged,
|
|
1120
|
+
* because the row goes through the same {@link writeClaudeTurn}: an open turn is
|
|
1121
|
+
* refused and reported `not_yet_closed` (#2264, #2436), an empty one is handed
|
|
1122
|
+
* back to the scraper, and a second read finds the row by its key and answers
|
|
1123
|
+
* true. The key is the one difference, and {@link buildHeadlessClaudeTurn} says
|
|
1124
|
+
* why it can be written under at all: it names the record that closed the turn,
|
|
1125
|
+
* which every later read sees too.
|
|
1126
|
+
*
|
|
1127
|
+
* Neither the live bubble's body nor its key. {@link collectHeadlessClaudeTurn}
|
|
1128
|
+
* builds the bubble from the first window under the session's one live key, and
|
|
1129
|
+
* that key, written, would make the session's next long turn read "already
|
|
1130
|
+
* saved".
|
|
1131
|
+
*
|
|
1132
|
+
* @returns Whether History now holds the turn, marked as missing its beginning
|
|
1133
|
+
*/
|
|
1134
|
+
async function captureHeadlessClaudeTurn(target, records, sessionId, path, report) {
|
|
1135
|
+
const instanceId = target.instanceId ?? target.cliToolId;
|
|
1136
|
+
const headless = (0, transcript_1.buildHeadlessClaudeTurn)(records, sessionId);
|
|
1137
|
+
if (headless === null) {
|
|
1138
|
+
logger.debug('claude-transcript-headless-unkeyed', {
|
|
1139
|
+
worktreeId: target.worktreeId,
|
|
1140
|
+
instanceId,
|
|
1141
|
+
path,
|
|
1142
|
+
});
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
const { turn } = headless;
|
|
1146
|
+
const rendered = (0, transcript_1.renderClaudeTurn)(turn);
|
|
1147
|
+
logger.info('claude-transcript-headless-turn', {
|
|
1148
|
+
worktreeId: target.worktreeId,
|
|
1149
|
+
instanceId,
|
|
1150
|
+
sessionId: turn.sessionId,
|
|
1151
|
+
requestId: (0, agent_transcript_1.claudeTurnRequestId)(turn.promptUuid),
|
|
1152
|
+
path,
|
|
1153
|
+
assistantRecords: turn.assistantRecords,
|
|
1154
|
+
overflowed: turn.overflowed,
|
|
1155
|
+
});
|
|
1156
|
+
const captured = await writeClaudeTurn(target, turn,
|
|
1157
|
+
// An empty body stays empty, so the writer hands the turn back to the
|
|
1158
|
+
// scraper instead of saving a row that is nothing but the marker.
|
|
1159
|
+
rendered.body.length === 0 ? rendered : markClaudeTurnHeadMissing(rendered), resolveAssistantTimestampMs(turn, NO_USER_ROW, headless.lastRecordAt, null), path, report);
|
|
1160
|
+
if (captured) {
|
|
1161
|
+
// Issue #2437, for the reason the prompt-opened path gives.
|
|
1162
|
+
await (0, assistant_response_saver_1.advanceCapturedLineForTranscriptTurn)(target);
|
|
1163
|
+
}
|
|
1164
|
+
return captured;
|
|
1165
|
+
}
|
|
1166
|
+
/**
|
|
1167
|
+
* A rendered turn with {@link CLAUDE_TURN_HEAD_MISSING_MARKER} as its first line.
|
|
1168
|
+
*
|
|
1169
|
+
* Held to {@link MAX_CLAUDE_TURN_BODY_LENGTH} the way `renderClaudeTurn` holds
|
|
1170
|
+
* every body, so a turn already cut at its end is still cut the same way.
|
|
1171
|
+
*/
|
|
1172
|
+
function markClaudeTurnHeadMissing(rendered) {
|
|
1173
|
+
let body = `${exports.CLAUDE_TURN_HEAD_MISSING_MARKER}\n\n${rendered.body}`;
|
|
1174
|
+
if (body.length > transcript_1.MAX_CLAUDE_TURN_BODY_LENGTH) {
|
|
1175
|
+
body =
|
|
1176
|
+
body.slice(0, transcript_1.MAX_CLAUDE_TURN_BODY_LENGTH - transcript_1.CLAUDE_TURN_TRUNCATION_MARKER.length) +
|
|
1177
|
+
transcript_1.CLAUDE_TURN_TRUNCATION_MARKER;
|
|
1178
|
+
}
|
|
1179
|
+
return { ...rendered, body };
|
|
1180
|
+
}
|
|
826
1181
|
/**
|
|
827
1182
|
* How many already-written turns are re-rendered and compared (Issue #2264).
|
|
828
1183
|
*
|
|
@@ -935,7 +1290,9 @@ async function refreshClaudeTurnRows(target, candidates, path) {
|
|
|
935
1290
|
*
|
|
936
1291
|
* `findMessageByRequestId` is both the idempotency check and the reason a
|
|
937
1292
|
* repeat poll does not duplicate the row: the id is derived from the prompt
|
|
938
|
-
* record's `uuid
|
|
1293
|
+
* record's `uuid` — or, for a turn whose prompt record is out of reach (Issue
|
|
1294
|
+
* #2470), from the record that closed it (`claudeHeadlessTurnId`) — and neither
|
|
1295
|
+
* changes between reads of the same file.
|
|
939
1296
|
*
|
|
940
1297
|
* Answering **true** for a turn that was already saved is deliberate. It means
|
|
941
1298
|
* "History holds this turn as Markdown", which is exactly what the poller needs
|
|
@@ -68,7 +68,9 @@ exports.isClaudeTurnClosingRecord = isClaudeTurnClosingRecord;
|
|
|
68
68
|
exports.isClaudeTurnWritable = isClaudeTurnWritable;
|
|
69
69
|
exports.createClaudeTurn = createClaudeTurn;
|
|
70
70
|
exports.buildClaudeTurns = buildClaudeTurns;
|
|
71
|
+
exports.buildHeadlessClaudeTurn = buildHeadlessClaudeTurn;
|
|
71
72
|
exports.renderClaudeTurn = renderClaudeTurn;
|
|
73
|
+
const agent_transcript_1 = require("../../../../types/agent-transcript");
|
|
72
74
|
const event_mapper_1 = require("../event-mapper");
|
|
73
75
|
const turn_body_1 = require("../turn-body");
|
|
74
76
|
/**
|
|
@@ -588,6 +590,95 @@ function buildClaudeTurns(records, sessionId) {
|
|
|
588
590
|
}
|
|
589
591
|
return { turns, orphanedAssistantRecords, sidechainRecords };
|
|
590
592
|
}
|
|
593
|
+
/**
|
|
594
|
+
* The turn the records in front of the first prompt record belong to (Issue #2470).
|
|
595
|
+
*
|
|
596
|
+
* {@link buildClaudeTurns} counts these as orphans and drops them, and for the
|
|
597
|
+
* case it was written for that is right: a window that opens mid-turn over a
|
|
598
|
+
* long session holds the tail of a turn that was written back when it was the
|
|
599
|
+
* newest. This is for the other case, and `./history` only calls it there — a
|
|
600
|
+
* window widened to its limit that still holds **no** prompt record at all, so
|
|
601
|
+
* these records are the newest turn and no other reader is going to write them.
|
|
602
|
+
*
|
|
603
|
+
* Built by the rules `buildClaudeTurns` applies to a turn, so the two cannot
|
|
604
|
+
* disagree about what one is: sidechains skipped, `closed` read off the **last**
|
|
605
|
+
* assistant record ({@link isClaudeTurnClosingRecord}), `stopReasonObserved` set
|
|
606
|
+
* by any. `./history`'s writer therefore refuses a headless turn that is still
|
|
607
|
+
* open exactly as it refuses any other (#2264).
|
|
608
|
+
*
|
|
609
|
+
* The key is {@link claudeHeadlessTurnId} of the last assistant record's `uuid`;
|
|
610
|
+
* see that function for the census behind choosing it over the first record in
|
|
611
|
+
* the window. Once the turn is closed, the last assistant record is the one
|
|
612
|
+
* record every later read of it is guaranteed to see.
|
|
613
|
+
*
|
|
614
|
+
* **When the block cap bites, the end is kept, not the beginning** — the reverse
|
|
615
|
+
* of `buildClaudeTurns`. A prompt-opened turn keeps its first
|
|
616
|
+
* {@link MAX_CLAUDE_TURN_BLOCKS} because that is where its reply starts; a
|
|
617
|
+
* headless one has already lost its start, and what is worth keeping of the rest
|
|
618
|
+
* is where the reply ends.
|
|
619
|
+
*
|
|
620
|
+
* @param records - In file order
|
|
621
|
+
* @param sessionId - Fallback for records that carry no `sessionId`
|
|
622
|
+
* @returns The turn, or null when no assistant record precedes the first prompt
|
|
623
|
+
* record, or when the last one has no `uuid` to key the turn on
|
|
624
|
+
*/
|
|
625
|
+
function buildHeadlessClaudeTurn(records, sessionId) {
|
|
626
|
+
const blocks = [];
|
|
627
|
+
let assistantRecords = 0;
|
|
628
|
+
let closed = false;
|
|
629
|
+
let stopReasonObserved = false;
|
|
630
|
+
let superseded = false;
|
|
631
|
+
let firstRecordAt = 0;
|
|
632
|
+
let lastRecordAt = 0;
|
|
633
|
+
let last = null;
|
|
634
|
+
for (const record of records) {
|
|
635
|
+
if (record.isSidechain)
|
|
636
|
+
continue;
|
|
637
|
+
if (isClaudePromptRecord(record)) {
|
|
638
|
+
// A later prompt is proof nothing more is coming, as it is for
|
|
639
|
+
// `buildClaudeTurns`. `./history` never hands this a window holding one.
|
|
640
|
+
superseded = true;
|
|
641
|
+
break;
|
|
642
|
+
}
|
|
643
|
+
if (record.type !== 'assistant')
|
|
644
|
+
continue;
|
|
645
|
+
assistantRecords += 1;
|
|
646
|
+
// Assigned, not or-ed — the last record's answer stands. See buildClaudeTurns.
|
|
647
|
+
closed = isClaudeTurnClosingRecord(record);
|
|
648
|
+
if (record.stopReason !== null)
|
|
649
|
+
stopReasonObserved = true;
|
|
650
|
+
if (record.timestampMs !== null) {
|
|
651
|
+
if (firstRecordAt === 0)
|
|
652
|
+
firstRecordAt = record.timestampMs;
|
|
653
|
+
lastRecordAt = Math.max(lastRecordAt, record.timestampMs);
|
|
654
|
+
}
|
|
655
|
+
for (const block of record.blocks)
|
|
656
|
+
blocks.push(block);
|
|
657
|
+
last = record;
|
|
658
|
+
}
|
|
659
|
+
if (last === null || !last.uuid)
|
|
660
|
+
return null;
|
|
661
|
+
const turnSessionId = last.sessionId ?? sessionId;
|
|
662
|
+
const overflowed = blocks.length > exports.MAX_CLAUDE_TURN_BLOCKS;
|
|
663
|
+
return {
|
|
664
|
+
turn: {
|
|
665
|
+
sessionId: turnSessionId,
|
|
666
|
+
promptUuid: (0, agent_transcript_1.claudeHeadlessTurnId)(turnSessionId, last.uuid),
|
|
667
|
+
// The earliest instant the window can vouch for. `./history` only uses it
|
|
668
|
+
// as the floor it dates the row against, and it is never above lastRecordAt.
|
|
669
|
+
startedAt: firstRecordAt,
|
|
670
|
+
promptText: '',
|
|
671
|
+
promptIsOperatorInput: false,
|
|
672
|
+
blocks: overflowed ? blocks.slice(-exports.MAX_CLAUDE_TURN_BLOCKS) : blocks,
|
|
673
|
+
assistantRecords,
|
|
674
|
+
closed,
|
|
675
|
+
superseded,
|
|
676
|
+
stopReasonObserved,
|
|
677
|
+
overflowed,
|
|
678
|
+
},
|
|
679
|
+
lastRecordAt,
|
|
680
|
+
};
|
|
681
|
+
}
|
|
591
682
|
/**
|
|
592
683
|
* Block types that carry nothing a reader wants.
|
|
593
684
|
*
|