@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.
- package/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +48 -0
- package/dist/lib/chat/application/appliers-registrar.d.ts +16 -0
- package/dist/lib/chat/application/appliers-registrar.d.ts.map +1 -0
- package/dist/lib/chat/application/appliers-registrar.js +50 -0
- package/dist/lib/chat/application/capability-profile.service.d.ts +43 -0
- package/dist/lib/chat/application/capability-profile.service.d.ts.map +1 -0
- package/dist/lib/chat/application/capability-profile.service.js +75 -0
- package/dist/lib/chat/application/content-type-summary.service.d.ts +32 -0
- package/dist/lib/chat/application/content-type-summary.service.d.ts.map +1 -0
- package/dist/lib/chat/application/content-type-summary.service.js +64 -0
- package/dist/lib/chat/application/decide-proposal.service.d.ts +80 -0
- package/dist/lib/chat/application/decide-proposal.service.d.ts.map +1 -0
- package/dist/lib/chat/application/decide-proposal.service.js +112 -0
- package/dist/lib/chat/application/dto/create-run.dto.d.ts +138 -0
- package/dist/lib/chat/application/dto/create-run.dto.d.ts.map +1 -0
- package/dist/lib/chat/application/dto/create-run.dto.js +253 -0
- package/dist/lib/chat/application/dto/decide-tool-permission.dto.d.ts +20 -0
- package/dist/lib/chat/application/dto/decide-tool-permission.dto.d.ts.map +1 -0
- package/dist/lib/chat/application/dto/decide-tool-permission.dto.js +44 -0
- package/dist/lib/chat/application/dto/extend-tool-permission.dto.d.ts +18 -0
- package/dist/lib/chat/application/dto/extend-tool-permission.dto.d.ts.map +1 -0
- package/dist/lib/chat/application/dto/extend-tool-permission.dto.js +33 -0
- package/dist/lib/chat/application/dto/list-conversations-query.dto.d.ts +18 -0
- package/dist/lib/chat/application/dto/list-conversations-query.dto.d.ts.map +1 -0
- package/dist/lib/chat/application/dto/list-conversations-query.dto.js +39 -0
- package/dist/lib/chat/application/dto/list-proposals-query.dto.d.ts +15 -0
- package/dist/lib/chat/application/dto/list-proposals-query.dto.d.ts.map +1 -0
- package/dist/lib/chat/application/dto/list-proposals-query.dto.js +41 -0
- package/dist/lib/chat/application/dto/update-conversation.dto.d.ts +68 -0
- package/dist/lib/chat/application/dto/update-conversation.dto.d.ts.map +1 -0
- package/dist/lib/chat/application/dto/update-conversation.dto.js +115 -0
- package/dist/lib/chat/application/proposal-applier.registry.d.ts +27 -0
- package/dist/lib/chat/application/proposal-applier.registry.d.ts.map +1 -0
- package/dist/lib/chat/application/proposal-applier.registry.js +62 -0
- package/dist/lib/chat/application/run-engine.service.d.ts +243 -0
- package/dist/lib/chat/application/run-engine.service.d.ts.map +1 -0
- package/dist/lib/chat/application/run-engine.service.js +1058 -0
- package/dist/lib/chat/application/summarize-tool-output.d.ts +15 -0
- package/dist/lib/chat/application/summarize-tool-output.d.ts.map +1 -0
- package/dist/lib/chat/application/summarize-tool-output.js +61 -0
- package/dist/lib/chat/application/system-prompt.d.ts +81 -0
- package/dist/lib/chat/application/system-prompt.d.ts.map +1 -0
- package/dist/lib/chat/application/system-prompt.js +477 -0
- package/dist/lib/chat/application/tool-permission.broker.d.ts +118 -0
- package/dist/lib/chat/application/tool-permission.broker.d.ts.map +1 -0
- package/dist/lib/chat/application/tool-permission.broker.js +221 -0
- package/dist/lib/chat/http/controllers/create-run.controller.d.ts +30 -0
- package/dist/lib/chat/http/controllers/create-run.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/create-run.controller.js +159 -0
- package/dist/lib/chat/http/controllers/get-conversation.controller.d.ts +22 -0
- package/dist/lib/chat/http/controllers/get-conversation.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/get-conversation.controller.js +50 -0
- package/dist/lib/chat/http/controllers/list-conversations.controller.d.ts +20 -0
- package/dist/lib/chat/http/controllers/list-conversations.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/list-conversations.controller.js +47 -0
- package/dist/lib/chat/http/controllers/list-models.controller.d.ts +35 -0
- package/dist/lib/chat/http/controllers/list-models.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/list-models.controller.js +48 -0
- package/dist/lib/chat/http/controllers/proposals.controller.d.ts +43 -0
- package/dist/lib/chat/http/controllers/proposals.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/proposals.controller.js +98 -0
- package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts +57 -0
- package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/tool-permission.controller.js +116 -0
- package/dist/lib/chat/http/controllers/update-conversation.controller.d.ts +37 -0
- package/dist/lib/chat/http/controllers/update-conversation.controller.d.ts.map +1 -0
- package/dist/lib/chat/http/controllers/update-conversation.controller.js +108 -0
- package/dist/lib/chat/http/sse-stream.d.ts +41 -0
- package/dist/lib/chat/http/sse-stream.d.ts.map +1 -0
- package/dist/lib/chat/http/sse-stream.js +95 -0
- package/dist/lib/chat/infrastructure/persistence/conversation.repository.d.ts +159 -0
- package/dist/lib/chat/infrastructure/persistence/conversation.repository.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/persistence/conversation.repository.js +249 -0
- package/dist/lib/chat/infrastructure/persistence/derive-title.d.ts +13 -0
- package/dist/lib/chat/infrastructure/persistence/derive-title.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/persistence/derive-title.js +35 -0
- package/dist/lib/chat/infrastructure/persistence/proposal.repository.d.ts +86 -0
- package/dist/lib/chat/infrastructure/persistence/proposal.repository.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/persistence/proposal.repository.js +152 -0
- package/dist/lib/chat/infrastructure/schema/conversations.d.ts +189 -0
- package/dist/lib/chat/infrastructure/schema/conversations.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/schema/conversations.js +100 -0
- package/dist/lib/chat/infrastructure/schema/external-refs.d.ts +67 -0
- package/dist/lib/chat/infrastructure/schema/external-refs.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/schema/external-refs.js +28 -0
- package/dist/lib/chat/infrastructure/schema/index.d.ts +5 -0
- package/dist/lib/chat/infrastructure/schema/index.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/schema/index.js +12 -0
- package/dist/lib/chat/infrastructure/schema/messages.d.ts +264 -0
- package/dist/lib/chat/infrastructure/schema/messages.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/schema/messages.js +96 -0
- package/dist/lib/chat/infrastructure/schema/proposals.d.ts +328 -0
- package/dist/lib/chat/infrastructure/schema/proposals.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/schema/proposals.js +87 -0
- package/dist/lib/chat/infrastructure/schema/tool-calls.d.ts +203 -0
- package/dist/lib/chat/infrastructure/schema/tool-calls.d.ts.map +1 -0
- package/dist/lib/chat/infrastructure/schema/tool-calls.js +50 -0
- package/dist/lib/copilot.module.d.ts +30 -0
- package/dist/lib/copilot.module.d.ts.map +1 -0
- package/dist/lib/copilot.module.js +146 -0
- package/dist/lib/copilot.tokens.d.ts +34 -0
- package/dist/lib/copilot.tokens.d.ts.map +1 -0
- package/dist/lib/copilot.tokens.js +38 -0
- package/dist/lib/infrastructure/model-registry.d.ts +22 -0
- package/dist/lib/infrastructure/model-registry.d.ts.map +1 -0
- package/dist/lib/infrastructure/model-registry.js +54 -0
- package/dist/lib/skills/application/dto/create-skill.dto.d.ts +36 -0
- package/dist/lib/skills/application/dto/create-skill.dto.d.ts.map +1 -0
- package/dist/lib/skills/application/dto/create-skill.dto.js +106 -0
- package/dist/lib/skills/application/dto/update-skill.dto.d.ts +28 -0
- package/dist/lib/skills/application/dto/update-skill.dto.d.ts.map +1 -0
- package/dist/lib/skills/application/dto/update-skill.dto.js +96 -0
- package/dist/lib/skills/application/skill-catalog.service.d.ts +97 -0
- package/dist/lib/skills/application/skill-catalog.service.d.ts.map +1 -0
- package/dist/lib/skills/application/skill-catalog.service.js +157 -0
- package/dist/lib/skills/http/controllers/list-skills.controller.d.ts +19 -0
- package/dist/lib/skills/http/controllers/list-skills.controller.d.ts.map +1 -0
- package/dist/lib/skills/http/controllers/list-skills.controller.js +50 -0
- package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts +64 -0
- package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts.map +1 -0
- package/dist/lib/skills/http/controllers/manage-skills.controller.js +182 -0
- package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts +78 -0
- package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts.map +1 -0
- package/dist/lib/skills/infrastructure/persistence/skill.repository.js +116 -0
- package/dist/lib/skills/infrastructure/schema/index.d.ts +2 -0
- package/dist/lib/skills/infrastructure/schema/index.d.ts.map +1 -0
- package/dist/lib/skills/infrastructure/schema/index.js +6 -0
- package/dist/lib/skills/infrastructure/schema/skills.d.ts +212 -0
- package/dist/lib/skills/infrastructure/schema/skills.d.ts.map +1 -0
- package/dist/lib/skills/infrastructure/schema/skills.js +78 -0
- package/dist/lib/types/copilot-config.d.ts +55 -0
- package/dist/lib/types/copilot-config.d.ts.map +1 -0
- package/dist/lib/types/copilot-config.js +2 -0
- package/dist/lib/utils/copilot-plugin.d.ts +66 -0
- package/dist/lib/utils/copilot-plugin.d.ts.map +1 -0
- package/dist/lib/utils/copilot-plugin.js +87 -0
- package/migrations/0000_copilot_chat.sql +48 -0
- package/migrations/0001_proposals.sql +34 -0
- package/migrations/0002_drop_workspace_policies.sql +1 -0
- package/migrations/0003_conversation_allowed_tools.sql +1 -0
- package/migrations/0004_message_attachments.sql +1 -0
- package/migrations/0005_skills.sql +19 -0
- package/migrations/0006_conversation_model_choice.sql +1 -0
- package/migrations/meta/0000_snapshot.json +390 -0
- package/migrations/meta/0001_snapshot.json +657 -0
- package/migrations/meta/0002_snapshot.json +610 -0
- package/migrations/meta/0003_snapshot.json +617 -0
- package/migrations/meta/0004_snapshot.json +623 -0
- package/migrations/meta/0005_snapshot.json +771 -0
- package/migrations/meta/0006_snapshot.json +777 -0
- package/migrations/meta/_journal.json +55 -0
- package/package.json +46 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildModelRegistry = buildModelRegistry;
|
|
4
|
+
const copilot_domain_1 = require("@orthacms/copilot-domain");
|
|
5
|
+
/**
|
|
6
|
+
* Builds an immutable {@link ModelRegistry} over the host's provider list.
|
|
7
|
+
* Bound to `MODEL_REGISTRY` at the composition root; the resolver returns a
|
|
8
|
+
* name, the core looks the provider up here.
|
|
9
|
+
*
|
|
10
|
+
* Registration order is preserved, so {@link ModelRegistry.catalogue} renders
|
|
11
|
+
* in the order the operator wrote — the first entry reads as the house default.
|
|
12
|
+
*
|
|
13
|
+
* @throws When a name is blank or registered twice. A list can express both,
|
|
14
|
+
* unlike a map, so the guard has to be explicit — and silently dropping one
|
|
15
|
+
* of two same-named providers would route runs to a backend nobody chose.
|
|
16
|
+
*/
|
|
17
|
+
function buildModelRegistry(registrations) {
|
|
18
|
+
// Snapshot into a **null-prototype** object so a later mutation of the
|
|
19
|
+
// host's list can't reroute a run mid-flight, and a lookup of
|
|
20
|
+
// `constructor`/`toString` misses instead of resolving something off
|
|
21
|
+
// `Object.prototype` that is not a provider.
|
|
22
|
+
const entries = Object.create(null);
|
|
23
|
+
const order = [];
|
|
24
|
+
for (const { name, provider } of registrations) {
|
|
25
|
+
if (!name.trim()) {
|
|
26
|
+
throw new Error('Every copilot model provider needs a non-empty name — it is what a run, and a resolver, refer to it by.');
|
|
27
|
+
}
|
|
28
|
+
if (Object.prototype.hasOwnProperty.call(entries, name)) {
|
|
29
|
+
throw new Error(`Duplicate copilot model provider name "${name}". Names must be unique across the provider list.`);
|
|
30
|
+
}
|
|
31
|
+
entries[name] = provider;
|
|
32
|
+
order.push(name);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
get(name) {
|
|
36
|
+
const provider = entries[name];
|
|
37
|
+
if (!provider) {
|
|
38
|
+
throw new copilot_domain_1.UnknownModelProviderError(name, [...order]);
|
|
39
|
+
}
|
|
40
|
+
return provider;
|
|
41
|
+
},
|
|
42
|
+
has(name) {
|
|
43
|
+
return Object.prototype.hasOwnProperty.call(entries, name);
|
|
44
|
+
},
|
|
45
|
+
names() {
|
|
46
|
+
return [...order];
|
|
47
|
+
},
|
|
48
|
+
catalogue() {
|
|
49
|
+
return order.flatMap((name) => entries[name]
|
|
50
|
+
.models()
|
|
51
|
+
.map((model) => ({ provider: name, model })));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type SkillMode } from '@orthacms/copilot-domain';
|
|
2
|
+
/** The two modes, as a value the strict pipe can check against. */
|
|
3
|
+
export declare const SKILL_MODES: readonly SkillMode[];
|
|
4
|
+
/**
|
|
5
|
+
* Body of `POST /api/copilot/skills`.
|
|
6
|
+
*
|
|
7
|
+
* The bounds come from `@orthacms/copilot-domain` rather than being repeated
|
|
8
|
+
* here, because the same numbers govern the host's boot-time validation of
|
|
9
|
+
* code-defined skills — one set of limits, whichever way a skill was written.
|
|
10
|
+
*/
|
|
11
|
+
export declare class CreateSkillDto {
|
|
12
|
+
/**
|
|
13
|
+
* Machine name. Refused if the workspace, or a code-defined skill, already
|
|
14
|
+
* holds it — a name resolves to exactly one body or the model's
|
|
15
|
+
* instructions become a matter of row order.
|
|
16
|
+
*/
|
|
17
|
+
name: string;
|
|
18
|
+
/** What a person reads in the picker and on a chip. */
|
|
19
|
+
title: string;
|
|
20
|
+
/**
|
|
21
|
+
* When to use it, written for the model — and **required**. It is the only
|
|
22
|
+
* thing in the prompt for a skill nobody has attached, so a skill without
|
|
23
|
+
* one exists but can never be found.
|
|
24
|
+
*/
|
|
25
|
+
description: string;
|
|
26
|
+
/**
|
|
27
|
+
* The body — prompt text, not content. Bounded because it is spent from the
|
|
28
|
+
* same context window the conversation and the tool results come out of.
|
|
29
|
+
*/
|
|
30
|
+
instructions: string;
|
|
31
|
+
/** `always` puts it in force on every run in the workspace. */
|
|
32
|
+
mode?: SkillMode;
|
|
33
|
+
/** Defaults to on. */
|
|
34
|
+
enabled?: boolean;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=create-skill.dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-skill.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/application/dto/create-skill.dto.ts"],"names":[],"mappings":"AAWA,OAAO,EAMH,KAAK,SAAS,EACjB,MAAM,0BAA0B,CAAC;AAElC,mEAAmE;AACnE,eAAO,MAAM,WAAW,EAAE,SAAS,SAAS,EAAyB,CAAC;AAQtE;;;;;;GAMG;AACH,qBAAa,cAAc;IACvB;;;;OAIG;IAcH,IAAI,EAAG,MAAM,CAAC;IAEd,uDAAuD;IAUvD,KAAK,EAAG,MAAM,CAAC;IAEf;;;;OAIG;IAUH,WAAW,EAAG,MAAM,CAAC;IAErB;;;OAGG;IAQH,YAAY,EAAG,MAAM,CAAC;IAEtB,+DAA+D;IAI/D,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB,sBAAsB;IAItB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CreateSkillDto = exports.SKILL_MODES = 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
|
+
const copilot_domain_1 = require("@orthacms/copilot-domain");
|
|
9
|
+
/** The two modes, as a value the strict pipe can check against. */
|
|
10
|
+
exports.SKILL_MODES = ['manual', 'always'];
|
|
11
|
+
/** Trims a string value during transformation, before the validators run. */
|
|
12
|
+
const trimmed = () => (0, class_transformer_1.Transform)(({ value }) => typeof value === 'string' ? value.trim() : value);
|
|
13
|
+
/**
|
|
14
|
+
* Body of `POST /api/copilot/skills`.
|
|
15
|
+
*
|
|
16
|
+
* The bounds come from `@orthacms/copilot-domain` rather than being repeated
|
|
17
|
+
* here, because the same numbers govern the host's boot-time validation of
|
|
18
|
+
* code-defined skills — one set of limits, whichever way a skill was written.
|
|
19
|
+
*/
|
|
20
|
+
class CreateSkillDto {
|
|
21
|
+
/**
|
|
22
|
+
* Machine name. Refused if the workspace, or a code-defined skill, already
|
|
23
|
+
* holds it — a name resolves to exactly one body or the model's
|
|
24
|
+
* instructions become a matter of row order.
|
|
25
|
+
*/
|
|
26
|
+
name;
|
|
27
|
+
/** What a person reads in the picker and on a chip. */
|
|
28
|
+
title;
|
|
29
|
+
/**
|
|
30
|
+
* When to use it, written for the model — and **required**. It is the only
|
|
31
|
+
* thing in the prompt for a skill nobody has attached, so a skill without
|
|
32
|
+
* one exists but can never be found.
|
|
33
|
+
*/
|
|
34
|
+
description;
|
|
35
|
+
/**
|
|
36
|
+
* The body — prompt text, not content. Bounded because it is spent from the
|
|
37
|
+
* same context window the conversation and the tool results come out of.
|
|
38
|
+
*/
|
|
39
|
+
instructions;
|
|
40
|
+
/** `always` puts it in force on every run in the workspace. */
|
|
41
|
+
mode;
|
|
42
|
+
/** Defaults to on. */
|
|
43
|
+
enabled;
|
|
44
|
+
}
|
|
45
|
+
exports.CreateSkillDto = CreateSkillDto;
|
|
46
|
+
tslib_1.__decorate([
|
|
47
|
+
(0, swagger_1.ApiProperty)({
|
|
48
|
+
type: String,
|
|
49
|
+
maxLength: copilot_domain_1.MAX_SKILL_NAME_LENGTH,
|
|
50
|
+
pattern: copilot_domain_1.SKILL_NAME_PATTERN.source,
|
|
51
|
+
example: 'house-style'
|
|
52
|
+
}),
|
|
53
|
+
trimmed(),
|
|
54
|
+
(0, class_validator_1.IsString)(),
|
|
55
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_NAME_LENGTH),
|
|
56
|
+
(0, class_validator_1.Matches)(copilot_domain_1.SKILL_NAME_PATTERN, {
|
|
57
|
+
message: 'name must be lowercase letters, digits and hyphens, e.g. "house-style"'
|
|
58
|
+
}),
|
|
59
|
+
tslib_1.__metadata("design:type", String)
|
|
60
|
+
], CreateSkillDto.prototype, "name", void 0);
|
|
61
|
+
tslib_1.__decorate([
|
|
62
|
+
(0, swagger_1.ApiProperty)({
|
|
63
|
+
type: String,
|
|
64
|
+
maxLength: copilot_domain_1.MAX_SKILL_TITLE_LENGTH,
|
|
65
|
+
example: 'House style'
|
|
66
|
+
}),
|
|
67
|
+
trimmed(),
|
|
68
|
+
(0, class_validator_1.IsString)(),
|
|
69
|
+
(0, class_validator_1.MinLength)(1),
|
|
70
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_TITLE_LENGTH),
|
|
71
|
+
tslib_1.__metadata("design:type", String)
|
|
72
|
+
], CreateSkillDto.prototype, "title", void 0);
|
|
73
|
+
tslib_1.__decorate([
|
|
74
|
+
(0, swagger_1.ApiProperty)({
|
|
75
|
+
type: String,
|
|
76
|
+
maxLength: copilot_domain_1.MAX_SKILL_DESCRIPTION_LENGTH,
|
|
77
|
+
example: 'How we write product copy: sentence case, second person.'
|
|
78
|
+
}),
|
|
79
|
+
trimmed(),
|
|
80
|
+
(0, class_validator_1.IsString)(),
|
|
81
|
+
(0, class_validator_1.MinLength)(1),
|
|
82
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_DESCRIPTION_LENGTH),
|
|
83
|
+
tslib_1.__metadata("design:type", String)
|
|
84
|
+
], CreateSkillDto.prototype, "description", void 0);
|
|
85
|
+
tslib_1.__decorate([
|
|
86
|
+
(0, swagger_1.ApiProperty)({
|
|
87
|
+
type: String,
|
|
88
|
+
maxLength: copilot_domain_1.MAX_SKILL_INSTRUCTIONS_LENGTH
|
|
89
|
+
}),
|
|
90
|
+
(0, class_validator_1.IsString)(),
|
|
91
|
+
(0, class_validator_1.MinLength)(1),
|
|
92
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_INSTRUCTIONS_LENGTH),
|
|
93
|
+
tslib_1.__metadata("design:type", String)
|
|
94
|
+
], CreateSkillDto.prototype, "instructions", void 0);
|
|
95
|
+
tslib_1.__decorate([
|
|
96
|
+
(0, swagger_1.ApiPropertyOptional)({ enum: exports.SKILL_MODES, default: 'manual' }),
|
|
97
|
+
(0, class_validator_1.IsOptional)(),
|
|
98
|
+
(0, class_validator_1.IsIn)(exports.SKILL_MODES),
|
|
99
|
+
tslib_1.__metadata("design:type", String)
|
|
100
|
+
], CreateSkillDto.prototype, "mode", void 0);
|
|
101
|
+
tslib_1.__decorate([
|
|
102
|
+
(0, swagger_1.ApiPropertyOptional)({ type: Boolean, default: true }),
|
|
103
|
+
(0, class_validator_1.IsOptional)(),
|
|
104
|
+
(0, class_validator_1.IsBoolean)(),
|
|
105
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
106
|
+
], CreateSkillDto.prototype, "enabled", void 0);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { type SkillMode } from '@orthacms/copilot-domain';
|
|
2
|
+
/**
|
|
3
|
+
* Body of `PATCH /api/copilot/skills/:id`.
|
|
4
|
+
*
|
|
5
|
+
* A **patch**: every property is optional and only the ones present are
|
|
6
|
+
* written. The controller rejects an empty body — answering 200 to it would
|
|
7
|
+
* report success for a write that never happened.
|
|
8
|
+
*
|
|
9
|
+
* Not `PartialType(CreateSkillDto)`: the generated class would inherit whatever
|
|
10
|
+
* `CreateSkillDto` grows later, and this endpoint deliberately does not accept
|
|
11
|
+
* everything a create does (there is no `workspaceId` to move a skill between
|
|
12
|
+
* workspaces with, and there never should be).
|
|
13
|
+
*/
|
|
14
|
+
export declare class UpdateSkillDto {
|
|
15
|
+
/** Rename. Refused if the new name is taken, by either source. */
|
|
16
|
+
name?: string;
|
|
17
|
+
/** New display title. */
|
|
18
|
+
title?: string;
|
|
19
|
+
/** New description. */
|
|
20
|
+
description?: string;
|
|
21
|
+
/** New body. */
|
|
22
|
+
instructions?: string;
|
|
23
|
+
/** Switch between always-on and attach-to-use. */
|
|
24
|
+
mode?: SkillMode;
|
|
25
|
+
/** Turn the skill off without deleting it. */
|
|
26
|
+
enabled?: boolean;
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=update-skill.dto.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update-skill.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/application/dto/update-skill.dto.ts"],"names":[],"mappings":"AAWA,OAAO,EAMH,KAAK,SAAS,EACjB,MAAM,0BAA0B,CAAC;AAQlC;;;;;;;;;;;GAWG;AACH,qBAAa,cAAc;IACvB,kEAAkE;IAclE,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,yBAAyB;IAOzB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,uBAAuB;IAUvB,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,gBAAgB;IAShB,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,kDAAkD;IAIlD,IAAI,CAAC,EAAE,SAAS,CAAC;IAEjB,8CAA8C;IAI9C,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UpdateSkillDto = 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
|
+
const copilot_domain_1 = require("@orthacms/copilot-domain");
|
|
9
|
+
const create_skill_dto_1 = require("./create-skill.dto");
|
|
10
|
+
const trimmed = () => (0, class_transformer_1.Transform)(({ value }) => typeof value === 'string' ? value.trim() : value);
|
|
11
|
+
/**
|
|
12
|
+
* Body of `PATCH /api/copilot/skills/:id`.
|
|
13
|
+
*
|
|
14
|
+
* A **patch**: every property is optional and only the ones present are
|
|
15
|
+
* written. The controller rejects an empty body — answering 200 to it would
|
|
16
|
+
* report success for a write that never happened.
|
|
17
|
+
*
|
|
18
|
+
* Not `PartialType(CreateSkillDto)`: the generated class would inherit whatever
|
|
19
|
+
* `CreateSkillDto` grows later, and this endpoint deliberately does not accept
|
|
20
|
+
* everything a create does (there is no `workspaceId` to move a skill between
|
|
21
|
+
* workspaces with, and there never should be).
|
|
22
|
+
*/
|
|
23
|
+
class UpdateSkillDto {
|
|
24
|
+
/** Rename. Refused if the new name is taken, by either source. */
|
|
25
|
+
name;
|
|
26
|
+
/** New display title. */
|
|
27
|
+
title;
|
|
28
|
+
/** New description. */
|
|
29
|
+
description;
|
|
30
|
+
/** New body. */
|
|
31
|
+
instructions;
|
|
32
|
+
/** Switch between always-on and attach-to-use. */
|
|
33
|
+
mode;
|
|
34
|
+
/** Turn the skill off without deleting it. */
|
|
35
|
+
enabled;
|
|
36
|
+
}
|
|
37
|
+
exports.UpdateSkillDto = UpdateSkillDto;
|
|
38
|
+
tslib_1.__decorate([
|
|
39
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
40
|
+
type: String,
|
|
41
|
+
maxLength: copilot_domain_1.MAX_SKILL_NAME_LENGTH,
|
|
42
|
+
pattern: copilot_domain_1.SKILL_NAME_PATTERN.source
|
|
43
|
+
}),
|
|
44
|
+
(0, class_validator_1.IsOptional)(),
|
|
45
|
+
trimmed(),
|
|
46
|
+
(0, class_validator_1.IsString)(),
|
|
47
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_NAME_LENGTH),
|
|
48
|
+
(0, class_validator_1.Matches)(copilot_domain_1.SKILL_NAME_PATTERN, {
|
|
49
|
+
message: 'name must be lowercase letters, digits and hyphens, e.g. "house-style"'
|
|
50
|
+
}),
|
|
51
|
+
tslib_1.__metadata("design:type", String)
|
|
52
|
+
], UpdateSkillDto.prototype, "name", void 0);
|
|
53
|
+
tslib_1.__decorate([
|
|
54
|
+
(0, swagger_1.ApiPropertyOptional)({ type: String, maxLength: copilot_domain_1.MAX_SKILL_TITLE_LENGTH }),
|
|
55
|
+
(0, class_validator_1.IsOptional)(),
|
|
56
|
+
trimmed(),
|
|
57
|
+
(0, class_validator_1.IsString)(),
|
|
58
|
+
(0, class_validator_1.MinLength)(1),
|
|
59
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_TITLE_LENGTH),
|
|
60
|
+
tslib_1.__metadata("design:type", String)
|
|
61
|
+
], UpdateSkillDto.prototype, "title", void 0);
|
|
62
|
+
tslib_1.__decorate([
|
|
63
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
64
|
+
type: String,
|
|
65
|
+
maxLength: copilot_domain_1.MAX_SKILL_DESCRIPTION_LENGTH
|
|
66
|
+
}),
|
|
67
|
+
(0, class_validator_1.IsOptional)(),
|
|
68
|
+
trimmed(),
|
|
69
|
+
(0, class_validator_1.IsString)(),
|
|
70
|
+
(0, class_validator_1.MinLength)(1),
|
|
71
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_DESCRIPTION_LENGTH),
|
|
72
|
+
tslib_1.__metadata("design:type", String)
|
|
73
|
+
], UpdateSkillDto.prototype, "description", void 0);
|
|
74
|
+
tslib_1.__decorate([
|
|
75
|
+
(0, swagger_1.ApiPropertyOptional)({
|
|
76
|
+
type: String,
|
|
77
|
+
maxLength: copilot_domain_1.MAX_SKILL_INSTRUCTIONS_LENGTH
|
|
78
|
+
}),
|
|
79
|
+
(0, class_validator_1.IsOptional)(),
|
|
80
|
+
(0, class_validator_1.IsString)(),
|
|
81
|
+
(0, class_validator_1.MinLength)(1),
|
|
82
|
+
(0, class_validator_1.MaxLength)(copilot_domain_1.MAX_SKILL_INSTRUCTIONS_LENGTH),
|
|
83
|
+
tslib_1.__metadata("design:type", String)
|
|
84
|
+
], UpdateSkillDto.prototype, "instructions", void 0);
|
|
85
|
+
tslib_1.__decorate([
|
|
86
|
+
(0, swagger_1.ApiPropertyOptional)({ enum: create_skill_dto_1.SKILL_MODES }),
|
|
87
|
+
(0, class_validator_1.IsOptional)(),
|
|
88
|
+
(0, class_validator_1.IsIn)(create_skill_dto_1.SKILL_MODES),
|
|
89
|
+
tslib_1.__metadata("design:type", String)
|
|
90
|
+
], UpdateSkillDto.prototype, "mode", void 0);
|
|
91
|
+
tslib_1.__decorate([
|
|
92
|
+
(0, swagger_1.ApiPropertyOptional)({ type: Boolean }),
|
|
93
|
+
(0, class_validator_1.IsOptional)(),
|
|
94
|
+
(0, class_validator_1.IsBoolean)(),
|
|
95
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
96
|
+
], UpdateSkillDto.prototype, "enabled", void 0);
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { type Skill, type SkillRegistry } from '@orthacms/copilot-domain';
|
|
2
|
+
import { SkillRepository } from '../infrastructure/persistence/skill.repository';
|
|
3
|
+
/**
|
|
4
|
+
* A skill as the picker and the manage page render it — **never** carrying
|
|
5
|
+
* `instructions`.
|
|
6
|
+
*
|
|
7
|
+
* The body is withheld from the list on purpose. Everyone with `copilot:use`
|
|
8
|
+
* can read this route, and while a skill's text is not a secret, it is also not
|
|
9
|
+
* something a picker needs: shipping it would put an 8 000-character body per
|
|
10
|
+
* skill into a request the composer makes on every mount. The manage page,
|
|
11
|
+
* which does need it, reads one skill at a time behind
|
|
12
|
+
* `copilot:skills:manage`.
|
|
13
|
+
*/
|
|
14
|
+
export interface SkillSummary {
|
|
15
|
+
/** Row id for a CMS skill; `null` for a code-defined one, which has none. */
|
|
16
|
+
id: string | null;
|
|
17
|
+
name: string;
|
|
18
|
+
title: string;
|
|
19
|
+
description: string;
|
|
20
|
+
mode: Skill['mode'];
|
|
21
|
+
source: Skill['source'];
|
|
22
|
+
/** Always true in the run catalogue; the manage list shows both. */
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Whether this deployment lets the skill be edited here. False for code
|
|
26
|
+
* skills, so the manage page can render them read-only rather than
|
|
27
|
+
* offering a form whose save is refused.
|
|
28
|
+
*/
|
|
29
|
+
editable: boolean;
|
|
30
|
+
}
|
|
31
|
+
/** Thrown when a run names skills that do not resolve in its workspace. */
|
|
32
|
+
export declare class SkillResolutionError extends Error {
|
|
33
|
+
constructor(message: string);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The one place the two sources of skills become one catalogue.
|
|
37
|
+
*
|
|
38
|
+
* Code skills come from the registry the host built at boot; CMS skills come
|
|
39
|
+
* from this workspace's rows. `mergeSkills` decides collisions (code wins), and
|
|
40
|
+
* everything downstream — the picker, the prompt, the run resolver — reads the
|
|
41
|
+
* merged view rather than either source.
|
|
42
|
+
*/
|
|
43
|
+
export declare class SkillCatalogService {
|
|
44
|
+
private readonly registry;
|
|
45
|
+
private readonly repository;
|
|
46
|
+
constructor(registry: SkillRegistry, repository: SkillRepository);
|
|
47
|
+
/**
|
|
48
|
+
* Every skill a run in this workspace could use: the deployment's code
|
|
49
|
+
* skills plus the workspace's **enabled** rows.
|
|
50
|
+
*/
|
|
51
|
+
available(workspaceId: string): Promise<Skill[]>;
|
|
52
|
+
/** The picker's view — the available catalogue, without the bodies. */
|
|
53
|
+
catalogue(workspaceId: string): Promise<SkillSummary[]>;
|
|
54
|
+
/**
|
|
55
|
+
* The manage page's view — code skills (read-only) and **every** CMS row,
|
|
56
|
+
* disabled ones included.
|
|
57
|
+
*
|
|
58
|
+
* A shadowed row is still listed, because the manage page is the only place
|
|
59
|
+
* anyone could discover that a deploy has taken its name over; hiding it
|
|
60
|
+
* would leave an admin editing a skill that silently never runs.
|
|
61
|
+
*/
|
|
62
|
+
manageList(workspaceId: string): Promise<SkillSummary[]>;
|
|
63
|
+
/** Whether a name is already taken in this workspace, by either source. */
|
|
64
|
+
nameTaken(name: string, workspaceId: string, exceptId?: string): Promise<'code' | 'cms' | null>;
|
|
65
|
+
/**
|
|
66
|
+
* What one run needs to know about skills: everything the workspace offers,
|
|
67
|
+
* and the subset in force.
|
|
68
|
+
*
|
|
69
|
+
* Both together in one call because the prompt needs both and they come
|
|
70
|
+
* from the same query — the bodies of the active ones, and one line each
|
|
71
|
+
* for the rest so the model can recommend one it was not given.
|
|
72
|
+
*/
|
|
73
|
+
resolveRunSkills(workspaceId: string, requested: readonly string[]): Promise<{
|
|
74
|
+
available: Skill[];
|
|
75
|
+
inForce: Skill[];
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* The skills one run actually runs with: the workspace's always-on skills,
|
|
79
|
+
* then the ones the person attached, de-duplicated.
|
|
80
|
+
*
|
|
81
|
+
* **Always-on first.** They are the workspace's standing instructions and
|
|
82
|
+
* an attached skill is the specific request on top of them, so the specific
|
|
83
|
+
* one reads last — closest to the message, which is where a model resolves
|
|
84
|
+
* a conflict in its favour.
|
|
85
|
+
*
|
|
86
|
+
* @throws {SkillResolutionError} When a requested name does not resolve.
|
|
87
|
+
* The message names a **count**, never which name: this resolver is
|
|
88
|
+
* workspace-scoped, so "belongs to another workspace" and "was just
|
|
89
|
+
* deleted" are indistinguishable here, and saying which would turn the one
|
|
90
|
+
* place a caller picks names into an oracle for other workspaces' skills.
|
|
91
|
+
* Same rule the attachment resolver follows.
|
|
92
|
+
*/
|
|
93
|
+
resolveForRun(workspaceId: string, requested: readonly string[]): Promise<Skill[]>;
|
|
94
|
+
/** The pure half of {@link resolveForRun}, over an already-loaded catalogue. */
|
|
95
|
+
private selectInForce;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=skill-catalog.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skill-catalog.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/skills/application/skill-catalog.service.ts"],"names":[],"mappings":"AACA,OAAO,EAEH,KAAK,KAAK,EACV,KAAK,aAAa,EACrB,MAAM,0BAA0B,CAAC;AAElC,OAAO,EACH,eAAe,EAElB,MAAM,gDAAgD,CAAC;AAExD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,YAAY;IACzB,6EAA6E;IAC7E,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACpB,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;IACxB,oEAAoE;IACpE,OAAO,EAAE,OAAO,CAAC;IACjB;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CACrB;AAED,2EAA2E;AAC3E,qBAAa,oBAAqB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI9B;AAED;;;;;;;GAOG;AACH,qBACa,mBAAmB;IAGxB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;gBADV,QAAQ,EAAE,aAAa,EACvB,UAAU,EAAE,eAAe;IAGhD;;;OAGG;IACG,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAKtD,uEAAuE;IACjE,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAQ7D;;;;;;;OAOG;IACG,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC;IAmB9D,2EAA2E;IACrE,SAAS,CACX,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,KAAK,GAAG,IAAI,CAAC;IAWjC;;;;;;;OAOG;IACG,gBAAgB,CAClB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC;QAAE,SAAS,EAAE,KAAK,EAAE,CAAC;QAAC,OAAO,EAAE,KAAK,EAAE,CAAA;KAAE,CAAC;IAQpD;;;;;;;;;;;;;;;OAeG;IACG,aAAa,CACf,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,SAAS,MAAM,EAAE,GAC7B,OAAO,CAAC,KAAK,EAAE,CAAC;IAInB,gFAAgF;IAChF,OAAO,CAAC,aAAa;CA+BxB"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SkillCatalogService = exports.SkillResolutionError = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const common_1 = require("@nestjs/common");
|
|
6
|
+
const copilot_domain_1 = require("@orthacms/copilot-domain");
|
|
7
|
+
const copilot_tokens_1 = require("../../copilot.tokens");
|
|
8
|
+
const skill_repository_1 = require("../infrastructure/persistence/skill.repository");
|
|
9
|
+
/** Thrown when a run names skills that do not resolve in its workspace. */
|
|
10
|
+
class SkillResolutionError extends Error {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.name = 'SkillResolutionError';
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.SkillResolutionError = SkillResolutionError;
|
|
17
|
+
/**
|
|
18
|
+
* The one place the two sources of skills become one catalogue.
|
|
19
|
+
*
|
|
20
|
+
* Code skills come from the registry the host built at boot; CMS skills come
|
|
21
|
+
* from this workspace's rows. `mergeSkills` decides collisions (code wins), and
|
|
22
|
+
* everything downstream — the picker, the prompt, the run resolver — reads the
|
|
23
|
+
* merged view rather than either source.
|
|
24
|
+
*/
|
|
25
|
+
let SkillCatalogService = class SkillCatalogService {
|
|
26
|
+
registry;
|
|
27
|
+
repository;
|
|
28
|
+
constructor(registry, repository) {
|
|
29
|
+
this.registry = registry;
|
|
30
|
+
this.repository = repository;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Every skill a run in this workspace could use: the deployment's code
|
|
34
|
+
* skills plus the workspace's **enabled** rows.
|
|
35
|
+
*/
|
|
36
|
+
async available(workspaceId) {
|
|
37
|
+
const rows = await this.repository.list(workspaceId, true);
|
|
38
|
+
return (0, copilot_domain_1.mergeSkills)(this.registry.all(), rows);
|
|
39
|
+
}
|
|
40
|
+
/** The picker's view — the available catalogue, without the bodies. */
|
|
41
|
+
async catalogue(workspaceId) {
|
|
42
|
+
const rows = await this.repository.list(workspaceId, true);
|
|
43
|
+
const byName = new Map(rows.map((row) => [row.name, row]));
|
|
44
|
+
return (0, copilot_domain_1.mergeSkills)(this.registry.all(), rows).map((skill) => toSummary(skill, byName.get(skill.name) ?? null, true));
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* The manage page's view — code skills (read-only) and **every** CMS row,
|
|
48
|
+
* disabled ones included.
|
|
49
|
+
*
|
|
50
|
+
* A shadowed row is still listed, because the manage page is the only place
|
|
51
|
+
* anyone could discover that a deploy has taken its name over; hiding it
|
|
52
|
+
* would leave an admin editing a skill that silently never runs.
|
|
53
|
+
*/
|
|
54
|
+
async manageList(workspaceId) {
|
|
55
|
+
const rows = await this.repository.list(workspaceId);
|
|
56
|
+
const shadowed = new Set(rows
|
|
57
|
+
.filter((row) => this.registry.has(row.name))
|
|
58
|
+
.map((row) => row.name));
|
|
59
|
+
return [
|
|
60
|
+
...this.registry.all().map((skill) => toSummary(skill, null, true)),
|
|
61
|
+
...rows.map((row) => ({
|
|
62
|
+
...toSummary(row, row, row.enabled),
|
|
63
|
+
// A shadowed row can be edited (renaming it is the fix) but is
|
|
64
|
+
// reported as not enabled, because it is not: the code skill
|
|
65
|
+
// holds the name and this row never reaches a run.
|
|
66
|
+
enabled: row.enabled && !shadowed.has(row.name)
|
|
67
|
+
}))
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
/** Whether a name is already taken in this workspace, by either source. */
|
|
71
|
+
async nameTaken(name, workspaceId, exceptId) {
|
|
72
|
+
if (this.registry.has(name)) {
|
|
73
|
+
return 'code';
|
|
74
|
+
}
|
|
75
|
+
const existing = await this.repository.findByName(name, workspaceId);
|
|
76
|
+
if (existing && existing.id !== exceptId) {
|
|
77
|
+
return 'cms';
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* What one run needs to know about skills: everything the workspace offers,
|
|
83
|
+
* and the subset in force.
|
|
84
|
+
*
|
|
85
|
+
* Both together in one call because the prompt needs both and they come
|
|
86
|
+
* from the same query — the bodies of the active ones, and one line each
|
|
87
|
+
* for the rest so the model can recommend one it was not given.
|
|
88
|
+
*/
|
|
89
|
+
async resolveRunSkills(workspaceId, requested) {
|
|
90
|
+
const available = await this.available(workspaceId);
|
|
91
|
+
return {
|
|
92
|
+
available,
|
|
93
|
+
inForce: this.selectInForce(available, requested)
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* The skills one run actually runs with: the workspace's always-on skills,
|
|
98
|
+
* then the ones the person attached, de-duplicated.
|
|
99
|
+
*
|
|
100
|
+
* **Always-on first.** They are the workspace's standing instructions and
|
|
101
|
+
* an attached skill is the specific request on top of them, so the specific
|
|
102
|
+
* one reads last — closest to the message, which is where a model resolves
|
|
103
|
+
* a conflict in its favour.
|
|
104
|
+
*
|
|
105
|
+
* @throws {SkillResolutionError} When a requested name does not resolve.
|
|
106
|
+
* The message names a **count**, never which name: this resolver is
|
|
107
|
+
* workspace-scoped, so "belongs to another workspace" and "was just
|
|
108
|
+
* deleted" are indistinguishable here, and saying which would turn the one
|
|
109
|
+
* place a caller picks names into an oracle for other workspaces' skills.
|
|
110
|
+
* Same rule the attachment resolver follows.
|
|
111
|
+
*/
|
|
112
|
+
async resolveForRun(workspaceId, requested) {
|
|
113
|
+
return this.selectInForce(await this.available(workspaceId), requested);
|
|
114
|
+
}
|
|
115
|
+
/** The pure half of {@link resolveForRun}, over an already-loaded catalogue. */
|
|
116
|
+
selectInForce(available, requested) {
|
|
117
|
+
const always = available.filter((skill) => skill.mode === 'always');
|
|
118
|
+
if (requested.length === 0) {
|
|
119
|
+
return always;
|
|
120
|
+
}
|
|
121
|
+
const byName = new Map(available.map((skill) => [skill.name, skill]));
|
|
122
|
+
const unique = [...new Set(requested)];
|
|
123
|
+
const attached = unique
|
|
124
|
+
.map((name) => byName.get(name))
|
|
125
|
+
.filter((skill) => skill !== undefined);
|
|
126
|
+
if (attached.length !== unique.length) {
|
|
127
|
+
const missing = unique.length - attached.length;
|
|
128
|
+
throw new SkillResolutionError(missing === 1
|
|
129
|
+
? 'One of the selected skills is no longer available.'
|
|
130
|
+
: `${missing} of the selected skills are no longer available.`);
|
|
131
|
+
}
|
|
132
|
+
const seen = new Set(always.map((skill) => skill.name));
|
|
133
|
+
return [
|
|
134
|
+
...always,
|
|
135
|
+
...attached.filter((skill) => !seen.has(skill.name))
|
|
136
|
+
];
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
exports.SkillCatalogService = SkillCatalogService;
|
|
140
|
+
exports.SkillCatalogService = SkillCatalogService = tslib_1.__decorate([
|
|
141
|
+
(0, common_1.Injectable)(),
|
|
142
|
+
tslib_1.__param(0, (0, common_1.Inject)(copilot_tokens_1.COPILOT_SKILL_REGISTRY)),
|
|
143
|
+
tslib_1.__metadata("design:paramtypes", [Object, skill_repository_1.SkillRepository])
|
|
144
|
+
], SkillCatalogService);
|
|
145
|
+
/** The list view of a skill, with its row id when it has one. */
|
|
146
|
+
function toSummary(skill, row, enabled) {
|
|
147
|
+
return {
|
|
148
|
+
id: row?.id ?? null,
|
|
149
|
+
name: skill.name,
|
|
150
|
+
title: skill.title,
|
|
151
|
+
description: skill.description,
|
|
152
|
+
mode: skill.mode,
|
|
153
|
+
source: skill.source,
|
|
154
|
+
enabled,
|
|
155
|
+
editable: skill.source === 'cms'
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SkillCatalogService, type SkillSummary } from '../../application/skill-catalog.service';
|
|
2
|
+
/**
|
|
3
|
+
* `GET /api/copilot/skills` — the catalogue the composer's picker renders.
|
|
4
|
+
*
|
|
5
|
+
* Gated on `copilot:use`, not on the manage permission: everybody who can chat
|
|
6
|
+
* can attach a skill, and a picker that showed nothing to a contributor would
|
|
7
|
+
* make the feature invisible to the people it is mostly for. What it does
|
|
8
|
+
* **not** carry is `instructions` — see `SkillSummary` for why.
|
|
9
|
+
*
|
|
10
|
+
* `WorkspaceGuard` because the catalogue differs per workspace: code skills are
|
|
11
|
+
* deployment-wide, CMS skills are not, and serving the union to an unvalidated
|
|
12
|
+
* workspace id would leak one workspace's editorial guidance to another.
|
|
13
|
+
*/
|
|
14
|
+
export declare class ListSkillsController {
|
|
15
|
+
private readonly catalog;
|
|
16
|
+
constructor(catalog: SkillCatalogService);
|
|
17
|
+
list(workspaceId: string): Promise<SkillSummary[]>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=list-skills.controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"list-skills.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/http/controllers/list-skills.controller.ts"],"names":[],"mappings":"AAQA,OAAO,EACH,mBAAmB,EACnB,KAAK,YAAY,EACpB,MAAM,yCAAyC,CAAC;AAEjD;;;;;;;;;;;GAWG;AACH,qBAGa,oBAAoB;IACjB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,mBAAmB;IAUnD,IAAI,CACc,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,YAAY,EAAE,CAAC;CAG7B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ListSkillsController = 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 skill_catalog_service_1 = require("../../application/skill-catalog.service");
|
|
10
|
+
/**
|
|
11
|
+
* `GET /api/copilot/skills` — the catalogue the composer's picker renders.
|
|
12
|
+
*
|
|
13
|
+
* Gated on `copilot:use`, not on the manage permission: everybody who can chat
|
|
14
|
+
* can attach a skill, and a picker that showed nothing to a contributor would
|
|
15
|
+
* make the feature invisible to the people it is mostly for. What it does
|
|
16
|
+
* **not** carry is `instructions` — see `SkillSummary` for why.
|
|
17
|
+
*
|
|
18
|
+
* `WorkspaceGuard` because the catalogue differs per workspace: code skills are
|
|
19
|
+
* deployment-wide, CMS skills are not, and serving the union to an unvalidated
|
|
20
|
+
* workspace id would leak one workspace's editorial guidance to another.
|
|
21
|
+
*/
|
|
22
|
+
let ListSkillsController = class ListSkillsController {
|
|
23
|
+
catalog;
|
|
24
|
+
constructor(catalog) {
|
|
25
|
+
this.catalog = catalog;
|
|
26
|
+
}
|
|
27
|
+
async list(workspaceId) {
|
|
28
|
+
return this.catalog.catalogue(workspaceId);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
exports.ListSkillsController = ListSkillsController;
|
|
32
|
+
tslib_1.__decorate([
|
|
33
|
+
(0, common_1.Get)('skills'),
|
|
34
|
+
(0, swagger_1.ApiOperation)({
|
|
35
|
+
summary: 'List the skills available in this workspace',
|
|
36
|
+
description: 'Code-defined skills first, then the workspace’s own enabled ones. ' +
|
|
37
|
+
'Instruction bodies are not included.'
|
|
38
|
+
}),
|
|
39
|
+
(0, swagger_1.ApiResponse)({ status: 200, description: 'The available skills.' }),
|
|
40
|
+
tslib_1.__param(0, (0, workspaces_server_1.CurrentWorkspace)()),
|
|
41
|
+
tslib_1.__metadata("design:type", Function),
|
|
42
|
+
tslib_1.__metadata("design:paramtypes", [String]),
|
|
43
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
44
|
+
], ListSkillsController.prototype, "list", null);
|
|
45
|
+
exports.ListSkillsController = ListSkillsController = 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", [skill_catalog_service_1.SkillCatalogService])
|
|
50
|
+
], ListSkillsController);
|