@rudderhq/server 0.7.24-canary.21 → 0.7.24-canary.23

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.
Files changed (151) hide show
  1. package/dist/bootstrap/register-api-routes.d.ts.map +1 -1
  2. package/dist/bootstrap/register-api-routes.js +2 -2
  3. package/dist/bootstrap/register-api-routes.js.map +1 -1
  4. package/dist/home-paths.d.ts.map +1 -1
  5. package/dist/home-paths.js +8 -6
  6. package/dist/home-paths.js.map +1 -1
  7. package/dist/routes/approvals.d.ts +2 -1
  8. package/dist/routes/approvals.d.ts.map +1 -1
  9. package/dist/routes/approvals.js +49 -4
  10. package/dist/routes/approvals.js.map +1 -1
  11. package/dist/routes/chats.d.ts +2 -1
  12. package/dist/routes/chats.d.ts.map +1 -1
  13. package/dist/routes/chats.js +3 -3
  14. package/dist/routes/chats.js.map +1 -1
  15. package/dist/routes/chats.stream-routes.d.ts.map +1 -1
  16. package/dist/routes/chats.stream-routes.js +1 -0
  17. package/dist/routes/chats.stream-routes.js.map +1 -1
  18. package/dist/routes/organization-rust-foundation-routes.d.ts.map +1 -1
  19. package/dist/routes/organization-rust-foundation-routes.js +27 -14
  20. package/dist/routes/organization-rust-foundation-routes.js.map +1 -1
  21. package/dist/routes/orgs.d.ts +1 -1
  22. package/dist/routes/orgs.d.ts.map +1 -1
  23. package/dist/routes/orgs.js +27 -21
  24. package/dist/routes/orgs.js.map +1 -1
  25. package/dist/services/chat-organization-branding-rust-idempotency.test.d.ts +2 -0
  26. package/dist/services/chat-organization-branding-rust-idempotency.test.d.ts.map +1 -0
  27. package/dist/services/chat-organization-branding-rust-idempotency.test.js +367 -0
  28. package/dist/services/chat-organization-branding-rust-idempotency.test.js.map +1 -0
  29. package/dist/services/chat-organization-branding.d.ts +78 -0
  30. package/dist/services/chat-organization-branding.d.ts.map +1 -0
  31. package/dist/services/chat-organization-branding.js +212 -0
  32. package/dist/services/chat-organization-branding.js.map +1 -0
  33. package/dist/services/chats.d.ts +24 -6
  34. package/dist/services/chats.d.ts.map +1 -1
  35. package/dist/services/chats.js +76 -86
  36. package/dist/services/chats.js.map +1 -1
  37. package/dist/services/organization-branding-fence.d.ts +4 -3
  38. package/dist/services/organization-branding-fence.d.ts.map +1 -1
  39. package/dist/services/organization-branding-fence.js +4 -3
  40. package/dist/services/organization-branding-fence.js.map +1 -1
  41. package/dist/services/orgs.d.ts +1 -0
  42. package/dist/services/orgs.d.ts.map +1 -1
  43. package/dist/services/orgs.js +11 -2
  44. package/dist/services/orgs.js.map +1 -1
  45. package/dist/services/rust-foundation-bridge.d.ts +8 -0
  46. package/dist/services/rust-foundation-bridge.d.ts.map +1 -1
  47. package/dist/services/rust-foundation-bridge.js +203 -25
  48. package/dist/services/rust-foundation-bridge.js.map +1 -1
  49. package/dist/services/rust-foundation-bridge.test.js +183 -4
  50. package/dist/services/rust-foundation-bridge.test.js.map +1 -1
  51. package/dist/services/workspace-backup-read-bridge.d.ts +11 -0
  52. package/dist/services/workspace-backup-read-bridge.d.ts.map +1 -0
  53. package/dist/services/workspace-backup-read-bridge.js +51 -0
  54. package/dist/services/workspace-backup-read-bridge.js.map +1 -0
  55. package/dist/services/workspace-backups.d.ts +0 -1
  56. package/dist/services/workspace-backups.d.ts.map +1 -1
  57. package/dist/services/workspace-backups.js +0 -11
  58. package/dist/services/workspace-backups.js.map +1 -1
  59. package/package.json +13 -13
  60. package/resources/bundled-skills/rudder-docs/references/cli-reference.md +1 -1
  61. package/resources/native/aarch64-apple-darwin/rudder-server-foundation +0 -0
  62. package/resources/native/aarch64-pc-windows-msvc/migration-preflight.exe +0 -0
  63. package/resources/native/aarch64-pc-windows-msvc/rudder-server-foundation.exe +0 -0
  64. package/resources/native/aarch64-unknown-linux-gnu/rudder-server-foundation +0 -0
  65. package/resources/native/x86_64-apple-darwin/rudder-server-foundation +0 -0
  66. package/resources/native/x86_64-pc-windows-msvc/migration-preflight.exe +0 -0
  67. package/resources/native/x86_64-pc-windows-msvc/rudder-server-foundation.exe +0 -0
  68. package/resources/native/x86_64-unknown-linux-gnu/rudder-server-foundation +0 -0
  69. package/skills/rudder-docs/references/cli-reference.md +1 -1
  70. package/ui-dist/assets/{LibraryLiveSurface-Bs8V_pMU.js → LibraryLiveSurface-CodApFW-.js} +1 -1
  71. package/ui-dist/assets/{abnfDiagram-VRR7QNED-DneqWru9.js → abnfDiagram-VRR7QNED-BDz58rQS.js} +1 -1
  72. package/ui-dist/assets/{addon-fit-DCRNyXYv.js → addon-fit-DZOvRzIT.js} +1 -1
  73. package/ui-dist/assets/{arc-D_2I_R5Z.js → arc-C9TMZvvd.js} +1 -1
  74. package/ui-dist/assets/{architectureDiagram-ZJ3FMSHR-C0ANAv78.js → architectureDiagram-ZJ3FMSHR-D4twxoE1.js} +1 -1
  75. package/ui-dist/assets/{blockDiagram-677ZJIJ3-BqFGOix5.js → blockDiagram-677ZJIJ3-DJs0gNG4.js} +1 -1
  76. package/ui-dist/assets/{c4Diagram-LMCZKHZV-82LfEi1P.js → c4Diagram-LMCZKHZV-6oq8lq8q.js} +1 -1
  77. package/ui-dist/assets/channel-CmUGU_Az.js +1 -0
  78. package/ui-dist/assets/{chunk-2Q5K7J3B-DijvvGcl.js → chunk-2Q5K7J3B-m2MTxaYE.js} +1 -1
  79. package/ui-dist/assets/{chunk-32BRIVSS-Z09s337w.js → chunk-32BRIVSS-BYbeAqiG.js} +1 -1
  80. package/ui-dist/assets/{chunk-5VM5RSS4-DgQWkO6B.js → chunk-5VM5RSS4-Dy6Af71u.js} +1 -1
  81. package/ui-dist/assets/{chunk-EX3LRPZG-UTEQqeds.js → chunk-EX3LRPZG-DBbU0fB6.js} +1 -1
  82. package/ui-dist/assets/{chunk-JWPE2WC7-DAaSu1vD.js → chunk-JWPE2WC7-D3_gHXg6.js} +1 -1
  83. package/ui-dist/assets/{chunk-MOJQB5TN-D_fTXJM3.js → chunk-MOJQB5TN-Di4_CsSE.js} +1 -1
  84. package/ui-dist/assets/{chunk-RYQCIY6F-C7H_axhC.js → chunk-RYQCIY6F-CnLAkiBe.js} +1 -1
  85. package/ui-dist/assets/{chunk-V7JOEXUC-B2toa05O.js → chunk-V7JOEXUC-D3UVXcJk.js} +1 -1
  86. package/ui-dist/assets/{chunk-VR4S4FIN-CkRvPkE2.js → chunk-VR4S4FIN-BJHYVSXv.js} +1 -1
  87. package/ui-dist/assets/{chunk-XXDRQBXY-DOO_r0VK.js → chunk-XXDRQBXY-C4VVlQgO.js} +1 -1
  88. package/ui-dist/assets/classDiagram-OUVF2IWQ-Dy5r3CRw.js +1 -0
  89. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-Dy5r3CRw.js +1 -0
  90. package/ui-dist/assets/{cose-bilkent-JH36ORCC-BzYjE_M-.js → cose-bilkent-JH36ORCC-2YJ4B2bE.js} +1 -1
  91. package/ui-dist/assets/{cynefin-VYW2F7L2-Dcf0eQJ5.js → cynefin-VYW2F7L2-293DKtI3.js} +1 -1
  92. package/ui-dist/assets/{cynefinDiagram-TSTJHNR4-Cs_Lv8Ua.js → cynefinDiagram-TSTJHNR4-d23XGkDy.js} +1 -1
  93. package/ui-dist/assets/{dagre-VKFMJZFB-B8oZME7g.js → dagre-VKFMJZFB-EJXBSBZ7.js} +1 -1
  94. package/ui-dist/assets/{diagram-FQU43EPY-DFHkB-_t.js → diagram-FQU43EPY-CcQzLlPj.js} +1 -1
  95. package/ui-dist/assets/{diagram-G47NLZAW-FGxRm1A-.js → diagram-G47NLZAW-Bke8VP0c.js} +1 -1
  96. package/ui-dist/assets/{diagram-NH7WQ7WH-Ot83ScOb.js → diagram-NH7WQ7WH-CDVrkJeo.js} +1 -1
  97. package/ui-dist/assets/{diagram-OA4YK3LP-CkiHNUvw.js → diagram-OA4YK3LP-Dw_uKvbb.js} +1 -1
  98. package/ui-dist/assets/{diagram-WEI45ONY-CxM3g_L3.js → diagram-WEI45ONY-DmhFc8DI.js} +1 -1
  99. package/ui-dist/assets/{ebnfDiagram-CCIWWBDH-DbcqsHcY.js → ebnfDiagram-CCIWWBDH-CdqBJkjm.js} +1 -1
  100. package/ui-dist/assets/{erDiagram-Q63AITRT-DLROlxDN.js → erDiagram-Q63AITRT-DOkjjwWL.js} +1 -1
  101. package/ui-dist/assets/{flowDiagram-23GEKE2U-DphM_YLM.js → flowDiagram-23GEKE2U-DG8rqW-P.js} +1 -1
  102. package/ui-dist/assets/{ganttDiagram-NO4QXBWP--QIRtWYP.js → ganttDiagram-NO4QXBWP-BEkgTSEf.js} +1 -1
  103. package/ui-dist/assets/{gitGraphDiagram-IHSO6WYX-Bg3QwNvE.js → gitGraphDiagram-IHSO6WYX-BjXwkXS6.js} +1 -1
  104. package/ui-dist/assets/{graph-B_1y14m_.js → graph-D3KzI4sR.js} +1 -1
  105. package/ui-dist/assets/{index-E2v-0TkN.js → index-B2UcZ_IJ.js} +1 -1
  106. package/ui-dist/assets/{index-Bi9I1TFJ.js → index-BDH__Lgs.js} +5 -5
  107. package/ui-dist/assets/{index-CcWXJMSa.js → index-BIQKbQUa.js} +1 -1
  108. package/ui-dist/assets/{index-XWYyP2hU.js → index-BIwzh-U4.js} +1 -1
  109. package/ui-dist/assets/{index-BlFoD6Gg.js → index-BNdZbjyn.js} +1 -1
  110. package/ui-dist/assets/{index-B-yeMW3t.js → index-BONjSnI2.js} +1 -1
  111. package/ui-dist/assets/{index-Aj3Mi7R6.js → index-BrgGIOAR.js} +1 -1
  112. package/ui-dist/assets/{index-CS2TW9nf.js → index-By6wVGNY.js} +1 -1
  113. package/ui-dist/assets/{index-BABow2kh.js → index-C6RzrVdO.js} +1 -1
  114. package/ui-dist/assets/{index-BueQLOw6.js → index-ChpzeG1B.js} +1 -1
  115. package/ui-dist/assets/{index-6O1Ef9oA.js → index-D-jdXuoy.js} +1 -1
  116. package/ui-dist/assets/{index-CdAtz86o.js → index-D2RUPIgv.js} +1 -1
  117. package/ui-dist/assets/{index-BEgTumxB.js → index-D3MTazer.js} +1 -1
  118. package/ui-dist/assets/{index-CMUgKK2j.js → index-DepPPcF2.js} +1 -1
  119. package/ui-dist/assets/{index-BQheb_6L.js → index-fZqqDrca.js} +1 -1
  120. package/ui-dist/assets/{infoDiagram-FWYZ7A6U-BxygKDVp.js → infoDiagram-FWYZ7A6U-sxoY23rO.js} +1 -1
  121. package/ui-dist/assets/{ishikawaDiagram-FXEZZL3T-47zucfG2.js → ishikawaDiagram-FXEZZL3T-DS6bgeAA.js} +1 -1
  122. package/ui-dist/assets/{journeyDiagram-5HDEW3XC-D8cyI8aY.js → journeyDiagram-5HDEW3XC-DzVKXP7n.js} +1 -1
  123. package/ui-dist/assets/{kanban-definition-HUTT4EX6-CrxRNese.js → kanban-definition-HUTT4EX6--VTcXs7-.js} +1 -1
  124. package/ui-dist/assets/{layout-OyZo5VN3.js → layout-DMOyxlK9.js} +1 -1
  125. package/ui-dist/assets/{linear-CuiwlwQB.js → linear-C76BMUjd.js} +1 -1
  126. package/ui-dist/assets/{map-rixHzQ5d.js → map-CeACQf-M.js} +1 -1
  127. package/ui-dist/assets/{mermaid.core-BBG92fxH.js → mermaid.core-C39RyJW7.js} +4 -4
  128. package/ui-dist/assets/{mindmap-definition-LN4V7U3C-CivqbhCV.js → mindmap-definition-LN4V7U3C-CC5aZy3e.js} +1 -1
  129. package/ui-dist/assets/{pegDiagram-2B236MQR-JrPSWk9k.js → pegDiagram-2B236MQR-Pfr90o3u.js} +1 -1
  130. package/ui-dist/assets/{pieDiagram-ENE6RG2P-F4HEWR5n.js → pieDiagram-ENE6RG2P-cFuaxMwf.js} +1 -1
  131. package/ui-dist/assets/{quadrantDiagram-ABIIQ3AL-BJNly8dn.js → quadrantDiagram-ABIIQ3AL-CcQ7rg5U.js} +1 -1
  132. package/ui-dist/assets/{railroadDiagram-RFXS5EU6-DNORDnb2.js → railroadDiagram-RFXS5EU6-Dn0G1sUQ.js} +1 -1
  133. package/ui-dist/assets/{requirementDiagram-TGXJPOKE-710xtGEU.js → requirementDiagram-TGXJPOKE-C55Bzmxm.js} +1 -1
  134. package/ui-dist/assets/{sankeyDiagram-HTMAVEWB-BmWJHmHA.js → sankeyDiagram-HTMAVEWB-rC-F0WZY.js} +1 -1
  135. package/ui-dist/assets/{sequenceDiagram-DBY2YBRQ-A73TFc3V.js → sequenceDiagram-DBY2YBRQ-DKnrOS-d.js} +1 -1
  136. package/ui-dist/assets/{sizeCapture-X5ZJPWSS-B8otrPI2.js → sizeCapture-X5ZJPWSS-CftJxMqM.js} +1 -1
  137. package/ui-dist/assets/{stateDiagram-2N3HPSRC-BZ9k729q.js → stateDiagram-2N3HPSRC-BWk2fDZE.js} +1 -1
  138. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-CAKipX8h.js +1 -0
  139. package/ui-dist/assets/{swimlanes-5IMT3BWC-W_CH_z9Z.js → swimlanes-5IMT3BWC-DEOS3o8Q.js} +2 -2
  140. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-BICPV_pK.js +8 -0
  141. package/ui-dist/assets/{timeline-definition-FHXFAJF6-CcXbC6J1.js → timeline-definition-FHXFAJF6-_6-BAwLW.js} +1 -1
  142. package/ui-dist/assets/{vennDiagram-L72KCM5P-BMHmsQqg.js → vennDiagram-L72KCM5P-BjCktz3U.js} +1 -1
  143. package/ui-dist/assets/{wardleyDiagram-EHGQE667-Bq2-CEvU.js → wardleyDiagram-EHGQE667-DrmL6CAw.js} +1 -1
  144. package/ui-dist/assets/{xterm-8vrqRuWm.js → xterm-Jvo0xPzf.js} +1 -1
  145. package/ui-dist/assets/{xychartDiagram-FW5EYKEG-De3RUAME.js → xychartDiagram-FW5EYKEG-DyJoUa3f.js} +1 -1
  146. package/ui-dist/index.html +1 -1
  147. package/ui-dist/assets/channel-DjvWm5jR.js +0 -1
  148. package/ui-dist/assets/classDiagram-OUVF2IWQ-CNSRItW2.js +0 -1
  149. package/ui-dist/assets/classDiagram-v2-EOCWNBFH-CNSRItW2.js +0 -1
  150. package/ui-dist/assets/stateDiagram-v2-6OUMAXLB-DepHJpk7.js +0 -1
  151. package/ui-dist/assets/swimlanesDiagram-G3AALYLV-BRL-ddud.js +0 -8
@@ -3,6 +3,7 @@ import { parseShortRef, sanitizeChatStructuredPayload } from "@rudderhq/shared";
3
3
  import { withChatTranscriptGenerationProvenance } from "@rudderhq/shared/chat-transcript-provenance";
4
4
  import { and, asc, desc, eq, gt, inArray, isNotNull, isNull, lt, lte, or, sql } from "drizzle-orm";
5
5
  import { randomUUID } from "node:crypto";
6
+ import { isDeepStrictEqual } from "node:util";
6
7
  import { conflict, HttpError, notFound, unprocessable } from "../errors.js";
7
8
  import { logActivity } from "./activity-log.js";
8
9
  import { agentService } from "./agents.js";
@@ -11,6 +12,7 @@ import { ensureChatFamilyGroup } from "./chat-family-groups.js";
11
12
  import { chatGenerationProtocolService } from "./chat-generation-protocol.js";
12
13
  import { validateCanonicalChatInlineAnnotations } from "./chat-inline-annotation-validation.js";
13
14
  import { selectedChatMessageBranchCondition } from "./chat-message-branch.js";
15
+ import { createChatOrganizationBrandingService } from "./chat-organization-branding.js";
14
16
  import { hydrateQueuedMessage, materializeQueuedUserMessage, normalizeQueuedMessagePayload, QUEUED_ANNOTATION_ASSETS_KEY, queuedAnnotationAssetState, queuedMessageMutationFingerprint, withQueuedAnnotationAssetState, } from "./chat-queued-message-materialization.js";
15
17
  import { compareChatGenerationSelection, listChatGenerationTranscripts, listDetachedChatTranscripts, listDetachedChatTranscriptSummaries, loadChatTranscripts, replaceDetachedChatTranscript, selectChatTranscript, transcriptSummaryFromSources, } from "./chat-transcript-persistence.js";
16
18
  import { createChatAnnotationMessagePersistence, createChatMessageMutationLookup } from "./chats.annotation-persistence.js";
@@ -34,7 +36,7 @@ const CHAT_TITLE_MAX_LENGTH = 200;
34
36
  class InvalidQueueDeliveryActionLinkError extends Error {
35
37
  }
36
38
  import { createConversationUserStateInitializer } from "./chats.user-state-initialization.js";
37
- export function chatService(db, storage) {
39
+ export function chatService(db, storage, rustFoundationBridge) {
38
40
  const generationProtocol = chatGenerationProtocolService(db);
39
41
  const QUEUED_MESSAGE_CLAIM_LEASE_MS = 2 * 60 * 1000;
40
42
  const issuesSvc = issueService(db, storage);
@@ -44,6 +46,15 @@ export function chatService(db, storage) {
44
46
  const agentsSvc = agentService(db);
45
47
  const addUserChatMessage = createChatAnnotationMessagePersistence(db, getMessage);
46
48
  const getUserMessageMutationByClientMutationId = createChatMessageMutationLookup(db, getMessage);
49
+ const organizationBrandingSvc = createChatOrganizationBrandingService({
50
+ db,
51
+ organizationsSvc,
52
+ agentsSvc,
53
+ bridge: rustFoundationBridge,
54
+ getMessage,
55
+ updateMessageStructuredPayload,
56
+ addMessage,
57
+ });
47
58
  const ensureConversationUserStates = createConversationUserStateInitializer(db);
48
59
  async function listConversationUserStates(orgId, userId, conversationIds) {
49
60
  if (conversationIds.length === 0)
@@ -3233,10 +3244,8 @@ export function chatService(db, storage) {
3233
3244
  const sanitizedPayload = sanitizeChatStructuredPayload(input.structuredPayload ?? null);
3234
3245
  const transcriptFromPayload = chatTranscriptFromPayload(sanitizedPayload);
3235
3246
  const transcript = input.transcript ?? transcriptFromPayload;
3236
- const { message } = await db.transaction(async (tx) => {
3237
- const [inserted] = await tx
3238
- .insert(chatMessages)
3239
- .values({
3247
+ const { message, created } = await db.transaction(async (tx) => {
3248
+ const values = {
3240
3249
  orgId: input.orgId,
3241
3250
  conversationId,
3242
3251
  role: input.role,
@@ -3247,24 +3256,51 @@ export function chatService(db, storage) {
3247
3256
  approvalId: input.approvalId ?? null,
3248
3257
  runId: input.runId ?? null,
3249
3258
  replyingAgentId: input.replyingAgentId ?? null,
3259
+ clientMutationId: input.clientMutationId ?? null,
3250
3260
  chatTurnId: input.chatTurnId ?? null,
3251
3261
  turnVariant: input.turnVariant ?? 0,
3252
- })
3253
- .returning();
3254
- if (!inserted)
3262
+ };
3263
+ const [inserted] = input.clientMutationId
3264
+ ? await tx.insert(chatMessages).values(values).onConflictDoNothing().returning()
3265
+ : await tx.insert(chatMessages).values(values).returning();
3266
+ if (inserted) {
3267
+ if (transcript.length > 0) {
3268
+ await replaceDetachedChatTranscript(tx, {
3269
+ orgId: input.orgId,
3270
+ messageId: inserted.id,
3271
+ entries: transcript,
3272
+ });
3273
+ }
3274
+ return { message: inserted, created: true };
3275
+ }
3276
+ if (!input.clientMutationId)
3255
3277
  throw new Error("Failed to create chat message");
3256
3278
  if (transcript.length > 0) {
3257
- await replaceDetachedChatTranscript(tx, {
3258
- orgId: input.orgId,
3259
- messageId: inserted.id,
3260
- entries: transcript,
3261
- });
3279
+ throw conflict("Idempotent Chat system events cannot replace transcript content");
3262
3280
  }
3263
- return { message: inserted };
3281
+ const existing = await tx
3282
+ .select()
3283
+ .from(chatMessages)
3284
+ .where(and(eq(chatMessages.conversationId, conversationId), eq(chatMessages.clientMutationId, input.clientMutationId)))
3285
+ .then((rows) => rows[0] ?? null);
3286
+ if (!existing)
3287
+ throw new Error("Chat message idempotency key conflicted without a matching message");
3288
+ if (existing.orgId !== values.orgId
3289
+ || existing.role !== values.role
3290
+ || existing.kind !== values.kind
3291
+ || existing.status !== values.status
3292
+ || existing.body !== values.body
3293
+ || existing.approvalId !== values.approvalId
3294
+ || existing.runId !== values.runId
3295
+ || existing.replyingAgentId !== values.replyingAgentId
3296
+ || !isDeepStrictEqual(existing.structuredPayload, values.structuredPayload)) {
3297
+ throw conflict("Chat message idempotency key was reused with different content");
3298
+ }
3299
+ return { message: existing, created: false };
3264
3300
  });
3265
3301
  if (!message)
3266
3302
  throw new Error("Failed to create chat message");
3267
- if (input.role === "user" || isVisibleIncomingChatMessage(message)) {
3303
+ if (created && (input.role === "user" || isVisibleIncomingChatMessage(message))) {
3268
3304
  await refreshConversationTouch(conversationId, message.createdAt);
3269
3305
  }
3270
3306
  const [hydrated] = await hydrateMessages([message]);
@@ -3631,9 +3667,6 @@ export function chatService(db, storage) {
3631
3667
  throw unprocessable("This operation proposal is managed through approvals");
3632
3668
  }
3633
3669
  const currentState = operationProposalDecisionStatusFromPayload(message.structuredPayload);
3634
- if (currentState.status !== "pending") {
3635
- throw unprocessable("Only pending lightweight changes can be resolved");
3636
- }
3637
3670
  const proposal = operationProposalFromPayload(message.structuredPayload);
3638
3671
  if (!proposal) {
3639
3672
  throw unprocessable("Chat operation proposal payload was incomplete");
@@ -3647,52 +3680,25 @@ export function chatService(db, storage) {
3647
3680
  throw unprocessable("Agent lightweight changes must target an agent in the same organization");
3648
3681
  }
3649
3682
  }
3683
+ if (input.action === "approve" && proposal.targetType === "organization") {
3684
+ return organizationBrandingSvc.resolveOperationProposal({
3685
+ conversationId,
3686
+ messageId,
3687
+ orgId: conversation.orgId,
3688
+ messagePayload: message.structuredPayload,
3689
+ proposal: { ...proposal, targetType: "organization" },
3690
+ currentState,
3691
+ actorUserId: input.actorUserId,
3692
+ actor: input.actor,
3693
+ decisionNote: input.decisionNote,
3694
+ });
3695
+ }
3696
+ if (currentState.status !== "pending") {
3697
+ throw unprocessable("Only pending lightweight changes can be resolved");
3698
+ }
3650
3699
  const decisionNote = safeTrim(input.decisionNote);
3651
3700
  const decidedAtIso = new Date().toISOString();
3652
3701
  if (input.action === "approve") {
3653
- if (proposal.targetType === "organization") {
3654
- const updated = await organizationsSvc.update(proposal.targetId, proposal.patch);
3655
- if (!updated)
3656
- throw notFound("Organization not found");
3657
- const updatedMessage = await updateMessageStructuredPayload(conversationId, messageId, withOperationProposalDecisionState(message.structuredPayload, {
3658
- status: "approved",
3659
- decisionNote,
3660
- decidedByUserId: input.actorUserId,
3661
- decidedAt: decidedAtIso,
3662
- }));
3663
- if (!updatedMessage) {
3664
- throw notFound("Operation proposal not found");
3665
- }
3666
- const systemMessage = await addMessage(conversationId, {
3667
- orgId: conversation.orgId,
3668
- role: "system",
3669
- kind: "system_event",
3670
- body: `Applied lightweight change: ${proposal.summary}.`,
3671
- structuredPayload: {
3672
- eventType: "operation_applied",
3673
- source: "chat",
3674
- sourceMessageId: messageId,
3675
- targetType: "organization",
3676
- targetId: proposal.targetId,
3677
- decisionNote,
3678
- },
3679
- });
3680
- await logActivity(db, {
3681
- orgId: conversation.orgId,
3682
- actorType: "user",
3683
- actorId: input.actorUserId ?? "board",
3684
- action: "organization.updated",
3685
- entityType: "organization",
3686
- entityId: proposal.targetId,
3687
- details: {
3688
- source: "chat_lightweight_change",
3689
- sourceMessageId: messageId,
3690
- decisionNote,
3691
- ...proposal.patch,
3692
- },
3693
- });
3694
- return { message: updatedMessage, systemMessage };
3695
- }
3696
3702
  const updated = await agentsSvc.update(proposal.targetId, proposal.patch);
3697
3703
  if (!updated || updated.orgId !== conversation.orgId) {
3698
3704
  throw notFound("Agent not found");
@@ -3763,7 +3769,7 @@ export function chatService(db, storage) {
3763
3769
  });
3764
3770
  return { message: updatedMessage, systemMessage };
3765
3771
  }
3766
- async function applyApprovedApproval(approval, actorUserId) {
3772
+ async function applyApprovedApproval(approval, actorUserId, actor, options = {}) {
3767
3773
  if (approval.type !== "chat_issue_creation" && approval.type !== "chat_operation") {
3768
3774
  return null;
3769
3775
  }
@@ -3866,31 +3872,14 @@ export function chatService(db, storage) {
3866
3872
  }
3867
3873
  }
3868
3874
  if (proposal.targetType === "organization") {
3869
- const updated = await organizationsSvc.update(proposal.targetId, proposal.patch);
3870
- if (!updated)
3871
- throw notFound("Organization not found");
3872
- await addMessage(conversationId, {
3873
- orgId: approval.orgId,
3874
- role: "system",
3875
- kind: "system_event",
3876
- body: `Applied approved organization change: ${proposal.summary}.`,
3877
- structuredPayload: {
3878
- eventType: "operation_applied",
3879
- approvalId: approval.id,
3880
- targetType: "organization",
3881
- targetId: proposal.targetId,
3882
- },
3883
- });
3884
- await logActivity(db, {
3885
- orgId: approval.orgId,
3886
- actorType: "user",
3887
- actorId: actorUserId ?? "board",
3888
- action: "organization.updated",
3889
- entityType: "organization",
3890
- entityId: proposal.targetId,
3891
- details: proposal.patch,
3875
+ return organizationBrandingSvc.applyApprovedApproval({
3876
+ approval,
3877
+ actorUserId,
3878
+ actor,
3879
+ recoveryOnly: options.recoveryOnly ?? false,
3880
+ conversationId,
3881
+ proposal: { ...proposal, targetType: "organization" },
3892
3882
  });
3893
- return updated;
3894
3883
  }
3895
3884
  const updated = await agentsSvc.update(proposal.targetId, proposal.patch);
3896
3885
  if (!updated)
@@ -4005,6 +3994,7 @@ export function chatService(db, storage) {
4005
3994
  getMessage,
4006
3995
  getUserMessageMutationByClientMutationId,
4007
3996
  applyApprovedApproval,
3997
+ validateApprovedApproval: organizationBrandingSvc.validateApprovedApproval,
4008
3998
  createProposalApproval,
4009
3999
  resolveOperationProposal,
4010
4000
  };