@xopcai/xopc 0.0.123 → 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-Bicr3ZBg.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-CbEy9sR1.js → app-management-settings-panel-CYCONLIL.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CTHja6n9.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-Bpy64GjU.js → archive-plugin-CXUGRp2i.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-DMkp4xSb.js → attachment-load-Cmiub3Wg.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-KnYaP7NT.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-DA9C5mCw.js → binary-plugins-0Izz-N3p.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-CiSXk_YC.js → block-editor-C1Kv0IkJ.js} +6 -6
- package/dist/gateway/static/root/assets/browser-settings-page-CMeBJzlS.js +8 -0
- package/dist/gateway/static/root/assets/{capability-presets-api-CEJ44WCU.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-CDhrhfhN.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-7jKfbH0J.js → directory-picker-path-field-2rjzUu57.js} +1 -1
- package/dist/gateway/static/root/assets/{dreaming-settings-m1-wmz4o.js → dreaming-settings-DnHO686d.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-C9qQQrBS.js → extension-debug-page-BGLRuWVN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DcBBMEGl.js → extension-page-D978ePp9.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BQ-jMMP9.js → extension-settings-page-B6Ih4Swx.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-Dtpbbe0s.js → fetch-BU5S0Sn7.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-BN6pyX2c.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-BIET2x3y.js → gateway-startup-retry-Cd0Zz30N.js} +1 -1
- package/dist/gateway/static/root/assets/{goal-create-dialog-BuIMDx6C.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-CQm2r1Ty.js → goals-config-api-B95KdGMj.js} +1 -1
- package/dist/gateway/static/root/assets/goals-page-BTJzIl28.js +2 -0
- package/dist/gateway/static/root/assets/{goals-settings-DDrdRMxB.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-CBDr5xjB.css → index-BLaXr6Ev.css} +1 -1
- package/dist/gateway/static/root/assets/index-c_9TVr_C.js +191 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-C2TuQppb.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-DhONQ81k.js → markdown-split-Dv66bO2b.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-DS44pJzr.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-Dej6zHnr.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-Zu1FzH6z.js → save-bar-controls-Cqgb-jqa.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-BW78mKc0.js → schedule-field-DVZcI8Yt.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-Cc-IOydE.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-DIpIYht_.js → settings-collapsible-section-BmXn-8N6.js} +1 -1
- package/dist/gateway/static/root/assets/{settings-form-section-BFxBNxOG.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-CLx4o2PN.js → settings-page-layout-CdWZU1hd.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CWtPM7RK.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-ZswWCrDu.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-3ZNcGRhS.js → system-settings-panel-BxQ384RC.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-bpzZjiVF.js → typed-models-lib-BkcpvFYO.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-BCjZLZ3O.js → use-channel-catalog-Cd0FaEQ4.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-CIXafITi.js → user-profile-settings-panel-DQ7NnRSY.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-DXMtz5j_.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-BYD9BtOq.js +3 -0
- package/dist/gateway/static/root/assets/{workflow-start-dialog-B4sZpTDQ.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 +7 -8
- package/dist/src/agent/tools/factory.js.map +1 -1
- package/dist/src/agent/tools/index.d.ts +3 -2
- 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/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/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/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 +40 -2
- 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/mcp/channel-bridge.js +1 -1
- package/dist/src/projects/index.d.ts +2 -0
- package/dist/src/projects/index.js +3 -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 +1 -0
- package/dist/src/projects/project-service.js +1 -1
- 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 +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 +1 -0
- package/dist/src/tui/backends/embedded-backend.js +20 -0
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/dist/src/tui/backends/gateway-sse-backend.d.ts +2 -0
- package/dist/src/tui/backends/gateway-sse-backend.js +7 -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 +4 -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 +16 -3
- package/dist/src/tui/tui.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-CxDSFHGx.js +0 -2
- package/dist/gateway/static/root/assets/apps-page-CyZdgqvm.js +0 -1
- package/dist/gateway/static/root/assets/automations-page-8VLKM4au.js +0 -1
- package/dist/gateway/static/root/assets/browser-settings-page-5tPg0DcP.js +0 -8
- package/dist/gateway/static/root/assets/capability-presets-settings-panel-BJW3aVST.js +0 -1
- package/dist/gateway/static/root/assets/channels-settings-CorUCTrs.js +0 -1
- package/dist/gateway/static/root/assets/connectors-page-VKstC3sP.js +0 -2
- package/dist/gateway/static/root/assets/gateway-settings-hQtgVmSR.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-FEypCq74.js +0 -1
- package/dist/gateway/static/root/assets/goals-page-C4-2_MLG.js +0 -2
- package/dist/gateway/static/root/assets/heartbeat-settings-BJVok3Sl.js +0 -1
- package/dist/gateway/static/root/assets/index-T0SaNd5a.js +0 -191
- package/dist/gateway/static/root/assets/logs-page-DwkwPgid.js +0 -2
- package/dist/gateway/static/root/assets/messages-avfvyZuQ.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-huNY4A1G.js +0 -2
- package/dist/gateway/static/root/assets/notes-page-DdfGuEZ1.js +0 -1
- package/dist/gateway/static/root/assets/notes-workbench-B0bT9Hqa.js +0 -2
- package/dist/gateway/static/root/assets/project-detail-page-CjxhPorG.js +0 -1
- package/dist/gateway/static/root/assets/projects-page-C4Z5OJMF.js +0 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CVCOPUVd.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-B4qExXHC.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-Ce8yLTBi.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-C4fivemT.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-Kyc4eUf0.js +0 -1
- package/dist/gateway/static/root/assets/shares-settings-Bu76CMiC.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-D-CFg86j.js +0 -1
- package/dist/gateway/static/root/assets/skills-page-CUU4f6Ss.js +0 -2
- package/dist/gateway/static/root/assets/voice-api-key-field-C1MrQD8R.js +0 -1
- package/dist/gateway/static/root/assets/workflow-run-setup-panel-DqizIcY5.js +0 -3
- package/dist/gateway/static/root/assets/workflows-page-B3sVNPFk.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
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type ReviewPriority = 0 | 1 | 2 | 3;
|
|
2
|
+
export interface ReviewFinding {
|
|
3
|
+
title: string;
|
|
4
|
+
body: string;
|
|
5
|
+
priority: ReviewPriority;
|
|
6
|
+
confidenceScore?: number;
|
|
7
|
+
filePath?: string;
|
|
8
|
+
lineStart?: number;
|
|
9
|
+
lineEnd?: number;
|
|
10
|
+
}
|
|
11
|
+
export interface ReviewOutput {
|
|
12
|
+
type: 'review';
|
|
13
|
+
target: string;
|
|
14
|
+
summary: string;
|
|
15
|
+
findings: ReviewFinding[];
|
|
16
|
+
overallCorrectness: 'patch is correct' | 'patch is incorrect' | 'unknown';
|
|
17
|
+
overallExplanation: string;
|
|
18
|
+
overallConfidenceScore?: number;
|
|
19
|
+
generatedAt: number;
|
|
20
|
+
source: 'model' | 'local';
|
|
21
|
+
}
|
|
22
|
+
export declare function formatReviewMarkdown(review: ReviewOutput): string;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//#region src/review/review-types.ts
|
|
2
|
+
function formatLocation(finding) {
|
|
3
|
+
if (!finding.filePath) return "";
|
|
4
|
+
if (!finding.lineStart) return finding.filePath;
|
|
5
|
+
const end = finding.lineEnd && finding.lineEnd !== finding.lineStart ? `-${finding.lineEnd}` : "";
|
|
6
|
+
return `${finding.filePath}:${finding.lineStart}${end}`;
|
|
7
|
+
}
|
|
8
|
+
function formatReviewMarkdown(review) {
|
|
9
|
+
const lines = ["Code review", ""];
|
|
10
|
+
if (review.findings.length === 0) lines.push("No findings.");
|
|
11
|
+
else {
|
|
12
|
+
lines.push("Findings:");
|
|
13
|
+
for (const finding of review.findings) {
|
|
14
|
+
const location = formatLocation(finding);
|
|
15
|
+
const suffix = location ? ` - ${location}` : "";
|
|
16
|
+
lines.push(`- [P${finding.priority}] ${finding.title}${suffix}`);
|
|
17
|
+
if (finding.body.trim()) lines.push(` ${finding.body.trim().replace(/\n+/g, "\n ")}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
lines.push("");
|
|
21
|
+
lines.push(`Overall correctness: ${review.overallCorrectness}`);
|
|
22
|
+
if (review.overallExplanation.trim()) lines.push(review.overallExplanation.trim());
|
|
23
|
+
if (typeof review.overallConfidenceScore === "number") lines.push(`Confidence: ${review.overallConfidenceScore.toFixed(2)}`);
|
|
24
|
+
return lines.join("\n");
|
|
25
|
+
}
|
|
26
|
+
//#endregion
|
|
27
|
+
export { formatReviewMarkdown };
|
|
28
|
+
|
|
29
|
+
//# sourceMappingURL=review-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-types.js","names":[],"sources":["../../../src/review/review-types.ts"],"sourcesContent":["export type ReviewPriority = 0 | 1 | 2 | 3;\n\nexport interface ReviewFinding {\n title: string;\n body: string;\n priority: ReviewPriority;\n confidenceScore?: number;\n filePath?: string;\n lineStart?: number;\n lineEnd?: number;\n}\n\nexport interface ReviewOutput {\n type: 'review';\n target: string;\n summary: string;\n findings: ReviewFinding[];\n overallCorrectness: 'patch is correct' | 'patch is incorrect' | 'unknown';\n overallExplanation: string;\n overallConfidenceScore?: number;\n generatedAt: number;\n source: 'model' | 'local';\n}\n\nfunction formatLocation(finding: ReviewFinding): string {\n if (!finding.filePath) return '';\n if (!finding.lineStart) return finding.filePath;\n const end = finding.lineEnd && finding.lineEnd !== finding.lineStart ? `-${finding.lineEnd}` : '';\n return `${finding.filePath}:${finding.lineStart}${end}`;\n}\n\nexport function formatReviewMarkdown(review: ReviewOutput): string {\n const lines: string[] = ['Code review', ''];\n if (review.findings.length === 0) {\n lines.push('No findings.');\n } else {\n lines.push('Findings:');\n for (const finding of review.findings) {\n const location = formatLocation(finding);\n const suffix = location ? ` - ${location}` : '';\n lines.push(`- [P${finding.priority}] ${finding.title}${suffix}`);\n if (finding.body.trim()) {\n lines.push(` ${finding.body.trim().replace(/\\n+/g, '\\n ')}`);\n }\n }\n }\n lines.push('');\n lines.push(`Overall correctness: ${review.overallCorrectness}`);\n if (review.overallExplanation.trim()) {\n lines.push(review.overallExplanation.trim());\n }\n if (typeof review.overallConfidenceScore === 'number') {\n lines.push(`Confidence: ${review.overallConfidenceScore.toFixed(2)}`);\n }\n return lines.join('\\n');\n}\n"],"mappings":";AAwBA,SAAS,eAAe,SAAgC;AACtD,KAAI,CAAC,QAAQ,SAAU,QAAO;AAC9B,KAAI,CAAC,QAAQ,UAAW,QAAO,QAAQ;CACvC,MAAM,MAAM,QAAQ,WAAW,QAAQ,YAAY,QAAQ,YAAY,IAAI,QAAQ,YAAY;AAC/F,QAAO,GAAG,QAAQ,SAAS,GAAG,QAAQ,YAAY;;AAGpD,SAAgB,qBAAqB,QAA8B;CACjE,MAAM,QAAkB,CAAC,eAAe,GAAG;AAC3C,KAAI,OAAO,SAAS,WAAW,EAC7B,OAAM,KAAK,eAAe;MACrB;AACL,QAAM,KAAK,YAAY;AACvB,OAAK,MAAM,WAAW,OAAO,UAAU;GACrC,MAAM,WAAW,eAAe,QAAQ;GACxC,MAAM,SAAS,WAAW,MAAM,aAAa;AAC7C,SAAM,KAAK,OAAO,QAAQ,SAAS,IAAI,QAAQ,QAAQ,SAAS;AAChE,OAAI,QAAQ,KAAK,MAAM,CACrB,OAAM,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,QAAQ,QAAQ,OAAO,GAAG;;;AAIpE,OAAM,KAAK,GAAG;AACd,OAAM,KAAK,wBAAwB,OAAO,qBAAqB;AAC/D,KAAI,OAAO,mBAAmB,MAAM,CAClC,OAAM,KAAK,OAAO,mBAAmB,MAAM,CAAC;AAE9C,KAAI,OAAO,OAAO,2BAA2B,SAC3C,OAAM,KAAK,eAAe,OAAO,uBAAuB,QAAQ,EAAE,GAAG;AAEvE,QAAO,MAAM,KAAK,KAAK"}
|
|
@@ -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 };
|
|
@@ -50,6 +50,7 @@ export declare class EmbeddedBackend implements TuiBackend {
|
|
|
50
50
|
searchWorkspaceFiles(sessionKey: string, query: string, options?: {
|
|
51
51
|
limit?: number;
|
|
52
52
|
}): Promise<TuiWorkspaceFileSearchEntry[]>;
|
|
53
|
+
getReviewContext(sessionKey: string): Promise<import("../../review/review-git.js").ReviewContext>;
|
|
53
54
|
sendChat(opts: ChatSendOptions): Promise<{
|
|
54
55
|
runId: string;
|
|
55
56
|
}>;
|
|
@@ -7,6 +7,7 @@ import { loadConfig, saveConfig } from "../../config/loader.js";
|
|
|
7
7
|
import { openXopcDatabase } from "../../storage/sqlite/connection.js";
|
|
8
8
|
import { getSessionMetadata } from "../../storage/sqlite/session-repository.js";
|
|
9
9
|
import "../../storage/sqlite/index.js";
|
|
10
|
+
import { effectiveWorkspacePathForSession } from "../../session/session-workspace.js";
|
|
10
11
|
import { createCreateShareTool, isShareToolAvailable } from "../../agent/tools/create-share-tool.js";
|
|
11
12
|
import { evictEmbeddedSessionRunner } from "../../agent/embedded/session-runner.js";
|
|
12
13
|
import { prependEnvelopeTimestamp } from "../../channels/envelope-timestamp.js";
|
|
@@ -14,6 +15,7 @@ import { SessionIndex } from "../../session/manager.js";
|
|
|
14
15
|
import { transcriptRowsToClientHistory } from "../../session/client-history.js";
|
|
15
16
|
import "../../session/index.js";
|
|
16
17
|
import { parseModelRef } from "../../agent/models/selection.js";
|
|
18
|
+
import { buildReviewContext, resolveGitRoot } from "../../review/review-git.js";
|
|
17
19
|
import { MessageBus, MessageBusShutdownError } from "../../infra/bus/queue.js";
|
|
18
20
|
import "../../infra/bus/index.js";
|
|
19
21
|
import { setTuiDefaultAgentConfig } from "../../commands/agents.config.js";
|
|
@@ -23,6 +25,8 @@ import { buildWorkflowChildTools } from "../../agent/workflow/workflow-child-too
|
|
|
23
25
|
import { WorkflowRunService } from "../../workflows/service/workflow-run-service.js";
|
|
24
26
|
import { WorkflowSessionBridge } from "../../workflows/service/workflow-session-bridge.js";
|
|
25
27
|
import { ProjectService } from "../../projects/project-service.js";
|
|
28
|
+
import { inferSuggestedProjectDefaultAgentId } from "../../projects/project-agent-suggestion.js";
|
|
29
|
+
import "../../projects/index.js";
|
|
26
30
|
import { ChatStreamMapper } from "../../gateway/chat-stream/mapper.js";
|
|
27
31
|
import { fuzzySearchWorkspaceFiles } from "../../gateway/workspace-file-search.js";
|
|
28
32
|
import { sessionMetadataToTuiItem } from "../tui-session-format.js";
|
|
@@ -197,12 +201,23 @@ var EmbeddedBackend = class {
|
|
|
197
201
|
await this.sessionIndexReady;
|
|
198
202
|
if (!this.sessionIndex) return { project: null };
|
|
199
203
|
const projects = new ProjectService();
|
|
204
|
+
const defaultAgentId = inferSuggestedProjectDefaultAgentId({
|
|
205
|
+
config: this.activeConfig(),
|
|
206
|
+
workspaceRoot: opts.workspacePath
|
|
207
|
+
});
|
|
200
208
|
const match = projects.resolveOrCreateForWorkspacePath({
|
|
201
209
|
workspacePath: opts.workspacePath,
|
|
202
210
|
agentId: opts.agentId,
|
|
211
|
+
defaultAgentId,
|
|
203
212
|
autoCreate: opts.autoCreate !== false
|
|
204
213
|
});
|
|
205
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
|
+
};
|
|
206
221
|
if (!getSessionMetadata(opts.sessionKey)) await this.sessionIndex.getStore().resolveTranscriptPath(opts.sessionKey, { metadata: {
|
|
207
222
|
sourceChannel: "tui",
|
|
208
223
|
sourceChatId: `default:direct:${opts.sessionKey}`,
|
|
@@ -237,6 +252,11 @@ var EmbeddedBackend = class {
|
|
|
237
252
|
return [];
|
|
238
253
|
}
|
|
239
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
|
+
}
|
|
240
260
|
async sendChat(opts) {
|
|
241
261
|
const agent = await this.ensureAgent();
|
|
242
262
|
const runId = crypto.randomUUID();
|