@rudderhq/server 0.3.6-canary.9 → 0.4.1
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/dist/bootstrap/register-api-routes.d.ts.map +1 -1
- package/dist/bootstrap/register-api-routes.js +2 -0
- package/dist/bootstrap/register-api-routes.js.map +1 -1
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +37 -2
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
- package/dist/config.d.ts +1 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +15 -1
- package/dist/config.js.map +1 -1
- package/dist/home-paths.d.ts +11 -0
- package/dist/home-paths.d.ts.map +1 -1
- package/dist/home-paths.js +151 -8
- package/dist/home-paths.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +103 -26
- package/dist/index.js.map +1 -1
- package/dist/routes/activity.d.ts +1 -1
- package/dist/routes/activity.d.ts.map +1 -1
- package/dist/routes/activity.js +21 -5
- package/dist/routes/activity.js.map +1 -1
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +254 -4
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/agents.management-routes.d.ts.map +1 -1
- package/dist/routes/agents.management-routes.js +132 -40
- package/dist/routes/agents.management-routes.js.map +1 -1
- package/dist/routes/automations.d.ts +8 -0
- package/dist/routes/automations.d.ts.map +1 -1
- package/dist/routes/automations.js.map +1 -1
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +228 -115
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/chats.stream-routes.d.ts.map +1 -1
- package/dist/routes/chats.stream-routes.js +94 -5
- package/dist/routes/chats.stream-routes.js.map +1 -1
- package/dist/routes/instance-settings.d.ts.map +1 -1
- package/dist/routes/instance-settings.js +60 -9
- package/dist/routes/instance-settings.js.map +1 -1
- package/dist/routes/issues.comments-attachments.d.ts.map +1 -1
- package/dist/routes/issues.comments-attachments.js +72 -83
- package/dist/routes/issues.comments-attachments.js.map +1 -1
- package/dist/routes/issues.d.ts +8 -0
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +27 -4
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/issues.mutations.d.ts.map +1 -1
- package/dist/routes/issues.mutations.js +2 -26
- package/dist/routes/issues.mutations.js.map +1 -1
- package/dist/routes/messenger.d.ts.map +1 -1
- package/dist/routes/messenger.js +39 -1
- package/dist/routes/messenger.js.map +1 -1
- package/dist/routes/orgs.d.ts.map +1 -1
- package/dist/routes/orgs.js +16 -0
- package/dist/routes/orgs.js.map +1 -1
- package/dist/routes/plugin-ui-static.d.ts +4 -4
- package/dist/routes/plugin-ui-static.js +5 -5
- package/dist/routes/plugin-ui-static.js.map +1 -1
- package/dist/routes/plugins.d.ts +4 -4
- package/dist/routes/plugins.js +10 -10
- package/dist/routes/plugins.operations-routes.d.ts +1 -1
- package/dist/routes/plugins.operations-routes.js +2 -2
- package/dist/routes/plugins.operations-routes.js.map +1 -1
- package/dist/routes/run-intelligence.js +5 -5
- package/dist/routes/run-intelligence.js.map +1 -1
- package/dist/routes/sidebar-badges.d.ts.map +1 -1
- package/dist/routes/sidebar-badges.js +6 -10
- package/dist/routes/sidebar-badges.js.map +1 -1
- package/dist/routes/website-metadata.d.ts +8 -0
- package/dist/routes/website-metadata.d.ts.map +1 -0
- package/dist/routes/website-metadata.js +67 -0
- package/dist/routes/website-metadata.js.map +1 -0
- package/dist/services/activity.d.ts.map +1 -1
- package/dist/services/activity.js +1 -1
- package/dist/services/activity.js.map +1 -1
- package/dist/services/agent-instructions.d.ts +1 -0
- package/dist/services/agent-instructions.d.ts.map +1 -1
- package/dist/services/agent-instructions.js +57 -6
- package/dist/services/agent-instructions.js.map +1 -1
- package/dist/services/agent-run-context.d.ts +3 -1
- package/dist/services/agent-run-context.d.ts.map +1 -1
- package/dist/services/agent-run-context.js +7 -0
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/agent-startup-context.d.ts +2 -1
- package/dist/services/agent-startup-context.d.ts.map +1 -1
- package/dist/services/agent-startup-context.js +6 -26
- package/dist/services/agent-startup-context.js.map +1 -1
- package/dist/services/agents.d.ts +9 -1
- package/dist/services/agents.d.ts.map +1 -1
- package/dist/services/agents.js.map +1 -1
- package/dist/services/approvals.d.ts +8 -0
- package/dist/services/approvals.d.ts.map +1 -1
- package/dist/services/approvals.js.map +1 -1
- package/dist/services/automation-chat-output.d.ts +1 -1
- package/dist/services/automations.d.ts +4 -4
- package/dist/services/automations.d.ts.map +1 -1
- package/dist/services/automations.js +12 -0
- package/dist/services/automations.js.map +1 -1
- package/dist/services/budgets.d.ts +8 -0
- package/dist/services/budgets.d.ts.map +1 -1
- package/dist/services/budgets.js.map +1 -1
- package/dist/services/calendar.d.ts +2 -2
- package/dist/services/calendar.js +3 -3
- package/dist/services/calendar.js.map +1 -1
- package/dist/services/chat-agent-runs.d.ts +2 -0
- package/dist/services/chat-agent-runs.d.ts.map +1 -1
- package/dist/services/chat-agent-runs.js +6 -0
- package/dist/services/chat-agent-runs.js.map +1 -1
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.d.ts +6 -1
- package/dist/services/chat-assistant.helpers.d.ts.map +1 -1
- package/dist/services/chat-assistant.helpers.js +25 -11
- package/dist/services/chat-assistant.helpers.js.map +1 -1
- package/dist/services/chat-assistant.js +230 -95
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chat-generation-locks.d.ts +6 -1
- package/dist/services/chat-generation-locks.d.ts.map +1 -1
- package/dist/services/chat-generation-locks.js +20 -2
- package/dist/services/chat-generation-locks.js.map +1 -1
- package/dist/services/chat-title-generation.d.ts +36 -0
- package/dist/services/chat-title-generation.d.ts.map +1 -0
- package/dist/services/chat-title-generation.js +107 -0
- package/dist/services/chat-title-generation.js.map +1 -0
- package/dist/services/chats.d.ts +471 -25
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.helpers.d.ts +2 -2
- package/dist/services/chats.js +681 -35
- package/dist/services/chats.js.map +1 -1
- package/dist/services/costs.d.ts +1 -1
- package/dist/services/finance.d.ts +2 -2
- package/dist/services/finance.js +1 -1
- package/dist/services/finance.js.map +1 -1
- package/dist/services/goals.d.ts +8 -0
- package/dist/services/goals.d.ts.map +1 -1
- package/dist/services/goals.js.map +1 -1
- package/dist/services/heartbeat-run-reference.d.ts +1 -0
- package/dist/services/heartbeat-run-reference.d.ts.map +1 -1
- package/dist/services/heartbeat-run-reference.js +3 -2
- package/dist/services/heartbeat-run-reference.js.map +1 -1
- package/dist/services/heartbeat-run-summary.js +1 -1
- package/dist/services/heartbeat-run-summary.js.map +1 -1
- package/dist/services/integrations/agent-integrations.d.ts +19 -0
- package/dist/services/integrations/agent-integrations.d.ts.map +1 -1
- package/dist/services/integrations/agent-integrations.js +30 -0
- package/dist/services/integrations/agent-integrations.js.map +1 -1
- package/dist/services/integrations/feishu/app-registration.d.ts +74 -0
- package/dist/services/integrations/feishu/app-registration.d.ts.map +1 -0
- package/dist/services/integrations/feishu/app-registration.js +236 -0
- package/dist/services/integrations/feishu/app-registration.js.map +1 -0
- package/dist/services/integrations/feishu/inbound-dispatcher-db.d.ts +5 -0
- package/dist/services/integrations/feishu/inbound-dispatcher-db.d.ts.map +1 -1
- package/dist/services/integrations/feishu/inbound-dispatcher-db.js +178 -7
- package/dist/services/integrations/feishu/inbound-dispatcher-db.js.map +1 -1
- package/dist/services/integrations/feishu/inbound-dispatcher.d.ts +23 -0
- package/dist/services/integrations/feishu/inbound-dispatcher.d.ts.map +1 -1
- package/dist/services/integrations/feishu/inbound-dispatcher.js +25 -0
- package/dist/services/integrations/feishu/inbound-dispatcher.js.map +1 -1
- package/dist/services/integrations/feishu/runtime-registry.d.ts +20 -0
- package/dist/services/integrations/feishu/runtime-registry.d.ts.map +1 -0
- package/dist/services/integrations/feishu/runtime-registry.js +41 -0
- package/dist/services/integrations/feishu/runtime-registry.js.map +1 -0
- package/dist/services/integrations/feishu/runtime.d.ts +39 -23
- package/dist/services/integrations/feishu/runtime.d.ts.map +1 -1
- package/dist/services/integrations/feishu/runtime.js +333 -95
- package/dist/services/integrations/feishu/runtime.js.map +1 -1
- package/dist/services/integrations/feishu/user-bindings.d.ts +22 -0
- package/dist/services/integrations/feishu/user-bindings.d.ts.map +1 -0
- package/dist/services/integrations/feishu/user-bindings.js +57 -0
- package/dist/services/integrations/feishu/user-bindings.js.map +1 -0
- package/dist/services/issue-approvals.d.ts +2 -2
- package/dist/services/issue-review-wakeup.js +1 -1
- package/dist/services/issue-review-wakeup.js.map +1 -1
- package/dist/services/issues.comments-attachments.d.ts +3 -3
- package/dist/services/issues.d.ts +14 -6
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.helpers.d.ts +12 -1
- package/dist/services/issues.helpers.d.ts.map +1 -1
- package/dist/services/issues.helpers.js +8 -0
- package/dist/services/issues.helpers.js.map +1 -1
- package/dist/services/issues.js +28 -2
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.catalog.d.ts +1 -1
- package/dist/services/library-entries.d.ts +8 -0
- package/dist/services/library-entries.d.ts.map +1 -1
- package/dist/services/library-entries.js +8 -0
- package/dist/services/library-entries.js.map +1 -1
- package/dist/services/managed-workspace-preflight.js +1 -1
- package/dist/services/managed-workspace-preflight.js.map +1 -1
- package/dist/services/messenger.d.ts +23 -4
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +187 -25
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/plugin-capability-validator.d.ts +1 -1
- package/dist/services/plugin-capability-validator.js +2 -2
- package/dist/services/plugin-dev-watcher.js +1 -1
- package/dist/services/plugin-event-bus.d.ts +3 -3
- package/dist/services/plugin-event-bus.js +3 -3
- package/dist/services/plugin-job-coordinator.d.ts +1 -1
- package/dist/services/plugin-job-coordinator.js +1 -1
- package/dist/services/plugin-job-scheduler.d.ts +1 -1
- package/dist/services/plugin-job-scheduler.js +1 -1
- package/dist/services/plugin-job-store.d.ts +2 -2
- package/dist/services/plugin-job-store.js +2 -2
- package/dist/services/plugin-lifecycle.d.ts +4 -4
- package/dist/services/plugin-lifecycle.js +3 -3
- package/dist/services/plugin-loader.core.d.ts +4 -4
- package/dist/services/plugin-loader.core.js +2 -2
- package/dist/services/plugin-loader.core.js.map +1 -1
- package/dist/services/plugin-loader.d.ts +3 -3
- package/dist/services/plugin-loader.helpers.d.ts +26 -25
- package/dist/services/plugin-loader.helpers.d.ts.map +1 -1
- package/dist/services/plugin-loader.helpers.js +10 -9
- package/dist/services/plugin-loader.helpers.js.map +1 -1
- package/dist/services/plugin-loader.js +3 -3
- package/dist/services/plugin-loader.worker-paths.d.ts +3 -3
- package/dist/services/plugin-manifest-validator.d.ts +2 -2
- package/dist/services/plugin-registry.d.ts +9 -9
- package/dist/services/plugin-registry.js +1 -1
- package/dist/services/plugin-secrets-handler.d.ts +2 -2
- package/dist/services/plugin-secrets-handler.js +2 -2
- package/dist/services/plugin-state-store.d.ts +3 -3
- package/dist/services/plugin-state-store.js +3 -3
- package/dist/services/plugin-stream-bus.d.ts +1 -1
- package/dist/services/plugin-stream-bus.js +1 -1
- package/dist/services/plugin-tool-dispatcher.d.ts +2 -2
- package/dist/services/plugin-tool-dispatcher.js +2 -2
- package/dist/services/plugin-tool-registry.d.ts +2 -2
- package/dist/services/plugin-tool-registry.js +2 -2
- package/dist/services/plugin-worker-manager.d.ts +4 -4
- package/dist/services/plugin-worker-manager.d.ts.map +1 -1
- package/dist/services/plugin-worker-manager.js +8 -7
- package/dist/services/plugin-worker-manager.js.map +1 -1
- package/dist/services/run-intelligence.js +3 -3
- package/dist/services/run-intelligence.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.core.d.ts +9 -1
- package/dist/services/runtime-kernel/heartbeat.core.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.core.js +16 -5
- package/dist/services/runtime-kernel/heartbeat.core.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.d.ts +3 -3
- package/dist/services/runtime-kernel/heartbeat.execute.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.execute.js +17 -3
- package/dist/services/runtime-kernel/heartbeat.execute.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.misc.js +1 -1
- package/dist/services/runtime-kernel/heartbeat.recovery.d.ts +1 -1
- package/dist/services/runtime-kernel/heartbeat.recovery.js +7 -7
- package/dist/services/runtime-kernel/heartbeat.recovery.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.release.js +4 -4
- package/dist/services/runtime-kernel/heartbeat.release.js.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.wakeup.js +1 -1
- package/dist/services/sidebar-badges.d.ts +3 -0
- package/dist/services/sidebar-badges.d.ts.map +1 -1
- package/dist/services/sidebar-badges.js +29 -1
- package/dist/services/sidebar-badges.js.map +1 -1
- package/dist/services/title-generation.d.ts +38 -0
- package/dist/services/title-generation.d.ts.map +1 -0
- package/dist/services/title-generation.js +119 -0
- package/dist/services/title-generation.js.map +1 -0
- package/dist/services/website-metadata.d.ts +17 -0
- package/dist/services/website-metadata.d.ts.map +1 -0
- package/dist/services/website-metadata.js +293 -0
- package/dist/services/website-metadata.js.map +1 -0
- package/dist/services/workspace-backups.d.ts +22 -0
- package/dist/services/workspace-backups.d.ts.map +1 -1
- package/dist/services/workspace-backups.js +161 -5
- package/dist/services/workspace-backups.js.map +1 -1
- package/dist/services/workspace-runtime.services.d.ts +1 -1
- package/package.json +15 -14
- package/resources/bundled-skills/rudder/SKILL.md +7 -6
- package/resources/bundled-skills/rudder/references/cli-reference.md +3 -3
- package/resources/bundled-skills/rudder-create-plugin/SKILL.md +2 -2
- package/skills/rudder/SKILL.md +7 -6
- package/skills/rudder/references/cli-reference.md +3 -3
- package/skills/rudder-create-plugin/SKILL.md +2 -2
- package/ui-dist/assets/{_basePickBy-CM0XatHw.js → _basePickBy-RX5N2cj2.js} +1 -1
- package/ui-dist/assets/{_baseUniq-CdBBmdoP.js → _baseUniq-Br4fg1iw.js} +1 -1
- package/ui-dist/assets/{arc-DSTtxyJ9.js → arc-B8svvCeo.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BkHMYb5I.js → architectureDiagram-2XIMDMQ5-C5dYOfG1.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-EYczZxwW.js → blockDiagram-WCTKOSBZ-B6XhVXfw.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-Xo9qijMi.js → c4Diagram-IC4MRINW-D31WV7FX.js} +1 -1
- package/ui-dist/assets/channel-BD332hbj.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-CKzta1FF.js → chunk-4BX2VUAB-CxoBpj5-.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-BxxUB0ww.js → chunk-55IACEB6-kF3-cxbP.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-0F4oxVtY.js → chunk-FMBD7UC4-CmnJkmf-.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-CVMPMlVD.js → chunk-JSJVCQXG-U_rZ0R1l.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-lPrAWT94.js → chunk-KX2RTZJC-DB5h51jY.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-Bufcm7Iv.js → chunk-NQ4KR5QH-D00Zb66Y.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-CucmdWnl.js → chunk-QZHKN3VN-CZOQzYse.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-Dz0oYQrJ.js → chunk-WL4C6EOR-CiFO_DRi.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-BJA0edKB.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-BJA0edKB.js +1 -0
- package/ui-dist/assets/clone-BXc7Yvje.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-DzyX-Er8.js → cose-bilkent-S5V4N54A-BvkdnfgN.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-BbZzCKuN.js → dagre-KLK3FWXG-DcTva6B8.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-B5Rb7Bm4.js → diagram-E7M64L7V-sXDqPfE1.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-Cpu1Xc8G.js → diagram-IFDJBPK2-BVBaBBbz.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-kLfHqRlk.js → diagram-P4PSJMXO-cjQe9ndf.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-rCnUyQUb.js → erDiagram-INFDFZHY-CFGKze0m.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-BhOz8cvV.js → flowDiagram-PKNHOUZH-D7oV-h10.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-Csg9g3hz.js → ganttDiagram-A5KZAMGK-Rdv3FkwM.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-DLPnSlRs.js → gitGraphDiagram-K3NZZRJ6-BejgDMgO.js} +1 -1
- package/ui-dist/assets/{graph-BrxYh68N.js → graph-FYmROErt.js} +1 -1
- package/ui-dist/assets/{index-BeZv_Z0b.js → index-4AkwW8qm.js} +1 -1
- package/ui-dist/assets/{index-Dj72ldIc.js → index-BArpEgpP.js} +1 -1
- package/ui-dist/assets/index-BPlr2F3e.css +1 -0
- package/ui-dist/assets/{index-Br07RRw1.js → index-BXKKdE9_.js} +1 -1
- package/ui-dist/assets/{index-mOyLwPeE.js → index-BeEzPu0t.js} +1 -1
- package/ui-dist/assets/{index-B6_gFYNj.js → index-BipRYXd-.js} +1 -1
- package/ui-dist/assets/{index-Bw8MgLaM.js → index-CEvBo8i0.js} +450 -445
- package/ui-dist/assets/{index-BkzdTp2b.js → index-CWj4VVKu.js} +1 -1
- package/ui-dist/assets/{index-D0rP99dj.js → index-CaLoiEJB.js} +1 -1
- package/ui-dist/assets/{index-DMkClCka.js → index-DDHYeuwy.js} +1 -1
- package/ui-dist/assets/{index-DiMeST8g.js → index-DSnSYKAK.js} +1 -1
- package/ui-dist/assets/{index-DpdsNIGb.js → index-DUeBWXLE.js} +1 -1
- package/ui-dist/assets/{index-G_y-oBne.js → index-DWxCltvd.js} +1 -1
- package/ui-dist/assets/{index-COBwmF-C.js → index-DYYXNc6j.js} +1 -1
- package/ui-dist/assets/{index-D_06pAuN.js → index-DaNviTjf.js} +1 -1
- package/ui-dist/assets/{index-BQW242nS.js → index-c9oJP_Zz.js} +1 -1
- package/ui-dist/assets/{index-Cb2qts0o.js → index-hUcZY0te.js} +1 -1
- package/ui-dist/assets/{index-D4FFno9t.js → index-ul4vjkem.js} +1 -1
- package/ui-dist/assets/{index-CsraICzz.js → index-uraSHmk-.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-CRBKgiEf.js → infoDiagram-LFFYTUFH-D6ROD6kG.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-DI3jquVE.js → ishikawaDiagram-PHBUUO56-DcVc0cdJ.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-DDFZ2Q6D.js → journeyDiagram-4ABVD52K-BRufUm59.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-yL5u3cXV.js → kanban-definition-K7BYSVSG-DEQ5w7B0.js} +1 -1
- package/ui-dist/assets/{layout-DDZaRkp7.js → layout-F5Axhkca.js} +1 -1
- package/ui-dist/assets/{linear-CWngWuQa.js → linear-BllnS5sO.js} +1 -1
- package/ui-dist/assets/{mermaid.core-CQuYoq9K.js → mermaid.core-CK8iLvY2.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-BwSsHtTO.js → mindmap-definition-YRQLILUH-CXI6vt68.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-BasUkvwH.js → pieDiagram-SKSYHLDU-DDUy_f_g.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-D0D2xp36.js → quadrantDiagram-337W2JSQ-Cpns5Yb1.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-CztN9kDh.js → requirementDiagram-Z7DCOOCP-6p6V6XH6.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-2xDqN9ox.js → sankeyDiagram-WA2Y5GQK-C52tgQmQ.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-CO3OUPIJ.js → sequenceDiagram-2WXFIKYE-CxlW_veo.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-DuQ1QZcT.js → stateDiagram-RAJIS63D-NFOjAzLj.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DblG-d9U.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-CnOxbQeb.js → timeline-definition-YZTLITO2-DvENY4C6.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-DD77IFOl.js → treemap-KZPCXAKY-BJfIPF5s.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-DOXTGJn1.js → vennDiagram-LZ73GAT5-DmGW-P4z.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-DWkrcdri.js → xychartDiagram-JWTSCODW-bbLe3hqw.js} +1 -1
- package/ui-dist/brands/feishu-logo.svg +5 -0
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/channel-BRnz7pSY.js +0 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-Q3YuI9dv.js +0 -1
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Q3YuI9dv.js +0 -1
- package/ui-dist/assets/clone-C1exIyAN.js +0 -1
- package/ui-dist/assets/index-Dz8xxyqB.css +0 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DQbZqbl9.js +0 -1
package/dist/services/chats.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { approvals, assets, chatAttachments, chatContextLinks, chatConversations, chatConversationUserStates, chatMessages } from "@rudderhq/db";
|
|
2
|
-
import { sanitizeChatStructuredPayload } from "@rudderhq/shared";
|
|
3
|
-
import { and, desc, eq, gt, gte, inArray, isNull, sql } from "drizzle-orm";
|
|
1
|
+
import { agentIntegrationChatBindings, agentIntegrations, approvals, assets, chatAttachments, chatContextLinks, chatConversations, chatConversationUserStates, chatGenerations, chatMessages, chatQueuedMessages, messengerCustomGroupEntries, messengerCustomGroups } from "@rudderhq/db";
|
|
2
|
+
import { MESSENGER_FORK_GROUP_DEFAULT_ICON, sanitizeChatStructuredPayload } from "@rudderhq/shared";
|
|
3
|
+
import { and, asc, desc, eq, gt, gte, inArray, isNull, lt, or, sql } from "drizzle-orm";
|
|
4
4
|
import { randomUUID } from "node:crypto";
|
|
5
|
-
import { notFound, unprocessable } from "../errors.js";
|
|
5
|
+
import { conflict, notFound, unprocessable } from "../errors.js";
|
|
6
6
|
import { logActivity } from "./activity-log.js";
|
|
7
7
|
import { agentService } from "./agents.js";
|
|
8
8
|
import { approvalService } from "./approvals.js";
|
|
@@ -10,8 +10,18 @@ import { issueApprovalService } from "./issue-approvals.js";
|
|
|
10
10
|
import { issueService } from "./issues.js";
|
|
11
11
|
import { normalizeLocalLibraryPathMarkdown } from "./library-path-markdown.js";
|
|
12
12
|
import { organizationService } from "./orgs.js";
|
|
13
|
+
function conversationMutability(row, sourceMetadata, sourceMetadataByConversationId) {
|
|
14
|
+
if (sourceMetadata)
|
|
15
|
+
return "external_bound_chat";
|
|
16
|
+
if ((row.forkedFromConversationId && sourceMetadataByConversationId.has(row.forkedFromConversationId)) ||
|
|
17
|
+
(row.forkRootConversationId && sourceMetadataByConversationId.has(row.forkRootConversationId))) {
|
|
18
|
+
return "native_fork_from_external";
|
|
19
|
+
}
|
|
20
|
+
return "native_chat";
|
|
21
|
+
}
|
|
13
22
|
import { buildSearchSnippet, CHAT_TRANSCRIPT_KEY, chatTranscriptFromPayload, chatTranscriptSummaryFromEntries, contentPath, escapeLikePattern, incomingMessagePreviewSql, issueProposalFromPayload, isVisibleIncomingChatMessage, listContextLinksForConversationIds, listPrimaryIssues, operationProposalDecisionStatusFromPayload, operationProposalFromPayload, resolveContextEntities, safeTrim, stripChatMetadataFromPayload, textContains, truncatePreview, visibleIncomingMessageSql, withOperationProposalDecisionState, withPersistedTranscript, } from "./chats.helpers.js";
|
|
14
23
|
export function chatService(db) {
|
|
24
|
+
const QUEUED_MESSAGE_CLAIM_LEASE_MS = 2 * 60 * 1000;
|
|
15
25
|
const issuesSvc = issueService(db);
|
|
16
26
|
const approvalsSvc = approvalService(db);
|
|
17
27
|
const issueApprovalsSvc = issueApprovalService(db);
|
|
@@ -51,7 +61,12 @@ export function chatService(db) {
|
|
|
51
61
|
})
|
|
52
62
|
.from(chatMessages)
|
|
53
63
|
.innerJoin(chatConversationUserStates, and(eq(chatConversationUserStates.orgId, orgId), eq(chatConversationUserStates.userId, userId), eq(chatConversationUserStates.conversationId, chatMessages.conversationId)))
|
|
54
|
-
.where(and(eq(chatMessages.orgId, orgId), inArray(chatMessages.conversationId, conversationIds), isNull(chatMessages.supersededAt), visibleIncomingMessageSql(), gt(chatMessages.createdAt, chatConversationUserStates.lastReadAt)
|
|
64
|
+
.where(and(eq(chatMessages.orgId, orgId), inArray(chatMessages.conversationId, conversationIds), isNull(chatMessages.supersededAt), visibleIncomingMessageSql(), gt(chatMessages.createdAt, chatConversationUserStates.lastReadAt), sql `not exists (
|
|
65
|
+
select 1
|
|
66
|
+
from ${agentIntegrationChatBindings}
|
|
67
|
+
where ${agentIntegrationChatBindings.orgId} = ${orgId}
|
|
68
|
+
and ${agentIntegrationChatBindings.conversationId} = ${chatMessages.conversationId}
|
|
69
|
+
)`))
|
|
55
70
|
.groupBy(chatMessages.conversationId);
|
|
56
71
|
return new Map(rows.map((row) => [row.conversationId, Number(row.count ?? 0)]));
|
|
57
72
|
}
|
|
@@ -68,6 +83,63 @@ export function chatService(db) {
|
|
|
68
83
|
.groupBy(chatMessages.conversationId);
|
|
69
84
|
return new Set(rows.map((row) => row.conversationId));
|
|
70
85
|
}
|
|
86
|
+
async function listConversationSourceMetadata(orgId, conversationIds) {
|
|
87
|
+
if (conversationIds.length === 0)
|
|
88
|
+
return new Map();
|
|
89
|
+
const rows = await db
|
|
90
|
+
.select({
|
|
91
|
+
conversationId: agentIntegrationChatBindings.conversationId,
|
|
92
|
+
integrationId: agentIntegrationChatBindings.integrationId,
|
|
93
|
+
provider: agentIntegrations.provider,
|
|
94
|
+
externalChatId: agentIntegrationChatBindings.externalChatId,
|
|
95
|
+
externalChatType: agentIntegrationChatBindings.externalChatType,
|
|
96
|
+
})
|
|
97
|
+
.from(agentIntegrationChatBindings)
|
|
98
|
+
.innerJoin(agentIntegrations, eq(agentIntegrations.id, agentIntegrationChatBindings.integrationId))
|
|
99
|
+
.where(and(eq(agentIntegrationChatBindings.orgId, orgId), inArray(agentIntegrationChatBindings.conversationId, conversationIds)))
|
|
100
|
+
.orderBy(agentIntegrationChatBindings.createdAt);
|
|
101
|
+
const map = new Map();
|
|
102
|
+
for (const row of rows) {
|
|
103
|
+
if (map.has(row.conversationId))
|
|
104
|
+
continue;
|
|
105
|
+
map.set(row.conversationId, {
|
|
106
|
+
source: "agent_integration",
|
|
107
|
+
provider: row.provider,
|
|
108
|
+
integrationId: row.integrationId,
|
|
109
|
+
externalChatId: row.externalChatId,
|
|
110
|
+
externalChatType: row.externalChatType,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
const missingConversationIds = conversationIds.filter((id) => !map.has(id));
|
|
114
|
+
if (missingConversationIds.length === 0)
|
|
115
|
+
return map;
|
|
116
|
+
const historicalRows = await db
|
|
117
|
+
.select({
|
|
118
|
+
conversationId: chatMessages.conversationId,
|
|
119
|
+
payload: chatMessages.structuredPayload,
|
|
120
|
+
})
|
|
121
|
+
.from(chatMessages)
|
|
122
|
+
.where(and(eq(chatMessages.orgId, orgId), inArray(chatMessages.conversationId, missingConversationIds), sql `${chatMessages.structuredPayload}->>'source' = 'agent_integration'`, sql `${chatMessages.structuredPayload}->>'provider' = 'feishu'`))
|
|
123
|
+
.orderBy(chatMessages.createdAt);
|
|
124
|
+
for (const row of historicalRows) {
|
|
125
|
+
if (map.has(row.conversationId))
|
|
126
|
+
continue;
|
|
127
|
+
const payload = row.payload ?? {};
|
|
128
|
+
const integrationId = typeof payload.integrationId === "string" ? payload.integrationId : null;
|
|
129
|
+
const externalChatId = typeof payload.externalChatId === "string" ? payload.externalChatId : null;
|
|
130
|
+
const externalChatType = typeof payload.externalChatType === "string" ? payload.externalChatType : null;
|
|
131
|
+
if (!integrationId || !externalChatId || !externalChatType)
|
|
132
|
+
continue;
|
|
133
|
+
map.set(row.conversationId, {
|
|
134
|
+
source: "agent_integration",
|
|
135
|
+
provider: "feishu",
|
|
136
|
+
integrationId,
|
|
137
|
+
externalChatId,
|
|
138
|
+
externalChatType,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return map;
|
|
142
|
+
}
|
|
71
143
|
async function listLatestReplyPreviews(orgId, conversationIds) {
|
|
72
144
|
if (conversationIds.length === 0)
|
|
73
145
|
return new Map();
|
|
@@ -175,8 +247,14 @@ export function chatService(db) {
|
|
|
175
247
|
await ensureConversationUserStates(rows, userId);
|
|
176
248
|
}
|
|
177
249
|
const conversationIds = rows.map((row) => row.id);
|
|
250
|
+
const sourceLookupConversationIds = [
|
|
251
|
+
...new Set([
|
|
252
|
+
...conversationIds,
|
|
253
|
+
...rows.flatMap((row) => [row.forkedFromConversationId, row.forkRootConversationId].filter((id) => Boolean(id))),
|
|
254
|
+
]),
|
|
255
|
+
];
|
|
178
256
|
const orgId = rows[0]?.orgId ?? null;
|
|
179
|
-
const [contextLinksByConversationId, primaryIssuesById, userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId,] = await Promise.all([
|
|
257
|
+
const [contextLinksByConversationId, primaryIssuesById, userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId, sourceMetadataByConversationId,] = await Promise.all([
|
|
180
258
|
listContextLinksForConversationIds(db, rows.map((row) => row.id)),
|
|
181
259
|
listPrimaryIssues(db, rows),
|
|
182
260
|
userId && orgId
|
|
@@ -194,29 +272,45 @@ export function chatService(db) {
|
|
|
194
272
|
orgId
|
|
195
273
|
? listUserMessageSummaries(orgId, conversationIds)
|
|
196
274
|
: Promise.resolve(new Map()),
|
|
275
|
+
orgId
|
|
276
|
+
? listConversationSourceMetadata(orgId, sourceLookupConversationIds)
|
|
277
|
+
: Promise.resolve(new Map()),
|
|
197
278
|
]);
|
|
198
|
-
return rows.map((row) =>
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
279
|
+
return rows.map((row) => {
|
|
280
|
+
const sourceMetadata = sourceMetadataByConversationId.get(row.id) ?? null;
|
|
281
|
+
const isExternalBound = Boolean(sourceMetadata);
|
|
282
|
+
const unreadCount = isExternalBound ? 0 : (unreadCountsByConversationId.get(row.id) ?? 0);
|
|
283
|
+
return {
|
|
284
|
+
...row,
|
|
285
|
+
primaryIssue: row.primaryIssueId ? (primaryIssuesById.get(row.primaryIssueId) ?? null) : null,
|
|
286
|
+
latestReplyPreview: latestReplyPreviewsByConversationId.get(row.id) ?? null,
|
|
287
|
+
latestUserMessagePreview: userMessageSummariesByConversationId.get(row.id)?.latestPreview ?? null,
|
|
288
|
+
userMessageCount: userMessageSummariesByConversationId.get(row.id)?.count ?? 0,
|
|
289
|
+
contextLinks: contextLinksByConversationId.get(row.id) ?? [],
|
|
290
|
+
sourceMetadata,
|
|
291
|
+
mutability: conversationMutability(row, sourceMetadata, sourceMetadataByConversationId),
|
|
292
|
+
lastReadAt: userStatesByConversationId.get(row.id)?.lastReadAt ?? null,
|
|
293
|
+
isPinned: Boolean(userStatesByConversationId.get(row.id)?.pinnedAt),
|
|
294
|
+
unreadCount,
|
|
295
|
+
isUnread: unreadCount > 0,
|
|
296
|
+
needsAttention: !isExternalBound && (unreadCount > 0 ||
|
|
297
|
+
pendingProposalConversationIds.has(row.id)),
|
|
298
|
+
};
|
|
299
|
+
});
|
|
212
300
|
}
|
|
213
301
|
async function hydrateConversationSummaries(rows, userId) {
|
|
214
302
|
if (userId) {
|
|
215
303
|
await ensureConversationUserStates(rows, userId);
|
|
216
304
|
}
|
|
217
305
|
const conversationIds = rows.map((row) => row.id);
|
|
306
|
+
const sourceLookupConversationIds = [
|
|
307
|
+
...new Set([
|
|
308
|
+
...conversationIds,
|
|
309
|
+
...rows.flatMap((row) => [row.forkedFromConversationId, row.forkRootConversationId].filter((id) => Boolean(id))),
|
|
310
|
+
]),
|
|
311
|
+
];
|
|
218
312
|
const orgId = rows[0]?.orgId ?? null;
|
|
219
|
-
const [userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId,] = await Promise.all([
|
|
313
|
+
const [userStatesByConversationId, unreadCountsByConversationId, pendingProposalConversationIds, latestReplyPreviewsByConversationId, userMessageSummariesByConversationId, sourceMetadataByConversationId,] = await Promise.all([
|
|
220
314
|
userId && orgId
|
|
221
315
|
? listConversationUserStates(orgId, userId, conversationIds)
|
|
222
316
|
: Promise.resolve(new Map()),
|
|
@@ -232,19 +326,29 @@ export function chatService(db) {
|
|
|
232
326
|
orgId
|
|
233
327
|
? listUserMessageSummaries(orgId, conversationIds)
|
|
234
328
|
: Promise.resolve(new Map()),
|
|
329
|
+
orgId
|
|
330
|
+
? listConversationSourceMetadata(orgId, sourceLookupConversationIds)
|
|
331
|
+
: Promise.resolve(new Map()),
|
|
235
332
|
]);
|
|
236
|
-
return rows.map((row) =>
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
333
|
+
return rows.map((row) => {
|
|
334
|
+
const sourceMetadata = sourceMetadataByConversationId.get(row.id) ?? null;
|
|
335
|
+
const isExternalBound = Boolean(sourceMetadata);
|
|
336
|
+
const unreadCount = isExternalBound ? 0 : (unreadCountsByConversationId.get(row.id) ?? 0);
|
|
337
|
+
return {
|
|
338
|
+
...row,
|
|
339
|
+
latestReplyPreview: latestReplyPreviewsByConversationId.get(row.id) ?? null,
|
|
340
|
+
latestUserMessagePreview: userMessageSummariesByConversationId.get(row.id)?.latestPreview ?? null,
|
|
341
|
+
userMessageCount: userMessageSummariesByConversationId.get(row.id)?.count ?? 0,
|
|
342
|
+
sourceMetadata,
|
|
343
|
+
mutability: conversationMutability(row, sourceMetadata, sourceMetadataByConversationId),
|
|
344
|
+
lastReadAt: userStatesByConversationId.get(row.id)?.lastReadAt ?? null,
|
|
345
|
+
isPinned: Boolean(userStatesByConversationId.get(row.id)?.pinnedAt),
|
|
346
|
+
unreadCount,
|
|
347
|
+
isUnread: unreadCount > 0,
|
|
348
|
+
needsAttention: !isExternalBound && (unreadCount > 0 ||
|
|
349
|
+
pendingProposalConversationIds.has(row.id)),
|
|
350
|
+
};
|
|
351
|
+
});
|
|
248
352
|
}
|
|
249
353
|
async function getConversationOrThrow(id) {
|
|
250
354
|
const row = await db
|
|
@@ -305,6 +409,328 @@ export function chatService(db) {
|
|
|
305
409
|
.where(inArray(approvals.id, approvalIds));
|
|
306
410
|
return new Map(approvalRows.map((row) => [row.id, row]));
|
|
307
411
|
}
|
|
412
|
+
function isQueuePositionConflict(error) {
|
|
413
|
+
return Boolean(error
|
|
414
|
+
&& typeof error === "object"
|
|
415
|
+
&& "code" in error
|
|
416
|
+
&& error.code === "23505");
|
|
417
|
+
}
|
|
418
|
+
function normalizeQueuedPayload(payload) {
|
|
419
|
+
return {
|
|
420
|
+
body: String(payload.body ?? ""),
|
|
421
|
+
attachmentIds: Array.isArray(payload.attachmentIds)
|
|
422
|
+
? payload.attachmentIds.filter((id) => typeof id === "string")
|
|
423
|
+
: [],
|
|
424
|
+
projectId: typeof payload.projectId === "string" ? payload.projectId : null,
|
|
425
|
+
skillRefs: Array.isArray(payload.skillRefs)
|
|
426
|
+
? payload.skillRefs.filter((ref) => typeof ref === "string")
|
|
427
|
+
: [],
|
|
428
|
+
accessMode: typeof payload.accessMode === "string" ? payload.accessMode : null,
|
|
429
|
+
model: typeof payload.model === "string" ? payload.model : null,
|
|
430
|
+
effort: typeof payload.effort === "string" ? payload.effort : null,
|
|
431
|
+
metadata: payload.metadata && typeof payload.metadata === "object" && !Array.isArray(payload.metadata)
|
|
432
|
+
? payload.metadata
|
|
433
|
+
: null,
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
function hydrateQueuedMessage(row) {
|
|
437
|
+
return {
|
|
438
|
+
...row,
|
|
439
|
+
payload: normalizeQueuedPayload(row.payload),
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
async function createGeneration(orgId, conversationId) {
|
|
443
|
+
const [row] = await db
|
|
444
|
+
.insert(chatGenerations)
|
|
445
|
+
.values({
|
|
446
|
+
orgId,
|
|
447
|
+
conversationId,
|
|
448
|
+
status: "active",
|
|
449
|
+
})
|
|
450
|
+
.returning();
|
|
451
|
+
if (!row)
|
|
452
|
+
throw new Error("Failed to create chat generation");
|
|
453
|
+
return row;
|
|
454
|
+
}
|
|
455
|
+
async function markGenerationTerminal(generationId, status) {
|
|
456
|
+
if (!generationId)
|
|
457
|
+
return null;
|
|
458
|
+
const now = new Date();
|
|
459
|
+
const [row] = await db
|
|
460
|
+
.update(chatGenerations)
|
|
461
|
+
.set({
|
|
462
|
+
status,
|
|
463
|
+
terminalReason: status,
|
|
464
|
+
completedAt: now,
|
|
465
|
+
updatedAt: now,
|
|
466
|
+
})
|
|
467
|
+
.where(eq(chatGenerations.id, generationId))
|
|
468
|
+
.returning();
|
|
469
|
+
return row ?? null;
|
|
470
|
+
}
|
|
471
|
+
async function getLatestActiveGeneration(conversationId) {
|
|
472
|
+
return db
|
|
473
|
+
.select()
|
|
474
|
+
.from(chatGenerations)
|
|
475
|
+
.where(and(eq(chatGenerations.conversationId, conversationId), inArray(chatGenerations.status, ["active", "tool_busy", "closing"])))
|
|
476
|
+
.orderBy(desc(chatGenerations.startedAt), desc(chatGenerations.createdAt))
|
|
477
|
+
.limit(1)
|
|
478
|
+
.then((rows) => rows[0] ?? null);
|
|
479
|
+
}
|
|
480
|
+
async function getLatestGeneration(conversationId) {
|
|
481
|
+
return db
|
|
482
|
+
.select()
|
|
483
|
+
.from(chatGenerations)
|
|
484
|
+
.where(eq(chatGenerations.conversationId, conversationId))
|
|
485
|
+
.orderBy(desc(chatGenerations.startedAt), desc(chatGenerations.createdAt))
|
|
486
|
+
.limit(1)
|
|
487
|
+
.then((rows) => rows[0] ?? null);
|
|
488
|
+
}
|
|
489
|
+
async function listQueuedMessages(conversationId) {
|
|
490
|
+
await reclaimStaleQueuedMessageClaims(conversationId);
|
|
491
|
+
const rows = await db
|
|
492
|
+
.select()
|
|
493
|
+
.from(chatQueuedMessages)
|
|
494
|
+
.where(and(eq(chatQueuedMessages.conversationId, conversationId), inArray(chatQueuedMessages.status, ["queued", "steer_pending", "dequeue_claimed", "running"])))
|
|
495
|
+
.orderBy(asc(chatQueuedMessages.position), asc(chatQueuedMessages.createdAt));
|
|
496
|
+
return rows.map(hydrateQueuedMessage);
|
|
497
|
+
}
|
|
498
|
+
async function getQueueSnapshot(conversationId, activeGenerationId) {
|
|
499
|
+
const activeGeneration = activeGenerationId
|
|
500
|
+
? { id: activeGenerationId }
|
|
501
|
+
: await getLatestActiveGeneration(conversationId);
|
|
502
|
+
return {
|
|
503
|
+
activeGenerationId: activeGeneration?.id ?? null,
|
|
504
|
+
items: await listQueuedMessages(conversationId),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
async function createQueuedMessage(input) {
|
|
508
|
+
const payload = {
|
|
509
|
+
...input.payload,
|
|
510
|
+
body: input.payload.body.trim(),
|
|
511
|
+
attachmentIds: input.payload.attachmentIds ?? [],
|
|
512
|
+
skillRefs: input.payload.skillRefs ?? [],
|
|
513
|
+
projectId: input.payload.projectId ?? null,
|
|
514
|
+
accessMode: input.payload.accessMode ?? null,
|
|
515
|
+
model: input.payload.model ?? null,
|
|
516
|
+
effort: input.payload.effort ?? null,
|
|
517
|
+
metadata: input.payload.metadata ?? null,
|
|
518
|
+
};
|
|
519
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
520
|
+
try {
|
|
521
|
+
return await db.transaction(async (tx) => {
|
|
522
|
+
const existing = await tx
|
|
523
|
+
.select()
|
|
524
|
+
.from(chatQueuedMessages)
|
|
525
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.clientMutationId, input.clientMutationId)))
|
|
526
|
+
.limit(1)
|
|
527
|
+
.then((rows) => rows[0] ?? null);
|
|
528
|
+
if (existing) {
|
|
529
|
+
if (JSON.stringify(existing.payload) !== JSON.stringify(payload)) {
|
|
530
|
+
throw conflict("Queued message idempotency key reused with a different payload");
|
|
531
|
+
}
|
|
532
|
+
return hydrateQueuedMessage(existing);
|
|
533
|
+
}
|
|
534
|
+
const [positionRow] = await tx
|
|
535
|
+
.select({
|
|
536
|
+
nextPosition: sql `coalesce(max(${chatQueuedMessages.position}), 0) + 1`,
|
|
537
|
+
})
|
|
538
|
+
.from(chatQueuedMessages)
|
|
539
|
+
.where(eq(chatQueuedMessages.conversationId, input.conversationId));
|
|
540
|
+
const [row] = await tx
|
|
541
|
+
.insert(chatQueuedMessages)
|
|
542
|
+
.values({
|
|
543
|
+
orgId: input.orgId,
|
|
544
|
+
conversationId: input.conversationId,
|
|
545
|
+
clientMutationId: input.clientMutationId,
|
|
546
|
+
position: Number(positionRow?.nextPosition ?? 1),
|
|
547
|
+
payload,
|
|
548
|
+
expectedGenerationId: input.expectedGenerationId ?? null,
|
|
549
|
+
})
|
|
550
|
+
.returning();
|
|
551
|
+
if (!row)
|
|
552
|
+
throw new Error("Failed to create queued chat message");
|
|
553
|
+
return hydrateQueuedMessage(row);
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
catch (error) {
|
|
557
|
+
if (attempt < 2 && isQueuePositionConflict(error))
|
|
558
|
+
continue;
|
|
559
|
+
throw error;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
throw new Error("Failed to create queued chat message");
|
|
563
|
+
}
|
|
564
|
+
async function updateQueuedMessage(input) {
|
|
565
|
+
const now = new Date();
|
|
566
|
+
const [row] = await db
|
|
567
|
+
.update(chatQueuedMessages)
|
|
568
|
+
.set({
|
|
569
|
+
payload: {
|
|
570
|
+
...input.payload,
|
|
571
|
+
body: input.payload.body.trim(),
|
|
572
|
+
attachmentIds: input.payload.attachmentIds ?? [],
|
|
573
|
+
skillRefs: input.payload.skillRefs ?? [],
|
|
574
|
+
projectId: input.payload.projectId ?? null,
|
|
575
|
+
accessMode: input.payload.accessMode ?? null,
|
|
576
|
+
model: input.payload.model ?? null,
|
|
577
|
+
effort: input.payload.effort ?? null,
|
|
578
|
+
metadata: input.payload.metadata ?? null,
|
|
579
|
+
},
|
|
580
|
+
version: input.version + 1,
|
|
581
|
+
lastDeliveryReason: null,
|
|
582
|
+
updatedAt: now,
|
|
583
|
+
})
|
|
584
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.version, input.version), eq(chatQueuedMessages.status, "queued")))
|
|
585
|
+
.returning();
|
|
586
|
+
if (!row)
|
|
587
|
+
throw conflict("Queued message was changed or is no longer editable");
|
|
588
|
+
return hydrateQueuedMessage(row);
|
|
589
|
+
}
|
|
590
|
+
async function cancelQueuedMessage(input) {
|
|
591
|
+
const now = new Date();
|
|
592
|
+
const conditions = [
|
|
593
|
+
eq(chatQueuedMessages.conversationId, input.conversationId),
|
|
594
|
+
eq(chatQueuedMessages.id, input.itemId),
|
|
595
|
+
eq(chatQueuedMessages.status, "queued"),
|
|
596
|
+
];
|
|
597
|
+
if (input.version)
|
|
598
|
+
conditions.push(eq(chatQueuedMessages.version, input.version));
|
|
599
|
+
const [row] = await db
|
|
600
|
+
.update(chatQueuedMessages)
|
|
601
|
+
.set({
|
|
602
|
+
status: "cancelled",
|
|
603
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
604
|
+
cancelledAt: now,
|
|
605
|
+
updatedAt: now,
|
|
606
|
+
})
|
|
607
|
+
.where(and(...conditions))
|
|
608
|
+
.returning();
|
|
609
|
+
if (!row)
|
|
610
|
+
throw conflict("Queued message was changed or is no longer cancellable");
|
|
611
|
+
return hydrateQueuedMessage(row);
|
|
612
|
+
}
|
|
613
|
+
async function markQueuedMessageSteerFallback(input) {
|
|
614
|
+
const now = new Date();
|
|
615
|
+
const [row] = await db
|
|
616
|
+
.update(chatQueuedMessages)
|
|
617
|
+
.set({
|
|
618
|
+
status: "queued",
|
|
619
|
+
activeGenerationId: input.activeGenerationId ?? null,
|
|
620
|
+
deliveryAttempts: sql `${chatQueuedMessages.deliveryAttempts} + 1`,
|
|
621
|
+
lastAttemptAt: now,
|
|
622
|
+
lastDeliveryReason: input.reason,
|
|
623
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
624
|
+
updatedAt: now,
|
|
625
|
+
})
|
|
626
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.status, "queued")))
|
|
627
|
+
.returning();
|
|
628
|
+
if (!row)
|
|
629
|
+
throw conflict("Queued message was changed or is no longer steerable");
|
|
630
|
+
return hydrateQueuedMessage(row);
|
|
631
|
+
}
|
|
632
|
+
async function claimNextQueuedMessage(conversationId) {
|
|
633
|
+
await reclaimStaleQueuedMessageClaims(conversationId);
|
|
634
|
+
const now = new Date();
|
|
635
|
+
return db.transaction(async (tx) => {
|
|
636
|
+
const [candidate] = await tx
|
|
637
|
+
.select()
|
|
638
|
+
.from(chatQueuedMessages)
|
|
639
|
+
.where(and(eq(chatQueuedMessages.conversationId, conversationId), eq(chatQueuedMessages.status, "queued")))
|
|
640
|
+
.orderBy(asc(chatQueuedMessages.position), asc(chatQueuedMessages.createdAt))
|
|
641
|
+
.limit(1);
|
|
642
|
+
if (!candidate)
|
|
643
|
+
return null;
|
|
644
|
+
const [row] = await tx
|
|
645
|
+
.update(chatQueuedMessages)
|
|
646
|
+
.set({
|
|
647
|
+
status: "dequeue_claimed",
|
|
648
|
+
dequeuedAt: now,
|
|
649
|
+
deliveryAttempts: sql `${chatQueuedMessages.deliveryAttempts} + 1`,
|
|
650
|
+
lastAttemptAt: now,
|
|
651
|
+
lastDeliveryReason: null,
|
|
652
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
653
|
+
updatedAt: now,
|
|
654
|
+
})
|
|
655
|
+
.where(and(eq(chatQueuedMessages.id, candidate.id), eq(chatQueuedMessages.conversationId, conversationId), eq(chatQueuedMessages.status, "queued"), eq(chatQueuedMessages.version, candidate.version)))
|
|
656
|
+
.returning();
|
|
657
|
+
return row ? hydrateQueuedMessage(row) : null;
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
async function releaseQueuedMessageClaim(input) {
|
|
661
|
+
const now = new Date();
|
|
662
|
+
const [row] = await db
|
|
663
|
+
.update(chatQueuedMessages)
|
|
664
|
+
.set({
|
|
665
|
+
status: "queued",
|
|
666
|
+
lastDeliveryReason: input.reason,
|
|
667
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
668
|
+
updatedAt: now,
|
|
669
|
+
})
|
|
670
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.status, "dequeue_claimed")))
|
|
671
|
+
.returning();
|
|
672
|
+
return row ? hydrateQueuedMessage(row) : null;
|
|
673
|
+
}
|
|
674
|
+
async function reclaimStaleQueuedMessageClaims(conversationId) {
|
|
675
|
+
const cutoff = new Date(Date.now() - QUEUED_MESSAGE_CLAIM_LEASE_MS);
|
|
676
|
+
await db
|
|
677
|
+
.update(chatQueuedMessages)
|
|
678
|
+
.set({
|
|
679
|
+
status: "queued",
|
|
680
|
+
lastDeliveryReason: "claim_expired",
|
|
681
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
682
|
+
updatedAt: new Date(),
|
|
683
|
+
})
|
|
684
|
+
.where(and(eq(chatQueuedMessages.conversationId, conversationId), eq(chatQueuedMessages.status, "dequeue_claimed"), lt(chatQueuedMessages.updatedAt, cutoff)));
|
|
685
|
+
}
|
|
686
|
+
async function assertQueuedMessageClaimedForDelivery(input) {
|
|
687
|
+
const row = await db
|
|
688
|
+
.select()
|
|
689
|
+
.from(chatQueuedMessages)
|
|
690
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId)))
|
|
691
|
+
.limit(1)
|
|
692
|
+
.then((rows) => rows[0] ?? null);
|
|
693
|
+
if (!row || row.status !== "dequeue_claimed") {
|
|
694
|
+
throw conflict("Queued message is not claimed for delivery");
|
|
695
|
+
}
|
|
696
|
+
const payload = normalizeQueuedPayload(row.payload);
|
|
697
|
+
if (payload.body.trim() !== input.body.trim()) {
|
|
698
|
+
throw conflict("Queued message body no longer matches claimed payload");
|
|
699
|
+
}
|
|
700
|
+
return hydrateQueuedMessage(row);
|
|
701
|
+
}
|
|
702
|
+
async function markQueuedMessageRunning(input) {
|
|
703
|
+
const now = new Date();
|
|
704
|
+
const [row] = await db
|
|
705
|
+
.update(chatQueuedMessages)
|
|
706
|
+
.set({
|
|
707
|
+
status: "running",
|
|
708
|
+
sourceMessageId: input.sourceMessageId,
|
|
709
|
+
deliveredMessageId: input.sourceMessageId,
|
|
710
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
711
|
+
updatedAt: now,
|
|
712
|
+
})
|
|
713
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), eq(chatQueuedMessages.status, "dequeue_claimed")))
|
|
714
|
+
.returning();
|
|
715
|
+
if (!row)
|
|
716
|
+
throw conflict("Queued message is no longer deliverable");
|
|
717
|
+
return hydrateQueuedMessage(row);
|
|
718
|
+
}
|
|
719
|
+
async function markQueuedMessageDeliveryTerminal(input) {
|
|
720
|
+
const now = new Date();
|
|
721
|
+
const nextStatus = input.status === "completed" ? "completed" : "failed";
|
|
722
|
+
const [row] = await db
|
|
723
|
+
.update(chatQueuedMessages)
|
|
724
|
+
.set({
|
|
725
|
+
status: nextStatus,
|
|
726
|
+
lastDeliveryReason: input.status === "completed" ? null : input.status,
|
|
727
|
+
version: sql `${chatQueuedMessages.version} + 1`,
|
|
728
|
+
updatedAt: now,
|
|
729
|
+
})
|
|
730
|
+
.where(and(eq(chatQueuedMessages.conversationId, input.conversationId), eq(chatQueuedMessages.id, input.itemId), inArray(chatQueuedMessages.status, ["dequeue_claimed", "running"])))
|
|
731
|
+
.returning();
|
|
732
|
+
return row ? hydrateQueuedMessage(row) : null;
|
|
733
|
+
}
|
|
308
734
|
async function hydrateMessages(rows, options = {}) {
|
|
309
735
|
const includeTranscript = options.includeTranscript !== false;
|
|
310
736
|
const [attachmentsByMessageId, approvalsById] = await Promise.all([
|
|
@@ -493,6 +919,210 @@ export function chatService(db) {
|
|
|
493
919
|
});
|
|
494
920
|
return getById(created.id);
|
|
495
921
|
}
|
|
922
|
+
function forkSystemEventBody(sourceConversation, sourceMessageId) {
|
|
923
|
+
const messageSuffix = sourceMessageId ? " at message" : "";
|
|
924
|
+
return `Forked from [${sourceConversation.title}](chat://${sourceConversation.id})${messageSuffix}.`;
|
|
925
|
+
}
|
|
926
|
+
async function findForkFamilyGroup(client, orgId, userId, rootConversationId) {
|
|
927
|
+
const rootThreadKey = `chat:${rootConversationId}`;
|
|
928
|
+
return client
|
|
929
|
+
.select({ id: messengerCustomGroups.id })
|
|
930
|
+
.from(messengerCustomGroups)
|
|
931
|
+
.innerJoin(messengerCustomGroupEntries, eq(messengerCustomGroupEntries.groupId, messengerCustomGroups.id))
|
|
932
|
+
.where(and(eq(messengerCustomGroups.orgId, orgId), eq(messengerCustomGroups.userId, userId), eq(messengerCustomGroupEntries.orgId, orgId), eq(messengerCustomGroupEntries.userId, userId), eq(messengerCustomGroupEntries.threadKey, rootThreadKey)))
|
|
933
|
+
.orderBy(asc(messengerCustomGroups.sortOrder), asc(messengerCustomGroups.createdAt))
|
|
934
|
+
.limit(1)
|
|
935
|
+
.then((rows) => rows[0] ?? null);
|
|
936
|
+
}
|
|
937
|
+
async function createForkFamilyGroup(client, orgId, userId, name) {
|
|
938
|
+
const [lastGroup] = await client
|
|
939
|
+
.select({ sortOrder: messengerCustomGroups.sortOrder })
|
|
940
|
+
.from(messengerCustomGroups)
|
|
941
|
+
.where(and(eq(messengerCustomGroups.orgId, orgId), eq(messengerCustomGroups.userId, userId)))
|
|
942
|
+
.orderBy(desc(messengerCustomGroups.sortOrder))
|
|
943
|
+
.limit(1);
|
|
944
|
+
const now = new Date();
|
|
945
|
+
const [group] = await client
|
|
946
|
+
.insert(messengerCustomGroups)
|
|
947
|
+
.values({
|
|
948
|
+
orgId,
|
|
949
|
+
userId,
|
|
950
|
+
name: name.trim() || "Forked conversation",
|
|
951
|
+
icon: MESSENGER_FORK_GROUP_DEFAULT_ICON,
|
|
952
|
+
sortOrder: (lastGroup?.sortOrder ?? -1) + 1,
|
|
953
|
+
updatedAt: now,
|
|
954
|
+
})
|
|
955
|
+
.returning();
|
|
956
|
+
if (!group)
|
|
957
|
+
throw new Error("Failed to create Messenger fork group");
|
|
958
|
+
return group;
|
|
959
|
+
}
|
|
960
|
+
async function assignForkThreadToGroup(client, orgId, userId, groupId, threadKey) {
|
|
961
|
+
const [lastEntry] = await client
|
|
962
|
+
.select({ sortOrder: messengerCustomGroupEntries.sortOrder })
|
|
963
|
+
.from(messengerCustomGroupEntries)
|
|
964
|
+
.where(and(eq(messengerCustomGroupEntries.orgId, orgId), eq(messengerCustomGroupEntries.userId, userId), eq(messengerCustomGroupEntries.groupId, groupId)))
|
|
965
|
+
.orderBy(desc(messengerCustomGroupEntries.sortOrder))
|
|
966
|
+
.limit(1);
|
|
967
|
+
const now = new Date();
|
|
968
|
+
await client
|
|
969
|
+
.insert(messengerCustomGroupEntries)
|
|
970
|
+
.values({
|
|
971
|
+
orgId,
|
|
972
|
+
userId,
|
|
973
|
+
groupId,
|
|
974
|
+
threadKey,
|
|
975
|
+
sortOrder: (lastEntry?.sortOrder ?? -1) + 1,
|
|
976
|
+
updatedAt: now,
|
|
977
|
+
})
|
|
978
|
+
.onConflictDoNothing();
|
|
979
|
+
}
|
|
980
|
+
async function ensureForkFamilyGroup(client, orgId, userId, rootConversationId, sourceConversationId, childConversationId, groupName) {
|
|
981
|
+
const existing = await findForkFamilyGroup(client, orgId, userId, rootConversationId);
|
|
982
|
+
const group = existing ?? await createForkFamilyGroup(client, orgId, userId, groupName);
|
|
983
|
+
for (const threadKey of [
|
|
984
|
+
`chat:${rootConversationId}`,
|
|
985
|
+
`chat:${sourceConversationId}`,
|
|
986
|
+
`chat:${childConversationId}`,
|
|
987
|
+
]) {
|
|
988
|
+
await assignForkThreadToGroup(client, orgId, userId, group.id, threadKey);
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
async function forkConversation(input) {
|
|
992
|
+
const created = await db.transaction(async (tx) => {
|
|
993
|
+
const source = await tx
|
|
994
|
+
.select()
|
|
995
|
+
.from(chatConversations)
|
|
996
|
+
.where(and(eq(chatConversations.id, input.sourceConversationId), eq(chatConversations.orgId, input.orgId)))
|
|
997
|
+
.then((rows) => rows[0] ?? null);
|
|
998
|
+
if (!source)
|
|
999
|
+
throw notFound("Chat conversation not found");
|
|
1000
|
+
const rootConversationId = source.forkRootConversationId ?? source.id;
|
|
1001
|
+
const messageConditions = [
|
|
1002
|
+
eq(chatMessages.conversationId, source.id),
|
|
1003
|
+
eq(chatMessages.orgId, input.orgId),
|
|
1004
|
+
isNull(chatMessages.supersededAt),
|
|
1005
|
+
];
|
|
1006
|
+
let forkSourceCreatedAt = null;
|
|
1007
|
+
if (input.sourceMessageId) {
|
|
1008
|
+
const sourceMessage = await tx
|
|
1009
|
+
.select({
|
|
1010
|
+
id: chatMessages.id,
|
|
1011
|
+
kind: chatMessages.kind,
|
|
1012
|
+
role: chatMessages.role,
|
|
1013
|
+
createdAt: chatMessages.createdAt,
|
|
1014
|
+
})
|
|
1015
|
+
.from(chatMessages)
|
|
1016
|
+
.where(and(...messageConditions, eq(chatMessages.id, input.sourceMessageId)))
|
|
1017
|
+
.then((rows) => rows[0] ?? null);
|
|
1018
|
+
if (!sourceMessage)
|
|
1019
|
+
throw unprocessable("Fork source message must belong to the source conversation");
|
|
1020
|
+
if (sourceMessage.role !== "assistant" || sourceMessage.kind !== "message") {
|
|
1021
|
+
throw unprocessable("Fork source message must be an assistant response");
|
|
1022
|
+
}
|
|
1023
|
+
forkSourceCreatedAt = sourceMessage.createdAt;
|
|
1024
|
+
}
|
|
1025
|
+
const now = new Date();
|
|
1026
|
+
const [child] = await tx
|
|
1027
|
+
.insert(chatConversations)
|
|
1028
|
+
.values({
|
|
1029
|
+
orgId: input.orgId,
|
|
1030
|
+
status: "active",
|
|
1031
|
+
title: input.title?.trim() || source.title,
|
|
1032
|
+
summary: source.summary,
|
|
1033
|
+
preferredAgentId: source.preferredAgentId,
|
|
1034
|
+
routedAgentId: source.routedAgentId,
|
|
1035
|
+
primaryIssueId: source.primaryIssueId,
|
|
1036
|
+
forkedFromConversationId: source.id,
|
|
1037
|
+
forkedFromMessageId: input.sourceMessageId ?? null,
|
|
1038
|
+
forkRootConversationId: rootConversationId,
|
|
1039
|
+
issueCreationMode: source.issueCreationMode,
|
|
1040
|
+
planMode: source.planMode,
|
|
1041
|
+
createdByUserId: input.createdByUserId,
|
|
1042
|
+
createdAt: now,
|
|
1043
|
+
updatedAt: now,
|
|
1044
|
+
})
|
|
1045
|
+
.returning();
|
|
1046
|
+
if (!child)
|
|
1047
|
+
throw new Error("Failed to create forked chat conversation");
|
|
1048
|
+
const contextLinks = await tx
|
|
1049
|
+
.select()
|
|
1050
|
+
.from(chatContextLinks)
|
|
1051
|
+
.where(eq(chatContextLinks.conversationId, source.id))
|
|
1052
|
+
.orderBy(chatContextLinks.createdAt);
|
|
1053
|
+
if (contextLinks.length > 0) {
|
|
1054
|
+
await tx
|
|
1055
|
+
.insert(chatContextLinks)
|
|
1056
|
+
.values(contextLinks.map((link) => ({
|
|
1057
|
+
orgId: input.orgId,
|
|
1058
|
+
conversationId: child.id,
|
|
1059
|
+
entityType: link.entityType,
|
|
1060
|
+
entityId: link.entityId,
|
|
1061
|
+
metadata: link.metadata,
|
|
1062
|
+
})))
|
|
1063
|
+
.onConflictDoNothing();
|
|
1064
|
+
}
|
|
1065
|
+
const messagesToCopy = await tx
|
|
1066
|
+
.select()
|
|
1067
|
+
.from(chatMessages)
|
|
1068
|
+
.where(and(...messageConditions, ...(forkSourceCreatedAt && input.sourceMessageId
|
|
1069
|
+
? [or(lt(chatMessages.createdAt, forkSourceCreatedAt), eq(chatMessages.id, input.sourceMessageId))]
|
|
1070
|
+
: [])))
|
|
1071
|
+
.orderBy(chatMessages.createdAt, chatMessages.id);
|
|
1072
|
+
const forkMessages = input.sourceMessageId
|
|
1073
|
+
? messagesToCopy.slice(0, messagesToCopy.findIndex((message) => message.id === input.sourceMessageId) + 1)
|
|
1074
|
+
: messagesToCopy;
|
|
1075
|
+
if (forkMessages.length > 0) {
|
|
1076
|
+
await tx.insert(chatMessages).values(forkMessages.map((message) => ({
|
|
1077
|
+
orgId: input.orgId,
|
|
1078
|
+
conversationId: child.id,
|
|
1079
|
+
role: message.role,
|
|
1080
|
+
kind: message.kind,
|
|
1081
|
+
status: message.status === "streaming" ? "interrupted" : message.status,
|
|
1082
|
+
body: message.body,
|
|
1083
|
+
structuredPayload: null,
|
|
1084
|
+
approvalId: null,
|
|
1085
|
+
runId: null,
|
|
1086
|
+
replyingAgentId: message.replyingAgentId,
|
|
1087
|
+
chatTurnId: null,
|
|
1088
|
+
turnVariant: 0,
|
|
1089
|
+
createdAt: message.createdAt,
|
|
1090
|
+
updatedAt: message.updatedAt,
|
|
1091
|
+
})));
|
|
1092
|
+
}
|
|
1093
|
+
const [systemEvent] = await tx
|
|
1094
|
+
.insert(chatMessages)
|
|
1095
|
+
.values({
|
|
1096
|
+
orgId: input.orgId,
|
|
1097
|
+
conversationId: child.id,
|
|
1098
|
+
role: "system",
|
|
1099
|
+
kind: "system_event",
|
|
1100
|
+
status: "completed",
|
|
1101
|
+
body: forkSystemEventBody(source, input.sourceMessageId ?? null),
|
|
1102
|
+
structuredPayload: {
|
|
1103
|
+
eventType: "chat_fork",
|
|
1104
|
+
type: "chat_fork",
|
|
1105
|
+
sourceConversationId: source.id,
|
|
1106
|
+
sourceConversationTitle: source.title,
|
|
1107
|
+
sourceMessageId: input.sourceMessageId ?? null,
|
|
1108
|
+
forkRootConversationId: rootConversationId,
|
|
1109
|
+
},
|
|
1110
|
+
createdAt: now,
|
|
1111
|
+
updatedAt: now,
|
|
1112
|
+
})
|
|
1113
|
+
.returning();
|
|
1114
|
+
await tx
|
|
1115
|
+
.update(chatConversations)
|
|
1116
|
+
.set({
|
|
1117
|
+
lastMessageAt: systemEvent?.createdAt ?? now,
|
|
1118
|
+
updatedAt: now,
|
|
1119
|
+
})
|
|
1120
|
+
.where(eq(chatConversations.id, child.id));
|
|
1121
|
+
await ensureForkFamilyGroup(tx, input.orgId, input.userId, rootConversationId, source.id, child.id, source.title);
|
|
1122
|
+
return child;
|
|
1123
|
+
});
|
|
1124
|
+
return getById(created.id, input.userId);
|
|
1125
|
+
}
|
|
496
1126
|
async function update(id, patch) {
|
|
497
1127
|
const [updated] = await db
|
|
498
1128
|
.update(chatConversations)
|
|
@@ -506,14 +1136,14 @@ export function chatService(db) {
|
|
|
506
1136
|
return null;
|
|
507
1137
|
return getById(id);
|
|
508
1138
|
}
|
|
509
|
-
async function updateDefaultTitle(id, title) {
|
|
1139
|
+
async function updateDefaultTitle(id, title, expectedCurrentTitle = "New chat") {
|
|
510
1140
|
const [updated] = await db
|
|
511
1141
|
.update(chatConversations)
|
|
512
1142
|
.set({
|
|
513
1143
|
title,
|
|
514
1144
|
updatedAt: new Date(),
|
|
515
1145
|
})
|
|
516
|
-
.where(and(eq(chatConversations.id, id), eq(chatConversations.title,
|
|
1146
|
+
.where(and(eq(chatConversations.id, id), eq(chatConversations.title, expectedCurrentTitle)))
|
|
517
1147
|
.returning();
|
|
518
1148
|
if (!updated)
|
|
519
1149
|
return null;
|
|
@@ -1462,10 +2092,26 @@ export function chatService(db) {
|
|
|
1462
2092
|
replaceSystemGeneratedTitle,
|
|
1463
2093
|
listAttachmentsForConversation,
|
|
1464
2094
|
remove,
|
|
2095
|
+
forkConversation,
|
|
1465
2096
|
resolve,
|
|
1466
2097
|
markRead,
|
|
1467
2098
|
markUnread,
|
|
1468
2099
|
setPinned,
|
|
2100
|
+
createGeneration,
|
|
2101
|
+
markGenerationTerminal,
|
|
2102
|
+
getLatestActiveGeneration,
|
|
2103
|
+
getLatestGeneration,
|
|
2104
|
+
getQueueSnapshot,
|
|
2105
|
+
listQueuedMessages,
|
|
2106
|
+
createQueuedMessage,
|
|
2107
|
+
updateQueuedMessage,
|
|
2108
|
+
cancelQueuedMessage,
|
|
2109
|
+
markQueuedMessageSteerFallback,
|
|
2110
|
+
claimNextQueuedMessage,
|
|
2111
|
+
releaseQueuedMessageClaim,
|
|
2112
|
+
assertQueuedMessageClaimedForDelivery,
|
|
2113
|
+
markQueuedMessageRunning,
|
|
2114
|
+
markQueuedMessageDeliveryTerminal,
|
|
1469
2115
|
listMessages,
|
|
1470
2116
|
getMessageTranscript,
|
|
1471
2117
|
addMessage,
|