@xopcai/xopc 0.0.122 → 0.0.124
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 +28 -11
- package/README.zh-CN.md +28 -11
- package/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CK3s5cJo.js → agent-avatar-dicebear-B5uSVAyJ.js} +1 -1
- package/dist/gateway/static/root/assets/agents-BjIxt67Z.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-CWsNHtTs.js → app-management-settings-panel-CYCONLIL.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-C4b8y6iZ.js → appearance-settings-Clw3ryKg.js} +1 -1
- package/dist/gateway/static/root/assets/apps-page-QICTeGjX.js +1 -0
- package/dist/gateway/static/root/assets/{archive-plugin-DAtNW3Ia.js → archive-plugin-CXUGRp2i.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-Dm-RY54y.js → attachment-load-Cmiub3Wg.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-BKHewSVT.js → automation-suggestion-card-CU0-4nQ-.js} +1 -1
- package/dist/gateway/static/root/assets/automations-page-Car-jtiz.js +1 -0
- package/dist/gateway/static/root/assets/{binary-plugins-BG6Xnk71.js → binary-plugins-0Izz-N3p.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DfndiO3h.js → block-editor-C1Kv0IkJ.js} +28 -28
- package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-Cx93GLdw.js → capability-presets-api-BmQTM9Il.js} +1 -1
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-DZEJEoCF.js +1 -0
- package/dist/gateway/static/root/assets/{channel-recipient-api-DdPTtEs8.js → channel-recipient-api-BeA2aVor.js} +1 -1
- package/dist/gateway/static/root/assets/channels-settings-kkW-xFau.js +1 -0
- package/dist/gateway/static/root/assets/connectors-page-BvaQvO5F.js +2 -0
- package/dist/gateway/static/root/assets/{directory-picker-path-field-D835EHJd.js → directory-picker-path-field-2rjzUu57.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-CCa1F7Fg.js → dreaming-settings-DnHO686d.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-W2ITCPSA.js → extension-debug-page-BGLRuWVN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DrvUytvn.js → extension-page-D978ePp9.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-qM8aLT5u.js → extension-settings-page-B6Ih4Swx.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-ovkvM8KQ.js → fetch-BU5S0Sn7.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BB3biuD0.js → gateway-config-api-BIPGwrl1.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-settings-CMEA3r5u.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-startup-retry-CanQ8UcR.js → gateway-startup-retry-Cd0Zz30N.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-CNqNdEkQ.js → goal-create-dialog-B-KHKeXN.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-BxrSoWdj.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-BBOX6mUl.js → goals-config-api-B95KdGMj.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-BP1kfvWv.js → goals-page-BTJzIl28.js} +2 -2
- package/dist/gateway/static/root/assets/{goals-settings-Bh3A7OZV.js → goals-settings-BJyEuLLm.js} +1 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-wkOjYNQT.js +1 -0
- package/dist/gateway/static/root/assets/index-BLaXr6Ev.css +1 -0
- package/dist/gateway/static/root/assets/index-c_9TVr_C.js +191 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-B4TKJHla.js → keyboard-shortcuts-settings-BT8T4DyC.js} +1 -1
- package/dist/gateway/static/root/assets/logs-page-CrxBJUgI.js +2 -0
- package/dist/gateway/static/root/assets/{markdown-split-DcFtdLci.js → markdown-split-Dv66bO2b.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-B6SmeLVB.js → media-plugins-BhIQU5yS.js} +1 -1
- package/dist/gateway/static/root/assets/messages-O6TAtdFC.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-DOOpz6y1.js +2 -0
- package/dist/gateway/static/root/assets/notes-page-Br16pK9v.js +1 -0
- package/dist/gateway/static/root/assets/notes-workbench-PSJAi0ZC.js +2 -0
- package/dist/gateway/static/root/assets/{preview-open-alternatives-C29O19s7.js → preview-open-alternatives-DGzet1d7.js} +1 -1
- package/dist/gateway/static/root/assets/project-detail-page-CIGGgKuw.js +1 -0
- package/dist/gateway/static/root/assets/projects-page-B2grhsBt.js +1 -0
- package/dist/gateway/static/root/assets/remote-access-hub-CXMORxBD.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-B4ad9GfB.js → save-bar-controls-Cqgb-jqa.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-DdYncfLR.js → schedule-field-DVZcI8Yt.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-BMTrDE-7.js → schema-form-Bj7sOxC8.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-Bi2wkBzU.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-B1L3G4IA.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-MwTW2eK4.js → settings-collapsible-section-BmXn-8N6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-Dg6LBbcr.js → settings-form-section-BnqYMWHR.js} +1 -1
- package/dist/gateway/static/root/assets/settings-page-BfRSHJmO.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-BX4T-OLF.js → settings-page-layout-CdWZU1hd.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-Ce1pcFtR.js → setup-status-panel-CWtPM7RK.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-DWE-dUFh.js → share-preview-page-IB1v8i86.js} +1 -1
- package/dist/gateway/static/root/assets/shares-settings-ngbsBgij.js +1 -0
- package/dist/gateway/static/root/assets/skill-reload-api-BIe442Gv.js +1 -0
- package/dist/gateway/static/root/assets/skills-page-BfchZRVa.js +2 -0
- package/dist/gateway/static/root/assets/{system-settings-panel-Dd6Y35Cm.js → system-settings-panel-BxQ384RC.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-Cx6XV5A2.js → typed-models-lib-BkcpvFYO.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-D4sMbmy7.js → use-channel-catalog-Cd0FaEQ4.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-Bx-hjMQc.js → user-profile-settings-panel-DQ7NnRSY.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-D7b_vZnv.js → utils-YKhVf7Ka.js} +1 -1
- package/dist/gateway/static/root/assets/voice-api-key-field-C5Ov8Uk_.js +1 -0
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel--TdohF5N.js → workflow-run-setup-panel-BYD9BtOq.js} +2 -2
- package/dist/gateway/static/root/assets/{workflow-start-dialog-B0lavx_o.js → workflow-start-dialog-BNJVCWoS.js} +1 -1
- package/dist/gateway/static/root/assets/workflows-page-BYF29ANm.js +18 -0
- package/dist/gateway/static/root/index.html +5 -5
- package/dist/package.js +1 -1
- package/dist/src/agent/agent-manager.d.ts +6 -1
- package/dist/src/agent/agent-manager.js +28 -12
- package/dist/src/agent/agent-manager.js.map +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +1 -1
- package/dist/src/agent/embedded/xopc-tools-bridge.js +11 -7
- package/dist/src/agent/embedded/xopc-tools-bridge.js.map +1 -1
- package/dist/src/agent/lifecycle/progress.js +3 -1
- package/dist/src/agent/lifecycle/progress.js.map +1 -1
- package/dist/src/agent/lifecycle/timeout-wrapper.d.ts +2 -2
- package/dist/src/agent/lifecycle/timeout-wrapper.js +4 -4
- package/dist/src/agent/lifecycle/timeout-wrapper.js.map +1 -1
- package/dist/src/agent/memory/summary-generator.d.ts +1 -1
- package/dist/src/agent/memory/summary-generator.js +1 -2
- package/dist/src/agent/memory/summary-generator.js.map +1 -1
- package/dist/src/agent/middleware/index.d.ts +1 -1
- package/dist/src/agent/middleware/self-verify.d.ts +44 -14
- package/dist/src/agent/middleware/self-verify.js +214 -44
- package/dist/src/agent/middleware/self-verify.js.map +1 -1
- package/dist/src/agent/orchestration/agent-event-handler.js +57 -15
- package/dist/src/agent/orchestration/agent-event-handler.js.map +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +18 -13
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/prompt/system-reminder.js +2 -2
- package/dist/src/agent/prompt/system-reminder.js.map +1 -1
- package/dist/src/agent/sandbox/sanitize-env-vars.d.ts +2 -2
- package/dist/src/agent/sandbox/sanitize-env-vars.js +2 -2
- package/dist/src/agent/sandbox/sanitize-env-vars.js.map +1 -1
- package/dist/src/agent/sandbox/types.d.ts +2 -2
- package/dist/src/agent/sandbox/types.js.map +1 -1
- package/dist/src/agent/service/btw-query.js +1 -1
- package/dist/src/agent/service/btw-query.js.map +1 -1
- package/dist/src/agent/service/process-direct-one-shot.js +6 -2
- package/dist/src/agent/service/process-direct-one-shot.js.map +1 -1
- package/dist/src/agent/service/process-direct-streaming.js +12 -7
- package/dist/src/agent/service/process-direct-streaming.js.map +1 -1
- package/dist/src/agent/service.js +2 -1
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/skills/skill-tool-gating.js +1 -1
- package/dist/src/agent/skills/skill-tool-gating.js.map +1 -1
- package/dist/src/agent/starter-agents.js +9 -9
- package/dist/src/agent/starter-agents.js.map +1 -1
- package/dist/src/agent/tools/apply-patch.d.ts +25 -0
- package/dist/src/agent/tools/apply-patch.js +256 -0
- package/dist/src/agent/tools/apply-patch.js.map +1 -0
- package/dist/src/agent/tools/delegate-tool.d.ts +1 -1
- package/dist/src/agent/tools/delegate-tool.js +3 -3
- package/dist/src/agent/tools/delegate-tool.js.map +1 -1
- package/dist/src/agent/tools/exec-command.d.ts +30 -0
- package/dist/src/agent/tools/exec-command.js +224 -0
- package/dist/src/agent/tools/exec-command.js.map +1 -0
- package/dist/src/agent/tools/execute-code-tool.js +7 -4
- package/dist/src/agent/tools/execute-code-tool.js.map +1 -1
- package/dist/src/agent/tools/factory.d.ts +3 -3
- package/dist/src/agent/tools/factory.js +8 -9
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/goal-tool.d.ts +5 -1
- package/dist/src/agent/tools/goal-tool.js +8 -2
- package/dist/src/agent/tools/goal-tool.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +4 -3
- package/dist/src/agent/tools/index.js +5 -4
- package/dist/src/agent/tools/skills-tools.d.ts +1 -1
- package/dist/src/agent/tools/skills-tools.js.map +1 -1
- package/dist/src/agent/tools/update-plan-tool.d.ts +11 -0
- package/dist/src/agent/tools/update-plan-tool.js +72 -0
- package/dist/src/agent/tools/update-plan-tool.js.map +1 -0
- package/dist/src/agent/workflow/builtins/debug-incident.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js +1 -1
- package/dist/src/agent/workflow/builtins/debug-incident.js.map +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js +1 -1
- package/dist/src/agent/workflow/builtins/pr-review.js.map +1 -1
- package/dist/src/agent/workflow/builtins/release-check.d.ts +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js +1 -1
- package/dist/src/agent/workflow/builtins/release-check.js.map +1 -1
- package/dist/src/agent/workflow/step-labels.js +7 -3
- package/dist/src/agent/workflow/step-labels.js.map +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/agent-edit.js.map +1 -1
- package/dist/src/chat-commands/builtins/goal.js +4 -1
- package/dist/src/chat-commands/builtins/goal.js.map +1 -1
- package/dist/src/chat-commands/builtins/review.d.ts +1 -0
- package/dist/src/chat-commands/builtins/review.js +167 -0
- package/dist/src/chat-commands/builtins/review.js.map +1 -0
- package/dist/src/chat-commands/index.d.ts +1 -0
- package/dist/src/chat-commands/index.js +3 -1
- package/dist/src/chat-commands/index.js.map +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/gateway/agent-builtin-tools.d.ts +1 -1
- package/dist/src/gateway/agent-builtin-tools.js +3 -2
- package/dist/src/gateway/agent-builtin-tools.js.map +1 -1
- package/dist/src/gateway/agents-admin.js +1 -1
- package/dist/src/gateway/chat-stream/mapper.d.ts +6 -0
- package/dist/src/gateway/chat-stream/mapper.js +149 -6
- package/dist/src/gateway/chat-stream/mapper.js.map +1 -1
- package/dist/src/gateway/chat-stream/protocol.d.ts +51 -1
- package/dist/src/gateway/hono/routes/commands-skills.js +21 -0
- package/dist/src/gateway/hono/routes/commands-skills.js.map +1 -1
- package/dist/src/gateway/hono/routes/index.js +2 -0
- package/dist/src/gateway/hono/routes/index.js.map +1 -1
- package/dist/src/gateway/hono/routes/lazy-bundles.js +1 -0
- package/dist/src/gateway/hono/routes/lazy-bundles.js.map +1 -1
- package/dist/src/gateway/hono/routes/projects.js +136 -14
- package/dist/src/gateway/hono/routes/projects.js.map +1 -1
- package/dist/src/gateway/hono/routes/search.d.ts +3 -0
- package/dist/src/gateway/hono/routes/search.js +47 -0
- package/dist/src/gateway/hono/routes/search.js.map +1 -0
- package/dist/src/gateway/security/dangerous-tools.js +1 -1
- package/dist/src/gateway/security/dangerous-tools.js.map +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/projects/index.d.ts +3 -0
- package/dist/src/projects/index.js +4 -1
- package/dist/src/projects/project-agent-suggestion.d.ts +14 -0
- package/dist/src/projects/project-agent-suggestion.js +19 -0
- package/dist/src/projects/project-agent-suggestion.js.map +1 -0
- package/dist/src/projects/project-kind.d.ts +13 -0
- package/dist/src/projects/project-kind.js +147 -0
- package/dist/src/projects/project-kind.js.map +1 -0
- package/dist/src/projects/project-service.d.ts +15 -0
- package/dist/src/projects/project-service.js +96 -3
- package/dist/src/projects/project-service.js.map +1 -1
- package/dist/src/projects/project-store.js +32 -8
- package/dist/src/projects/project-store.js.map +1 -1
- package/dist/src/projects/types.d.ts +4 -1
- package/dist/src/projects/workspace-project.d.ts +22 -0
- package/dist/src/projects/workspace-project.js +143 -0
- package/dist/src/projects/workspace-project.js.map +1 -0
- package/dist/src/review/review-git.d.ts +48 -0
- package/dist/src/review/review-git.js +333 -0
- package/dist/src/review/review-git.js.map +1 -0
- package/dist/src/review/review-types.d.ts +22 -0
- package/dist/src/review/review-types.js +29 -0
- package/dist/src/review/review-types.js.map +1 -0
- package/dist/src/session/session-context-for-llm.js +151 -0
- package/dist/src/session/session-context-for-llm.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/021_projects_fts.sql +21 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/migrations/runner.js.map +1 -1
- package/dist/src/storage/sqlite/migrations/runner.ts +1 -1
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tui/backends/embedded-backend.d.ts +8 -1
- package/dist/src/tui/backends/embedded-backend.js +60 -1
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +9 -1
- package/dist/src/tui/backends/gateway-sse-backend.js +29 -0
- package/dist/src/tui/backends/gateway-sse-backend.js.map +1 -1
- package/dist/src/tui/components/assistant-message.js +35 -0
- package/dist/src/tui/components/assistant-message.js.map +1 -1
- package/dist/src/tui/tui-agent-events.js +25 -0
- package/dist/src/tui/tui-agent-events.js.map +1 -1
- package/dist/src/tui/tui-backend.d.ts +20 -0
- package/dist/src/tui/tui-commands.d.ts +1 -0
- package/dist/src/tui/tui-commands.js +110 -4
- package/dist/src/tui/tui-commands.js.map +1 -1
- package/dist/src/tui/tui-picker-overlay.d.ts +2 -0
- package/dist/src/tui/tui-picker-overlay.js +153 -1
- package/dist/src/tui/tui-picker-overlay.js.map +1 -1
- package/dist/src/tui/tui.js +36 -2
- package/dist/src/tui/tui.js.map +1 -1
- package/dist/src/workflows/service/workflow-run-service.js +1 -1
- package/dist/src/workflows/service/workflow-run-service.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-SsQrj9B-.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-CYb6fame.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-DpOOq1nk.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-9nXxgh4u.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-D528_YFy.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-Ck-3h5lI.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-DL-fAqE_.js +0 -2
- package/dist/gateway/static/root/assets/gateway-settings-mmmnIH27.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-Ceu5zrN6.js +0 -1
- package/dist/gateway/static/root/assets/heartbeat-settings-YoYgKlfX.js +0 -1
- package/dist/gateway/static/root/assets/index-DdqSO9R2.js +0 -191
- package/dist/gateway/static/root/assets/index-jGB-2nI-.css +0 -1
- package/dist/gateway/static/root/assets/logs-page-BgVk5DvZ.js +0 -2
- package/dist/gateway/static/root/assets/messages-B5O4OIEo.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-BU0x8PGP.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-D6JYVP4I.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-mqRJYnJS.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-DOTgzoeh.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-DGUIb4y5.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-DVKKUj3u.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-BVbzUWxW.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DrNNoSqE.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-CMDqgeMR.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-aKSraR8l.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-CS-FU_B-.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-BaF3u0LA.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-CkhbRt1q.js +0 -1
- package/dist/gateway/static/root/assets/workflows-page-CZR05Bu_.js +0 -18
- package/dist/src/agent/tools/edit.d.ts +0 -14
- package/dist/src/agent/tools/edit.js +0 -132
- package/dist/src/agent/tools/edit.js.map +0 -1
- package/dist/src/agent/tools/shell.d.ts +0 -15
- package/dist/src/agent/tools/shell.js +0 -158
- package/dist/src/agent/tools/shell.js.map +0 -1
|
@@ -37,6 +37,8 @@ const LLM_ROLES = new Set([
|
|
|
37
37
|
"tool",
|
|
38
38
|
"toolResult"
|
|
39
39
|
]);
|
|
40
|
+
const CODING_CONTEXT_LOOKBACK = 80;
|
|
41
|
+
const CODING_CONTEXT_MAX_LINES = 24;
|
|
40
42
|
function isLikelyAgentMessage(x) {
|
|
41
43
|
if (!x || typeof x !== "object") return false;
|
|
42
44
|
const role = x.role;
|
|
@@ -85,6 +87,153 @@ function bashExecutionRowToLlmMessage(row) {
|
|
|
85
87
|
timestamp: parseTimestampValue(row.timestamp)
|
|
86
88
|
};
|
|
87
89
|
}
|
|
90
|
+
function asRecord(value) {
|
|
91
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
92
|
+
}
|
|
93
|
+
function stringValue(value) {
|
|
94
|
+
return typeof value === "string" ? value : void 0;
|
|
95
|
+
}
|
|
96
|
+
function parseJsonRecord(value) {
|
|
97
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
98
|
+
try {
|
|
99
|
+
return asRecord(JSON.parse(value));
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function textFromContent(content) {
|
|
105
|
+
if (typeof content === "string") return content;
|
|
106
|
+
if (!Array.isArray(content)) return "";
|
|
107
|
+
return content.map((part) => {
|
|
108
|
+
if (typeof part === "string") return part;
|
|
109
|
+
const rec = asRecord(part);
|
|
110
|
+
if (!rec) return "";
|
|
111
|
+
if (rec.type === "text" && typeof rec.text === "string") return rec.text;
|
|
112
|
+
return "";
|
|
113
|
+
}).filter(Boolean).join("");
|
|
114
|
+
}
|
|
115
|
+
function toolNameFromBlock(block) {
|
|
116
|
+
const fn = asRecord(block.function);
|
|
117
|
+
return stringValue(block.name) ?? stringValue(block.toolName) ?? stringValue(fn?.name) ?? "tool";
|
|
118
|
+
}
|
|
119
|
+
function toolArgsFromBlock(block) {
|
|
120
|
+
const fn = asRecord(block.function);
|
|
121
|
+
return asRecord(block.arguments) ?? asRecord(block.args) ?? asRecord(block.input) ?? asRecord(fn?.arguments) ?? parseJsonRecord(block.arguments) ?? parseJsonRecord(block.args) ?? parseJsonRecord(block.input) ?? parseJsonRecord(fn?.arguments) ?? {};
|
|
122
|
+
}
|
|
123
|
+
function toolCallIdFromBlock(block) {
|
|
124
|
+
return stringValue(block.id) ?? stringValue(block.toolCallId) ?? stringValue(block.tool_call_id) ?? stringValue(block.tool_call_id$);
|
|
125
|
+
}
|
|
126
|
+
function toolResultId(row) {
|
|
127
|
+
const rec = row;
|
|
128
|
+
return stringValue(rec.toolCallId) ?? stringValue(rec.tool_call_id) ?? stringValue(rec.tool_call_id$);
|
|
129
|
+
}
|
|
130
|
+
function extractResultDetails(row) {
|
|
131
|
+
if (!row) return null;
|
|
132
|
+
const topLevelDetails = asRecord(row.details);
|
|
133
|
+
if (topLevelDetails) return topLevelDetails;
|
|
134
|
+
const contentText = textFromContent(row.content);
|
|
135
|
+
if (!contentText.trim()) return null;
|
|
136
|
+
try {
|
|
137
|
+
const rec = asRecord(JSON.parse(contentText));
|
|
138
|
+
return asRecord(rec?.details) ?? rec;
|
|
139
|
+
} catch {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function collectToolResults(messages) {
|
|
144
|
+
const results = /* @__PURE__ */ new Map();
|
|
145
|
+
for (const message of messages) {
|
|
146
|
+
const role = message.role;
|
|
147
|
+
if (role !== "tool" && role !== "toolResult") continue;
|
|
148
|
+
const id = toolResultId(message);
|
|
149
|
+
if (id) results.set(id, message);
|
|
150
|
+
}
|
|
151
|
+
return results;
|
|
152
|
+
}
|
|
153
|
+
function extractToolCalls(message) {
|
|
154
|
+
const content = message.content;
|
|
155
|
+
const calls = [];
|
|
156
|
+
if (Array.isArray(content)) for (const block of content) {
|
|
157
|
+
const rec = asRecord(block);
|
|
158
|
+
const type = rec ? stringValue(rec.type) : void 0;
|
|
159
|
+
if (!rec || type !== "toolCall" && type !== "tool_use" && type !== "tool_call") continue;
|
|
160
|
+
calls.push({
|
|
161
|
+
id: toolCallIdFromBlock(rec),
|
|
162
|
+
name: toolNameFromBlock(rec),
|
|
163
|
+
args: toolArgsFromBlock(rec)
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const toolCalls = message.tool_calls;
|
|
167
|
+
if (Array.isArray(toolCalls)) for (const call of toolCalls) {
|
|
168
|
+
const rec = asRecord(call);
|
|
169
|
+
const fn = asRecord(rec?.function);
|
|
170
|
+
const name = stringValue(fn?.name) ?? stringValue(rec?.name);
|
|
171
|
+
if (!rec || !name) continue;
|
|
172
|
+
calls.push({
|
|
173
|
+
id: stringValue(rec.id),
|
|
174
|
+
name,
|
|
175
|
+
args: parseJsonRecord(fn?.arguments) ?? asRecord(fn?.arguments) ?? {}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return calls;
|
|
179
|
+
}
|
|
180
|
+
function summarizePlan(details) {
|
|
181
|
+
const lines = (Array.isArray(details.plan) ? details.plan : []).map((item) => {
|
|
182
|
+
const rec = asRecord(item);
|
|
183
|
+
const step = stringValue(rec?.step)?.trim();
|
|
184
|
+
const status = stringValue(rec?.status)?.trim();
|
|
185
|
+
return step && status ? `${status}: ${step}` : "";
|
|
186
|
+
}).filter(Boolean).slice(0, 8);
|
|
187
|
+
if (lines.length === 0) return void 0;
|
|
188
|
+
const explanation = stringValue(details.explanation)?.trim();
|
|
189
|
+
return `Plan${explanation ? ` (${explanation})` : ""}: ${lines.join(" | ")}`;
|
|
190
|
+
}
|
|
191
|
+
function summarizeCommand(args, details) {
|
|
192
|
+
const command = stringValue(details?.command) ?? stringValue(args.cmd) ?? stringValue(args.command) ?? "";
|
|
193
|
+
const status = stringValue(details?.status) ?? (details?.exitCode === 0 ? "success" : "unknown");
|
|
194
|
+
const exitCode = typeof details?.exitCode === "number" ? details.exitCode : void 0;
|
|
195
|
+
const hint = stringValue(details?.failureHint);
|
|
196
|
+
return [
|
|
197
|
+
`Command ${status}: ${command || "(unknown command)"}`,
|
|
198
|
+
exitCode !== void 0 ? `exit=${exitCode}` : "",
|
|
199
|
+
hint ? `hint=${hint}` : ""
|
|
200
|
+
].filter(Boolean).join(" ");
|
|
201
|
+
}
|
|
202
|
+
function summarizePatch(details) {
|
|
203
|
+
if (!details) return void 0;
|
|
204
|
+
const files = Array.isArray(details.files) ? details.files.filter((file) => typeof file === "string" && file.trim().length > 0) : [];
|
|
205
|
+
const added = typeof details.added === "number" ? details.added : 0;
|
|
206
|
+
const removed = typeof details.removed === "number" ? details.removed : 0;
|
|
207
|
+
const summary = stringValue(details.summary)?.trim();
|
|
208
|
+
return `Patch applied: ${files.length ? files.join(", ") : summary || "(files unknown)"} (+${added}/-${removed})`;
|
|
209
|
+
}
|
|
210
|
+
function buildCodingContextMessage(messages) {
|
|
211
|
+
const recent = messages.slice(-CODING_CONTEXT_LOOKBACK);
|
|
212
|
+
const results = collectToolResults(recent);
|
|
213
|
+
const lines = [];
|
|
214
|
+
for (const message of recent) {
|
|
215
|
+
if (message.role !== "assistant") continue;
|
|
216
|
+
for (const call of extractToolCalls(message)) {
|
|
217
|
+
if (call.name !== "update_plan" && call.name !== "exec_command" && call.name !== "apply_patch") continue;
|
|
218
|
+
const details = extractResultDetails(call.id ? results.get(call.id) : void 0);
|
|
219
|
+
const line = call.name === "update_plan" ? summarizePlan(details ?? call.args) : call.name === "exec_command" ? summarizeCommand(call.args, details) : summarizePatch(details);
|
|
220
|
+
if (line) lines.push(line);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const uniqueLines = [...new Set(lines)].slice(-CODING_CONTEXT_MAX_LINES);
|
|
224
|
+
if (uniqueLines.length === 0) return null;
|
|
225
|
+
return {
|
|
226
|
+
role: "user",
|
|
227
|
+
content: [{
|
|
228
|
+
type: "text",
|
|
229
|
+
text: [
|
|
230
|
+
"<coding_context>",
|
|
231
|
+
...uniqueLines.map((line) => `- ${line}`),
|
|
232
|
+
"</coding_context>"
|
|
233
|
+
].join("\n")
|
|
234
|
+
}]
|
|
235
|
+
};
|
|
236
|
+
}
|
|
88
237
|
/**
|
|
89
238
|
* Normalize a JSON array from on-disk transcript into stored rows (drops unrecognized objects).
|
|
90
239
|
*/
|
|
@@ -140,6 +289,8 @@ function buildSessionContextForLlm(rows) {
|
|
|
140
289
|
}
|
|
141
290
|
if (isLikelyAgentMessage(r)) out.push(r);
|
|
142
291
|
}
|
|
292
|
+
const codingContext = buildCodingContextMessage(out);
|
|
293
|
+
if (codingContext) out.push(codingContext);
|
|
143
294
|
return out;
|
|
144
295
|
}
|
|
145
296
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-context-for-llm.js","names":[],"sources":["../../../src/session/session-context-for-llm.ts"],"sourcesContent":["/**\n * Transcript rows persisted on disk may include non-LLM entries (e.g. `kind: 'context'`).\n * {@link buildSessionContextForLlm} is the single choke point for provider-facing history.\n *\n * Do not pass raw on-disk JSONL rows into pi-agent / providers — always run\n * {@link buildSessionContextForLlm} first (or use {@link SessionStore.loadMessages}, which already does).\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\n/** Persisted-only row: never sent to the model as a chat message. */\nexport interface XopcTranscriptContextEntry {\n kind: 'context';\n id?: string;\n /** Short human-readable line for UIs / logs. */\n text?: string;\n /** Structured payload (tool summaries, delivery metadata, etc.). */\n data?: Record<string, unknown>;\n createdAt?: string;\n}\n\n/** Persisted-only row for replaying local shell executions in clients. */\nexport interface XopcTranscriptBashExecutionEntry {\n role: 'bashExecution';\n command?: string;\n output?: string | unknown[];\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n excludedFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n timestamp?: string | number;\n}\n\n/** Persisted extension-injected message row for client replay. */\nexport interface XopcTranscriptCustomMessageEntry {\n role: 'custom';\n customType?: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n timestamp?: string | number;\n}\n\n/** Persisted extension custom_message entry shape before conversion to an agent message. */\nexport interface XopcTranscriptCustomMessageFileEntry {\n type: 'custom_message';\n customType?: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n timestamp?: string | number;\n}\n\n/** Persisted extension state entry for replay by extension sessionManager APIs. */\nexport interface XopcTranscriptCustomStateEntry {\n type: 'custom';\n customType?: string;\n data?: unknown;\n timestamp?: string | number;\n}\n\n/** Persisted branch summary row generated when returning from a branch. */\nexport interface XopcTranscriptBranchSummaryEntry {\n role: 'branchSummary';\n summary?: string;\n fromId?: string;\n timestamp?: string | number;\n}\n\n/** Persisted compaction summary row generated after transcript compaction. */\nexport interface XopcTranscriptCompactionSummaryEntry {\n role: 'compactionSummary';\n summary?: string;\n tokensBefore?: number;\n timestamp?: string | number;\n}\n\n/** Persisted label change for a transcript entry. */\nexport interface XopcTranscriptLabelEntry {\n type: 'label';\n id?: string;\n parentId?: string | null;\n targetId?: string;\n label?: string;\n timestamp?: string | number;\n}\n\n/** Persisted thinking level change metadata row. */\nexport interface XopcTranscriptThinkingLevelChangeEntry {\n type: 'thinking_level_change';\n id?: string;\n parentId?: string | null;\n thinkingLevel?: string;\n timestamp?: string | number;\n}\n\n/** Persisted model change metadata row. */\nexport interface XopcTranscriptModelChangeEntry {\n type: 'model_change';\n id?: string;\n parentId?: string | null;\n provider?: string;\n modelId?: string;\n timestamp?: string | number;\n}\n\n/** Persisted session info metadata row. */\nexport interface XopcTranscriptSessionInfoEntry {\n type: 'session_info';\n id?: string;\n parentId?: string | null;\n name?: string;\n timestamp?: string | number;\n}\n\nexport type TranscriptStoredRow =\n | AgentMessage\n | XopcTranscriptContextEntry\n | XopcTranscriptBashExecutionEntry\n | XopcTranscriptCustomMessageEntry\n | XopcTranscriptCustomMessageFileEntry\n | XopcTranscriptCustomStateEntry\n | XopcTranscriptBranchSummaryEntry\n | XopcTranscriptCompactionSummaryEntry\n | XopcTranscriptLabelEntry\n | XopcTranscriptThinkingLevelChangeEntry\n | XopcTranscriptModelChangeEntry\n | XopcTranscriptSessionInfoEntry;\n\nexport function isTranscriptContextEntry(x: unknown): x is XopcTranscriptContextEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).kind === 'context';\n}\n\nexport function isTranscriptBashExecutionEntry(x: unknown): x is XopcTranscriptBashExecutionEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).role === 'bashExecution';\n}\n\nexport function isTranscriptCustomMessageEntry(\n x: unknown,\n): x is XopcTranscriptCustomMessageEntry | XopcTranscriptCustomMessageFileEntry {\n if (!x || typeof x !== 'object') return false;\n const row = x as Record<string, unknown>;\n return row.role === 'custom' || row.type === 'custom_message';\n}\n\nexport function isTranscriptCustomStateEntry(x: unknown): x is XopcTranscriptCustomStateEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).type === 'custom';\n}\n\nexport function isTranscriptSummaryMessageEntry(\n x: unknown,\n): x is XopcTranscriptBranchSummaryEntry | XopcTranscriptCompactionSummaryEntry {\n if (!x || typeof x !== 'object') return false;\n const role = (x as Record<string, unknown>).role;\n return role === 'branchSummary' || role === 'compactionSummary';\n}\n\nexport function isTranscriptLabelEntry(x: unknown): x is XopcTranscriptLabelEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).type === 'label';\n}\n\nexport function isTranscriptMetadataEntry(\n x: unknown,\n): x is XopcTranscriptThinkingLevelChangeEntry | XopcTranscriptModelChangeEntry | XopcTranscriptSessionInfoEntry {\n if (!x || typeof x !== 'object') return false;\n const type = (x as Record<string, unknown>).type;\n return type === 'thinking_level_change' || type === 'model_change' || type === 'session_info';\n}\n\nconst LLM_ROLES = new Set(['user', 'assistant', 'system', 'tool', 'toolResult']);\n\nfunction isLikelyAgentMessage(x: unknown): x is AgentMessage {\n if (!x || typeof x !== 'object') return false;\n const role = (x as Record<string, unknown>).role;\n return typeof role === 'string' && LLM_ROLES.has(role);\n}\n\nfunction parseTimestampValue(timestamp: string | number | undefined): number | undefined {\n if (typeof timestamp === 'number') {\n return Number.isFinite(timestamp) ? timestamp : undefined;\n }\n if (typeof timestamp !== 'string') {\n return undefined;\n }\n const ms = Date.parse(timestamp);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction customMessageRowToLlmMessage(\n row: XopcTranscriptCustomMessageEntry | XopcTranscriptCustomMessageFileEntry,\n): AgentMessage {\n const content = typeof row.content === 'string'\n ? [{ type: 'text' as const, text: row.content }]\n : Array.isArray(row.content)\n ? row.content\n : [{ type: 'text' as const, text: '' }];\n return {\n role: 'user',\n content,\n timestamp: parseTimestampValue(row.timestamp),\n } as AgentMessage;\n}\n\nfunction bashExecutionRowToLlmMessage(row: XopcTranscriptBashExecutionEntry): AgentMessage | null {\n if (row.excludeFromContext === true || row.excludedFromContext === true) {\n return null;\n }\n const command = row.command?.trim();\n if (!command) {\n return null;\n }\n const output = typeof row.output === 'string'\n ? row.output\n : Array.isArray(row.output)\n ? row.output\n .filter((part): part is { type: string; text?: string } =>\n typeof part === 'object' && part !== null && 'type' in part)\n .map((part) => (part.type === 'text' && typeof part.text === 'string' ? part.text : ''))\n .join('')\n : '';\n const exit = row.exitCode == null ? '?' : String(row.exitCode);\n const signal = row.signal ? ` signal=${row.signal}` : '';\n const truncated = row.truncated ? '\\n[output truncated]' : '';\n return {\n role: 'user',\n content: [{\n type: 'text',\n text: [\n '<local_shell>',\n `$ ${command}`,\n `exit=${exit}${signal}`,\n output ? 'output:' : 'output: (empty)',\n output.trimEnd(),\n `${truncated}</local_shell>`,\n ].join('\\n'),\n }],\n timestamp: parseTimestampValue(row.timestamp),\n } as AgentMessage;\n}\n\n/**\n * Normalize a JSON array from on-disk transcript into stored rows (drops unrecognized objects).\n */\nexport function transcriptRowsFromJsonArray(arr: unknown[]): TranscriptStoredRow[] {\n const out: TranscriptStoredRow[] = [];\n for (const x of arr) {\n if (isTranscriptContextEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptBashExecutionEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptCustomMessageEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptCustomStateEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptSummaryMessageEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptLabelEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptMetadataEntry(x)) {\n out.push(x);\n continue;\n }\n if (isLikelyAgentMessage(x)) {\n out.push(x);\n }\n }\n return out;\n}\n\n/** Messages only — what providers and pi-agent should see. */\nexport function buildSessionContextForLlm(rows: TranscriptStoredRow[]): AgentMessage[] {\n const out: AgentMessage[] = [];\n for (const r of rows) {\n if (isTranscriptContextEntry(r)) {\n continue;\n }\n if ((r as { type?: string }).type === 'compaction') {\n continue;\n }\n if (isTranscriptBashExecutionEntry(r)) {\n const msg = bashExecutionRowToLlmMessage(r);\n if (msg) out.push(msg);\n continue;\n }\n if (isTranscriptCustomMessageEntry(r)) {\n out.push(customMessageRowToLlmMessage(r));\n continue;\n }\n if (isLikelyAgentMessage(r)) {\n out.push(r);\n }\n }\n return out;\n}\n\n/**\n * When persisting LLM messages, keep prior `kind: 'context'` rows in their relative positions:\n * each non-context slot in the previous file is replaced by the next incoming LLM message;\n * trailing new LLM rows are appended. Extra old LLM rows are dropped if the new list is shorter.\n */\nexport function mergeLlmMessagesPreservingContextRows(\n prevRows: TranscriptStoredRow[],\n llmMessages: AgentMessage[],\n): TranscriptStoredRow[] {\n let i = 0;\n const out: TranscriptStoredRow[] = [];\n for (const r of prevRows) {\n if (\n isTranscriptContextEntry(r) ||\n isTranscriptBashExecutionEntry(r) ||\n isTranscriptCustomMessageEntry(r) ||\n isTranscriptCustomStateEntry(r) ||\n isTranscriptSummaryMessageEntry(r) ||\n isTranscriptLabelEntry(r) ||\n isTranscriptMetadataEntry(r)\n ) {\n out.push(r);\n } else {\n if (i < llmMessages.length) {\n out.push(llmMessages[i]);\n i += 1;\n }\n }\n }\n while (i < llmMessages.length) {\n out.push(llmMessages[i]);\n i += 1;\n }\n return out;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"session-context-for-llm.js","names":[],"sources":["../../../src/session/session-context-for-llm.ts"],"sourcesContent":["/**\n * Transcript rows persisted on disk may include non-LLM entries (e.g. `kind: 'context'`).\n * {@link buildSessionContextForLlm} is the single choke point for provider-facing history.\n *\n * Do not pass raw on-disk JSONL rows into pi-agent / providers — always run\n * {@link buildSessionContextForLlm} first (or use {@link SessionStore.loadMessages}, which already does).\n */\n\nimport type { AgentMessage } from '@earendil-works/pi-agent-core';\n\n/** Persisted-only row: never sent to the model as a chat message. */\nexport interface XopcTranscriptContextEntry {\n kind: 'context';\n id?: string;\n /** Short human-readable line for UIs / logs. */\n text?: string;\n /** Structured payload (tool summaries, delivery metadata, etc.). */\n data?: Record<string, unknown>;\n createdAt?: string;\n}\n\n/** Persisted-only row for replaying local shell executions in clients. */\nexport interface XopcTranscriptBashExecutionEntry {\n role: 'bashExecution';\n command?: string;\n output?: string | unknown[];\n exitCode?: number | null;\n signal?: string | null;\n excludeFromContext?: boolean;\n excludedFromContext?: boolean;\n truncated?: boolean;\n fullOutputPath?: string;\n timestamp?: string | number;\n}\n\n/** Persisted extension-injected message row for client replay. */\nexport interface XopcTranscriptCustomMessageEntry {\n role: 'custom';\n customType?: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n timestamp?: string | number;\n}\n\n/** Persisted extension custom_message entry shape before conversion to an agent message. */\nexport interface XopcTranscriptCustomMessageFileEntry {\n type: 'custom_message';\n customType?: string;\n content?: string | unknown[];\n display?: boolean;\n details?: unknown;\n timestamp?: string | number;\n}\n\n/** Persisted extension state entry for replay by extension sessionManager APIs. */\nexport interface XopcTranscriptCustomStateEntry {\n type: 'custom';\n customType?: string;\n data?: unknown;\n timestamp?: string | number;\n}\n\n/** Persisted branch summary row generated when returning from a branch. */\nexport interface XopcTranscriptBranchSummaryEntry {\n role: 'branchSummary';\n summary?: string;\n fromId?: string;\n timestamp?: string | number;\n}\n\n/** Persisted compaction summary row generated after transcript compaction. */\nexport interface XopcTranscriptCompactionSummaryEntry {\n role: 'compactionSummary';\n summary?: string;\n tokensBefore?: number;\n timestamp?: string | number;\n}\n\n/** Persisted label change for a transcript entry. */\nexport interface XopcTranscriptLabelEntry {\n type: 'label';\n id?: string;\n parentId?: string | null;\n targetId?: string;\n label?: string;\n timestamp?: string | number;\n}\n\n/** Persisted thinking level change metadata row. */\nexport interface XopcTranscriptThinkingLevelChangeEntry {\n type: 'thinking_level_change';\n id?: string;\n parentId?: string | null;\n thinkingLevel?: string;\n timestamp?: string | number;\n}\n\n/** Persisted model change metadata row. */\nexport interface XopcTranscriptModelChangeEntry {\n type: 'model_change';\n id?: string;\n parentId?: string | null;\n provider?: string;\n modelId?: string;\n timestamp?: string | number;\n}\n\n/** Persisted session info metadata row. */\nexport interface XopcTranscriptSessionInfoEntry {\n type: 'session_info';\n id?: string;\n parentId?: string | null;\n name?: string;\n timestamp?: string | number;\n}\n\nexport type TranscriptStoredRow =\n | AgentMessage\n | XopcTranscriptContextEntry\n | XopcTranscriptBashExecutionEntry\n | XopcTranscriptCustomMessageEntry\n | XopcTranscriptCustomMessageFileEntry\n | XopcTranscriptCustomStateEntry\n | XopcTranscriptBranchSummaryEntry\n | XopcTranscriptCompactionSummaryEntry\n | XopcTranscriptLabelEntry\n | XopcTranscriptThinkingLevelChangeEntry\n | XopcTranscriptModelChangeEntry\n | XopcTranscriptSessionInfoEntry;\n\nexport function isTranscriptContextEntry(x: unknown): x is XopcTranscriptContextEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).kind === 'context';\n}\n\nexport function isTranscriptBashExecutionEntry(x: unknown): x is XopcTranscriptBashExecutionEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).role === 'bashExecution';\n}\n\nexport function isTranscriptCustomMessageEntry(\n x: unknown,\n): x is XopcTranscriptCustomMessageEntry | XopcTranscriptCustomMessageFileEntry {\n if (!x || typeof x !== 'object') return false;\n const row = x as Record<string, unknown>;\n return row.role === 'custom' || row.type === 'custom_message';\n}\n\nexport function isTranscriptCustomStateEntry(x: unknown): x is XopcTranscriptCustomStateEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).type === 'custom';\n}\n\nexport function isTranscriptSummaryMessageEntry(\n x: unknown,\n): x is XopcTranscriptBranchSummaryEntry | XopcTranscriptCompactionSummaryEntry {\n if (!x || typeof x !== 'object') return false;\n const role = (x as Record<string, unknown>).role;\n return role === 'branchSummary' || role === 'compactionSummary';\n}\n\nexport function isTranscriptLabelEntry(x: unknown): x is XopcTranscriptLabelEntry {\n if (!x || typeof x !== 'object') return false;\n return (x as Record<string, unknown>).type === 'label';\n}\n\nexport function isTranscriptMetadataEntry(\n x: unknown,\n): x is XopcTranscriptThinkingLevelChangeEntry | XopcTranscriptModelChangeEntry | XopcTranscriptSessionInfoEntry {\n if (!x || typeof x !== 'object') return false;\n const type = (x as Record<string, unknown>).type;\n return type === 'thinking_level_change' || type === 'model_change' || type === 'session_info';\n}\n\nconst LLM_ROLES = new Set(['user', 'assistant', 'system', 'tool', 'toolResult']);\nconst CODING_CONTEXT_LOOKBACK = 80;\nconst CODING_CONTEXT_MAX_LINES = 24;\n\nfunction isLikelyAgentMessage(x: unknown): x is AgentMessage {\n if (!x || typeof x !== 'object') return false;\n const role = (x as Record<string, unknown>).role;\n return typeof role === 'string' && LLM_ROLES.has(role);\n}\n\nfunction parseTimestampValue(timestamp: string | number | undefined): number | undefined {\n if (typeof timestamp === 'number') {\n return Number.isFinite(timestamp) ? timestamp : undefined;\n }\n if (typeof timestamp !== 'string') {\n return undefined;\n }\n const ms = Date.parse(timestamp);\n return Number.isFinite(ms) ? ms : undefined;\n}\n\nfunction customMessageRowToLlmMessage(\n row: XopcTranscriptCustomMessageEntry | XopcTranscriptCustomMessageFileEntry,\n): AgentMessage {\n const content = typeof row.content === 'string'\n ? [{ type: 'text' as const, text: row.content }]\n : Array.isArray(row.content)\n ? row.content\n : [{ type: 'text' as const, text: '' }];\n return {\n role: 'user',\n content,\n timestamp: parseTimestampValue(row.timestamp),\n } as AgentMessage;\n}\n\nfunction bashExecutionRowToLlmMessage(row: XopcTranscriptBashExecutionEntry): AgentMessage | null {\n if (row.excludeFromContext === true || row.excludedFromContext === true) {\n return null;\n }\n const command = row.command?.trim();\n if (!command) {\n return null;\n }\n const output = typeof row.output === 'string'\n ? row.output\n : Array.isArray(row.output)\n ? row.output\n .filter((part): part is { type: string; text?: string } =>\n typeof part === 'object' && part !== null && 'type' in part)\n .map((part) => (part.type === 'text' && typeof part.text === 'string' ? part.text : ''))\n .join('')\n : '';\n const exit = row.exitCode == null ? '?' : String(row.exitCode);\n const signal = row.signal ? ` signal=${row.signal}` : '';\n const truncated = row.truncated ? '\\n[output truncated]' : '';\n return {\n role: 'user',\n content: [{\n type: 'text',\n text: [\n '<local_shell>',\n `$ ${command}`,\n `exit=${exit}${signal}`,\n output ? 'output:' : 'output: (empty)',\n output.trimEnd(),\n `${truncated}</local_shell>`,\n ].join('\\n'),\n }],\n timestamp: parseTimestampValue(row.timestamp),\n } as AgentMessage;\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === 'object' && !Array.isArray(value)\n ? value as Record<string, unknown>\n : null;\n}\n\nfunction stringValue(value: unknown): string | undefined {\n return typeof value === 'string' ? value : undefined;\n}\n\nfunction parseJsonRecord(value: unknown): Record<string, unknown> | null {\n if (typeof value !== 'string' || !value.trim()) return null;\n try {\n return asRecord(JSON.parse(value));\n } catch {\n return null;\n }\n}\n\nfunction textFromContent(content: unknown): string {\n if (typeof content === 'string') return content;\n if (!Array.isArray(content)) return '';\n return content\n .map((part) => {\n if (typeof part === 'string') return part;\n const rec = asRecord(part);\n if (!rec) return '';\n if (rec.type === 'text' && typeof rec.text === 'string') return rec.text;\n return '';\n })\n .filter(Boolean)\n .join('');\n}\n\nfunction toolNameFromBlock(block: Record<string, unknown>): string {\n const fn = asRecord(block.function);\n return stringValue(block.name) ?? stringValue(block.toolName) ?? stringValue(fn?.name) ?? 'tool';\n}\n\nfunction toolArgsFromBlock(block: Record<string, unknown>): Record<string, unknown> {\n const fn = asRecord(block.function);\n return (\n asRecord(block.arguments) ??\n asRecord(block.args) ??\n asRecord(block.input) ??\n asRecord(fn?.arguments) ??\n parseJsonRecord(block.arguments) ??\n parseJsonRecord(block.args) ??\n parseJsonRecord(block.input) ??\n parseJsonRecord(fn?.arguments) ??\n {}\n );\n}\n\nfunction toolCallIdFromBlock(block: Record<string, unknown>): string | undefined {\n return (\n stringValue(block.id) ??\n stringValue(block.toolCallId) ??\n stringValue(block.tool_call_id) ??\n stringValue(block.tool_call_id$)\n );\n}\n\nfunction toolResultId(row: AgentMessage): string | undefined {\n const rec = row as unknown as Record<string, unknown>;\n return (\n stringValue(rec.toolCallId) ??\n stringValue(rec.tool_call_id) ??\n stringValue(rec.tool_call_id$)\n );\n}\n\nfunction extractResultDetails(row: AgentMessage | undefined): Record<string, unknown> | null {\n if (!row) return null;\n const topLevelDetails = asRecord((row as unknown as { details?: unknown }).details);\n if (topLevelDetails) return topLevelDetails;\n\n const contentText = textFromContent((row as { content?: unknown }).content);\n if (!contentText.trim()) return null;\n try {\n const parsed = JSON.parse(contentText) as unknown;\n const rec = asRecord(parsed);\n const details = asRecord(rec?.details);\n return details ?? rec;\n } catch {\n return null;\n }\n}\n\nfunction collectToolResults(messages: AgentMessage[]): Map<string, AgentMessage> {\n const results = new Map<string, AgentMessage>();\n for (const message of messages) {\n const role = (message as { role?: unknown }).role;\n if (role !== 'tool' && role !== 'toolResult') continue;\n const id = toolResultId(message);\n if (id) results.set(id, message);\n }\n return results;\n}\n\nfunction extractToolCalls(message: AgentMessage): Array<{ id?: string; name: string; args: Record<string, unknown> }> {\n const content = (message as { content?: unknown }).content;\n const calls: Array<{ id?: string; name: string; args: Record<string, unknown> }> = [];\n if (Array.isArray(content)) {\n for (const block of content) {\n const rec = asRecord(block);\n const type = rec ? stringValue(rec.type) : undefined;\n if (!rec || (type !== 'toolCall' && type !== 'tool_use' && type !== 'tool_call')) continue;\n calls.push({ id: toolCallIdFromBlock(rec), name: toolNameFromBlock(rec), args: toolArgsFromBlock(rec) });\n }\n }\n const toolCalls = (message as { tool_calls?: unknown }).tool_calls;\n if (Array.isArray(toolCalls)) {\n for (const call of toolCalls) {\n const rec = asRecord(call);\n const fn = asRecord(rec?.function);\n const name = stringValue(fn?.name) ?? stringValue(rec?.name);\n if (!rec || !name) continue;\n calls.push({\n id: stringValue(rec.id),\n name,\n args: parseJsonRecord(fn?.arguments) ?? asRecord(fn?.arguments) ?? {},\n });\n }\n }\n return calls;\n}\n\nfunction summarizePlan(details: Record<string, unknown>): string | undefined {\n const plan = Array.isArray(details.plan) ? details.plan : [];\n const lines = plan\n .map((item) => {\n const rec = asRecord(item);\n const step = stringValue(rec?.step)?.trim();\n const status = stringValue(rec?.status)?.trim();\n return step && status ? `${status}: ${step}` : '';\n })\n .filter(Boolean)\n .slice(0, 8);\n if (lines.length === 0) return undefined;\n const explanation = stringValue(details.explanation)?.trim();\n return `Plan${explanation ? ` (${explanation})` : ''}: ${lines.join(' | ')}`;\n}\n\nfunction summarizeCommand(args: Record<string, unknown>, details: Record<string, unknown> | null): string {\n const command = stringValue(details?.command) ?? stringValue(args.cmd) ?? stringValue(args.command) ?? '';\n const status = stringValue(details?.status) ?? (details?.exitCode === 0 ? 'success' : 'unknown');\n const exitCode = typeof details?.exitCode === 'number' ? details.exitCode : undefined;\n const hint = stringValue(details?.failureHint);\n return [\n `Command ${status}: ${command || '(unknown command)'}`,\n exitCode !== undefined ? `exit=${exitCode}` : '',\n hint ? `hint=${hint}` : '',\n ].filter(Boolean).join(' ');\n}\n\nfunction summarizePatch(details: Record<string, unknown> | null): string | undefined {\n if (!details) return undefined;\n const files = Array.isArray(details.files)\n ? details.files.filter((file): file is string => typeof file === 'string' && file.trim().length > 0)\n : [];\n const added = typeof details.added === 'number' ? details.added : 0;\n const removed = typeof details.removed === 'number' ? details.removed : 0;\n const summary = stringValue(details.summary)?.trim();\n return `Patch applied: ${files.length ? files.join(', ') : summary || '(files unknown)'} (+${added}/-${removed})`;\n}\n\nfunction buildCodingContextMessage(messages: AgentMessage[]): AgentMessage | null {\n const recent = messages.slice(-CODING_CONTEXT_LOOKBACK);\n const results = collectToolResults(recent);\n const lines: string[] = [];\n\n for (const message of recent) {\n if ((message as { role?: unknown }).role !== 'assistant') continue;\n for (const call of extractToolCalls(message)) {\n if (call.name !== 'update_plan' && call.name !== 'exec_command' && call.name !== 'apply_patch') {\n continue;\n }\n const result = call.id ? results.get(call.id) : undefined;\n const details = extractResultDetails(result);\n const line = call.name === 'update_plan'\n ? summarizePlan(details ?? call.args)\n : call.name === 'exec_command'\n ? summarizeCommand(call.args, details)\n : summarizePatch(details);\n if (line) lines.push(line);\n }\n }\n\n const uniqueLines = [...new Set(lines)].slice(-CODING_CONTEXT_MAX_LINES);\n if (uniqueLines.length === 0) return null;\n return {\n role: 'user',\n content: [{\n type: 'text',\n text: ['<coding_context>', ...uniqueLines.map((line) => `- ${line}`), '</coding_context>'].join('\\n'),\n }],\n } as AgentMessage;\n}\n\n/**\n * Normalize a JSON array from on-disk transcript into stored rows (drops unrecognized objects).\n */\nexport function transcriptRowsFromJsonArray(arr: unknown[]): TranscriptStoredRow[] {\n const out: TranscriptStoredRow[] = [];\n for (const x of arr) {\n if (isTranscriptContextEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptBashExecutionEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptCustomMessageEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptCustomStateEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptSummaryMessageEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptLabelEntry(x)) {\n out.push(x);\n continue;\n }\n if (isTranscriptMetadataEntry(x)) {\n out.push(x);\n continue;\n }\n if (isLikelyAgentMessage(x)) {\n out.push(x);\n }\n }\n return out;\n}\n\n/** Messages only — what providers and pi-agent should see. */\nexport function buildSessionContextForLlm(rows: TranscriptStoredRow[]): AgentMessage[] {\n const out: AgentMessage[] = [];\n for (const r of rows) {\n if (isTranscriptContextEntry(r)) {\n continue;\n }\n if ((r as { type?: string }).type === 'compaction') {\n continue;\n }\n if (isTranscriptBashExecutionEntry(r)) {\n const msg = bashExecutionRowToLlmMessage(r);\n if (msg) out.push(msg);\n continue;\n }\n if (isTranscriptCustomMessageEntry(r)) {\n out.push(customMessageRowToLlmMessage(r));\n continue;\n }\n if (isLikelyAgentMessage(r)) {\n out.push(r);\n }\n }\n const codingContext = buildCodingContextMessage(out);\n if (codingContext) out.push(codingContext);\n return out;\n}\n\n/**\n * When persisting LLM messages, keep prior `kind: 'context'` rows in their relative positions:\n * each non-context slot in the previous file is replaced by the next incoming LLM message;\n * trailing new LLM rows are appended. Extra old LLM rows are dropped if the new list is shorter.\n */\nexport function mergeLlmMessagesPreservingContextRows(\n prevRows: TranscriptStoredRow[],\n llmMessages: AgentMessage[],\n): TranscriptStoredRow[] {\n let i = 0;\n const out: TranscriptStoredRow[] = [];\n for (const r of prevRows) {\n if (\n isTranscriptContextEntry(r) ||\n isTranscriptBashExecutionEntry(r) ||\n isTranscriptCustomMessageEntry(r) ||\n isTranscriptCustomStateEntry(r) ||\n isTranscriptSummaryMessageEntry(r) ||\n isTranscriptLabelEntry(r) ||\n isTranscriptMetadataEntry(r)\n ) {\n out.push(r);\n } else {\n if (i < llmMessages.length) {\n out.push(llmMessages[i]);\n i += 1;\n }\n }\n }\n while (i < llmMessages.length) {\n out.push(llmMessages[i]);\n i += 1;\n }\n return out;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS projects_fts USING fts5(
|
|
2
|
+
content,
|
|
3
|
+
project_id UNINDEXED,
|
|
4
|
+
tokenize='unicode61'
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
DELETE FROM projects_fts;
|
|
8
|
+
|
|
9
|
+
INSERT INTO projects_fts (content, project_id)
|
|
10
|
+
SELECT
|
|
11
|
+
trim(
|
|
12
|
+
coalesce(name, '') || char(10) ||
|
|
13
|
+
coalesce(slug, '') || char(10) ||
|
|
14
|
+
coalesce(description, '') || char(10) ||
|
|
15
|
+
coalesce(workspace_root, '') || char(10) ||
|
|
16
|
+
coalesce(brief, '') || char(10) ||
|
|
17
|
+
coalesce(instructions, '') || char(10) ||
|
|
18
|
+
coalesce(default_agent_id, '')
|
|
19
|
+
) AS content,
|
|
20
|
+
project_id
|
|
21
|
+
FROM projects;
|
|
@@ -3,7 +3,7 @@ import type { ApplyMigrationsOptions } from './types.js';
|
|
|
3
3
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
4
4
|
export declare const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
5
5
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
6
|
-
export declare const XOPC_DB_SCHEMA_VERSION =
|
|
6
|
+
export declare const XOPC_DB_SCHEMA_VERSION = 21;
|
|
7
7
|
export declare function resolveMigrationsDir(override?: string): string;
|
|
8
8
|
/**
|
|
9
9
|
* Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.
|
|
@@ -12,7 +12,7 @@ const log = createLogger("Sqlite:Migrations");
|
|
|
12
12
|
/** Baseline schema version applied from schema.sql on first open. */
|
|
13
13
|
const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
14
14
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
15
|
-
const XOPC_DB_SCHEMA_VERSION =
|
|
15
|
+
const XOPC_DB_SCHEMA_VERSION = 21;
|
|
16
16
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
17
17
|
function resolveMigrationsDir(override) {
|
|
18
18
|
if (override) return override;
|
|
@@ -46,7 +46,7 @@ function applySingleMigration(db, migration) {
|
|
|
46
46
|
* Each step runs in its own transaction and updates schema_meta on success.
|
|
47
47
|
*/
|
|
48
48
|
function applyPendingMigrations(db, options = {}) {
|
|
49
|
-
const targetVersion = options.targetVersion ??
|
|
49
|
+
const targetVersion = options.targetVersion ?? 21;
|
|
50
50
|
let currentVersion = readSchemaVersion(db);
|
|
51
51
|
if (currentVersion > targetVersion) throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);
|
|
52
52
|
if (currentVersion >= targetVersion) return currentVersion;
|
|
@@ -62,7 +62,7 @@ function applyPendingMigrations(db, options = {}) {
|
|
|
62
62
|
}
|
|
63
63
|
/** Inspect schema version without mutating the database. */
|
|
64
64
|
function inspectSchemaMigrationStatus(db, options = {}) {
|
|
65
|
-
const appVersion = options.targetVersion ??
|
|
65
|
+
const appVersion = options.targetVersion ?? 21;
|
|
66
66
|
const dbVersion = readSchemaVersion(db);
|
|
67
67
|
if (dbVersion > appVersion) return {
|
|
68
68
|
dbVersion,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION =
|
|
1
|
+
{"version":3,"file":"runner.js","names":[],"sources":["../../../../../src/storage/sqlite/migrations/runner.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport { fileURLToPath } from 'node:url';\n\nimport type { DatabaseSync } from 'node:sqlite';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { readSchemaVersion, setSchemaVersion } from '../schema-version.js';\nimport { discoverSqlMigrations } from './discover.js';\nimport {\n DatabaseSchemaMigrationGapError,\n DatabaseSchemaTooNewError,\n} from './errors.js';\nimport type { ApplyMigrationsOptions, SqlMigration } from './types.js';\n\nconst log = createLogger('Sqlite:Migrations');\n\n/** Baseline schema version applied from schema.sql on first open. */\nexport const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;\n\n/** Latest schema version this release supports (increment when adding migrations). */\nexport const XOPC_DB_SCHEMA_VERSION = 21;\n\nconst MODULE_DIR = dirname(fileURLToPath(import.meta.url));\n\nexport function resolveMigrationsDir(override?: string): string {\n if (override) {\n return override;\n }\n // Packaged Electron gateway bundle: `out/server/index.js` + `out/server/migrations/`.\n const siblingDir = join(MODULE_DIR, 'migrations');\n if (existsSync(siblingDir)) {\n return siblingDir;\n }\n // Dev / dist: SQL files live next to `migrations/runner.js`.\n return MODULE_DIR;\n}\n\nfunction migrationByTarget(\n migrations: SqlMigration[],\n targetVersion: number,\n): SqlMigration | undefined {\n return migrations.find((migration) => migration.targetVersion === targetVersion);\n}\n\nfunction applySingleMigration(db: DatabaseSync, migration: SqlMigration): void {\n log.info({ targetVersion: migration.targetVersion, file: migration.filename }, 'Applying SQLite migration');\n db.exec('BEGIN IMMEDIATE');\n try {\n db.exec(migration.sql);\n setSchemaVersion(db, migration.targetVersion);\n db.exec('COMMIT');\n } catch (error) {\n try {\n db.exec('ROLLBACK');\n } catch {\n /* preserve original error */\n }\n const em = error instanceof Error ? error.message : String(error);\n throw new Error(\n `SQLite migration to v${migration.targetVersion} (${migration.filename}) failed: ${em}`,\n { cause: error },\n );\n }\n}\n\n/**\n * Apply sequential SQL migrations from (currentVersion + 1) through targetVersion.\n * Each step runs in its own transaction and updates schema_meta on success.\n */\nexport function applyPendingMigrations(\n db: DatabaseSync,\n options: ApplyMigrationsOptions = {},\n): number {\n const targetVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n let currentVersion = readSchemaVersion(db);\n\n if (currentVersion > targetVersion) {\n throw new DatabaseSchemaTooNewError(currentVersion, targetVersion);\n }\n\n if (currentVersion >= targetVersion) {\n return currentVersion;\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n\n while (currentVersion < targetVersion) {\n const nextVersion = currentVersion + 1;\n const migration = migrationByTarget(migrations, nextVersion);\n if (!migration) {\n throw new DatabaseSchemaMigrationGapError(currentVersion, targetVersion, nextVersion);\n }\n applySingleMigration(db, migration);\n currentVersion = nextVersion;\n }\n\n return currentVersion;\n}\n\nexport type SchemaMigrationStatus = {\n dbVersion: number;\n appVersion: number;\n pendingVersions: number[];\n isTooNew: boolean;\n hasMigrationGap: boolean;\n missingVersion: number | null;\n};\n\n/** Inspect schema version without mutating the database. */\nexport function inspectSchemaMigrationStatus(\n db: DatabaseSync,\n options: Pick<ApplyMigrationsOptions, 'migrationsDir' | 'targetVersion'> = {},\n): SchemaMigrationStatus {\n const appVersion = options.targetVersion ?? XOPC_DB_SCHEMA_VERSION;\n const dbVersion = readSchemaVersion(db);\n const isTooNew = dbVersion > appVersion;\n\n if (isTooNew) {\n return {\n dbVersion,\n appVersion,\n pendingVersions: [],\n isTooNew: true,\n hasMigrationGap: false,\n missingVersion: null,\n };\n }\n\n const migrations = discoverSqlMigrations(resolveMigrationsDir(options.migrationsDir));\n const pendingVersions: number[] = [];\n let hasMigrationGap = false;\n let missingVersion: number | null = null;\n\n for (let version = dbVersion + 1; version <= appVersion; version++) {\n if (migrationByTarget(migrations, version)) {\n pendingVersions.push(version);\n } else {\n hasMigrationGap = true;\n missingVersion = version;\n break;\n }\n }\n\n return {\n dbVersion,\n appVersion,\n pendingVersions,\n isTooNew: false,\n hasMigrationGap,\n missingVersion,\n };\n}\n"],"mappings":";;;;;;;;;aAMwD;AASxD,MAAM,MAAM,aAAa,oBAAoB;;AAG7C,MAAa,kCAAkC;;AAG/C,MAAa,yBAAyB;AAEtC,MAAM,aAAa,QAAQ,cAAc,OAAO,KAAK,IAAI,CAAC;AAE1D,SAAgB,qBAAqB,UAA2B;AAC9D,KAAI,SACF,QAAO;CAGT,MAAM,aAAa,KAAK,YAAY,aAAa;AACjD,KAAI,WAAW,WAAW,CACxB,QAAO;AAGT,QAAO;;AAGT,SAAS,kBACP,YACA,eAC0B;AAC1B,QAAO,WAAW,MAAM,cAAc,UAAU,kBAAkB,cAAc;;AAGlF,SAAS,qBAAqB,IAAkB,WAA+B;AAC7E,KAAI,KAAK;EAAE,eAAe,UAAU;EAAe,MAAM,UAAU;EAAU,EAAE,4BAA4B;AAC3G,IAAG,KAAK,kBAAkB;AAC1B,KAAI;AACF,KAAG,KAAK,UAAU,IAAI;AACtB,mBAAiB,IAAI,UAAU,cAAc;AAC7C,KAAG,KAAK,SAAS;UACV,OAAO;AACd,MAAI;AACF,MAAG,KAAK,WAAW;UACb;EAGR,MAAM,KAAK,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACjE,QAAM,IAAI,MACR,wBAAwB,UAAU,cAAc,IAAI,UAAU,SAAS,YAAY,MACnF,EAAE,OAAO,OAAO,CACjB;;;;;;;AAQL,SAAgB,uBACd,IACA,UAAkC,EAAE,EAC5B;CACR,MAAM,gBAAgB,QAAQ,iBAAA;CAC9B,IAAI,iBAAiB,kBAAkB,GAAG;AAE1C,KAAI,iBAAiB,cACnB,OAAM,IAAI,0BAA0B,gBAAgB,cAAc;AAGpE,KAAI,kBAAkB,cACpB,QAAO;CAGT,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;AAErF,QAAO,iBAAiB,eAAe;EACrC,MAAM,cAAc,iBAAiB;EACrC,MAAM,YAAY,kBAAkB,YAAY,YAAY;AAC5D,MAAI,CAAC,UACH,OAAM,IAAI,gCAAgC,gBAAgB,eAAe,YAAY;AAEvF,uBAAqB,IAAI,UAAU;AACnC,mBAAiB;;AAGnB,QAAO;;;AAaT,SAAgB,6BACd,IACA,UAA2E,EAAE,EACtD;CACvB,MAAM,aAAa,QAAQ,iBAAA;CAC3B,MAAM,YAAY,kBAAkB,GAAG;AAGvC,KAFiB,YAAY,WAG3B,QAAO;EACL;EACA;EACA,iBAAiB,EAAE;EACnB,UAAU;EACV,iBAAiB;EACjB,gBAAgB;EACjB;CAGH,MAAM,aAAa,sBAAsB,qBAAqB,QAAQ,cAAc,CAAC;CACrF,MAAM,kBAA4B,EAAE;CACpC,IAAI,kBAAkB;CACtB,IAAI,iBAAgC;AAEpC,MAAK,IAAI,UAAU,YAAY,GAAG,WAAW,YAAY,UACvD,KAAI,kBAAkB,YAAY,QAAQ,CACxC,iBAAgB,KAAK,QAAQ;MACxB;AACL,oBAAkB;AAClB,mBAAiB;AACjB;;AAIJ,QAAO;EACL;EACA;EACA;EACA,UAAU;EACV;EACA;EACD"}
|
|
@@ -19,7 +19,7 @@ const log = createLogger('Sqlite:Migrations');
|
|
|
19
19
|
export const XOPC_DB_BASELINE_SCHEMA_VERSION = 11;
|
|
20
20
|
|
|
21
21
|
/** Latest schema version this release supports (increment when adding migrations). */
|
|
22
|
-
export const XOPC_DB_SCHEMA_VERSION =
|
|
22
|
+
export const XOPC_DB_SCHEMA_VERSION = 21;
|
|
23
23
|
|
|
24
24
|
const MODULE_DIR = dirname(fileURLToPath(import.meta.url));
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ function ensureXopcDatabaseSchema(db) {
|
|
|
31
31
|
bootstrapFreshDatabase(db);
|
|
32
32
|
currentVersion = 11;
|
|
33
33
|
}
|
|
34
|
-
applyPendingMigrations(db, { targetVersion:
|
|
34
|
+
applyPendingMigrations(db, { targetVersion: 21 });
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
37
|
export { SCHEMA_META_SCHEMA_VERSION_KEY, XOPC_DB_BASELINE_SCHEMA_VERSION, XOPC_DB_SCHEMA_VERSION, ensureSchemaMetaTable, ensureXopcDatabaseSchema, readSchemaVersion, readSchemaVersionForTest, setSchemaVersion };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ExtensionRegistryImpl } from '../../extensions/loader.js';
|
|
2
2
|
import { type Config } from '../../config/schema.js';
|
|
3
3
|
import type { ExportFormat } from '../../session/types.js';
|
|
4
|
-
import type { ChatSendOptions, HistoryMessage, TuiBackend, TuiCompactionResult, TuiEvent, TuiModelChoice, TuiShareRequest, TuiShareResult, TuiSessionStats, TuiSessionItem, TuiTranscriptTreeEntry, TuiAgentInfo, TuiWorkspaceFileSearchEntry, TuiWorkflowRunStartRequest, TuiWorkflowRunStartResult } from '../tui-backend.js';
|
|
4
|
+
import type { ChatSendOptions, HistoryMessage, TuiBackend, TuiCompactionResult, TuiEvent, TuiModelChoice, TuiShareRequest, TuiShareResult, TuiSessionStats, TuiSessionItem, TuiTranscriptTreeEntry, TuiAgentInfo, TuiWorkspaceFileSearchEntry, TuiWorkflowRunStartRequest, TuiWorkflowRunStartResult, TuiStartupProjectResult } from '../tui-backend.js';
|
|
5
5
|
import type { SessionInfo } from '../tui-types.js';
|
|
6
6
|
interface EmbeddedBackendOptions {
|
|
7
7
|
config?: Config;
|
|
@@ -41,9 +41,16 @@ export declare class EmbeddedBackend implements TuiBackend {
|
|
|
41
41
|
private activeConfig;
|
|
42
42
|
getStartupResources(sessionKey: string): Promise<import("../tui-backend.js").TuiStartupResources>;
|
|
43
43
|
startWorkflowRun(opts: TuiWorkflowRunStartRequest): Promise<TuiWorkflowRunStartResult>;
|
|
44
|
+
resolveStartupProject(opts: {
|
|
45
|
+
workspacePath: string;
|
|
46
|
+
sessionKey: string;
|
|
47
|
+
agentId: string;
|
|
48
|
+
autoCreate?: boolean;
|
|
49
|
+
}): Promise<TuiStartupProjectResult>;
|
|
44
50
|
searchWorkspaceFiles(sessionKey: string, query: string, options?: {
|
|
45
51
|
limit?: number;
|
|
46
52
|
}): Promise<TuiWorkspaceFileSearchEntry[]>;
|
|
53
|
+
getReviewContext(sessionKey: string): Promise<import("../../review/review-git.js").ReviewContext>;
|
|
47
54
|
sendChat(opts: ChatSendOptions): Promise<{
|
|
48
55
|
runId: string;
|
|
49
56
|
}>;
|
|
@@ -5,7 +5,9 @@ import { createLogger } from "../../utils/logger/index.js";
|
|
|
5
5
|
import { init_logger } from "../../utils/logger.js";
|
|
6
6
|
import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
7
7
|
import { openXopcDatabase } from "../../storage/sqlite/connection.js";
|
|
8
|
+
import { getSessionMetadata } from "../../storage/sqlite/session-repository.js";
|
|
8
9
|
import "../../storage/sqlite/index.js";
|
|
10
|
+
import { effectiveWorkspacePathForSession } from "../../session/session-workspace.js";
|
|
9
11
|
import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
|
|
10
12
|
import { evictEmbeddedSessionRunner } from "../../agent/embedded/session-runner.js";
|
|
11
13
|
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
|
|
@@ -13,6 +15,7 @@ import { SessionIndex } from "../../session/manager.js";
|
|
|
13
15
|
import { transcriptRowsToClientHistory } from "../../session/client-history.js";
|
|
14
16
|
import "../../session/index.js";
|
|
15
17
|
import { parseModelRef } from "../../agent/models/selection.js";
|
|
18
|
+
import { buildReviewContext, resolveGitRoot } from "../../review/review-git.js";
|
|
16
19
|
import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
|
|
17
20
|
import "../../infra/bus/index.js";
|
|
18
21
|
import { setTuiDefaultAgentConfig } from "../../commands/agents.config.js";
|
|
@@ -21,6 +24,9 @@ import "../../config/index.js";
|
|
|
21
24
|
import { buildWorkflowChildTools } from "../../agent/workflow/workflow-child-tools.js";
|
|
22
25
|
import { WorkflowRunService } from "../../workflows/service/workflow-run-service.js";
|
|
23
26
|
import { WorkflowSessionBridge } from "../../workflows/service/workflow-session-bridge.js";
|
|
27
|
+
import { ProjectService } from "../../projects/project-service.js";
|
|
28
|
+
import { inferSuggestedProjectDefaultAgentId } from "../../projects/project-agent-suggestion.js";
|
|
29
|
+
import "../../projects/index.js";
|
|
24
30
|
import { ChatStreamMapper } from "../../gateway/chat-stream/mapper.js";
|
|
25
31
|
import { fuzzySearchWorkspaceFiles } from "../../gateway/workspace-file-search.js";
|
|
26
32
|
import { sessionMetadataToTuiItem } from "../tui-session-format.js";
|
|
@@ -191,6 +197,47 @@ var EmbeddedBackend = class {
|
|
|
191
197
|
definitionId: opts.definitionId
|
|
192
198
|
};
|
|
193
199
|
}
|
|
200
|
+
async resolveStartupProject(opts) {
|
|
201
|
+
await this.sessionIndexReady;
|
|
202
|
+
if (!this.sessionIndex) return { project: null };
|
|
203
|
+
const projects = new ProjectService();
|
|
204
|
+
const defaultAgentId = inferSuggestedProjectDefaultAgentId({
|
|
205
|
+
config: this.activeConfig(),
|
|
206
|
+
workspaceRoot: opts.workspacePath
|
|
207
|
+
});
|
|
208
|
+
const match = projects.resolveOrCreateForWorkspacePath({
|
|
209
|
+
workspacePath: opts.workspacePath,
|
|
210
|
+
agentId: opts.agentId,
|
|
211
|
+
defaultAgentId,
|
|
212
|
+
autoCreate: opts.autoCreate !== false
|
|
213
|
+
});
|
|
214
|
+
if (!match) return { project: null };
|
|
215
|
+
const projectAgentId = match.project.defaultAgentId?.trim() ? normalizeAgentId(match.project.defaultAgentId) : void 0;
|
|
216
|
+
if (projectAgentId && projectAgentId !== normalizeAgentId(opts.agentId)) return {
|
|
217
|
+
project: match.project,
|
|
218
|
+
created: match.created,
|
|
219
|
+
reason: match.reason
|
|
220
|
+
};
|
|
221
|
+
if (!getSessionMetadata(opts.sessionKey)) await this.sessionIndex.getStore().resolveTranscriptPath(opts.sessionKey, { metadata: {
|
|
222
|
+
sourceChannel: "tui",
|
|
223
|
+
sourceChatId: `default:direct:${opts.sessionKey}`,
|
|
224
|
+
sessionType: "chat",
|
|
225
|
+
projectId: match.project.id,
|
|
226
|
+
routing: {
|
|
227
|
+
agentId: opts.agentId,
|
|
228
|
+
source: "tui",
|
|
229
|
+
accountId: "default",
|
|
230
|
+
peerKind: "direct",
|
|
231
|
+
peerId: opts.sessionKey
|
|
232
|
+
}
|
|
233
|
+
} });
|
|
234
|
+
projects.attachSession(opts.sessionKey, match.project.id);
|
|
235
|
+
return {
|
|
236
|
+
project: match.project,
|
|
237
|
+
created: match.created,
|
|
238
|
+
reason: match.reason
|
|
239
|
+
};
|
|
240
|
+
}
|
|
194
241
|
async searchWorkspaceFiles(sessionKey, query, options) {
|
|
195
242
|
if (!this.agent) return [];
|
|
196
243
|
try {
|
|
@@ -205,6 +252,11 @@ var EmbeddedBackend = class {
|
|
|
205
252
|
return [];
|
|
206
253
|
}
|
|
207
254
|
}
|
|
255
|
+
async getReviewContext(sessionKey) {
|
|
256
|
+
const config = this.activeConfig();
|
|
257
|
+
const metadata = getSessionMetadata(sessionKey);
|
|
258
|
+
return buildReviewContext(await resolveGitRoot(effectiveWorkspacePathForSession(config, sessionKey, null, metadata?.projectId ? new ProjectService().get(metadata.projectId) : null)));
|
|
259
|
+
}
|
|
208
260
|
async sendChat(opts) {
|
|
209
261
|
const agent = await this.ensureAgent();
|
|
210
262
|
const runId = crypto.randomUUID();
|
|
@@ -419,7 +471,14 @@ var EmbeddedBackend = class {
|
|
|
419
471
|
await this.agent.resetSession(sessionKey);
|
|
420
472
|
}
|
|
421
473
|
async patchSession(sessionKey, patch) {
|
|
422
|
-
const
|
|
474
|
+
const agent = await this.ensureAgent();
|
|
475
|
+
const projectId = typeof patch.projectId === "string" ? patch.projectId.trim() : "";
|
|
476
|
+
if (projectId) {
|
|
477
|
+
await this.sessionIndexReady;
|
|
478
|
+
await this.sessionIndex?.getStore().resolveTranscriptPath(sessionKey);
|
|
479
|
+
new ProjectService().attachSession(sessionKey, projectId);
|
|
480
|
+
}
|
|
481
|
+
const result = await agent.sessionConfig.patch(sessionKey, {
|
|
423
482
|
model: typeof patch.model === "string" ? patch.model : void 0,
|
|
424
483
|
thinkingLevel: typeof patch.thinkingLevel === "string" ? patch.thinkingLevel : void 0,
|
|
425
484
|
reasoningLevel: typeof patch.reasoningLevel === "string" ? patch.reasoningLevel : void 0,
|