@orthacms/copilot-server 0.0.0-reserve.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.
Files changed (156) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/index.d.ts +24 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +48 -0
  6. package/dist/lib/chat/application/appliers-registrar.d.ts +16 -0
  7. package/dist/lib/chat/application/appliers-registrar.d.ts.map +1 -0
  8. package/dist/lib/chat/application/appliers-registrar.js +50 -0
  9. package/dist/lib/chat/application/capability-profile.service.d.ts +43 -0
  10. package/dist/lib/chat/application/capability-profile.service.d.ts.map +1 -0
  11. package/dist/lib/chat/application/capability-profile.service.js +75 -0
  12. package/dist/lib/chat/application/content-type-summary.service.d.ts +32 -0
  13. package/dist/lib/chat/application/content-type-summary.service.d.ts.map +1 -0
  14. package/dist/lib/chat/application/content-type-summary.service.js +64 -0
  15. package/dist/lib/chat/application/decide-proposal.service.d.ts +80 -0
  16. package/dist/lib/chat/application/decide-proposal.service.d.ts.map +1 -0
  17. package/dist/lib/chat/application/decide-proposal.service.js +112 -0
  18. package/dist/lib/chat/application/dto/create-run.dto.d.ts +138 -0
  19. package/dist/lib/chat/application/dto/create-run.dto.d.ts.map +1 -0
  20. package/dist/lib/chat/application/dto/create-run.dto.js +253 -0
  21. package/dist/lib/chat/application/dto/decide-tool-permission.dto.d.ts +20 -0
  22. package/dist/lib/chat/application/dto/decide-tool-permission.dto.d.ts.map +1 -0
  23. package/dist/lib/chat/application/dto/decide-tool-permission.dto.js +44 -0
  24. package/dist/lib/chat/application/dto/extend-tool-permission.dto.d.ts +18 -0
  25. package/dist/lib/chat/application/dto/extend-tool-permission.dto.d.ts.map +1 -0
  26. package/dist/lib/chat/application/dto/extend-tool-permission.dto.js +33 -0
  27. package/dist/lib/chat/application/dto/list-conversations-query.dto.d.ts +18 -0
  28. package/dist/lib/chat/application/dto/list-conversations-query.dto.d.ts.map +1 -0
  29. package/dist/lib/chat/application/dto/list-conversations-query.dto.js +39 -0
  30. package/dist/lib/chat/application/dto/list-proposals-query.dto.d.ts +15 -0
  31. package/dist/lib/chat/application/dto/list-proposals-query.dto.d.ts.map +1 -0
  32. package/dist/lib/chat/application/dto/list-proposals-query.dto.js +41 -0
  33. package/dist/lib/chat/application/dto/update-conversation.dto.d.ts +68 -0
  34. package/dist/lib/chat/application/dto/update-conversation.dto.d.ts.map +1 -0
  35. package/dist/lib/chat/application/dto/update-conversation.dto.js +115 -0
  36. package/dist/lib/chat/application/proposal-applier.registry.d.ts +27 -0
  37. package/dist/lib/chat/application/proposal-applier.registry.d.ts.map +1 -0
  38. package/dist/lib/chat/application/proposal-applier.registry.js +62 -0
  39. package/dist/lib/chat/application/run-engine.service.d.ts +243 -0
  40. package/dist/lib/chat/application/run-engine.service.d.ts.map +1 -0
  41. package/dist/lib/chat/application/run-engine.service.js +1058 -0
  42. package/dist/lib/chat/application/summarize-tool-output.d.ts +15 -0
  43. package/dist/lib/chat/application/summarize-tool-output.d.ts.map +1 -0
  44. package/dist/lib/chat/application/summarize-tool-output.js +61 -0
  45. package/dist/lib/chat/application/system-prompt.d.ts +81 -0
  46. package/dist/lib/chat/application/system-prompt.d.ts.map +1 -0
  47. package/dist/lib/chat/application/system-prompt.js +477 -0
  48. package/dist/lib/chat/application/tool-permission.broker.d.ts +118 -0
  49. package/dist/lib/chat/application/tool-permission.broker.d.ts.map +1 -0
  50. package/dist/lib/chat/application/tool-permission.broker.js +221 -0
  51. package/dist/lib/chat/http/controllers/create-run.controller.d.ts +30 -0
  52. package/dist/lib/chat/http/controllers/create-run.controller.d.ts.map +1 -0
  53. package/dist/lib/chat/http/controllers/create-run.controller.js +159 -0
  54. package/dist/lib/chat/http/controllers/get-conversation.controller.d.ts +22 -0
  55. package/dist/lib/chat/http/controllers/get-conversation.controller.d.ts.map +1 -0
  56. package/dist/lib/chat/http/controllers/get-conversation.controller.js +50 -0
  57. package/dist/lib/chat/http/controllers/list-conversations.controller.d.ts +20 -0
  58. package/dist/lib/chat/http/controllers/list-conversations.controller.d.ts.map +1 -0
  59. package/dist/lib/chat/http/controllers/list-conversations.controller.js +47 -0
  60. package/dist/lib/chat/http/controllers/list-models.controller.d.ts +35 -0
  61. package/dist/lib/chat/http/controllers/list-models.controller.d.ts.map +1 -0
  62. package/dist/lib/chat/http/controllers/list-models.controller.js +48 -0
  63. package/dist/lib/chat/http/controllers/proposals.controller.d.ts +43 -0
  64. package/dist/lib/chat/http/controllers/proposals.controller.d.ts.map +1 -0
  65. package/dist/lib/chat/http/controllers/proposals.controller.js +98 -0
  66. package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts +57 -0
  67. package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts.map +1 -0
  68. package/dist/lib/chat/http/controllers/tool-permission.controller.js +116 -0
  69. package/dist/lib/chat/http/controllers/update-conversation.controller.d.ts +37 -0
  70. package/dist/lib/chat/http/controllers/update-conversation.controller.d.ts.map +1 -0
  71. package/dist/lib/chat/http/controllers/update-conversation.controller.js +108 -0
  72. package/dist/lib/chat/http/sse-stream.d.ts +41 -0
  73. package/dist/lib/chat/http/sse-stream.d.ts.map +1 -0
  74. package/dist/lib/chat/http/sse-stream.js +95 -0
  75. package/dist/lib/chat/infrastructure/persistence/conversation.repository.d.ts +159 -0
  76. package/dist/lib/chat/infrastructure/persistence/conversation.repository.d.ts.map +1 -0
  77. package/dist/lib/chat/infrastructure/persistence/conversation.repository.js +249 -0
  78. package/dist/lib/chat/infrastructure/persistence/derive-title.d.ts +13 -0
  79. package/dist/lib/chat/infrastructure/persistence/derive-title.d.ts.map +1 -0
  80. package/dist/lib/chat/infrastructure/persistence/derive-title.js +35 -0
  81. package/dist/lib/chat/infrastructure/persistence/proposal.repository.d.ts +86 -0
  82. package/dist/lib/chat/infrastructure/persistence/proposal.repository.d.ts.map +1 -0
  83. package/dist/lib/chat/infrastructure/persistence/proposal.repository.js +152 -0
  84. package/dist/lib/chat/infrastructure/schema/conversations.d.ts +189 -0
  85. package/dist/lib/chat/infrastructure/schema/conversations.d.ts.map +1 -0
  86. package/dist/lib/chat/infrastructure/schema/conversations.js +100 -0
  87. package/dist/lib/chat/infrastructure/schema/external-refs.d.ts +67 -0
  88. package/dist/lib/chat/infrastructure/schema/external-refs.d.ts.map +1 -0
  89. package/dist/lib/chat/infrastructure/schema/external-refs.js +28 -0
  90. package/dist/lib/chat/infrastructure/schema/index.d.ts +5 -0
  91. package/dist/lib/chat/infrastructure/schema/index.d.ts.map +1 -0
  92. package/dist/lib/chat/infrastructure/schema/index.js +12 -0
  93. package/dist/lib/chat/infrastructure/schema/messages.d.ts +264 -0
  94. package/dist/lib/chat/infrastructure/schema/messages.d.ts.map +1 -0
  95. package/dist/lib/chat/infrastructure/schema/messages.js +96 -0
  96. package/dist/lib/chat/infrastructure/schema/proposals.d.ts +328 -0
  97. package/dist/lib/chat/infrastructure/schema/proposals.d.ts.map +1 -0
  98. package/dist/lib/chat/infrastructure/schema/proposals.js +87 -0
  99. package/dist/lib/chat/infrastructure/schema/tool-calls.d.ts +203 -0
  100. package/dist/lib/chat/infrastructure/schema/tool-calls.d.ts.map +1 -0
  101. package/dist/lib/chat/infrastructure/schema/tool-calls.js +50 -0
  102. package/dist/lib/copilot.module.d.ts +30 -0
  103. package/dist/lib/copilot.module.d.ts.map +1 -0
  104. package/dist/lib/copilot.module.js +146 -0
  105. package/dist/lib/copilot.tokens.d.ts +34 -0
  106. package/dist/lib/copilot.tokens.d.ts.map +1 -0
  107. package/dist/lib/copilot.tokens.js +38 -0
  108. package/dist/lib/infrastructure/model-registry.d.ts +22 -0
  109. package/dist/lib/infrastructure/model-registry.d.ts.map +1 -0
  110. package/dist/lib/infrastructure/model-registry.js +54 -0
  111. package/dist/lib/skills/application/dto/create-skill.dto.d.ts +36 -0
  112. package/dist/lib/skills/application/dto/create-skill.dto.d.ts.map +1 -0
  113. package/dist/lib/skills/application/dto/create-skill.dto.js +106 -0
  114. package/dist/lib/skills/application/dto/update-skill.dto.d.ts +28 -0
  115. package/dist/lib/skills/application/dto/update-skill.dto.d.ts.map +1 -0
  116. package/dist/lib/skills/application/dto/update-skill.dto.js +96 -0
  117. package/dist/lib/skills/application/skill-catalog.service.d.ts +97 -0
  118. package/dist/lib/skills/application/skill-catalog.service.d.ts.map +1 -0
  119. package/dist/lib/skills/application/skill-catalog.service.js +157 -0
  120. package/dist/lib/skills/http/controllers/list-skills.controller.d.ts +19 -0
  121. package/dist/lib/skills/http/controllers/list-skills.controller.d.ts.map +1 -0
  122. package/dist/lib/skills/http/controllers/list-skills.controller.js +50 -0
  123. package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts +64 -0
  124. package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts.map +1 -0
  125. package/dist/lib/skills/http/controllers/manage-skills.controller.js +182 -0
  126. package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts +78 -0
  127. package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts.map +1 -0
  128. package/dist/lib/skills/infrastructure/persistence/skill.repository.js +116 -0
  129. package/dist/lib/skills/infrastructure/schema/index.d.ts +2 -0
  130. package/dist/lib/skills/infrastructure/schema/index.d.ts.map +1 -0
  131. package/dist/lib/skills/infrastructure/schema/index.js +6 -0
  132. package/dist/lib/skills/infrastructure/schema/skills.d.ts +212 -0
  133. package/dist/lib/skills/infrastructure/schema/skills.d.ts.map +1 -0
  134. package/dist/lib/skills/infrastructure/schema/skills.js +78 -0
  135. package/dist/lib/types/copilot-config.d.ts +55 -0
  136. package/dist/lib/types/copilot-config.d.ts.map +1 -0
  137. package/dist/lib/types/copilot-config.js +2 -0
  138. package/dist/lib/utils/copilot-plugin.d.ts +66 -0
  139. package/dist/lib/utils/copilot-plugin.d.ts.map +1 -0
  140. package/dist/lib/utils/copilot-plugin.js +87 -0
  141. package/migrations/0000_copilot_chat.sql +48 -0
  142. package/migrations/0001_proposals.sql +34 -0
  143. package/migrations/0002_drop_workspace_policies.sql +1 -0
  144. package/migrations/0003_conversation_allowed_tools.sql +1 -0
  145. package/migrations/0004_message_attachments.sql +1 -0
  146. package/migrations/0005_skills.sql +19 -0
  147. package/migrations/0006_conversation_model_choice.sql +1 -0
  148. package/migrations/meta/0000_snapshot.json +390 -0
  149. package/migrations/meta/0001_snapshot.json +657 -0
  150. package/migrations/meta/0002_snapshot.json +610 -0
  151. package/migrations/meta/0003_snapshot.json +617 -0
  152. package/migrations/meta/0004_snapshot.json +623 -0
  153. package/migrations/meta/0005_snapshot.json +771 -0
  154. package/migrations/meta/0006_snapshot.json +777 -0
  155. package/migrations/meta/_journal.json +55 -0
  156. package/package.json +46 -0
@@ -0,0 +1,118 @@
1
+ import type { ToolPermissionDecision } from '@orthacms/copilot-domain';
2
+ /**
3
+ * How long a parked run waits for an answer before giving up — **for the whole
4
+ * run**, not per call.
5
+ *
6
+ * The SSE stream heartbeats every 15s, so the connection itself will survive
7
+ * far longer than this; the limit is about the user, not the socket. Five
8
+ * minutes is "they went to look at the entry and came back".
9
+ *
10
+ * Per **run** rather than per **call** because a turn may ask about several
11
+ * calls, and the ceilings in `RunLimits` cannot bound any of it: the wall clock
12
+ * is checked at the top of a step and a park happens inside one. Charging each
13
+ * call its own five minutes made a single turn requesting thirty writes hold
14
+ * the connection, the generator and the model context for two and a half
15
+ * hours — while "they went to look and came back" is one absence, not thirty.
16
+ */
17
+ export declare const DEFAULT_RUN_DECISION_BUDGET_MS: number;
18
+ /** A decision, plus how it was reached — the timeout is not a user's `deny`. */
19
+ export interface PermissionOutcome {
20
+ decision: ToolPermissionDecision;
21
+ /** True when nobody answered in time. */
22
+ timedOut: boolean;
23
+ }
24
+ /**
25
+ * Who a parked run belongs to. Only they may answer it.
26
+ *
27
+ * Both halves are load-bearing and neither is redundant. `WorkspaceGuard` on
28
+ * the answering route proves the caller belongs to *the workspace they named*,
29
+ * which says nothing about the run: a member of workspace B naming B passes it
30
+ * while answering a run parked in A. And `copilot:use` is held by every role,
31
+ * so a colleague in the same workspace passes every guard on the route.
32
+ */
33
+ export interface RunOwner {
34
+ userId: string;
35
+ workspaceId: string;
36
+ }
37
+ /**
38
+ * Parks a run while its user decides whether one tool call may proceed.
39
+ *
40
+ * **In-memory, and that is a real deployment constraint, not an oversight.**
41
+ * The promise a run awaits lives in this process, so the `POST …/permission`
42
+ * that resolves it has to reach the same instance. A single-node self-hosted
43
+ * CMS — which is what this is — is fine. A horizontally scaled one needs sticky
44
+ * routing by `runId`, or this registry moved behind a shared channel. Say so in
45
+ * the deployment notes rather than discovering it as an occasional hang.
46
+ *
47
+ * Four things it has to get right. The first three are ways a run could
48
+ * otherwise be stranded forever; the fourth is who is allowed to end the wait.
49
+ *
50
+ * - **A timeout.** Documented above, and spent against a per-run budget. It
51
+ * resolves as a refusal rather than throwing, so the model is told and the
52
+ * answer still lands.
53
+ * - **Abort.** The user closing the window is the common case, and the engine's
54
+ * `signal` fires; the waiter has to reject then, or the generator never
55
+ * unwinds and the run leaks.
56
+ * - **Cleanup.** Every exit path clears the entry. A registry that only deletes
57
+ * on the happy path grows one dangling promise per abandoned run.
58
+ * - **Ownership.** A `runId` is not a secret — it is handed to the client in
59
+ * the `run-started` frame — and it is the *only* thing the answering route
60
+ * used to key on. See {@link RunOwner}.
61
+ */
62
+ export declare class ToolPermissionBroker {
63
+ private readonly logger;
64
+ /**
65
+ * The waiting budget, in ms. Set once from the plugin config at
66
+ * composition; {@link DEFAULT_RUN_DECISION_BUDGET_MS} when the operator
67
+ * said nothing.
68
+ */
69
+ private budgetMs;
70
+ /**
71
+ * Applies the operator's configured budget. Called by the plugin module at
72
+ * startup — a setter rather than constructor injection because the broker
73
+ * is also constructed directly in tests, where the default is what is
74
+ * wanted.
75
+ */
76
+ configure(budgetMs: number | undefined): void;
77
+ /**
78
+ * What is left of `runId`'s waiting budget, in ms — what the run engine puts
79
+ * on the permission frame as a deadline, so the prompt can show a countdown
80
+ * and warn before it expires instead of simply vanishing (`ORT-118`).
81
+ */
82
+ budgetRemaining(runId: string): number;
83
+ /** Waiters by `runId:callId`. */
84
+ private readonly waiting;
85
+ /** Milliseconds each run has already spent parked, and when it last did. */
86
+ private readonly spent;
87
+ /**
88
+ * Waits for the user's answer to one call.
89
+ *
90
+ * Resolves rather than rejects on a timeout, because a refusal is an
91
+ * ordinary tool error the model can report — the same treatment every other
92
+ * refused call gets. Only an abort rejects, and only because that means
93
+ * nobody is listening any more.
94
+ */
95
+ ask(runId: string, callId: string, owner: RunOwner, signal: AbortSignal): Promise<PermissionOutcome>;
96
+ /**
97
+ * Delivers a decision. Returns false when nothing was waiting, or when the
98
+ * caller does not own the parked run.
99
+ *
100
+ * **Both answer false, hence the same 404**, deliberately: whether a run
101
+ * exists is not something a caller who does not own it may learn, and the
102
+ * route's message ("no longer waiting for an answer") is true either way
103
+ * from where they stand.
104
+ */
105
+ decide(runId: string, callId: string, decision: ToolPermissionDecision, by: RunOwner): boolean;
106
+ /**
107
+ * Grants a parked call more time, at the user's request. Returns the new
108
+ * allowance in ms, or `null` when nothing is waiting or the caller does not
109
+ * own the run — the same conflation, for the same reason, as
110
+ * {@link decide}.
111
+ */
112
+ extend(runId: string, callId: string, by: RunOwner): number | null;
113
+ /** What is left of `runId`'s waiting budget, pruning stale entries first. */
114
+ private remainingBudget;
115
+ /** Charges `ms` of waiting to `runId`. */
116
+ private charge;
117
+ }
118
+ //# sourceMappingURL=tool-permission.broker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-permission.broker.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/tool-permission.broker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAEvE;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,8BAA8B,QAAa,CAAC;AAsBzD,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAC9B,QAAQ,EAAE,sBAAsB,CAAC;IACjC,yCAAyC;IACzC,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,QAAQ;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACvB;AAUD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,qBACa,oBAAoB;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAyC;IAEhE;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAkC;IAElD;;;;;OAKG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAI7C;;;;OAIG;IACH,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAItC,iCAAiC;IACjC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;IAErD,4EAA4E;IAC5E,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAiD;IAEvE;;;;;;;OAOG;IACG,GAAG,CACL,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,QAAQ,EACf,MAAM,EAAE,WAAW,GACpB,OAAO,CAAC,iBAAiB,CAAC;IA6D7B;;;;;;;;OAQG;IACH,MAAM,CACF,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,sBAAsB,EAChC,EAAE,EAAE,QAAQ,GACb,OAAO;IAoBV;;;;;OAKG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI;IAgBlE,6EAA6E;IAC7E,OAAO,CAAC,eAAe;IAUvB,0CAA0C;IAC1C,OAAO,CAAC,MAAM;CAOjB"}
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ var ToolPermissionBroker_1;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ToolPermissionBroker = exports.DEFAULT_RUN_DECISION_BUDGET_MS = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const common_1 = require("@nestjs/common");
7
+ /**
8
+ * How long a parked run waits for an answer before giving up — **for the whole
9
+ * run**, not per call.
10
+ *
11
+ * The SSE stream heartbeats every 15s, so the connection itself will survive
12
+ * far longer than this; the limit is about the user, not the socket. Five
13
+ * minutes is "they went to look at the entry and came back".
14
+ *
15
+ * Per **run** rather than per **call** because a turn may ask about several
16
+ * calls, and the ceilings in `RunLimits` cannot bound any of it: the wall clock
17
+ * is checked at the top of a step and a park happens inside one. Charging each
18
+ * call its own five minutes made a single turn requesting thirty writes hold
19
+ * the connection, the generator and the model context for two and a half
20
+ * hours — while "they went to look and came back" is one absence, not thirty.
21
+ */
22
+ exports.DEFAULT_RUN_DECISION_BUDGET_MS = 5 * 60_000;
23
+ /**
24
+ * How much time one {@link ToolPermissionBroker.extend} grants.
25
+ *
26
+ * A full fresh budget rather than a token top-up: the user pressing "I need
27
+ * more time" is saying the first allowance was not enough, and WCAG 2.2.1 asks
28
+ * that a limit be extendable "at least ten times" over — which repeated
29
+ * extensions of the full amount satisfy without inventing a second number.
30
+ */
31
+ const EXTENSION_MS = exports.DEFAULT_RUN_DECISION_BUDGET_MS;
32
+ /**
33
+ * How long a run's spent budget is remembered after its last park.
34
+ *
35
+ * There is no "run ended" signal to clean up on — the generator simply stops
36
+ * asking — so entries are pruned opportunistically, on the next `ask`. Anything
37
+ * older than the budget can no longer constrain a live run, because a run that
38
+ * parked that long ago has already exhausted it.
39
+ */
40
+ const BUDGET_TTL_MS = exports.DEFAULT_RUN_DECISION_BUDGET_MS;
41
+ /**
42
+ * Parks a run while its user decides whether one tool call may proceed.
43
+ *
44
+ * **In-memory, and that is a real deployment constraint, not an oversight.**
45
+ * The promise a run awaits lives in this process, so the `POST …/permission`
46
+ * that resolves it has to reach the same instance. A single-node self-hosted
47
+ * CMS — which is what this is — is fine. A horizontally scaled one needs sticky
48
+ * routing by `runId`, or this registry moved behind a shared channel. Say so in
49
+ * the deployment notes rather than discovering it as an occasional hang.
50
+ *
51
+ * Four things it has to get right. The first three are ways a run could
52
+ * otherwise be stranded forever; the fourth is who is allowed to end the wait.
53
+ *
54
+ * - **A timeout.** Documented above, and spent against a per-run budget. It
55
+ * resolves as a refusal rather than throwing, so the model is told and the
56
+ * answer still lands.
57
+ * - **Abort.** The user closing the window is the common case, and the engine's
58
+ * `signal` fires; the waiter has to reject then, or the generator never
59
+ * unwinds and the run leaks.
60
+ * - **Cleanup.** Every exit path clears the entry. A registry that only deletes
61
+ * on the happy path grows one dangling promise per abandoned run.
62
+ * - **Ownership.** A `runId` is not a secret — it is handed to the client in
63
+ * the `run-started` frame — and it is the *only* thing the answering route
64
+ * used to key on. See {@link RunOwner}.
65
+ */
66
+ let ToolPermissionBroker = ToolPermissionBroker_1 = class ToolPermissionBroker {
67
+ logger = new common_1.Logger(ToolPermissionBroker_1.name);
68
+ /**
69
+ * The waiting budget, in ms. Set once from the plugin config at
70
+ * composition; {@link DEFAULT_RUN_DECISION_BUDGET_MS} when the operator
71
+ * said nothing.
72
+ */
73
+ budgetMs = exports.DEFAULT_RUN_DECISION_BUDGET_MS;
74
+ /**
75
+ * Applies the operator's configured budget. Called by the plugin module at
76
+ * startup — a setter rather than constructor injection because the broker
77
+ * is also constructed directly in tests, where the default is what is
78
+ * wanted.
79
+ */
80
+ configure(budgetMs) {
81
+ if (budgetMs !== undefined && budgetMs > 0)
82
+ this.budgetMs = budgetMs;
83
+ }
84
+ /**
85
+ * What is left of `runId`'s waiting budget, in ms — what the run engine puts
86
+ * on the permission frame as a deadline, so the prompt can show a countdown
87
+ * and warn before it expires instead of simply vanishing (`ORT-118`).
88
+ */
89
+ budgetRemaining(runId) {
90
+ return this.remainingBudget(runId);
91
+ }
92
+ /** Waiters by `runId:callId`. */
93
+ waiting = new Map();
94
+ /** Milliseconds each run has already spent parked, and when it last did. */
95
+ spent = new Map();
96
+ /**
97
+ * Waits for the user's answer to one call.
98
+ *
99
+ * Resolves rather than rejects on a timeout, because a refusal is an
100
+ * ordinary tool error the model can report — the same treatment every other
101
+ * refused call gets. Only an abort rejects, and only because that means
102
+ * nobody is listening any more.
103
+ */
104
+ async ask(runId, callId, owner, signal) {
105
+ const key = `${runId}:${callId}`;
106
+ const remaining = this.remainingBudget(runId);
107
+ if (remaining <= 0) {
108
+ // The run has already spent its whole waiting budget on earlier
109
+ // calls in this turn. Refusing straight away is the same outcome a
110
+ // timeout produces, without holding the connection for it.
111
+ this.logger.warn(`Run ${runId}: the waiting budget is exhausted; call ${callId} was refused without asking.`);
112
+ return { decision: 'deny', timedOut: true };
113
+ }
114
+ const askedAt = Date.now();
115
+ return new Promise((resolve, reject) => {
116
+ const settle = (outcome) => {
117
+ cleanup();
118
+ resolve(outcome);
119
+ };
120
+ let timer;
121
+ const expire = (after) => {
122
+ timer = setTimeout(() => {
123
+ this.logger.warn(`Run ${runId}: nobody answered the permission request ` +
124
+ `for call ${callId} within ${after}ms; refusing.`);
125
+ settle({ decision: 'deny', timedOut: true });
126
+ }, after);
127
+ };
128
+ expire(remaining);
129
+ // Re-arms the timer and tells the caller how long they now have.
130
+ // The budget already spent is *not* refunded: this grants a fresh
131
+ // allowance rather than rewinding the clock, so a run cannot be
132
+ // held open indefinitely by a client that never stops asking
133
+ // without a user pressing the button each time.
134
+ const extend = () => {
135
+ clearTimeout(timer);
136
+ expire(EXTENSION_MS);
137
+ return EXTENSION_MS;
138
+ };
139
+ const onAbort = () => {
140
+ cleanup();
141
+ reject(signal.reason ?? new Error('aborted'));
142
+ };
143
+ const cleanup = () => {
144
+ clearTimeout(timer);
145
+ signal.removeEventListener('abort', onAbort);
146
+ this.waiting.delete(key);
147
+ this.charge(runId, Date.now() - askedAt);
148
+ };
149
+ if (signal.aborted) {
150
+ onAbort();
151
+ return;
152
+ }
153
+ signal.addEventListener('abort', onAbort, { once: true });
154
+ this.waiting.set(key, { owner, settle, extend });
155
+ });
156
+ }
157
+ /**
158
+ * Delivers a decision. Returns false when nothing was waiting, or when the
159
+ * caller does not own the parked run.
160
+ *
161
+ * **Both answer false, hence the same 404**, deliberately: whether a run
162
+ * exists is not something a caller who does not own it may learn, and the
163
+ * route's message ("no longer waiting for an answer") is true either way
164
+ * from where they stand.
165
+ */
166
+ decide(runId, callId, decision, by) {
167
+ const key = `${runId}:${callId}`;
168
+ const waiter = this.waiting.get(key);
169
+ if (!waiter) {
170
+ return false;
171
+ }
172
+ if (waiter.owner.userId !== by.userId ||
173
+ waiter.owner.workspaceId !== by.workspaceId) {
174
+ this.logger.warn(`Run ${runId}: user ${by.userId} tried to answer a permission ` +
175
+ 'request for a run they do not own; refused.');
176
+ return false;
177
+ }
178
+ waiter.settle({ decision, timedOut: false });
179
+ return true;
180
+ }
181
+ /**
182
+ * Grants a parked call more time, at the user's request. Returns the new
183
+ * allowance in ms, or `null` when nothing is waiting or the caller does not
184
+ * own the run — the same conflation, for the same reason, as
185
+ * {@link decide}.
186
+ */
187
+ extend(runId, callId, by) {
188
+ const waiter = this.waiting.get(`${runId}:${callId}`);
189
+ if (!waiter)
190
+ return null;
191
+ if (waiter.owner.userId !== by.userId ||
192
+ waiter.owner.workspaceId !== by.workspaceId) {
193
+ this.logger.warn(`Run ${runId}: user ${by.userId} tried to extend a permission ` +
194
+ 'request for a run they do not own; refused.');
195
+ return null;
196
+ }
197
+ return waiter.extend();
198
+ }
199
+ /** What is left of `runId`'s waiting budget, pruning stale entries first. */
200
+ remainingBudget(runId) {
201
+ const now = Date.now();
202
+ for (const [id, entry] of this.spent) {
203
+ if (now - entry.at > BUDGET_TTL_MS) {
204
+ this.spent.delete(id);
205
+ }
206
+ }
207
+ return this.budgetMs - (this.spent.get(runId)?.ms ?? 0);
208
+ }
209
+ /** Charges `ms` of waiting to `runId`. */
210
+ charge(runId, ms) {
211
+ const entry = this.spent.get(runId);
212
+ this.spent.set(runId, {
213
+ ms: (entry?.ms ?? 0) + Math.max(0, ms),
214
+ at: Date.now()
215
+ });
216
+ }
217
+ };
218
+ exports.ToolPermissionBroker = ToolPermissionBroker;
219
+ exports.ToolPermissionBroker = ToolPermissionBroker = ToolPermissionBroker_1 = tslib_1.__decorate([
220
+ (0, common_1.Injectable)()
221
+ ], ToolPermissionBroker);
@@ -0,0 +1,30 @@
1
+ import type { Response } from 'express';
2
+ import { type PublicUser } from '@orthacms/identity-server';
3
+ import { CreateRunDto } from '../../application/dto/create-run.dto';
4
+ import { RunEngine } from '../../application/run-engine.service';
5
+ import { ContentTypeSummaryService } from '../../application/content-type-summary.service';
6
+ /**
7
+ * `POST /api/copilot/runs` — starts a turn and streams it back as Server-Sent
8
+ * Events (`docs/design/copilot.md` §5).
9
+ *
10
+ * **Every guard is explicit, and that is the point.** Only `AuthGuard` is
11
+ * global (`APP_GUARD`); `OriginGuard`, `PermissionsGuard` and `WorkspaceGuard`
12
+ * are per-controller decorators, so a copilot route that forgot
13
+ * `WorkspaceGuard` would authenticate fine, pass its permission check, and then
14
+ * run every tool with an **unvalidated** workspace id taken straight from a
15
+ * client header — silently unscoping the whole tool catalogue. The three below
16
+ * are, in order: reject cross-site POSTs, require `copilot:use`, and prove the
17
+ * caller is a member of the workspace they named.
18
+ *
19
+ * SSE on a POST rather than a GET + `EventSource`, because the turn has a body
20
+ * (the message and its context) and `EventSource` cannot send one. Verified to
21
+ * stream unbuffered through the admin's Vite dev proxy.
22
+ */
23
+ export declare class CreateRunController {
24
+ private readonly engine;
25
+ private readonly types;
26
+ private readonly logger;
27
+ constructor(engine: RunEngine, types: ContentTypeSummaryService);
28
+ create(body: CreateRunDto, user: PublicUser, workspaceId: string, res: Response): Promise<void>;
29
+ }
30
+ //# sourceMappingURL=create-run.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-run.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/http/controllers/create-run.controller.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,YAAY,EAAE,MAAM,sCAAsC,CAAC;AACpE,OAAO,EAGH,SAAS,EAEZ,MAAM,sCAAsC,CAAC;AAE9C,OAAO,EAAE,yBAAyB,EAAE,MAAM,gDAAgD,CAAC;AAI3F;;;;;;;;;;;;;;;;GAgBG;AACH,qBAGa,mBAAmB;IAIxB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAJ1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;gBAG1C,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,yBAAyB;IAkB/C,MAAM,CACA,IAAI,EAAE,YAAY,EACX,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,EAChC,GAAG,EAAE,QAAQ,GACrB,OAAO,CAAC,IAAI,CAAC;CA6FnB"}
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var CreateRunController_1;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.CreateRunController = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const common_1 = require("@nestjs/common");
7
+ const swagger_1 = require("@nestjs/swagger");
8
+ const identity_server_1 = require("@orthacms/identity-server");
9
+ const workspaces_server_1 = require("@orthacms/workspaces-server");
10
+ const create_run_dto_1 = require("../../application/dto/create-run.dto");
11
+ const run_engine_service_1 = require("../../application/run-engine.service");
12
+ const copilot_domain_1 = require("@orthacms/copilot-domain");
13
+ const content_type_summary_service_1 = require("../../application/content-type-summary.service");
14
+ const skill_catalog_service_1 = require("../../../skills/application/skill-catalog.service");
15
+ const sse_stream_1 = require("../sse-stream");
16
+ /**
17
+ * `POST /api/copilot/runs` — starts a turn and streams it back as Server-Sent
18
+ * Events (`docs/design/copilot.md` §5).
19
+ *
20
+ * **Every guard is explicit, and that is the point.** Only `AuthGuard` is
21
+ * global (`APP_GUARD`); `OriginGuard`, `PermissionsGuard` and `WorkspaceGuard`
22
+ * are per-controller decorators, so a copilot route that forgot
23
+ * `WorkspaceGuard` would authenticate fine, pass its permission check, and then
24
+ * run every tool with an **unvalidated** workspace id taken straight from a
25
+ * client header — silently unscoping the whole tool catalogue. The three below
26
+ * are, in order: reject cross-site POSTs, require `copilot:use`, and prove the
27
+ * caller is a member of the workspace they named.
28
+ *
29
+ * SSE on a POST rather than a GET + `EventSource`, because the turn has a body
30
+ * (the message and its context) and `EventSource` cannot send one. Verified to
31
+ * stream unbuffered through the admin's Vite dev proxy.
32
+ */
33
+ let CreateRunController = CreateRunController_1 = class CreateRunController {
34
+ engine;
35
+ types;
36
+ logger = new common_1.Logger(CreateRunController_1.name);
37
+ constructor(engine, types) {
38
+ this.engine = engine;
39
+ this.types = types;
40
+ }
41
+ async create(body, user, workspaceId, res) {
42
+ const stream = new sse_stream_1.SseStream(res);
43
+ const abort = new AbortController();
44
+ // Wired before the first await: a client that gives up during setup
45
+ // must still cancel the run.
46
+ stream.onClientDisconnect(abort);
47
+ const typeSummaries = await this.types.summaries(workspaceId);
48
+ stream.open();
49
+ try {
50
+ const events = this.engine.run({
51
+ userId: user.id,
52
+ userEmail: user.email,
53
+ roleId: user.roleId,
54
+ workspaceId,
55
+ conversationId: body.conversationId,
56
+ message: body.message,
57
+ ...(body.attachments?.length
58
+ ? {
59
+ attachments: body.attachments.map((attachment) => attachment.assetId)
60
+ }
61
+ : {}),
62
+ ...(body.skills?.length
63
+ ? { skills: body.skills.map((skill) => skill.name) }
64
+ : {}),
65
+ context: body.context ?? {},
66
+ uiLocale: body.uiLocale ?? 'en',
67
+ ...(body.provider || body.model
68
+ ? {
69
+ choice: {
70
+ ...(body.provider
71
+ ? { provider: body.provider }
72
+ : {}),
73
+ ...(body.model ? { model: body.model } : {})
74
+ }
75
+ }
76
+ : {}),
77
+ typeSummaries,
78
+ signal: abort.signal
79
+ });
80
+ for await (const event of events) {
81
+ stream.send(event);
82
+ }
83
+ }
84
+ catch (error) {
85
+ // The headers went out with `stream.open()`, so an error here can
86
+ // no longer become a status code — rethrowing would hand Nest's
87
+ // exception filter a response it cannot write to. It has to arrive
88
+ // as a frame instead, and be logged here rather than by the filter.
89
+ if (error instanceof run_engine_service_1.CopilotDisabledError) {
90
+ stream.send({
91
+ type: 'error',
92
+ message: 'Ortha AI is turned off for this deployment. An administrator can enable it.'
93
+ });
94
+ }
95
+ else if (error instanceof run_engine_service_1.AttachmentError ||
96
+ error instanceof skill_catalog_service_1.SkillResolutionError ||
97
+ error instanceof run_engine_service_1.UnknownModelChoiceError ||
98
+ error instanceof copilot_domain_1.UnknownModelError) {
99
+ // A bad attachment, an unavailable skill or a bad
100
+ // provider/model is the caller's
101
+ // mistake, and its own
102
+ // message already names what was asked for and what exists —
103
+ // far more useful than a generic failure, and safe to show
104
+ // because the catalogue is public to anyone with `copilot:use`.
105
+ stream.send({ type: 'error', message: error.message });
106
+ }
107
+ else {
108
+ this.logger.error('Copilot run failed after the stream was opened', error instanceof Error ? error.stack : String(error));
109
+ stream.send({
110
+ type: 'error',
111
+ message: 'Ortha AI could not complete this run.'
112
+ });
113
+ }
114
+ // Always terminate with `done`, whatever went wrong: the client's
115
+ // reducer keys "the turn is over" off this one frame, and a stream
116
+ // that just stops would leave the UI spinning forever.
117
+ stream.send({
118
+ type: 'done',
119
+ stopReason: 'error',
120
+ usage: { inputTokens: 0, outputTokens: 0 }
121
+ });
122
+ }
123
+ finally {
124
+ stream.close();
125
+ }
126
+ }
127
+ };
128
+ exports.CreateRunController = CreateRunController;
129
+ tslib_1.__decorate([
130
+ (0, common_1.Post)('runs')
131
+ // The response is written by hand, so Nest must not also send one.
132
+ ,
133
+ (0, common_1.HttpCode)(common_1.HttpStatus.OK),
134
+ (0, swagger_1.ApiOperation)({
135
+ summary: 'Start a copilot run',
136
+ description: 'Streams the run back as Server-Sent Events: `run-started`, then any ' +
137
+ 'number of `text-delta` / `tool-call` / `tool-result` frames, then exactly ' +
138
+ 'one `done`. An `error` frame may precede `done`.'
139
+ }),
140
+ (0, swagger_1.ApiBody)({ type: create_run_dto_1.CreateRunDto }),
141
+ (0, swagger_1.ApiResponse)({
142
+ status: 200,
143
+ description: 'An event stream (`text/event-stream`).'
144
+ }),
145
+ tslib_1.__param(0, (0, common_1.Body)()),
146
+ tslib_1.__param(1, (0, identity_server_1.CurrentUser)()),
147
+ tslib_1.__param(2, (0, workspaces_server_1.CurrentWorkspace)()),
148
+ tslib_1.__param(3, (0, common_1.Res)()),
149
+ tslib_1.__metadata("design:type", Function),
150
+ tslib_1.__metadata("design:paramtypes", [create_run_dto_1.CreateRunDto, Object, String, Object]),
151
+ tslib_1.__metadata("design:returntype", Promise)
152
+ ], CreateRunController.prototype, "create", null);
153
+ exports.CreateRunController = CreateRunController = CreateRunController_1 = tslib_1.__decorate([
154
+ (0, common_1.UseGuards)(identity_server_1.OriginGuard, identity_server_1.PermissionsGuard, workspaces_server_1.WorkspaceGuard),
155
+ (0, identity_server_1.RequirePermissions)(identity_server_1.PERMISSIONS.COPILOT_USE),
156
+ (0, common_1.Controller)('copilot'),
157
+ tslib_1.__metadata("design:paramtypes", [run_engine_service_1.RunEngine,
158
+ content_type_summary_service_1.ContentTypeSummaryService])
159
+ ], CreateRunController);
@@ -0,0 +1,22 @@
1
+ import { type PublicUser } from '@orthacms/identity-server';
2
+ import { ConversationRepository, type ConversationView, type MessageView } from '../../infrastructure/persistence/conversation.repository';
3
+ /** A thread plus its transcript. */
4
+ export interface ConversationDetail {
5
+ conversation: ConversationView;
6
+ messages: MessageView[];
7
+ }
8
+ /**
9
+ * `GET /api/copilot/conversations/:id` — one thread and its transcript, so a
10
+ * reopened panel or a reloaded tab renders what was persisted rather than only
11
+ * what streamed.
12
+ *
13
+ * A thread the caller doesn't own is a **404, not a 403** — the repository
14
+ * filters on user and workspace and reports "not found" either way, so an id
15
+ * cannot be probed for existence.
16
+ */
17
+ export declare class GetConversationController {
18
+ private readonly conversations;
19
+ constructor(conversations: ConversationRepository);
20
+ get(id: string, user: PublicUser, workspaceId: string): Promise<ConversationDetail>;
21
+ }
22
+ //# sourceMappingURL=get-conversation.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-conversation.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/http/controllers/get-conversation.controller.ts"],"names":[],"mappings":"AAQA,OAAO,EAKH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACH,sBAAsB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACnB,MAAM,0DAA0D,CAAC;AAElE,oCAAoC;AACpC,MAAM,WAAW,kBAAkB;IAC/B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,QAAQ,EAAE,WAAW,EAAE,CAAC;CAC3B;AAED;;;;;;;;GAQG;AACH,qBAGa,yBAAyB;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAAb,aAAa,EAAE,sBAAsB;IAM5D,GAAG,CACuB,EAAE,EAAE,MAAM,EACvB,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,kBAAkB,CAAC;CAWjC"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.GetConversationController = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const swagger_1 = require("@nestjs/swagger");
7
+ const identity_server_1 = require("@orthacms/identity-server");
8
+ const workspaces_server_1 = require("@orthacms/workspaces-server");
9
+ const conversation_repository_1 = require("../../infrastructure/persistence/conversation.repository");
10
+ /**
11
+ * `GET /api/copilot/conversations/:id` — one thread and its transcript, so a
12
+ * reopened panel or a reloaded tab renders what was persisted rather than only
13
+ * what streamed.
14
+ *
15
+ * A thread the caller doesn't own is a **404, not a 403** — the repository
16
+ * filters on user and workspace and reports "not found" either way, so an id
17
+ * cannot be probed for existence.
18
+ */
19
+ let GetConversationController = class GetConversationController {
20
+ conversations;
21
+ constructor(conversations) {
22
+ this.conversations = conversations;
23
+ }
24
+ async get(id, user, workspaceId) {
25
+ const conversation = await this.conversations.findOrFail(id, user.id, workspaceId);
26
+ return {
27
+ conversation,
28
+ messages: await this.conversations.messages(conversation.id)
29
+ };
30
+ }
31
+ };
32
+ exports.GetConversationController = GetConversationController;
33
+ tslib_1.__decorate([
34
+ (0, common_1.Get)('conversations/:id'),
35
+ (0, swagger_1.ApiOperation)({
36
+ summary: 'Get one copilot conversation and its transcript'
37
+ }),
38
+ tslib_1.__param(0, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
39
+ tslib_1.__param(1, (0, identity_server_1.CurrentUser)()),
40
+ tslib_1.__param(2, (0, workspaces_server_1.CurrentWorkspace)()),
41
+ tslib_1.__metadata("design:type", Function),
42
+ tslib_1.__metadata("design:paramtypes", [String, Object, String]),
43
+ tslib_1.__metadata("design:returntype", Promise)
44
+ ], GetConversationController.prototype, "get", null);
45
+ exports.GetConversationController = GetConversationController = tslib_1.__decorate([
46
+ (0, common_1.UseGuards)(identity_server_1.PermissionsGuard, workspaces_server_1.WorkspaceGuard),
47
+ (0, identity_server_1.RequirePermissions)(identity_server_1.PERMISSIONS.COPILOT_USE),
48
+ (0, common_1.Controller)('copilot'),
49
+ tslib_1.__metadata("design:paramtypes", [conversation_repository_1.ConversationRepository])
50
+ ], GetConversationController);
@@ -0,0 +1,20 @@
1
+ import { type PublicUser } from '@orthacms/identity-server';
2
+ import { ConversationRepository, type ConversationView } from '../../infrastructure/persistence/conversation.repository';
3
+ import { ListConversationsQueryDto } from '../../application/dto/list-conversations-query.dto';
4
+ /**
5
+ * `GET /api/copilot/conversations` — the signed-in user's threads in the open
6
+ * workspace, most recently used first.
7
+ *
8
+ * No `OriginGuard`: it guards state-changing requests, and this reads. The
9
+ * scoping that matters is in the repository, which filters on user **and**
10
+ * workspace — the guard proves the caller belongs to the workspace, and the
11
+ * query proves the threads belong to the caller.
12
+ */
13
+ export declare class ListConversationsController {
14
+ private readonly conversations;
15
+ constructor(conversations: ConversationRepository);
16
+ list(query: ListConversationsQueryDto, user: PublicUser, workspaceId: string): Promise<{
17
+ items: ConversationView[];
18
+ }>;
19
+ }
20
+ //# sourceMappingURL=list-conversations.controller.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-conversations.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/http/controllers/list-conversations.controller.ts"],"names":[],"mappings":"AAEA,OAAO,EAKH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACH,sBAAsB,EACtB,KAAK,gBAAgB,EACxB,MAAM,0DAA0D,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,oDAAoD,CAAC;AAE/F;;;;;;;;GAQG;AACH,qBAGa,2BAA2B;IACxB,OAAO,CAAC,QAAQ,CAAC,aAAa;gBAAb,aAAa,EAAE,sBAAsB;IAI5D,IAAI,CACG,KAAK,EAAE,yBAAyB,EAC1B,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC;QAAE,KAAK,EAAE,gBAAgB,EAAE,CAAA;KAAE,CAAC;CAS5C"}