@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,68 @@
1
+ /**
2
+ * Longest title we accept.
3
+ *
4
+ * Generous next to what the server derives from a first message, because this
5
+ * one is typed by a human who may be filing a thread rather than naming it — and
6
+ * still bounded, since the rail renders it on one truncated line.
7
+ */
8
+ export declare const MAX_TITLE_LENGTH = 200;
9
+ /**
10
+ * The stored form of "let the host's resolver decide".
11
+ *
12
+ * **Legacy: accepted, no longer produced.** It was a real option in the picker
13
+ * back when the deployment had a `defaultProvider` for it to mean; both are
14
+ * gone, and the admin now opens a chat on the catalogue's first entry and names
15
+ * it per turn. Still accepted here because rows carrying it exist and an older
16
+ * client may still send it — a client that does gets its thread read back as
17
+ * "nobody picked", which lands on that same first entry. It is unambiguous
18
+ * against a `'<provider>:<model>'` key because it contains no colon.
19
+ */
20
+ export declare const MODEL_CHOICE_DEFAULT = "default";
21
+ /**
22
+ * Longest model choice we accept: a provider name plus a model id plus the
23
+ * separator. Bounded because it is stored and echoed back to the picker.
24
+ */
25
+ export declare const MAX_MODEL_CHOICE_LENGTH = 200;
26
+ /**
27
+ * Body of `PATCH /api/copilot/conversations/:id`.
28
+ *
29
+ * A **patch**, so every property is optional and only the ones present are
30
+ * written. The controller rejects a body with neither: an empty patch is a
31
+ * request that cannot mean anything, and silently answering 200 to it would
32
+ * report success for a write that never happened.
33
+ *
34
+ * Every property is declared because the host's `ValidationPipe` runs with
35
+ * `forbidNonWhitelisted` — an undeclared key is a 400 naming it.
36
+ */
37
+ export declare class UpdateConversationDto {
38
+ /**
39
+ * Rename the thread.
40
+ *
41
+ * Trimmed before validation, so " " is the empty string and fails the
42
+ * length check rather than being stored as a title that renders as nothing.
43
+ */
44
+ title?: string;
45
+ /**
46
+ * Hide the thread from the list, or bring it back.
47
+ *
48
+ * Archiving is the only removal this API offers, and it is deliberately
49
+ * reversible — the transcript is kept, and `GET /conversations/:id` still
50
+ * serves it, so a link to an archived thread keeps working.
51
+ */
52
+ archived?: boolean;
53
+ /**
54
+ * Record the model picked for this thread, so reopening it in another tab
55
+ * offers the backend the person chose instead of the deployment default.
56
+ *
57
+ * `'<provider>:<model>'` — or the legacy `'default'`, see above —
58
+ * the same key the picker uses, split on its **first** colon (a model id
59
+ * routinely contains one; a provider name cannot). The controller checks the
60
+ * pair against `ModelRegistry.catalogue()`, so this can only ever name a
61
+ * backend the operator configured.
62
+ *
63
+ * It is a **memory, not a pin**: the run route still takes its provider and
64
+ * model per turn, and nothing here constrains the next one.
65
+ */
66
+ modelChoice?: string;
67
+ }
68
+ //# sourceMappingURL=update-conversation.dto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update-conversation.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/application/dto/update-conversation.dto.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,MAAM,CAAC;AAEpC;;;;;;;;;;GAUG;AACH,eAAO,MAAM,oBAAoB,YAAY,CAAC;AAE9C;;;GAGG;AACH,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;;;;;;;GAUG;AACH,qBAAa,qBAAqB;IAC9B;;;;;OAKG;IAkBH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;OAMG;IAQH,QAAQ,CAAC,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;;;;OAYG;IAYH,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB"}
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UpdateConversationDto = exports.MAX_MODEL_CHOICE_LENGTH = exports.MODEL_CHOICE_DEFAULT = exports.MAX_TITLE_LENGTH = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const swagger_1 = require("@nestjs/swagger");
6
+ const class_transformer_1 = require("class-transformer");
7
+ const class_validator_1 = require("class-validator");
8
+ /**
9
+ * Longest title we accept.
10
+ *
11
+ * Generous next to what the server derives from a first message, because this
12
+ * one is typed by a human who may be filing a thread rather than naming it — and
13
+ * still bounded, since the rail renders it on one truncated line.
14
+ */
15
+ exports.MAX_TITLE_LENGTH = 200;
16
+ /**
17
+ * The stored form of "let the host's resolver decide".
18
+ *
19
+ * **Legacy: accepted, no longer produced.** It was a real option in the picker
20
+ * back when the deployment had a `defaultProvider` for it to mean; both are
21
+ * gone, and the admin now opens a chat on the catalogue's first entry and names
22
+ * it per turn. Still accepted here because rows carrying it exist and an older
23
+ * client may still send it — a client that does gets its thread read back as
24
+ * "nobody picked", which lands on that same first entry. It is unambiguous
25
+ * against a `'<provider>:<model>'` key because it contains no colon.
26
+ */
27
+ exports.MODEL_CHOICE_DEFAULT = 'default';
28
+ /**
29
+ * Longest model choice we accept: a provider name plus a model id plus the
30
+ * separator. Bounded because it is stored and echoed back to the picker.
31
+ */
32
+ exports.MAX_MODEL_CHOICE_LENGTH = 200;
33
+ /**
34
+ * Body of `PATCH /api/copilot/conversations/:id`.
35
+ *
36
+ * A **patch**, so every property is optional and only the ones present are
37
+ * written. The controller rejects a body with neither: an empty patch is a
38
+ * request that cannot mean anything, and silently answering 200 to it would
39
+ * report success for a write that never happened.
40
+ *
41
+ * Every property is declared because the host's `ValidationPipe` runs with
42
+ * `forbidNonWhitelisted` — an undeclared key is a 400 naming it.
43
+ */
44
+ class UpdateConversationDto {
45
+ /**
46
+ * Rename the thread.
47
+ *
48
+ * Trimmed before validation, so " " is the empty string and fails the
49
+ * length check rather than being stored as a title that renders as nothing.
50
+ */
51
+ title;
52
+ /**
53
+ * Hide the thread from the list, or bring it back.
54
+ *
55
+ * Archiving is the only removal this API offers, and it is deliberately
56
+ * reversible — the transcript is kept, and `GET /conversations/:id` still
57
+ * serves it, so a link to an archived thread keeps working.
58
+ */
59
+ archived;
60
+ /**
61
+ * Record the model picked for this thread, so reopening it in another tab
62
+ * offers the backend the person chose instead of the deployment default.
63
+ *
64
+ * `'<provider>:<model>'` — or the legacy `'default'`, see above —
65
+ * the same key the picker uses, split on its **first** colon (a model id
66
+ * routinely contains one; a provider name cannot). The controller checks the
67
+ * pair against `ModelRegistry.catalogue()`, so this can only ever name a
68
+ * backend the operator configured.
69
+ *
70
+ * It is a **memory, not a pin**: the run route still takes its provider and
71
+ * model per turn, and nothing here constrains the next one.
72
+ */
73
+ modelChoice;
74
+ }
75
+ exports.UpdateConversationDto = UpdateConversationDto;
76
+ tslib_1.__decorate([
77
+ (0, swagger_1.ApiPropertyOptional)({
78
+ type: String,
79
+ minLength: 1,
80
+ maxLength: exports.MAX_TITLE_LENGTH,
81
+ description: 'A new display title. Trimmed; must not be blank once trimmed.',
82
+ example: 'Invoice copy review'
83
+ }),
84
+ (0, class_validator_1.IsOptional)()
85
+ // Runs during transformation, i.e. **before** the validators below — so a
86
+ // title of spaces is measured as the empty string it is.
87
+ ,
88
+ (0, class_transformer_1.Transform)(({ value }) => typeof value === 'string' ? value.trim() : value),
89
+ (0, class_validator_1.IsString)(),
90
+ (0, class_validator_1.MinLength)(1),
91
+ (0, class_validator_1.MaxLength)(exports.MAX_TITLE_LENGTH),
92
+ tslib_1.__metadata("design:type", String)
93
+ ], UpdateConversationDto.prototype, "title", void 0);
94
+ tslib_1.__decorate([
95
+ (0, swagger_1.ApiPropertyOptional)({
96
+ type: Boolean,
97
+ description: 'Hide the thread from the default list (`true`) or restore it (`false`).'
98
+ }),
99
+ (0, class_validator_1.IsOptional)(),
100
+ (0, class_validator_1.IsBoolean)(),
101
+ tslib_1.__metadata("design:type", Boolean)
102
+ ], UpdateConversationDto.prototype, "archived", void 0);
103
+ tslib_1.__decorate([
104
+ (0, swagger_1.ApiPropertyOptional)({
105
+ type: String,
106
+ maxLength: exports.MAX_MODEL_CHOICE_LENGTH,
107
+ description: "The model picked for this thread: `default`, or `<provider>:<model>` naming a pair from `GET /api/copilot/models`. Doesn't constrain the next run.",
108
+ example: 'anthropic:claude-opus-5'
109
+ }),
110
+ (0, class_validator_1.IsOptional)(),
111
+ (0, class_validator_1.IsString)(),
112
+ (0, class_validator_1.MinLength)(1),
113
+ (0, class_validator_1.MaxLength)(exports.MAX_MODEL_CHOICE_LENGTH),
114
+ tslib_1.__metadata("design:type", String)
115
+ ], UpdateConversationDto.prototype, "modelChoice", void 0);
@@ -0,0 +1,27 @@
1
+ import { type ProposalApplier } from '@orthacms/copilot-domain';
2
+ /**
3
+ * The `kind` → applier lookup, filled by the plugins that own the writes.
4
+ *
5
+ * Same shape and same reasoning as {@link CopilotToolRegistry}: registration is
6
+ * a **runtime `register(...)` call**, because Nest cannot merge a
7
+ * multi-provider token across independent dynamic modules and every plugin here
8
+ * is one. A statically bound {@link COPILOT_PROPOSAL_APPLIER} is honoured too,
9
+ * for a host that binds exactly one.
10
+ *
11
+ * A duplicate `kind` is **refused, not overwritten**. Silently letting the last
12
+ * registration win would let a plugin registered later take over another's
13
+ * writes — the same shadowing the capability profile refuses for tool names,
14
+ * and rather more consequential here, since these are the things that mutate.
15
+ */
16
+ export declare class ProposalApplierRegistry {
17
+ private readonly logger;
18
+ private readonly byKind;
19
+ constructor(injected?: ProposalApplier[] | ProposalApplier | null);
20
+ /** Registers an applier. Called by binding plugins at bootstrap. */
21
+ register(applier: ProposalApplier): void;
22
+ /** The applier for `kind`, or undefined when no plugin claimed it. */
23
+ get(kind: string): ProposalApplier | undefined;
24
+ /** Every registered kind — for diagnostics and the settings UI. */
25
+ kinds(): string[];
26
+ }
27
+ //# sourceMappingURL=proposal-applier.registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal-applier.registry.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/proposal-applier.registry.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AAElC;;;;;;;;;;;;;GAaG;AACH,qBACa,uBAAuB;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA4C;IAEnE,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;gBAKzD,QAAQ,GAAE,eAAe,EAAE,GAAG,eAAe,GAAG,IAAW;IAY/D,oEAAoE;IACpE,QAAQ,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI;IAcxC,sEAAsE;IACtE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS;IAI9C,mEAAmE;IACnE,KAAK,IAAI,MAAM,EAAE;CAGpB"}
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ var ProposalApplierRegistry_1;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ProposalApplierRegistry = void 0;
5
+ const tslib_1 = require("tslib");
6
+ const common_1 = require("@nestjs/common");
7
+ const copilot_domain_1 = require("@orthacms/copilot-domain");
8
+ /**
9
+ * The `kind` → applier lookup, filled by the plugins that own the writes.
10
+ *
11
+ * Same shape and same reasoning as {@link CopilotToolRegistry}: registration is
12
+ * a **runtime `register(...)` call**, because Nest cannot merge a
13
+ * multi-provider token across independent dynamic modules and every plugin here
14
+ * is one. A statically bound {@link COPILOT_PROPOSAL_APPLIER} is honoured too,
15
+ * for a host that binds exactly one.
16
+ *
17
+ * A duplicate `kind` is **refused, not overwritten**. Silently letting the last
18
+ * registration win would let a plugin registered later take over another's
19
+ * writes — the same shadowing the capability profile refuses for tool names,
20
+ * and rather more consequential here, since these are the things that mutate.
21
+ */
22
+ let ProposalApplierRegistry = ProposalApplierRegistry_1 = class ProposalApplierRegistry {
23
+ logger = new common_1.Logger(ProposalApplierRegistry_1.name);
24
+ byKind = new Map();
25
+ constructor(injected = null) {
26
+ const initial = injected
27
+ ? Array.isArray(injected)
28
+ ? injected
29
+ : [injected]
30
+ : [];
31
+ for (const applier of initial) {
32
+ this.register(applier);
33
+ }
34
+ }
35
+ /** Registers an applier. Called by binding plugins at bootstrap. */
36
+ register(applier) {
37
+ const existing = this.byKind.get(applier.kind);
38
+ if (existing) {
39
+ this.logger.error(`Two appliers registered for proposal kind "${applier.kind}"; ` +
40
+ 'keeping the first. This is a wiring bug — a proposal of ' +
41
+ 'this kind would otherwise be applied by whichever plugin ' +
42
+ 'happened to load last.');
43
+ return;
44
+ }
45
+ this.byKind.set(applier.kind, applier);
46
+ }
47
+ /** The applier for `kind`, or undefined when no plugin claimed it. */
48
+ get(kind) {
49
+ return this.byKind.get(kind);
50
+ }
51
+ /** Every registered kind — for diagnostics and the settings UI. */
52
+ kinds() {
53
+ return [...this.byKind.keys()];
54
+ }
55
+ };
56
+ exports.ProposalApplierRegistry = ProposalApplierRegistry;
57
+ exports.ProposalApplierRegistry = ProposalApplierRegistry = ProposalApplierRegistry_1 = tslib_1.__decorate([
58
+ (0, common_1.Injectable)(),
59
+ tslib_1.__param(0, (0, common_1.Optional)()),
60
+ tslib_1.__param(0, (0, common_1.Inject)(copilot_domain_1.COPILOT_PROPOSAL_APPLIER)),
61
+ tslib_1.__metadata("design:paramtypes", [Object])
62
+ ], ProposalApplierRegistry);
@@ -0,0 +1,243 @@
1
+ import { type AttachmentResolver, type CopilotRunEvent, type ModelRegistry, type ModelResolver, type RunLimits } from '@orthacms/copilot-domain';
2
+ import { ToolRegistry } from '@orthacms/tools-server';
3
+ import type { CopilotPluginConfig } from '../../types/copilot-config';
4
+ import { ConversationRepository } from '../infrastructure/persistence/conversation.repository';
5
+ import { ProposalRepository } from '../infrastructure/persistence/proposal.repository';
6
+ import { CapabilityProfileService } from './capability-profile.service';
7
+ import { DecideProposalService } from './decide-proposal.service';
8
+ import { ToolPermissionBroker } from './tool-permission.broker';
9
+ import { SkillCatalogService } from '../../skills/application/skill-catalog.service';
10
+ import { type SurfaceContext } from './system-prompt';
11
+ /** What the controller hands the engine to start one turn. */
12
+ export interface StartRunInput {
13
+ /** The user the run acts as. */
14
+ userId: string;
15
+ /**
16
+ * That user's email. Carried so an auto-applied proposal can freeze the
17
+ * same actor snapshot onto its audit event that a hand-made edit would —
18
+ * without a lookup in the one path where getting the actor wrong is least
19
+ * acceptable.
20
+ */
21
+ userEmail: string;
22
+ /** The user's role, for resolving the capability profile. */
23
+ roleId: string;
24
+ /** The workspace the run is scoped to; membership already proven. */
25
+ workspaceId: string;
26
+ /** Continue this thread, or start a new one when absent. */
27
+ conversationId?: string;
28
+ /** What the user typed. */
29
+ message: string;
30
+ /**
31
+ * Media asset ids the user attached, already uploaded under their own
32
+ * authority. Resolved against the run's workspace before anything is
33
+ * persisted; an id that does not resolve ends the run.
34
+ */
35
+ attachments?: readonly string[];
36
+ /**
37
+ * Skill **names** the person attached to this turn.
38
+ *
39
+ * Names, never the instruction text: the catalogue is the only thing that
40
+ * can say what a name means, and a request carrying a body would let anyone
41
+ * holding `copilot:use` write their own system prompt. The same rule
42
+ * attachments follow, for a sharper reason.
43
+ */
44
+ skills?: readonly string[];
45
+ /** Where the user is. */
46
+ context: SurfaceContext;
47
+ /** The admin UI's locale — the language to answer in. */
48
+ uiLocale: string;
49
+ /** Content-type summaries for the workspace. */
50
+ typeSummaries: readonly string[];
51
+ /**
52
+ * The backend this turn should run on. Either half may be omitted: no
53
+ * provider means the host's resolver picks, and no model means that
54
+ * provider's default. A user switching model mid-conversation simply sends
55
+ * a different pair on the next turn — nothing is pinned to the thread.
56
+ */
57
+ choice?: {
58
+ provider?: string;
59
+ model?: string;
60
+ };
61
+ /** Aborted when the client disconnects. */
62
+ signal: AbortSignal;
63
+ }
64
+ /**
65
+ * Thrown when a run names a provider or model that isn't registered. The
66
+ * controller turns it into an error frame rather than a 500 — it is a bad
67
+ * request, and by the time we know, the stream is usually already open.
68
+ */
69
+ export declare class UnknownModelChoiceError extends Error {
70
+ constructor(message: string);
71
+ }
72
+ /** Thrown when a run cannot start at all — the controller maps it to a 4xx. */
73
+ export declare class CopilotDisabledError extends Error {
74
+ constructor();
75
+ }
76
+ /**
77
+ * The run engine: a **bounded** loop that calls the model, executes the tools
78
+ * it asks for, feeds the results back, and stops on a final answer or a ceiling
79
+ * ([`docs/design/copilot.md`](../../../../../../docs/design/copilot.md) §5).
80
+ *
81
+ * It is an async generator rather than a service that writes to a response.
82
+ * That keeps the transport out of the engine — the SSE controller serializes
83
+ * whatever this yields — and it is what makes the loop testable without a
84
+ * socket: a test drains the generator and asserts on the event sequence.
85
+ */
86
+ export declare class RunEngine {
87
+ private readonly registry;
88
+ private readonly resolver;
89
+ private readonly config;
90
+ private readonly profiles;
91
+ private readonly tools;
92
+ private readonly conversations;
93
+ private readonly proposals;
94
+ private readonly decisions;
95
+ private readonly permissions;
96
+ private readonly skills;
97
+ private readonly attachments;
98
+ private readonly logger;
99
+ /** The ceilings this engine enforces. */
100
+ private readonly limits;
101
+ constructor(registry: ModelRegistry, resolver: ModelResolver, config: CopilotPluginConfig, profiles: CapabilityProfileService, tools: ToolRegistry, conversations: ConversationRepository, proposals: ProposalRepository, decisions: DecideProposalService, permissions: ToolPermissionBroker, skills: SkillCatalogService, attachments?: AttachmentResolver | null, limits?: RunLimits | null);
102
+ /** Runs one turn, yielding events as they happen. */
103
+ run(input: StartRunInput): AsyncGenerator<CopilotRunEvent>;
104
+ /**
105
+ * The loop itself: model call → tool calls → repeat. Returns the reason the
106
+ * run ended.
107
+ */
108
+ private loop;
109
+ /** One model call, reduced to text, tool uses, usage and a stop reason. */
110
+ private streamTurn;
111
+ /**
112
+ * Authorizes, validates and runs one tool call, and writes its audit row.
113
+ *
114
+ * **Never throws.** Every failure — unknown tool, revoked permission,
115
+ * malformed arguments, a tool that blew up — comes back as a tool *error*
116
+ * the model can recover from, and the run continues (design §5, step 6).
117
+ * Turning a model mistake into a 500 would lose the whole turn.
118
+ */
119
+ /**
120
+ * Decides whether `call` needs the user's say-so, and parks the run if so.
121
+ *
122
+ * Returns `null` when it may just run — which is the overwhelmingly common
123
+ * case, because **only write tools ask**. Prompting on reads was
124
+ * considered and rejected: a model does three or four searches before it
125
+ * answers anything, so a fresh chat would open with four prompts, and
126
+ * everyone would learn to click through them without reading. That is worse
127
+ * than not asking, because it also devalues the prompt that matters.
128
+ *
129
+ * Otherwise it hands back the frame to emit and a `settle` to await, rather
130
+ * than doing both itself — the caller is the generator, and only a
131
+ * generator can `yield`.
132
+ */
133
+ private mayRun;
134
+ private executeTool;
135
+ /**
136
+ * Persists a `propose` tool's draft, applies it, and produces both the
137
+ * model's receipt and the client's proposal card.
138
+ *
139
+ * Applies it **always** — the per-workspace auto-apply opt-in this doc used
140
+ * to name was deleted with the policy table
141
+ * ([ADR-0009](../../../../../../docs/adr/0009-copilot-applies-directly.md)
142
+ * §4). The row is still written first, and that ordering is now the only
143
+ * thing carrying "undoable, never invisible".
144
+ *
145
+ * **The model is told what happened, not handed the patch back.** It
146
+ * already knows what it asked for; echoing the whole change would spend the
147
+ * tokens twice and invite the model to "confirm" by proposing again. What
148
+ * it needs is the id, the summary, and whether the write landed — which is
149
+ * what makes it say "I've fixed the headline" or "that did not save",
150
+ * rather than the pre-ADR-0009 "I've drafted this, accept it below" for a
151
+ * change that has already happened.
152
+ */
153
+ private recordProposal;
154
+ /**
155
+ * Records the receipt for an `effect: 'apply'` tool — one that does its own
156
+ * writing instead of handing back a draft for the engine to apply.
157
+ *
158
+ * The registry has always allowed the effect and
159
+ * [ADR-0009](../../../../../../docs/adr/0009-copilot-applies-directly.md)
160
+ * §5 still offers such a tool "exactly when a `read` one with the same
161
+ * `requires` would be", while §2 keeps the `copilot_proposals` row as "the
162
+ * whole paper trail" and names a binder that writes directly as the failure
163
+ * the split exists to prevent. Both were true at once: an `apply` tool
164
+ * parked for permission like a write, ran like a write, and left nothing
165
+ * behind. Nothing shipped declares the effect today, which is why it went
166
+ * unnoticed — and is exactly why the next binder to reach for it must not
167
+ * have to know this.
168
+ *
169
+ * Two differences from {@link recordProposal}, both forced by the effect:
170
+ *
171
+ * - **The row is written after the fact.** The write has already happened
172
+ * by the time the handler returns; there was never a moment this engine
173
+ * could have stopped it. The permission prompt is what does that.
174
+ * - **`kind` is `tool.<name>` and has no applier.** There is nothing left
175
+ * to carry out. The row exists to be read — which is the whole of what
176
+ * "undoable, never invisible" asks of it here.
177
+ *
178
+ * The model still gets the handler's own return value: for an `apply` tool
179
+ * the return value is a *result*, not a change (`ToolEffect`).
180
+ */
181
+ private recordApplied;
182
+ /**
183
+ * One tool call that did not happen: the audit row, the block the model is
184
+ * told, and the frame the UI draws.
185
+ *
186
+ * Shared by `executeTool`'s own refusals and by the loop's time-limit
187
+ * check, so a call refused before it was ever dispatched is recorded
188
+ * exactly like one refused after — a reviewer reading `copilot_tool_calls`
189
+ * should not have to know which stage said no.
190
+ */
191
+ private toolFailure;
192
+ /** Writes one `copilot_tool_calls` row. Never fails the run. */
193
+ private audit;
194
+ /**
195
+ * Turns the attached asset ids into what the model is told about them.
196
+ *
197
+ * Three refusals, each a different wrong thing to do:
198
+ *
199
+ * - **No resolver bound** — the deployment has no media plugin, so there is
200
+ * nothing that could have produced these ids. Failing beats describing
201
+ * files nobody can look up.
202
+ * - **An id that did not resolve** — it belongs to another workspace, or to
203
+ * a file deleted between the upload and the send. The resolver is
204
+ * workspace-scoped and omits what it cannot see, so the two are
205
+ * indistinguishable here, which is deliberate: telling a caller *which*
206
+ * of their ids exists elsewhere is an oracle.
207
+ * - Neither case is reported per-id. One message names the count, because
208
+ * the user's recourse is the same either way — re-attach the file.
209
+ */
210
+ private resolveAttachments;
211
+ /**
212
+ * The persisted transcript, as the port's message shape.
213
+ *
214
+ * A turn's attachments are folded back in as a **fenced text block**, which
215
+ * is what makes them survive into later turns: "summarise the file I sent"
216
+ * on turn three has to reach a manifest written on turn one, and the
217
+ * transcript is the only thing carried forward. Fenced because a file name
218
+ * is user-authored text arriving in the prompt — the same treatment a tool
219
+ * result gets, for the same reason.
220
+ *
221
+ * The rows are then run through `normalizeTranscript`, which is what makes
222
+ * a *continued* conversation replayable at all. A run is stored as one
223
+ * assistant row holding text, `tool_use` **and** `tool_result` blocks
224
+ * together — the shape the transcript UI reads — and Anthropic rejects that
225
+ * on the way back in: a tool result has to ride on a user turn after the
226
+ * assistant turn that asked for it. It also drops `tool_use` blocks left
227
+ * unanswered by a run that was aborted mid-step, which is the same 400
228
+ * wearing a different message. The repair is here rather than at the write
229
+ * so it also fixes the threads already in the database.
230
+ */
231
+ private loadHistory;
232
+ }
233
+ /**
234
+ * Thrown when a turn's attachments cannot be resolved. Surfaced by the
235
+ * controller as an error frame rather than a 500 — like `UnknownModelChoiceError`
236
+ * it is a bad request, and by the time we know, the stream is already open.
237
+ */
238
+ export declare class AttachmentError extends Error {
239
+ constructor(message: string);
240
+ }
241
+ /** The prompt version this engine builds with, for the run record. */
242
+ export declare const ENGINE_PROMPT_VERSION = 10;
243
+ //# sourceMappingURL=run-engine.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run-engine.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/run-engine.service.ts"],"names":[],"mappings":"AAEA,OAAO,EAcH,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGpB,KAAK,aAAa,EAClB,KAAK,aAAa,EAKlB,KAAK,SAAS,EAKjB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACH,YAAY,EAGf,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uDAAuD,CAAC;AAC/F,OAAO,EAAE,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AACvF,OAAO,EACH,wBAAwB,EAE3B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,gDAAgD,CAAC;AAErF,OAAO,EAGH,KAAK,cAAc,EACtB,MAAM,iBAAiB,CAAC;AAEzB,8DAA8D;AAC9D,MAAM,WAAW,aAAa;IAC1B,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,SAAS,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAC;IACf,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,4DAA4D;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,2BAA2B;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAChC;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3B,yBAAyB;IACzB,OAAO,EAAE,cAAc,CAAC;IACxB,yDAAyD;IACzD,QAAQ,EAAE,MAAM,CAAC;IACjB,gDAAgD;IAChD,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;;OAKG;IACH,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,2CAA2C;IAC3C,MAAM,EAAE,WAAW,CAAC;CACvB;AAED;;;;GAIG;AACH,qBAAa,uBAAwB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI9B;AAED,+EAA+E;AAC/E,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C;AAED;;;;;;;;;GASG;AACH,qBACa,SAAS;IAOU,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAC9C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAOvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAtBhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IAErD,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;gBAGU,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,aAAa,EACxB,MAAM,EAAE,mBAAmB,EAClD,QAAQ,EAAE,wBAAwB,EAClC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,sBAAsB,EACrC,SAAS,EAAE,kBAAkB,EAC7B,SAAS,EAAE,qBAAqB,EAChC,WAAW,EAAE,oBAAoB,EACjC,MAAM,EAAE,mBAAmB,EAO3B,WAAW,GAAE,kBAAkB,GAAG,IAAW,EAG9D,MAAM,GAAE,SAAS,GAAG,IAAW;IAQnC,qDAAqD;IAC9C,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAAC,eAAe,CAAC;IAyKjE;;;OAGG;YACY,IAAI;IA4InB,2EAA2E;YAC5D,UAAU;IA6DzB;;;;;;;OAOG;IACH;;;;;;;;;;;;;OAaG;YACW,MAAM;YA2GN,WAAW;IAwJzB;;;;;;;;;;;;;;;;;OAiBG;YACW,cAAc;IAqI5B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;YACW,aAAa;IA6E3B;;;;;;;;OAQG;YACW,WAAW;IAiCzB,gEAAgE;YAClD,KAAK;IA6BnB;;;;;;;;;;;;;;;OAeG;YACW,kBAAkB;IAmChC;;;;;;;;;;;;;;;;;;;OAmBG;YACW,WAAW;CAwC5B;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI9B;AA0BD,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,KAAwB,CAAC"}