@jingyi0605/codingns 0.7.4 → 0.8.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/README.md +6 -0
- package/bin/codingns.mjs +747 -28
- package/bin/office-mcp-server.mjs +620 -0
- package/dist/public/assets/{AdaptiveButlerPage-CgBX49t-.js → AdaptiveButlerPage-BsgVNRAa.js} +1 -1
- package/dist/public/assets/{App-tXOqoHNl.js → App-tPcbyRdS.js} +3 -3
- package/dist/public/assets/{BootstrapPage-DoRMz87R.js → BootstrapPage--MDOigQi.js} +1 -1
- package/dist/public/assets/{ConversationPage-DXR6Hp4O.js → ConversationPage-BBss5ED8.js} +6 -6
- package/dist/public/assets/{DesktopDetachPreviewPage-Cyk-ZYCk.js → DesktopDetachPreviewPage-CB8DoqwU.js} +1 -1
- package/dist/public/assets/{DesktopWindowPage-DNVNK3hs.js → DesktopWindowPage-GtIx5m8K.js} +1 -1
- package/dist/public/assets/FileContextPanel-BcM7AIT4.js +1 -0
- package/dist/public/assets/{GitSidebar-Cr3Z9OUI.js → GitSidebar-CMtkaxuI.js} +2 -2
- package/dist/public/assets/{MobileCreateSessionSheet-DMW0V6GJ.js → MobileCreateSessionSheet-CrFY41_8.js} +1 -1
- package/dist/public/assets/{MobileTopHeaderFrame-CkdnZ_MU.js → MobileTopHeaderFrame-DGVOzXyg.js} +1 -1
- package/dist/public/assets/{MobileWorkspaceSwitcherHeader-KIbqBYJN.js → MobileWorkspaceSwitcherHeader-DLkACTnQ.js} +1 -1
- package/dist/public/assets/{RelayConnectEntryPage-DIRBH3hw.js → RelayConnectEntryPage-0MPPjxtQ.js} +1 -1
- package/dist/public/assets/{ServerSettingsModal-C-9RxdWP.js → ServerSettingsModal-vgOhwus4.js} +1 -1
- package/dist/public/assets/{SessionIndexPage-bRlIydRA.js → SessionIndexPage-KK626Ra9.js} +1 -1
- package/dist/public/assets/{SettingsPage-CMEt4ua9.js → SettingsPage-B3edBJIo.js} +2 -2
- package/dist/public/assets/{TerminalManagerPanel-2bi9wVhT.js → TerminalManagerPanel-BxlhZp8c.js} +1 -1
- package/dist/public/assets/{TerminalPage-DayZz2Tf.js → TerminalPage-B6Rdhylx.js} +1 -1
- package/dist/public/assets/{TerminalRuntimeFallbackModal-DgwYcp-Y.js → TerminalRuntimeFallbackModal-BVLfrpSa.js} +1 -1
- package/dist/public/assets/{ToolFilesPage-YvnP_FXW.js → ToolFilesPage-N_gwwUjD.js} +1 -1
- package/dist/public/assets/{ToolGitPage-GMcQKtV9.js → ToolGitPage-DOcuuWM1.js} +1 -1
- package/dist/public/assets/{ToolProcessesPage-DFIQ7BCd.js → ToolProcessesPage-D-FfJ7Re.js} +1 -1
- package/dist/public/assets/{ToolsHomePage-CSilFzXR.js → ToolsHomePage-CHfPxd20.js} +1 -1
- package/dist/public/assets/{WorkbenchLandingPage-1VtToSz9.js → WorkbenchLandingPage-CTTnfovY.js} +1 -1
- package/dist/public/assets/WorkbenchLayout-CbpJg0g1.js +244 -0
- package/dist/public/assets/{WorkbenchModal-BWXYSXmC.js → WorkbenchModal-Bt_1fYmM.js} +1 -1
- package/dist/public/assets/WorkbenchShellRoute-B4XB8SwG.css +1 -0
- package/dist/public/assets/WorkbenchShellRoute-DyaMnPfS.js +1 -0
- package/dist/public/assets/{WorkspaceDebugDetailPage-Ux8_Q7la.js → WorkspaceDebugDetailPage-s7yuDIxR.js} +1 -1
- package/dist/public/assets/{WorkspaceDetailPage-B402p99m.js → WorkspaceDetailPage-Cf-gVpqK.js} +1 -1
- package/dist/public/assets/{WorkspaceHomePage-D2pob6HI.js → WorkspaceHomePage-COf6I8sT.js} +1 -1
- package/dist/public/assets/{client-runtime-manager-C5D76ewj.js → client-runtime-manager-DGdKvYzx.js} +1 -1
- package/dist/public/assets/file-tree-icon-BeHqeru9.js +590 -0
- package/dist/public/assets/index-CcaQt50x.css +1 -0
- package/dist/public/assets/index-CuzMc7q2.js +42 -0
- package/dist/public/assets/{login-direct-candidate-resolver-wXSaB0i7.js → login-direct-candidate-resolver-DEP_xCmR.js} +1 -1
- package/dist/public/assets/{model-switch-api-CPtou49j.js → model-switch-api-c6kcbBGm.js} +1 -1
- package/dist/public/assets/{preferences-service-CdaK7zA8.js → preferences-service-CV6Ih0BG.js} +1 -1
- package/dist/public/assets/{realtime-client-BjQazYsK.js → realtime-client-CRCx5xBt.js} +1 -1
- package/dist/public/assets/{relay-entry-BwE5nw0l.js → relay-entry-C751A-Sm.js} +1 -1
- package/dist/public/assets/{terminal-runtime-meta-C-Lbyx2i.js → terminal-runtime-meta-CRAVR-8G.js} +1 -1
- package/dist/public/assets/{useRegisteredDebugTemplates-BM7-c-gx.js → useRegisteredDebugTemplates-D6YtNS0r.js} +1 -1
- package/dist/public/index.html +2 -2
- package/dist/server/config/env.d.ts +3 -0
- package/dist/server/config/env.js +67 -1
- package/dist/server/config/env.js.map +1 -1
- package/dist/server/config/opencode-base-url-resolver.d.ts +3 -2
- package/dist/server/config/opencode-base-url-resolver.js +64 -24
- package/dist/server/config/opencode-base-url-resolver.js.map +1 -1
- package/dist/server/middlewares/auth-guard.js +4 -0
- package/dist/server/middlewares/auth-guard.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-controller.d.ts +168 -1
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js +205 -4
- package/dist/server/modules/assistant-capability/assistant-capability-controller.js.map +1 -1
- package/dist/server/modules/assistant-capability/assistant-capability-service.d.ts +296 -2
- package/dist/server/modules/assistant-capability/assistant-capability-service.js +872 -3
- package/dist/server/modules/assistant-capability/assistant-capability-service.js.map +1 -1
- package/dist/server/modules/auth/auth-service.d.ts +21 -1
- package/dist/server/modules/auth/auth-service.js +64 -0
- package/dist/server/modules/auth/auth-service.js.map +1 -1
- package/dist/server/modules/browser-runtime/browser-profile-service.d.ts +26 -0
- package/dist/server/modules/browser-runtime/browser-profile-service.js +85 -0
- package/dist/server/modules/browser-runtime/browser-profile-service.js.map +1 -0
- package/dist/server/modules/browser-runtime/browser-runtime-controller.d.ts +69 -0
- package/dist/server/modules/browser-runtime/browser-runtime-controller.js +83 -0
- package/dist/server/modules/browser-runtime/browser-runtime-controller.js.map +1 -0
- package/dist/server/modules/browser-runtime/browser-runtime-service.d.ts +56 -0
- package/dist/server/modules/browser-runtime/browser-runtime-service.js +215 -0
- package/dist/server/modules/browser-runtime/browser-runtime-service.js.map +1 -0
- package/dist/server/modules/browser-runtime/browser-task-execution-support.d.ts +65 -0
- package/dist/server/modules/browser-runtime/browser-task-execution-support.js +432 -0
- package/dist/server/modules/browser-runtime/browser-task-execution-support.js.map +1 -0
- package/dist/server/modules/browser-runtime/browser-task-executor-registry.d.ts +7 -0
- package/dist/server/modules/browser-runtime/browser-task-executor-registry.js +21 -0
- package/dist/server/modules/browser-runtime/browser-task-executor-registry.js.map +1 -0
- package/dist/server/modules/browser-runtime/browser-task-executor.d.ts +55 -0
- package/dist/server/modules/browser-runtime/browser-task-executor.js +2 -0
- package/dist/server/modules/browser-runtime/browser-task-executor.js.map +1 -0
- package/dist/server/modules/browser-runtime/browser-task-payload.d.ts +31 -0
- package/dist/server/modules/browser-runtime/browser-task-payload.js +55 -0
- package/dist/server/modules/browser-runtime/browser-task-payload.js.map +1 -0
- package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.d.ts +19 -0
- package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.js +219 -0
- package/dist/server/modules/browser-runtime/opencli-bridge-browser-executor.js.map +1 -0
- package/dist/server/modules/browser-runtime/opencli-browser-bridge-service.d.ts +15 -0
- package/dist/server/modules/browser-runtime/opencli-browser-bridge-service.js +33 -0
- package/dist/server/modules/browser-runtime/opencli-browser-bridge-service.js.map +1 -0
- package/dist/server/modules/browser-runtime/playwright-browser-executor.d.ts +16 -0
- package/dist/server/modules/browser-runtime/playwright-browser-executor.js +272 -0
- package/dist/server/modules/browser-runtime/playwright-browser-executor.js.map +1 -0
- package/dist/server/modules/butler/butler-auth-service.js +4 -0
- package/dist/server/modules/butler/butler-auth-service.js.map +1 -1
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.js +1 -0
- package/dist/server/modules/butler/butler-inbox-instruction-adapter.js.map +1 -1
- package/dist/server/modules/butler/butler-session-summary-service.d.ts +1 -0
- package/dist/server/modules/butler/butler-session-summary-service.js +5 -3
- package/dist/server/modules/butler/butler-session-summary-service.js.map +1 -1
- package/dist/server/modules/butler/butler-workspace-context.js +23 -0
- package/dist/server/modules/butler/butler-workspace-context.js.map +1 -1
- package/dist/server/modules/debug-target/debug-target-service.d.ts +2 -0
- package/dist/server/modules/debug-target/debug-target-service.js +14 -0
- package/dist/server/modules/debug-target/debug-target-service.js.map +1 -1
- package/dist/server/modules/document-runtime/document-docx-fallback-renderer.py +139 -0
- package/dist/server/modules/document-runtime/document-export-executor.d.ts +50 -0
- package/dist/server/modules/document-runtime/document-export-executor.js +827 -0
- package/dist/server/modules/document-runtime/document-export-executor.js.map +1 -0
- package/dist/server/modules/document-runtime/document-runtime-controller.d.ts +127 -0
- package/dist/server/modules/document-runtime/document-runtime-controller.js +131 -0
- package/dist/server/modules/document-runtime/document-runtime-controller.js.map +1 -0
- package/dist/server/modules/document-runtime/document-runtime-service.d.ts +125 -0
- package/dist/server/modules/document-runtime/document-runtime-service.js +706 -0
- package/dist/server/modules/document-runtime/document-runtime-service.js.map +1 -0
- package/dist/server/modules/office/office-controller.d.ts +77 -0
- package/dist/server/modules/office/office-controller.js +174 -0
- package/dist/server/modules/office/office-controller.js.map +1 -0
- package/dist/server/modules/office/office-preview-link-service.d.ts +27 -0
- package/dist/server/modules/office/office-preview-link-service.js +121 -0
- package/dist/server/modules/office/office-preview-link-service.js.map +1 -0
- package/dist/server/modules/office/office-service.d.ts +67 -0
- package/dist/server/modules/office/office-service.js +359 -0
- package/dist/server/modules/office/office-service.js.map +1 -0
- package/dist/server/modules/opencli/opencli-bridge-skill-service.js +38 -14
- package/dist/server/modules/opencli/opencli-bridge-skill-service.js.map +1 -1
- package/dist/server/modules/opencli/opencli-install-discovery.d.ts +4 -0
- package/dist/server/modules/opencli/opencli-install-discovery.js +94 -0
- package/dist/server/modules/opencli/opencli-install-discovery.js.map +1 -1
- package/dist/server/modules/opencli/opencli-runtime-builder.js +29 -0
- package/dist/server/modules/opencli/opencli-runtime-builder.js.map +1 -1
- package/dist/server/modules/opencli/opencli-runtime-guard.d.ts +2 -0
- package/dist/server/modules/opencli/opencli-runtime-guard.js +5 -0
- package/dist/server/modules/opencli/opencli-runtime-guard.js.map +1 -0
- package/dist/server/modules/ops-runtime/ops-runtime-controller.d.ts +70 -0
- package/dist/server/modules/ops-runtime/ops-runtime-controller.js +83 -0
- package/dist/server/modules/ops-runtime/ops-runtime-controller.js.map +1 -0
- package/dist/server/modules/ops-runtime/ops-runtime-service.d.ts +80 -0
- package/dist/server/modules/ops-runtime/ops-runtime-service.js +327 -0
- package/dist/server/modules/ops-runtime/ops-runtime-service.js.map +1 -0
- package/dist/server/modules/ops-runtime/ssh-ops-executor.d.ts +41 -0
- package/dist/server/modules/ops-runtime/ssh-ops-executor.js +478 -0
- package/dist/server/modules/ops-runtime/ssh-ops-executor.js.map +1 -0
- package/dist/server/modules/presentation/presentation-controller.d.ts +22 -0
- package/dist/server/modules/presentation/presentation-controller.js +59 -0
- package/dist/server/modules/presentation/presentation-controller.js.map +1 -0
- package/dist/server/modules/presentation/presentation-export-task-service.d.ts +24 -0
- package/dist/server/modules/presentation/presentation-export-task-service.js +137 -0
- package/dist/server/modules/presentation/presentation-export-task-service.js.map +1 -0
- package/dist/server/modules/presentation/presentation-export-types.d.ts +12 -0
- package/dist/server/modules/presentation/presentation-export-types.js +2 -0
- package/dist/server/modules/presentation/presentation-export-types.js.map +1 -0
- package/dist/server/modules/presentation/presentation-pdf-export-service.d.ts +20 -0
- package/dist/server/modules/presentation/presentation-pdf-export-service.js +29 -0
- package/dist/server/modules/presentation/presentation-pdf-export-service.js.map +1 -0
- package/dist/server/modules/presentation/presentation-pptx-export-service.d.ts +20 -0
- package/dist/server/modules/presentation/presentation-pptx-export-service.js +64 -0
- package/dist/server/modules/presentation/presentation-pptx-export-service.js.map +1 -0
- package/dist/server/modules/presentation/presentation-renderer.d.ts +21 -0
- package/dist/server/modules/presentation/presentation-renderer.js +208 -0
- package/dist/server/modules/presentation/presentation-renderer.js.map +1 -0
- package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js +3 -3
- package/dist/server/modules/relay-tunnel/relay-tunnel-gateway-service.js.map +1 -1
- package/dist/server/modules/relay-tunnel/relay-tunnel-service.js +6 -1
- package/dist/server/modules/relay-tunnel/relay-tunnel-service.js.map +1 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js +2 -1
- package/dist/server/modules/sessions/codex-app-server-helper-process.js.map +1 -1
- package/dist/server/modules/sessions/session-controller.d.ts +1 -0
- package/dist/server/modules/sessions/session-controller.js +59 -4
- package/dist/server/modules/sessions/session-controller.js.map +1 -1
- package/dist/server/modules/sessions/session-history-service.js +17 -5
- package/dist/server/modules/sessions/session-history-service.js.map +1 -1
- package/dist/server/modules/sessions/session-live-runtime-service.d.ts +5 -1
- package/dist/server/modules/sessions/session-live-runtime-service.js +86 -8
- package/dist/server/modules/sessions/session-live-runtime-service.js.map +1 -1
- package/dist/server/modules/sessions/session-provider-config-service.d.ts +25 -1
- package/dist/server/modules/sessions/session-provider-config-service.js +54 -5
- package/dist/server/modules/sessions/session-provider-config-service.js.map +1 -1
- package/dist/server/modules/sessions/workspace-office-mcp-config.d.ts +14 -0
- package/dist/server/modules/sessions/workspace-office-mcp-config.js +54 -0
- package/dist/server/modules/sessions/workspace-office-mcp-config.js.map +1 -0
- package/dist/server/modules/sessions/workspace-session-auth-service.d.ts +27 -0
- package/dist/server/modules/sessions/workspace-session-auth-service.js +109 -0
- package/dist/server/modules/sessions/workspace-session-auth-service.js.map +1 -0
- package/dist/server/modules/sessions/workspace-session-runtime-context-service.d.ts +50 -0
- package/dist/server/modules/sessions/workspace-session-runtime-context-service.js +332 -0
- package/dist/server/modules/sessions/workspace-session-runtime-context-service.js.map +1 -0
- package/dist/server/modules/skills/assistant-runtime-skill-catalog.js +5 -0
- package/dist/server/modules/skills/assistant-runtime-skill-catalog.js.map +1 -1
- package/dist/server/modules/skills/builtin-skills/codingns-workspace-session/SKILL.md +67 -0
- package/dist/server/modules/skills/builtin-skills/codingns-workspace-session/agents/openai.yaml +4 -0
- package/dist/server/modules/skills/builtin-skills/codingns-workspace-session/references/cli-workflow.md +133 -0
- package/dist/server/modules/skills/skill-controller.d.ts +7 -0
- package/dist/server/modules/skills/skill-controller.js +7 -0
- package/dist/server/modules/skills/skill-controller.js.map +1 -1
- package/dist/server/modules/skills/skill-manager-service.d.ts +61 -0
- package/dist/server/modules/skills/skill-manager-service.js +218 -0
- package/dist/server/modules/skills/skill-manager-service.js.map +1 -1
- package/dist/server/modules/skills/skill-name-policy.js +2 -1
- package/dist/server/modules/skills/skill-name-policy.js.map +1 -1
- package/dist/server/modules/tasks/task-helper-client.d.ts +1 -0
- package/dist/server/modules/tasks/task-helper-client.js +45 -9
- package/dist/server/modules/tasks/task-helper-client.js.map +1 -1
- package/dist/server/modules/tasks/task-types.d.ts +5 -0
- package/dist/server/modules/tasks/task-types.js +6 -1
- package/dist/server/modules/tasks/task-types.js.map +1 -1
- package/dist/server/modules/terminal/runtime/conpty-session-agent-process.js +2 -1
- package/dist/server/modules/terminal/runtime/conpty-session-agent-process.js.map +1 -1
- package/dist/server/modules/terminal/runtime/node-pty-loader.d.ts +5 -0
- package/dist/server/modules/terminal/runtime/node-pty-loader.js +68 -0
- package/dist/server/modules/terminal/runtime/node-pty-loader.js.map +1 -0
- package/dist/server/modules/terminal/runtime/pty-broker-agent-process.js +2 -1
- package/dist/server/modules/terminal/runtime/pty-broker-agent-process.js.map +1 -1
- package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.js +6 -9
- package/dist/server/modules/terminal/runtime/pty-host-attachment-manager.js.map +1 -1
- package/dist/server/modules/terminal/runtime/pty-runtime-manager.js +6 -9
- package/dist/server/modules/terminal/runtime/pty-runtime-manager.js.map +1 -1
- package/dist/server/routes/assistant.d.ts +2 -1
- package/dist/server/routes/assistant.js +20 -1
- package/dist/server/routes/assistant.js.map +1 -1
- package/dist/server/routes/browser-runtime.d.ts +3 -0
- package/dist/server/routes/browser-runtime.js +14 -0
- package/dist/server/routes/browser-runtime.js.map +1 -0
- package/dist/server/routes/document-runtime.d.ts +3 -0
- package/dist/server/routes/document-runtime.js +18 -0
- package/dist/server/routes/document-runtime.js.map +1 -0
- package/dist/server/routes/office.d.ts +3 -0
- package/dist/server/routes/office.js +16 -0
- package/dist/server/routes/office.js.map +1 -0
- package/dist/server/routes/ops-runtime.d.ts +3 -0
- package/dist/server/routes/ops-runtime.js +13 -0
- package/dist/server/routes/ops-runtime.js.map +1 -0
- package/dist/server/routes/presentation.d.ts +3 -0
- package/dist/server/routes/presentation.js +5 -0
- package/dist/server/routes/presentation.js.map +1 -0
- package/dist/server/routes/skills.js +1 -0
- package/dist/server/routes/skills.js.map +1 -1
- package/dist/server/server/create-server.d.ts +36 -0
- package/dist/server/server/create-server.js +215 -4
- package/dist/server/server/create-server.js.map +1 -1
- package/dist/server/server/release-manifest-sync.d.ts +1 -0
- package/dist/server/server/release-manifest-sync.js +2 -2
- package/dist/server/server/release-manifest-sync.js.map +1 -1
- package/dist/server/server/start-host.js +1 -1
- package/dist/server/server/start-host.js.map +1 -1
- package/dist/server/storage/repositories/auth-token-repository.js +22 -6
- package/dist/server/storage/repositories/auth-token-repository.js.map +1 -1
- package/dist/server/storage/repositories/browser-profile-repository.d.ts +18 -0
- package/dist/server/storage/repositories/browser-profile-repository.js +134 -0
- package/dist/server/storage/repositories/browser-profile-repository.js.map +1 -0
- package/dist/server/storage/repositories/document-comment-repository.d.ts +10 -0
- package/dist/server/storage/repositories/document-comment-repository.js +118 -0
- package/dist/server/storage/repositories/document-comment-repository.js.map +1 -0
- package/dist/server/storage/repositories/document-repository.d.ts +16 -0
- package/dist/server/storage/repositories/document-repository.js +109 -0
- package/dist/server/storage/repositories/document-repository.js.map +1 -0
- package/dist/server/storage/repositories/document-revision-repository.d.ts +10 -0
- package/dist/server/storage/repositories/document-revision-repository.js +79 -0
- package/dist/server/storage/repositories/document-revision-repository.js.map +1 -0
- package/dist/server/storage/repositories/document-template-repository.d.ts +13 -0
- package/dist/server/storage/repositories/document-template-repository.js +244 -0
- package/dist/server/storage/repositories/document-template-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-approval-repository.d.ts +11 -0
- package/dist/server/storage/repositories/office-approval-repository.js +109 -0
- package/dist/server/storage/repositories/office-approval-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-artifact-repository.d.ts +10 -0
- package/dist/server/storage/repositories/office-artifact-repository.js +89 -0
- package/dist/server/storage/repositories/office-artifact-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-audit-event-repository.d.ts +8 -0
- package/dist/server/storage/repositories/office-audit-event-repository.js +54 -0
- package/dist/server/storage/repositories/office-audit-event-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-connector-repository.d.ts +10 -0
- package/dist/server/storage/repositories/office-connector-repository.js +97 -0
- package/dist/server/storage/repositories/office-connector-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-receipt-repository.d.ts +8 -0
- package/dist/server/storage/repositories/office-receipt-repository.js +48 -0
- package/dist/server/storage/repositories/office-receipt-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-rollback-record-repository.d.ts +8 -0
- package/dist/server/storage/repositories/office-rollback-record-repository.js +60 -0
- package/dist/server/storage/repositories/office-rollback-record-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-task-repository.d.ts +19 -0
- package/dist/server/storage/repositories/office-task-repository.js +199 -0
- package/dist/server/storage/repositories/office-task-repository.js.map +1 -0
- package/dist/server/storage/repositories/office-task-step-repository.d.ts +10 -0
- package/dist/server/storage/repositories/office-task-step-repository.js +110 -0
- package/dist/server/storage/repositories/office-task-step-repository.js.map +1 -0
- package/dist/server/storage/repositories/ops-target-repository.d.ts +16 -0
- package/dist/server/storage/repositories/ops-target-repository.js +119 -0
- package/dist/server/storage/repositories/ops-target-repository.js.map +1 -0
- package/dist/server/storage/repositories/session-binding-repository.d.ts +4 -0
- package/dist/server/storage/repositories/session-binding-repository.js +70 -69
- package/dist/server/storage/repositories/session-binding-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-changed-file-repository.d.ts +6 -0
- package/dist/server/storage/repositories/session-changed-file-repository.js +44 -43
- package/dist/server/storage/repositories/session-changed-file-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-fork-repository.d.ts +2 -0
- package/dist/server/storage/repositories/session-fork-repository.js +42 -41
- package/dist/server/storage/repositories/session-fork-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-index-repository.d.ts +5 -0
- package/dist/server/storage/repositories/session-index-repository.js +153 -152
- package/dist/server/storage/repositories/session-index-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-message-attachment-repository.d.ts +7 -0
- package/dist/server/storage/repositories/session-message-attachment-repository.js +91 -90
- package/dist/server/storage/repositories/session-message-attachment-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-message-origin-repository.d.ts +2 -0
- package/dist/server/storage/repositories/session-message-origin-repository.js +25 -24
- package/dist/server/storage/repositories/session-message-origin-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-state-repository.d.ts +2 -0
- package/dist/server/storage/repositories/session-state-repository.js +35 -34
- package/dist/server/storage/repositories/session-state-repository.js.map +1 -1
- package/dist/server/storage/repositories/session-status-snapshot-repository.d.ts +2 -0
- package/dist/server/storage/repositories/session-status-snapshot-repository.js +25 -24
- package/dist/server/storage/repositories/session-status-snapshot-repository.js.map +1 -1
- package/dist/server/storage/sqlite/client.js +123 -1
- package/dist/server/storage/sqlite/client.js.map +1 -1
- package/dist/server/storage/sqlite/schema.sql +300 -1
- package/dist/server/types/domain.d.ts +205 -1
- package/package.json +14 -7
- package/scripts/postinstall.mjs +159 -7
- package/dist/public/assets/FileContextPanel-xGTYDclT.js +0 -1
- package/dist/public/assets/WorkbenchLayout-DScHaza9.js +0 -244
- package/dist/public/assets/WorkbenchShellRoute-DN6LdrqC.js +0 -1
- package/dist/public/assets/WorkbenchShellRoute-DhQo_0vu.css +0 -1
- package/dist/public/assets/file-tree-icon-lfU9Ag77.js +0 -3
- package/dist/public/assets/index-CFYXCsyx.css +0 -1
- package/dist/public/assets/index-NGxWr8Ix.js +0 -42
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.d.ts +0 -42
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js +0 -346
- package/node_modules/@codingns/session-sync-core/dist/claude-message-utils.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.d.ts +0 -1
- package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.js +0 -80
- package/node_modules/@codingns/session-sync-core/dist/codex-resume-history.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/index.d.ts +0 -18
- package/node_modules/@codingns/session-sync-core/dist/index.js +0 -19
- package/node_modules/@codingns/session-sync-core/dist/index.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.d.ts +0 -18
- package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js +0 -659
- package/node_modules/@codingns/session-sync-core/dist/kimi-message-normalizer.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/kimi-shared.d.ts +0 -11
- package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js +0 -72
- package/node_modules/@codingns/session-sync-core/dist/kimi-shared.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.d.ts +0 -67
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js +0 -752
- package/node_modules/@codingns/session-sync-core/dist/patch-builder.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.d.ts +0 -48
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js +0 -1184
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-code.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.d.ts +0 -11
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js +0 -105
- package/node_modules/@codingns/session-sync-core/dist/providers/claude-session-store.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.d.ts +0 -84
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js +0 -2436
- package/node_modules/@codingns/session-sync-core/dist/providers/codex.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.d.ts +0 -47
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js +0 -1480
- package/node_modules/@codingns/session-sync-core/dist/providers/gemini.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.d.ts +0 -33
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js +0 -684
- package/node_modules/@codingns/session-sync-core/dist/providers/kimi.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.d.ts +0 -9
- package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js +0 -17
- package/node_modules/@codingns/session-sync-core/dist/providers/legna-code.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.d.ts +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.js +0 -8
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-permissions.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.d.ts +0 -48
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js +0 -373
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode-shared.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.d.ts +0 -61
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js +0 -1191
- package/node_modules/@codingns/session-sync-core/dist/providers/opencode.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.d.ts +0 -27
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js +0 -415
- package/node_modules/@codingns/session-sync-core/dist/providers/utils.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/registry.d.ts +0 -7
- package/node_modules/@codingns/session-sync-core/dist/registry.js +0 -22
- package/node_modules/@codingns/session-sync-core/dist/registry.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.d.ts +0 -24
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js +0 -329
- package/node_modules/@codingns/session-sync-core/dist/runtime/active-run-registry.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.d.ts +0 -30
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js +0 -939
- package/node_modules/@codingns/session-sync-core/dist/runtime/claude-runtime.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.d.ts +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js +0 -16
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-permissions.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.d.ts +0 -70
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js +0 -2571
- package/node_modules/@codingns/session-sync-core/dist/runtime/codex-runtime.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.d.ts +0 -21
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js +0 -561
- package/node_modules/@codingns/session-sync-core/dist/runtime/gemini-runtime.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.d.ts +0 -38
- package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js +0 -911
- package/node_modules/@codingns/session-sync-core/dist/runtime/kimi-runtime.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.d.ts +0 -15
- package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js +0 -16
- package/node_modules/@codingns/session-sync-core/dist/runtime/legna-runtime.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.d.ts +0 -37
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js +0 -963
- package/node_modules/@codingns/session-sync-core/dist/runtime/opencode-runtime.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.d.ts +0 -21
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js +0 -168
- package/node_modules/@codingns/session-sync-core/dist/runtime/provider-runtime-service.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.d.ts +0 -152
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.js +0 -2
- package/node_modules/@codingns/session-sync-core/dist/runtime/types.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/services.d.ts +0 -28
- package/node_modules/@codingns/session-sync-core/dist/services.js +0 -148
- package/node_modules/@codingns/session-sync-core/dist/services.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.d.ts +0 -6
- package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js +0 -9
- package/node_modules/@codingns/session-sync-core/dist/sqlite/node-sqlite.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/dist/types.d.ts +0 -198
- package/node_modules/@codingns/session-sync-core/dist/types.js +0 -2
- package/node_modules/@codingns/session-sync-core/dist/types.js.map +0 -1
- package/node_modules/@codingns/session-sync-core/package.json +0 -33
|
@@ -1,2436 +0,0 @@
|
|
|
1
|
-
import { basename, dirname, join } from "node:path";
|
|
2
|
-
import { existsSync, readFileSync, readdirSync, renameSync, rmSync, statSync } from "node:fs";
|
|
3
|
-
import crypto from "node:crypto";
|
|
4
|
-
import { appendJsonLine, createRawRef, encodeCursor, ensureDirectory, extractTextBlocks, ensureText, messageIdFromRawRef, messageIdFromStableKey, nextTimestamp, normalizeWorkspacePath, readFirstNonEmptyLine, readJsonLines, readTrailingJsonLines, safeDate, sliceHistory, stringifyStructuredValue, walkJsonlFiles } from "./utils.js";
|
|
5
|
-
import { buildCodexResumeHistoryFromRawStore } from "../codex-resume-history.js";
|
|
6
|
-
import { buildApplyPatchFromCodexCommandLikeValue } from "../patch-builder.js";
|
|
7
|
-
import { loadDatabaseSync } from "../sqlite/node-sqlite.js";
|
|
8
|
-
const CODEX_SESSION_TITLE_MAX_LENGTH = 48;
|
|
9
|
-
const HISTORY_CACHE_LIMIT = 6;
|
|
10
|
-
const SESSION_SUMMARY_CACHE_LIMIT = 512;
|
|
11
|
-
const SPAWN_RELATION_SCAN_CACHE_LIMIT = 512;
|
|
12
|
-
const RECENT_HISTORY_INITIAL_BYTES = 256 * 1024;
|
|
13
|
-
const RECENT_HISTORY_MAX_BYTES = 4 * 1024 * 1024;
|
|
14
|
-
const RECENT_HISTORY_BUFFER_MESSAGES = 24;
|
|
15
|
-
const CODEX_CONFIG_CONTEXT_WINDOW_PATTERN = /(?:^|\n)\s*model_context_window\s*=\s*(\d+)\s*(?:\n|$)/i;
|
|
16
|
-
const KNOWN_CODEX_CONTEXT_WINDOWS = new Map([
|
|
17
|
-
["gpt-5.3-codex", 400_000],
|
|
18
|
-
["codex-mini-latest", 200_000]
|
|
19
|
-
]);
|
|
20
|
-
export class CodexAdapter {
|
|
21
|
-
options;
|
|
22
|
-
providerId = "codex";
|
|
23
|
-
historyCache = new Map();
|
|
24
|
-
sessionSummaryCache = new Map();
|
|
25
|
-
spawnRelationScanCache = new Map();
|
|
26
|
-
threadMetadataIndexCache = null;
|
|
27
|
-
constructor(options) {
|
|
28
|
-
this.options = options;
|
|
29
|
-
}
|
|
30
|
-
async detectSessions(workspacePath, options) {
|
|
31
|
-
const discovery = await this.detectSessionsDetailed(workspacePath, options);
|
|
32
|
-
return discovery.sessions;
|
|
33
|
-
}
|
|
34
|
-
async detectSessionsDetailed(workspacePath, options) {
|
|
35
|
-
const startedAt = Date.now();
|
|
36
|
-
const targetPath = normalizeWorkspacePath(workspacePath);
|
|
37
|
-
const knownSessions = (options?.knownSessions ?? []).filter((session) => session.provider === this.providerId);
|
|
38
|
-
const threadMetadataIndex = this.readThreadMetadataIndex();
|
|
39
|
-
const files = this.listSessionFiles(targetPath, threadMetadataIndex, knownSessions);
|
|
40
|
-
const knownByRawStoreRef = new Map(knownSessions.map((session) => [session.rawStoreRef, session]));
|
|
41
|
-
const knownByProviderSessionId = new Map(knownSessions.map((session) => [session.providerSessionId, session]));
|
|
42
|
-
const sessionsByProviderSessionId = new Map();
|
|
43
|
-
const retainedSummaries = [];
|
|
44
|
-
const pendingFiles = [];
|
|
45
|
-
let scannedFiles = 0;
|
|
46
|
-
let skippedByMtimeSize = 0;
|
|
47
|
-
let parsedFiles = 0;
|
|
48
|
-
let bytesRead = 0;
|
|
49
|
-
for (const filePath of files) {
|
|
50
|
-
scannedFiles += 1;
|
|
51
|
-
const stats = statSync(filePath);
|
|
52
|
-
const cachedSummary = this.sessionSummaryCache.get(filePath);
|
|
53
|
-
const fileSessionId = basename(filePath, ".jsonl");
|
|
54
|
-
const sessionIdentity = this.readSessionIdentity(filePath, fileSessionId);
|
|
55
|
-
if (cachedSummary
|
|
56
|
-
&& cachedSummary.mtimeMs === stats.mtimeMs
|
|
57
|
-
&& cachedSummary.size === stats.size
|
|
58
|
-
&& (!cachedSummary.summary
|
|
59
|
-
|| !sessionIdentity
|
|
60
|
-
|| cachedSummary.summary.providerSessionId === sessionIdentity.threadId)) {
|
|
61
|
-
this.touchSessionSummaryCache(filePath, cachedSummary);
|
|
62
|
-
if (cachedSummary.summary
|
|
63
|
-
&& hasUsableCodexTitle(cachedSummary.summary.title)
|
|
64
|
-
&& normalizeWorkspacePath(cachedSummary.summary.workspacePath) === targetPath) {
|
|
65
|
-
skippedByMtimeSize += 1;
|
|
66
|
-
retainedSummaries.push({
|
|
67
|
-
filePath,
|
|
68
|
-
stats,
|
|
69
|
-
sessionIdentity,
|
|
70
|
-
summary: cachedSummary.summary
|
|
71
|
-
});
|
|
72
|
-
continue;
|
|
73
|
-
}
|
|
74
|
-
if (cachedSummary.workspacePath
|
|
75
|
-
&& normalizeWorkspacePath(cachedSummary.workspacePath) !== targetPath) {
|
|
76
|
-
continue;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
const knownByPath = knownByRawStoreRef.get(filePath);
|
|
80
|
-
if (knownByPath
|
|
81
|
-
&& knownByPath.sourceMtimeMs === stats.mtimeMs
|
|
82
|
-
&& knownByPath.sourceSizeBytes === stats.size
|
|
83
|
-
&& hasUsableCodexTitle(knownByPath.title)
|
|
84
|
-
&& (!sessionIdentity
|
|
85
|
-
|| knownByPath.providerSessionId === sessionIdentity.threadId)) {
|
|
86
|
-
skippedByMtimeSize += 1;
|
|
87
|
-
if (normalizeWorkspacePath(knownByPath.workspacePath) === targetPath) {
|
|
88
|
-
retainedSummaries.push({
|
|
89
|
-
filePath,
|
|
90
|
-
stats,
|
|
91
|
-
sessionIdentity,
|
|
92
|
-
summary: knownByPath
|
|
93
|
-
});
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
this.touchSessionSummaryCache(filePath, {
|
|
97
|
-
filePath,
|
|
98
|
-
mtimeMs: stats.mtimeMs,
|
|
99
|
-
size: stats.size,
|
|
100
|
-
workspacePath: knownByPath.workspacePath,
|
|
101
|
-
providerSessionId: knownByPath.providerSessionId,
|
|
102
|
-
title: null,
|
|
103
|
-
summary: null
|
|
104
|
-
});
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
if (sessionIdentity?.cwd
|
|
108
|
-
&& normalizeWorkspacePath(sessionIdentity.cwd) !== targetPath) {
|
|
109
|
-
this.touchSessionSummaryCache(filePath, {
|
|
110
|
-
filePath,
|
|
111
|
-
mtimeMs: stats.mtimeMs,
|
|
112
|
-
size: stats.size,
|
|
113
|
-
workspacePath: sessionIdentity.cwd,
|
|
114
|
-
providerSessionId: sessionIdentity.threadId,
|
|
115
|
-
title: null,
|
|
116
|
-
summary: null
|
|
117
|
-
});
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
pendingFiles.push({
|
|
121
|
-
filePath,
|
|
122
|
-
fileSessionId,
|
|
123
|
-
stats: {
|
|
124
|
-
mtimeMs: stats.mtimeMs,
|
|
125
|
-
size: stats.size
|
|
126
|
-
},
|
|
127
|
-
sessionIdentity
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
if (pendingFiles.length === 0 && retainedSummaries.length === 0) {
|
|
131
|
-
const sessions = [...sessionsByProviderSessionId.values()].sort((left, right) => (left.lastMessageAt ?? "").localeCompare(right.lastMessageAt ?? ""));
|
|
132
|
-
const diagnostic = {
|
|
133
|
-
provider: this.providerId,
|
|
134
|
-
status: "success",
|
|
135
|
-
durationMs: Date.now() - startedAt,
|
|
136
|
-
sessionCount: sessions.length,
|
|
137
|
-
isComplete: true,
|
|
138
|
-
errorMessage: null,
|
|
139
|
-
scannedFiles,
|
|
140
|
-
skippedByMtimeSize,
|
|
141
|
-
parsedFiles,
|
|
142
|
-
bytesRead
|
|
143
|
-
};
|
|
144
|
-
return {
|
|
145
|
-
sessions,
|
|
146
|
-
isComplete: true,
|
|
147
|
-
providerDiagnostics: [diagnostic]
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
const pendingThreadIds = new Set([...pendingFiles, ...retainedSummaries]
|
|
151
|
-
.map((entry) => entry.sessionIdentity?.threadId ?? null)
|
|
152
|
-
.filter((value) => value !== null));
|
|
153
|
-
const spawnedAgentRelationIndex = this.readSpawnedAgentRelationIndex([...pendingFiles, ...retainedSummaries].map((entry) => ({
|
|
154
|
-
filePath: entry.filePath,
|
|
155
|
-
stats: entry.stats,
|
|
156
|
-
sessionIdentity: entry.sessionIdentity
|
|
157
|
-
})), targetPath, threadMetadataIndex, pendingThreadIds);
|
|
158
|
-
for (const entry of retainedSummaries) {
|
|
159
|
-
const currentThreadId = entry.sessionIdentity?.threadId ?? entry.summary.providerSessionId;
|
|
160
|
-
const currentThreadMetadata = threadMetadataIndex.get(currentThreadId) ?? null;
|
|
161
|
-
const currentSpawnRelation = spawnedAgentRelationIndex.get(currentThreadId) ?? null;
|
|
162
|
-
const summary = this.hydrateSessionSummary({
|
|
163
|
-
...entry.summary,
|
|
164
|
-
workspacePath: entry.sessionIdentity?.cwd || entry.summary.workspacePath
|
|
165
|
-
}, entry.filePath, entry.stats, currentThreadMetadata, currentSpawnRelation);
|
|
166
|
-
this.touchSessionSummaryCache(entry.filePath, {
|
|
167
|
-
filePath: entry.filePath,
|
|
168
|
-
mtimeMs: entry.stats.mtimeMs,
|
|
169
|
-
size: entry.stats.size,
|
|
170
|
-
workspacePath: summary.workspacePath,
|
|
171
|
-
providerSessionId: summary.providerSessionId,
|
|
172
|
-
title: summary.title,
|
|
173
|
-
summary
|
|
174
|
-
});
|
|
175
|
-
sessionsByProviderSessionId.set(summary.providerSessionId, summary);
|
|
176
|
-
}
|
|
177
|
-
for (const entry of pendingFiles) {
|
|
178
|
-
const { filePath, fileSessionId, stats, sessionIdentity } = entry;
|
|
179
|
-
parsedFiles += 1;
|
|
180
|
-
bytesRead += stats.size;
|
|
181
|
-
const records = readJsonLines(filePath);
|
|
182
|
-
const meta = records.find((record) => record.data.type === "session_meta")?.data;
|
|
183
|
-
const metaPayload = (meta?.payload ?? {});
|
|
184
|
-
const codexSessionId = this.resolveCodexSessionId(metaPayload, fileSessionId);
|
|
185
|
-
if (shouldIgnoreCodingNsDraftSession(metaPayload)) {
|
|
186
|
-
this.touchSessionSummaryCache(filePath, {
|
|
187
|
-
filePath,
|
|
188
|
-
mtimeMs: stats.mtimeMs,
|
|
189
|
-
size: stats.size,
|
|
190
|
-
workspacePath: null,
|
|
191
|
-
providerSessionId: codexSessionId,
|
|
192
|
-
title: null,
|
|
193
|
-
summary: null
|
|
194
|
-
});
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
const cwd = ensureText(metaPayload.cwd);
|
|
198
|
-
const cachedWorkspacePath = cwd || null;
|
|
199
|
-
const sessionWorkspacePath = cwd || workspacePath;
|
|
200
|
-
if (normalizeWorkspacePath(cwd) !== targetPath) {
|
|
201
|
-
this.touchSessionSummaryCache(filePath, {
|
|
202
|
-
filePath,
|
|
203
|
-
mtimeMs: stats.mtimeMs,
|
|
204
|
-
size: stats.size,
|
|
205
|
-
workspacePath: cachedWorkspacePath,
|
|
206
|
-
providerSessionId: codexSessionId,
|
|
207
|
-
title: null,
|
|
208
|
-
summary: null
|
|
209
|
-
});
|
|
210
|
-
continue;
|
|
211
|
-
}
|
|
212
|
-
const currentThreadMetadata = threadMetadataIndex.get(codexSessionId) ??
|
|
213
|
-
(sessionIdentity ? threadMetadataIndex.get(sessionIdentity.threadId) : null);
|
|
214
|
-
const currentSpawnRelation = spawnedAgentRelationIndex.get(codexSessionId) ??
|
|
215
|
-
(sessionIdentity ? spawnedAgentRelationIndex.get(sessionIdentity.threadId) : null);
|
|
216
|
-
const knownBySessionId = knownByProviderSessionId.get(codexSessionId);
|
|
217
|
-
if (knownBySessionId
|
|
218
|
-
&& knownBySessionId.sourceMtimeMs === stats.mtimeMs
|
|
219
|
-
&& knownBySessionId.sourceSizeBytes === stats.size
|
|
220
|
-
&& hasUsableCodexTitle(knownBySessionId.title)) {
|
|
221
|
-
const summary = this.hydrateSessionSummary({
|
|
222
|
-
...knownBySessionId,
|
|
223
|
-
workspacePath: sessionWorkspacePath
|
|
224
|
-
}, filePath, stats, currentThreadMetadata, currentSpawnRelation);
|
|
225
|
-
this.touchSessionSummaryCache(filePath, {
|
|
226
|
-
filePath,
|
|
227
|
-
mtimeMs: stats.mtimeMs,
|
|
228
|
-
size: stats.size,
|
|
229
|
-
workspacePath: sessionWorkspacePath,
|
|
230
|
-
providerSessionId: summary.providerSessionId,
|
|
231
|
-
title: summary.title,
|
|
232
|
-
summary
|
|
233
|
-
});
|
|
234
|
-
sessionsByProviderSessionId.set(codexSessionId, summary);
|
|
235
|
-
continue;
|
|
236
|
-
}
|
|
237
|
-
const messages = this.parseMessagesFromEntries(filePath, records, codexSessionId);
|
|
238
|
-
const title = this.resolveIndexedTitle(threadMetadataIndex, codexSessionId) ??
|
|
239
|
-
resolveCodexFallbackTitle(messages) ??
|
|
240
|
-
fileSessionId;
|
|
241
|
-
const lastMessageAt = messages.at(-1)?.timestamp ?? (ensureText(metaPayload.timestamp) || null);
|
|
242
|
-
const summary = this.hydrateSessionSummary({
|
|
243
|
-
provider: this.providerId,
|
|
244
|
-
providerSessionId: codexSessionId,
|
|
245
|
-
title,
|
|
246
|
-
workspacePath: sessionWorkspacePath,
|
|
247
|
-
rawStoreRef: filePath,
|
|
248
|
-
lastMessageAt,
|
|
249
|
-
messageCount: messages.length,
|
|
250
|
-
isArchived: false
|
|
251
|
-
}, filePath, stats, currentThreadMetadata, currentSpawnRelation);
|
|
252
|
-
sessionsByProviderSessionId.set(codexSessionId, summary);
|
|
253
|
-
this.touchSessionSummaryCache(filePath, {
|
|
254
|
-
filePath,
|
|
255
|
-
mtimeMs: stats.mtimeMs,
|
|
256
|
-
size: stats.size,
|
|
257
|
-
workspacePath: sessionWorkspacePath,
|
|
258
|
-
providerSessionId: summary.providerSessionId,
|
|
259
|
-
title: summary.title,
|
|
260
|
-
summary
|
|
261
|
-
});
|
|
262
|
-
}
|
|
263
|
-
const sessions = [...sessionsByProviderSessionId.values()].sort((left, right) => (left.lastMessageAt ?? "").localeCompare(right.lastMessageAt ?? ""));
|
|
264
|
-
const diagnostic = {
|
|
265
|
-
provider: this.providerId,
|
|
266
|
-
status: "success",
|
|
267
|
-
durationMs: Date.now() - startedAt,
|
|
268
|
-
sessionCount: sessions.length,
|
|
269
|
-
isComplete: true,
|
|
270
|
-
errorMessage: null,
|
|
271
|
-
scannedFiles,
|
|
272
|
-
skippedByMtimeSize,
|
|
273
|
-
parsedFiles,
|
|
274
|
-
bytesRead
|
|
275
|
-
};
|
|
276
|
-
return {
|
|
277
|
-
sessions,
|
|
278
|
-
isComplete: true,
|
|
279
|
-
providerDiagnostics: [diagnostic]
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
async readSessionHistory(providerSessionId, rawStoreRef, cursor, limit, direction = "forward") {
|
|
283
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
284
|
-
if (!existsSync(resolvedStoreRef)) {
|
|
285
|
-
return {
|
|
286
|
-
messages: [],
|
|
287
|
-
cursor,
|
|
288
|
-
nextCursor: null,
|
|
289
|
-
total: 0
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
const messages = this.getParsedMessages(resolvedStoreRef, providerSessionId);
|
|
293
|
-
return sliceHistory(messages, cursor, limit, direction);
|
|
294
|
-
}
|
|
295
|
-
async readRecentSessionHistory(providerSessionId, rawStoreRef, totalMessageCount, limit) {
|
|
296
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
297
|
-
if (!existsSync(resolvedStoreRef)) {
|
|
298
|
-
return null;
|
|
299
|
-
}
|
|
300
|
-
const stats = statSync(resolvedStoreRef);
|
|
301
|
-
const cached = this.historyCache.get(resolvedStoreRef);
|
|
302
|
-
if (cached
|
|
303
|
-
&& cached.providerSessionId === providerSessionId
|
|
304
|
-
&& cached.mtimeMs === stats.mtimeMs
|
|
305
|
-
&& cached.size === stats.size) {
|
|
306
|
-
this.touchHistoryCache(resolvedStoreRef, cached);
|
|
307
|
-
return sliceHistory(cached.messages, null, limit, "backward");
|
|
308
|
-
}
|
|
309
|
-
const safeLimit = Math.max(1, Math.min(Math.trunc(limit), 100));
|
|
310
|
-
let maxBytes = Math.min(RECENT_HISTORY_INITIAL_BYTES, stats.size);
|
|
311
|
-
while (maxBytes > 0) {
|
|
312
|
-
const lines = readTrailingJsonLines(resolvedStoreRef, maxBytes);
|
|
313
|
-
if (lines.length > 0) {
|
|
314
|
-
const messages = this.parseMessagesFromEntries(resolvedStoreRef, lines, providerSessionId);
|
|
315
|
-
if (messages.length > 0
|
|
316
|
-
&& (messages.length >= Math.min(totalMessageCount, safeLimit + RECENT_HISTORY_BUFFER_MESSAGES)
|
|
317
|
-
|| maxBytes >= stats.size)) {
|
|
318
|
-
return buildRecentHistoryPage(messages, totalMessageCount, safeLimit);
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
if (maxBytes >= stats.size || maxBytes >= RECENT_HISTORY_MAX_BYTES) {
|
|
322
|
-
break;
|
|
323
|
-
}
|
|
324
|
-
maxBytes = Math.min(stats.size, maxBytes * 2, RECENT_HISTORY_MAX_BYTES);
|
|
325
|
-
}
|
|
326
|
-
return null;
|
|
327
|
-
}
|
|
328
|
-
subscribeSession(providerSessionId, rawStoreRef, cursor, limit, onEvent) {
|
|
329
|
-
let currentCursor = cursor;
|
|
330
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
331
|
-
let lastMtime = existsSync(resolvedStoreRef) ? statSync(resolvedStoreRef).mtimeMs : 0;
|
|
332
|
-
const timer = setInterval(async () => {
|
|
333
|
-
if (!existsSync(resolvedStoreRef)) {
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
const nextStat = statSync(resolvedStoreRef);
|
|
337
|
-
if (nextStat.mtimeMs <= lastMtime) {
|
|
338
|
-
return;
|
|
339
|
-
}
|
|
340
|
-
lastMtime = nextStat.mtimeMs;
|
|
341
|
-
const page = await this.readSessionHistory(providerSessionId, rawStoreRef, currentCursor, limit);
|
|
342
|
-
if (page.messages.length === 0) {
|
|
343
|
-
return;
|
|
344
|
-
}
|
|
345
|
-
currentCursor = page.cursor;
|
|
346
|
-
await onEvent({
|
|
347
|
-
messages: page.messages,
|
|
348
|
-
cursor: page.cursor
|
|
349
|
-
});
|
|
350
|
-
}, 300);
|
|
351
|
-
return {
|
|
352
|
-
close() {
|
|
353
|
-
clearInterval(timer);
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
}
|
|
357
|
-
async resumeSession(providerSessionId, rawStoreRef) {
|
|
358
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
359
|
-
statSync(resolvedStoreRef);
|
|
360
|
-
return {
|
|
361
|
-
provider: this.providerId,
|
|
362
|
-
providerSessionId,
|
|
363
|
-
resumedAt: nextTimestamp(),
|
|
364
|
-
rawStoreRef: resolvedStoreRef
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
async startSession(workspacePath, options) {
|
|
368
|
-
const now = new Date();
|
|
369
|
-
const sessionId = `rollout-${now.toISOString().replaceAll(":", "-")}-${crypto.randomUUID()}`;
|
|
370
|
-
const folder = join(this.options.homeDir, "sessions", `${now.getUTCFullYear()}`, `${String(now.getUTCMonth() + 1).padStart(2, "0")}`, `${String(now.getUTCDate()).padStart(2, "0")}`);
|
|
371
|
-
ensureDirectory(folder);
|
|
372
|
-
const filePath = join(folder, `${sessionId}.jsonl`);
|
|
373
|
-
const nowIso = nextTimestamp();
|
|
374
|
-
appendJsonLine(filePath, {
|
|
375
|
-
timestamp: nowIso,
|
|
376
|
-
type: "session_meta",
|
|
377
|
-
payload: {
|
|
378
|
-
id: sessionId,
|
|
379
|
-
timestamp: nowIso,
|
|
380
|
-
cwd: workspacePath,
|
|
381
|
-
originator: "CodingNS Host",
|
|
382
|
-
source: "codingns"
|
|
383
|
-
}
|
|
384
|
-
});
|
|
385
|
-
if (options.initialPrompt) {
|
|
386
|
-
appendJsonLine(filePath, {
|
|
387
|
-
timestamp: nextTimestamp(),
|
|
388
|
-
type: "event_msg",
|
|
389
|
-
payload: {
|
|
390
|
-
type: "user_message",
|
|
391
|
-
message: options.initialPrompt
|
|
392
|
-
}
|
|
393
|
-
});
|
|
394
|
-
}
|
|
395
|
-
return {
|
|
396
|
-
session: {
|
|
397
|
-
provider: this.providerId,
|
|
398
|
-
providerSessionId: sessionId,
|
|
399
|
-
title: options.initialPrompt?.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH) || "New Codex session",
|
|
400
|
-
workspacePath,
|
|
401
|
-
rawStoreRef: filePath,
|
|
402
|
-
isArchived: false,
|
|
403
|
-
lastMessageAt: nextTimestamp(),
|
|
404
|
-
messageCount: options.initialPrompt ? 1 : 0
|
|
405
|
-
},
|
|
406
|
-
initialCursor: encodeCursor(options.initialPrompt ? 1 : 0)
|
|
407
|
-
};
|
|
408
|
-
}
|
|
409
|
-
async forkSession(providerSessionId, workspacePath, options) {
|
|
410
|
-
const transportFactory = this.options.forkTransportFactory;
|
|
411
|
-
if (!transportFactory) {
|
|
412
|
-
throw new Error("CODEX_FORK_TRANSPORT_NOT_CONFIGURED");
|
|
413
|
-
}
|
|
414
|
-
const transport = transportFactory();
|
|
415
|
-
try {
|
|
416
|
-
await transport.initialize();
|
|
417
|
-
if (options.sourceType === "session") {
|
|
418
|
-
const forked = await this.forkThreadWithHistoryFallback(transport, providerSessionId, workspacePath, options.rawStoreRef);
|
|
419
|
-
return await this.buildForkResultFromTransport({
|
|
420
|
-
providerSessionId: forked.providerSessionId,
|
|
421
|
-
rawStoreRef: forked.rawStoreRef,
|
|
422
|
-
workspacePath,
|
|
423
|
-
fallbackParentProviderSessionId: providerSessionId,
|
|
424
|
-
forkMethod: "native_session_fork",
|
|
425
|
-
forkSourceType: "session",
|
|
426
|
-
providerSourceMessageId: null
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
const targetMessageId = options.sourceMessageId?.trim();
|
|
430
|
-
if (!targetMessageId) {
|
|
431
|
-
throw new Error("FORK_SOURCE_MESSAGE_ID_REQUIRED");
|
|
432
|
-
}
|
|
433
|
-
if (options.strategy === "reconstruct-only") {
|
|
434
|
-
throw new Error("CODEX_RECONSTRUCTED_MESSAGE_FORK_NOT_SUPPORTED");
|
|
435
|
-
}
|
|
436
|
-
const resolvedStoreRef = this.resolveSessionFilePath(options.rawStoreRef, providerSessionId);
|
|
437
|
-
const parsedMessages = this.getParsedMessages(resolvedStoreRef, providerSessionId);
|
|
438
|
-
const targetMessage = parsedMessages.find((message) => message.messageId === targetMessageId);
|
|
439
|
-
if (!targetMessage) {
|
|
440
|
-
throw new Error("FORK_SOURCE_MESSAGE_NOT_FOUND");
|
|
441
|
-
}
|
|
442
|
-
const targetSnapshot = applyForkSourceMessageSnapshot(targetMessage, options.sourceMessageSnapshot);
|
|
443
|
-
const threadReadResult = await transport.readThread(providerSessionId);
|
|
444
|
-
const threadSnapshot = extractCodexThreadHistorySnapshot(threadReadResult);
|
|
445
|
-
const truncatedHistory = truncateCodexThreadHistory(threadSnapshot.value, parsedMessages, targetSnapshot);
|
|
446
|
-
if (truncatedHistory.length === 0) {
|
|
447
|
-
throw new Error("CODEX_FORK_HISTORY_EMPTY");
|
|
448
|
-
}
|
|
449
|
-
if (threadSnapshot.kind !== "turns") {
|
|
450
|
-
throw new Error("CODEX_RECONSTRUCTED_MESSAGE_FORK_NOT_SUPPORTED");
|
|
451
|
-
}
|
|
452
|
-
const rollbackPlan = buildCodexTurnRollbackPlan(threadSnapshot, parsedMessages, targetSnapshot);
|
|
453
|
-
const forked = await transport.forkThread(providerSessionId);
|
|
454
|
-
const finalized = rollbackPlan.numTurnsToRollback > 0
|
|
455
|
-
? await transport.rollbackThread(forked.providerSessionId, rollbackPlan.numTurnsToRollback)
|
|
456
|
-
: forked;
|
|
457
|
-
const childThreadReadResult = await transport.readThread(finalized.providerSessionId);
|
|
458
|
-
if (!this.isForkedChildHistoryAligned(childThreadReadResult, truncatedHistory)) {
|
|
459
|
-
throw new Error("CODEX_NATIVE_MESSAGE_FORK_DIRTY");
|
|
460
|
-
}
|
|
461
|
-
return await this.buildForkResultFromTransport({
|
|
462
|
-
providerSessionId: finalized.providerSessionId,
|
|
463
|
-
rawStoreRef: finalized.rawStoreRef,
|
|
464
|
-
workspacePath,
|
|
465
|
-
fallbackParentProviderSessionId: providerSessionId,
|
|
466
|
-
forkMethod: "native_message_fork",
|
|
467
|
-
forkSourceType: "message",
|
|
468
|
-
providerSourceMessageId: null,
|
|
469
|
-
messageCountOverride: targetSnapshot.sequence,
|
|
470
|
-
inheritedPrefixMessageCountOverride: targetSnapshot.sequence,
|
|
471
|
-
lastMessageAtOverride: targetSnapshot.timestamp
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
finally {
|
|
475
|
-
transport.close();
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
async sendMessage(providerSessionId, rawStoreRef, content, clientRequestId, _permissionMode) {
|
|
479
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
480
|
-
const lineNumber = readJsonLines(resolvedStoreRef).length + 1;
|
|
481
|
-
const acceptedAt = nextTimestamp();
|
|
482
|
-
appendJsonLine(resolvedStoreRef, {
|
|
483
|
-
timestamp: acceptedAt,
|
|
484
|
-
type: "event_msg",
|
|
485
|
-
payload: {
|
|
486
|
-
type: "user_message",
|
|
487
|
-
message: content,
|
|
488
|
-
clientRequestId
|
|
489
|
-
}
|
|
490
|
-
});
|
|
491
|
-
const rawRef = createRawRef(this.providerId, resolvedStoreRef, lineNumber);
|
|
492
|
-
this.historyCache.delete(resolvedStoreRef);
|
|
493
|
-
return {
|
|
494
|
-
acceptedAt,
|
|
495
|
-
clientRequestId,
|
|
496
|
-
message: {
|
|
497
|
-
messageId: messageIdFromRawRef(rawRef),
|
|
498
|
-
provider: this.providerId,
|
|
499
|
-
providerSessionId,
|
|
500
|
-
role: "user",
|
|
501
|
-
kind: "text",
|
|
502
|
-
content,
|
|
503
|
-
toolCall: null,
|
|
504
|
-
timestamp: acceptedAt,
|
|
505
|
-
sequence: this.parseMessages(rawStoreRef, readJsonLines(resolvedStoreRef), providerSessionId).length,
|
|
506
|
-
rawRef
|
|
507
|
-
}
|
|
508
|
-
};
|
|
509
|
-
}
|
|
510
|
-
async readSessionTitle(providerSessionId, rawStoreRef) {
|
|
511
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
512
|
-
const fileSessionId = basename(resolvedStoreRef, ".jsonl");
|
|
513
|
-
const sessionIdentity = this.readSessionIdentity(resolvedStoreRef, fileSessionId);
|
|
514
|
-
const threadMetadataIndex = this.readThreadMetadataIndex();
|
|
515
|
-
const indexedTitle = this.resolveIndexedTitle(threadMetadataIndex, providerSessionId) ??
|
|
516
|
-
(sessionIdentity
|
|
517
|
-
? this.resolveIndexedTitle(threadMetadataIndex, sessionIdentity.threadId)
|
|
518
|
-
: null);
|
|
519
|
-
if (indexedTitle) {
|
|
520
|
-
return indexedTitle;
|
|
521
|
-
}
|
|
522
|
-
const stats = statSync(resolvedStoreRef);
|
|
523
|
-
const cachedSummary = this.sessionSummaryCache.get(resolvedStoreRef);
|
|
524
|
-
if (cachedSummary
|
|
525
|
-
&& cachedSummary.mtimeMs === stats.mtimeMs
|
|
526
|
-
&& cachedSummary.size === stats.size
|
|
527
|
-
&& (cachedSummary.providerSessionId === providerSessionId
|
|
528
|
-
|| (sessionIdentity
|
|
529
|
-
&& cachedSummary.providerSessionId === sessionIdentity.threadId))) {
|
|
530
|
-
this.touchSessionSummaryCache(resolvedStoreRef, cachedSummary);
|
|
531
|
-
if (cachedSummary.title) {
|
|
532
|
-
return cachedSummary.title;
|
|
533
|
-
}
|
|
534
|
-
if (cachedSummary.summary) {
|
|
535
|
-
return cachedSummary.summary.title;
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
const records = readJsonLines(resolvedStoreRef);
|
|
539
|
-
const meta = records.find((record) => record.data.type === "session_meta")?.data;
|
|
540
|
-
const metaPayload = (meta?.payload ?? {});
|
|
541
|
-
const codexSessionId = this.resolveCodexSessionId(metaPayload, providerSessionId || fileSessionId);
|
|
542
|
-
const messages = this.parseMessagesFromEntries(resolvedStoreRef, records, codexSessionId);
|
|
543
|
-
const resolvedTitle = (this.resolveIndexedTitle(threadMetadataIndex, codexSessionId) ??
|
|
544
|
-
resolveCodexFallbackTitle(messages) ??
|
|
545
|
-
fileSessionId);
|
|
546
|
-
this.touchSessionSummaryCache(resolvedStoreRef, {
|
|
547
|
-
filePath: resolvedStoreRef,
|
|
548
|
-
mtimeMs: stats.mtimeMs,
|
|
549
|
-
size: stats.size,
|
|
550
|
-
workspacePath: (sessionIdentity?.cwd ?? ensureText(metaPayload.cwd)) || null,
|
|
551
|
-
providerSessionId: codexSessionId,
|
|
552
|
-
title: resolvedTitle,
|
|
553
|
-
summary: null
|
|
554
|
-
});
|
|
555
|
-
return resolvedTitle;
|
|
556
|
-
}
|
|
557
|
-
async renameSessionTitle(providerSessionId, rawStoreRef, title) {
|
|
558
|
-
const nextTitle = title.trim();
|
|
559
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
560
|
-
const indexPath = join(this.options.homeDir, "session_index.jsonl");
|
|
561
|
-
const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
|
|
562
|
-
statSync(resolvedStoreRef);
|
|
563
|
-
ensureDirectory(this.options.homeDir);
|
|
564
|
-
appendJsonLine(indexPath, {
|
|
565
|
-
id: providerSessionId,
|
|
566
|
-
thread_name: nextTitle
|
|
567
|
-
});
|
|
568
|
-
if (stateDbPath) {
|
|
569
|
-
const DatabaseSync = loadDatabaseSync();
|
|
570
|
-
let db = null;
|
|
571
|
-
try {
|
|
572
|
-
db = new DatabaseSync(stateDbPath, { open: true });
|
|
573
|
-
db.prepare("UPDATE threads SET title = ? WHERE id = ?").run(nextTitle, providerSessionId);
|
|
574
|
-
}
|
|
575
|
-
finally {
|
|
576
|
-
db?.close();
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
this.sessionSummaryCache.delete(resolvedStoreRef);
|
|
580
|
-
return nextTitle;
|
|
581
|
-
}
|
|
582
|
-
async updateSessionArchiveState(providerSessionId, rawStoreRef, isArchived) {
|
|
583
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
584
|
-
const currentFileName = basename(resolvedStoreRef) || `${providerSessionId}.jsonl`;
|
|
585
|
-
const nextStoreRef = isArchived
|
|
586
|
-
? join(this.options.homeDir, "archived_sessions", currentFileName)
|
|
587
|
-
: buildCodexActiveSessionPath(this.options.homeDir, currentFileName);
|
|
588
|
-
const controlResult = await this.updateArchiveStateViaThreadControlTransport(providerSessionId, resolvedStoreRef, nextStoreRef, isArchived);
|
|
589
|
-
let finalStoreRef = nextStoreRef;
|
|
590
|
-
if (controlResult) {
|
|
591
|
-
finalStoreRef = controlResult.rawStoreRef;
|
|
592
|
-
}
|
|
593
|
-
else {
|
|
594
|
-
const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
|
|
595
|
-
statSync(resolvedStoreRef);
|
|
596
|
-
if (resolvedStoreRef !== nextStoreRef) {
|
|
597
|
-
ensureDirectory(dirname(nextStoreRef));
|
|
598
|
-
renameSync(resolvedStoreRef, nextStoreRef);
|
|
599
|
-
}
|
|
600
|
-
if (stateDbPath) {
|
|
601
|
-
const DatabaseSync = loadDatabaseSync();
|
|
602
|
-
let db = null;
|
|
603
|
-
try {
|
|
604
|
-
db = new DatabaseSync(stateDbPath, { open: true });
|
|
605
|
-
db.prepare(`UPDATE threads
|
|
606
|
-
SET archived = ?,
|
|
607
|
-
archived_at = ?,
|
|
608
|
-
rollout_path = ?
|
|
609
|
-
WHERE id = ?`).run(isArchived ? 1 : 0, isArchived ? Math.floor(Date.now() / 1000) : null, nextStoreRef, providerSessionId);
|
|
610
|
-
}
|
|
611
|
-
finally {
|
|
612
|
-
db?.close();
|
|
613
|
-
}
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
this.sessionSummaryCache.delete(resolvedStoreRef);
|
|
617
|
-
this.sessionSummaryCache.delete(finalStoreRef);
|
|
618
|
-
// 归档切换后线程索引的 archived / rollout_path 也变了,不能继续赌文件系统 mtime 一定会跳。
|
|
619
|
-
this.invalidateThreadMetadataIndexCache();
|
|
620
|
-
return {
|
|
621
|
-
rawStoreRef: finalStoreRef,
|
|
622
|
-
isArchived
|
|
623
|
-
};
|
|
624
|
-
}
|
|
625
|
-
async deleteSession(providerSessionId, rawStoreRef) {
|
|
626
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
627
|
-
const threadMetadata = this.readThreadMetadataIndex().get(providerSessionId) ?? null;
|
|
628
|
-
const resolvedMetadataStoreRef = threadMetadata?.rolloutPath && threadMetadata.rolloutPath.trim()
|
|
629
|
-
? this.resolveSessionFilePath(threadMetadata.rolloutPath, providerSessionId)
|
|
630
|
-
: null;
|
|
631
|
-
const candidateFilePaths = new Set([resolvedStoreRef, resolvedMetadataStoreRef].filter((value) => typeof value === "string" && value.trim().length > 0));
|
|
632
|
-
const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
|
|
633
|
-
let deletedAny = false;
|
|
634
|
-
for (const filePath of candidateFilePaths) {
|
|
635
|
-
if (!existsSync(filePath)) {
|
|
636
|
-
continue;
|
|
637
|
-
}
|
|
638
|
-
rmSync(filePath, { force: true });
|
|
639
|
-
this.historyCache.delete(filePath);
|
|
640
|
-
this.sessionSummaryCache.delete(filePath);
|
|
641
|
-
deletedAny = true;
|
|
642
|
-
}
|
|
643
|
-
if (stateDbPath) {
|
|
644
|
-
const DatabaseSync = loadDatabaseSync();
|
|
645
|
-
let db = null;
|
|
646
|
-
try {
|
|
647
|
-
db = new DatabaseSync(stateDbPath, { open: true });
|
|
648
|
-
const result = db.prepare("DELETE FROM threads WHERE id = ?").run(providerSessionId);
|
|
649
|
-
deletedAny = deletedAny || result.changes > 0;
|
|
650
|
-
}
|
|
651
|
-
finally {
|
|
652
|
-
db?.close();
|
|
653
|
-
}
|
|
654
|
-
}
|
|
655
|
-
this.invalidateThreadMetadataIndexCache();
|
|
656
|
-
if (!deletedAny) {
|
|
657
|
-
throw new Error("PROVIDER_SESSION_NOT_FOUND");
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
async updateArchiveStateViaThreadControlTransport(providerSessionId, resolvedStoreRef, nextStoreRef, isArchived) {
|
|
661
|
-
const createTransport = this.options.threadControlTransportFactory;
|
|
662
|
-
if (!createTransport) {
|
|
663
|
-
return null;
|
|
664
|
-
}
|
|
665
|
-
const transport = createTransport();
|
|
666
|
-
try {
|
|
667
|
-
await transport.initialize();
|
|
668
|
-
if (isArchived) {
|
|
669
|
-
await transport.archiveThread(providerSessionId);
|
|
670
|
-
}
|
|
671
|
-
else {
|
|
672
|
-
await transport.unarchiveThread(providerSessionId);
|
|
673
|
-
}
|
|
674
|
-
const result = await transport.readThread(providerSessionId).catch(() => null);
|
|
675
|
-
const thread = result && typeof result === "object"
|
|
676
|
-
? (result.thread ?? null)
|
|
677
|
-
: null;
|
|
678
|
-
const appServerRawStoreRef = ensureText(thread?.path).trim();
|
|
679
|
-
const resolvedNextStoreRef = appServerRawStoreRef.length > 0
|
|
680
|
-
? this.resolveSessionFilePath(appServerRawStoreRef, providerSessionId)
|
|
681
|
-
: this.resolveSessionFilePath(nextStoreRef, providerSessionId);
|
|
682
|
-
this.sessionSummaryCache.delete(resolvedStoreRef);
|
|
683
|
-
this.sessionSummaryCache.delete(resolvedNextStoreRef);
|
|
684
|
-
return {
|
|
685
|
-
rawStoreRef: resolvedNextStoreRef
|
|
686
|
-
};
|
|
687
|
-
}
|
|
688
|
-
catch {
|
|
689
|
-
return null;
|
|
690
|
-
}
|
|
691
|
-
finally {
|
|
692
|
-
transport.close();
|
|
693
|
-
}
|
|
694
|
-
}
|
|
695
|
-
getProviderCapabilities() {
|
|
696
|
-
return {
|
|
697
|
-
provider: this.providerId,
|
|
698
|
-
canStartSession: true,
|
|
699
|
-
canResumeSession: true,
|
|
700
|
-
canSendMessage: true,
|
|
701
|
-
inRunInputMode: "streaming_guidance",
|
|
702
|
-
supportsSubagents: true,
|
|
703
|
-
supportsInterrupt: true,
|
|
704
|
-
supportsStructuredToolCalls: true,
|
|
705
|
-
supportsTokenUsage: true,
|
|
706
|
-
supportsAttachments: true,
|
|
707
|
-
supportsPermissionPrompt: true,
|
|
708
|
-
supportsCheckpoint: false,
|
|
709
|
-
supportsSessionFork: true,
|
|
710
|
-
supportsSessionDelete: true,
|
|
711
|
-
limitations: [
|
|
712
|
-
"运行中追加消息依赖 Codex CLI app-server 暴露 turn/steer;当前项目实测 codex-cli 0.118.0 可用。",
|
|
713
|
-
"当前 npm SDK 仍只有 run/runStreamed 轮询式接口,宿主运行时需经由 Codex CLI app-server 才能直发 steer。"
|
|
714
|
-
]
|
|
715
|
-
};
|
|
716
|
-
}
|
|
717
|
-
async getSessionCapabilities() {
|
|
718
|
-
return this.getProviderCapabilities();
|
|
719
|
-
}
|
|
720
|
-
async readContextUsage(providerSessionId, rawStoreRef) {
|
|
721
|
-
const resolvedStoreRef = this.resolveSessionFilePath(rawStoreRef, providerSessionId);
|
|
722
|
-
const records = readJsonLines(resolvedStoreRef).map((record) => record.data);
|
|
723
|
-
for (let index = records.length - 1; index >= 0; index -= 1) {
|
|
724
|
-
const record = records[index];
|
|
725
|
-
if (ensureText(record.type).trim() !== "event_msg") {
|
|
726
|
-
continue;
|
|
727
|
-
}
|
|
728
|
-
const payload = (record.payload ?? {});
|
|
729
|
-
if (ensureText(payload.type).trim() !== "token_count") {
|
|
730
|
-
continue;
|
|
731
|
-
}
|
|
732
|
-
const info = (payload.info ?? {});
|
|
733
|
-
const lastUsage = (info.last_token_usage ?? {});
|
|
734
|
-
const uncachedInputTokens = readNonNegativeInteger(lastUsage.input_tokens);
|
|
735
|
-
const cachedInputTokens = readNonNegativeInteger(lastUsage.cached_input_tokens);
|
|
736
|
-
if (uncachedInputTokens === null && cachedInputTokens === null) {
|
|
737
|
-
continue;
|
|
738
|
-
}
|
|
739
|
-
const promptTokens = uncachedInputTokens ?? 0;
|
|
740
|
-
const modelId = ensureText(info.model ?? info.model_id).trim() || null;
|
|
741
|
-
const runtimeContextWindow = readNonNegativeInteger(info.model_context_window);
|
|
742
|
-
const contextWindow = runtimeContextWindow
|
|
743
|
-
?? resolveCodexKnownContextWindow(modelId)
|
|
744
|
-
?? readCodexConfigContextWindow(this.options.homeDir);
|
|
745
|
-
if (contextWindow === null || contextWindow <= 0) {
|
|
746
|
-
return null;
|
|
747
|
-
}
|
|
748
|
-
return {
|
|
749
|
-
provider: this.providerId,
|
|
750
|
-
promptTokens,
|
|
751
|
-
uncachedInputTokens: uncachedInputTokens ?? 0,
|
|
752
|
-
cachedInputTokens: cachedInputTokens ?? 0,
|
|
753
|
-
contextWindow,
|
|
754
|
-
usageRatio: clampUsageRatio(promptTokens, contextWindow),
|
|
755
|
-
source: "provider-log",
|
|
756
|
-
contextWindowSource: runtimeContextWindow !== null
|
|
757
|
-
? "provider-log"
|
|
758
|
-
: resolveCodexKnownContextWindow(modelId) !== null
|
|
759
|
-
? "model-map"
|
|
760
|
-
: "provider-config",
|
|
761
|
-
modelId,
|
|
762
|
-
capturedAt: safeDate(record.timestamp, "").trim() || null,
|
|
763
|
-
isEstimated: runtimeContextWindow === null
|
|
764
|
-
};
|
|
765
|
-
}
|
|
766
|
-
return null;
|
|
767
|
-
}
|
|
768
|
-
readThreadMetadataIndex() {
|
|
769
|
-
const indexPath = join(this.options.homeDir, "session_index.jsonl");
|
|
770
|
-
const indexPathMtimeMs = existsSync(indexPath) ? statSync(indexPath).mtimeMs : null;
|
|
771
|
-
const stateDbPath = findLatestCodexStateDatabase(this.options.homeDir);
|
|
772
|
-
const stateDbMtimeMs = stateDbPath && existsSync(stateDbPath) ? statSync(stateDbPath).mtimeMs : null;
|
|
773
|
-
const cached = this.threadMetadataIndexCache;
|
|
774
|
-
if (cached
|
|
775
|
-
&& cached.indexPathMtimeMs === indexPathMtimeMs
|
|
776
|
-
&& cached.stateDbPath === stateDbPath
|
|
777
|
-
&& cached.stateDbMtimeMs === stateDbMtimeMs) {
|
|
778
|
-
return cached.index;
|
|
779
|
-
}
|
|
780
|
-
const index = new Map();
|
|
781
|
-
if (existsSync(indexPath)) {
|
|
782
|
-
const lines = readFileSync(indexPath, "utf8")
|
|
783
|
-
.split(/\r?\n/)
|
|
784
|
-
.filter((line) => line.trim().length > 0);
|
|
785
|
-
// 这里容忍单行脏数据,避免某一条坏记录把整个会话列表拖死。
|
|
786
|
-
for (const line of lines) {
|
|
787
|
-
try {
|
|
788
|
-
const record = JSON.parse(line);
|
|
789
|
-
const id = ensureText(record.id).trim();
|
|
790
|
-
if (id.length === 0) {
|
|
791
|
-
continue;
|
|
792
|
-
}
|
|
793
|
-
index.set(id, {
|
|
794
|
-
title: normalizeCodexIndexedTitle(ensureText(record.thread_name)) || null,
|
|
795
|
-
cwd: null,
|
|
796
|
-
createdAtMs: null,
|
|
797
|
-
firstUserMessage: null,
|
|
798
|
-
agentNickname: null,
|
|
799
|
-
agentRole: null,
|
|
800
|
-
isArchived: null,
|
|
801
|
-
rolloutPath: null
|
|
802
|
-
});
|
|
803
|
-
}
|
|
804
|
-
catch {
|
|
805
|
-
continue;
|
|
806
|
-
}
|
|
807
|
-
}
|
|
808
|
-
}
|
|
809
|
-
if (!stateDbPath) {
|
|
810
|
-
this.threadMetadataIndexCache = {
|
|
811
|
-
indexPathMtimeMs,
|
|
812
|
-
stateDbPath: null,
|
|
813
|
-
stateDbMtimeMs: null,
|
|
814
|
-
index
|
|
815
|
-
};
|
|
816
|
-
return index;
|
|
817
|
-
}
|
|
818
|
-
const DatabaseSync = loadDatabaseSync();
|
|
819
|
-
let db = null;
|
|
820
|
-
try {
|
|
821
|
-
db = new DatabaseSync(stateDbPath, { open: true, readOnly: true });
|
|
822
|
-
const rows = db.prepare(`SELECT
|
|
823
|
-
id,
|
|
824
|
-
title,
|
|
825
|
-
cwd,
|
|
826
|
-
created_at,
|
|
827
|
-
archived,
|
|
828
|
-
first_user_message,
|
|
829
|
-
agent_nickname,
|
|
830
|
-
agent_role,
|
|
831
|
-
rollout_path
|
|
832
|
-
FROM threads`).all();
|
|
833
|
-
for (const row of rows) {
|
|
834
|
-
const id = ensureText(row.id).trim();
|
|
835
|
-
if (id.length === 0) {
|
|
836
|
-
continue;
|
|
837
|
-
}
|
|
838
|
-
const current = index.get(id);
|
|
839
|
-
const dbTitle = normalizeCodexIndexedTitle(ensureText(row.title)) || null;
|
|
840
|
-
const createdAtSeconds = typeof row.created_at === "number"
|
|
841
|
-
? row.created_at
|
|
842
|
-
: Number.parseInt(ensureText(row.created_at), 10);
|
|
843
|
-
index.set(id, {
|
|
844
|
-
title: current?.title ?? dbTitle,
|
|
845
|
-
cwd: ensureText(row.cwd).trim() || (current?.cwd ?? null),
|
|
846
|
-
createdAtMs: Number.isFinite(createdAtSeconds) ? createdAtSeconds * 1000 : null,
|
|
847
|
-
firstUserMessage: ensureText(row.first_user_message).trim() || (current?.firstUserMessage ?? null),
|
|
848
|
-
agentNickname: ensureText(row.agent_nickname).trim() || (current?.agentNickname ?? null),
|
|
849
|
-
agentRole: ensureText(row.agent_role).trim() || (current?.agentRole ?? null),
|
|
850
|
-
rolloutPath: ensureText(row.rollout_path).trim() || (current?.rolloutPath ?? null),
|
|
851
|
-
isArchived: typeof row.archived === "number"
|
|
852
|
-
? row.archived === 1
|
|
853
|
-
: ensureText(row.rollout_path).includes("archived_sessions")
|
|
854
|
-
? true
|
|
855
|
-
: (current?.isArchived ?? null)
|
|
856
|
-
});
|
|
857
|
-
}
|
|
858
|
-
}
|
|
859
|
-
catch {
|
|
860
|
-
this.threadMetadataIndexCache = {
|
|
861
|
-
indexPathMtimeMs,
|
|
862
|
-
stateDbPath,
|
|
863
|
-
stateDbMtimeMs,
|
|
864
|
-
index
|
|
865
|
-
};
|
|
866
|
-
return index;
|
|
867
|
-
}
|
|
868
|
-
finally {
|
|
869
|
-
db?.close();
|
|
870
|
-
}
|
|
871
|
-
this.threadMetadataIndexCache = {
|
|
872
|
-
indexPathMtimeMs,
|
|
873
|
-
stateDbPath,
|
|
874
|
-
stateDbMtimeMs,
|
|
875
|
-
index
|
|
876
|
-
};
|
|
877
|
-
return index;
|
|
878
|
-
}
|
|
879
|
-
listSessionFiles(targetPath, threadMetadataIndex, knownSessions) {
|
|
880
|
-
const activeFiles = walkJsonlFiles(join(this.options.homeDir, "sessions"));
|
|
881
|
-
const archivedFiles = this.listArchivedSessionFiles(targetPath, threadMetadataIndex, knownSessions);
|
|
882
|
-
return [...activeFiles, ...archivedFiles];
|
|
883
|
-
}
|
|
884
|
-
listArchivedSessionFiles(targetPath, threadMetadataIndex, knownSessions) {
|
|
885
|
-
const archivedFiles = new Set();
|
|
886
|
-
for (const metadata of threadMetadataIndex.values()) {
|
|
887
|
-
if (metadata.isArchived !== true
|
|
888
|
-
|| !metadata.rolloutPath) {
|
|
889
|
-
continue;
|
|
890
|
-
}
|
|
891
|
-
if (targetPath.length > 0
|
|
892
|
-
&& normalizeWorkspacePath(metadata.cwd ?? "") !== targetPath) {
|
|
893
|
-
continue;
|
|
894
|
-
}
|
|
895
|
-
archivedFiles.add(metadata.rolloutPath);
|
|
896
|
-
}
|
|
897
|
-
for (const metadata of threadMetadataIndex.values()) {
|
|
898
|
-
if (metadata.isArchived === true
|
|
899
|
-
|| !metadata.rolloutPath) {
|
|
900
|
-
continue;
|
|
901
|
-
}
|
|
902
|
-
if (targetPath.length > 0
|
|
903
|
-
&& normalizeWorkspacePath(metadata.cwd ?? "") !== targetPath) {
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
const archivedCandidate = this.resolveArchivedSessionCandidate(metadata.rolloutPath);
|
|
907
|
-
if (archivedCandidate) {
|
|
908
|
-
archivedFiles.add(archivedCandidate);
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
for (const session of knownSessions) {
|
|
912
|
-
if (session.isArchived !== true
|
|
913
|
-
|| normalizeWorkspacePath(session.workspacePath) !== targetPath
|
|
914
|
-
|| !isCodexArchivedFilePath(session.rawStoreRef)) {
|
|
915
|
-
continue;
|
|
916
|
-
}
|
|
917
|
-
archivedFiles.add(session.rawStoreRef);
|
|
918
|
-
}
|
|
919
|
-
for (const session of knownSessions) {
|
|
920
|
-
if (session.isArchived === true
|
|
921
|
-
|| normalizeWorkspacePath(session.workspacePath) !== targetPath) {
|
|
922
|
-
continue;
|
|
923
|
-
}
|
|
924
|
-
const archivedCandidate = this.resolveArchivedSessionCandidate(session.rawStoreRef);
|
|
925
|
-
if (archivedCandidate) {
|
|
926
|
-
archivedFiles.add(archivedCandidate);
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
if (archivedFiles.size > 0) {
|
|
930
|
-
return [...archivedFiles].filter((filePath) => existsSync(filePath));
|
|
931
|
-
}
|
|
932
|
-
if (threadMetadataIndex.size === 0) {
|
|
933
|
-
return walkJsonlFiles(join(this.options.homeDir, "archived_sessions"));
|
|
934
|
-
}
|
|
935
|
-
return [];
|
|
936
|
-
}
|
|
937
|
-
resolveArchivedSessionCandidate(filePath) {
|
|
938
|
-
if (!filePath || existsSync(filePath)) {
|
|
939
|
-
return null;
|
|
940
|
-
}
|
|
941
|
-
const archivedCandidate = join(this.options.homeDir, "archived_sessions", basename(filePath));
|
|
942
|
-
if (!existsSync(archivedCandidate)) {
|
|
943
|
-
return null;
|
|
944
|
-
}
|
|
945
|
-
return archivedCandidate;
|
|
946
|
-
}
|
|
947
|
-
resolveSessionFilePath(rawStoreRef, providerSessionId) {
|
|
948
|
-
const matchedByThreadId = this.findSessionFileByThreadId(providerSessionId);
|
|
949
|
-
if (existsSync(rawStoreRef)) {
|
|
950
|
-
const boundThreadId = this.readThreadIdFromRawStore(rawStoreRef);
|
|
951
|
-
if (!boundThreadId || boundThreadId === providerSessionId) {
|
|
952
|
-
return rawStoreRef;
|
|
953
|
-
}
|
|
954
|
-
if (matchedByThreadId) {
|
|
955
|
-
return matchedByThreadId;
|
|
956
|
-
}
|
|
957
|
-
return buildSyntheticCodexHistoryPath(this.options.homeDir, providerSessionId);
|
|
958
|
-
}
|
|
959
|
-
const fileName = basename(rawStoreRef) || `${providerSessionId}.jsonl`;
|
|
960
|
-
const match = fileName.match(/^rollout-(\d{4})-(\d{2})-(\d{2})T.+\.jsonl$/);
|
|
961
|
-
const candidates = [
|
|
962
|
-
join(this.options.homeDir, "archived_sessions", fileName),
|
|
963
|
-
match
|
|
964
|
-
? join(this.options.homeDir, "sessions", match[1], match[2], match[3], fileName)
|
|
965
|
-
: null
|
|
966
|
-
].filter((value) => value !== null);
|
|
967
|
-
const matchedPath = candidates.find((candidate) => existsSync(candidate));
|
|
968
|
-
if (matchedPath) {
|
|
969
|
-
return matchedPath;
|
|
970
|
-
}
|
|
971
|
-
if (matchedByThreadId) {
|
|
972
|
-
return matchedByThreadId;
|
|
973
|
-
}
|
|
974
|
-
if (isSyntheticCodexHistoryPath(rawStoreRef)) {
|
|
975
|
-
return rawStoreRef;
|
|
976
|
-
}
|
|
977
|
-
return buildSyntheticCodexHistoryPath(this.options.homeDir, providerSessionId);
|
|
978
|
-
}
|
|
979
|
-
findSessionFileByThreadId(providerSessionId) {
|
|
980
|
-
const threadMetadataIndex = this.readThreadMetadataIndex();
|
|
981
|
-
const activeFiles = walkJsonlFiles(join(this.options.homeDir, "sessions"));
|
|
982
|
-
const archivedFiles = this.listArchivedSessionFiles("", threadMetadataIndex, []);
|
|
983
|
-
for (const filePath of [...activeFiles, ...archivedFiles]) {
|
|
984
|
-
const threadId = this.readThreadIdFromRawStore(filePath);
|
|
985
|
-
if (threadId === providerSessionId) {
|
|
986
|
-
return filePath;
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
return null;
|
|
990
|
-
}
|
|
991
|
-
readThreadIdFromRawStore(filePath) {
|
|
992
|
-
if (!existsSync(filePath)) {
|
|
993
|
-
return null;
|
|
994
|
-
}
|
|
995
|
-
const firstLine = readFirstNonEmptyLine(filePath);
|
|
996
|
-
if (!firstLine) {
|
|
997
|
-
return null;
|
|
998
|
-
}
|
|
999
|
-
try {
|
|
1000
|
-
const record = JSON.parse(firstLine);
|
|
1001
|
-
if (ensureText(record.type).trim() !== "session_meta") {
|
|
1002
|
-
return null;
|
|
1003
|
-
}
|
|
1004
|
-
const threadId = ensureText(record.payload?.id).trim();
|
|
1005
|
-
return threadId.length > 0 ? threadId : null;
|
|
1006
|
-
}
|
|
1007
|
-
catch {
|
|
1008
|
-
return null;
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
getParsedMessages(filePath, providerSessionId) {
|
|
1012
|
-
const stats = statSync(filePath);
|
|
1013
|
-
const cached = this.historyCache.get(filePath);
|
|
1014
|
-
if (cached
|
|
1015
|
-
&& cached.providerSessionId === providerSessionId
|
|
1016
|
-
&& cached.mtimeMs === stats.mtimeMs
|
|
1017
|
-
&& cached.size === stats.size) {
|
|
1018
|
-
this.touchHistoryCache(filePath, cached);
|
|
1019
|
-
return cached.messages;
|
|
1020
|
-
}
|
|
1021
|
-
const records = readJsonLines(filePath);
|
|
1022
|
-
const messages = this.parseMessagesFromEntries(filePath, records, providerSessionId);
|
|
1023
|
-
this.touchHistoryCache(filePath, {
|
|
1024
|
-
filePath,
|
|
1025
|
-
providerSessionId,
|
|
1026
|
-
mtimeMs: stats.mtimeMs,
|
|
1027
|
-
size: stats.size,
|
|
1028
|
-
messages
|
|
1029
|
-
});
|
|
1030
|
-
return messages;
|
|
1031
|
-
}
|
|
1032
|
-
touchHistoryCache(filePath, entry) {
|
|
1033
|
-
this.historyCache.delete(filePath);
|
|
1034
|
-
this.historyCache.set(filePath, entry);
|
|
1035
|
-
while (this.historyCache.size > HISTORY_CACHE_LIMIT) {
|
|
1036
|
-
const oldestKey = this.historyCache.keys().next().value;
|
|
1037
|
-
if (!oldestKey) {
|
|
1038
|
-
break;
|
|
1039
|
-
}
|
|
1040
|
-
this.historyCache.delete(oldestKey);
|
|
1041
|
-
}
|
|
1042
|
-
}
|
|
1043
|
-
async buildForkResultFromTransport(input) {
|
|
1044
|
-
const resolvedStoreRef = input.rawStoreRef
|
|
1045
|
-
? this.resolveSessionFilePath(input.rawStoreRef, input.providerSessionId)
|
|
1046
|
-
: this.findSessionFileByThreadId(input.providerSessionId)
|
|
1047
|
-
?? buildCodexActiveSessionPath(this.options.homeDir, `${input.providerSessionId}.jsonl`);
|
|
1048
|
-
const messages = existsSync(resolvedStoreRef)
|
|
1049
|
-
? this.getParsedMessages(resolvedStoreRef, input.providerSessionId)
|
|
1050
|
-
: [];
|
|
1051
|
-
const threadMetadataIndex = this.readThreadMetadataIndex();
|
|
1052
|
-
const threadMetadata = threadMetadataIndex.get(input.providerSessionId) ?? null;
|
|
1053
|
-
const title = this.resolveIndexedTitle(threadMetadataIndex, input.providerSessionId)
|
|
1054
|
-
?? resolveCodexFallbackTitle(messages)
|
|
1055
|
-
?? "";
|
|
1056
|
-
return {
|
|
1057
|
-
session: {
|
|
1058
|
-
provider: this.providerId,
|
|
1059
|
-
providerSessionId: input.providerSessionId,
|
|
1060
|
-
title,
|
|
1061
|
-
workspacePath: input.workspacePath,
|
|
1062
|
-
rawStoreRef: resolvedStoreRef,
|
|
1063
|
-
isArchived: resolveCodexArchivedState(threadMetadata, resolvedStoreRef),
|
|
1064
|
-
lastMessageAt: input.lastMessageAtOverride ?? messages.at(-1)?.timestamp ?? nextTimestamp(),
|
|
1065
|
-
messageCount: input.messageCountOverride ?? messages.length,
|
|
1066
|
-
parentProviderSessionId: input.fallbackParentProviderSessionId
|
|
1067
|
-
},
|
|
1068
|
-
forkMethod: input.forkMethod,
|
|
1069
|
-
forkSourceType: input.forkSourceType,
|
|
1070
|
-
inheritedPrefixMessageCount: input.inheritedPrefixMessageCountOverride ?? messages.length,
|
|
1071
|
-
providerSourceMessageId: input.providerSourceMessageId
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
async forkThreadWithHistoryFallback(transport, providerSessionId, workspacePath, rawStoreRef) {
|
|
1075
|
-
try {
|
|
1076
|
-
return await transport.forkThread(providerSessionId);
|
|
1077
|
-
}
|
|
1078
|
-
catch (error) {
|
|
1079
|
-
const history = buildCodexResumeHistoryFromRawStore(rawStoreRef);
|
|
1080
|
-
if (!shouldFallbackCodexForkFromHistory(error, history)) {
|
|
1081
|
-
throw error;
|
|
1082
|
-
}
|
|
1083
|
-
// app-server 的 thread/fork 依赖源 thread 已经挂在当前连接上。
|
|
1084
|
-
// 这个前提跨请求就会失效,所以这里退回到本地 transcript 冷恢复一次。
|
|
1085
|
-
const rebuilt = await transport.resumeThreadFromHistory({
|
|
1086
|
-
providerSessionId: null,
|
|
1087
|
-
workspacePath,
|
|
1088
|
-
history,
|
|
1089
|
-
model: null
|
|
1090
|
-
});
|
|
1091
|
-
return await transport.forkThread(rebuilt.providerSessionId);
|
|
1092
|
-
}
|
|
1093
|
-
}
|
|
1094
|
-
touchSessionSummaryCache(filePath, entry) {
|
|
1095
|
-
this.sessionSummaryCache.delete(filePath);
|
|
1096
|
-
this.sessionSummaryCache.set(filePath, entry);
|
|
1097
|
-
while (this.sessionSummaryCache.size > SESSION_SUMMARY_CACHE_LIMIT) {
|
|
1098
|
-
const oldestKey = this.sessionSummaryCache.keys().next().value;
|
|
1099
|
-
if (!oldestKey) {
|
|
1100
|
-
break;
|
|
1101
|
-
}
|
|
1102
|
-
this.sessionSummaryCache.delete(oldestKey);
|
|
1103
|
-
}
|
|
1104
|
-
}
|
|
1105
|
-
invalidateThreadMetadataIndexCache() {
|
|
1106
|
-
this.threadMetadataIndexCache = null;
|
|
1107
|
-
}
|
|
1108
|
-
touchSpawnRelationScanCache(filePath, entry) {
|
|
1109
|
-
this.spawnRelationScanCache.delete(filePath);
|
|
1110
|
-
this.spawnRelationScanCache.set(filePath, entry);
|
|
1111
|
-
while (this.spawnRelationScanCache.size > SPAWN_RELATION_SCAN_CACHE_LIMIT) {
|
|
1112
|
-
const oldestKey = this.spawnRelationScanCache.keys().next().value;
|
|
1113
|
-
if (!oldestKey) {
|
|
1114
|
-
break;
|
|
1115
|
-
}
|
|
1116
|
-
this.spawnRelationScanCache.delete(oldestKey);
|
|
1117
|
-
}
|
|
1118
|
-
}
|
|
1119
|
-
isForkedChildHistoryAligned(childThreadReadResult, expectedHistory) {
|
|
1120
|
-
const expectedSignatures = collectCodexForkComparableSignatures(expectedHistory);
|
|
1121
|
-
if (expectedSignatures.length === 0) {
|
|
1122
|
-
return false;
|
|
1123
|
-
}
|
|
1124
|
-
let childHistory;
|
|
1125
|
-
try {
|
|
1126
|
-
childHistory = extractCodexThreadHistory(childThreadReadResult);
|
|
1127
|
-
}
|
|
1128
|
-
catch {
|
|
1129
|
-
return false;
|
|
1130
|
-
}
|
|
1131
|
-
const childSignatures = collectCodexForkComparableSignatures(childHistory);
|
|
1132
|
-
if (childSignatures.length !== expectedSignatures.length) {
|
|
1133
|
-
return false;
|
|
1134
|
-
}
|
|
1135
|
-
return expectedSignatures.every((signature, index) => childSignatures[index] === signature);
|
|
1136
|
-
}
|
|
1137
|
-
resolveCodexSessionId(metaPayload, providerSessionId) {
|
|
1138
|
-
const metaId = ensureText(metaPayload.id).trim();
|
|
1139
|
-
const normalizedProviderSessionId = ensureText(providerSessionId).trim();
|
|
1140
|
-
if (looksLikeCodexThreadId(metaId)) {
|
|
1141
|
-
return metaId;
|
|
1142
|
-
}
|
|
1143
|
-
const matched = normalizedProviderSessionId.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i);
|
|
1144
|
-
if (matched?.[1]) {
|
|
1145
|
-
return matched[1];
|
|
1146
|
-
}
|
|
1147
|
-
return metaId || normalizedProviderSessionId;
|
|
1148
|
-
}
|
|
1149
|
-
resolveIndexedTitle(index, sessionId) {
|
|
1150
|
-
const metadata = index.get(sessionId);
|
|
1151
|
-
const indexedTitle = normalizeCodexIndexedTitle(metadata?.title);
|
|
1152
|
-
const normalizedFirstUserMessage = normalizeCodexIndexedTitle(metadata?.firstUserMessage);
|
|
1153
|
-
if (indexedTitle) {
|
|
1154
|
-
// Codex 有时会把第一条用户消息原样回填成 title,这种脏标题仍然按统一长度预算裁掉。
|
|
1155
|
-
if (normalizedFirstUserMessage && indexedTitle === normalizedFirstUserMessage) {
|
|
1156
|
-
return indexedTitle.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH);
|
|
1157
|
-
}
|
|
1158
|
-
return indexedTitle;
|
|
1159
|
-
}
|
|
1160
|
-
return normalizeCodexMessageTitle(metadata?.firstUserMessage);
|
|
1161
|
-
}
|
|
1162
|
-
readSpawnedAgentRelationIndex(files, targetPath, threadMetadataIndex, candidateThreadIds) {
|
|
1163
|
-
const directRelations = new Map();
|
|
1164
|
-
const spawnRecords = [];
|
|
1165
|
-
for (const entry of files) {
|
|
1166
|
-
const { filePath, stats } = entry;
|
|
1167
|
-
const cached = this.spawnRelationScanCache.get(filePath);
|
|
1168
|
-
if (cached
|
|
1169
|
-
&& cached.mtimeMs === stats.mtimeMs
|
|
1170
|
-
&& cached.size === stats.size) {
|
|
1171
|
-
this.touchSpawnRelationScanCache(filePath, cached);
|
|
1172
|
-
if (cached.workspacePath !== targetPath) {
|
|
1173
|
-
continue;
|
|
1174
|
-
}
|
|
1175
|
-
for (const [threadId, relation] of cached.directRelations) {
|
|
1176
|
-
directRelations.set(threadId, relation);
|
|
1177
|
-
}
|
|
1178
|
-
spawnRecords.push(...cached.spawnRecords);
|
|
1179
|
-
continue;
|
|
1180
|
-
}
|
|
1181
|
-
const sessionIdentity = entry.sessionIdentity ?? this.readSessionIdentity(filePath, basename(filePath, ".jsonl"));
|
|
1182
|
-
if (!sessionIdentity) {
|
|
1183
|
-
this.touchSpawnRelationScanCache(filePath, {
|
|
1184
|
-
filePath,
|
|
1185
|
-
mtimeMs: stats.mtimeMs,
|
|
1186
|
-
size: stats.size,
|
|
1187
|
-
workspacePath: null,
|
|
1188
|
-
directRelations: [],
|
|
1189
|
-
spawnRecords: []
|
|
1190
|
-
});
|
|
1191
|
-
continue;
|
|
1192
|
-
}
|
|
1193
|
-
const workspacePath = normalizeWorkspacePath(sessionIdentity.cwd);
|
|
1194
|
-
if (workspacePath !== targetPath) {
|
|
1195
|
-
this.touchSpawnRelationScanCache(filePath, {
|
|
1196
|
-
filePath,
|
|
1197
|
-
mtimeMs: stats.mtimeMs,
|
|
1198
|
-
size: stats.size,
|
|
1199
|
-
workspacePath,
|
|
1200
|
-
directRelations: [],
|
|
1201
|
-
spawnRecords: []
|
|
1202
|
-
});
|
|
1203
|
-
continue;
|
|
1204
|
-
}
|
|
1205
|
-
if (sessionIdentity.parentThreadId) {
|
|
1206
|
-
const relation = {
|
|
1207
|
-
parentProviderSessionId: sessionIdentity.parentThreadId,
|
|
1208
|
-
kind: sessionIdentity.parentThreadKind ?? "fork"
|
|
1209
|
-
};
|
|
1210
|
-
directRelations.set(sessionIdentity.threadId, relation);
|
|
1211
|
-
this.touchSpawnRelationScanCache(filePath, {
|
|
1212
|
-
filePath,
|
|
1213
|
-
mtimeMs: stats.mtimeMs,
|
|
1214
|
-
size: stats.size,
|
|
1215
|
-
workspacePath,
|
|
1216
|
-
directRelations: [[sessionIdentity.threadId, relation]],
|
|
1217
|
-
spawnRecords: []
|
|
1218
|
-
});
|
|
1219
|
-
continue;
|
|
1220
|
-
}
|
|
1221
|
-
const records = readJsonLines(filePath).map((record) => record.data);
|
|
1222
|
-
const spawnCallById = new Map();
|
|
1223
|
-
const fileSpawnRecords = [];
|
|
1224
|
-
const fileDirectRelations = [];
|
|
1225
|
-
for (const record of records) {
|
|
1226
|
-
if (record.type !== "response_item") {
|
|
1227
|
-
continue;
|
|
1228
|
-
}
|
|
1229
|
-
const payload = (record.payload ?? {});
|
|
1230
|
-
const payloadType = ensureText(payload.type).trim();
|
|
1231
|
-
if (payloadType === "function_call" && ensureText(payload.name).trim() === "spawn_agent") {
|
|
1232
|
-
const callId = ensureText(payload.call_id).trim();
|
|
1233
|
-
const args = parseStructuredJson(ensureText(payload.arguments));
|
|
1234
|
-
const message = ensureText(args?.message).trim();
|
|
1235
|
-
if (callId.length === 0 || message.length === 0) {
|
|
1236
|
-
continue;
|
|
1237
|
-
}
|
|
1238
|
-
const spawnRecord = {
|
|
1239
|
-
parentProviderSessionId: sessionIdentity.threadId,
|
|
1240
|
-
workspacePath: sessionIdentity.cwd,
|
|
1241
|
-
message,
|
|
1242
|
-
timestampMs: toTimestampMs(record.timestamp)
|
|
1243
|
-
};
|
|
1244
|
-
spawnCallById.set(callId, spawnRecord);
|
|
1245
|
-
fileSpawnRecords.push(spawnRecord);
|
|
1246
|
-
spawnRecords.push(spawnRecord);
|
|
1247
|
-
continue;
|
|
1248
|
-
}
|
|
1249
|
-
if (payloadType !== "function_call_output") {
|
|
1250
|
-
continue;
|
|
1251
|
-
}
|
|
1252
|
-
const callId = ensureText(payload.call_id).trim();
|
|
1253
|
-
const spawnRecord = spawnCallById.get(callId);
|
|
1254
|
-
if (!spawnRecord) {
|
|
1255
|
-
continue;
|
|
1256
|
-
}
|
|
1257
|
-
const agentId = parseCodexAgentIdFromToolOutput(ensureText(payload.output));
|
|
1258
|
-
if (!agentId) {
|
|
1259
|
-
continue;
|
|
1260
|
-
}
|
|
1261
|
-
const relation = {
|
|
1262
|
-
parentProviderSessionId: spawnRecord.parentProviderSessionId,
|
|
1263
|
-
kind: "spawn"
|
|
1264
|
-
};
|
|
1265
|
-
directRelations.set(agentId, relation);
|
|
1266
|
-
fileDirectRelations.push([agentId, relation]);
|
|
1267
|
-
}
|
|
1268
|
-
this.touchSpawnRelationScanCache(filePath, {
|
|
1269
|
-
filePath,
|
|
1270
|
-
mtimeMs: stats.mtimeMs,
|
|
1271
|
-
size: stats.size,
|
|
1272
|
-
workspacePath,
|
|
1273
|
-
directRelations: fileDirectRelations,
|
|
1274
|
-
spawnRecords: fileSpawnRecords
|
|
1275
|
-
});
|
|
1276
|
-
}
|
|
1277
|
-
const relationCandidates = candidateThreadIds === undefined
|
|
1278
|
-
? [...threadMetadataIndex.entries()]
|
|
1279
|
-
: [...new Set(candidateThreadIds)]
|
|
1280
|
-
.map((threadId) => [threadId, threadMetadataIndex.get(threadId) ?? null])
|
|
1281
|
-
.filter((entry) => entry[1] !== null);
|
|
1282
|
-
for (const [threadId, metadata] of relationCandidates) {
|
|
1283
|
-
if (directRelations.has(threadId)) {
|
|
1284
|
-
continue;
|
|
1285
|
-
}
|
|
1286
|
-
if (!metadata.agentRole && !metadata.agentNickname) {
|
|
1287
|
-
continue;
|
|
1288
|
-
}
|
|
1289
|
-
const firstUserMessage = metadata.firstUserMessage?.trim();
|
|
1290
|
-
const workspacePath = metadata.cwd ? normalizeWorkspacePath(metadata.cwd) : null;
|
|
1291
|
-
if (!firstUserMessage || workspacePath !== targetPath) {
|
|
1292
|
-
continue;
|
|
1293
|
-
}
|
|
1294
|
-
const matchedSpawn = pickClosestCodexSpawnRecord(spawnRecords, workspacePath, firstUserMessage, metadata.createdAtMs);
|
|
1295
|
-
if (!matchedSpawn) {
|
|
1296
|
-
continue;
|
|
1297
|
-
}
|
|
1298
|
-
directRelations.set(threadId, {
|
|
1299
|
-
parentProviderSessionId: matchedSpawn.parentProviderSessionId,
|
|
1300
|
-
kind: "spawn"
|
|
1301
|
-
});
|
|
1302
|
-
}
|
|
1303
|
-
return directRelations;
|
|
1304
|
-
}
|
|
1305
|
-
readSessionIdentity(filePath, fallbackSessionId) {
|
|
1306
|
-
if (!existsSync(filePath)) {
|
|
1307
|
-
return null;
|
|
1308
|
-
}
|
|
1309
|
-
const firstLine = readFirstNonEmptyLine(filePath);
|
|
1310
|
-
if (!firstLine) {
|
|
1311
|
-
return null;
|
|
1312
|
-
}
|
|
1313
|
-
try {
|
|
1314
|
-
const record = JSON.parse(firstLine);
|
|
1315
|
-
if (ensureText(record.type).trim() !== "session_meta") {
|
|
1316
|
-
return null;
|
|
1317
|
-
}
|
|
1318
|
-
const payload = (record.payload ?? {});
|
|
1319
|
-
const parentThreadRelation = resolveCodexParentThreadRelation(payload);
|
|
1320
|
-
return {
|
|
1321
|
-
threadId: this.resolveCodexSessionId(payload, fallbackSessionId),
|
|
1322
|
-
cwd: ensureText(payload.cwd).trim(),
|
|
1323
|
-
parentThreadId: parentThreadRelation.parentThreadId,
|
|
1324
|
-
parentThreadKind: parentThreadRelation.kind
|
|
1325
|
-
};
|
|
1326
|
-
}
|
|
1327
|
-
catch {
|
|
1328
|
-
return null;
|
|
1329
|
-
}
|
|
1330
|
-
}
|
|
1331
|
-
hydrateSessionSummary(summary, filePath, stats, metadata, relation) {
|
|
1332
|
-
const resolvedRelation = relation ?? null;
|
|
1333
|
-
const resolvedMetadata = metadata ?? null;
|
|
1334
|
-
const isSubagent = resolvedMetadata || resolvedRelation
|
|
1335
|
-
? isCodexSubagentThread(resolvedMetadata, resolvedRelation)
|
|
1336
|
-
: Boolean(summary.isSubagent);
|
|
1337
|
-
return {
|
|
1338
|
-
...summary,
|
|
1339
|
-
rawStoreRef: filePath,
|
|
1340
|
-
isArchived: resolveCodexArchivedState(resolvedMetadata, filePath),
|
|
1341
|
-
parentProviderSessionId: resolvedRelation?.parentProviderSessionId ?? summary.parentProviderSessionId ?? null,
|
|
1342
|
-
isSubagent,
|
|
1343
|
-
subagentLabel: resolvedMetadata !== null
|
|
1344
|
-
? buildCodexSubagentLabel(resolvedMetadata)
|
|
1345
|
-
: summary.subagentLabel ?? null,
|
|
1346
|
-
sourceMtimeMs: stats.mtimeMs,
|
|
1347
|
-
sourceSizeBytes: stats.size
|
|
1348
|
-
};
|
|
1349
|
-
}
|
|
1350
|
-
parseMessages(filePath, records, providerSessionId) {
|
|
1351
|
-
return this.parseMessagesFromEntries(filePath, records, providerSessionId);
|
|
1352
|
-
}
|
|
1353
|
-
parseMessagesFromEntries(filePath, records, providerSessionId) {
|
|
1354
|
-
const effectiveRecords = filterRolledBackCodexRecords(records);
|
|
1355
|
-
const messages = [];
|
|
1356
|
-
const messageIndexesByKey = new Map();
|
|
1357
|
-
const toolNameById = new Map();
|
|
1358
|
-
const toolInputById = new Map();
|
|
1359
|
-
const commandPatchByCallId = collectCodexCommandPatchesByCallId(effectiveRecords, filePath);
|
|
1360
|
-
let sequence = 0;
|
|
1361
|
-
const pushMessage = (source, message) => {
|
|
1362
|
-
const dedupeKey = buildCodexMessageDedupeKey(message);
|
|
1363
|
-
const candidateIndexes = messageIndexesByKey.get(dedupeKey) ?? [];
|
|
1364
|
-
for (let index = candidateIndexes.length - 1; index >= 0; index -= 1) {
|
|
1365
|
-
const existingIndex = candidateIndexes[index];
|
|
1366
|
-
const existing = messages[existingIndex];
|
|
1367
|
-
if (!isEquivalentCodexMessage(existing.message, message)) {
|
|
1368
|
-
continue;
|
|
1369
|
-
}
|
|
1370
|
-
const mergedEquivalent = mergeEquivalentCodexMessages(existing.message, existing.source, message, source);
|
|
1371
|
-
if (mergedEquivalent.source !== existing.source
|
|
1372
|
-
|| mergedEquivalent.message.messageId !== existing.message.messageId
|
|
1373
|
-
|| mergedEquivalent.message.rawRef !== existing.message.rawRef
|
|
1374
|
-
|| mergedEquivalent.message.timestamp !== existing.message.timestamp
|
|
1375
|
-
|| JSON.stringify(mergedEquivalent.message.toolCall) !== JSON.stringify(existing.message.toolCall)) {
|
|
1376
|
-
messages[existingIndex] = {
|
|
1377
|
-
source: mergedEquivalent.source,
|
|
1378
|
-
dedupeKey: buildCodexMessageDedupeKey(mergedEquivalent.message),
|
|
1379
|
-
message: {
|
|
1380
|
-
...mergedEquivalent.message,
|
|
1381
|
-
sequence: existing.message.sequence
|
|
1382
|
-
}
|
|
1383
|
-
};
|
|
1384
|
-
}
|
|
1385
|
-
return;
|
|
1386
|
-
}
|
|
1387
|
-
sequence += 1;
|
|
1388
|
-
messageIndexesByKey.set(dedupeKey, [...candidateIndexes, messages.length]);
|
|
1389
|
-
messages.push({
|
|
1390
|
-
source,
|
|
1391
|
-
dedupeKey,
|
|
1392
|
-
message: {
|
|
1393
|
-
...message,
|
|
1394
|
-
sequence
|
|
1395
|
-
}
|
|
1396
|
-
});
|
|
1397
|
-
};
|
|
1398
|
-
effectiveRecords.forEach(({ lineNumber, partIndex, data: record }) => {
|
|
1399
|
-
const rawRef = createRawRef(this.providerId, filePath, lineNumber, partIndex || undefined);
|
|
1400
|
-
if (record.type === "event_msg") {
|
|
1401
|
-
const payload = (record.payload ?? {});
|
|
1402
|
-
const eventType = ensureText(payload.type);
|
|
1403
|
-
if (eventType === "user_message") {
|
|
1404
|
-
const content = ensureText(payload.message);
|
|
1405
|
-
if (content.length > 0) {
|
|
1406
|
-
pushMessage("event_msg", {
|
|
1407
|
-
messageId: resolveCodexParsedMessageId({
|
|
1408
|
-
providerSessionId,
|
|
1409
|
-
rawRef,
|
|
1410
|
-
role: "user",
|
|
1411
|
-
kind: "text",
|
|
1412
|
-
payloadId: payload.id
|
|
1413
|
-
}),
|
|
1414
|
-
provider: this.providerId,
|
|
1415
|
-
providerSessionId,
|
|
1416
|
-
role: "user",
|
|
1417
|
-
kind: "text",
|
|
1418
|
-
content,
|
|
1419
|
-
toolCall: null,
|
|
1420
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1421
|
-
rawRef
|
|
1422
|
-
});
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
if (eventType === "agent_message") {
|
|
1426
|
-
const content = ensureText(payload.message);
|
|
1427
|
-
if (content.length > 0) {
|
|
1428
|
-
pushMessage("event_msg", {
|
|
1429
|
-
messageId: resolveCodexParsedMessageId({
|
|
1430
|
-
providerSessionId,
|
|
1431
|
-
rawRef,
|
|
1432
|
-
role: "assistant",
|
|
1433
|
-
kind: "text",
|
|
1434
|
-
payloadId: payload.id
|
|
1435
|
-
}),
|
|
1436
|
-
provider: this.providerId,
|
|
1437
|
-
providerSessionId,
|
|
1438
|
-
role: "assistant",
|
|
1439
|
-
kind: "text",
|
|
1440
|
-
content,
|
|
1441
|
-
toolCall: null,
|
|
1442
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1443
|
-
rawRef
|
|
1444
|
-
});
|
|
1445
|
-
}
|
|
1446
|
-
}
|
|
1447
|
-
if (eventType === "agent_reasoning") {
|
|
1448
|
-
const content = extractTextBlocks(payload.text ?? payload.message).trim();
|
|
1449
|
-
if (content.length > 0) {
|
|
1450
|
-
pushMessage("event_msg", {
|
|
1451
|
-
messageId: resolveCodexParsedMessageId({
|
|
1452
|
-
providerSessionId,
|
|
1453
|
-
rawRef,
|
|
1454
|
-
role: "assistant",
|
|
1455
|
-
kind: "thinking",
|
|
1456
|
-
payloadId: payload.id
|
|
1457
|
-
}),
|
|
1458
|
-
provider: this.providerId,
|
|
1459
|
-
providerSessionId,
|
|
1460
|
-
role: "assistant",
|
|
1461
|
-
kind: "thinking",
|
|
1462
|
-
content,
|
|
1463
|
-
toolCall: null,
|
|
1464
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1465
|
-
rawRef
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
if (record.type === "response_item") {
|
|
1471
|
-
const payload = (record.payload ?? {});
|
|
1472
|
-
const payloadType = ensureText(payload.type);
|
|
1473
|
-
if (payloadType === "reasoning") {
|
|
1474
|
-
const content = extractTextFromArray(payload.summary);
|
|
1475
|
-
if (content.length === 0) {
|
|
1476
|
-
return;
|
|
1477
|
-
}
|
|
1478
|
-
pushMessage("response_item", {
|
|
1479
|
-
messageId: resolveCodexParsedMessageId({
|
|
1480
|
-
providerSessionId,
|
|
1481
|
-
rawRef,
|
|
1482
|
-
role: "assistant",
|
|
1483
|
-
kind: "thinking",
|
|
1484
|
-
payloadId: payload.id
|
|
1485
|
-
}),
|
|
1486
|
-
provider: this.providerId,
|
|
1487
|
-
providerSessionId,
|
|
1488
|
-
role: "assistant",
|
|
1489
|
-
kind: "thinking",
|
|
1490
|
-
content,
|
|
1491
|
-
toolCall: null,
|
|
1492
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1493
|
-
rawRef
|
|
1494
|
-
});
|
|
1495
|
-
return;
|
|
1496
|
-
}
|
|
1497
|
-
if (payloadType === "function_call" || payloadType === "custom_tool_call") {
|
|
1498
|
-
const callId = ensureText(payload.call_id).trim() || rawRef;
|
|
1499
|
-
const rawName = ensureText(payload.name).trim() || "tool";
|
|
1500
|
-
const inputSource = payloadType === "custom_tool_call" ? payload.input : payload.arguments;
|
|
1501
|
-
const commandPatch = buildApplyPatchFromCodexCommandLikeValue(inputSource) ?? commandPatchByCallId.get(callId) ?? null;
|
|
1502
|
-
const name = commandPatch ? "apply_patch" : rawName;
|
|
1503
|
-
const input = commandPatch ?? stringifyStructuredValue(inputSource);
|
|
1504
|
-
toolNameById.set(callId, name);
|
|
1505
|
-
toolInputById.set(callId, input);
|
|
1506
|
-
pushMessage("response_item", {
|
|
1507
|
-
messageId: resolveCodexParsedMessageId({
|
|
1508
|
-
providerSessionId,
|
|
1509
|
-
rawRef,
|
|
1510
|
-
role: "tool",
|
|
1511
|
-
kind: "tool_call",
|
|
1512
|
-
callId
|
|
1513
|
-
}),
|
|
1514
|
-
provider: this.providerId,
|
|
1515
|
-
providerSessionId,
|
|
1516
|
-
role: "tool",
|
|
1517
|
-
kind: "tool_call",
|
|
1518
|
-
content: input,
|
|
1519
|
-
toolCall: {
|
|
1520
|
-
callId,
|
|
1521
|
-
name,
|
|
1522
|
-
input,
|
|
1523
|
-
output: null,
|
|
1524
|
-
error: null,
|
|
1525
|
-
status: "running"
|
|
1526
|
-
},
|
|
1527
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1528
|
-
rawRef
|
|
1529
|
-
});
|
|
1530
|
-
return;
|
|
1531
|
-
}
|
|
1532
|
-
if (payloadType === "function_call_output" || payloadType === "custom_tool_call_output") {
|
|
1533
|
-
const callId = ensureText(payload.call_id).trim() || rawRef;
|
|
1534
|
-
const output = extractTextBlocks(payload.output).trim() || stringifyStructuredValue(payload.output);
|
|
1535
|
-
const outputPatch = buildApplyPatchFromCodexCommandLikeValue(payload.output);
|
|
1536
|
-
const name = outputPatch ? "apply_patch" : (toolNameById.get(callId) ?? "tool");
|
|
1537
|
-
const input = resolveCodexCommandPatchResultInput(outputPatch, toolInputById.get(callId));
|
|
1538
|
-
const resultState = resolveToolResultState(payload, output);
|
|
1539
|
-
pushMessage("response_item", {
|
|
1540
|
-
messageId: resolveCodexParsedMessageId({
|
|
1541
|
-
providerSessionId,
|
|
1542
|
-
rawRef,
|
|
1543
|
-
role: "tool",
|
|
1544
|
-
kind: "tool_result",
|
|
1545
|
-
callId
|
|
1546
|
-
}),
|
|
1547
|
-
provider: this.providerId,
|
|
1548
|
-
providerSessionId,
|
|
1549
|
-
role: "tool",
|
|
1550
|
-
kind: "tool_result",
|
|
1551
|
-
content: output,
|
|
1552
|
-
toolCall: {
|
|
1553
|
-
callId,
|
|
1554
|
-
name,
|
|
1555
|
-
input,
|
|
1556
|
-
output: resultState.status === "failed" ? null : output,
|
|
1557
|
-
error: resultState.status === "failed" ? output : null,
|
|
1558
|
-
status: resultState.status
|
|
1559
|
-
},
|
|
1560
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1561
|
-
rawRef
|
|
1562
|
-
});
|
|
1563
|
-
return;
|
|
1564
|
-
}
|
|
1565
|
-
if (payloadType !== "message") {
|
|
1566
|
-
return;
|
|
1567
|
-
}
|
|
1568
|
-
const role = ensureText(payload.role);
|
|
1569
|
-
const content = extractTextFromArray(payload.content);
|
|
1570
|
-
if (content.length === 0 || (role !== "assistant" && role !== "user")) {
|
|
1571
|
-
return;
|
|
1572
|
-
}
|
|
1573
|
-
pushMessage("response_item", {
|
|
1574
|
-
messageId: resolveCodexParsedMessageId({
|
|
1575
|
-
providerSessionId,
|
|
1576
|
-
rawRef,
|
|
1577
|
-
role,
|
|
1578
|
-
kind: "text",
|
|
1579
|
-
payloadId: payload.id
|
|
1580
|
-
}),
|
|
1581
|
-
provider: this.providerId,
|
|
1582
|
-
providerSessionId,
|
|
1583
|
-
role,
|
|
1584
|
-
kind: "text",
|
|
1585
|
-
content,
|
|
1586
|
-
toolCall: null,
|
|
1587
|
-
timestamp: safeDate(record.timestamp, nextTimestamp()),
|
|
1588
|
-
rawRef
|
|
1589
|
-
});
|
|
1590
|
-
}
|
|
1591
|
-
});
|
|
1592
|
-
return messages.map((entry) => entry.message);
|
|
1593
|
-
}
|
|
1594
|
-
}
|
|
1595
|
-
function resolveCodexCommandPatchResultInput(outputPatch, storedInput) {
|
|
1596
|
-
if (!outputPatch) {
|
|
1597
|
-
return storedInput ?? "";
|
|
1598
|
-
}
|
|
1599
|
-
if (storedInput && isCodexPatchWithHunks(storedInput) && !isCodexPatchWithHunks(outputPatch)) {
|
|
1600
|
-
return storedInput;
|
|
1601
|
-
}
|
|
1602
|
-
return outputPatch;
|
|
1603
|
-
}
|
|
1604
|
-
function isCodexPatchWithHunks(value) {
|
|
1605
|
-
return /(?:^|\n)@@\s/.test(value);
|
|
1606
|
-
}
|
|
1607
|
-
function collectCodexCommandPatchesByCallId(records, filePath) {
|
|
1608
|
-
const patches = new Map();
|
|
1609
|
-
for (const { lineNumber, partIndex, data: record } of records) {
|
|
1610
|
-
if (record.type !== "response_item") {
|
|
1611
|
-
continue;
|
|
1612
|
-
}
|
|
1613
|
-
const payload = (record.payload ?? {});
|
|
1614
|
-
const payloadType = ensureText(payload.type).trim();
|
|
1615
|
-
if (payloadType !== "function_call_output" && payloadType !== "custom_tool_call_output") {
|
|
1616
|
-
continue;
|
|
1617
|
-
}
|
|
1618
|
-
const rawRef = createRawRef("codex", filePath, lineNumber, partIndex || undefined);
|
|
1619
|
-
const callId = ensureText(payload.call_id).trim() || rawRef;
|
|
1620
|
-
const patchText = buildApplyPatchFromCodexCommandLikeValue(payload.output);
|
|
1621
|
-
if (patchText && !patches.has(callId)) {
|
|
1622
|
-
patches.set(callId, patchText);
|
|
1623
|
-
}
|
|
1624
|
-
}
|
|
1625
|
-
return patches;
|
|
1626
|
-
}
|
|
1627
|
-
function filterRolledBackCodexRecords(records) {
|
|
1628
|
-
const completedTurnSegments = [];
|
|
1629
|
-
let activeTurnStartLineNumber = null;
|
|
1630
|
-
let sawRollbackEvent = false;
|
|
1631
|
-
for (const recordEntry of records) {
|
|
1632
|
-
const record = recordEntry.data;
|
|
1633
|
-
if (record.type !== "event_msg") {
|
|
1634
|
-
continue;
|
|
1635
|
-
}
|
|
1636
|
-
const payload = (record.payload ?? {});
|
|
1637
|
-
const eventType = ensureText(payload.type).trim();
|
|
1638
|
-
if (eventType === "task_started") {
|
|
1639
|
-
activeTurnStartLineNumber = recordEntry.lineNumber;
|
|
1640
|
-
continue;
|
|
1641
|
-
}
|
|
1642
|
-
if (eventType === "task_complete" || eventType === "task_failed") {
|
|
1643
|
-
if (activeTurnStartLineNumber !== null) {
|
|
1644
|
-
completedTurnSegments.push({
|
|
1645
|
-
startLineNumber: activeTurnStartLineNumber,
|
|
1646
|
-
endLineNumber: recordEntry.lineNumber,
|
|
1647
|
-
rolledBack: false
|
|
1648
|
-
});
|
|
1649
|
-
}
|
|
1650
|
-
activeTurnStartLineNumber = null;
|
|
1651
|
-
continue;
|
|
1652
|
-
}
|
|
1653
|
-
if (eventType !== "thread_rolled_back") {
|
|
1654
|
-
continue;
|
|
1655
|
-
}
|
|
1656
|
-
sawRollbackEvent = true;
|
|
1657
|
-
const requestedTurnCount = Math.max(0, Math.trunc(typeof payload.num_turns === "number"
|
|
1658
|
-
? payload.num_turns
|
|
1659
|
-
: Number.parseInt(ensureText(payload.num_turns), 10)) || 0);
|
|
1660
|
-
if (requestedTurnCount <= 0) {
|
|
1661
|
-
continue;
|
|
1662
|
-
}
|
|
1663
|
-
let remainingTurnsToRollback = requestedTurnCount;
|
|
1664
|
-
for (let index = completedTurnSegments.length - 1; index >= 0; index -= 1) {
|
|
1665
|
-
const segment = completedTurnSegments[index];
|
|
1666
|
-
if (!segment || segment.rolledBack) {
|
|
1667
|
-
continue;
|
|
1668
|
-
}
|
|
1669
|
-
segment.rolledBack = true;
|
|
1670
|
-
remainingTurnsToRollback -= 1;
|
|
1671
|
-
if (remainingTurnsToRollback <= 0) {
|
|
1672
|
-
break;
|
|
1673
|
-
}
|
|
1674
|
-
}
|
|
1675
|
-
}
|
|
1676
|
-
if (!sawRollbackEvent) {
|
|
1677
|
-
return records;
|
|
1678
|
-
}
|
|
1679
|
-
const rolledBackSegments = completedTurnSegments
|
|
1680
|
-
.filter((segment) => segment.rolledBack)
|
|
1681
|
-
.sort((left, right) => left.startLineNumber - right.startLineNumber);
|
|
1682
|
-
if (rolledBackSegments.length === 0) {
|
|
1683
|
-
return records;
|
|
1684
|
-
}
|
|
1685
|
-
const filteredRecords = [];
|
|
1686
|
-
let segmentIndex = 0;
|
|
1687
|
-
for (const recordEntry of records) {
|
|
1688
|
-
while (segmentIndex < rolledBackSegments.length
|
|
1689
|
-
&& recordEntry.lineNumber > rolledBackSegments[segmentIndex].endLineNumber) {
|
|
1690
|
-
segmentIndex += 1;
|
|
1691
|
-
}
|
|
1692
|
-
const activeSegment = segmentIndex < rolledBackSegments.length ? rolledBackSegments[segmentIndex] : null;
|
|
1693
|
-
if (activeSegment
|
|
1694
|
-
&& recordEntry.lineNumber >= activeSegment.startLineNumber
|
|
1695
|
-
&& recordEntry.lineNumber <= activeSegment.endLineNumber) {
|
|
1696
|
-
continue;
|
|
1697
|
-
}
|
|
1698
|
-
filteredRecords.push(recordEntry);
|
|
1699
|
-
}
|
|
1700
|
-
return filteredRecords;
|
|
1701
|
-
}
|
|
1702
|
-
function buildRecentHistoryPage(messages, totalMessageCount, limit) {
|
|
1703
|
-
const effectiveTotal = Math.max(totalMessageCount, messages.length);
|
|
1704
|
-
const pageMessages = messages.slice(-Math.min(limit, messages.length)).map((message, index, items) => ({
|
|
1705
|
-
...message,
|
|
1706
|
-
sequence: effectiveTotal - items.length + index + 1
|
|
1707
|
-
}));
|
|
1708
|
-
const nextCursor = effectiveTotal > pageMessages.length
|
|
1709
|
-
? encodeCursor(effectiveTotal - pageMessages.length)
|
|
1710
|
-
: null;
|
|
1711
|
-
return {
|
|
1712
|
-
messages: pageMessages,
|
|
1713
|
-
cursor: encodeCursor(effectiveTotal),
|
|
1714
|
-
nextCursor,
|
|
1715
|
-
total: effectiveTotal
|
|
1716
|
-
};
|
|
1717
|
-
}
|
|
1718
|
-
function buildCodexMessageDedupeKey(message) {
|
|
1719
|
-
const comparable = toComparableCodexMessage(message);
|
|
1720
|
-
return JSON.stringify({
|
|
1721
|
-
role: comparable.role,
|
|
1722
|
-
kind: comparable.kind,
|
|
1723
|
-
content: comparable.content,
|
|
1724
|
-
toolCall: comparable.toolCall
|
|
1725
|
-
? {
|
|
1726
|
-
callId: comparable.toolCall.callId,
|
|
1727
|
-
name: comparable.toolCall.name,
|
|
1728
|
-
input: comparable.toolCall.input,
|
|
1729
|
-
output: comparable.toolCall.output,
|
|
1730
|
-
error: comparable.toolCall.error,
|
|
1731
|
-
status: comparable.toolCall.status
|
|
1732
|
-
}
|
|
1733
|
-
: null
|
|
1734
|
-
});
|
|
1735
|
-
}
|
|
1736
|
-
function mergeEquivalentCodexMessages(current, currentSource, incoming, incomingSource) {
|
|
1737
|
-
const preferredBySource = codexMessageSourcePriority(incomingSource) > codexMessageSourcePriority(currentSource)
|
|
1738
|
-
? incoming
|
|
1739
|
-
: current;
|
|
1740
|
-
const preferredSource = codexMessageSourcePriority(incomingSource) > codexMessageSourcePriority(currentSource)
|
|
1741
|
-
? incomingSource
|
|
1742
|
-
: currentSource;
|
|
1743
|
-
const preferredStableMessageId = pickPreferredCodexEquivalentMessageId(current, incoming);
|
|
1744
|
-
return {
|
|
1745
|
-
source: preferredSource,
|
|
1746
|
-
message: {
|
|
1747
|
-
...preferredBySource,
|
|
1748
|
-
messageId: preferredStableMessageId
|
|
1749
|
-
}
|
|
1750
|
-
};
|
|
1751
|
-
}
|
|
1752
|
-
function pickPreferredCodexEquivalentMessageId(current, incoming) {
|
|
1753
|
-
const currentUsesStableIdentity = current.messageId !== messageIdFromRawRef(current.rawRef);
|
|
1754
|
-
const incomingUsesStableIdentity = incoming.messageId !== messageIdFromRawRef(incoming.rawRef);
|
|
1755
|
-
if (currentUsesStableIdentity !== incomingUsesStableIdentity) {
|
|
1756
|
-
return currentUsesStableIdentity ? current.messageId : incoming.messageId;
|
|
1757
|
-
}
|
|
1758
|
-
return incoming.messageId;
|
|
1759
|
-
}
|
|
1760
|
-
function resolveCodexFallbackTitle(messages) {
|
|
1761
|
-
const preferredMessage = messages.find((message) => message.role === "user" && !looksLikeCodexRulesMessage(message.content));
|
|
1762
|
-
if (preferredMessage) {
|
|
1763
|
-
return normalizeCodexMessageTitle(preferredMessage.content);
|
|
1764
|
-
}
|
|
1765
|
-
const firstUserMessage = messages.find((message) => message.role === "user");
|
|
1766
|
-
return normalizeCodexMessageTitle(firstUserMessage?.content);
|
|
1767
|
-
}
|
|
1768
|
-
function looksLikeCodexRulesMessage(content) {
|
|
1769
|
-
const normalized = content.trim();
|
|
1770
|
-
const beginsWithRulesHeader = /^#?\s*AGENTS\.md instructions for\b/i.test(normalized);
|
|
1771
|
-
if (beginsWithRulesHeader) {
|
|
1772
|
-
return true;
|
|
1773
|
-
}
|
|
1774
|
-
return /AGENTS\.md instructions for/i.test(normalized)
|
|
1775
|
-
&& /<INSTRUCTIONS>/i.test(normalized);
|
|
1776
|
-
}
|
|
1777
|
-
function codexMessageSourcePriority(source) {
|
|
1778
|
-
return source === "response_item" ? 2 : 1;
|
|
1779
|
-
}
|
|
1780
|
-
function isEquivalentCodexMessage(left, right) {
|
|
1781
|
-
const comparableLeft = toComparableCodexMessage(left);
|
|
1782
|
-
const comparableRight = toComparableCodexMessage(right);
|
|
1783
|
-
if (comparableLeft.role !== comparableRight.role ||
|
|
1784
|
-
comparableLeft.kind !== comparableRight.kind ||
|
|
1785
|
-
comparableLeft.content !== comparableRight.content) {
|
|
1786
|
-
return false;
|
|
1787
|
-
}
|
|
1788
|
-
if (JSON.stringify(comparableLeft.toolCall) !== JSON.stringify(comparableRight.toolCall)) {
|
|
1789
|
-
return false;
|
|
1790
|
-
}
|
|
1791
|
-
return areCodexTimestampsNear(left.timestamp, right.timestamp);
|
|
1792
|
-
}
|
|
1793
|
-
function toComparableCodexMessage(message) {
|
|
1794
|
-
return {
|
|
1795
|
-
role: message.role,
|
|
1796
|
-
kind: message.kind,
|
|
1797
|
-
content: normalizeComparableCodexContent(message.kind, message.content),
|
|
1798
|
-
toolCall: message.toolCall
|
|
1799
|
-
? {
|
|
1800
|
-
callId: message.toolCall.callId,
|
|
1801
|
-
name: message.toolCall.name,
|
|
1802
|
-
input: normalizeComparableCodexLineEndings(message.toolCall.input),
|
|
1803
|
-
output: message.toolCall.output === null
|
|
1804
|
-
? null
|
|
1805
|
-
: normalizeComparableCodexLineEndings(message.toolCall.output),
|
|
1806
|
-
error: message.toolCall.error === null
|
|
1807
|
-
? null
|
|
1808
|
-
: normalizeComparableCodexLineEndings(message.toolCall.error),
|
|
1809
|
-
status: message.toolCall.status
|
|
1810
|
-
}
|
|
1811
|
-
: null
|
|
1812
|
-
};
|
|
1813
|
-
}
|
|
1814
|
-
function normalizeComparableCodexContent(kind, content) {
|
|
1815
|
-
const normalized = normalizeComparableCodexLineEndings(content);
|
|
1816
|
-
if (kind === "text" || kind === "thinking") {
|
|
1817
|
-
return normalized.trimEnd();
|
|
1818
|
-
}
|
|
1819
|
-
return normalized;
|
|
1820
|
-
}
|
|
1821
|
-
function normalizeComparableCodexLineEndings(content) {
|
|
1822
|
-
return content.replace(/\r\n/g, "\n");
|
|
1823
|
-
}
|
|
1824
|
-
function areCodexTimestampsNear(left, right) {
|
|
1825
|
-
const leftMs = Date.parse(left);
|
|
1826
|
-
const rightMs = Date.parse(right);
|
|
1827
|
-
if (!Number.isFinite(leftMs) || !Number.isFinite(rightMs)) {
|
|
1828
|
-
return left === right;
|
|
1829
|
-
}
|
|
1830
|
-
return Math.abs(leftMs - rightMs) <= 1000;
|
|
1831
|
-
}
|
|
1832
|
-
function resolveCodexParsedMessageId(input) {
|
|
1833
|
-
const stableIdentity = resolveCodexStableIdentity(input);
|
|
1834
|
-
if (!stableIdentity) {
|
|
1835
|
-
return messageIdFromRawRef(input.rawRef);
|
|
1836
|
-
}
|
|
1837
|
-
return messageIdFromStableKey(buildCodexStableMessageKey(input.providerSessionId, stableIdentity));
|
|
1838
|
-
}
|
|
1839
|
-
function resolveCodexStableIdentity(input) {
|
|
1840
|
-
if (input.kind === "tool_call" || input.kind === "tool_result") {
|
|
1841
|
-
const normalizedCallId = ensureText(input.callId).trim();
|
|
1842
|
-
if (!normalizedCallId) {
|
|
1843
|
-
return null;
|
|
1844
|
-
}
|
|
1845
|
-
return input.kind === "tool_call"
|
|
1846
|
-
? `tool:call:${normalizedCallId}`
|
|
1847
|
-
: `tool:result:${normalizedCallId}`;
|
|
1848
|
-
}
|
|
1849
|
-
if (input.role !== "assistant"
|
|
1850
|
-
&& input.role !== "user") {
|
|
1851
|
-
return null;
|
|
1852
|
-
}
|
|
1853
|
-
const normalizedPayloadId = ensureText(input.payloadId).trim();
|
|
1854
|
-
if (!normalizedPayloadId) {
|
|
1855
|
-
return null;
|
|
1856
|
-
}
|
|
1857
|
-
const identityKind = input.kind === "thinking" ? "thinking" : "text";
|
|
1858
|
-
return `${input.role}:${identityKind}:${normalizedPayloadId}`;
|
|
1859
|
-
}
|
|
1860
|
-
function buildCodexStableMessageKey(providerSessionId, stableIdentity) {
|
|
1861
|
-
return `codex:${providerSessionId}:${stableIdentity}`;
|
|
1862
|
-
}
|
|
1863
|
-
function extractTextFromArray(value) {
|
|
1864
|
-
if (!Array.isArray(value)) {
|
|
1865
|
-
return "";
|
|
1866
|
-
}
|
|
1867
|
-
return value
|
|
1868
|
-
.map((item) => extractTextBlocks(item).trim())
|
|
1869
|
-
.filter((item) => item.length > 0)
|
|
1870
|
-
.join("\n");
|
|
1871
|
-
}
|
|
1872
|
-
function resolveToolResultState(payload, output) {
|
|
1873
|
-
const statusText = ensureText(payload.status).trim().toLowerCase();
|
|
1874
|
-
if (statusText === "failed" || statusText === "error") {
|
|
1875
|
-
return { status: "failed" };
|
|
1876
|
-
}
|
|
1877
|
-
if (output.toLowerCase().includes("apply_patch was requested via exec_command")) {
|
|
1878
|
-
return { status: "failed" };
|
|
1879
|
-
}
|
|
1880
|
-
if (statusText === "completed" || statusText === "success" || statusText === "succeeded") {
|
|
1881
|
-
return { status: "completed" };
|
|
1882
|
-
}
|
|
1883
|
-
if (typeof payload.success === "boolean") {
|
|
1884
|
-
return {
|
|
1885
|
-
status: payload.success ? "completed" : "failed"
|
|
1886
|
-
};
|
|
1887
|
-
}
|
|
1888
|
-
if (typeof payload.is_error === "boolean") {
|
|
1889
|
-
return {
|
|
1890
|
-
status: payload.is_error ? "failed" : "completed"
|
|
1891
|
-
};
|
|
1892
|
-
}
|
|
1893
|
-
if (typeof payload.exit_code === "number") {
|
|
1894
|
-
return {
|
|
1895
|
-
status: payload.exit_code === 0 ? "completed" : "failed"
|
|
1896
|
-
};
|
|
1897
|
-
}
|
|
1898
|
-
const exitCodeMatch = output.match(/(?:^|\n)Exit code:\s*(-?\d+)/i);
|
|
1899
|
-
if (exitCodeMatch) {
|
|
1900
|
-
return {
|
|
1901
|
-
status: Number(exitCodeMatch[1]) === 0 ? "completed" : "failed"
|
|
1902
|
-
};
|
|
1903
|
-
}
|
|
1904
|
-
if (payload.error != null) {
|
|
1905
|
-
return { status: "failed" };
|
|
1906
|
-
}
|
|
1907
|
-
return { status: "completed" };
|
|
1908
|
-
}
|
|
1909
|
-
function shouldIgnoreCodingNsDraftSession(metaPayload) {
|
|
1910
|
-
const source = ensureText(metaPayload.source).trim().toLowerCase();
|
|
1911
|
-
const sessionId = ensureText(metaPayload.id).trim().toLowerCase();
|
|
1912
|
-
return source === "codingns" && sessionId.startsWith("rollout-");
|
|
1913
|
-
}
|
|
1914
|
-
function looksLikeCodexThreadId(value) {
|
|
1915
|
-
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
|
|
1916
|
-
}
|
|
1917
|
-
function findLatestCodexStateDatabase(homeDir) {
|
|
1918
|
-
if (!existsSync(homeDir)) {
|
|
1919
|
-
return null;
|
|
1920
|
-
}
|
|
1921
|
-
const candidates = readdirSync(homeDir, { withFileTypes: true })
|
|
1922
|
-
.filter((entry) => entry.isFile() && /^state_\d+\.sqlite$/i.test(entry.name))
|
|
1923
|
-
.map((entry) => {
|
|
1924
|
-
const filePath = join(homeDir, entry.name);
|
|
1925
|
-
return {
|
|
1926
|
-
filePath,
|
|
1927
|
-
mtimeMs: statSync(filePath).mtimeMs
|
|
1928
|
-
};
|
|
1929
|
-
})
|
|
1930
|
-
.sort((left, right) => right.mtimeMs - left.mtimeMs);
|
|
1931
|
-
return candidates[0]?.filePath ?? null;
|
|
1932
|
-
}
|
|
1933
|
-
function parseStructuredJson(value) {
|
|
1934
|
-
const text = value.trim();
|
|
1935
|
-
if (text.length === 0) {
|
|
1936
|
-
return null;
|
|
1937
|
-
}
|
|
1938
|
-
try {
|
|
1939
|
-
const parsed = JSON.parse(text);
|
|
1940
|
-
return parsed && typeof parsed === "object" ? parsed : null;
|
|
1941
|
-
}
|
|
1942
|
-
catch {
|
|
1943
|
-
return null;
|
|
1944
|
-
}
|
|
1945
|
-
}
|
|
1946
|
-
function readNonNegativeInteger(value) {
|
|
1947
|
-
if (typeof value === "number" && Number.isFinite(value) && value >= 0) {
|
|
1948
|
-
return Math.trunc(value);
|
|
1949
|
-
}
|
|
1950
|
-
if (typeof value === "string" && /^\d+$/.test(value.trim())) {
|
|
1951
|
-
return Number.parseInt(value.trim(), 10);
|
|
1952
|
-
}
|
|
1953
|
-
return null;
|
|
1954
|
-
}
|
|
1955
|
-
function clampUsageRatio(promptTokens, contextWindow) {
|
|
1956
|
-
if (contextWindow <= 0) {
|
|
1957
|
-
return 0;
|
|
1958
|
-
}
|
|
1959
|
-
return Math.min(Math.max(promptTokens / contextWindow, 0), 1);
|
|
1960
|
-
}
|
|
1961
|
-
function resolveCodexKnownContextWindow(modelId) {
|
|
1962
|
-
if (!modelId) {
|
|
1963
|
-
return null;
|
|
1964
|
-
}
|
|
1965
|
-
return KNOWN_CODEX_CONTEXT_WINDOWS.get(modelId) ?? null;
|
|
1966
|
-
}
|
|
1967
|
-
function readCodexConfigContextWindow(homeDir) {
|
|
1968
|
-
const configPath = join(homeDir, "config.toml");
|
|
1969
|
-
if (!existsSync(configPath)) {
|
|
1970
|
-
return null;
|
|
1971
|
-
}
|
|
1972
|
-
try {
|
|
1973
|
-
const content = readFileSync(configPath, "utf8");
|
|
1974
|
-
const matched = content.match(CODEX_CONFIG_CONTEXT_WINDOW_PATTERN);
|
|
1975
|
-
if (!matched?.[1]) {
|
|
1976
|
-
return null;
|
|
1977
|
-
}
|
|
1978
|
-
return Number.parseInt(matched[1], 10);
|
|
1979
|
-
}
|
|
1980
|
-
catch {
|
|
1981
|
-
return null;
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
function parseCodexAgentIdFromToolOutput(output) {
|
|
1985
|
-
const parsed = parseStructuredJson(output);
|
|
1986
|
-
const agentId = ensureText(parsed?.agent_id ?? parsed?.agentId).trim();
|
|
1987
|
-
if (looksLikeCodexThreadId(agentId)) {
|
|
1988
|
-
return agentId;
|
|
1989
|
-
}
|
|
1990
|
-
const matched = output.match(/"agent(?:_id|Id)"\s*:\s*"([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})"/i);
|
|
1991
|
-
return matched?.[1] ?? null;
|
|
1992
|
-
}
|
|
1993
|
-
function resolveCodexParentThreadRelation(payload) {
|
|
1994
|
-
const source = typeof payload.source === "object" && payload.source !== null
|
|
1995
|
-
? payload.source
|
|
1996
|
-
: null;
|
|
1997
|
-
const subagent = typeof source?.subagent === "object" && source.subagent !== null
|
|
1998
|
-
? source.subagent
|
|
1999
|
-
: null;
|
|
2000
|
-
const threadSpawn = typeof subagent?.thread_spawn === "object" && subagent.thread_spawn !== null
|
|
2001
|
-
? subagent.thread_spawn
|
|
2002
|
-
: null;
|
|
2003
|
-
const nestedParentThreadId = ensureText(threadSpawn?.parent_thread_id).trim();
|
|
2004
|
-
if (nestedParentThreadId.length > 0) {
|
|
2005
|
-
return {
|
|
2006
|
-
parentThreadId: nestedParentThreadId,
|
|
2007
|
-
kind: "spawn"
|
|
2008
|
-
};
|
|
2009
|
-
}
|
|
2010
|
-
const directParentThreadId = ensureText(payload.forked_from_id).trim();
|
|
2011
|
-
if (directParentThreadId.length > 0) {
|
|
2012
|
-
return {
|
|
2013
|
-
parentThreadId: directParentThreadId,
|
|
2014
|
-
kind: "fork"
|
|
2015
|
-
};
|
|
2016
|
-
}
|
|
2017
|
-
return {
|
|
2018
|
-
parentThreadId: null,
|
|
2019
|
-
kind: null
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
function toTimestampMs(value) {
|
|
2023
|
-
const timestampMs = Date.parse(ensureText(value).trim());
|
|
2024
|
-
return Number.isFinite(timestampMs) ? timestampMs : null;
|
|
2025
|
-
}
|
|
2026
|
-
function pickClosestCodexSpawnRecord(spawnRecords, workspacePath, message, createdAtMs) {
|
|
2027
|
-
const matchedRecords = spawnRecords.filter((record) => record.workspacePath !== null &&
|
|
2028
|
-
normalizeWorkspacePath(record.workspacePath) === workspacePath &&
|
|
2029
|
-
record.message === message);
|
|
2030
|
-
if (matchedRecords.length === 0) {
|
|
2031
|
-
return null;
|
|
2032
|
-
}
|
|
2033
|
-
if (createdAtMs === null) {
|
|
2034
|
-
return matchedRecords.at(-1) ?? null;
|
|
2035
|
-
}
|
|
2036
|
-
const closeRecord = matchedRecords
|
|
2037
|
-
.filter((record) => record.timestampMs !== null)
|
|
2038
|
-
.sort((left, right) => Math.abs((left.timestampMs ?? createdAtMs) - createdAtMs) -
|
|
2039
|
-
Math.abs((right.timestampMs ?? createdAtMs) - createdAtMs))
|
|
2040
|
-
.find((record) => Math.abs((record.timestampMs ?? createdAtMs) - createdAtMs) <= 120_000);
|
|
2041
|
-
return closeRecord ?? matchedRecords.at(-1) ?? null;
|
|
2042
|
-
}
|
|
2043
|
-
function isCodexSubagentThread(metadata, relation) {
|
|
2044
|
-
return Boolean(relation?.kind === "spawn" || metadata?.agentRole || metadata?.agentNickname);
|
|
2045
|
-
}
|
|
2046
|
-
function buildCodexSubagentLabel(metadata) {
|
|
2047
|
-
const agentRole = metadata?.agentRole?.trim() || "";
|
|
2048
|
-
const agentNickname = metadata?.agentNickname?.trim() || "";
|
|
2049
|
-
if (agentRole && agentNickname) {
|
|
2050
|
-
return `${agentRole} · ${agentNickname}`;
|
|
2051
|
-
}
|
|
2052
|
-
return agentNickname || agentRole || null;
|
|
2053
|
-
}
|
|
2054
|
-
function resolveCodexArchivedState(metadata, filePath) {
|
|
2055
|
-
if (isCodexArchivedFilePath(filePath)) {
|
|
2056
|
-
return true;
|
|
2057
|
-
}
|
|
2058
|
-
if (typeof metadata?.isArchived === "boolean") {
|
|
2059
|
-
return metadata.isArchived;
|
|
2060
|
-
}
|
|
2061
|
-
return false;
|
|
2062
|
-
}
|
|
2063
|
-
function isCodexArchivedFilePath(filePath) {
|
|
2064
|
-
return filePath.replaceAll("\\", "/").includes("/archived_sessions/");
|
|
2065
|
-
}
|
|
2066
|
-
function buildCodexActiveSessionPath(homeDir, fileName) {
|
|
2067
|
-
const match = fileName.match(/^rollout-(\d{4})-(\d{2})-(\d{2})T.+\.jsonl$/);
|
|
2068
|
-
if (!match) {
|
|
2069
|
-
return join(homeDir, "sessions", fileName);
|
|
2070
|
-
}
|
|
2071
|
-
return join(homeDir, "sessions", match[1], match[2], match[3], fileName);
|
|
2072
|
-
}
|
|
2073
|
-
function buildSyntheticCodexHistoryPath(homeDir, providerSessionId) {
|
|
2074
|
-
return join(homeDir, "runtime", "codex", `${providerSessionId}.jsonl`);
|
|
2075
|
-
}
|
|
2076
|
-
function isSyntheticCodexHistoryPath(rawStoreRef) {
|
|
2077
|
-
const normalized = rawStoreRef.replaceAll("\\", "/").toLowerCase();
|
|
2078
|
-
return normalized.includes("/runtime/codex/") || normalized.startsWith("runtime/codex/");
|
|
2079
|
-
}
|
|
2080
|
-
function hasUsableCodexTitle(title) {
|
|
2081
|
-
return normalizeCodexIndexedTitle(title) !== null;
|
|
2082
|
-
}
|
|
2083
|
-
function normalizeCodexIndexedTitle(title) {
|
|
2084
|
-
const normalized = ensureText(title).trim();
|
|
2085
|
-
if (normalized.length === 0 || looksLikeCodexRulesMessage(normalized)) {
|
|
2086
|
-
return null;
|
|
2087
|
-
}
|
|
2088
|
-
return normalized;
|
|
2089
|
-
}
|
|
2090
|
-
function normalizeCodexMessageTitle(content) {
|
|
2091
|
-
const normalized = normalizeCodexIndexedTitle(content);
|
|
2092
|
-
return normalized ? normalized.slice(0, CODEX_SESSION_TITLE_MAX_LENGTH) : null;
|
|
2093
|
-
}
|
|
2094
|
-
function extractCodexThreadHistory(result) {
|
|
2095
|
-
const snapshot = extractCodexThreadHistorySnapshot(result);
|
|
2096
|
-
return snapshot.value;
|
|
2097
|
-
}
|
|
2098
|
-
function extractCodexThreadHistorySnapshot(result) {
|
|
2099
|
-
const directHistory = pickCodexHistoryArray(result)
|
|
2100
|
-
?? pickCodexHistoryArray(toRecord(result.thread))
|
|
2101
|
-
?? pickCodexHistoryArray(toRecord(result.data));
|
|
2102
|
-
if (directHistory) {
|
|
2103
|
-
return {
|
|
2104
|
-
kind: "entries",
|
|
2105
|
-
value: directHistory,
|
|
2106
|
-
comparableEntries: directHistory.flatMap((entry, entryIndex) => {
|
|
2107
|
-
const signature = buildCodexThreadHistorySignature(entry);
|
|
2108
|
-
return signature ? [{ signature, entryIndex }] : [];
|
|
2109
|
-
})
|
|
2110
|
-
};
|
|
2111
|
-
}
|
|
2112
|
-
const turns = pickCodexTurnArray(result.turns)
|
|
2113
|
-
?? pickCodexTurnArray(toRecord(result.thread)?.turns)
|
|
2114
|
-
?? pickCodexTurnArray(toRecord(result.data)?.turns);
|
|
2115
|
-
if (!turns) {
|
|
2116
|
-
throw new Error("CODEX_THREAD_HISTORY_MISSING");
|
|
2117
|
-
}
|
|
2118
|
-
const comparableEntries = turns.flatMap((turn, turnIndex) => collectCodexTurnComparableEntries(turn, turnIndex));
|
|
2119
|
-
if (comparableEntries.length === 0) {
|
|
2120
|
-
throw new Error("CODEX_THREAD_HISTORY_MISSING");
|
|
2121
|
-
}
|
|
2122
|
-
return {
|
|
2123
|
-
kind: "turns",
|
|
2124
|
-
value: turns,
|
|
2125
|
-
comparableEntries
|
|
2126
|
-
};
|
|
2127
|
-
}
|
|
2128
|
-
function isSyntheticCodexSessionTitle(title) {
|
|
2129
|
-
const normalizedTitle = ensureText(title).trim();
|
|
2130
|
-
if (normalizedTitle.length === 0) {
|
|
2131
|
-
return false;
|
|
2132
|
-
}
|
|
2133
|
-
return (/^rollout-\d{4}-\d{2}-\d{2}t/i.test(normalizedTitle) ||
|
|
2134
|
-
/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(normalizedTitle));
|
|
2135
|
-
}
|
|
2136
|
-
function pickCodexHistoryArray(value) {
|
|
2137
|
-
const record = toRecord(value);
|
|
2138
|
-
if (!record) {
|
|
2139
|
-
return null;
|
|
2140
|
-
}
|
|
2141
|
-
for (const key of ["history", "items"]) {
|
|
2142
|
-
const candidate = record[key];
|
|
2143
|
-
if (Array.isArray(candidate)
|
|
2144
|
-
&& candidate.some((entry) => buildCodexThreadHistorySignature(entry) !== null)) {
|
|
2145
|
-
return candidate;
|
|
2146
|
-
}
|
|
2147
|
-
}
|
|
2148
|
-
return null;
|
|
2149
|
-
}
|
|
2150
|
-
function pickCodexTurnArray(value) {
|
|
2151
|
-
return Array.isArray(value) ? value : null;
|
|
2152
|
-
}
|
|
2153
|
-
function collectCodexTurnComparableEntries(value, turnIndex, parentPath = []) {
|
|
2154
|
-
const record = toRecord(value);
|
|
2155
|
-
if (!record) {
|
|
2156
|
-
return [];
|
|
2157
|
-
}
|
|
2158
|
-
for (const key of ["history", "items"]) {
|
|
2159
|
-
const candidate = record[key];
|
|
2160
|
-
if (Array.isArray(candidate)
|
|
2161
|
-
&& candidate.some((entry) => buildCodexThreadHistorySignature(entry) !== null)) {
|
|
2162
|
-
const containerPath = [...parentPath, key];
|
|
2163
|
-
return candidate.flatMap((entry, entryIndex) => {
|
|
2164
|
-
const signature = buildCodexThreadHistorySignature(entry);
|
|
2165
|
-
return signature
|
|
2166
|
-
? [{
|
|
2167
|
-
signature,
|
|
2168
|
-
turnIndex,
|
|
2169
|
-
containerPath,
|
|
2170
|
-
entryIndex
|
|
2171
|
-
}]
|
|
2172
|
-
: [];
|
|
2173
|
-
});
|
|
2174
|
-
}
|
|
2175
|
-
}
|
|
2176
|
-
return [
|
|
2177
|
-
["input", record.input],
|
|
2178
|
-
["output", record.output],
|
|
2179
|
-
["turn", record.turn],
|
|
2180
|
-
["data", record.data],
|
|
2181
|
-
["result", record.result]
|
|
2182
|
-
].flatMap(([key, candidate]) => collectCodexTurnComparableEntries(candidate, turnIndex, [...parentPath, key]));
|
|
2183
|
-
}
|
|
2184
|
-
function toRecord(value) {
|
|
2185
|
-
return typeof value === "object" && value !== null
|
|
2186
|
-
? value
|
|
2187
|
-
: null;
|
|
2188
|
-
}
|
|
2189
|
-
function truncateCodexThreadHistory(history, parsedMessages, targetMessage) {
|
|
2190
|
-
const snapshot = normalizeCodexThreadHistorySnapshot(history);
|
|
2191
|
-
if (snapshot.kind === "entries") {
|
|
2192
|
-
const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
|
|
2193
|
-
return snapshot.value.slice(0, targetEntry.entryIndex + 1);
|
|
2194
|
-
}
|
|
2195
|
-
const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
|
|
2196
|
-
const truncatedTurns = snapshot.value.slice(0, targetEntry.turnIndex + 1);
|
|
2197
|
-
const lastTurn = truncatedTurns.at(-1);
|
|
2198
|
-
if (!lastTurn) {
|
|
2199
|
-
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
2200
|
-
}
|
|
2201
|
-
return [
|
|
2202
|
-
...flattenCodexTurnHistory(truncatedTurns.slice(0, -1)),
|
|
2203
|
-
...collectCodexTurnHistoryItems(truncateCodexTurnAtPath(lastTurn, targetEntry.containerPath, targetEntry.entryIndex))
|
|
2204
|
-
];
|
|
2205
|
-
}
|
|
2206
|
-
function normalizeCodexThreadHistorySnapshot(history) {
|
|
2207
|
-
const directComparableEntries = history.flatMap((entry, entryIndex) => {
|
|
2208
|
-
const signature = buildCodexThreadHistorySignature(entry);
|
|
2209
|
-
return signature ? [{ signature, entryIndex }] : [];
|
|
2210
|
-
});
|
|
2211
|
-
if (directComparableEntries.length > 0) {
|
|
2212
|
-
return {
|
|
2213
|
-
kind: "entries",
|
|
2214
|
-
value: history,
|
|
2215
|
-
comparableEntries: directComparableEntries
|
|
2216
|
-
};
|
|
2217
|
-
}
|
|
2218
|
-
const turnComparableEntries = history.flatMap((turn, turnIndex) => collectCodexTurnComparableEntries(turn, turnIndex));
|
|
2219
|
-
if (turnComparableEntries.length > 0) {
|
|
2220
|
-
return {
|
|
2221
|
-
kind: "turns",
|
|
2222
|
-
value: history,
|
|
2223
|
-
comparableEntries: turnComparableEntries
|
|
2224
|
-
};
|
|
2225
|
-
}
|
|
2226
|
-
throw new Error("CODEX_THREAD_HISTORY_MISSING");
|
|
2227
|
-
}
|
|
2228
|
-
function resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage) {
|
|
2229
|
-
const targetSignature = buildCodexThreadHistorySignature(targetMessage);
|
|
2230
|
-
if (!targetSignature) {
|
|
2231
|
-
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
2232
|
-
}
|
|
2233
|
-
const matchingEntries = snapshot.comparableEntries.filter((entry) => entry.signature === targetSignature);
|
|
2234
|
-
if (matchingEntries.length === 0) {
|
|
2235
|
-
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
2236
|
-
}
|
|
2237
|
-
const targetOccurrence = resolveCodexMessageSignatureOccurrence(parsedMessages, targetMessage);
|
|
2238
|
-
return matchingEntries[Math.min(targetOccurrence, matchingEntries.length) - 1] ?? matchingEntries.at(-1);
|
|
2239
|
-
}
|
|
2240
|
-
function resolveCodexMessageSignatureOccurrence(parsedMessages, targetMessage) {
|
|
2241
|
-
const targetSignature = buildCodexThreadHistorySignature(targetMessage);
|
|
2242
|
-
if (!targetSignature) {
|
|
2243
|
-
return 1;
|
|
2244
|
-
}
|
|
2245
|
-
let occurrence = 0;
|
|
2246
|
-
for (const message of parsedMessages) {
|
|
2247
|
-
if (buildCodexThreadHistorySignature(message) !== targetSignature) {
|
|
2248
|
-
continue;
|
|
2249
|
-
}
|
|
2250
|
-
occurrence += 1;
|
|
2251
|
-
if (message.messageId === targetMessage.messageId) {
|
|
2252
|
-
return occurrence;
|
|
2253
|
-
}
|
|
2254
|
-
}
|
|
2255
|
-
return Math.max(1, occurrence);
|
|
2256
|
-
}
|
|
2257
|
-
function buildCodexTurnRollbackPlan(snapshot, parsedMessages, targetMessage) {
|
|
2258
|
-
const targetEntry = resolveCodexThreadHistoryTargetEntry(snapshot, parsedMessages, targetMessage);
|
|
2259
|
-
const turnEntries = snapshot.comparableEntries.filter((entry) => entry.turnIndex === targetEntry.turnIndex);
|
|
2260
|
-
const lastComparableEntryInTurn = turnEntries.at(-1) ?? null;
|
|
2261
|
-
if (!lastComparableEntryInTurn) {
|
|
2262
|
-
throw new Error("CODEX_FORK_SOURCE_MESSAGE_UNMAPPABLE");
|
|
2263
|
-
}
|
|
2264
|
-
if (lastComparableEntryInTurn.containerPath.join("/") !== targetEntry.containerPath.join("/")
|
|
2265
|
-
|| lastComparableEntryInTurn.entryIndex !== targetEntry.entryIndex) {
|
|
2266
|
-
throw new Error("CODEX_MESSAGE_FORK_TURN_BOUNDARY_REQUIRED");
|
|
2267
|
-
}
|
|
2268
|
-
return {
|
|
2269
|
-
targetTurnIndex: targetEntry.turnIndex,
|
|
2270
|
-
numTurnsToRollback: Math.max(0, snapshot.value.length - targetEntry.turnIndex - 1)
|
|
2271
|
-
};
|
|
2272
|
-
}
|
|
2273
|
-
function truncateCodexTurnAtPath(turn, containerPath, entryIndex) {
|
|
2274
|
-
if (containerPath.length === 0) {
|
|
2275
|
-
return turn;
|
|
2276
|
-
}
|
|
2277
|
-
const record = toRecord(turn);
|
|
2278
|
-
if (!record) {
|
|
2279
|
-
return turn;
|
|
2280
|
-
}
|
|
2281
|
-
const [head, ...rest] = containerPath;
|
|
2282
|
-
const current = record[head];
|
|
2283
|
-
if (rest.length === 0) {
|
|
2284
|
-
if (!Array.isArray(current)) {
|
|
2285
|
-
return turn;
|
|
2286
|
-
}
|
|
2287
|
-
return {
|
|
2288
|
-
...record,
|
|
2289
|
-
[head]: current.slice(0, entryIndex + 1)
|
|
2290
|
-
};
|
|
2291
|
-
}
|
|
2292
|
-
return {
|
|
2293
|
-
...record,
|
|
2294
|
-
[head]: truncateCodexTurnAtPath(current, rest, entryIndex)
|
|
2295
|
-
};
|
|
2296
|
-
}
|
|
2297
|
-
function flattenCodexTurnHistory(turns) {
|
|
2298
|
-
return turns.flatMap((turn) => collectCodexTurnHistoryItems(turn));
|
|
2299
|
-
}
|
|
2300
|
-
function collectCodexTurnHistoryItems(value) {
|
|
2301
|
-
const direct = pickCodexHistoryArray(value);
|
|
2302
|
-
if (direct) {
|
|
2303
|
-
return direct;
|
|
2304
|
-
}
|
|
2305
|
-
const record = toRecord(value);
|
|
2306
|
-
if (!record) {
|
|
2307
|
-
return [];
|
|
2308
|
-
}
|
|
2309
|
-
return [
|
|
2310
|
-
record.input,
|
|
2311
|
-
record.output,
|
|
2312
|
-
record.turn,
|
|
2313
|
-
record.data,
|
|
2314
|
-
record.result
|
|
2315
|
-
].flatMap((candidate) => collectCodexTurnHistoryItems(candidate));
|
|
2316
|
-
}
|
|
2317
|
-
function buildCodexThreadHistorySignature(value) {
|
|
2318
|
-
if (!value || typeof value !== "object") {
|
|
2319
|
-
return null;
|
|
2320
|
-
}
|
|
2321
|
-
const item = value;
|
|
2322
|
-
const normalizedKind = ensureText(item.kind).trim();
|
|
2323
|
-
const normalizedRole = ensureText(item.role).trim();
|
|
2324
|
-
const normalizedContent = ensureText(item.content).trim();
|
|
2325
|
-
if (normalizedKind && normalizedRole) {
|
|
2326
|
-
return `${normalizedRole}:${normalizedKind}:${normalizedContent}`;
|
|
2327
|
-
}
|
|
2328
|
-
const type = ensureText(item.type).trim();
|
|
2329
|
-
if (type === "userMessage") {
|
|
2330
|
-
const content = stringifyCodexThreadMessageContent(item.content);
|
|
2331
|
-
return content ? `user:text:${content}` : null;
|
|
2332
|
-
}
|
|
2333
|
-
if (type === "agentMessage") {
|
|
2334
|
-
const content = ensureText(item.text).trim() || stringifyCodexThreadMessageContent(item.content);
|
|
2335
|
-
return content ? `assistant:text:${content}` : null;
|
|
2336
|
-
}
|
|
2337
|
-
if (type === "message") {
|
|
2338
|
-
const role = ensureText(item.role).trim();
|
|
2339
|
-
const content = stringifyCodexThreadMessageContent(item.content);
|
|
2340
|
-
if (!role || !content) {
|
|
2341
|
-
return null;
|
|
2342
|
-
}
|
|
2343
|
-
return `${role}:text:${content}`;
|
|
2344
|
-
}
|
|
2345
|
-
if (type === "reasoning") {
|
|
2346
|
-
const content = stringifyCodexReasoningContent(item.summary ?? item.content);
|
|
2347
|
-
return content ? `assistant:thinking:${content}` : null;
|
|
2348
|
-
}
|
|
2349
|
-
if (type === "function_call" || type === "tool_call") {
|
|
2350
|
-
const name = ensureText(item.name).trim();
|
|
2351
|
-
const inputValue = item.arguments ?? item.input;
|
|
2352
|
-
const content = stringifyStructuredValue(inputValue);
|
|
2353
|
-
return name || content ? `assistant:tool_call:${name}:${content}` : null;
|
|
2354
|
-
}
|
|
2355
|
-
if (type === "function_call_output" || type === "tool_result") {
|
|
2356
|
-
const content = ensureText(item.output ?? item.content).trim();
|
|
2357
|
-
return content ? `tool:tool_result:${content}` : null;
|
|
2358
|
-
}
|
|
2359
|
-
if (normalizedRole && normalizedContent) {
|
|
2360
|
-
return `${normalizedRole}:text:${normalizedContent}`;
|
|
2361
|
-
}
|
|
2362
|
-
return null;
|
|
2363
|
-
}
|
|
2364
|
-
function applyForkSourceMessageSnapshot(targetMessage, snapshot) {
|
|
2365
|
-
if (!snapshot) {
|
|
2366
|
-
return targetMessage;
|
|
2367
|
-
}
|
|
2368
|
-
return {
|
|
2369
|
-
...targetMessage,
|
|
2370
|
-
role: snapshot.role,
|
|
2371
|
-
kind: snapshot.kind,
|
|
2372
|
-
content: snapshot.content
|
|
2373
|
-
};
|
|
2374
|
-
}
|
|
2375
|
-
function collectCodexForkComparableSignatures(history) {
|
|
2376
|
-
try {
|
|
2377
|
-
return normalizeCodexThreadHistorySnapshot(history).comparableEntries.map((entry) => entry.signature);
|
|
2378
|
-
}
|
|
2379
|
-
catch {
|
|
2380
|
-
return [];
|
|
2381
|
-
}
|
|
2382
|
-
}
|
|
2383
|
-
function stringifyCodexThreadMessageContent(content) {
|
|
2384
|
-
if (typeof content === "string") {
|
|
2385
|
-
return content.trim();
|
|
2386
|
-
}
|
|
2387
|
-
if (!Array.isArray(content)) {
|
|
2388
|
-
return "";
|
|
2389
|
-
}
|
|
2390
|
-
return content
|
|
2391
|
-
.map((part) => {
|
|
2392
|
-
if (!part || typeof part !== "object") {
|
|
2393
|
-
return "";
|
|
2394
|
-
}
|
|
2395
|
-
const record = part;
|
|
2396
|
-
return ensureText(record.text
|
|
2397
|
-
?? record.input_text
|
|
2398
|
-
?? record.output_text
|
|
2399
|
-
?? (ensureText(record.type).trim() === "text" ? record.text : null)).trim();
|
|
2400
|
-
})
|
|
2401
|
-
.filter((value) => value.length > 0)
|
|
2402
|
-
.join("\n")
|
|
2403
|
-
.trim();
|
|
2404
|
-
}
|
|
2405
|
-
function stringifyCodexReasoningContent(content) {
|
|
2406
|
-
if (typeof content === "string") {
|
|
2407
|
-
return content.trim();
|
|
2408
|
-
}
|
|
2409
|
-
if (!Array.isArray(content)) {
|
|
2410
|
-
return "";
|
|
2411
|
-
}
|
|
2412
|
-
return content
|
|
2413
|
-
.map((part) => {
|
|
2414
|
-
if (!part || typeof part !== "object") {
|
|
2415
|
-
return "";
|
|
2416
|
-
}
|
|
2417
|
-
const record = part;
|
|
2418
|
-
return ensureText(record.text ?? record.summary_text).trim();
|
|
2419
|
-
})
|
|
2420
|
-
.filter((value) => value.length > 0)
|
|
2421
|
-
.join("\n")
|
|
2422
|
-
.trim();
|
|
2423
|
-
}
|
|
2424
|
-
function shouldFallbackCodexForkFromHistory(error, history) {
|
|
2425
|
-
if (history.length === 0) {
|
|
2426
|
-
return false;
|
|
2427
|
-
}
|
|
2428
|
-
return isCodexThreadLoadError(error);
|
|
2429
|
-
}
|
|
2430
|
-
function isCodexThreadLoadError(error) {
|
|
2431
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
2432
|
-
const normalized = message.trim().toLowerCase();
|
|
2433
|
-
return (normalized.includes("thread not loaded") ||
|
|
2434
|
-
normalized.includes("no rollout found for thread id"));
|
|
2435
|
-
}
|
|
2436
|
-
//# sourceMappingURL=codex.js.map
|