commandmate 0.30.0 → 0.30.2
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 +158 -158
- package/.next/app-path-routes-manifest.json +34 -34
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- 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.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.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 +2 -2
- 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.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/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.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.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.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.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.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_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/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.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.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_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.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_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_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.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.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_client-reference-manifest.js +1 -1
- 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_client-reference-manifest.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_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.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_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_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 +12 -12
- 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 +34 -34
- package/.next/server/chunks/1058.js +1 -1
- package/.next/server/chunks/1886.js +2 -2
- package/.next/server/chunks/2119.js +1 -1
- package/.next/server/chunks/2445.js +1 -1
- package/.next/server/chunks/3099.js +5 -0
- package/.next/server/chunks/5017.js +1 -1
- package/.next/server/chunks/5043.js +1 -0
- package/.next/server/chunks/552.js +25 -0
- package/.next/server/chunks/6130.js +1 -1
- package/.next/server/chunks/6894.js +1 -1
- package/.next/server/chunks/9696.js +2 -2
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{6381-28d0e95620aa018e.js → 6381-147dde2a967e9458.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-2f73d69187936627.js +1 -0
- package/.next/static/css/23f9c40ecc458c77.css +1 -0
- package/.next/trace +13 -13
- package/dist/server/src/lib/history/user-turn-recorder.js +9 -4
- package/dist/server/src/lib/hooks/agent-event-service.js +40 -6
- package/dist/server/src/lib/hooks/sources/antigravity/history.js +125 -18
- package/dist/server/src/lib/hooks/sources/antigravity/transcript.js +39 -0
- package/dist/server/src/lib/hooks/sources/claude/history.js +312 -21
- package/dist/server/src/lib/hooks/sources/claude/transcript.js +175 -13
- package/dist/server/src/lib/hooks/sources/codex/history.js +100 -13
- package/dist/server/src/lib/hooks/stop-history-capture.js +194 -0
- package/dist/server/src/lib/polling/response-checker.js +43 -3
- package/dist/server/src/lib/polling/structured-history-gate.js +131 -6
- package/dist/server/src/lib/session/current-output-builder.js +118 -16
- package/package.json +1 -1
- package/.next/server/chunks/2827.js +0 -29
- package/.next/server/chunks/6781.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-d48b95ed554bcd13.js +0 -1
- package/.next/static/css/61e10d4b1b11b193.css +0 -1
- /package/.next/static/{7K0XXFu_ty4wRSkl4X21E → QXxQbXWoo7ldUyOcv7PCZ}/_buildManifest.js +0 -0
- /package/.next/static/{7K0XXFu_ty4wRSkl4X21E → QXxQbXWoo7ldUyOcv7PCZ}/_ssgManifest.js +0 -0
|
@@ -90,13 +90,14 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
90
90
|
};
|
|
91
91
|
})();
|
|
92
92
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
93
|
-
exports.CLAUDE_TRANSCRIPT_TAIL_BYTES = void 0;
|
|
93
|
+
exports.CLAUDE_TURN_RECHECK_LIMIT = exports.CLAUDE_TRANSCRIPT_TAIL_BYTES = void 0;
|
|
94
94
|
exports.resetClaudeTranscriptSessions = resetClaudeTranscriptSessions;
|
|
95
95
|
exports.resolveClaudeSessionId = resolveClaudeSessionId;
|
|
96
96
|
exports.claudeProjectsRoot = claudeProjectsRoot;
|
|
97
97
|
exports.claudeTranscriptPath = claudeTranscriptPath;
|
|
98
98
|
exports.acceptClaudeTranscriptHint = acceptClaudeTranscriptHint;
|
|
99
99
|
exports.captureClaudeTranscriptTurn = captureClaudeTranscriptTurn;
|
|
100
|
+
exports.resolveClaudeTranscriptPath = resolveClaudeTranscriptPath;
|
|
100
101
|
exports.readClaudeTurnProgress = readClaudeTurnProgress;
|
|
101
102
|
const promises_1 = require("fs/promises");
|
|
102
103
|
const os_1 = require("os");
|
|
@@ -113,8 +114,9 @@ const logger = (0, logger_1.createLogger)('lib/hooks/sources/claude/history');
|
|
|
113
114
|
* The file grows for the life of the session and a long one is tens of
|
|
114
115
|
* megabytes — the largest on this machine on 2026-08-31 was 23 MB — so reading
|
|
115
116
|
* it whole on every finished turn would be the most expensive thing the poller
|
|
116
|
-
* does. Only
|
|
117
|
-
*
|
|
117
|
+
* does. Only turns whose prompt record is inside the window are ever written
|
|
118
|
+
* (see {@link captureClaudeTranscriptTurn}), which is also what stops a turn the
|
|
119
|
+
* window opened halfway through being written from its middle.
|
|
118
120
|
*
|
|
119
121
|
* 4 MiB is roughly two orders of magnitude above the measured size of a single
|
|
120
122
|
* turn and still small enough to read and parse in one tick. A turn that
|
|
@@ -216,15 +218,38 @@ function acceptClaudeTranscriptHint(homeDir, hint) {
|
|
|
216
218
|
return resolved;
|
|
217
219
|
}
|
|
218
220
|
/**
|
|
219
|
-
* Read
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
* `
|
|
224
|
-
*
|
|
225
|
-
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
221
|
+
* Read this instance's unwritten turns out of its transcript and write them.
|
|
222
|
+
*
|
|
223
|
+
* ## Why this is no longer "only the newest turn" (Issue #2246)
|
|
224
|
+
*
|
|
225
|
+
* #2121 wrote `built.turns.at(-1)` and nothing else, for a reason that was
|
|
226
|
+
* sound and is still half true: every earlier turn of this session already has
|
|
227
|
+
* a `chat_messages` row that the *scraper* wrote, and writing a Markdown row
|
|
228
|
+
* for it as well would put the same reply in History twice — once as prose and
|
|
229
|
+
* once as the pane drew it.
|
|
230
|
+
*
|
|
231
|
+
* What that argument did not cover is a turn **no writer recorded at all**. The
|
|
232
|
+
* trigger for this reader is the poller deciding a turn finished, so a poll that
|
|
233
|
+
* misjudges one completion (#2247's launch-banner heuristic, measured on
|
|
234
|
+
* 2026-09-02) does not merely delay the turn: by the time the next completion is
|
|
235
|
+
* judged, "the newest turn" is the *next* one, and the missed turn is nobody's.
|
|
236
|
+
* One dropped completion cost one turn permanently.
|
|
237
|
+
*
|
|
238
|
+
* So the unit of work is now "every turn in the window that is not already a
|
|
239
|
+
* row", written oldest first, and what preserves #2121's argument is the
|
|
240
|
+
* **anchor**: the newest turn in the window that this reader has already written
|
|
241
|
+
* is where the backfill starts. Turns after it are turns this reader was live
|
|
242
|
+
* for, so a scraper row for them exists only in the case the anchor cannot see
|
|
243
|
+
* — the reader answering false once for a transient reason (an empty body) and
|
|
244
|
+
* the scraper saving that turn's pane copy. That trade is deliberate and in the
|
|
245
|
+
* direction every Issue in this subsystem picks: two writers duplicate a reply,
|
|
246
|
+
* no writer loses one.
|
|
247
|
+
*
|
|
248
|
+
* When the window holds **no** anchor — a session the reader has never written
|
|
249
|
+
* for, and equally the first read after a `/clear` — this falls back to #2121's
|
|
250
|
+
* behaviour exactly: the newest turn, and nothing before it. That is the case
|
|
251
|
+
* where the earlier turns really are the scraper's, and there is no evidence in
|
|
252
|
+
* the window that says otherwise.
|
|
228
253
|
*
|
|
229
254
|
* The return value is the poller's instruction, so the two failure directions
|
|
230
255
|
* are worth stating plainly. **True** means this path has recorded the turn and
|
|
@@ -234,10 +259,15 @@ function acceptClaudeTranscriptHint(homeDir, hint) {
|
|
|
234
259
|
* false, which is the fail-open the acceptance criteria require: two writers
|
|
235
260
|
* duplicate a reply, no writer loses one.
|
|
236
261
|
*
|
|
262
|
+
* **It is the newest turn the answer is about**, backfill or not. A run that
|
|
263
|
+
* wrote three missed turns and then found the newest one still empty answers
|
|
264
|
+
* false, because the scrape the poller is holding is the pane's copy of *that*
|
|
265
|
+
* turn and dropping it would lose it.
|
|
266
|
+
*
|
|
237
267
|
* Never throws.
|
|
238
268
|
*
|
|
239
269
|
* @param target - The instance whose turn just ended
|
|
240
|
-
* @returns Whether History now holds this turn as
|
|
270
|
+
* @returns Whether History now holds this instance's newest turn as Markdown
|
|
241
271
|
*/
|
|
242
272
|
async function captureClaudeTranscriptTurn(target, capture) {
|
|
243
273
|
const instanceId = target.instanceId ?? target.cliToolId;
|
|
@@ -262,8 +292,7 @@ async function captureClaudeTranscriptTurn(target, capture) {
|
|
|
262
292
|
return false;
|
|
263
293
|
const parsed = (0, transcript_1.parseClaudeTranscript)(text);
|
|
264
294
|
const built = (0, transcript_1.buildClaudeTurns)(parsed.records, sessionId ?? '');
|
|
265
|
-
|
|
266
|
-
if (!turn) {
|
|
295
|
+
if (built.turns.length === 0) {
|
|
267
296
|
logger.info('claude-transcript-no-turn', {
|
|
268
297
|
worktreeId: target.worktreeId,
|
|
269
298
|
instanceId,
|
|
@@ -294,8 +323,49 @@ async function captureClaudeTranscriptTurn(target, capture) {
|
|
|
294
323
|
sidechainRecords: built.sidechainRecords,
|
|
295
324
|
});
|
|
296
325
|
}
|
|
297
|
-
const
|
|
298
|
-
|
|
326
|
+
const pending = await selectUnwrittenClaudeTurns(target, built.turns);
|
|
327
|
+
// Before anything is written: the rows that are already there (#2264). This
|
|
328
|
+
// is deliberately ahead of the early return below, because "the newest turn
|
|
329
|
+
// is already a row" is the state the nine short rows the Issue measured were
|
|
330
|
+
// stuck in — the reader answered true and did nothing while the transcript
|
|
331
|
+
// beside it held the missing paragraph.
|
|
332
|
+
await refreshClaudeTurnRows(target, built.turns.slice(0, built.turns.length - pending.turns.length).slice(-exports.CLAUDE_TURN_RECHECK_LIMIT), path);
|
|
333
|
+
if (pending.turns.length === 0) {
|
|
334
|
+
// The anchor is the newest turn in the window, so there is nothing to
|
|
335
|
+
// write and the newest turn is a row. True, for the reason
|
|
336
|
+
// {@link writeClaudeTurn} answers true to an already-saved turn: a second
|
|
337
|
+
// poll of one finished turn must not put the pane's copy on top of it.
|
|
338
|
+
logger.debug('claude-transcript-turns-already-saved', {
|
|
339
|
+
worktreeId: target.worktreeId,
|
|
340
|
+
instanceId,
|
|
341
|
+
turnsInWindow: built.turns.length,
|
|
342
|
+
});
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
if (pending.turns.length > 1) {
|
|
346
|
+
// The #2246 case, and the one worth a line in the log: more than one turn
|
|
347
|
+
// was unwritten, so a completion went unnoticed at the time.
|
|
348
|
+
logger.info('claude-transcript-backfilling-turns', {
|
|
349
|
+
worktreeId: target.worktreeId,
|
|
350
|
+
instanceId,
|
|
351
|
+
path,
|
|
352
|
+
pendingTurns: pending.turns.length,
|
|
353
|
+
turnsInWindow: built.turns.length,
|
|
354
|
+
anchored: pending.anchored,
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
// Oldest first, and the ordering is load-bearing twice over: History sorts
|
|
358
|
+
// by timestamp, and each turn's `/send` row is adopted against the previous
|
|
359
|
+
// turn's start, so a later turn must not get to claim a row an earlier one
|
|
360
|
+
// is about to ask for.
|
|
361
|
+
let captured = false;
|
|
362
|
+
for (let index = 0; index < pending.turns.length; index += 1) {
|
|
363
|
+
const turn = pending.turns[index];
|
|
364
|
+
const previousStartedAt = index === 0 ? pending.previousStartedAt : pending.turns[index - 1].startedAt;
|
|
365
|
+
const userRow = await recordClaudeUserTurn(target, turn, previousStartedAt);
|
|
366
|
+
captured = await writeClaudeTurn(target, turn, (0, transcript_1.renderClaudeTurn)(turn), resolveAssistantTimestampMs(turn, userRow), path);
|
|
367
|
+
}
|
|
368
|
+
return captured;
|
|
299
369
|
}
|
|
300
370
|
catch (error) {
|
|
301
371
|
logger.error('claude-transcript-capture-failed', {
|
|
@@ -306,6 +376,78 @@ async function captureClaudeTranscriptTurn(target, capture) {
|
|
|
306
376
|
return false;
|
|
307
377
|
}
|
|
308
378
|
}
|
|
379
|
+
/**
|
|
380
|
+
* The turns {@link captureClaudeTranscriptTurn} still has to write (Issue #2246).
|
|
381
|
+
*
|
|
382
|
+
* The search runs **backwards from the newest turn** and stops at the first one
|
|
383
|
+
* that is already a row. That turn is the *anchor*, and everything after it is
|
|
384
|
+
* pending — by construction, since the anchor is the newest written turn in the
|
|
385
|
+
* window.
|
|
386
|
+
*
|
|
387
|
+
* Two properties of that rule are worth stating because both were required:
|
|
388
|
+
*
|
|
389
|
+
* - **Order, not time.** The comparison is the transcript's own record order
|
|
390
|
+
* and never a timestamp. A turn's assistant row is dated one millisecond
|
|
391
|
+
* after its user row (`resolveAssistantTimestampMs`), so the rows do not
|
|
392
|
+
* carry an ordering that could be trusted for this.
|
|
393
|
+
* - **The window bounds it.** `buildClaudeTurns` only opens a turn on a prompt
|
|
394
|
+
* record, so a turn whose prompt fell outside
|
|
395
|
+
* {@link CLAUDE_TRANSCRIPT_TAIL_BYTES} is not in `turns` at all and cannot be
|
|
396
|
+
* backfilled from its middle. Its records are counted as
|
|
397
|
+
* `orphanedAssistantRecords` instead, which is what the caller reports.
|
|
398
|
+
*
|
|
399
|
+
* A window with no anchor answers with the newest turn alone. See
|
|
400
|
+
* {@link captureClaudeTranscriptTurn} for why that is #2121's behaviour rather
|
|
401
|
+
* than a degraded one.
|
|
402
|
+
*
|
|
403
|
+
* @param turns - Every turn in the window, oldest first
|
|
404
|
+
*/
|
|
405
|
+
async function selectUnwrittenClaudeTurns(target, turns) {
|
|
406
|
+
const [{ getDbInstance }, { findMessageByRequestId }] = await Promise.all([
|
|
407
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db/db-instance'))),
|
|
408
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db'))),
|
|
409
|
+
]);
|
|
410
|
+
const db = getDbInstance();
|
|
411
|
+
for (let index = turns.length - 1; index >= 0; index -= 1) {
|
|
412
|
+
const requestId = (0, agent_transcript_1.claudeTurnRequestId)(turns[index].promptUuid);
|
|
413
|
+
if (!findMessageByRequestId(db, target.worktreeId, requestId))
|
|
414
|
+
continue;
|
|
415
|
+
return {
|
|
416
|
+
turns: turns.slice(index + 1),
|
|
417
|
+
previousStartedAt: turns[index].startedAt,
|
|
418
|
+
anchored: true,
|
|
419
|
+
};
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
turns: turns.slice(-1),
|
|
423
|
+
previousStartedAt: turns.length > 1 ? turns[turns.length - 2].startedAt : 0,
|
|
424
|
+
anchored: false,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* The transcript file this instance would be read from, or null (Issue #2246).
|
|
429
|
+
*
|
|
430
|
+
* The same two steps {@link captureClaudeTranscriptTurn} opens with — resolve
|
|
431
|
+
* the session pointer, then check the filesystem — asked without reading or
|
|
432
|
+
* writing anything. The Stop receiver uses it to decide whether waiting half a
|
|
433
|
+
* second and asking again could possibly help: a session with no transcript to
|
|
434
|
+
* name will not have grown one by then, and a hook handler that sleeps for
|
|
435
|
+
* nothing is a hook handler that slows every turn of every tool.
|
|
436
|
+
*
|
|
437
|
+
* Never throws.
|
|
438
|
+
*/
|
|
439
|
+
async function resolveClaudeTranscriptPath(target, capture) {
|
|
440
|
+
try {
|
|
441
|
+
const homeDir = capture.homeDir ?? (0, os_1.homedir)();
|
|
442
|
+
if (typeof capture.worktreePath !== 'string' || capture.worktreePath.length === 0) {
|
|
443
|
+
return null;
|
|
444
|
+
}
|
|
445
|
+
return await locateClaudeTranscript(homeDir, capture, await resolveClaudeSessionId(target));
|
|
446
|
+
}
|
|
447
|
+
catch {
|
|
448
|
+
return null;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
309
451
|
/**
|
|
310
452
|
* The key for a body read with no prompt record in the window.
|
|
311
453
|
*
|
|
@@ -357,6 +499,11 @@ function collectHeadlessClaudeTurn(records, sessionId) {
|
|
|
357
499
|
promptIsOperatorInput: false,
|
|
358
500
|
blocks,
|
|
359
501
|
assistantRecords,
|
|
502
|
+
// Never handed to a writer, so neither flag can be read off it; false is the
|
|
503
|
+
// value that would refuse the write if one were ever attempted.
|
|
504
|
+
closed: false,
|
|
505
|
+
superseded: false,
|
|
506
|
+
stopReasonObserved: false,
|
|
360
507
|
overflowed,
|
|
361
508
|
};
|
|
362
509
|
}
|
|
@@ -463,8 +610,14 @@ async function readClaudeTurnProgress(target, capture) {
|
|
|
463
610
|
* agent really did reply, and #2121's behaviour for those turns is unchanged.
|
|
464
611
|
*
|
|
465
612
|
* Never throws; `recordUserTurn` reports its failures in the return value.
|
|
613
|
+
*
|
|
614
|
+
* @param previousStartedAt - When the turn before this one opened, or 0. Widens
|
|
615
|
+
* `/send` adoption backwards to that instant (Issue #2246): a prompt the agent
|
|
616
|
+
* queued for eleven minutes is still the row CommandMate wrote when it sent
|
|
617
|
+
* the text, and #2196's symmetric two-minute window cannot reach it. Never
|
|
618
|
+
* narrows — see {@link RecordUserTurnOptions}.
|
|
466
619
|
*/
|
|
467
|
-
async function recordClaudeUserTurn(target, turn) {
|
|
620
|
+
async function recordClaudeUserTurn(target, turn, previousStartedAt = 0) {
|
|
468
621
|
const instanceId = target.instanceId ?? target.cliToolId;
|
|
469
622
|
if (!turn.promptIsOperatorInput) {
|
|
470
623
|
logger.debug('claude-transcript-user-turn-skipped', {
|
|
@@ -475,7 +628,8 @@ async function recordClaudeUserTurn(target, turn) {
|
|
|
475
628
|
});
|
|
476
629
|
return { outcome: 'skipped', messageId: null, timestampMs: null };
|
|
477
630
|
}
|
|
478
|
-
const
|
|
631
|
+
const adoption = previousStartedAt > 0 ? { adoptionFromMs: previousStartedAt } : {};
|
|
632
|
+
const recorded = await (0, user_turn_recorder_1.recordUserTurn)(target, (0, agent_transcript_1.claudePromptRequestId)(turn.promptUuid), turn.promptText, turn.startedAt, adoption);
|
|
479
633
|
if (recorded.outcome === 'failed') {
|
|
480
634
|
logger.warn('claude-transcript-user-turn-failed', {
|
|
481
635
|
worktreeId: target.worktreeId,
|
|
@@ -574,6 +728,113 @@ async function readClaudeTranscriptTail(path) {
|
|
|
574
728
|
await handle?.close().catch(() => { });
|
|
575
729
|
}
|
|
576
730
|
}
|
|
731
|
+
/**
|
|
732
|
+
* How many already-written turns are re-rendered and compared (Issue #2264).
|
|
733
|
+
*
|
|
734
|
+
* The repair half of #2264 has to look at rows the reader has **already**
|
|
735
|
+
* written, which is the one thing the anchor rule deliberately does not do — so
|
|
736
|
+
* it is bounded here rather than by the window. Three, because the poller runs
|
|
737
|
+
* every two seconds and re-rendering the whole 4 MiB window on every tick would
|
|
738
|
+
* turn a repair into the most expensive thing the poll does, while a row that is
|
|
739
|
+
* going to grow grows within a second of being written.
|
|
740
|
+
*
|
|
741
|
+
* The cost of the bound is only paid by rows the old code left short: those are
|
|
742
|
+
* repaired on the next read that still has the turn among its newest three, and
|
|
743
|
+
* a session that has moved four turns past one of them keeps it. The Issue's own
|
|
744
|
+
* nine rows were all the newest turn at the time, so all nine are inside it.
|
|
745
|
+
*/
|
|
746
|
+
exports.CLAUDE_TURN_RECHECK_LIMIT = 3;
|
|
747
|
+
/**
|
|
748
|
+
* Replace a saved row whose body has since grown (Issue #2264).
|
|
749
|
+
*
|
|
750
|
+
* The second half of the fix, and the half that repairs what the first half
|
|
751
|
+
* only stops happening again. A row keyed `claude-turn:<uuid>` is written once
|
|
752
|
+
* and every later read answers "already saved" — so the nine short rows the
|
|
753
|
+
* Issue measured were frozen, and the scrape that could have replaced them was
|
|
754
|
+
* suppressed two seconds later by that same idempotency answer.
|
|
755
|
+
*
|
|
756
|
+
* **Strictly longer, never merely different.** Equality is the ordinary case and
|
|
757
|
+
* must cost nothing, and a body that got *shorter* between two reads is not a
|
|
758
|
+
* turn that grew — it is a window that slid, or a truncation marker, and
|
|
759
|
+
* overwriting a full reply with a shorter one is the one outcome worse than the
|
|
760
|
+
* bug. Longer implies different, so one comparison covers both halves of the
|
|
761
|
+
* Issue's "differs and is longer".
|
|
762
|
+
*
|
|
763
|
+
* `message_updated`, never `message`: the row already existed and was already
|
|
764
|
+
* delivered when it was created (#2195), so a client that appended instead of
|
|
765
|
+
* replacing would show the reply twice.
|
|
766
|
+
*
|
|
767
|
+
* @param existing - The row `findMessageByRequestId` answered with
|
|
768
|
+
* @returns Whether the row was replaced
|
|
769
|
+
*/
|
|
770
|
+
async function growClaudeTurnRow(target, existing, rendered, path) {
|
|
771
|
+
const instanceId = target.instanceId ?? target.cliToolId;
|
|
772
|
+
const previousLength = existing.content.length;
|
|
773
|
+
if (rendered.body.length <= previousLength)
|
|
774
|
+
return false;
|
|
775
|
+
const [{ getDbInstance }, { updateMessageContent }, { broadcastMessage }] = await Promise.all([
|
|
776
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db/db-instance'))),
|
|
777
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db'))),
|
|
778
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/ws-server'))),
|
|
779
|
+
]);
|
|
780
|
+
updateMessageContent(getDbInstance(), existing.id, rendered.body);
|
|
781
|
+
broadcastMessage('message_updated', {
|
|
782
|
+
worktreeId: target.worktreeId,
|
|
783
|
+
message: { ...existing, content: rendered.body },
|
|
784
|
+
});
|
|
785
|
+
logger.info('claude-transcript-turn-updated', {
|
|
786
|
+
worktreeId: target.worktreeId,
|
|
787
|
+
instanceId,
|
|
788
|
+
sessionId: rendered.sessionId,
|
|
789
|
+
requestId: existing.requestId,
|
|
790
|
+
path,
|
|
791
|
+
previousLength,
|
|
792
|
+
bodyLength: rendered.body.length,
|
|
793
|
+
textBlocks: rendered.textBlocks,
|
|
794
|
+
toolBlocks: rendered.toolBlocks,
|
|
795
|
+
});
|
|
796
|
+
return true;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* Re-read the newest already-written turns and grow the short ones (#2264).
|
|
800
|
+
*
|
|
801
|
+
* Runs before the pending turns are written and independently of whether there
|
|
802
|
+
* are any — the case it exists for is precisely the one
|
|
803
|
+
* {@link captureClaudeTranscriptTurn} used to return `true` from without doing
|
|
804
|
+
* anything: the newest turn already has a row, and that row is missing its last
|
|
805
|
+
* paragraph.
|
|
806
|
+
*
|
|
807
|
+
* Turns that are still open are skipped rather than compared. Their body is by
|
|
808
|
+
* definition not the final one, and a repair that raced the agent would rewrite
|
|
809
|
+
* the row on every poll of a long turn.
|
|
810
|
+
*
|
|
811
|
+
* The database is asked before the turn is rendered, so a candidate with no row
|
|
812
|
+
* — every candidate, in a session this reader has never written to — costs one
|
|
813
|
+
* indexed lookup and no Markdown.
|
|
814
|
+
*
|
|
815
|
+
* @param candidates - Already-written turns, oldest first, at most {@link CLAUDE_TURN_RECHECK_LIMIT}
|
|
816
|
+
* @returns How many rows were replaced
|
|
817
|
+
*/
|
|
818
|
+
async function refreshClaudeTurnRows(target, candidates, path) {
|
|
819
|
+
if (candidates.length === 0)
|
|
820
|
+
return 0;
|
|
821
|
+
const [{ getDbInstance }, { findMessageByRequestId }] = await Promise.all([
|
|
822
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db/db-instance'))),
|
|
823
|
+
Promise.resolve().then(() => __importStar(require('../../../../lib/db'))),
|
|
824
|
+
]);
|
|
825
|
+
const db = getDbInstance();
|
|
826
|
+
let updated = 0;
|
|
827
|
+
for (const turn of candidates) {
|
|
828
|
+
if (!(0, transcript_1.isClaudeTurnWritable)(turn))
|
|
829
|
+
continue;
|
|
830
|
+
const existing = findMessageByRequestId(db, target.worktreeId, (0, agent_transcript_1.claudeTurnRequestId)(turn.promptUuid));
|
|
831
|
+
if (!existing)
|
|
832
|
+
continue;
|
|
833
|
+
if (await growClaudeTurnRow(target, existing, (0, transcript_1.renderClaudeTurn)(turn), path))
|
|
834
|
+
updated += 1;
|
|
835
|
+
}
|
|
836
|
+
return updated;
|
|
837
|
+
}
|
|
577
838
|
/**
|
|
578
839
|
* Write one rendered turn, unless it is already there.
|
|
579
840
|
*
|
|
@@ -588,8 +849,33 @@ async function readClaudeTranscriptTail(path) {
|
|
|
588
849
|
*
|
|
589
850
|
* @returns Whether History holds this turn as the agent's own Markdown
|
|
590
851
|
*/
|
|
591
|
-
async function writeClaudeTurn(target, rendered, timestampMs, path) {
|
|
852
|
+
async function writeClaudeTurn(target, turn, rendered, timestampMs, path) {
|
|
592
853
|
const instanceId = target.instanceId ?? target.cliToolId;
|
|
854
|
+
if (!(0, transcript_1.isClaudeTurnWritable)(turn)) {
|
|
855
|
+
// The agent has not said `end_turn` for this prompt and no later prompt has
|
|
856
|
+
// taken over, so what is in the file is a turn in progress. Writing it would
|
|
857
|
+
// put a reply with its last paragraph missing into History **permanently** —
|
|
858
|
+
// the row is keyed on the prompt's `uuid`, so every later read finds it and
|
|
859
|
+
// answers "already saved". That is Issue #2264 exactly: 9 of `claude-2`'s 20
|
|
860
|
+
// turns on 2026-09-03 were saved as a bare `> **Tool calls (1)**` with not
|
|
861
|
+
// one character of prose, because the Stop hook beat the last append and the
|
|
862
|
+
// emptiness guard below cannot see the difference — a turn cut off after its
|
|
863
|
+
// tool calls renders a *non-empty* body.
|
|
864
|
+
//
|
|
865
|
+
// Handing it back to the scraper costs the Markdown rendering for this turn
|
|
866
|
+
// and nothing else: the Stop receiver asks again after a short delay, and
|
|
867
|
+
// the poller asks again when the pane returns to the composer.
|
|
868
|
+
logger.info('claude-transcript-turn-open', {
|
|
869
|
+
worktreeId: target.worktreeId,
|
|
870
|
+
instanceId,
|
|
871
|
+
sessionId: rendered.sessionId,
|
|
872
|
+
promptUuid: rendered.promptUuid,
|
|
873
|
+
assistantRecords: turn.assistantRecords,
|
|
874
|
+
textBlocks: rendered.textBlocks,
|
|
875
|
+
toolBlocks: rendered.toolBlocks,
|
|
876
|
+
});
|
|
877
|
+
return false;
|
|
878
|
+
}
|
|
593
879
|
if (rendered.body.length === 0) {
|
|
594
880
|
// The turn is open but the agent has not written anything to the file yet —
|
|
595
881
|
// the prompt record is there and the assistant records are not. Answering
|
|
@@ -619,12 +905,17 @@ async function writeClaudeTurn(target, rendered, timestampMs, path) {
|
|
|
619
905
|
Promise.resolve().then(() => __importStar(require('../../../../lib/ws-server'))),
|
|
620
906
|
]);
|
|
621
907
|
const db = getDbInstance();
|
|
622
|
-
|
|
908
|
+
const existing = findMessageByRequestId(db, target.worktreeId, requestId);
|
|
909
|
+
if (existing) {
|
|
623
910
|
logger.debug('claude-transcript-turn-already-saved', {
|
|
624
911
|
worktreeId: target.worktreeId,
|
|
625
912
|
instanceId,
|
|
626
913
|
requestId,
|
|
627
914
|
});
|
|
915
|
+
// The row may still be one of the short ones #2264 was reported for, and
|
|
916
|
+
// this is the one place that knows both the row and the turn. See
|
|
917
|
+
// {@link growClaudeTurnRow}.
|
|
918
|
+
await growClaudeTurnRow(target, existing, rendered, path);
|
|
628
919
|
return true;
|
|
629
920
|
}
|
|
630
921
|
const message = createMessage(db, {
|