commandmate 0.32.0 → 0.33.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 +159 -159
- package/.next/app-path-routes-manifest.json +28 -28
- package/.next/build-manifest.json +2 -2
- package/.next/prerender-manifest.json +12 -12
- 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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/messages/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/session/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/start/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/assistant/terminal/route_client-reference-manifest.js +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_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_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_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_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_client-reference-manifest.js +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_client-reference-manifest.js +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_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/repositories/scan/route_client-reference-manifest.js +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_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.js.nft.json +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_client-reference-manifest.js +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_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_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_client-reference-manifest.js +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_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_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_client-reference-manifest.js +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_client-reference-manifest.js +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.js.nft.json +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_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_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_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_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 +11 -11
- 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 +28 -28
- package/.next/server/chunks/1210.js +1 -1
- package/.next/server/chunks/5043.js +1 -1
- package/.next/server/chunks/5164.js +1 -1
- package/.next/server/chunks/6491.js +2 -2
- package/.next/server/chunks/6838.js +1 -1
- package/.next/server/chunks/7875.js +2 -2
- package/.next/server/chunks/8918.js +4 -4
- package/.next/server/chunks/9130.js +1 -1
- 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/{4718-0053d42fb635e700.js → 4718-d97f52cd932dc1ee.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-18a7d9374bd8c218.js +1 -0
- package/.next/static/css/{96d94a4ba771efc3.css → fb455b4e308adbaa.css} +1 -1
- package/.next/trace +13 -13
- package/README.md +6 -2
- package/dist/cli/commands/instances.d.ts.map +1 -1
- package/dist/cli/commands/instances.js +44 -15
- package/dist/cli/commands/peers.d.ts.map +1 -1
- package/dist/cli/commands/peers.js +12 -0
- package/dist/cli/commands/whoami.d.ts +67 -0
- package/dist/cli/commands/whoami.d.ts.map +1 -1
- package/dist/cli/commands/whoami.js +106 -0
- package/dist/cli/utils/api-client.d.ts +30 -1
- package/dist/cli/utils/api-client.d.ts.map +1 -1
- package/dist/cli/utils/api-client.js +28 -5
- package/dist/server/src/lib/detection/cli-patterns.js +206 -2
- package/dist/server/src/lib/hooks/agent-event-service.js +27 -5
- package/dist/server/src/lib/hooks/sources/codex/history.js +63 -3
- package/dist/server/src/lib/hooks/sources/codex/transcript.js +140 -6
- package/dist/server/src/lib/hooks/sources/launch-command.js +112 -15
- package/dist/server/src/lib/hooks/sources/turn-body.js +3 -1
- package/dist/server/src/lib/hooks/stop-history-capture.js +252 -50
- package/dist/server/src/lib/polling/response-checker.js +129 -5
- package/dist/server/src/lib/polling/response-dedup.js +136 -0
- package/dist/server/src/lib/relay/relay-delivery.js +160 -6
- package/package.json +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-06e1ff5ce2d126fd.js +0 -1
- /package/.next/static/{UIUPchtWvcEuQgcbGJv4g → eVkZd6vpcQwyJIlbymCUg}/_buildManifest.js +0 -0
- /package/.next/static/{UIUPchtWvcEuQgcbGJv4g → eVkZd6vpcQwyJIlbymCUg}/_ssgManifest.js +0 -0
|
@@ -4,13 +4,15 @@
|
|
|
4
4
|
* Shared between response-poller.ts and API routes
|
|
5
5
|
*/
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
8
|
-
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.extractAnsiSequences = exports.stripAnsi = exports.COMMAND_CODE_SKIP_PATTERNS = exports.COMMAND_CODE_HOOK_NOTICE_PATTERN = exports.COMMAND_CODE_BANNER_PATTERNS = exports.COMMAND_CODE_MODE_INDICATOR_PATTERN = exports.COMMAND_CODE_COMPLETION_PATTERN = exports.COMMAND_CODE_RESPONSE_MARKER_PATTERN = exports.COMMAND_CODE_THINKING_PATTERN = exports.COMMAND_CODE_INTERRUPT_HINT_PATTERN = exports.COMMAND_CODE_SEPARATOR_PATTERN = exports.COMMAND_CODE_PROMPT_PATTERN = exports.COMMAND_CODE_SPINNER_CHARS = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.ANTIGRAVITY_SURVEY_PATTERN = exports.ANTIGRAVITY_DIALOG_MAX_ROWS = exports.ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN = exports.ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN = exports.ANTIGRAVITY_NUMBERED_OPTION_PATTERN = exports.ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.COPILOT_SKIP_PATTERNS = exports.COPILOT_FOLDER_TRUST_ANSWER_KEY = exports.COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN = exports.COPILOT_FOLDER_TRUST_ANCHORS = exports.COPILOT_SELECTION_FOOTER_PATTERN = exports.COPILOT_BOOT_BANNER_ANCHORS = exports.COPILOT_TRANSCRIPT_CONTINUATION_PATTERN = void 0;
|
|
7
|
+
exports.COPILOT_TOOL_VERBS = exports.COPILOT_TRANSCRIPT_DIVIDER_PATTERN = exports.COPILOT_USER_ECHO_PATTERN = exports.COPILOT_REASONING_HEADER_PATTERN = exports.COPILOT_BOX_ROW_PATTERN = exports.COPILOT_SEPARATOR_PATTERN = exports.COPILOT_IDLE_STATUS_PATTERN = exports.COPILOT_WORKING_STATUS_PATTERN = exports.COPILOT_THINKING_PATTERN = exports.COPILOT_PROMPT_PATTERN = exports.OPENCODE_SKIP_PATTERNS = exports.OPENCODE_SEPARATOR_PATTERN = exports.CLAUDE_SELECTION_LIST_FOOTER = exports.CLAUDE_MODEL_OVERLAY_FOOTER_PATTERN = exports.OPENCODE_SELECTION_LIST_PATTERN = exports.OPENCODE_FOOTER_STATUS_PATTERN = exports.OPENCODE_USER_ECHO_PATTERN = exports.OPENCODE_GUTTER_ROW_PATTERN = exports.OPENCODE_COMPOSER_BOTTOM_BORDER = exports.OPENCODE_PROCESSING_INDICATOR = exports.OPENCODE_PERMISSION_PATTERN = exports.OPENCODE_TURN_COMPLETE_PATTERN = exports.OPENCODE_RESPONSE_COMPLETE = exports.OPENCODE_LOADING_PATTERN = exports.OPENCODE_THINKING_PATTERN = exports.OPENCODE_PROMPT_AFTER_RESPONSE = exports.OPENCODE_IDLE_COMPOSER_PATTERN = exports.OPENCODE_PROMPT_PATTERN = exports.GEMINI_THINKING_PATTERN = exports.GEMINI_PROMPT_PATTERN = exports.MAX_PASTED_TEXT_RETRIES = exports.PASTED_TEXT_DETECT_DELAY = exports.PASTED_TEXT_PATTERN = exports.CODEX_USER_ECHO_PATTERN = exports.CODEX_STATUS_BAR_PATTERN = exports.CODEX_PAGER_FOOTER_PATTERN = exports.CODEX_APPROVAL_FOOTER_PATTERN = exports.CODEX_SELECTION_LIST_PATTERN = exports.CODEX_SEPARATOR_PATTERN = exports.CODEX_HOOKS_REVIEW_ANCHORS = exports.CODEX_DIALOG_PATTERN = exports.CODEX_PROMPT_PATTERN = exports.CLAUDE_TRUST_DIALOG_PATTERN = exports.CLAUDE_SEPARATOR_PATTERN = exports.CLAUDE_PROMPT_PATTERN = exports.CODEX_INTERRUPT_HINT_PATTERN = exports.CODEX_THINKING_PATTERN = exports.CLAUDE_INTERRUPT_HINT_PATTERN = exports.CLAUDE_THINKING_PATTERN = exports.CLAUDE_SPINNER_CHARS = void 0;
|
|
8
|
+
exports.CLAUDE_SESSION_ERROR_REGEX_PATTERNS = exports.CLAUDE_SESSION_ERROR_PATTERNS = exports.extractAnsiSequences = exports.stripAnsi = exports.COMMAND_CODE_SKIP_PATTERNS = exports.COMMAND_CODE_HOOK_NOTICE_PATTERN = exports.COMMAND_CODE_BANNER_PATTERNS = exports.COMMAND_CODE_MODE_INDICATOR_PATTERN = exports.COMMAND_CODE_COMPLETION_PATTERN = exports.COMMAND_CODE_RESPONSE_MARKER_PATTERN = exports.COMMAND_CODE_THINKING_PATTERN = exports.COMMAND_CODE_INTERRUPT_HINT_PATTERN = exports.COMMAND_CODE_SEPARATOR_PATTERN = exports.COMMAND_CODE_PROMPT_PATTERN = exports.COMMAND_CODE_SPINNER_CHARS = exports.ANTIGRAVITY_SKIP_PATTERNS = exports.ANTIGRAVITY_SURVEY_PATTERN = exports.ANTIGRAVITY_DIALOG_MAX_ROWS = exports.ANTIGRAVITY_DIALOG_BOUNDARY_PATTERN = exports.ANTIGRAVITY_SWITCH_MODEL_HEADER_PATTERN = exports.ANTIGRAVITY_NUMBERED_OPTION_PATTERN = exports.ANTIGRAVITY_NAVIGATE_FOOTER_PATTERN = exports.ANTIGRAVITY_SELECTION_LIST_PATTERN = exports.ANTIGRAVITY_SEPARATOR_PATTERN = exports.ANTIGRAVITY_THINKING_PATTERN = exports.ANTIGRAVITY_PROMPT_PATTERN = exports.VIBE_LOCAL_THINKING_PATTERN = exports.VIBE_LOCAL_PROMPT_PATTERN = exports.COPILOT_SKIP_PATTERNS = exports.COPILOT_FOLDER_TRUST_ANSWER_KEY = exports.COPILOT_FOLDER_TRUST_SESSION_OPTION_PATTERN = exports.COPILOT_FOLDER_TRUST_ANCHORS = exports.COPILOT_SELECTION_FOOTER_PATTERN = exports.COPILOT_BOOT_BANNER_ANCHORS = exports.COPILOT_TRANSCRIPT_CONTINUATION_PATTERN = exports.COPILOT_TOOL_ROW_PATTERN = void 0;
|
|
9
9
|
exports.isCodexTurnActive = isCodexTurnActive;
|
|
10
10
|
exports.findClaudeChromeStart = findClaudeChromeStart;
|
|
11
11
|
exports.isCodexPromptReady = isCodexPromptReady;
|
|
12
12
|
exports.getCodexActiveDialog = getCodexActiveDialog;
|
|
13
13
|
exports.getCodexLifecycleDialog = getCodexLifecycleDialog;
|
|
14
|
+
exports.findCodexChromeStart = findCodexChromeStart;
|
|
15
|
+
exports.findCodexUserEchoIndex = findCodexUserEchoIndex;
|
|
14
16
|
exports.findOpenCodeChromeStart = findOpenCodeChromeStart;
|
|
15
17
|
exports.findOpenCodeUserEchoEnd = findOpenCodeUserEchoEnd;
|
|
16
18
|
exports.findOpenCodeComposerRows = findOpenCodeComposerRows;
|
|
@@ -29,6 +31,7 @@ exports.buildDetectPromptOptions = buildDetectPromptOptions;
|
|
|
29
31
|
const logger_1 = require("../../lib/logger");
|
|
30
32
|
const ansi_1 = require("./ansi");
|
|
31
33
|
const composer_text_1 = require("./composer-text");
|
|
34
|
+
const cli_patterns_1 = require("./tools/codex/cli-patterns");
|
|
32
35
|
const thinking_constants_1 = require("../../config/thinking-constants");
|
|
33
36
|
const logger = (0, logger_1.createLogger)('cli-patterns');
|
|
34
37
|
/**
|
|
@@ -589,6 +592,207 @@ exports.CODEX_PAGER_FOOTER_PATTERN = /(?:↑\/↓|pgup\/pgdn|home\/end)\s+to\s+(
|
|
|
589
592
|
* line. No nested quantifiers (ReDoS-safe; adjacent greedy quantifiers only).
|
|
590
593
|
*/
|
|
591
594
|
exports.CODEX_STATUS_BAR_PATTERN = /^\s*\S.*·\s*~?\/\S*\s*$/;
|
|
595
|
+
/**
|
|
596
|
+
* How far above the last non-blank row {@link findCodexChromeStart} looks for the
|
|
597
|
+
* composer.
|
|
598
|
+
*
|
|
599
|
+
* Same allowance and the same reason as {@link CODEX_COMPOSER_SEARCH_ROWS}: codex
|
|
600
|
+
* pins the composer two to three rows above the bottom in every measured frame
|
|
601
|
+
* (`tests/fixtures/codex-live-2310/`), and a wider band would let the search walk
|
|
602
|
+
* into the transcript and mistake the echoed user message — drawn with the same
|
|
603
|
+
* `›` — for the input box.
|
|
604
|
+
*
|
|
605
|
+
* Wider than the 8 rows of the liveness search because 0.15x can draw notices
|
|
606
|
+
* BELOW the composer (`N background terminal running · /ps to view · /stop to
|
|
607
|
+
* close`) that the liveness search never had to step over.
|
|
608
|
+
*/
|
|
609
|
+
const CODEX_CHROME_SEARCH_ROWS = 12;
|
|
610
|
+
/**
|
|
611
|
+
* A row whose first character is codex's `›` glyph, with something after it.
|
|
612
|
+
*
|
|
613
|
+
* Deliberately not {@link CODEX_PROMPT_PATTERN}: that one is multiline and
|
|
614
|
+
* matches a bare `›`, which is right for "is a prompt on screen anywhere?" and
|
|
615
|
+
* wrong for classifying ONE row. Anchored at column 0 because all three of
|
|
616
|
+
* codex's `›` uses are, and an indented `›` in a reply is quoted text.
|
|
617
|
+
*/
|
|
618
|
+
const CODEX_CHROME_GLYPH_ROW_PATTERN = /^›(\s|$)/;
|
|
619
|
+
/**
|
|
620
|
+
* Locate the start of codex's bottom-pinned chrome within a captured pane.
|
|
621
|
+
*
|
|
622
|
+
* The fifth reader of this shape, after {@link findClaudeChromeStart} (#1289),
|
|
623
|
+
* {@link findCopilotChromeStart} (#1897), {@link findOpenCodeChromeStart}
|
|
624
|
+
* (#1911) and {@link findCommandCodeChromeStart} (#2250). codex is the tool that
|
|
625
|
+
* never got one, and Issue #2400 is the bill for that.
|
|
626
|
+
*
|
|
627
|
+
* codex renders inline and pins two rows to the bottom of a settled pane:
|
|
628
|
+
*
|
|
629
|
+
* ```text
|
|
630
|
+
* › Ask Codex to do anything ← composer (placeholder or typed text)
|
|
631
|
+
*
|
|
632
|
+
* gpt-6-astra xhigh · ~/share/work/…/CommandMate ← status bar (model · cwd)
|
|
633
|
+
* ```
|
|
634
|
+
*
|
|
635
|
+
* Below the composer codex may also draw its own notices (`N background terminal
|
|
636
|
+
* running · /ps to view`), so the boundary is "the composer row" rather than a
|
|
637
|
+
* list of footer shapes: everything from the composer down is chrome by
|
|
638
|
+
* construction, whatever codex adds there next.
|
|
639
|
+
*
|
|
640
|
+
* ## What went wrong without it (#2400)
|
|
641
|
+
*
|
|
642
|
+
* While the capture window is NOT saturated codex's extraction starts at
|
|
643
|
+
* `lastCapturedLine`, so the composer only ever mattered as the `endIndex` break
|
|
644
|
+
* — which the extraction loop already had. Once the pane outgrows
|
|
645
|
+
* `CACHE_MAX_CAPTURE_LINES` (#1670) the cursor stops being a position in the
|
|
646
|
+
* capture and `resolveExtractionStartIndex` switches to the newest echoed user
|
|
647
|
+
* prompt. With no `contentEnd`, that backwards search started at the very bottom
|
|
648
|
+
* of the pane and the first `›` it met was the COMPOSER. Extraction then began
|
|
649
|
+
* on the row after it, i.e. on the status bar, and the saved "reply" for every
|
|
650
|
+
* turn on a saturated pane was one row:
|
|
651
|
+
*
|
|
652
|
+
* ```text
|
|
653
|
+
* gpt-6-astra xhigh · ~/share/work/github_kewton/CommandAgent-develop
|
|
654
|
+
* ```
|
|
655
|
+
*
|
|
656
|
+
* Identical on every turn, so `isDuplicateResponse` then locked on it and the
|
|
657
|
+
* pane could not record another reply at all. That is #1289's defect verbatim,
|
|
658
|
+
* one tool later — the same reason `findCommandCodeChromeStart` exists.
|
|
659
|
+
*
|
|
660
|
+
* ## Why the attributes and not the placeholder text
|
|
661
|
+
*
|
|
662
|
+
* The codex branch of `findRecentUserPromptIndex` used to exclude the composer by
|
|
663
|
+
* naming its placeholders (`Implement`, `Find and fix`, `Type`, `Summarize`).
|
|
664
|
+
* Those are codex 0.1x wording, and 0.15x draws `Ask Codex to do anything`, so
|
|
665
|
+
* the list silently stopped matching the thing it was written for. Issue #2310
|
|
666
|
+
* measured what actually separates codex's three uses of `›` (U+203A), and it is
|
|
667
|
+
* the SGR attributes, not the text: the composer glyph is bold (`ESC[1m›`), a
|
|
668
|
+
* transcript echo is dim (`ESC[1;2m›`), a dialog option carries a coloured glyph
|
|
669
|
+
* or a bold label. {@link readCodexGlyphRowKind} is that measurement, and this
|
|
670
|
+
* reader is one of its callers.
|
|
671
|
+
*
|
|
672
|
+
* `-1` is returned for a frame whose bottom-most `›` is an option row: codex
|
|
673
|
+
* replaces the composer with the dialog, so there is no chrome to trim and the
|
|
674
|
+
* caller resolves the frame on the prompt path instead.
|
|
675
|
+
*
|
|
676
|
+
* ## The stripped-capture fallback
|
|
677
|
+
*
|
|
678
|
+
* Auto-Yes hands the detection layer a capture that has already been through
|
|
679
|
+
* `stripAnsi`, and there every `›` is the same byte — {@link
|
|
680
|
+
* readCodexGlyphRowKind} answers `null` on purpose rather than guessing. This
|
|
681
|
+
* reader still has to answer for those frames, so it falls back to the one
|
|
682
|
+
* structural landmark codex pins BELOW the composer and nowhere else: the status
|
|
683
|
+
* bar ({@link CODEX_STATUS_BAR_PATTERN}, `model · /path`). Requiring it means a
|
|
684
|
+
* frame with no bar — a pane mid-redraw, an overlay — yields `-1` and the
|
|
685
|
+
* pre-#2400 reading, which is the direction that costs nothing.
|
|
686
|
+
*
|
|
687
|
+
* @param lines - Captured pane lines, ANSI-bearing or not; trailing blanks tolerated
|
|
688
|
+
* @returns Index of the composer row, or -1 when no composer chrome is present
|
|
689
|
+
*/
|
|
690
|
+
function findCodexChromeStart(lines) {
|
|
691
|
+
let lastRow = lines.length - 1;
|
|
692
|
+
while (lastRow >= 0 && (0, ansi_1.stripAnsi)(lines[lastRow]).trim() === '')
|
|
693
|
+
lastRow--;
|
|
694
|
+
if (lastRow < 0)
|
|
695
|
+
return -1;
|
|
696
|
+
// Bottom-most `›` row within the band codex reserves for its chrome. Anything
|
|
697
|
+
// further up is transcript, and latching onto an echo there would cut the
|
|
698
|
+
// reply this whole reader exists to keep.
|
|
699
|
+
let glyphRow = -1;
|
|
700
|
+
for (let i = lastRow; i >= Math.max(0, lastRow - CODEX_CHROME_SEARCH_ROWS); i--) {
|
|
701
|
+
if (CODEX_CHROME_GLYPH_ROW_PATTERN.test((0, ansi_1.stripAnsi)(lines[i]))) {
|
|
702
|
+
glyphRow = i;
|
|
703
|
+
break;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
if (glyphRow < 0)
|
|
707
|
+
return -1;
|
|
708
|
+
const kind = (0, cli_patterns_1.readCodexGlyphRowKind)(lines[glyphRow]);
|
|
709
|
+
if (kind === 'composer')
|
|
710
|
+
return glyphRow;
|
|
711
|
+
// A dialog is up (no composer drawn) or the bottom-most `›` is a transcript
|
|
712
|
+
// echo mid-redraw. Neither is chrome to trim.
|
|
713
|
+
if (kind !== null)
|
|
714
|
+
return -1;
|
|
715
|
+
// Stripped capture: no attributes to read. Accept the row as the composer only
|
|
716
|
+
// when codex's status bar is drawn below it, which is where it always sits and
|
|
717
|
+
// where a transcript echo can never be.
|
|
718
|
+
for (let i = glyphRow + 1; i <= lastRow; i++) {
|
|
719
|
+
if (exports.CODEX_STATUS_BAR_PATTERN.test((0, ansi_1.stripAnsi)(lines[i])))
|
|
720
|
+
return glyphRow;
|
|
721
|
+
}
|
|
722
|
+
return -1;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* The shape of a codex row that could be the echo of a message the user sent.
|
|
726
|
+
*
|
|
727
|
+
* Shape only — `›` at column 0 with text after it — which all three of codex's
|
|
728
|
+
* `›` uses share. {@link findCodexUserEchoIndex} is what tells them apart.
|
|
729
|
+
*/
|
|
730
|
+
exports.CODEX_USER_ECHO_PATTERN = /^›\s+\S/;
|
|
731
|
+
/**
|
|
732
|
+
* Find the newest transcript echo of an operator message in a codex capture.
|
|
733
|
+
*
|
|
734
|
+
* The reader `findRecentUserPromptIndex` anchors codex turns on (Issue #2400).
|
|
735
|
+
* It replaces a negative lookahead over composer placeholder strings
|
|
736
|
+
* (`(?!Implement|Find and fix|Type|Summarize)`) written against codex 0.1x:
|
|
737
|
+
* 0.15x draws `Ask Codex to do anything`, so the guard matched nothing it was
|
|
738
|
+
* written for. The composer became the newest "echo", and on a saturated pane —
|
|
739
|
+
* the one path where this anchor decides where extraction STARTS (#1670) — the
|
|
740
|
+
* reply saved for every turn was the single status-bar row below it.
|
|
741
|
+
*
|
|
742
|
+
* ## What the attributes can and cannot separate
|
|
743
|
+
*
|
|
744
|
+
* #2310 measured the three uses of `›` and this reader adds the fourth reading
|
|
745
|
+
* they left open, captured for #2400 on codex-cli 0.153.4
|
|
746
|
+
* (`tests/fixtures/codex-live-2310/turn-submitted-no-status.txt`): the echo of
|
|
747
|
+
* a message the operator has JUST submitted is drawn `ESC[1m› ESC[0m<text>` —
|
|
748
|
+
* bold glyph, plain label — and only becomes the dim `ESC[1;2m› ` of the
|
|
749
|
+
* measured frames once the turn settles. That shape is indistinguishable from a
|
|
750
|
+
* composer holding typed text, so no per-row attribute rule can separate them.
|
|
751
|
+
*
|
|
752
|
+
* What separates them is position, which codex's layout fixes: the composer is
|
|
753
|
+
* the BOTTOM-MOST `›` row of a frame. So the reader takes the boundary from its
|
|
754
|
+
* caller:
|
|
755
|
+
*
|
|
756
|
+
* - `composerTrimmed` — `findCodexChromeStart` located the chrome and `lines`
|
|
757
|
+
* has already been cut above it, so every `›` row left is transcript and the
|
|
758
|
+
* newest one wins outright.
|
|
759
|
+
* - otherwise — the composer may still be the bottom-most `›` row, so the first
|
|
760
|
+
* candidate is stepped over. This is the structural spelling of the guard the
|
|
761
|
+
* placeholder list used to be, and unlike that list it cannot go stale.
|
|
762
|
+
*
|
|
763
|
+
* Dialog option rows are refused wherever they appear: codex renders inline, so
|
|
764
|
+
* a dialog answered minutes ago is still in the scrollback with its options
|
|
765
|
+
* intact (#1160), and anchoring on one would cut the reply mid-way.
|
|
766
|
+
*
|
|
767
|
+
* @param lines - Captured pane rows, ANSI intact where the caller has it
|
|
768
|
+
* @param contentEnd - Exclusive end of the conversation region
|
|
769
|
+
* @param windowSize - How many rows above `contentEnd` to search
|
|
770
|
+
* @param composerTrimmed - Whether `contentEnd` already excludes the composer
|
|
771
|
+
* @returns Index of the newest echo row, or -1 when none is in the window
|
|
772
|
+
*/
|
|
773
|
+
function findCodexUserEchoIndex(lines, contentEnd, windowSize, composerTrimmed) {
|
|
774
|
+
let composerHandled = composerTrimmed;
|
|
775
|
+
for (let i = Math.min(contentEnd, lines.length) - 1; i >= Math.max(0, contentEnd - windowSize); i--) {
|
|
776
|
+
if (!exports.CODEX_USER_ECHO_PATTERN.test((0, ansi_1.stripAnsi)(lines[i])))
|
|
777
|
+
continue;
|
|
778
|
+
const kind = (0, cli_patterns_1.readCodexGlyphRowKind)(lines[i]);
|
|
779
|
+
// An option row of a dialog still sitting in the scrollback.
|
|
780
|
+
if (kind === 'option')
|
|
781
|
+
continue;
|
|
782
|
+
// Positively an echo: codex has settled the row and drawn its glyph dim.
|
|
783
|
+
if (kind === 'transcript-echo')
|
|
784
|
+
return i;
|
|
785
|
+
// `composer` (bold glyph) or `null` (an ANSI-stripped capture, where all
|
|
786
|
+
// three uses are one byte). Either could be the input box, and the input box
|
|
787
|
+
// is always the bottom-most `›` row.
|
|
788
|
+
if (!composerHandled) {
|
|
789
|
+
composerHandled = true;
|
|
790
|
+
continue;
|
|
791
|
+
}
|
|
792
|
+
return i;
|
|
793
|
+
}
|
|
794
|
+
return -1;
|
|
795
|
+
}
|
|
592
796
|
/**
|
|
593
797
|
* Pasted text pattern
|
|
594
798
|
*
|
|
@@ -161,16 +161,29 @@ function safeRealpath(target) {
|
|
|
161
161
|
* skip it, and after `recordAgentStopEvent` because the reader resolves its
|
|
162
162
|
* transcript through the session pointer that call refreshes.
|
|
163
163
|
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
164
|
+
* ## Exactly one read is awaited (Issue #2398)
|
|
165
|
+
*
|
|
166
|
+
* The comment that stood here said the read could be awaited freely, because
|
|
167
|
+
* "the turn has already ended, so the cost is paid by nobody who is waiting for
|
|
168
|
+
* an answer". That is true of Claude Code, which writes its transcript and then
|
|
169
|
+
* fires the hook, and false of codex, which does not append the record closing
|
|
170
|
+
* the turn until this hook's command exits — so for codex the agent *is* waiting
|
|
171
|
+
* for this answer, and the retries that were waiting for the file were waiting
|
|
172
|
+
* for themselves. Measured 2026-09-07: 105 codex stop events, 0 captured, 1 s of
|
|
173
|
+
* the agent's stop path spent on each.
|
|
174
|
+
*
|
|
175
|
+
* So the reader is asked once here, and anything further happens after this
|
|
176
|
+
* function has returned — detached, and observed through the log rather than
|
|
177
|
+
* through the return value, which is what
|
|
178
|
+
* {@link AgentStopOutcome.structuredHistoryOutcome} exists to say.
|
|
167
179
|
*
|
|
168
180
|
* Never throws. A hook is fire-and-forget from a CLI's stop handler, and a
|
|
169
181
|
* failure here must not become the agent's problem.
|
|
170
182
|
*/
|
|
171
183
|
async function applyAgentStopEvent(db, worktree, cliToolId, instanceId) {
|
|
172
184
|
(0, agent_event_state_1.recordAgentStopEvent)(worktree.id, cliToolId, instanceId);
|
|
173
|
-
const
|
|
185
|
+
const { status: structuredHistoryOutcome } = await (0, stop_history_capture_1.resolveStopTranscriptCapture)(worktree, cliToolId, instanceId);
|
|
186
|
+
const structuredHistoryCaptured = structuredHistoryOutcome === 'captured';
|
|
174
187
|
let task;
|
|
175
188
|
try {
|
|
176
189
|
task = (0, tasks_db_1.getActiveTaskForInstance)(db, worktree.id, cliToolId, instanceId);
|
|
@@ -185,6 +198,7 @@ async function applyAgentStopEvent(db, worktree, cliToolId, instanceId) {
|
|
|
185
198
|
taskEventApplied: false,
|
|
186
199
|
verificationRunId: null,
|
|
187
200
|
structuredHistoryCaptured,
|
|
201
|
+
structuredHistoryOutcome,
|
|
188
202
|
};
|
|
189
203
|
}
|
|
190
204
|
if (!task) {
|
|
@@ -193,6 +207,7 @@ async function applyAgentStopEvent(db, worktree, cliToolId, instanceId) {
|
|
|
193
207
|
taskEventApplied: false,
|
|
194
208
|
verificationRunId: null,
|
|
195
209
|
structuredHistoryCaptured,
|
|
210
|
+
structuredHistoryOutcome,
|
|
196
211
|
};
|
|
197
212
|
}
|
|
198
213
|
let taskEventApplied = false;
|
|
@@ -212,6 +227,7 @@ async function applyAgentStopEvent(db, worktree, cliToolId, instanceId) {
|
|
|
212
227
|
taskEventApplied,
|
|
213
228
|
verificationRunId: null,
|
|
214
229
|
structuredHistoryCaptured,
|
|
230
|
+
structuredHistoryOutcome,
|
|
215
231
|
};
|
|
216
232
|
}
|
|
217
233
|
let verificationRunId = null;
|
|
@@ -235,5 +251,11 @@ async function applyAgentStopEvent(db, worktree, cliToolId, instanceId) {
|
|
|
235
251
|
});
|
|
236
252
|
}
|
|
237
253
|
}
|
|
238
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
taskId: task.id,
|
|
256
|
+
taskEventApplied,
|
|
257
|
+
verificationRunId,
|
|
258
|
+
structuredHistoryCaptured,
|
|
259
|
+
structuredHistoryOutcome,
|
|
260
|
+
};
|
|
239
261
|
}
|
|
@@ -323,6 +323,13 @@ async function scanForRollout(dir, sessionId, depth) {
|
|
|
323
323
|
* attach` is the thing #2196 exists to record, and it is worth recording next to
|
|
324
324
|
* a *scraped* reply just as much as next to a Markdown one.
|
|
325
325
|
*
|
|
326
|
+
* A turn whose `task_started` fell outside the window is written too, behind
|
|
327
|
+
* `CODEX_TURN_HEAD_TRUNCATION_MARKER` and beside a
|
|
328
|
+
* `codex-transcript-turn-headless` report (Issue #2402). See `./transcript`'s
|
|
329
|
+
* module docblock for why codex may do that where `../claude/history` may not,
|
|
330
|
+
* and `docs/design/codex-transcript-reader.md` §4.5 for why the prompts that
|
|
331
|
+
* went out of the window with it are left unadopted rather than guessed at.
|
|
332
|
+
*
|
|
326
333
|
* Never throws.
|
|
327
334
|
*
|
|
328
335
|
* @param target - The instance whose turn just ended
|
|
@@ -411,11 +418,17 @@ async function captureCodexTranscriptTurn(target, capture = {}) {
|
|
|
411
418
|
const previousStartedAt = index === 0 ? pending.previousStartedAt : pending.turns[index - 1].startedAt;
|
|
412
419
|
userRows.push(await recordCodexUserTurns(target, pending.turns[index], previousStartedAt));
|
|
413
420
|
}
|
|
421
|
+
const windowFacts = {
|
|
422
|
+
path,
|
|
423
|
+
firstRecordAt: firstCodexRecordAt(parsed.records),
|
|
424
|
+
turnlessRecords: built.turnlessRecords,
|
|
425
|
+
malformedLines: parsed.malformedLines,
|
|
426
|
+
};
|
|
414
427
|
const lastRecordAt = lastCodexRecordAt(parsed.records);
|
|
415
428
|
let captured = false;
|
|
416
429
|
for (let index = 0; index < pending.turns.length; index += 1) {
|
|
417
430
|
const turn = pending.turns[index];
|
|
418
|
-
captured = await writeCodexTurn(target, turn, (0, transcript_1.renderCodexTurn)(turn), resolveAssistantTimestampMs(turn, userRows[index], lastRecordAt.get(turn.turnId) ?? 0, nextTurnOpensAt(pending.turns, userRows, index)),
|
|
431
|
+
captured = await writeCodexTurn(target, turn, (0, transcript_1.renderCodexTurn)(turn), resolveAssistantTimestampMs(turn, userRows[index], lastRecordAt.get(turn.turnId) ?? 0, nextTurnOpensAt(pending.turns, userRows, index)), windowFacts);
|
|
419
432
|
}
|
|
420
433
|
return captured;
|
|
421
434
|
}
|
|
@@ -448,6 +461,21 @@ async function resolveCodexTranscriptPath(target, capture = {}) {
|
|
|
448
461
|
return null;
|
|
449
462
|
}
|
|
450
463
|
}
|
|
464
|
+
/**
|
|
465
|
+
* When the window's first timestamped record was written (Issue #2402).
|
|
466
|
+
*
|
|
467
|
+
* The floor on "everything this read could possibly have seen". For a headless
|
|
468
|
+
* turn it is also the only honest thing to say about where the missing head
|
|
469
|
+
* ends: the turn began before this instant, and by how much is not in the file
|
|
470
|
+
* this read opened.
|
|
471
|
+
*/
|
|
472
|
+
function firstCodexRecordAt(records) {
|
|
473
|
+
for (const record of records) {
|
|
474
|
+
if (record.timestampMs !== null)
|
|
475
|
+
return record.timestampMs;
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
451
479
|
/**
|
|
452
480
|
* The turns {@link captureCodexTranscriptTurn} still has to write (Issue #2246).
|
|
453
481
|
*
|
|
@@ -642,7 +670,7 @@ function nextTurnOpensAt(turns, userRows, index) {
|
|
|
642
670
|
*
|
|
643
671
|
* @returns Whether History holds this turn as the agent's own Markdown
|
|
644
672
|
*/
|
|
645
|
-
async function writeCodexTurn(target, turn, rendered, timestampMs,
|
|
673
|
+
async function writeCodexTurn(target, turn, rendered, timestampMs, windowFacts) {
|
|
646
674
|
const instanceId = target.instanceId ?? target.cliToolId;
|
|
647
675
|
if (!turn.closed) {
|
|
648
676
|
// codex has not written the `task_complete` for this `turn_id` yet, so what
|
|
@@ -706,6 +734,38 @@ async function writeCodexTurn(target, turn, rendered, timestampMs, path) {
|
|
|
706
734
|
});
|
|
707
735
|
return true;
|
|
708
736
|
}
|
|
737
|
+
if (rendered.headless) {
|
|
738
|
+
// Issue #2402. The turn's `task_started` is outside the window, so this row
|
|
739
|
+
// carries `CODEX_TURN_HEAD_TRUNCATION_MARKER` and the operator can see that
|
|
740
|
+
// — but the operator cannot see WHY, and neither can anyone reading the logs
|
|
741
|
+
// afterwards. Everything below is measured: where the window opens, what it
|
|
742
|
+
// held that no turn claimed, and how many of the turn's prompts were inside
|
|
743
|
+
// it. How much fell out is deliberately NOT here. It is not knowable from a
|
|
744
|
+
// read that never saw those bytes, and a guess would be worse than silence.
|
|
745
|
+
//
|
|
746
|
+
// `promptsInWindow` is the one to read when a prompt is missing from the
|
|
747
|
+
// conversation: codex folds a prompt submitted mid-turn into that same turn
|
|
748
|
+
// (23 of 326 archived turns carry more than one), so a headless turn's
|
|
749
|
+
// earlier prompts can be outside the window too — and unlike the body, those
|
|
750
|
+
// are NOT recoverable. `recordCodexUserTurns` only ever sees `turn.prompts`,
|
|
751
|
+
// which is what the window held; a `/send` row for a prompt written before
|
|
752
|
+
// the window stays unkeyed. Adopting it would mean guessing which unkeyed
|
|
753
|
+
// row belongs to this turn with no text to match on — see
|
|
754
|
+
// `docs/design/codex-transcript-reader.md` §4.5.
|
|
755
|
+
logger.info('codex-transcript-turn-headless', {
|
|
756
|
+
worktreeId: target.worktreeId,
|
|
757
|
+
instanceId,
|
|
758
|
+
sessionId: rendered.sessionId,
|
|
759
|
+
turnId: rendered.turnId,
|
|
760
|
+
path: windowFacts.path,
|
|
761
|
+
windowBytes: exports.CODEX_TRANSCRIPT_TAIL_BYTES,
|
|
762
|
+
windowFirstRecordAt: windowFacts.firstRecordAt,
|
|
763
|
+
turnlessRecords: windowFacts.turnlessRecords,
|
|
764
|
+
malformedLines: windowFacts.malformedLines,
|
|
765
|
+
promptsInWindow: turn.prompts.length,
|
|
766
|
+
itemsInWindow: turn.items.length,
|
|
767
|
+
});
|
|
768
|
+
}
|
|
709
769
|
const message = createMessage(db, {
|
|
710
770
|
worktreeId: target.worktreeId,
|
|
711
771
|
role: 'assistant',
|
|
@@ -722,7 +782,7 @@ async function writeCodexTurn(target, turn, rendered, timestampMs, path) {
|
|
|
722
782
|
instanceId,
|
|
723
783
|
sessionId: rendered.sessionId,
|
|
724
784
|
requestId,
|
|
725
|
-
path,
|
|
785
|
+
path: windowFacts.path,
|
|
726
786
|
bodyLength: rendered.body.length,
|
|
727
787
|
textBlocks: rendered.textBlocks,
|
|
728
788
|
toolBlocks: rendered.toolBlocks,
|
|
@@ -53,6 +53,34 @@
|
|
|
53
53
|
* boundary is a field, and a turn is closed by the `task_complete` that carries
|
|
54
54
|
* its id — measured on 326 of 326 archived turns.
|
|
55
55
|
*
|
|
56
|
+
* ## A turn can begin outside the window (Issue #2402)
|
|
57
|
+
*
|
|
58
|
+
* `./history` reads the rollout's last `CODEX_TRANSCRIPT_TAIL_BYTES` and nothing
|
|
59
|
+
* more, because the largest rollout on this machine was 273 MB. A long turn
|
|
60
|
+
* outgrows that window: measured on 2026-09-07 on a 30 MB / 7,408-line rollout
|
|
61
|
+
* whose newest turn ran seven hours over 387 items and four compactions, so its
|
|
62
|
+
* `task_started` and its first two prompts sat outside the 4 MiB tail while its
|
|
63
|
+
* later items sat inside it.
|
|
64
|
+
*
|
|
65
|
+
* The `turn_id` stamp is what makes that recoverable. Because it is on **every**
|
|
66
|
+
* `item_completed` and not only on `task_started`, a window that opens in the
|
|
67
|
+
* middle of a turn still reads the turn's real id — so the turn can be rendered
|
|
68
|
+
* AND written under the key a later read will recognise. What is lost is the
|
|
69
|
+
* head of the body, and {@link CodexTurnAccumulator.started} is how that becomes
|
|
70
|
+
* a fact the writer can state: it is the opening mark, exactly as `closed` is
|
|
71
|
+
* the closing one, and a turn with `started: false` is rendered behind
|
|
72
|
+
* {@link CODEX_TURN_HEAD_TRUNCATION_MARKER}.
|
|
73
|
+
*
|
|
74
|
+
* **This is where codex and claude diverge, and the divergence is deliberate.**
|
|
75
|
+
* `../claude/history`'s `collectHeadlessClaudeTurn` renders the same situation
|
|
76
|
+
* and refuses to write it, because claude's transcript carries no link from a
|
|
77
|
+
* reply to the prompt it answers: a turn whose prompt record is outside the
|
|
78
|
+
* window has no key at all, so writing it would mean inventing one, and an
|
|
79
|
+
* invented key is a row no later run can recognise as already written. codex is
|
|
80
|
+
* not under that constraint — the key is a field on the records that ARE in the
|
|
81
|
+
* window — so the argument that stops claude does not reach here. Copying
|
|
82
|
+
* claude's refusal would drop a body codex handed over for free.
|
|
83
|
+
*
|
|
56
84
|
* ## Pure on purpose
|
|
57
85
|
*
|
|
58
86
|
* No filesystem, no database, no `globalThis` — `./history` owns all three, the
|
|
@@ -63,7 +91,7 @@
|
|
|
63
91
|
* @module lib/hooks/sources/codex/transcript
|
|
64
92
|
*/
|
|
65
93
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
66
|
-
exports.CODEX_THINKING_LABEL = exports.MAX_CODEX_TOOL_DETAIL_LENGTH = exports.MAX_CODEX_TURN_ITEMS = exports.CODEX_TURN_TRUNCATION_MARKER = exports.MAX_CODEX_TURN_BODY_LENGTH = exports.CODEX_ROLLOUT_EXTENSION = exports.CODEX_SESSIONS_DIR_SEGMENTS = void 0;
|
|
94
|
+
exports.CODEX_COMMENTARY_PHASE = exports.CODEX_FINAL_ANSWER_PHASE = exports.CODEX_THINKING_LABEL = exports.MAX_CODEX_TOOL_DETAIL_LENGTH = exports.MAX_CODEX_TURN_ITEMS = exports.CODEX_TURN_HEAD_TRUNCATION_MARKER = exports.CODEX_TURN_TRUNCATION_MARKER = exports.MAX_CODEX_TURN_BODY_LENGTH = exports.CODEX_ROLLOUT_EXTENSION = exports.CODEX_SESSIONS_DIR_SEGMENTS = void 0;
|
|
67
95
|
exports.readCodexRolloutItem = readCodexRolloutItem;
|
|
68
96
|
exports.readCodexRolloutRecord = readCodexRolloutRecord;
|
|
69
97
|
exports.parseCodexRollout = parseCodexRollout;
|
|
@@ -79,6 +107,17 @@ exports.CODEX_ROLLOUT_EXTENSION = '.jsonl';
|
|
|
79
107
|
exports.MAX_CODEX_TURN_BODY_LENGTH = 200_000;
|
|
80
108
|
/** Appended when {@link MAX_CODEX_TURN_BODY_LENGTH} truncates a turn. */
|
|
81
109
|
exports.CODEX_TURN_TRUNCATION_MARKER = '\n\n_(truncated)_';
|
|
110
|
+
/**
|
|
111
|
+
* Prepended when a turn's opening records fell outside the window (#2402).
|
|
112
|
+
*
|
|
113
|
+
* The mirror of {@link CODEX_TURN_TRUNCATION_MARKER} — same italic parenthetical
|
|
114
|
+
* in the same plain Markdown, on the other end of the body — because the two say
|
|
115
|
+
* the same thing about the same row and a reader who has learned to read one
|
|
116
|
+
* should not have to learn a second notation for the other. Written into
|
|
117
|
+
* `chat_messages.content` at read time like every other string these readers
|
|
118
|
+
* emit, so it is English and outlives any later locale change.
|
|
119
|
+
*/
|
|
120
|
+
exports.CODEX_TURN_HEAD_TRUNCATION_MARKER = '_(head truncated)_\n\n';
|
|
82
121
|
/**
|
|
83
122
|
* Cap on items kept for one turn.
|
|
84
123
|
*
|
|
@@ -91,6 +130,21 @@ exports.MAX_CODEX_TURN_ITEMS = 2048;
|
|
|
91
130
|
exports.MAX_CODEX_TOOL_DETAIL_LENGTH = 200;
|
|
92
131
|
/** The label a reasoning summary is folded behind. Same word claude uses. */
|
|
93
132
|
exports.CODEX_THINKING_LABEL = 'Thinking';
|
|
133
|
+
/**
|
|
134
|
+
* `AgentMessage.phase` for the reply codex means the operator to read.
|
|
135
|
+
*
|
|
136
|
+
* Measured 268 of 2,211 archived `AgentMessage` items
|
|
137
|
+
* (`docs/design/codex-transcript-reader.md` §2.3).
|
|
138
|
+
*/
|
|
139
|
+
exports.CODEX_FINAL_ANSWER_PHASE = 'final_answer';
|
|
140
|
+
/**
|
|
141
|
+
* `AgentMessage.phase` for the progress narration codex writes while it works.
|
|
142
|
+
*
|
|
143
|
+
* Measured 1,943 of 2,211 — the majority of what codex says in a turn is this.
|
|
144
|
+
* The one phase {@link renderCodexTurn} folds; see its docblock for why the
|
|
145
|
+
* classification is an allow-list of exactly this value and nothing else.
|
|
146
|
+
*/
|
|
147
|
+
exports.CODEX_COMMENTARY_PHASE = 'commentary';
|
|
94
148
|
/**
|
|
95
149
|
* Read one `item_completed` item.
|
|
96
150
|
*
|
|
@@ -322,11 +376,19 @@ function buildCodexTurns(records, sessionId) {
|
|
|
322
376
|
startedAt: record.timestampMs ?? 0,
|
|
323
377
|
prompts: [],
|
|
324
378
|
items: [],
|
|
379
|
+
started: false,
|
|
325
380
|
closed: false,
|
|
326
381
|
overflowed: false,
|
|
327
382
|
};
|
|
328
383
|
turns.set(turnId, turn);
|
|
329
384
|
}
|
|
385
|
+
if (record.payloadType === 'task_started') {
|
|
386
|
+
// The opening mark (Issue #2402). Recorded rather than assumed from "this
|
|
387
|
+
// is the first record we saw for the id", which is true of every turn in
|
|
388
|
+
// the window — including the one whose real first record is outside it.
|
|
389
|
+
turn.started = true;
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
330
392
|
if (record.payloadType === 'task_complete') {
|
|
331
393
|
turn.closed = true;
|
|
332
394
|
continue;
|
|
@@ -390,14 +452,79 @@ function renderReasoningItem(text) {
|
|
|
390
452
|
.join('\n');
|
|
391
453
|
return `> **${exports.CODEX_THINKING_LABEL}**\n>\n${quoted}`;
|
|
392
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* The prose one `AgentMessage` contributes, or `''` when it contributes none.
|
|
457
|
+
*
|
|
458
|
+
* The single definition of "non-empty body" for this reader, shared by
|
|
459
|
+
* {@link hasFinalAnswerProse} and by {@link renderCodexTurn}'s loop on purpose:
|
|
460
|
+
* the fallback asks whether the turn renders an answer, and if the two spelled
|
|
461
|
+
* that test out separately they could disagree about a message that is nothing
|
|
462
|
+
* but whitespace — the exact turn the fallback exists for.
|
|
463
|
+
*/
|
|
464
|
+
function agentMessageText(item) {
|
|
465
|
+
return item.text?.trim() ?? '';
|
|
466
|
+
}
|
|
467
|
+
/**
|
|
468
|
+
* Whether this turn renders at least one non-empty `final_answer`.
|
|
469
|
+
*
|
|
470
|
+
* The test is on the **rendered body**, not on the presence of an item: item
|
|
471
|
+
* count and drawn paragraphs are different quantities, because a message whose
|
|
472
|
+
* text is empty or whitespace is dropped before it reaches the body. A turn
|
|
473
|
+
* whose only `final_answer` is blank would, on an item-count test, fold its
|
|
474
|
+
* commentary away and leave an empty bubble.
|
|
475
|
+
*/
|
|
476
|
+
function hasFinalAnswerProse(turn) {
|
|
477
|
+
return turn.items.some((item) => item.type === 'AgentMessage' &&
|
|
478
|
+
item.phase === exports.CODEX_FINAL_ANSWER_PHASE &&
|
|
479
|
+
agentMessageText(item).length > 0);
|
|
480
|
+
}
|
|
393
481
|
/**
|
|
394
482
|
* Render one turn to Markdown.
|
|
395
483
|
*
|
|
396
484
|
* Transcript order within each kind — the same decision #2041 and #2121 took,
|
|
397
485
|
* and for the same reason: the order is the only record of what happened when,
|
|
398
|
-
* and this row is the record.
|
|
399
|
-
*
|
|
400
|
-
*
|
|
486
|
+
* and this row is the record.
|
|
487
|
+
*
|
|
488
|
+
* ## Why `commentary` is folded now (Issue #2420)
|
|
489
|
+
*
|
|
490
|
+
* #2197 kept `commentary` in the body on the argument that "the sentence that
|
|
491
|
+
* explains what the tool line underneath it is for" would otherwise be lost.
|
|
492
|
+
* **#2234 retired that argument by moving every tool line to a folded section
|
|
493
|
+
* at the end of the body** — there is no line underneath the commentary any
|
|
494
|
+
* more, so what leads the bubble is narration with nothing left to narrate.
|
|
495
|
+
* Measured on one live session (`codex-turn:01a07e37-0658`): 4 commentary
|
|
496
|
+
* blocks / 754 characters in front of a single 495-character answer, so the
|
|
497
|
+
* operator read four paragraphs of progress notes before reaching the reply.
|
|
498
|
+
*
|
|
499
|
+
* ## The classification is an allow-list
|
|
500
|
+
*
|
|
501
|
+
* Exactly `phase === 'commentary'` folds. `final_answer`, a missing `phase` and
|
|
502
|
+
* any value codex has not shipped yet all stay in the body. The deny-list
|
|
503
|
+
* spelling — "fold everything that is not `final_answer`" — is rejected on
|
|
504
|
+
* #2196's discipline: it hands codex the power to hide the body of a bubble by
|
|
505
|
+
* adding a phase name, and the failure would be silent. Every `final_answer` is
|
|
506
|
+
* kept, not just the last: the measured corpus has turns carrying more than one.
|
|
507
|
+
*
|
|
508
|
+
* ## The fallback
|
|
509
|
+
*
|
|
510
|
+
* A turn with no non-empty `final_answer` keeps its commentary in the body —
|
|
511
|
+
* see {@link hasFinalAnswerProse}. Folding is only ever an improvement when
|
|
512
|
+
* there is something better to lead with.
|
|
513
|
+
*
|
|
514
|
+
* ## Why the `Thinking` chip and not a new label
|
|
515
|
+
*
|
|
516
|
+
* The folded commentary rides `../turn-body`'s existing `reasoning` kind, so it
|
|
517
|
+
* lands under the `Thinking (N)` heading the other readers already write, and
|
|
518
|
+
* `splitChatThinking` in `components/worktree/ChatMessageBubble` draws it in
|
|
519
|
+
* the chip that is already there. A second label (`Progress`) would read more
|
|
520
|
+
* precisely, and it was rejected on cost: these labels are baked into
|
|
521
|
+
* `chat_messages.content` **at read time**, so a new one is a string every
|
|
522
|
+
* future reader of the table has to keep folding forever, for rows nothing can
|
|
523
|
+
* re-label. Against that, the chip already means the right thing — "subordinate
|
|
524
|
+
* narration the reader may want and does not want first" — and the operator who
|
|
525
|
+
* reported #2420 called this content "thinking" in their own words. Same
|
|
526
|
+
* reasoning as {@link CODEX_THINKING_LABEL}, which is `Thinking` for the same
|
|
527
|
+
* reason.
|
|
401
528
|
*
|
|
402
529
|
* The layout — prose first, the calls folded into one labelled section — is
|
|
403
530
|
* `../turn-body`'s and is shared with the other three readers (#2234).
|
|
@@ -407,12 +534,14 @@ function renderCodexTurn(turn) {
|
|
|
407
534
|
const unknown = new Set();
|
|
408
535
|
let textBlocks = 0;
|
|
409
536
|
let toolBlocks = 0;
|
|
537
|
+
const foldsCommentary = hasFinalAnswerProse(turn);
|
|
410
538
|
for (const item of turn.items) {
|
|
411
539
|
if (item.type === 'AgentMessage') {
|
|
412
|
-
const text = item
|
|
540
|
+
const text = agentMessageText(item);
|
|
413
541
|
if (text.length === 0)
|
|
414
542
|
continue;
|
|
415
|
-
|
|
543
|
+
const folds = foldsCommentary && item.phase === exports.CODEX_COMMENTARY_PHASE;
|
|
544
|
+
rendered.push({ kind: folds ? 'reasoning' : 'prose', text });
|
|
416
545
|
textBlocks += 1;
|
|
417
546
|
continue;
|
|
418
547
|
}
|
|
@@ -432,6 +561,10 @@ function renderCodexTurn(turn) {
|
|
|
432
561
|
unknown.add(item.type);
|
|
433
562
|
}
|
|
434
563
|
let body = (0, turn_body_1.separateTurnBody)(rendered).body;
|
|
564
|
+
// The head mark goes on before the length bound, so a headless turn long
|
|
565
|
+
// enough to be truncated at both ends still says so at both ends.
|
|
566
|
+
if (!turn.started && body.length > 0)
|
|
567
|
+
body = exports.CODEX_TURN_HEAD_TRUNCATION_MARKER + body;
|
|
435
568
|
if (body.length > exports.MAX_CODEX_TURN_BODY_LENGTH) {
|
|
436
569
|
body =
|
|
437
570
|
body.slice(0, exports.MAX_CODEX_TURN_BODY_LENGTH - exports.CODEX_TURN_TRUNCATION_MARKER.length) +
|
|
@@ -441,6 +574,7 @@ function renderCodexTurn(turn) {
|
|
|
441
574
|
sessionId: turn.sessionId,
|
|
442
575
|
turnId: turn.turnId,
|
|
443
576
|
body,
|
|
577
|
+
headless: !turn.started,
|
|
444
578
|
textBlocks,
|
|
445
579
|
toolBlocks,
|
|
446
580
|
unknownBlockTypes: [...unknown],
|