@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,86 @@
1
+ import { type Database } from '@orthacms/database';
2
+ import type { ProposalChange, ProposalStatus, ProposalTarget } from '@orthacms/copilot-domain';
3
+ /** One proposal as the chat panel and the review queue render it. */
4
+ export interface ProposalView {
5
+ id: string;
6
+ conversationId: string;
7
+ runId: string;
8
+ toolCallId: string;
9
+ toolName: string;
10
+ kind: string;
11
+ workspaceId: string;
12
+ createdBy: string;
13
+ target: ProposalTarget;
14
+ patch: Readonly<Record<string, unknown>>;
15
+ summary: string;
16
+ changes: readonly ProposalChange[] | null;
17
+ status: ProposalStatus;
18
+ decidedBy: string | null;
19
+ decidedAt: Date | null;
20
+ result: Record<string, unknown> | null;
21
+ error: string | null;
22
+ createdAt: Date;
23
+ }
24
+ /** What {@link ProposalRepository.create} writes. */
25
+ export interface NewProposal {
26
+ conversationId: string;
27
+ runId: string;
28
+ toolCallId: string;
29
+ toolName: string;
30
+ kind: string;
31
+ workspaceId: string;
32
+ createdBy: string;
33
+ target: ProposalTarget;
34
+ patch: Readonly<Record<string, unknown>>;
35
+ summary: string;
36
+ changes?: readonly ProposalChange[];
37
+ }
38
+ /**
39
+ * Reads and writes `copilot_proposals`.
40
+ *
41
+ * **Every method takes the owning `workspaceId` and filters on it**, for the
42
+ * same reason `ConversationRepository` does: `WorkspaceGuard` proves the caller
43
+ * belongs to the workspace they named, but nothing upstream proves a *proposal
44
+ * id* belongs to that workspace.
45
+ *
46
+ * It deliberately does **not** filter by `createdBy`. A proposal is a review
47
+ * item, not private correspondence: a second editor should be able to accept a
48
+ * change a colleague's copilot drafted, which is the ordinary case for a review
49
+ * queue. What bounds that is the accepting user's own permissions, re-resolved
50
+ * at accept time.
51
+ */
52
+ export declare class ProposalRepository {
53
+ private readonly db;
54
+ constructor(db: Database);
55
+ /** Persists a new `pending` proposal and returns it. */
56
+ create(input: NewProposal): Promise<ProposalView>;
57
+ /** One proposal in this workspace, or null. */
58
+ find(id: string, workspaceId: string): Promise<ProposalView | null>;
59
+ /** This workspace's proposals, newest first, optionally by status. */
60
+ list(workspaceId: string, options?: {
61
+ status?: ProposalStatus;
62
+ conversationId?: string;
63
+ }): Promise<ProposalView[]>;
64
+ /**
65
+ * Moves a **pending** proposal to `accepted` or `rejected`, returning the
66
+ * updated row — or `null` when it was already decided.
67
+ *
68
+ * The `status = 'pending'` predicate is the concurrency guard, not a
69
+ * convenience: two reviewers clicking Accept at the same moment would
70
+ * otherwise both apply the change. The loser gets `null` and reports that
71
+ * the proposal was already decided.
72
+ */
73
+ decide(id: string, workspaceId: string, status: Exclude<ProposalStatus, 'pending'>, decidedBy: string, result?: Record<string, unknown>): Promise<ProposalView | null>;
74
+ /** Records what the applier reported on an already-accepted proposal. */
75
+ recordResult(id: string, workspaceId: string, result: Record<string, unknown>): Promise<ProposalView | null>;
76
+ /**
77
+ * Returns a proposal whose apply failed to `pending`, recording why.
78
+ *
79
+ * A failed apply is deliberately **not** a status of its own: it is a
80
+ * proposal that still needs deciding, and a `failed` state would need
81
+ * something to clear it before a retry could work. Clearing `decidedBy` /
82
+ * `decidedAt` too keeps the row honest — nobody decided it.
83
+ */
84
+ reopen(id: string, workspaceId: string, message: string): Promise<void>;
85
+ }
86
+ //# sourceMappingURL=proposal.repository.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal.repository.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/infrastructure/persistence/proposal.repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ProposalRepository = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const common_1 = require("@nestjs/common");
6
+ const drizzle_orm_1 = require("drizzle-orm");
7
+ const database_1 = require("@orthacms/database");
8
+ const proposals_1 = require("../schema/proposals");
9
+ /**
10
+ * Reads and writes `copilot_proposals`.
11
+ *
12
+ * **Every method takes the owning `workspaceId` and filters on it**, for the
13
+ * same reason `ConversationRepository` does: `WorkspaceGuard` proves the caller
14
+ * belongs to the workspace they named, but nothing upstream proves a *proposal
15
+ * id* belongs to that workspace.
16
+ *
17
+ * It deliberately does **not** filter by `createdBy`. A proposal is a review
18
+ * item, not private correspondence: a second editor should be able to accept a
19
+ * change a colleague's copilot drafted, which is the ordinary case for a review
20
+ * queue. What bounds that is the accepting user's own permissions, re-resolved
21
+ * at accept time.
22
+ */
23
+ let ProposalRepository = class ProposalRepository {
24
+ db;
25
+ constructor(db) {
26
+ this.db = db;
27
+ }
28
+ /** Persists a new `pending` proposal and returns it. */
29
+ async create(input) {
30
+ const [row] = await this.db
31
+ .insert(proposals_1.copilotProposals)
32
+ .values({
33
+ conversationId: input.conversationId,
34
+ runId: input.runId,
35
+ toolCallId: input.toolCallId,
36
+ toolName: input.toolName,
37
+ kind: input.kind,
38
+ workspaceId: input.workspaceId,
39
+ createdBy: input.createdBy,
40
+ target: input.target,
41
+ patch: input.patch,
42
+ summary: input.summary,
43
+ changes: input.changes ?? null,
44
+ status: 'pending'
45
+ })
46
+ .returning();
47
+ return toView(row);
48
+ }
49
+ /** One proposal in this workspace, or null. */
50
+ async find(id, workspaceId) {
51
+ const [row] = await this.db
52
+ .select()
53
+ .from(proposals_1.copilotProposals)
54
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(proposals_1.copilotProposals.id, id), (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.workspaceId, workspaceId)))
55
+ .limit(1);
56
+ return row ? toView(row) : null;
57
+ }
58
+ /** This workspace's proposals, newest first, optionally by status. */
59
+ async list(workspaceId, options = {}) {
60
+ const rows = await this.db
61
+ .select()
62
+ .from(proposals_1.copilotProposals)
63
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(proposals_1.copilotProposals.workspaceId, workspaceId), options.status
64
+ ? (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.status, options.status)
65
+ : undefined, options.conversationId
66
+ ? (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.conversationId, options.conversationId)
67
+ : undefined))
68
+ .orderBy((0, drizzle_orm_1.desc)(proposals_1.copilotProposals.createdAt));
69
+ return rows.map(toView);
70
+ }
71
+ /**
72
+ * Moves a **pending** proposal to `accepted` or `rejected`, returning the
73
+ * updated row — or `null` when it was already decided.
74
+ *
75
+ * The `status = 'pending'` predicate is the concurrency guard, not a
76
+ * convenience: two reviewers clicking Accept at the same moment would
77
+ * otherwise both apply the change. The loser gets `null` and reports that
78
+ * the proposal was already decided.
79
+ */
80
+ async decide(id, workspaceId, status, decidedBy, result) {
81
+ const [row] = await this.db
82
+ .update(proposals_1.copilotProposals)
83
+ .set({
84
+ status,
85
+ decidedBy,
86
+ decidedAt: new Date(),
87
+ result: result ?? null,
88
+ error: null
89
+ })
90
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(proposals_1.copilotProposals.id, id), (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.workspaceId, workspaceId), (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.status, 'pending')))
91
+ .returning();
92
+ return row ? toView(row) : null;
93
+ }
94
+ /** Records what the applier reported on an already-accepted proposal. */
95
+ async recordResult(id, workspaceId, result) {
96
+ const [row] = await this.db
97
+ .update(proposals_1.copilotProposals)
98
+ .set({ result })
99
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(proposals_1.copilotProposals.id, id), (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.workspaceId, workspaceId)))
100
+ .returning();
101
+ return row ? toView(row) : null;
102
+ }
103
+ /**
104
+ * Returns a proposal whose apply failed to `pending`, recording why.
105
+ *
106
+ * A failed apply is deliberately **not** a status of its own: it is a
107
+ * proposal that still needs deciding, and a `failed` state would need
108
+ * something to clear it before a retry could work. Clearing `decidedBy` /
109
+ * `decidedAt` too keeps the row honest — nobody decided it.
110
+ */
111
+ async reopen(id, workspaceId, message) {
112
+ await this.db
113
+ .update(proposals_1.copilotProposals)
114
+ .set({
115
+ status: 'pending',
116
+ decidedBy: null,
117
+ decidedAt: null,
118
+ result: null,
119
+ error: message
120
+ })
121
+ .where((0, drizzle_orm_1.and)((0, drizzle_orm_1.eq)(proposals_1.copilotProposals.id, id), (0, drizzle_orm_1.eq)(proposals_1.copilotProposals.workspaceId, workspaceId)));
122
+ }
123
+ };
124
+ exports.ProposalRepository = ProposalRepository;
125
+ exports.ProposalRepository = ProposalRepository = tslib_1.__decorate([
126
+ (0, common_1.Injectable)(),
127
+ tslib_1.__param(0, (0, database_1.InjectDatabase)()),
128
+ tslib_1.__metadata("design:paramtypes", [Object])
129
+ ], ProposalRepository);
130
+ /** Row → view, narrowing the jsonb columns to their declared shapes. */
131
+ function toView(row) {
132
+ return {
133
+ id: row.id,
134
+ conversationId: row.conversationId,
135
+ runId: row.runId,
136
+ toolCallId: row.toolCallId,
137
+ toolName: row.toolName,
138
+ kind: row.kind,
139
+ workspaceId: row.workspaceId,
140
+ createdBy: row.createdBy,
141
+ target: row.target,
142
+ patch: row.patch,
143
+ summary: row.summary,
144
+ changes: row.changes ?? null,
145
+ status: row.status,
146
+ decidedBy: row.decidedBy,
147
+ decidedAt: row.decidedAt,
148
+ result: row.result ?? null,
149
+ error: row.error,
150
+ createdAt: row.createdAt
151
+ };
152
+ }
@@ -0,0 +1,189 @@
1
+ /**
2
+ * One chat thread, scoped to a **user × workspace**. There is no sharing in
3
+ * v1: a conversation belongs to the person who started it, and every read is
4
+ * filtered by both columns, so a valid session for workspace A can never reach
5
+ * a thread in workspace B.
6
+ *
7
+ * Both foreign keys cascade — deleting a user or a workspace takes their
8
+ * transcripts with it, which is the behaviour a deletion request needs.
9
+ */
10
+ export declare const copilotConversations: import("drizzle-orm/pg-core").PgTableWithColumns<{
11
+ name: "copilot_conversations";
12
+ schema: undefined;
13
+ columns: {
14
+ id: import("drizzle-orm/pg-core").PgColumn<{
15
+ name: "id";
16
+ tableName: "copilot_conversations";
17
+ dataType: "string";
18
+ columnType: "PgUUID";
19
+ data: string;
20
+ driverParam: string;
21
+ notNull: true;
22
+ hasDefault: true;
23
+ isPrimaryKey: true;
24
+ isAutoincrement: false;
25
+ hasRuntimeDefault: false;
26
+ enumValues: undefined;
27
+ baseColumn: never;
28
+ identity: undefined;
29
+ generated: undefined;
30
+ }, {}, {}>;
31
+ userId: import("drizzle-orm/pg-core").PgColumn<{
32
+ name: "user_id";
33
+ tableName: "copilot_conversations";
34
+ dataType: "string";
35
+ columnType: "PgUUID";
36
+ data: string;
37
+ driverParam: string;
38
+ notNull: true;
39
+ hasDefault: false;
40
+ isPrimaryKey: false;
41
+ isAutoincrement: false;
42
+ hasRuntimeDefault: false;
43
+ enumValues: undefined;
44
+ baseColumn: never;
45
+ identity: undefined;
46
+ generated: undefined;
47
+ }, {}, {}>;
48
+ workspaceId: import("drizzle-orm/pg-core").PgColumn<{
49
+ name: "workspace_id";
50
+ tableName: "copilot_conversations";
51
+ dataType: "string";
52
+ columnType: "PgUUID";
53
+ data: string;
54
+ driverParam: string;
55
+ notNull: true;
56
+ hasDefault: false;
57
+ isPrimaryKey: false;
58
+ isAutoincrement: false;
59
+ hasRuntimeDefault: false;
60
+ enumValues: undefined;
61
+ baseColumn: never;
62
+ identity: undefined;
63
+ generated: undefined;
64
+ }, {}, {}>;
65
+ title: import("drizzle-orm/pg-core").PgColumn<{
66
+ name: "title";
67
+ tableName: "copilot_conversations";
68
+ dataType: "string";
69
+ columnType: "PgText";
70
+ data: string;
71
+ driverParam: string;
72
+ notNull: false;
73
+ hasDefault: false;
74
+ isPrimaryKey: false;
75
+ isAutoincrement: false;
76
+ hasRuntimeDefault: false;
77
+ enumValues: [string, ...string[]];
78
+ baseColumn: never;
79
+ identity: undefined;
80
+ generated: undefined;
81
+ }, {}, {}>;
82
+ surface: import("drizzle-orm/pg-core").PgColumn<{
83
+ name: "surface";
84
+ tableName: "copilot_conversations";
85
+ dataType: "string";
86
+ columnType: "PgText";
87
+ data: string;
88
+ driverParam: string;
89
+ notNull: true;
90
+ hasDefault: true;
91
+ isPrimaryKey: false;
92
+ isAutoincrement: false;
93
+ hasRuntimeDefault: false;
94
+ enumValues: [string, ...string[]];
95
+ baseColumn: never;
96
+ identity: undefined;
97
+ generated: undefined;
98
+ }, {}, {}>;
99
+ archived: import("drizzle-orm/pg-core").PgColumn<{
100
+ name: "archived";
101
+ tableName: "copilot_conversations";
102
+ dataType: "boolean";
103
+ columnType: "PgBoolean";
104
+ data: boolean;
105
+ driverParam: boolean;
106
+ notNull: true;
107
+ hasDefault: true;
108
+ isPrimaryKey: false;
109
+ isAutoincrement: false;
110
+ hasRuntimeDefault: false;
111
+ enumValues: undefined;
112
+ baseColumn: never;
113
+ identity: undefined;
114
+ generated: undefined;
115
+ }, {}, {}>;
116
+ modelChoice: import("drizzle-orm/pg-core").PgColumn<{
117
+ name: "model_choice";
118
+ tableName: "copilot_conversations";
119
+ dataType: "string";
120
+ columnType: "PgText";
121
+ data: string;
122
+ driverParam: string;
123
+ notNull: false;
124
+ hasDefault: false;
125
+ isPrimaryKey: false;
126
+ isAutoincrement: false;
127
+ hasRuntimeDefault: false;
128
+ enumValues: [string, ...string[]];
129
+ baseColumn: never;
130
+ identity: undefined;
131
+ generated: undefined;
132
+ }, {}, {}>;
133
+ allowedTools: import("drizzle-orm/pg-core").PgColumn<{
134
+ name: "allowed_tools";
135
+ tableName: "copilot_conversations";
136
+ dataType: "json";
137
+ columnType: "PgJsonb";
138
+ data: string[];
139
+ driverParam: unknown;
140
+ notNull: true;
141
+ hasDefault: true;
142
+ isPrimaryKey: false;
143
+ isAutoincrement: false;
144
+ hasRuntimeDefault: false;
145
+ enumValues: undefined;
146
+ baseColumn: never;
147
+ identity: undefined;
148
+ generated: undefined;
149
+ }, {}, {
150
+ $type: string[];
151
+ }>;
152
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
153
+ name: "created_at";
154
+ tableName: "copilot_conversations";
155
+ dataType: "date";
156
+ columnType: "PgTimestamp";
157
+ data: Date;
158
+ driverParam: string;
159
+ notNull: true;
160
+ hasDefault: true;
161
+ isPrimaryKey: false;
162
+ isAutoincrement: false;
163
+ hasRuntimeDefault: false;
164
+ enumValues: undefined;
165
+ baseColumn: never;
166
+ identity: undefined;
167
+ generated: undefined;
168
+ }, {}, {}>;
169
+ updatedAt: import("drizzle-orm/pg-core").PgColumn<{
170
+ name: "updated_at";
171
+ tableName: "copilot_conversations";
172
+ dataType: "date";
173
+ columnType: "PgTimestamp";
174
+ data: Date;
175
+ driverParam: string;
176
+ notNull: true;
177
+ hasDefault: true;
178
+ isPrimaryKey: false;
179
+ isAutoincrement: false;
180
+ hasRuntimeDefault: false;
181
+ enumValues: undefined;
182
+ baseColumn: never;
183
+ identity: undefined;
184
+ generated: undefined;
185
+ }, {}, {}>;
186
+ };
187
+ dialect: "pg";
188
+ }>;
189
+ //# sourceMappingURL=conversations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/infrastructure/schema/conversations.ts"],"names":[],"mappings":"AAWA;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA6FhC,CAAC"}
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copilotConversations = void 0;
4
+ const pg_core_1 = require("drizzle-orm/pg-core");
5
+ const external_refs_1 = require("./external-refs");
6
+ /**
7
+ * One chat thread, scoped to a **user × workspace**. There is no sharing in
8
+ * v1: a conversation belongs to the person who started it, and every read is
9
+ * filtered by both columns, so a valid session for workspace A can never reach
10
+ * a thread in workspace B.
11
+ *
12
+ * Both foreign keys cascade — deleting a user or a workspace takes their
13
+ * transcripts with it, which is the behaviour a deletion request needs.
14
+ */
15
+ exports.copilotConversations = (0, pg_core_1.pgTable)('copilot_conversations', {
16
+ /** Primary key. */
17
+ id: (0, pg_core_1.uuid)('id').primaryKey().defaultRandom(),
18
+ /** The owning user. Threads are private to them. */
19
+ userId: (0, pg_core_1.uuid)('user_id')
20
+ .notNull()
21
+ .references(() => external_refs_1.users.id, { onDelete: 'cascade' }),
22
+ /** The workspace the thread's runs are scoped to. */
23
+ workspaceId: (0, pg_core_1.uuid)('workspace_id')
24
+ .notNull()
25
+ .references(() => external_refs_1.workspaces.id, { onDelete: 'cascade' }),
26
+ /**
27
+ * Display title. Null until the first turn names it — derived from the
28
+ * opening user message rather than costing a model call.
29
+ */
30
+ title: (0, pg_core_1.text)('title'),
31
+ /**
32
+ * Where the thread was started from (`chat`, `palette`, `entry`, …).
33
+ * Recorded now because a thread continued from an inline entry point
34
+ * should still render as the surface it came from (design §2).
35
+ */
36
+ surface: (0, pg_core_1.text)('surface').notNull().default('chat'),
37
+ /** Hidden from the list without losing the transcript. */
38
+ archived: (0, pg_core_1.boolean)('archived').notNull().default(false),
39
+ /**
40
+ * The model the person last **picked** for this thread, so reopening it
41
+ * in another tab — or after a reload — offers the backend they chose
42
+ * rather than silently falling back to the deployment default.
43
+ *
44
+ * A memory, **not a pin**: the run route still takes its provider and
45
+ * model per turn, so a conversation can start cheap and escalate. What
46
+ * this column fixes is forgetting the choice, which nobody asked for.
47
+ *
48
+ * Three states, and telling the last two apart is the whole reason it is
49
+ * one nullable text column rather than a `provider`/`model` pair:
50
+ *
51
+ * - `null` — nobody has picked on this thread. The client keeps whatever
52
+ * its own per-tab memory seeded the chat with.
53
+ * - `'default'` — the person explicitly picked **Default**, i.e. "let
54
+ * the host's resolver decide", which can differ per run. Pinning
55
+ * today's default provider instead would silently opt them out of
56
+ * that routing.
57
+ * - `'<provider>:<model>'` — a registered backend, in the same
58
+ * `modelChoiceKey` form the picker uses. A provider name cannot
59
+ * contain a colon and a model id routinely does, so the **first**
60
+ * colon separates the two — and `'default'` has none at all, which is
61
+ * what keeps the sentinel unambiguous.
62
+ *
63
+ * The value is validated against `ModelRegistry.catalogue()` on write,
64
+ * so it can only ever name a backend the operator configured.
65
+ */
66
+ modelChoice: (0, pg_core_1.text)('model_choice'),
67
+ /**
68
+ * Tool names the user answered "allow for this chat" to.
69
+ *
70
+ * **On the thread, not on the user**, and that is the whole scope: it
71
+ * dies when the thread does, so there is no standing permission anyone
72
+ * has to remember granting or go and revoke. A per-user allow-list
73
+ * would be a policy outliving its context, which is the shape
74
+ * [ADR-0009](../../../../../../docs/adr/0009-copilot-applies-directly.md)
75
+ * just finished removing.
76
+ *
77
+ * It is a **usability** memory, never an authority one: every call it
78
+ * skips the prompt for is still authorized against the caller's live
79
+ * grants by `ToolRegistry.call`.
80
+ */
81
+ allowedTools: (0, pg_core_1.jsonb)('allowed_tools')
82
+ .notNull()
83
+ .$type()
84
+ .default([]),
85
+ /** Row creation timestamp. */
86
+ createdAt: (0, pg_core_1.timestamp)('created_at', { withTimezone: true })
87
+ .notNull()
88
+ .defaultNow(),
89
+ /**
90
+ * Bumped on every turn, so the thread list can sort by recency without
91
+ * joining the messages table.
92
+ */
93
+ updatedAt: (0, pg_core_1.timestamp)('updated_at', { withTimezone: true })
94
+ .notNull()
95
+ .defaultNow()
96
+ }, (table) => [
97
+ // The thread list's exact query: this user's threads in this
98
+ // workspace, most recently used first.
99
+ (0, pg_core_1.index)('copilot_conversations_owner_idx').on(table.userId, table.workspaceId, table.updatedAt)
100
+ ]);
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Reference-only projections of tables other plugins own, carrying just the
3
+ * `id` column this plugin's foreign keys target. Same pattern (and same two
4
+ * reasons) as `workspaces/server`'s `external-refs.ts`:
5
+ *
6
+ * - the generated copilot migration can emit the cross-context FKs
7
+ * `copilot_conversations.user_id → users(id)` and
8
+ * `… .workspace_id → workspaces(id)` — the host applies identity's and
9
+ * workspaces' migrations first, so both physical tables already exist when
10
+ * this one runs; and
11
+ * - `drizzle-kit generate` sees a **pure-Drizzle** schema graph. Importing
12
+ * another plugin's runtime barrel here would pull its NestJS providers into
13
+ * drizzle-kit's esbuild pass, which has no `experimentalDecorators` and
14
+ * dies on their parameter decorators.
15
+ *
16
+ * Deliberately **not** re-exported from `schema/index.ts`, so drizzle-kit
17
+ * references these only in the FKs and never emits a duplicate `CREATE TABLE`.
18
+ */
19
+ export declare const users: import("drizzle-orm/pg-core").PgTableWithColumns<{
20
+ name: "users";
21
+ schema: undefined;
22
+ columns: {
23
+ id: import("drizzle-orm/pg-core").PgColumn<{
24
+ name: "id";
25
+ tableName: "users";
26
+ dataType: "string";
27
+ columnType: "PgUUID";
28
+ data: string;
29
+ driverParam: string;
30
+ notNull: true;
31
+ hasDefault: false;
32
+ isPrimaryKey: true;
33
+ isAutoincrement: false;
34
+ hasRuntimeDefault: false;
35
+ enumValues: undefined;
36
+ baseColumn: never;
37
+ identity: undefined;
38
+ generated: undefined;
39
+ }, {}, {}>;
40
+ };
41
+ dialect: "pg";
42
+ }>;
43
+ export declare const workspaces: import("drizzle-orm/pg-core").PgTableWithColumns<{
44
+ name: "workspaces";
45
+ schema: undefined;
46
+ columns: {
47
+ id: import("drizzle-orm/pg-core").PgColumn<{
48
+ name: "id";
49
+ tableName: "workspaces";
50
+ dataType: "string";
51
+ columnType: "PgUUID";
52
+ data: string;
53
+ driverParam: string;
54
+ notNull: true;
55
+ hasDefault: false;
56
+ isPrimaryKey: true;
57
+ isAutoincrement: false;
58
+ hasRuntimeDefault: false;
59
+ enumValues: undefined;
60
+ baseColumn: never;
61
+ identity: undefined;
62
+ generated: undefined;
63
+ }, {}, {}>;
64
+ };
65
+ dialect: "pg";
66
+ }>;
67
+ //# sourceMappingURL=external-refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"external-refs.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/infrastructure/schema/external-refs.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;;;;;EAEhB,CAAC;AAEH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;EAErB,CAAC"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.workspaces = exports.users = void 0;
4
+ const pg_core_1 = require("drizzle-orm/pg-core");
5
+ /**
6
+ * Reference-only projections of tables other plugins own, carrying just the
7
+ * `id` column this plugin's foreign keys target. Same pattern (and same two
8
+ * reasons) as `workspaces/server`'s `external-refs.ts`:
9
+ *
10
+ * - the generated copilot migration can emit the cross-context FKs
11
+ * `copilot_conversations.user_id → users(id)` and
12
+ * `… .workspace_id → workspaces(id)` — the host applies identity's and
13
+ * workspaces' migrations first, so both physical tables already exist when
14
+ * this one runs; and
15
+ * - `drizzle-kit generate` sees a **pure-Drizzle** schema graph. Importing
16
+ * another plugin's runtime barrel here would pull its NestJS providers into
17
+ * drizzle-kit's esbuild pass, which has no `experimentalDecorators` and
18
+ * dies on their parameter decorators.
19
+ *
20
+ * Deliberately **not** re-exported from `schema/index.ts`, so drizzle-kit
21
+ * references these only in the FKs and never emits a duplicate `CREATE TABLE`.
22
+ */
23
+ exports.users = (0, pg_core_1.pgTable)('users', {
24
+ id: (0, pg_core_1.uuid)('id').primaryKey()
25
+ });
26
+ exports.workspaces = (0, pg_core_1.pgTable)('workspaces', {
27
+ id: (0, pg_core_1.uuid)('id').primaryKey()
28
+ });
@@ -0,0 +1,5 @@
1
+ export { copilotConversations } from './conversations';
2
+ export { copilotMessages, copilotMessageRole } from './messages';
3
+ export { copilotToolCalls } from './tool-calls';
4
+ export { copilotProposals } from './proposals';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/infrastructure/schema/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copilotProposals = exports.copilotToolCalls = exports.copilotMessageRole = exports.copilotMessages = exports.copilotConversations = void 0;
4
+ var conversations_1 = require("./conversations");
5
+ Object.defineProperty(exports, "copilotConversations", { enumerable: true, get: function () { return conversations_1.copilotConversations; } });
6
+ var messages_1 = require("./messages");
7
+ Object.defineProperty(exports, "copilotMessages", { enumerable: true, get: function () { return messages_1.copilotMessages; } });
8
+ Object.defineProperty(exports, "copilotMessageRole", { enumerable: true, get: function () { return messages_1.copilotMessageRole; } });
9
+ var tool_calls_1 = require("./tool-calls");
10
+ Object.defineProperty(exports, "copilotToolCalls", { enumerable: true, get: function () { return tool_calls_1.copilotToolCalls; } });
11
+ var proposals_1 = require("./proposals");
12
+ Object.defineProperty(exports, "copilotProposals", { enumerable: true, get: function () { return proposals_1.copilotProposals; } });