@rudderhq/server 0.2.0-canary.9 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/ui/index.js +8 -1
- package/dist/bundled-plugins/plugin-linear/dist/ui/index.js.map +2 -2
- package/dist/bundled-plugins/plugin-linear/dist/worker.js +17 -3
- package/dist/bundled-plugins/plugin-linear/dist/worker.js.map +2 -2
- package/dist/home-paths.d.ts +2 -0
- package/dist/home-paths.d.ts.map +1 -1
- package/dist/home-paths.js +6 -1
- package/dist/home-paths.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +55 -2
- package/dist/index.js.map +1 -1
- package/dist/langfuse-transcript.d.ts.map +1 -1
- package/dist/langfuse-transcript.js +16 -2
- package/dist/langfuse-transcript.js.map +1 -1
- package/dist/middleware/auth.d.ts.map +1 -1
- package/dist/middleware/auth.js +54 -1
- package/dist/middleware/auth.js.map +1 -1
- package/dist/onboarding-assets/ceo/HEARTBEAT.md +8 -4
- package/dist/onboarding-assets/default/HEARTBEAT.md +7 -4
- package/dist/routes/agents.d.ts.map +1 -1
- package/dist/routes/agents.js +62 -3
- package/dist/routes/agents.js.map +1 -1
- package/dist/routes/approvals.d.ts.map +1 -1
- package/dist/routes/approvals.js +30 -1
- package/dist/routes/approvals.js.map +1 -1
- package/dist/routes/chats.d.ts.map +1 -1
- package/dist/routes/chats.js +285 -46
- package/dist/routes/chats.js.map +1 -1
- package/dist/routes/costs.d.ts.map +1 -1
- package/dist/routes/costs.js +20 -0
- package/dist/routes/costs.js.map +1 -1
- package/dist/routes/issues.d.ts.map +1 -1
- package/dist/routes/issues.js +229 -19
- package/dist/routes/issues.js.map +1 -1
- package/dist/routes/onboarding.d.ts +3 -0
- package/dist/routes/onboarding.d.ts.map +1 -0
- package/dist/routes/onboarding.js +545 -0
- package/dist/routes/onboarding.js.map +1 -0
- package/dist/routes/orgs.d.ts.map +1 -1
- package/dist/routes/orgs.js +22 -0
- package/dist/routes/orgs.js.map +1 -1
- package/dist/services/activity.d.ts.map +1 -1
- package/dist/services/activity.js +32 -1
- package/dist/services/activity.js.map +1 -1
- package/dist/services/agent-run-context.d.ts +1 -0
- package/dist/services/agent-run-context.d.ts.map +1 -1
- package/dist/services/agent-run-context.js +1 -0
- package/dist/services/agent-run-context.js.map +1 -1
- package/dist/services/agents.d.ts +13 -13
- package/dist/services/automations.d.ts +2 -2
- package/dist/services/calendar.d.ts +4 -4
- package/dist/services/chat-assistant.d.ts +10 -1
- package/dist/services/chat-assistant.d.ts.map +1 -1
- package/dist/services/chat-assistant.js +102 -3
- package/dist/services/chat-assistant.js.map +1 -1
- package/dist/services/chats.d.ts +87 -12
- package/dist/services/chats.d.ts.map +1 -1
- package/dist/services/chats.js +185 -10
- package/dist/services/chats.js.map +1 -1
- package/dist/services/costs.d.ts +21 -0
- package/dist/services/costs.d.ts.map +1 -1
- package/dist/services/costs.js +76 -2
- package/dist/services/costs.js.map +1 -1
- package/dist/services/finance.d.ts +2 -2
- package/dist/services/goals.d.ts +12 -12
- package/dist/services/instance-settings.d.ts.map +1 -1
- package/dist/services/instance-settings.js +25 -16
- package/dist/services/instance-settings.js.map +1 -1
- package/dist/services/issue-review-wakeup.d.ts +49 -1
- package/dist/services/issue-review-wakeup.d.ts.map +1 -1
- package/dist/services/issue-review-wakeup.js +39 -2
- package/dist/services/issue-review-wakeup.js.map +1 -1
- package/dist/services/issues.d.ts +2 -1
- package/dist/services/issues.d.ts.map +1 -1
- package/dist/services/issues.js +126 -5
- package/dist/services/issues.js.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.d.ts +1 -0
- package/dist/services/knowledge-portability/organization-skills.d.ts.map +1 -1
- package/dist/services/knowledge-portability/organization-skills.js +3 -2
- package/dist/services/knowledge-portability/organization-skills.js.map +1 -1
- package/dist/services/messenger.d.ts +5 -0
- package/dist/services/messenger.d.ts.map +1 -1
- package/dist/services/messenger.js +147 -9
- package/dist/services/messenger.js.map +1 -1
- package/dist/services/organization-workspace-browser.d.ts.map +1 -1
- package/dist/services/organization-workspace-browser.js +64 -9
- package/dist/services/organization-workspace-browser.js.map +1 -1
- package/dist/services/orgs.d.ts +1 -1
- package/dist/services/plugin-registry.d.ts +4 -4
- package/dist/services/projects.d.ts +1 -1
- package/dist/services/runtime-kernel/heartbeat.d.ts.map +1 -1
- package/dist/services/runtime-kernel/heartbeat.js +567 -29
- package/dist/services/runtime-kernel/heartbeat.js.map +1 -1
- package/dist/services/secrets.d.ts +5 -5
- package/dist/services/workspace-backups.d.ts.map +1 -1
- package/dist/services/workspace-backups.js +6 -0
- package/dist/services/workspace-backups.js.map +1 -1
- package/dist/services/workspace-runtime.d.ts.map +1 -1
- package/dist/services/workspace-runtime.js +2 -0
- package/dist/services/workspace-runtime.js.map +1 -1
- package/package.json +13 -13
- package/resources/bundled-skills/rudder/SKILL.md +72 -7
- package/resources/bundled-skills/rudder/references/cli-reference.md +34 -9
- package/resources/bundled-skills/rudder/references/organization-skills.md +12 -7
- package/resources/bundled-skills/rudder-create-agent/references/cli-reference.md +1 -0
- package/skills/rudder/SKILL.md +72 -7
- package/skills/rudder/references/cli-reference.md +34 -9
- package/skills/rudder/references/organization-skills.md +12 -7
- package/skills/rudder-create-agent/references/cli-reference.md +1 -0
- package/ui-dist/assets/{_basePickBy-aX2f6dVl.js → _basePickBy-3Hg7N37c.js} +1 -1
- package/ui-dist/assets/{_baseUniq-BYwL7heN.js → _baseUniq-Bvy8WJh0.js} +1 -1
- package/ui-dist/assets/{arc-BG9f5pwY.js → arc-DrmvGX4U.js} +1 -1
- package/ui-dist/assets/{architectureDiagram-2XIMDMQ5-BFFQoJJ1.js → architectureDiagram-2XIMDMQ5-vbevcV-8.js} +1 -1
- package/ui-dist/assets/{blockDiagram-WCTKOSBZ-Bvx1IB1z.js → blockDiagram-WCTKOSBZ-DvupMRN9.js} +1 -1
- package/ui-dist/assets/{c4Diagram-IC4MRINW-DJbCE4sh.js → c4Diagram-IC4MRINW-CbsNVA8e.js} +1 -1
- package/ui-dist/assets/channel-DhW0A-FV.js +1 -0
- package/ui-dist/assets/{chunk-4BX2VUAB-BOVbLFsN.js → chunk-4BX2VUAB-BL4OUqNV.js} +1 -1
- package/ui-dist/assets/{chunk-55IACEB6-D5pKj6S9.js → chunk-55IACEB6-DFwq2ebc.js} +1 -1
- package/ui-dist/assets/{chunk-FMBD7UC4-OY5xuJeR.js → chunk-FMBD7UC4-Cyl6kF9G.js} +1 -1
- package/ui-dist/assets/{chunk-JSJVCQXG-C5X67KZg.js → chunk-JSJVCQXG-v4mfLtsY.js} +1 -1
- package/ui-dist/assets/{chunk-KX2RTZJC-C-4PZ9Q_.js → chunk-KX2RTZJC-Bfg48g5k.js} +1 -1
- package/ui-dist/assets/{chunk-NQ4KR5QH-XysPlqPj.js → chunk-NQ4KR5QH-BcSdbequ.js} +1 -1
- package/ui-dist/assets/{chunk-QZHKN3VN-B5wEbFHo.js → chunk-QZHKN3VN-BT8QI712.js} +1 -1
- package/ui-dist/assets/{chunk-WL4C6EOR-BanwYFa2.js → chunk-WL4C6EOR-CqH2or9g.js} +1 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-Bw6kzUsz.js +1 -0
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-Bw6kzUsz.js +1 -0
- package/ui-dist/assets/clone-Luak8Fsn.js +1 -0
- package/ui-dist/assets/{cose-bilkent-S5V4N54A-Cd4q2swD.js → cose-bilkent-S5V4N54A-CLH06Lnz.js} +1 -1
- package/ui-dist/assets/{dagre-KLK3FWXG-B_VyOhf3.js → dagre-KLK3FWXG-DxNQPDBj.js} +1 -1
- package/ui-dist/assets/{diagram-E7M64L7V-BRoG4Mz6.js → diagram-E7M64L7V-BOcSeWh0.js} +1 -1
- package/ui-dist/assets/{diagram-IFDJBPK2-CRU_A9p9.js → diagram-IFDJBPK2-DXyaFKVr.js} +1 -1
- package/ui-dist/assets/{diagram-P4PSJMXO-BYSQDbfb.js → diagram-P4PSJMXO-DhY_ls3C.js} +1 -1
- package/ui-dist/assets/{erDiagram-INFDFZHY-v5j1kyWr.js → erDiagram-INFDFZHY-QtL5Yt_b.js} +1 -1
- package/ui-dist/assets/{flowDiagram-PKNHOUZH-C06ZQgTj.js → flowDiagram-PKNHOUZH-BYqyaowc.js} +1 -1
- package/ui-dist/assets/{ganttDiagram-A5KZAMGK-Dw9p5nQ1.js → ganttDiagram-A5KZAMGK-D4xd7J_z.js} +1 -1
- package/ui-dist/assets/{gitGraphDiagram-K3NZZRJ6-CrpXRIaP.js → gitGraphDiagram-K3NZZRJ6-Co9xqKNH.js} +1 -1
- package/ui-dist/assets/{graph-ClTUmULf.js → graph-DEC7S98H.js} +1 -1
- package/ui-dist/assets/{index-sLGLHxIu.js → index-4_gJOU3u.js} +1 -1
- package/ui-dist/assets/{index-Cr7n11UG.js → index-B8QjK4Xd.js} +1 -1
- package/ui-dist/assets/index-BLDnKx7N.js +1478 -0
- package/ui-dist/assets/{index-DxzAgTWd.js → index-BX6QyxsL.js} +1 -1
- package/ui-dist/assets/{index-CIr7H9OI.js → index-BZGiyL9p.js} +1 -1
- package/ui-dist/assets/{index-DK13xhRv.js → index-BelfAyHh.js} +1 -1
- package/ui-dist/assets/index-BisI78wU.css +1 -0
- package/ui-dist/assets/{index-D-6z8wxx.js → index-Bm86s0IY.js} +1 -1
- package/ui-dist/assets/{index-T81awgzh.js → index-Bz0jEwWG.js} +1 -1
- package/ui-dist/assets/{index-CqYInp-c.js → index-CFANc8oH.js} +1 -1
- package/ui-dist/assets/{index-Btwy7Cp-.js → index-CIAMqUzr.js} +1 -1
- package/ui-dist/assets/{index-L6M3nVxh.js → index-ClrueuiI.js} +1 -1
- package/ui-dist/assets/{index-C_BTFRTX.js → index-CpxwEuIg.js} +1 -1
- package/ui-dist/assets/{index-CQWmziMF.js → index-D1ZkASZY.js} +1 -1
- package/ui-dist/assets/{index-DWFMs9uk.js → index-DUP0i_Iv.js} +1 -1
- package/ui-dist/assets/{index-BVfM9ax8.js → index-DawkXomB.js} +1 -1
- package/ui-dist/assets/{index-DNlWBtHa.js → index-DxchV0Z7.js} +1 -1
- package/ui-dist/assets/{index-DkDkjZ-D.js → index-Dzd88G_H.js} +1 -1
- package/ui-dist/assets/{index-BvGogi9q.js → index-SklGX83C.js} +1 -1
- package/ui-dist/assets/{index-Bpc2gRVo.js → index-_xX3B4n0.js} +1 -1
- package/ui-dist/assets/{index-_x9smX4T.js → index-bVqVfFu5.js} +1 -1
- package/ui-dist/assets/{index-DAhPD1Ss.js → index-eIjkqSkc.js} +1 -1
- package/ui-dist/assets/{index-4uxadHo5.js → index-mIrYeZR2.js} +1 -1
- package/ui-dist/assets/{index-BYC_xlrx.js → index-xg2FQeSA.js} +1 -1
- package/ui-dist/assets/{infoDiagram-LFFYTUFH-BA3VxOIU.js → infoDiagram-LFFYTUFH-BQ0qsBJ6.js} +1 -1
- package/ui-dist/assets/{ishikawaDiagram-PHBUUO56-DGrizi0S.js → ishikawaDiagram-PHBUUO56-B1u2RAnY.js} +1 -1
- package/ui-dist/assets/{journeyDiagram-4ABVD52K-6ey34a7e.js → journeyDiagram-4ABVD52K-Dv5wJGwT.js} +1 -1
- package/ui-dist/assets/{kanban-definition-K7BYSVSG-CwNnmsam.js → kanban-definition-K7BYSVSG-CJOykCsT.js} +1 -1
- package/ui-dist/assets/{layout-buNxvllr.js → layout-BDcM6t-f.js} +1 -1
- package/ui-dist/assets/{linear-BPWhxaRl.js → linear-B9Sm5Y96.js} +1 -1
- package/ui-dist/assets/{mermaid.core-Cajx0s-z.js → mermaid.core-lZPaf_Ix.js} +4 -4
- package/ui-dist/assets/{mindmap-definition-YRQLILUH-Bf5InEp-.js → mindmap-definition-YRQLILUH-Cu4HfP8K.js} +1 -1
- package/ui-dist/assets/{pieDiagram-SKSYHLDU-CZFz7NWC.js → pieDiagram-SKSYHLDU-B_v-Vluc.js} +1 -1
- package/ui-dist/assets/{quadrantDiagram-337W2JSQ-XBmKVoc9.js → quadrantDiagram-337W2JSQ-BU1ZwGcS.js} +1 -1
- package/ui-dist/assets/{requirementDiagram-Z7DCOOCP-BkgdDv0H.js → requirementDiagram-Z7DCOOCP-DBOqB50G.js} +1 -1
- package/ui-dist/assets/{sankeyDiagram-WA2Y5GQK-CASFR28i.js → sankeyDiagram-WA2Y5GQK-CsXDIOlq.js} +1 -1
- package/ui-dist/assets/{sequenceDiagram-2WXFIKYE-BzY7LMRv.js → sequenceDiagram-2WXFIKYE-Cmgr7vKy.js} +1 -1
- package/ui-dist/assets/{stateDiagram-RAJIS63D-C9UMSk36.js → stateDiagram-RAJIS63D-Bd0uRbWd.js} +1 -1
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-qGaY7iN1.js +1 -0
- package/ui-dist/assets/{timeline-definition-YZTLITO2-D6m4R4xe.js → timeline-definition-YZTLITO2-B9OfCgYQ.js} +1 -1
- package/ui-dist/assets/{treemap-KZPCXAKY-7V9mnT8T.js → treemap-KZPCXAKY-FWWMNo03.js} +1 -1
- package/ui-dist/assets/{vennDiagram-LZ73GAT5-Ci-sfAyq.js → vennDiagram-LZ73GAT5-CGs3T7cn.js} +1 -1
- package/ui-dist/assets/{xychartDiagram-JWTSCODW-BayXhRSu.js → xychartDiagram-JWTSCODW-BJ6DrP1k.js} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/channel-ClX7n84B.js +0 -1
- package/ui-dist/assets/classDiagram-VBA2DB6C-DvWbsnVz.js +0 -1
- package/ui-dist/assets/classDiagram-v2-RAHNMMFH-DvWbsnVz.js +0 -1
- package/ui-dist/assets/clone-Dla3A8ZA.js +0 -1
- package/ui-dist/assets/index-CSANx6ee.css +0 -1
- package/ui-dist/assets/index-DCa9-Sy-.js +0 -1439
- package/ui-dist/assets/stateDiagram-v2-FVOUBMTO-DWVhbAdj.js +0 -1
package/dist/routes/chats.js
CHANGED
|
@@ -3,14 +3,14 @@ import { Router } from "express";
|
|
|
3
3
|
import multer from "multer";
|
|
4
4
|
import { addChatMessageSchema, updateChatConversationUserStateSchema, convertChatToIssueSchema, createChatAttachmentMetadataSchema, createChatContextLinkSchema, createChatConversationSchema, resolveChatOperationProposalSchema, setChatProjectContextSchema, updateChatConversationSchema, } from "@rudderhq/shared";
|
|
5
5
|
import { isAllowedContentType, MAX_ATTACHMENT_BYTES } from "../attachment-types.js";
|
|
6
|
-
import { HttpError } from "../errors.js";
|
|
6
|
+
import { forbidden, HttpError, unauthorized } from "../errors.js";
|
|
7
7
|
import { observeExecutionEvent, updateExecutionObservation, updateExecutionTraceIO, withExecutionObservation, } from "../langfuse.js";
|
|
8
8
|
import { emitExecutionTranscriptTree } from "../langfuse-transcript.js";
|
|
9
9
|
import { validate } from "../middleware/validate.js";
|
|
10
10
|
import { logger } from "../middleware/logger.js";
|
|
11
11
|
import { ChatAssistantStreamError, chatAssistantService, } from "../services/chat-assistant.js";
|
|
12
|
-
import { cancelActiveChatGeneration, claimChatGeneration } from "../services/chat-generation-locks.js";
|
|
13
|
-
import { agentService, chatService, operatorProfileService, organizationService, goalService, issueService, logActivity, projectService, } from "../services/index.js";
|
|
12
|
+
import { cancelActiveChatGeneration, claimChatGeneration, hasActiveChatGeneration } from "../services/chat-generation-locks.js";
|
|
13
|
+
import { accessService, agentService, chatService, operatorProfileService, organizationService, goalService, issueService, logActivity, projectService, } from "../services/index.js";
|
|
14
14
|
import { summarizeRuntimeSkillsForTrace } from "../services/runtime-trace-metadata.js";
|
|
15
15
|
import { assertBoard, assertCompanyAccess, getActorInfo } from "./authz.js";
|
|
16
16
|
export function chatRoutes(db, storage) {
|
|
@@ -21,6 +21,7 @@ export function chatRoutes(db, storage) {
|
|
|
21
21
|
const projectsSvc = projectService(db);
|
|
22
22
|
const agentsSvc = agentService(db);
|
|
23
23
|
const goalsSvc = goalService(db);
|
|
24
|
+
const access = accessService(db);
|
|
24
25
|
const assistantSvc = chatAssistantService(db);
|
|
25
26
|
const operatorProfiles = operatorProfileService(db);
|
|
26
27
|
const upload = multer({
|
|
@@ -84,6 +85,36 @@ export function chatRoutes(db, storage) {
|
|
|
84
85
|
assertBoard(req);
|
|
85
86
|
return req.actor.userId ?? "local-board";
|
|
86
87
|
}
|
|
88
|
+
function canCreateAgentsLegacy(agent) {
|
|
89
|
+
if (agent.role === "ceo")
|
|
90
|
+
return true;
|
|
91
|
+
if (!agent.permissions || typeof agent.permissions !== "object")
|
|
92
|
+
return false;
|
|
93
|
+
return Boolean(agent.permissions.canCreateAgents);
|
|
94
|
+
}
|
|
95
|
+
async function assertCanAssignTasks(req, orgId) {
|
|
96
|
+
assertCompanyAccess(req, orgId);
|
|
97
|
+
if (req.actor.type === "board") {
|
|
98
|
+
if (req.actor.source === "local_implicit" || req.actor.isInstanceAdmin)
|
|
99
|
+
return;
|
|
100
|
+
const allowed = await access.canUser(orgId, req.actor.userId, "tasks:assign");
|
|
101
|
+
if (!allowed)
|
|
102
|
+
throw forbidden("Missing permission: tasks:assign");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (req.actor.type === "agent") {
|
|
106
|
+
if (!req.actor.agentId)
|
|
107
|
+
throw forbidden("Agent authentication required");
|
|
108
|
+
const allowedByGrant = await access.hasPermission(orgId, "agent", req.actor.agentId, "tasks:assign");
|
|
109
|
+
if (allowedByGrant)
|
|
110
|
+
return;
|
|
111
|
+
const actorAgent = await agentsSvc.getById(req.actor.agentId);
|
|
112
|
+
if (actorAgent && actorAgent.orgId === orgId && canCreateAgentsLegacy(actorAgent))
|
|
113
|
+
return;
|
|
114
|
+
throw forbidden("Missing permission: tasks:assign");
|
|
115
|
+
}
|
|
116
|
+
throw unauthorized();
|
|
117
|
+
}
|
|
87
118
|
function buildChatObservabilityContext(conversation, input) {
|
|
88
119
|
return {
|
|
89
120
|
surface: input.surface ?? "chat_action",
|
|
@@ -153,6 +184,11 @@ export function chatRoutes(db, storage) {
|
|
|
153
184
|
eventType: typeof systemPayload?.eventType === "string" ? systemPayload.eventType : null,
|
|
154
185
|
};
|
|
155
186
|
}
|
|
187
|
+
function modelTurnInputFromInvocationMeta(invocationMeta) {
|
|
188
|
+
return typeof invocationMeta.prompt === "string" && invocationMeta.prompt.trim().length > 0
|
|
189
|
+
? invocationMeta.prompt
|
|
190
|
+
: undefined;
|
|
191
|
+
}
|
|
156
192
|
function buildChatTraceInput(input, invocationMeta) {
|
|
157
193
|
return {
|
|
158
194
|
conversationId: input.conversationId,
|
|
@@ -367,25 +403,121 @@ export function chatRoutes(db, storage) {
|
|
|
367
403
|
? structuredPayload.issueProposal
|
|
368
404
|
: structuredPayload;
|
|
369
405
|
}
|
|
370
|
-
|
|
406
|
+
function proposalAssignsOrReviewsIssue(proposal) {
|
|
407
|
+
if (!proposal)
|
|
408
|
+
return false;
|
|
409
|
+
return Boolean((typeof proposal.assigneeAgentId === "string" && proposal.assigneeAgentId.trim().length > 0)
|
|
410
|
+
|| (typeof proposal.assigneeUserId === "string" && proposal.assigneeUserId.trim().length > 0)
|
|
411
|
+
|| (typeof proposal.reviewerAgentId === "string" && proposal.reviewerAgentId.trim().length > 0)
|
|
412
|
+
|| (typeof proposal.reviewerUserId === "string" && proposal.reviewerUserId.trim().length > 0));
|
|
413
|
+
}
|
|
414
|
+
async function proposedIssuePayloadForConversion(conversationId, input) {
|
|
415
|
+
if (input.proposal)
|
|
416
|
+
return proposedIssuePayload(input.proposal);
|
|
417
|
+
if (input.messageId) {
|
|
418
|
+
const message = await svc.getMessage(conversationId, input.messageId);
|
|
419
|
+
return proposedIssuePayload(message?.structuredPayload ?? null);
|
|
420
|
+
}
|
|
421
|
+
const messages = await svc.listMessages(conversationId);
|
|
422
|
+
const message = [...messages].reverse().find((entry) => entry.kind === "issue_proposal");
|
|
423
|
+
return proposedIssuePayload(message?.structuredPayload ?? null);
|
|
424
|
+
}
|
|
425
|
+
async function assertCanConvertIssueProposal(req, conversation, input) {
|
|
426
|
+
const proposal = await proposedIssuePayloadForConversion(conversation.id, input);
|
|
427
|
+
if (proposalAssignsOrReviewsIssue(proposal)) {
|
|
428
|
+
await assertCanAssignTasks(req, conversation.orgId);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
function proposedPlanDocumentPayload(structuredPayload) {
|
|
432
|
+
if (!structuredPayload)
|
|
433
|
+
return null;
|
|
434
|
+
const rawDocument = structuredPayload.planDocument
|
|
435
|
+
&& typeof structuredPayload.planDocument === "object"
|
|
436
|
+
&& !Array.isArray(structuredPayload.planDocument)
|
|
437
|
+
? structuredPayload.planDocument
|
|
438
|
+
: structuredPayload.plan && typeof structuredPayload.plan === "object" && !Array.isArray(structuredPayload.plan)
|
|
439
|
+
? structuredPayload.plan
|
|
440
|
+
: null;
|
|
441
|
+
return rawDocument ? rawDocument : null;
|
|
442
|
+
}
|
|
443
|
+
async function persistAssistantReply(req, conversation, actor, assistantReply, turnContext, transcript = [], replyingAgentId = assistantReply.replyingAgentId ?? chatReplyingAgentId(conversation), existingMessageId) {
|
|
371
444
|
const createdMessages = [];
|
|
372
445
|
const { chatTurnId, turnVariant } = turnContext;
|
|
446
|
+
const attachGeneratedFiles = async (message, generatedAttachments) => {
|
|
447
|
+
if (!generatedAttachments || generatedAttachments.length === 0)
|
|
448
|
+
return message;
|
|
449
|
+
const attachments = [];
|
|
450
|
+
for (const generated of generatedAttachments) {
|
|
451
|
+
if (generated.body.length > MAX_ATTACHMENT_BYTES) {
|
|
452
|
+
throw new ChatAssistantStreamError(`Generated attachment exceeds ${MAX_ATTACHMENT_BYTES} bytes`, assistantReply.body, generatedAttachments);
|
|
453
|
+
}
|
|
454
|
+
const stored = await storage.putFile({
|
|
455
|
+
orgId: conversation.orgId,
|
|
456
|
+
namespace: `chats/${conversation.id}/generated`,
|
|
457
|
+
originalFilename: generated.originalFilename,
|
|
458
|
+
contentType: generated.contentType,
|
|
459
|
+
body: generated.body,
|
|
460
|
+
});
|
|
461
|
+
const attachment = await svc.createAttachment({
|
|
462
|
+
orgId: conversation.orgId,
|
|
463
|
+
conversationId: conversation.id,
|
|
464
|
+
messageId: message.id,
|
|
465
|
+
provider: stored.provider,
|
|
466
|
+
objectKey: stored.objectKey,
|
|
467
|
+
contentType: stored.contentType,
|
|
468
|
+
byteSize: stored.byteSize,
|
|
469
|
+
sha256: stored.sha256,
|
|
470
|
+
originalFilename: stored.originalFilename,
|
|
471
|
+
createdByAgentId: replyingAgentId,
|
|
472
|
+
createdByUserId: null,
|
|
473
|
+
});
|
|
474
|
+
attachments.push(attachment);
|
|
475
|
+
}
|
|
476
|
+
return {
|
|
477
|
+
...message,
|
|
478
|
+
attachments: [...(message.attachments ?? []), ...attachments],
|
|
479
|
+
};
|
|
480
|
+
};
|
|
481
|
+
const saveAssistantMessage = async (input) => {
|
|
482
|
+
if (existingMessageId) {
|
|
483
|
+
const updated = await svc.updateMessage(conversation.id, existingMessageId, {
|
|
484
|
+
kind: input.kind,
|
|
485
|
+
status: "completed",
|
|
486
|
+
body: input.body,
|
|
487
|
+
structuredPayload: input.structuredPayload ?? null,
|
|
488
|
+
transcript,
|
|
489
|
+
approvalId: input.approvalId ?? null,
|
|
490
|
+
replyingAgentId,
|
|
491
|
+
});
|
|
492
|
+
if (updated)
|
|
493
|
+
return updated;
|
|
494
|
+
}
|
|
495
|
+
return svc.addMessage(conversation.id, {
|
|
496
|
+
orgId: conversation.orgId,
|
|
497
|
+
role: "assistant",
|
|
498
|
+
kind: input.kind,
|
|
499
|
+
body: input.body,
|
|
500
|
+
structuredPayload: input.structuredPayload ?? null,
|
|
501
|
+
transcript,
|
|
502
|
+
approvalId: input.approvalId ?? null,
|
|
503
|
+
replyingAgentId,
|
|
504
|
+
chatTurnId,
|
|
505
|
+
turnVariant,
|
|
506
|
+
});
|
|
507
|
+
};
|
|
373
508
|
if (assistantReply.kind === "issue_proposal") {
|
|
374
509
|
const issueProposalStructuredPayload = withDefaultIssueProposalAssignee(assistantReply.structuredPayload, await defaultIssueAssigneeAgentId(conversation));
|
|
375
|
-
const shouldAutoCreateIssue = conversation.planMode
|
|
510
|
+
const shouldAutoCreateIssue = !conversation.planMode && conversation.issueCreationMode === "auto_create";
|
|
376
511
|
if (shouldAutoCreateIssue) {
|
|
377
|
-
const proposalMessage = await
|
|
378
|
-
orgId: conversation.orgId,
|
|
379
|
-
role: "assistant",
|
|
512
|
+
const proposalMessage = await saveAssistantMessage({
|
|
380
513
|
kind: "issue_proposal",
|
|
381
514
|
body: assistantReply.body,
|
|
382
515
|
structuredPayload: issueProposalStructuredPayload,
|
|
383
|
-
transcript,
|
|
384
|
-
replyingAgentId,
|
|
385
|
-
chatTurnId,
|
|
386
|
-
turnVariant,
|
|
387
516
|
});
|
|
388
|
-
createdMessages.push(proposalMessage);
|
|
517
|
+
createdMessages.push(await attachGeneratedFiles(proposalMessage, assistantReply.generatedAttachments));
|
|
518
|
+
await assertCanConvertIssueProposal(req, conversation, {
|
|
519
|
+
proposal: issueProposalStructuredPayload,
|
|
520
|
+
});
|
|
389
521
|
const issue = await svc.convertToIssue(conversation.id, {
|
|
390
522
|
actorUserId: actor.actorType === "user" ? actor.actorId : null,
|
|
391
523
|
messageId: proposalMessage.id,
|
|
@@ -414,32 +546,28 @@ export function chatRoutes(db, storage) {
|
|
|
414
546
|
details: {
|
|
415
547
|
issueId: issue.id,
|
|
416
548
|
issueIdentifier: issue.identifier,
|
|
417
|
-
source:
|
|
549
|
+
source: "auto_create",
|
|
418
550
|
},
|
|
419
551
|
});
|
|
420
552
|
return createdMessages;
|
|
421
553
|
}
|
|
554
|
+
const planDocument = proposedPlanDocumentPayload(issueProposalStructuredPayload);
|
|
422
555
|
const approval = await svc.createProposalApproval(conversation.orgId, {
|
|
423
556
|
type: "chat_issue_creation",
|
|
424
557
|
requestedByUserId: actor.actorType === "user" ? actor.actorId : null,
|
|
425
558
|
payload: {
|
|
426
559
|
chatConversationId: conversation.id,
|
|
427
560
|
proposedIssue: proposedIssuePayload(issueProposalStructuredPayload),
|
|
561
|
+
...(planDocument ? { planDocument } : {}),
|
|
428
562
|
},
|
|
429
563
|
});
|
|
430
|
-
const proposalMessage = await
|
|
431
|
-
orgId: conversation.orgId,
|
|
432
|
-
role: "assistant",
|
|
564
|
+
const proposalMessage = await saveAssistantMessage({
|
|
433
565
|
kind: "issue_proposal",
|
|
434
566
|
body: assistantReply.body,
|
|
435
567
|
structuredPayload: issueProposalStructuredPayload,
|
|
436
|
-
transcript,
|
|
437
568
|
approvalId: approval.id,
|
|
438
|
-
replyingAgentId,
|
|
439
|
-
chatTurnId,
|
|
440
|
-
turnVariant,
|
|
441
569
|
});
|
|
442
|
-
createdMessages.push(proposalMessage);
|
|
570
|
+
createdMessages.push(await attachGeneratedFiles(proposalMessage, assistantReply.generatedAttachments));
|
|
443
571
|
return createdMessages;
|
|
444
572
|
}
|
|
445
573
|
if (assistantReply.kind === "operation_proposal") {
|
|
@@ -455,9 +583,7 @@ export function chatRoutes(db, storage) {
|
|
|
455
583
|
: assistantReply.structuredPayload,
|
|
456
584
|
},
|
|
457
585
|
});
|
|
458
|
-
const proposalMessage = await
|
|
459
|
-
orgId: conversation.orgId,
|
|
460
|
-
role: "assistant",
|
|
586
|
+
const proposalMessage = await saveAssistantMessage({
|
|
461
587
|
kind: "operation_proposal",
|
|
462
588
|
body: assistantReply.body,
|
|
463
589
|
structuredPayload: {
|
|
@@ -469,41 +595,80 @@ export function chatRoutes(db, storage) {
|
|
|
469
595
|
decidedAt: null,
|
|
470
596
|
},
|
|
471
597
|
},
|
|
472
|
-
transcript,
|
|
473
598
|
approvalId: approval.id,
|
|
474
|
-
replyingAgentId,
|
|
475
|
-
chatTurnId,
|
|
476
|
-
turnVariant,
|
|
477
599
|
});
|
|
478
|
-
createdMessages.push(proposalMessage);
|
|
600
|
+
createdMessages.push(await attachGeneratedFiles(proposalMessage, assistantReply.generatedAttachments));
|
|
479
601
|
return createdMessages;
|
|
480
602
|
}
|
|
481
|
-
const assistantMessage = await
|
|
482
|
-
orgId: conversation.orgId,
|
|
483
|
-
role: "assistant",
|
|
603
|
+
const assistantMessage = await saveAssistantMessage({
|
|
484
604
|
kind: assistantReply.kind === "routing_suggestion" ? "routing_suggestion" : "message",
|
|
485
605
|
body: assistantReply.body,
|
|
486
606
|
structuredPayload: assistantReply.structuredPayload,
|
|
487
|
-
transcript,
|
|
488
|
-
replyingAgentId,
|
|
489
|
-
chatTurnId,
|
|
490
|
-
turnVariant,
|
|
491
607
|
});
|
|
492
|
-
createdMessages.push(assistantMessage);
|
|
608
|
+
createdMessages.push(await attachGeneratedFiles(assistantMessage, assistantReply.generatedAttachments));
|
|
493
609
|
return createdMessages;
|
|
494
610
|
}
|
|
495
|
-
async function
|
|
611
|
+
async function attachGeneratedFilesToPartialMessage(conversation, message, generatedAttachments, replyingAgentId) {
|
|
612
|
+
if (!message || !generatedAttachments || generatedAttachments.length === 0)
|
|
613
|
+
return message;
|
|
614
|
+
const attachments = [];
|
|
615
|
+
for (const generated of generatedAttachments) {
|
|
616
|
+
if (generated.body.length > MAX_ATTACHMENT_BYTES)
|
|
617
|
+
continue;
|
|
618
|
+
const stored = await storage.putFile({
|
|
619
|
+
orgId: conversation.orgId,
|
|
620
|
+
namespace: `chats/${conversation.id}/generated`,
|
|
621
|
+
originalFilename: generated.originalFilename,
|
|
622
|
+
contentType: generated.contentType,
|
|
623
|
+
body: generated.body,
|
|
624
|
+
});
|
|
625
|
+
const attachment = await svc.createAttachment({
|
|
626
|
+
orgId: conversation.orgId,
|
|
627
|
+
conversationId: conversation.id,
|
|
628
|
+
messageId: message.id,
|
|
629
|
+
provider: stored.provider,
|
|
630
|
+
objectKey: stored.objectKey,
|
|
631
|
+
contentType: stored.contentType,
|
|
632
|
+
byteSize: stored.byteSize,
|
|
633
|
+
sha256: stored.sha256,
|
|
634
|
+
originalFilename: stored.originalFilename,
|
|
635
|
+
createdByAgentId: replyingAgentId,
|
|
636
|
+
createdByUserId: null,
|
|
637
|
+
});
|
|
638
|
+
attachments.push(attachment);
|
|
639
|
+
}
|
|
640
|
+
return {
|
|
641
|
+
...message,
|
|
642
|
+
attachments: [...(message.attachments ?? []), ...attachments],
|
|
643
|
+
};
|
|
644
|
+
}
|
|
645
|
+
async function persistPartialAssistantMessage(conversation, body, status, turnContext, transcript = [], replyingAgentId = chatReplyingAgentId(conversation), existingMessageId) {
|
|
496
646
|
const trimmed = body.trim();
|
|
497
|
-
|
|
647
|
+
const fallbackBody = status === "stopped"
|
|
648
|
+
? "Chat run stopped before a final reply. Continue the conversation to resume from the preserved context."
|
|
649
|
+
: "Chat run failed before a final reply. Continue the conversation to resume from the preserved context.";
|
|
650
|
+
const durableBody = trimmed || (transcript.length > 0 ? fallbackBody : "");
|
|
651
|
+
if (!durableBody)
|
|
498
652
|
return null;
|
|
499
653
|
const chatTurnId = turnContext?.chatTurnId ?? randomUUID();
|
|
500
654
|
const turnVariant = turnContext?.turnVariant ?? 0;
|
|
655
|
+
if (existingMessageId) {
|
|
656
|
+
const updated = await svc.updateMessage(conversation.id, existingMessageId, {
|
|
657
|
+
kind: "message",
|
|
658
|
+
status,
|
|
659
|
+
body: durableBody,
|
|
660
|
+
transcript,
|
|
661
|
+
replyingAgentId,
|
|
662
|
+
});
|
|
663
|
+
if (updated)
|
|
664
|
+
return updated;
|
|
665
|
+
}
|
|
501
666
|
const message = await svc.addMessage(conversation.id, {
|
|
502
667
|
orgId: conversation.orgId,
|
|
503
668
|
role: "assistant",
|
|
504
669
|
kind: "message",
|
|
505
670
|
status,
|
|
506
|
-
body:
|
|
671
|
+
body: durableBody,
|
|
507
672
|
transcript,
|
|
508
673
|
replyingAgentId,
|
|
509
674
|
chatTurnId,
|
|
@@ -524,8 +689,9 @@ export function chatRoutes(db, storage) {
|
|
|
524
689
|
const status = statusParam === "resolved" || statusParam === "archived" || statusParam === "all"
|
|
525
690
|
? statusParam
|
|
526
691
|
: "active";
|
|
692
|
+
const q = typeof req.query.q === "string" ? req.query.q : undefined;
|
|
527
693
|
const userId = req.actor.type === "board" ? (req.actor.userId ?? "local-board") : null;
|
|
528
|
-
const conversations = await svc.list(orgId, { status }, userId);
|
|
694
|
+
const conversations = await svc.list(orgId, { status, q }, userId);
|
|
529
695
|
res.json(await assistantSvc.enrichConversations(conversations));
|
|
530
696
|
});
|
|
531
697
|
router.post("/orgs/:orgId/chats", validate(createChatConversationSchema), async (req, res) => {
|
|
@@ -538,6 +704,13 @@ export function chatRoutes(db, storage) {
|
|
|
538
704
|
}
|
|
539
705
|
const contextLinks = req.body.contextLinks ?? [];
|
|
540
706
|
await assertContextLinksBelongToCompany(orgId, contextLinks);
|
|
707
|
+
if (req.body.preferredAgentId) {
|
|
708
|
+
const agent = await agentsSvc.getById(req.body.preferredAgentId);
|
|
709
|
+
if (!agent || agent.orgId !== orgId) {
|
|
710
|
+
res.status(422).json({ error: "Preferred agent must belong to the same organization" });
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
541
714
|
const actor = getActorInfo(req);
|
|
542
715
|
const conversation = await svc.create(orgId, {
|
|
543
716
|
title: req.body.title,
|
|
@@ -629,6 +802,9 @@ export function chatRoutes(db, storage) {
|
|
|
629
802
|
res.status(404).json({ error: "Chat conversation not found" });
|
|
630
803
|
return;
|
|
631
804
|
}
|
|
805
|
+
if (!hasActiveChatGeneration(conversation.id)) {
|
|
806
|
+
await svc.markInterruptedStreamingMessages(conversation.id);
|
|
807
|
+
}
|
|
632
808
|
const messages = await svc.listMessages(conversation.id);
|
|
633
809
|
res.json(messages);
|
|
634
810
|
});
|
|
@@ -678,6 +854,7 @@ export function chatRoutes(db, storage) {
|
|
|
678
854
|
const assistantInput = await loadAssistantInput(conversation, actor);
|
|
679
855
|
const transcript = [];
|
|
680
856
|
const observedTranscript = [];
|
|
857
|
+
let modelTurnInput;
|
|
681
858
|
let fallbackOutput = null;
|
|
682
859
|
let finalChatOutput = null;
|
|
683
860
|
let finalChatStatus = "completed";
|
|
@@ -685,6 +862,7 @@ export function chatRoutes(db, storage) {
|
|
|
685
862
|
const streamed = await assistantSvc.streamChatAssistantReply({
|
|
686
863
|
...assistantInput,
|
|
687
864
|
onInvocationMeta: async (meta) => {
|
|
865
|
+
modelTurnInput = modelTurnInputFromInvocationMeta(meta);
|
|
688
866
|
currentChatTraceInput = buildChatTraceInput(traceInputBase, meta);
|
|
689
867
|
mergeChatInvocationTraceMetadata(chatObservation, meta);
|
|
690
868
|
updateExecutionObservation(observation, chatObservation, {
|
|
@@ -704,7 +882,7 @@ export function chatRoutes(db, storage) {
|
|
|
704
882
|
finalChatStatus = "failed";
|
|
705
883
|
throw new Error("Chat assistant reply was stopped before completion");
|
|
706
884
|
}
|
|
707
|
-
const created = await persistAssistantReply(assistantInput.conversation, actor, streamed.reply, turnContext, transcript, streamed.replyingAgentId);
|
|
885
|
+
const created = await persistAssistantReply(req, assistantInput.conversation, actor, streamed.reply, turnContext, transcript, streamed.replyingAgentId);
|
|
708
886
|
finalChatOutput = streamed.reply.body;
|
|
709
887
|
await logChatMessagesAdded(assistantInput.conversation, created, {
|
|
710
888
|
actorType: "system",
|
|
@@ -735,6 +913,7 @@ export function chatRoutes(db, storage) {
|
|
|
735
913
|
context: chatObservation,
|
|
736
914
|
parentObservation: observation,
|
|
737
915
|
transcript: observedTranscript,
|
|
916
|
+
initialTurnInput: modelTurnInput,
|
|
738
917
|
fallbackResult: fallbackOutput
|
|
739
918
|
? {
|
|
740
919
|
output: fallbackOutput,
|
|
@@ -779,6 +958,9 @@ export function chatRoutes(db, storage) {
|
|
|
779
958
|
});
|
|
780
959
|
}
|
|
781
960
|
logger.warn({ err, conversationId: conversation.id }, "chat assistant reply failed");
|
|
961
|
+
if (err instanceof HttpError) {
|
|
962
|
+
throw err;
|
|
963
|
+
}
|
|
782
964
|
res.status(502).json({
|
|
783
965
|
error: err instanceof Error ? err.message : "Chat assistant failed to respond",
|
|
784
966
|
});
|
|
@@ -837,6 +1019,42 @@ export function chatRoutes(db, storage) {
|
|
|
837
1019
|
let chatObservation = null;
|
|
838
1020
|
const transcript = [];
|
|
839
1021
|
const observedTranscript = [];
|
|
1022
|
+
let modelTurnInput;
|
|
1023
|
+
let assistantProgressMessage = null;
|
|
1024
|
+
let assistantProgressMessageId = null;
|
|
1025
|
+
let assistantDraftBody = "";
|
|
1026
|
+
const persistStreamProgress = async (progressConversation, replyingAgentId = chatReplyingAgentId(progressConversation)) => {
|
|
1027
|
+
if (!turnContextForPartial)
|
|
1028
|
+
return null;
|
|
1029
|
+
const input = {
|
|
1030
|
+
kind: "message",
|
|
1031
|
+
status: "streaming",
|
|
1032
|
+
body: assistantDraftBody,
|
|
1033
|
+
transcript,
|
|
1034
|
+
replyingAgentId,
|
|
1035
|
+
};
|
|
1036
|
+
if (assistantProgressMessage) {
|
|
1037
|
+
const updated = await svc.updateMessage(progressConversation.id, assistantProgressMessage.id, input);
|
|
1038
|
+
if (updated) {
|
|
1039
|
+
assistantProgressMessage = updated;
|
|
1040
|
+
assistantProgressMessageId = assistantProgressMessage.id;
|
|
1041
|
+
return assistantProgressMessage;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
assistantProgressMessage = await svc.addMessage(progressConversation.id, {
|
|
1045
|
+
orgId: progressConversation.orgId,
|
|
1046
|
+
role: "assistant",
|
|
1047
|
+
kind: "message",
|
|
1048
|
+
status: "streaming",
|
|
1049
|
+
body: assistantDraftBody,
|
|
1050
|
+
transcript,
|
|
1051
|
+
replyingAgentId,
|
|
1052
|
+
chatTurnId: turnContextForPartial.chatTurnId,
|
|
1053
|
+
turnVariant: turnContextForPartial.turnVariant,
|
|
1054
|
+
});
|
|
1055
|
+
assistantProgressMessageId = assistantProgressMessage.id;
|
|
1056
|
+
return assistantProgressMessage;
|
|
1057
|
+
};
|
|
840
1058
|
let clientClosed = false;
|
|
841
1059
|
const handleClosed = () => {
|
|
842
1060
|
if (clientClosed || res.writableEnded)
|
|
@@ -894,6 +1112,7 @@ export function chatRoutes(db, storage) {
|
|
|
894
1112
|
...assistantInput,
|
|
895
1113
|
abortSignal: abortController.signal,
|
|
896
1114
|
onInvocationMeta: async (meta) => {
|
|
1115
|
+
modelTurnInput = modelTurnInputFromInvocationMeta(meta);
|
|
897
1116
|
currentChatTraceInput = buildChatTraceInput(traceInputBase, meta);
|
|
898
1117
|
mergeChatInvocationTraceMetadata(chatObservation, meta);
|
|
899
1118
|
updateExecutionObservation(observation, chatObservation, {
|
|
@@ -902,12 +1121,17 @@ export function chatRoutes(db, storage) {
|
|
|
902
1121
|
updateExecutionTraceIO(observation, { input: currentChatTraceInput });
|
|
903
1122
|
},
|
|
904
1123
|
onAssistantDelta: async (delta) => {
|
|
1124
|
+
assistantDraftBody = `${assistantDraftBody}${delta}`;
|
|
1125
|
+
await persistStreamProgress(assistantInput.conversation);
|
|
1126
|
+
if (clientClosed)
|
|
1127
|
+
return;
|
|
905
1128
|
writeStreamEvent(res, {
|
|
906
1129
|
type: "assistant_delta",
|
|
907
1130
|
delta,
|
|
908
1131
|
});
|
|
909
1132
|
},
|
|
910
1133
|
onAssistantState: async (state) => {
|
|
1134
|
+
await persistStreamProgress(assistantInput.conversation);
|
|
911
1135
|
if (clientClosed)
|
|
912
1136
|
return;
|
|
913
1137
|
writeStreamEvent(res, {
|
|
@@ -917,6 +1141,7 @@ export function chatRoutes(db, storage) {
|
|
|
917
1141
|
},
|
|
918
1142
|
onTranscriptEntry: async (entry) => {
|
|
919
1143
|
transcript.push(entry);
|
|
1144
|
+
await persistStreamProgress(assistantInput.conversation);
|
|
920
1145
|
if (clientClosed)
|
|
921
1146
|
return;
|
|
922
1147
|
writeStreamEvent(res, {
|
|
@@ -931,7 +1156,7 @@ export function chatRoutes(db, storage) {
|
|
|
931
1156
|
if (streamed.outcome === "stopped") {
|
|
932
1157
|
finalChatStatus = "stopped";
|
|
933
1158
|
finalChatOutput = streamed.partialBody;
|
|
934
|
-
const stoppedMessage = await persistPartialAssistantMessage(assistantInput.conversation, streamed.partialBody, "stopped", turnContextForPartial, transcript, streamed.replyingAgentId);
|
|
1159
|
+
const stoppedMessage = await persistPartialAssistantMessage(assistantInput.conversation, streamed.partialBody, "stopped", turnContextForPartial, transcript, streamed.replyingAgentId, assistantProgressMessageId);
|
|
935
1160
|
if (stoppedMessage) {
|
|
936
1161
|
await logChatMessagesAdded(assistantInput.conversation, [stoppedMessage], {
|
|
937
1162
|
actorType: "system",
|
|
@@ -957,7 +1182,7 @@ export function chatRoutes(db, storage) {
|
|
|
957
1182
|
}
|
|
958
1183
|
return;
|
|
959
1184
|
}
|
|
960
|
-
const createdMessages = await persistAssistantReply(assistantInput.conversation, actor, streamed.reply, turnContextForPartial, transcript, streamed.replyingAgentId);
|
|
1185
|
+
const createdMessages = await persistAssistantReply(req, assistantInput.conversation, actor, streamed.reply, turnContextForPartial, transcript, streamed.replyingAgentId, assistantProgressMessageId);
|
|
961
1186
|
finalChatOutput = streamed.reply.body;
|
|
962
1187
|
await logChatMessagesAdded(assistantInput.conversation, createdMessages, {
|
|
963
1188
|
actorType: "system",
|
|
@@ -993,6 +1218,14 @@ export function chatRoutes(db, storage) {
|
|
|
993
1218
|
context: chatObservation,
|
|
994
1219
|
parentObservation: observation,
|
|
995
1220
|
transcript: observedTranscript,
|
|
1221
|
+
initialTurnInput: modelTurnInput,
|
|
1222
|
+
fallbackResult: finalChatOutput
|
|
1223
|
+
? {
|
|
1224
|
+
output: finalChatOutput,
|
|
1225
|
+
subtype: finalChatStatus,
|
|
1226
|
+
isError: finalChatStatus === "failed",
|
|
1227
|
+
}
|
|
1228
|
+
: null,
|
|
996
1229
|
});
|
|
997
1230
|
finalChatOutput = finalChatOutput ?? transcriptStats.finalOutput ?? null;
|
|
998
1231
|
}
|
|
@@ -1020,8 +1253,10 @@ export function chatRoutes(db, storage) {
|
|
|
1020
1253
|
}
|
|
1021
1254
|
catch (err) {
|
|
1022
1255
|
const partialBody = err instanceof ChatAssistantStreamError ? err.partialBody : "";
|
|
1256
|
+
const generatedAttachments = err instanceof ChatAssistantStreamError ? err.generatedAttachments : [];
|
|
1023
1257
|
const failedReplyingAgentId = chatReplyingAgentId(assistantConversationForPartial);
|
|
1024
|
-
|
|
1258
|
+
let failedMessage = await persistPartialAssistantMessage(assistantConversationForPartial ?? conversation, partialBody, "failed", turnContextForPartial, transcript, failedReplyingAgentId, assistantProgressMessageId).catch(() => null);
|
|
1259
|
+
failedMessage = await attachGeneratedFilesToPartialMessage(assistantConversationForPartial ?? conversation, failedMessage, generatedAttachments, failedReplyingAgentId).catch(() => failedMessage);
|
|
1025
1260
|
if (failedMessage && assistantConversationForPartial) {
|
|
1026
1261
|
await logChatMessagesAdded(assistantConversationForPartial, [failedMessage], {
|
|
1027
1262
|
actorType: "system",
|
|
@@ -1219,6 +1454,10 @@ export function chatRoutes(db, storage) {
|
|
|
1219
1454
|
return;
|
|
1220
1455
|
}
|
|
1221
1456
|
}
|
|
1457
|
+
await assertCanConvertIssueProposal(req, conversation, {
|
|
1458
|
+
messageId: req.body.messageId ?? null,
|
|
1459
|
+
proposal: req.body.proposal ?? null,
|
|
1460
|
+
});
|
|
1222
1461
|
const chatObservation = buildChatObservabilityContext(conversation, {
|
|
1223
1462
|
rootExecutionId: req.body.messageId ?? `chat-convert:${conversation.id}`,
|
|
1224
1463
|
trigger: "convert_to_issue",
|