@orthacms/copilot-server 0.4.2 → 0.5.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/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/lib/chat/application/decide-proposal.service.d.ts.map +1 -1
- package/dist/lib/chat/application/decide-proposal.service.js +6 -1
- package/dist/lib/chat/application/dto/create-run.dto.d.ts +9 -2
- package/dist/lib/chat/application/dto/create-run.dto.d.ts.map +1 -1
- package/dist/lib/chat/application/dto/create-run.dto.js +15 -2
- package/dist/lib/chat/application/proposal-event-actor.d.ts +26 -0
- package/dist/lib/chat/application/proposal-event-actor.d.ts.map +1 -0
- package/dist/lib/chat/application/proposal-event-actor.js +36 -0
- package/dist/lib/chat/application/run-engine.service.d.ts +10 -1
- package/dist/lib/chat/application/run-engine.service.d.ts.map +1 -1
- package/dist/lib/chat/application/run-engine.service.js +38 -7
- package/dist/lib/chat/application/system-prompt.d.ts.map +1 -1
- package/dist/lib/chat/application/system-prompt.js +7 -0
- package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts +5 -2
- package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts.map +1 -1
- package/dist/lib/chat/http/controllers/tool-permission.controller.js +27 -4
- package/dist/lib/copilot.events.d.ts +56 -0
- package/dist/lib/copilot.events.d.ts.map +1 -0
- package/dist/lib/copilot.events.js +74 -0
- package/dist/lib/copilot.module.d.ts.map +1 -1
- package/dist/lib/copilot.module.js +55 -31
- package/dist/lib/infrastructure/model-registry.d.ts +1 -1
- package/dist/lib/infrastructure/model-registry.d.ts.map +1 -1
- package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts +11 -3
- package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts.map +1 -1
- package/dist/lib/skills/http/controllers/manage-skills.controller.js +84 -26
- package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts +10 -3
- package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts.map +1 -1
- package/dist/lib/skills/infrastructure/persistence/skill.repository.js +13 -5
- package/dist/lib/utils/copilot-plugin.d.ts +9 -2
- package/dist/lib/utils/copilot-plugin.d.ts.map +1 -1
- package/dist/lib/utils/copilot-plugin.js +21 -9
- package/package.json +7 -7
package/dist/index.d.ts
CHANGED
|
@@ -21,4 +21,5 @@ export { SkillRepository } from './lib/skills/infrastructure/persistence/skill.r
|
|
|
21
21
|
export type { SkillRecord } from './lib/skills/infrastructure/persistence/skill.repository';
|
|
22
22
|
export * from './lib/chat/infrastructure/schema';
|
|
23
23
|
export * from './lib/skills/infrastructure/schema';
|
|
24
|
+
export { proposalEventActor } from './lib/chat/application/proposal-event-actor';
|
|
24
25
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,YAAY,EACR,oBAAoB,EACpB,6BAA6B,EAChC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACH,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAMhF,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAG3F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AAKrF,OAAO,EACH,SAAS,EACT,oBAAoB,EACpB,uBAAuB,EAC1B,MAAM,2CAA2C,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,+DAA+D,CAAC;AACvG,YAAY,EACR,gBAAgB,EAChB,WAAW,EACX,cAAc,EACjB,MAAM,+DAA+D,CAAC;AACvE,OAAO,EACH,iBAAiB,EACjB,qBAAqB,EACxB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACR,cAAc,EACd,iBAAiB,EACpB,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EACH,mBAAmB,EACnB,oBAAoB,EACvB,MAAM,gDAAgD,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,YAAY,EAAE,WAAW,EAAE,MAAM,0DAA0D,CAAC;AAG5F,cAAc,kCAAkC,CAAC;AACjD,cAAc,oCAAoC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,YAAY,EACR,oBAAoB,EACpB,6BAA6B,EAChC,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,YAAY,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EACH,cAAc,EACd,kBAAkB,EAClB,sBAAsB,EACtB,mBAAmB,EACtB,MAAM,sBAAsB,CAAC;AAC9B,YAAY,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,YAAY,EAAE,oBAAoB,EAAE,MAAM,qCAAqC,CAAC;AAMhF,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAG3F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AAKrF,OAAO,EACH,SAAS,EACT,oBAAoB,EACpB,uBAAuB,EAC1B,MAAM,2CAA2C,CAAC;AACnD,YAAY,EAAE,aAAa,EAAE,MAAM,2CAA2C,CAAC;AAC/E,OAAO,EAAE,wBAAwB,EAAE,MAAM,mDAAmD,CAAC;AAC7F,OAAO,EAAE,sBAAsB,EAAE,MAAM,+DAA+D,CAAC;AACvG,YAAY,EACR,gBAAgB,EAChB,WAAW,EACX,cAAc,EACjB,MAAM,+DAA+D,CAAC;AACvE,OAAO,EACH,iBAAiB,EACjB,qBAAqB,EACxB,MAAM,sCAAsC,CAAC;AAC9C,YAAY,EACR,cAAc,EACd,iBAAiB,EACpB,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EACH,mBAAmB,EACnB,oBAAoB,EACvB,MAAM,gDAAgD,CAAC;AACxD,YAAY,EAAE,YAAY,EAAE,MAAM,gDAAgD,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,0DAA0D,CAAC;AAC3F,YAAY,EAAE,WAAW,EAAE,MAAM,0DAA0D,CAAC;AAG5F,cAAc,kCAAkC,CAAC;AACjD,cAAc,oCAAoC,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,6CAA6C,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SkillRepository = exports.SkillResolutionError = exports.SkillCatalogService = exports.SYSTEM_PROMPT_VERSION = exports.buildSystemPrompt = exports.ConversationRepository = exports.CapabilityProfileService = exports.UnknownModelChoiceError = exports.CopilotDisabledError = exports.RunEngine = exports.copilotAppliersRegistrar = exports.ProposalApplierRegistry = exports.buildModelRegistry = exports.InjectCopilotConfig = exports.COPILOT_SKILL_REGISTRY = exports.COPILOT_RUN_LIMITS = exports.COPILOT_CONFIG = exports.CopilotModule = exports.CopilotPlugin = void 0;
|
|
3
|
+
exports.proposalEventActor = exports.SkillRepository = exports.SkillResolutionError = exports.SkillCatalogService = exports.SYSTEM_PROMPT_VERSION = exports.buildSystemPrompt = exports.ConversationRepository = exports.CapabilityProfileService = exports.UnknownModelChoiceError = exports.CopilotDisabledError = exports.RunEngine = exports.copilotAppliersRegistrar = exports.ProposalApplierRegistry = exports.buildModelRegistry = exports.InjectCopilotConfig = exports.COPILOT_SKILL_REGISTRY = exports.COPILOT_RUN_LIMITS = exports.COPILOT_CONFIG = exports.CopilotModule = exports.CopilotPlugin = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
var copilot_plugin_1 = require("./lib/utils/copilot-plugin");
|
|
6
6
|
Object.defineProperty(exports, "CopilotPlugin", { enumerable: true, get: function () { return copilot_plugin_1.CopilotPlugin; } });
|
|
@@ -46,3 +46,5 @@ Object.defineProperty(exports, "SkillRepository", { enumerable: true, get: funct
|
|
|
46
46
|
// The Drizzle tables this plugin owns and migrates.
|
|
47
47
|
tslib_1.__exportStar(require("./lib/chat/infrastructure/schema"), exports);
|
|
48
48
|
tslib_1.__exportStar(require("./lib/skills/infrastructure/schema"), exports);
|
|
49
|
+
var proposal_event_actor_1 = require("./lib/chat/application/proposal-event-actor");
|
|
50
|
+
Object.defineProperty(exports, "proposalEventActor", { enumerable: true, get: function () { return proposal_event_actor_1.proposalEventActor; } });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decide-proposal.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/decide-proposal.service.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,kBAAkB,EAClB,KAAK,YAAY,EACpB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,6CAA6C;AAC7C,MAAM,MAAM,YAAY;AACpB,kCAAkC;AAChC,iBAAiB;AACnB,+EAA+E;GAC7E,YAAY;AACd,8DAA8D;GAC5D,cAAc,CAAC;AAErB,+BAA+B;AAC/B,MAAM,MAAM,YAAY,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBACa,qBAAqB;IAI1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;gBAG5C,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,uBAAuB;IAGtD;;;;;;;;OAQG;IACG,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;IAkBzE;;;;;;;OAOG;YACW,iBAAiB;
|
|
1
|
+
{"version":3,"file":"decide-proposal.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/decide-proposal.service.ts"],"names":[],"mappings":"AAEA,OAAO,EACH,kBAAkB,EAClB,KAAK,YAAY,EACpB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAEtE,4CAA4C;AAC5C,MAAM,WAAW,SAAS;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,6CAA6C;AAC7C,MAAM,MAAM,YAAY;AACpB,kCAAkC;AAChC,iBAAiB;AACnB,+EAA+E;GAC7E,YAAY;AACd,8DAA8D;GAC5D,cAAc,CAAC;AAErB,+BAA+B;AAC/B,MAAM,MAAM,YAAY,GAClB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACpC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,YAAY,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBACa,qBAAqB;IAI1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAJ7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA0C;gBAG5C,SAAS,EAAE,kBAAkB,EAC7B,QAAQ,EAAE,uBAAuB;IAGtD;;;;;;;;OAQG;IACG,KAAK,CAAC,QAAQ,EAAE,YAAY,EAAE,EAAE,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC;IAkBzE;;;;;;;OAOG;YACW,iBAAiB;CAwDlC"}
|
|
@@ -81,7 +81,12 @@ let DecideProposalService = DecideProposalService_1 = class DecideProposalServic
|
|
|
81
81
|
userId: by.userId,
|
|
82
82
|
actorEmail: by.email,
|
|
83
83
|
workspaceId: by.workspaceId,
|
|
84
|
-
runId: proposal.runId
|
|
84
|
+
runId: proposal.runId,
|
|
85
|
+
// Provenance, alongside the run: the applier stamps the
|
|
86
|
+
// pair onto the domain event, so an audit row can say which
|
|
87
|
+
// change an agent turn actually made rather than only that
|
|
88
|
+
// one happened.
|
|
89
|
+
proposalId: proposal.id
|
|
85
90
|
});
|
|
86
91
|
// A second write rather than folding the result into the status
|
|
87
92
|
// transition: that transition only matches a `pending` row, and
|
|
@@ -8,8 +8,15 @@ export declare const MAX_MESSAGE_LENGTH = 8000;
|
|
|
8
8
|
* the model is ever called.
|
|
9
9
|
*/
|
|
10
10
|
export declare const MAX_RUN_ATTACHMENTS = 8;
|
|
11
|
-
/**
|
|
12
|
-
|
|
11
|
+
/**
|
|
12
|
+
* The surfaces a run can be started from (design §2).
|
|
13
|
+
*
|
|
14
|
+
* `create` is the new-entry form. It is a separate surface rather than
|
|
15
|
+
* `records` because the client cannot describe it any other way: the URL has a
|
|
16
|
+
* content type but no entry id, so the form used to arrive here claiming the
|
|
17
|
+
* person was looking at a list.
|
|
18
|
+
*/
|
|
19
|
+
export declare const RUN_SURFACES: readonly ["chat", "palette", "entry", "records", "create"];
|
|
13
20
|
/**
|
|
14
21
|
* Where the user was when they asked. Every field is optional — the chat panel
|
|
15
22
|
* knows only the workspace, the entry editor knows the type and the entry.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-run.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/application/dto/create-run.dto.ts"],"names":[],"mappings":"AAqBA,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC
|
|
1
|
+
{"version":3,"file":"create-run.dto.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/application/dto/create-run.dto.ts"],"names":[],"mappings":"AAqBA,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,OAAQ,CAAC;AAExC;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAErC;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,4DAMf,CAAC;AAEX;;;;;;;;;;GAUG;AACH,qBAAa,aAAa;IACtB,sCAAsC;IAItC,OAAO,CAAC,EAAE,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;IAExC,wCAAwC;IAKxC,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,iCAAiC;IAKjC,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,0CAA0C;IAK1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,gBAAgB;IACzB,oEAAoE;IAGpE,OAAO,EAAG,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;GAWG;AACH,qBAAa,WAAW;IACpB,wEAAwE;IAUxE,IAAI,EAAG,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,qBAAa,YAAY;IACrB,2BAA2B;IAK3B,OAAO,EAAG,MAAM,CAAC;IAEjB,qDAAqD;IAIrD,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;;OAKG;IAKH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;OAOG;IAKH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;;;;;;;;;OAUG;IAKH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yBAAyB;IAMzB,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB;;;;;;;;;;OAUG;IAOH,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAEjC;;;;;;;;;OASG;IAOH,MAAM,CAAC,EAAE,WAAW,EAAE,CAAC;CAC1B"}
|
|
@@ -16,8 +16,21 @@ exports.MAX_MESSAGE_LENGTH = 8_000;
|
|
|
16
16
|
* the model is ever called.
|
|
17
17
|
*/
|
|
18
18
|
exports.MAX_RUN_ATTACHMENTS = 8;
|
|
19
|
-
/**
|
|
20
|
-
|
|
19
|
+
/**
|
|
20
|
+
* The surfaces a run can be started from (design §2).
|
|
21
|
+
*
|
|
22
|
+
* `create` is the new-entry form. It is a separate surface rather than
|
|
23
|
+
* `records` because the client cannot describe it any other way: the URL has a
|
|
24
|
+
* content type but no entry id, so the form used to arrive here claiming the
|
|
25
|
+
* person was looking at a list.
|
|
26
|
+
*/
|
|
27
|
+
exports.RUN_SURFACES = [
|
|
28
|
+
'chat',
|
|
29
|
+
'palette',
|
|
30
|
+
'entry',
|
|
31
|
+
'records',
|
|
32
|
+
'create'
|
|
33
|
+
];
|
|
21
34
|
/**
|
|
22
35
|
* Where the user was when they asked. Every field is optional — the chat panel
|
|
23
36
|
* knows only the workspace, the entry editor knows the type and the entry.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { EventActor } from '@orthacms/database';
|
|
2
|
+
import type { ProposalActor } from '@orthacms/copilot-domain';
|
|
3
|
+
/**
|
|
4
|
+
* The {@link EventActor} an applier stamps on the domain events its write
|
|
5
|
+
* raises.
|
|
6
|
+
*
|
|
7
|
+
* The **actor is the human** who accepted — there is no copilot identity and
|
|
8
|
+
* there must not be one, since the write runs on their authority and their
|
|
9
|
+
* permissions. `via` is what stops the resulting audit row being
|
|
10
|
+
* indistinguishable from one they typed: under
|
|
11
|
+
* [ADR-0009](../../../../../../docs/adr/0009-copilot-applies-directly.md) a
|
|
12
|
+
* proposal applies as it is drafted, so without it "Ada updated three articles"
|
|
13
|
+
* could equally mean Ada edited three articles or that Ada accepted one agent
|
|
14
|
+
* turn that rewrote them.
|
|
15
|
+
*
|
|
16
|
+
* Shared rather than restated in each applier because there are nine of them
|
|
17
|
+
* across four packages, and an applier that quietly omitted `via` would produce
|
|
18
|
+
* exactly the row this exists to prevent — with nothing failing anywhere.
|
|
19
|
+
*
|
|
20
|
+
* **It lives here, not in `copilot-domain`, because that package imports
|
|
21
|
+
* nothing** — not even `@orthacms/database` — and `EventActor` would be an
|
|
22
|
+
* import. Every package that binds an applier already depends on this one for
|
|
23
|
+
* `copilotAppliersRegistrar`, so there is nothing new in the graph.
|
|
24
|
+
*/
|
|
25
|
+
export declare function proposalEventActor(actor: ProposalActor): EventActor;
|
|
26
|
+
//# sourceMappingURL=proposal-event-actor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposal-event-actor.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/proposal-event-actor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,UAAU,CAUnE"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.proposalEventActor = proposalEventActor;
|
|
4
|
+
/**
|
|
5
|
+
* The {@link EventActor} an applier stamps on the domain events its write
|
|
6
|
+
* raises.
|
|
7
|
+
*
|
|
8
|
+
* The **actor is the human** who accepted — there is no copilot identity and
|
|
9
|
+
* there must not be one, since the write runs on their authority and their
|
|
10
|
+
* permissions. `via` is what stops the resulting audit row being
|
|
11
|
+
* indistinguishable from one they typed: under
|
|
12
|
+
* [ADR-0009](../../../../../../docs/adr/0009-copilot-applies-directly.md) a
|
|
13
|
+
* proposal applies as it is drafted, so without it "Ada updated three articles"
|
|
14
|
+
* could equally mean Ada edited three articles or that Ada accepted one agent
|
|
15
|
+
* turn that rewrote them.
|
|
16
|
+
*
|
|
17
|
+
* Shared rather than restated in each applier because there are nine of them
|
|
18
|
+
* across four packages, and an applier that quietly omitted `via` would produce
|
|
19
|
+
* exactly the row this exists to prevent — with nothing failing anywhere.
|
|
20
|
+
*
|
|
21
|
+
* **It lives here, not in `copilot-domain`, because that package imports
|
|
22
|
+
* nothing** — not even `@orthacms/database` — and `EventActor` would be an
|
|
23
|
+
* import. Every package that binds an applier already depends on this one for
|
|
24
|
+
* `copilotAppliersRegistrar`, so there is nothing new in the graph.
|
|
25
|
+
*/
|
|
26
|
+
function proposalEventActor(actor) {
|
|
27
|
+
return {
|
|
28
|
+
id: actor.userId,
|
|
29
|
+
email: actor.actorEmail,
|
|
30
|
+
via: {
|
|
31
|
+
kind: 'copilot',
|
|
32
|
+
runId: actor.runId,
|
|
33
|
+
proposalId: actor.proposalId ?? null
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -69,7 +69,16 @@ export interface StartRunInput {
|
|
|
69
69
|
export declare class UnknownModelChoiceError extends Error {
|
|
70
70
|
constructor(message: string);
|
|
71
71
|
}
|
|
72
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* Thrown when a run cannot start at all — the controller turns it into an
|
|
74
|
+
* error frame.
|
|
75
|
+
*
|
|
76
|
+
* **Rarely reachable over HTTP**: the copilot's kill switch now unregisters
|
|
77
|
+
* every controller (`copilot.module.ts`), so a disabled deployment 404s the run
|
|
78
|
+
* route rather than opening a stream to say no. Kept because the engine is
|
|
79
|
+
* drainable without a socket, and a caller that gets one anyway deserves a
|
|
80
|
+
* named reason rather than a 500.
|
|
81
|
+
*/
|
|
73
82
|
export declare class CopilotDisabledError extends Error {
|
|
74
83
|
constructor();
|
|
75
84
|
}
|
|
@@ -1 +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
|
|
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;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C;AAiBD;;;;;;;;;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;IAyLjE;;;OAGG;YACY,IAAI;IAgJnB,2EAA2E;YAC5D,UAAU;IAiEzB;;;;;;;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"}
|
|
@@ -28,7 +28,16 @@ class UnknownModelChoiceError extends Error {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
exports.UnknownModelChoiceError = UnknownModelChoiceError;
|
|
31
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when a run cannot start at all — the controller turns it into an
|
|
33
|
+
* error frame.
|
|
34
|
+
*
|
|
35
|
+
* **Rarely reachable over HTTP**: the copilot's kill switch now unregisters
|
|
36
|
+
* every controller (`copilot.module.ts`), so a disabled deployment 404s the run
|
|
37
|
+
* route rather than opening a stream to say no. Kept because the engine is
|
|
38
|
+
* drainable without a socket, and a caller that gets one anyway deserves a
|
|
39
|
+
* named reason rather than a 500.
|
|
40
|
+
*/
|
|
32
41
|
class CopilotDisabledError extends Error {
|
|
33
42
|
constructor() {
|
|
34
43
|
super('The copilot is disabled.');
|
|
@@ -81,9 +90,12 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
81
90
|
/** Runs one turn, yielding events as they happen. */
|
|
82
91
|
async *run(input) {
|
|
83
92
|
if (!this.config.enabled) {
|
|
84
|
-
// The operator's kill switch (ADR-0005 §10)
|
|
85
|
-
//
|
|
86
|
-
//
|
|
93
|
+
// The operator's kill switch (ADR-0005 §10), belt-and-braces —
|
|
94
|
+
// `CopilotModule.forRoot` registers no controller when it is off,
|
|
95
|
+
// so a disabled deployment 404s before it reaches this line. This
|
|
96
|
+
// is the guard against a future wiring change, and against a
|
|
97
|
+
// caller that drains the generator without going through HTTP
|
|
98
|
+
// (a tool, a job, a test).
|
|
87
99
|
throw new CopilotDisabledError();
|
|
88
100
|
}
|
|
89
101
|
const runId = (0, node_crypto_1.randomUUID)();
|
|
@@ -154,6 +166,14 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
154
166
|
const history = await this.loadHistory(conversation.id);
|
|
155
167
|
const usage = { inputTokens: 0, outputTokens: 0 };
|
|
156
168
|
const assistantBlocks = [];
|
|
169
|
+
// Text the model has already streamed to the client but that no
|
|
170
|
+
// finished step has moved into `assistantBlocks` yet. A provider that
|
|
171
|
+
// throws mid-stream — a dropped connection, a malformed frame, a
|
|
172
|
+
// cancellation an adapter raises instead of ending the stream with —
|
|
173
|
+
// otherwise takes the whole partial answer with it: the person watched
|
|
174
|
+
// it arrive, and reopening the thread showed their question and
|
|
175
|
+
// silence.
|
|
176
|
+
const partial = { text: '' };
|
|
157
177
|
let stopReason = 'end';
|
|
158
178
|
try {
|
|
159
179
|
stopReason = yield* this.loop({
|
|
@@ -167,10 +187,15 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
167
187
|
history,
|
|
168
188
|
usage,
|
|
169
189
|
assistantBlocks,
|
|
190
|
+
partial,
|
|
170
191
|
startedAt
|
|
171
192
|
});
|
|
172
193
|
}
|
|
173
194
|
catch (error) {
|
|
195
|
+
// Whatever the step had already said, before whatever went wrong.
|
|
196
|
+
if (partial.text) {
|
|
197
|
+
assistantBlocks.push({ type: 'text', text: partial.text });
|
|
198
|
+
}
|
|
174
199
|
if ((0, copilot_domain_1.isAbortError)(error, input.signal)) {
|
|
175
200
|
stopReason = 'aborted';
|
|
176
201
|
}
|
|
@@ -240,6 +265,9 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
240
265
|
if (turn.text) {
|
|
241
266
|
ctx.assistantBlocks.push({ type: 'text', text: turn.text });
|
|
242
267
|
}
|
|
268
|
+
// Banked: anything that throws from here on must not append it a
|
|
269
|
+
// second time.
|
|
270
|
+
ctx.partial.text = '';
|
|
243
271
|
ctx.assistantBlocks.push(...turn.toolUses);
|
|
244
272
|
if (turn.stopReason === 'aborted')
|
|
245
273
|
return 'aborted';
|
|
@@ -326,7 +354,10 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
326
354
|
/** One model call, reduced to text, tool uses, usage and a stop reason. */
|
|
327
355
|
async *streamTurn(ctx, messages, tools) {
|
|
328
356
|
const toolUses = [];
|
|
329
|
-
|
|
357
|
+
// The turn's text lives on the caller's accumulator rather than in a
|
|
358
|
+
// local, so a stream that throws leaves what it had already said
|
|
359
|
+
// somewhere `run` can still persist it.
|
|
360
|
+
ctx.partial.text = '';
|
|
330
361
|
let usage = { inputTokens: 0, outputTokens: 0 };
|
|
331
362
|
let stopReason = 'end';
|
|
332
363
|
const stream = ctx.provider.provider.stream({
|
|
@@ -338,7 +369,7 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
338
369
|
}, ctx.input.signal);
|
|
339
370
|
for await (const event of stream) {
|
|
340
371
|
if (event.type === 'text-delta') {
|
|
341
|
-
text += event.text;
|
|
372
|
+
ctx.partial.text += event.text;
|
|
342
373
|
// Yielded, not collected. Buffering these into an array and
|
|
343
374
|
// flushing after the provider's stream ends turns the whole
|
|
344
375
|
// feature off: the answer arrives in one burst when the model
|
|
@@ -358,7 +389,7 @@ let RunEngine = RunEngine_1 = class RunEngine {
|
|
|
358
389
|
stopReason = event.stopReason;
|
|
359
390
|
}
|
|
360
391
|
}
|
|
361
|
-
return { text, toolUses, usage, stopReason };
|
|
392
|
+
return { text: ctx.partial.text, toolUses, usage, stopReason };
|
|
362
393
|
}
|
|
363
394
|
/**
|
|
364
395
|
* Authorizes, validates and runs one tool call, and writes its audit row.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/system-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,KAAK,EACb,MAAM,0BAA0B,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAC9B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,OAAO,EAAE,cAAc,CAAC;IACxB,wEAAwE;IACxE,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;;;;;OAQG;IACH,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/system-prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,KAAK,EACb,MAAM,0BAA0B,CAAC;AAElC;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC3B,oEAAoE;IACpE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,wCAAwC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,+CAA+C;AAC/C,MAAM,WAAW,iBAAiB;IAC9B,4EAA4E;IAC5E,QAAQ,EAAE,MAAM,CAAC;IACjB,oDAAoD;IACpD,OAAO,EAAE,cAAc,CAAC;IACxB,wEAAwE;IACxE,aAAa,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC;;;;;;;;OAQG;IACH,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;IAC7B;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;IACnC;;;;OAIG;IACH,aAAa,CAAC,EAAE,SAAS,KAAK,EAAE,CAAC;CACpC;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,KAAK,CAAC;AA6DxC;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAgHlE"}
|
|
@@ -55,6 +55,13 @@ const SURFACE_GUIDANCE = new Map([
|
|
|
55
55
|
'The person is looking at a list of entries of the content type named ' +
|
|
56
56
|
'above. Take a question that names no type to be about that one.'
|
|
57
57
|
],
|
|
58
|
+
[
|
|
59
|
+
'create',
|
|
60
|
+
'The person is filling in a new entry of the content type named above. ' +
|
|
61
|
+
'It does not exist yet and has no id, so do not look it up and do ' +
|
|
62
|
+
'not offer to change an existing entry — help them write the ' +
|
|
63
|
+
'values for the record in front of them.'
|
|
64
|
+
],
|
|
58
65
|
[
|
|
59
66
|
'palette',
|
|
60
67
|
'This came from the command palette: one instruction, not a conversation. ' +
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type PublicUser } from '@orthacms/identity-server';
|
|
2
|
+
import { OutboxWriter, UnitOfWork } from '@orthacms/database';
|
|
2
3
|
import { ToolPermissionBroker } from '../../application/tool-permission.broker';
|
|
3
4
|
import { DecideToolPermissionDto } from '../../application/dto/decide-tool-permission.dto';
|
|
4
5
|
import { ExtendToolPermissionDto } from '../../application/dto/extend-tool-permission.dto';
|
|
@@ -34,8 +35,10 @@ import { ExtendToolPermissionDto } from '../../application/dto/extend-tool-permi
|
|
|
34
35
|
*/
|
|
35
36
|
export declare class ToolPermissionController {
|
|
36
37
|
private readonly broker;
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
private readonly uow;
|
|
39
|
+
private readonly outbox;
|
|
40
|
+
constructor(broker: ToolPermissionBroker, uow: UnitOfWork, outbox: OutboxWriter);
|
|
41
|
+
decide(runId: string, body: DecideToolPermissionDto, user: PublicUser, workspaceId: string): Promise<void>;
|
|
39
42
|
/**
|
|
40
43
|
* `POST /api/copilot/runs/:runId/permission/extend` — "I need more time".
|
|
41
44
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tool-permission.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/http/controllers/tool-permission.controller.ts"],"names":[],"mappings":"AAWA,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAC3F,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAE3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAGa,wBAAwB;
|
|
1
|
+
{"version":3,"file":"tool-permission.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/chat/http/controllers/tool-permission.controller.ts"],"names":[],"mappings":"AAWA,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAEH,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAC3F,OAAO,EAAE,uBAAuB,EAAE,MAAM,kDAAkD,CAAC;AAE3F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,qBAGa,wBAAwB;IAE7B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAFN,MAAM,EAAE,oBAAoB,EAC5B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY;IASnC,MAAM,CACuB,KAAK,EAAE,MAAM,EACpC,IAAI,EAAE,uBAAuB,EACtB,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,IAAI,CAAC;IAwChB;;;;;;;;;;;;;OAaG;IAMH,MAAM,CAC6B,KAAK,EAAE,MAAM,EACpC,IAAI,EAAE,uBAAuB,EACtB,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE;CAY3B"}
|
|
@@ -6,6 +6,8 @@ const common_1 = require("@nestjs/common");
|
|
|
6
6
|
const swagger_1 = require("@nestjs/swagger");
|
|
7
7
|
const identity_server_1 = require("@orthacms/identity-server");
|
|
8
8
|
const workspaces_server_1 = require("@orthacms/workspaces-server");
|
|
9
|
+
const database_1 = require("@orthacms/database");
|
|
10
|
+
const copilot_events_1 = require("../../../copilot.events");
|
|
9
11
|
const tool_permission_broker_1 = require("../../application/tool-permission.broker");
|
|
10
12
|
const decide_tool_permission_dto_1 = require("../../application/dto/decide-tool-permission.dto");
|
|
11
13
|
const extend_tool_permission_dto_1 = require("../../application/dto/extend-tool-permission.dto");
|
|
@@ -41,10 +43,14 @@ const extend_tool_permission_dto_1 = require("../../application/dto/extend-tool-
|
|
|
41
43
|
*/
|
|
42
44
|
let ToolPermissionController = class ToolPermissionController {
|
|
43
45
|
broker;
|
|
44
|
-
|
|
46
|
+
uow;
|
|
47
|
+
outbox;
|
|
48
|
+
constructor(broker, uow, outbox) {
|
|
45
49
|
this.broker = broker;
|
|
50
|
+
this.uow = uow;
|
|
51
|
+
this.outbox = outbox;
|
|
46
52
|
}
|
|
47
|
-
decide(runId, body, user, workspaceId) {
|
|
53
|
+
async decide(runId, body, user, workspaceId) {
|
|
48
54
|
const delivered = this.broker.decide(runId, body.callId, body.decision, {
|
|
49
55
|
userId: user.id,
|
|
50
56
|
workspaceId
|
|
@@ -52,6 +58,21 @@ let ToolPermissionController = class ToolPermissionController {
|
|
|
52
58
|
if (!delivered) {
|
|
53
59
|
throw new common_1.NotFoundException('That request is no longer waiting for an answer.');
|
|
54
60
|
}
|
|
61
|
+
// Recorded only when the decision was actually **delivered** — a late
|
|
62
|
+
// answer to a run that already gave up had no effect, and a row for it
|
|
63
|
+
// would report authority that was never granted. Same rule the outbox
|
|
64
|
+
// producers follow everywhere: audit what happened, not what was asked.
|
|
65
|
+
//
|
|
66
|
+
// This is the moment a person hands the agent authority it did not
|
|
67
|
+
// otherwise hold. The run's transcript records it too, but a transcript
|
|
68
|
+
// is a conversation the author can delete, not an audit store.
|
|
69
|
+
await this.uow.run(() => this.outbox.append((0, database_1.attachActor)([
|
|
70
|
+
(0, copilot_events_1.toolPermissionEvent)(runId, {
|
|
71
|
+
workspaceId,
|
|
72
|
+
callId: body.callId,
|
|
73
|
+
decision: body.decision
|
|
74
|
+
})
|
|
75
|
+
], { id: user.id, email: user.email ?? null })));
|
|
55
76
|
}
|
|
56
77
|
/**
|
|
57
78
|
* `POST /api/copilot/runs/:runId/permission/extend` — "I need more time".
|
|
@@ -92,7 +113,7 @@ tslib_1.__decorate([
|
|
|
92
113
|
tslib_1.__param(3, (0, workspaces_server_1.CurrentWorkspace)()),
|
|
93
114
|
tslib_1.__metadata("design:type", Function),
|
|
94
115
|
tslib_1.__metadata("design:paramtypes", [String, decide_tool_permission_dto_1.DecideToolPermissionDto, Object, String]),
|
|
95
|
-
tslib_1.__metadata("design:returntype",
|
|
116
|
+
tslib_1.__metadata("design:returntype", Promise)
|
|
96
117
|
], ToolPermissionController.prototype, "decide", null);
|
|
97
118
|
tslib_1.__decorate([
|
|
98
119
|
(0, common_1.Post)('runs/:runId/permission/extend'),
|
|
@@ -112,5 +133,7 @@ exports.ToolPermissionController = ToolPermissionController = tslib_1.__decorate
|
|
|
112
133
|
(0, common_1.UseGuards)(identity_server_1.PermissionsGuard, workspaces_server_1.WorkspaceGuard),
|
|
113
134
|
(0, identity_server_1.RequirePermissions)(identity_server_1.PERMISSIONS.COPILOT_USE),
|
|
114
135
|
(0, common_1.Controller)('copilot'),
|
|
115
|
-
tslib_1.__metadata("design:paramtypes", [tool_permission_broker_1.ToolPermissionBroker
|
|
136
|
+
tslib_1.__metadata("design:paramtypes", [tool_permission_broker_1.ToolPermissionBroker,
|
|
137
|
+
database_1.UnitOfWork,
|
|
138
|
+
database_1.OutboxWriter])
|
|
116
139
|
], ToolPermissionController);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { type DomainEvent } from '@orthacms/database';
|
|
2
|
+
/**
|
|
3
|
+
* The domain events the copilot context raises.
|
|
4
|
+
*
|
|
5
|
+
* Two things are recorded and two deliberately are not, and the line between
|
|
6
|
+
* them is whether a person granted the agent something.
|
|
7
|
+
*
|
|
8
|
+
* **Recorded.** A **skill** is standing instruction: it changes what the
|
|
9
|
+
* copilot does in a workspace for everyone, on every future run, and a skill
|
|
10
|
+
* flipped to `auto` runs without anybody asking for it. A **tool permission**
|
|
11
|
+
* is the same decision at one moment — a person allowing one parked call to
|
|
12
|
+
* proceed. Both are authority granted to an agent by a human, which is the
|
|
13
|
+
* thing an audit trail exists for.
|
|
14
|
+
*
|
|
15
|
+
* **Not recorded.** Starting a run raises nothing: a run is a conversation
|
|
16
|
+
* turn, and everything a run actually *does* is already audited at the point it
|
|
17
|
+
* does it — an applied change is an `entry.*` row carrying its own `runId`
|
|
18
|
+
* (ADR-0009 applies directly, so there is no separate approval to record).
|
|
19
|
+
* Renaming a conversation raises nothing either; it is a title on the author's
|
|
20
|
+
* own thread. Both would be usage metrics in a log that has no way to filter
|
|
21
|
+
* them out.
|
|
22
|
+
*/
|
|
23
|
+
export declare const COPILOT_EVENT_KINDS: {
|
|
24
|
+
/** A skill was written into a workspace. */
|
|
25
|
+
readonly SKILL_CREATED: "copilot.skill.created";
|
|
26
|
+
/**
|
|
27
|
+
* A skill was edited.
|
|
28
|
+
*
|
|
29
|
+
* `mode` and `enabled` are the two that matter: `auto` means it applies
|
|
30
|
+
* without being asked for, and a disabled skill is indistinguishable from
|
|
31
|
+
* one that never matched.
|
|
32
|
+
*/
|
|
33
|
+
readonly SKILL_UPDATED: "copilot.skill.updated";
|
|
34
|
+
/** A skill was deleted. */
|
|
35
|
+
readonly SKILL_DELETED: "copilot.skill.deleted";
|
|
36
|
+
/**
|
|
37
|
+
* A person allowed or refused one tool call in a running turn.
|
|
38
|
+
*
|
|
39
|
+
* Bounded by human interaction — one decision per parked call — so this is
|
|
40
|
+
* not the volume problem auditing runs would be. It is the moment a human
|
|
41
|
+
* hands the agent authority it did not otherwise hold, and the run's own
|
|
42
|
+
* transcript is not an audit store.
|
|
43
|
+
*/
|
|
44
|
+
readonly TOOL_PERMISSION_DECIDED: "copilot.tool_permission.decided";
|
|
45
|
+
};
|
|
46
|
+
/** Builds a copilot-skill {@link DomainEvent}, stamping the aggregate type + id. */
|
|
47
|
+
export declare function copilotSkillEvent(kind: string, skillId: string, payload: Record<string, unknown>): DomainEvent;
|
|
48
|
+
/**
|
|
49
|
+
* Builds the `copilot.tool_permission.decided` event.
|
|
50
|
+
*
|
|
51
|
+
* The aggregate is the **run**, because that is the thing the decision is
|
|
52
|
+
* about and the handle a reviewer has: a transcript is read by run id, and the
|
|
53
|
+
* tool call this answered is one field inside it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function toolPermissionEvent(runId: string, payload: Record<string, unknown>): DomainEvent;
|
|
56
|
+
//# sourceMappingURL=copilot.events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"copilot.events.d.ts","sourceRoot":"","sources":["../../src/lib/copilot.events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAEzE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,mBAAmB;IAC5B,4CAA4C;;IAE5C;;;;;;OAMG;;IAEH,2BAA2B;;IAE3B;;;;;;;OAOG;;CAEG,CAAC;AAEX,oFAAoF;AACpF,wBAAgB,iBAAiB,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,WAAW,CAOb;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAC/B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjC,WAAW,CAOb"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.COPILOT_EVENT_KINDS = void 0;
|
|
4
|
+
exports.copilotSkillEvent = copilotSkillEvent;
|
|
5
|
+
exports.toolPermissionEvent = toolPermissionEvent;
|
|
6
|
+
const database_1 = require("@orthacms/database");
|
|
7
|
+
/**
|
|
8
|
+
* The domain events the copilot context raises.
|
|
9
|
+
*
|
|
10
|
+
* Two things are recorded and two deliberately are not, and the line between
|
|
11
|
+
* them is whether a person granted the agent something.
|
|
12
|
+
*
|
|
13
|
+
* **Recorded.** A **skill** is standing instruction: it changes what the
|
|
14
|
+
* copilot does in a workspace for everyone, on every future run, and a skill
|
|
15
|
+
* flipped to `auto` runs without anybody asking for it. A **tool permission**
|
|
16
|
+
* is the same decision at one moment — a person allowing one parked call to
|
|
17
|
+
* proceed. Both are authority granted to an agent by a human, which is the
|
|
18
|
+
* thing an audit trail exists for.
|
|
19
|
+
*
|
|
20
|
+
* **Not recorded.** Starting a run raises nothing: a run is a conversation
|
|
21
|
+
* turn, and everything a run actually *does* is already audited at the point it
|
|
22
|
+
* does it — an applied change is an `entry.*` row carrying its own `runId`
|
|
23
|
+
* (ADR-0009 applies directly, so there is no separate approval to record).
|
|
24
|
+
* Renaming a conversation raises nothing either; it is a title on the author's
|
|
25
|
+
* own thread. Both would be usage metrics in a log that has no way to filter
|
|
26
|
+
* them out.
|
|
27
|
+
*/
|
|
28
|
+
exports.COPILOT_EVENT_KINDS = {
|
|
29
|
+
/** A skill was written into a workspace. */
|
|
30
|
+
SKILL_CREATED: 'copilot.skill.created',
|
|
31
|
+
/**
|
|
32
|
+
* A skill was edited.
|
|
33
|
+
*
|
|
34
|
+
* `mode` and `enabled` are the two that matter: `auto` means it applies
|
|
35
|
+
* without being asked for, and a disabled skill is indistinguishable from
|
|
36
|
+
* one that never matched.
|
|
37
|
+
*/
|
|
38
|
+
SKILL_UPDATED: 'copilot.skill.updated',
|
|
39
|
+
/** A skill was deleted. */
|
|
40
|
+
SKILL_DELETED: 'copilot.skill.deleted',
|
|
41
|
+
/**
|
|
42
|
+
* A person allowed or refused one tool call in a running turn.
|
|
43
|
+
*
|
|
44
|
+
* Bounded by human interaction — one decision per parked call — so this is
|
|
45
|
+
* not the volume problem auditing runs would be. It is the moment a human
|
|
46
|
+
* hands the agent authority it did not otherwise hold, and the run's own
|
|
47
|
+
* transcript is not an audit store.
|
|
48
|
+
*/
|
|
49
|
+
TOOL_PERMISSION_DECIDED: 'copilot.tool_permission.decided'
|
|
50
|
+
};
|
|
51
|
+
/** Builds a copilot-skill {@link DomainEvent}, stamping the aggregate type + id. */
|
|
52
|
+
function copilotSkillEvent(kind, skillId, payload) {
|
|
53
|
+
return (0, database_1.createDomainEvent)({
|
|
54
|
+
kind,
|
|
55
|
+
aggregateType: 'copilot_skill',
|
|
56
|
+
aggregateId: skillId,
|
|
57
|
+
payload
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Builds the `copilot.tool_permission.decided` event.
|
|
62
|
+
*
|
|
63
|
+
* The aggregate is the **run**, because that is the thing the decision is
|
|
64
|
+
* about and the handle a reviewer has: a transcript is read by run id, and the
|
|
65
|
+
* tool call this answered is one field inside it.
|
|
66
|
+
*/
|
|
67
|
+
function toolPermissionEvent(runId, payload) {
|
|
68
|
+
return (0, database_1.createDomainEvent)({
|
|
69
|
+
kind: exports.COPILOT_EVENT_KINDS.TOOL_PERMISSION_DECIDED,
|
|
70
|
+
aggregateType: 'copilot_run',
|
|
71
|
+
aggregateId: runId,
|
|
72
|
+
payload
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copilot.module.d.ts","sourceRoot":"","sources":["../../src/lib/copilot.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAIH,KAAK,aAAa,EAClB,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EAEH,KAAK,oBAAoB,EAC5B,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAqBlE,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACjC,sEAAsE;IACtE,SAAS,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C,0DAA0D;IAC1D,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,+DAA+D;IAC/D,MAAM,EAAE,mBAAmB,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,qBACa,aAAa;IACtB,mEAAmE;IACnE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa;
|
|
1
|
+
{"version":3,"file":"copilot.module.d.ts","sourceRoot":"","sources":["../../src/lib/copilot.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAU,MAAM,gBAAgB,CAAC;AACvD,OAAO,EAIH,KAAK,aAAa,EAClB,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AAIlC,OAAO,EAEH,KAAK,oBAAoB,EAC5B,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAqBlE,qDAAqD;AACrD,MAAM,WAAW,oBAAoB;IACjC,sEAAsE;IACtE,SAAS,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C,0DAA0D;IAC1D,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,+DAA+D;IAC/D,MAAM,EAAE,mBAAmB,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,qBACa,aAAa;IACtB,mEAAmE;IACnE,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa;CAiI/D"}
|
|
@@ -47,11 +47,17 @@ let CopilotModule = CopilotModule_1 = class CopilotModule {
|
|
|
47
47
|
// and its order is the preference order (`catalogue()` renders in it,
|
|
48
48
|
// and the admin's picker opens on its first entry).
|
|
49
49
|
const [first] = options.providers;
|
|
50
|
-
if (!first) {
|
|
51
|
-
throw new Error('CopilotModule
|
|
50
|
+
if (!first && options.config.enabled) {
|
|
51
|
+
throw new Error('CopilotModule is enabled but has no model provider — the first one registered ' +
|
|
52
52
|
'is what serves a run that names none.');
|
|
53
53
|
}
|
|
54
|
-
|
|
54
|
+
// A disabled copilot registers no controller, so nothing can reach the
|
|
55
|
+
// resolver and there is no default to name. It still has to resolve to
|
|
56
|
+
// *something*: the module binds `MODEL_RESOLVER` either way, because the
|
|
57
|
+
// switch must not change the shape of the DI graph. Asking for a
|
|
58
|
+
// provider through it fails in `ModelRegistry.get`, naming the empty
|
|
59
|
+
// catalogue, which is the same error an unknown name gets.
|
|
60
|
+
const resolver = options.resolve ?? (() => first?.name ?? '');
|
|
55
61
|
return {
|
|
56
62
|
module: CopilotModule_1,
|
|
57
63
|
global: true,
|
|
@@ -62,34 +68,52 @@ let CopilotModule = CopilotModule_1 = class CopilotModule {
|
|
|
62
68
|
// so a deployment running the copilot *without* MCP still has one,
|
|
63
69
|
// and one running both has exactly one.
|
|
64
70
|
imports: [database_1.DatabaseModule, tools_server_1.ToolsModule],
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
71
|
+
// **The kill switch, applied where MCP applies its own**
|
|
72
|
+
// (`mcp.module.ts`): a disabled deployment registers no copilot
|
|
73
|
+
// controller at all, so every `/api/copilot/*` route 404s.
|
|
74
|
+
//
|
|
75
|
+
// It used to be read in one place only — `RunEngine.run` — which
|
|
76
|
+
// made "off" mean *the send button returns an error frame*. The
|
|
77
|
+
// panel, the Agents view, the model catalogue, the conversation and
|
|
78
|
+
// skill routes and the tables all stayed live, so a deployment that
|
|
79
|
+
// had opted out still offered the whole feature and refused at the
|
|
80
|
+
// last step. Off is now off.
|
|
81
|
+
//
|
|
82
|
+
// The providers, the registry and the skill catalogue below stay
|
|
83
|
+
// bound either way. They cost nothing unbuilt, and the shared
|
|
84
|
+
// `ToolRegistry` must not change shape with this switch — the MCP
|
|
85
|
+
// endpoint serves the same catalogue, and a tool contributed by
|
|
86
|
+
// `content` is registered against `ToolsModule`, not this one.
|
|
87
|
+
controllers: options.config.enabled
|
|
88
|
+
? [
|
|
89
|
+
create_run_controller_1.CreateRunController,
|
|
90
|
+
// Before `GetConversationController`: `conversations/:id` and
|
|
91
|
+
// `models` can't collide (different first segments), but keep
|
|
92
|
+
// the read routes grouped after the run route.
|
|
93
|
+
list_models_controller_1.ListModelsController,
|
|
94
|
+
list_conversations_controller_1.ListConversationsController,
|
|
95
|
+
// `PATCH conversations/:id`. No ordering hazard with the `GET`
|
|
96
|
+
// wildcard below — Express matches method and path together —
|
|
97
|
+
// but it reads with the other conversation routes.
|
|
98
|
+
update_conversation_controller_1.UpdateConversationController,
|
|
99
|
+
// Before `GetConversationController`, whose `conversations/:id`
|
|
100
|
+
// is the only wildcard here — Express matches in declaration
|
|
101
|
+
// order, so the literal-prefixed routes go first.
|
|
102
|
+
proposals_controller_1.ProposalsController,
|
|
103
|
+
// Before `GetConversationController` for the same reason as
|
|
104
|
+
// the others: its `conversations/:id` is the only wildcard, and
|
|
105
|
+
// Express matches in declaration order.
|
|
106
|
+
tool_permission_controller_1.ToolPermissionController,
|
|
107
|
+
// Both skill controllers before the conversation wildcard, for
|
|
108
|
+
// the same declaration-order reason as the rest. `skills` and
|
|
109
|
+
// `skills/:id` live in different controllers and cannot
|
|
110
|
+
// collide (different segment counts); `skills/manage` is
|
|
111
|
+
// declared above `skills/:id` inside the manage controller.
|
|
112
|
+
list_skills_controller_1.ListSkillsController,
|
|
113
|
+
manage_skills_controller_1.ManageSkillsController,
|
|
114
|
+
get_conversation_controller_1.GetConversationController
|
|
115
|
+
]
|
|
116
|
+
: [],
|
|
93
117
|
providers: [
|
|
94
118
|
{ provide: copilot_tokens_1.COPILOT_CONFIG, useValue: options.config },
|
|
95
119
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ModelProvider, type ModelRegistry } from '@orthacms/copilot-domain';
|
|
2
2
|
/** One provider the host offers, under the name runs refer to it by. */
|
|
3
3
|
export interface ProviderRegistration {
|
|
4
|
-
/** Unique, operator-chosen name — `claude`, `ollama-big`, `
|
|
4
|
+
/** Unique, operator-chosen name — `claude`, `ollama-big`, `local`. */
|
|
5
5
|
name: string;
|
|
6
6
|
/** The adapter instance, already constructed with its own config. */
|
|
7
7
|
provider: ModelProvider;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-registry.d.ts","sourceRoot":"","sources":["../../../src/lib/infrastructure/model-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,aAAa,EAClB,KAAK,aAAa,EACrB,MAAM,0BAA0B,CAAC;AAElC,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACjC,
|
|
1
|
+
{"version":3,"file":"model-registry.d.ts","sourceRoot":"","sources":["../../../src/lib/infrastructure/model-registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAGH,KAAK,aAAa,EAClB,KAAK,aAAa,EACrB,MAAM,0BAA0B,CAAC;AAElC,wEAAwE;AACxE,MAAM,WAAW,oBAAoB;IACjC,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC;IACb,qEAAqE;IACrE,QAAQ,EAAE,aAAa,CAAC;CAC3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAC9B,aAAa,EAAE,SAAS,oBAAoB,EAAE,GAC/C,aAAa,CA+Cf"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OutboxWriter, UnitOfWork } from '@orthacms/database';
|
|
1
2
|
import { type PublicUser } from '@orthacms/identity-server';
|
|
2
3
|
import { SkillCatalogService, type SkillSummary } from '../../application/skill-catalog.service';
|
|
3
4
|
import { SkillRepository, type SkillRecord } from '../../infrastructure/persistence/skill.repository';
|
|
@@ -27,7 +28,14 @@ import { UpdateSkillDto } from '../../application/dto/update-skill.dto';
|
|
|
27
28
|
export declare class ManageSkillsController {
|
|
28
29
|
private readonly skills;
|
|
29
30
|
private readonly catalog;
|
|
30
|
-
|
|
31
|
+
private readonly uow;
|
|
32
|
+
private readonly outbox;
|
|
33
|
+
constructor(skills: SkillRepository, catalog: SkillCatalogService, uow: UnitOfWork, outbox: OutboxWriter);
|
|
34
|
+
/**
|
|
35
|
+
* Appends one `copilot.skill.*` event from inside the active unit of work,
|
|
36
|
+
* stamped with the administrator who made the change.
|
|
37
|
+
*/
|
|
38
|
+
private emit;
|
|
31
39
|
/**
|
|
32
40
|
* Every skill the manage page lists — code skills (read-only) and all of
|
|
33
41
|
* the workspace's rows, disabled ones included.
|
|
@@ -39,7 +47,7 @@ export declare class ManageSkillsController {
|
|
|
39
47
|
manageList(workspaceId: string): Promise<SkillSummary[]>;
|
|
40
48
|
read(id: string, workspaceId: string): Promise<SkillRecord>;
|
|
41
49
|
create(body: CreateSkillDto, user: PublicUser, workspaceId: string): Promise<SkillRecord>;
|
|
42
|
-
update(id: string, body: UpdateSkillDto, workspaceId: string): Promise<SkillRecord>;
|
|
50
|
+
update(id: string, body: UpdateSkillDto, user: PublicUser, workspaceId: string): Promise<SkillRecord>;
|
|
43
51
|
/**
|
|
44
52
|
* Deletes a skill.
|
|
45
53
|
*
|
|
@@ -48,7 +56,7 @@ export declare class ManageSkillsController {
|
|
|
48
56
|
* turn that ran with it holds its own name/title snapshot, so the
|
|
49
57
|
* transcript survives.
|
|
50
58
|
*/
|
|
51
|
-
remove(id: string, workspaceId: string): Promise<void>;
|
|
59
|
+
remove(id: string, user: PublicUser, workspaceId: string): Promise<void>;
|
|
52
60
|
/**
|
|
53
61
|
* Refuses a name a code skill or another row already holds.
|
|
54
62
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manage-skills.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/http/controllers/manage-skills.controller.ts"],"names":[],"mappings":"AAiBA,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACH,mBAAmB,EACnB,KAAK,YAAY,EACpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACH,eAAe,EACf,KAAK,WAAW,EACnB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAGa,sBAAsB;IAE3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;
|
|
1
|
+
{"version":3,"file":"manage-skills.controller.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/http/controllers/manage-skills.controller.ts"],"names":[],"mappings":"AAiBA,OAAO,EAEH,YAAY,EACZ,UAAU,EACb,MAAM,oBAAoB,CAAC;AAK5B,OAAO,EAMH,KAAK,UAAU,EAClB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EACH,mBAAmB,EACnB,KAAK,YAAY,EACpB,MAAM,yCAAyC,CAAC;AACjD,OAAO,EACH,eAAe,EACf,KAAK,WAAW,EACnB,MAAM,mDAAmD,CAAC;AAC3D,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,MAAM,wCAAwC,CAAC;AAExE;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAGa,sBAAsB;IAE3B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAHN,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,mBAAmB,EAC5B,GAAG,EAAE,UAAU,EACf,MAAM,EAAE,YAAY;IAGzC;;;OAGG;YACW,IAAI;IAclB;;;;;;;OAOG;IAGG,UAAU,CACQ,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,YAAY,EAAE,CAAC;IAOpB,IAAI,CACsB,EAAE,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,WAAW,CAAC;IAYjB,MAAM,CACA,IAAI,EAAE,cAAc,EACb,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,WAAW,CAAC;IAoCjB,MAAM,CACoB,EAAE,EAAE,MAAM,EAC9B,IAAI,EAAE,cAAc,EACb,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,WAAW,CAAC;IAwCvB;;;;;;;OAOG;IAKG,MAAM,CACoB,EAAE,EAAE,MAAM,EACvB,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,IAAI,CAAC;IAuBhB;;;;;;;;;OASG;YACW,cAAc;CAiB/B"}
|
|
@@ -4,6 +4,8 @@ exports.ManageSkillsController = void 0;
|
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const common_1 = require("@nestjs/common");
|
|
6
6
|
const swagger_1 = require("@nestjs/swagger");
|
|
7
|
+
const database_1 = require("@orthacms/database");
|
|
8
|
+
const copilot_events_1 = require("../../../copilot.events");
|
|
7
9
|
const identity_server_1 = require("@orthacms/identity-server");
|
|
8
10
|
const workspaces_server_1 = require("@orthacms/workspaces-server");
|
|
9
11
|
const skill_catalog_service_1 = require("../../application/skill-catalog.service");
|
|
@@ -34,9 +36,23 @@ const update_skill_dto_1 = require("../../application/dto/update-skill.dto");
|
|
|
34
36
|
let ManageSkillsController = class ManageSkillsController {
|
|
35
37
|
skills;
|
|
36
38
|
catalog;
|
|
37
|
-
|
|
39
|
+
uow;
|
|
40
|
+
outbox;
|
|
41
|
+
constructor(skills, catalog, uow, outbox) {
|
|
38
42
|
this.skills = skills;
|
|
39
43
|
this.catalog = catalog;
|
|
44
|
+
this.uow = uow;
|
|
45
|
+
this.outbox = outbox;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Appends one `copilot.skill.*` event from inside the active unit of work,
|
|
49
|
+
* stamped with the administrator who made the change.
|
|
50
|
+
*/
|
|
51
|
+
async emit(kind, skillId, payload, user) {
|
|
52
|
+
await this.outbox.append((0, database_1.attachActor)([(0, copilot_events_1.copilotSkillEvent)(kind, skillId, payload)], {
|
|
53
|
+
id: user.id,
|
|
54
|
+
email: user.email ?? null
|
|
55
|
+
}));
|
|
40
56
|
}
|
|
41
57
|
/**
|
|
42
58
|
* Every skill the manage page lists — code skills (read-only) and all of
|
|
@@ -58,18 +74,30 @@ let ManageSkillsController = class ManageSkillsController {
|
|
|
58
74
|
}
|
|
59
75
|
async create(body, user, workspaceId) {
|
|
60
76
|
await this.assertNameFree(body.name, workspaceId);
|
|
61
|
-
return this.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
77
|
+
return this.uow.run(async () => {
|
|
78
|
+
const created = await this.skills.create({
|
|
79
|
+
workspaceId,
|
|
80
|
+
name: body.name,
|
|
81
|
+
title: body.title,
|
|
82
|
+
description: body.description,
|
|
83
|
+
instructions: body.instructions,
|
|
84
|
+
mode: body.mode ?? 'manual',
|
|
85
|
+
enabled: body.enabled ?? true,
|
|
86
|
+
createdBy: user.id
|
|
87
|
+
});
|
|
88
|
+
await this.emit(copilot_events_1.COPILOT_EVENT_KINDS.SKILL_CREATED, created.id, {
|
|
89
|
+
workspaceId,
|
|
90
|
+
name: created.name,
|
|
91
|
+
title: created.title,
|
|
92
|
+
// `auto` means it runs without anybody asking for it, which
|
|
93
|
+
// is the fact that makes a skill worth an audit row.
|
|
94
|
+
mode: created.mode,
|
|
95
|
+
enabled: created.enabled
|
|
96
|
+
}, user);
|
|
97
|
+
return created;
|
|
70
98
|
});
|
|
71
99
|
}
|
|
72
|
-
async update(id, body, workspaceId) {
|
|
100
|
+
async update(id, body, user, workspaceId) {
|
|
73
101
|
// An empty patch cannot mean anything, and answering 200 to it would
|
|
74
102
|
// report success for a write that never happened. The pipe cannot catch
|
|
75
103
|
// this: every property is legitimately optional on its own.
|
|
@@ -79,11 +107,26 @@ let ManageSkillsController = class ManageSkillsController {
|
|
|
79
107
|
if (body.name !== undefined) {
|
|
80
108
|
await this.assertNameFree(body.name, workspaceId, id);
|
|
81
109
|
}
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
110
|
+
const existing = await this.skills.find(id, workspaceId);
|
|
111
|
+
return this.uow.run(async () => {
|
|
112
|
+
const updated = await this.skills.update(id, workspaceId, body);
|
|
113
|
+
if (!updated) {
|
|
114
|
+
throw new common_1.NotFoundException('Skill not found.');
|
|
115
|
+
}
|
|
116
|
+
await this.emit(copilot_events_1.COPILOT_EVENT_KINDS.SKILL_UPDATED, id, {
|
|
117
|
+
workspaceId,
|
|
118
|
+
name: updated.name,
|
|
119
|
+
// The keys the caller sent — a patch, so "what changed" is
|
|
120
|
+
// the request rather than the row.
|
|
121
|
+
fields: Object.keys(body),
|
|
122
|
+
mode: { from: existing?.mode ?? null, to: updated.mode },
|
|
123
|
+
enabled: {
|
|
124
|
+
from: existing?.enabled ?? null,
|
|
125
|
+
to: updated.enabled
|
|
126
|
+
}
|
|
127
|
+
}, user);
|
|
128
|
+
return updated;
|
|
129
|
+
});
|
|
87
130
|
}
|
|
88
131
|
/**
|
|
89
132
|
* Deletes a skill.
|
|
@@ -93,11 +136,22 @@ let ManageSkillsController = class ManageSkillsController {
|
|
|
93
136
|
* turn that ran with it holds its own name/title snapshot, so the
|
|
94
137
|
* transcript survives.
|
|
95
138
|
*/
|
|
96
|
-
async remove(id, workspaceId) {
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
139
|
+
async remove(id, user, workspaceId) {
|
|
140
|
+
const existing = await this.skills.find(id, workspaceId);
|
|
141
|
+
await this.uow.run(async () => {
|
|
142
|
+
const removed = await this.skills.remove(id, workspaceId);
|
|
143
|
+
if (!removed) {
|
|
144
|
+
throw new common_1.NotFoundException('Skill not found.');
|
|
145
|
+
}
|
|
146
|
+
// The skill's own details, because after this commits there is
|
|
147
|
+
// nowhere left to look them up.
|
|
148
|
+
await this.emit(copilot_events_1.COPILOT_EVENT_KINDS.SKILL_DELETED, id, {
|
|
149
|
+
workspaceId,
|
|
150
|
+
name: existing?.name ?? null,
|
|
151
|
+
title: existing?.title ?? null,
|
|
152
|
+
mode: existing?.mode ?? null
|
|
153
|
+
}, user);
|
|
154
|
+
});
|
|
101
155
|
}
|
|
102
156
|
/**
|
|
103
157
|
* Refuses a name a code skill or another row already holds.
|
|
@@ -157,9 +211,10 @@ tslib_1.__decorate([
|
|
|
157
211
|
(0, swagger_1.ApiResponse)({ status: 409, description: 'The new name is already taken.' }),
|
|
158
212
|
tslib_1.__param(0, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
|
|
159
213
|
tslib_1.__param(1, (0, common_1.Body)()),
|
|
160
|
-
tslib_1.__param(2, (0,
|
|
214
|
+
tslib_1.__param(2, (0, identity_server_1.CurrentUser)()),
|
|
215
|
+
tslib_1.__param(3, (0, workspaces_server_1.CurrentWorkspace)()),
|
|
161
216
|
tslib_1.__metadata("design:type", Function),
|
|
162
|
-
tslib_1.__metadata("design:paramtypes", [String, update_skill_dto_1.UpdateSkillDto, String]),
|
|
217
|
+
tslib_1.__metadata("design:paramtypes", [String, update_skill_dto_1.UpdateSkillDto, Object, String]),
|
|
163
218
|
tslib_1.__metadata("design:returntype", Promise)
|
|
164
219
|
], ManageSkillsController.prototype, "update", null);
|
|
165
220
|
tslib_1.__decorate([
|
|
@@ -168,9 +223,10 @@ tslib_1.__decorate([
|
|
|
168
223
|
(0, common_1.HttpCode)(common_1.HttpStatus.NO_CONTENT),
|
|
169
224
|
(0, swagger_1.ApiOperation)({ summary: 'Delete a skill' }),
|
|
170
225
|
tslib_1.__param(0, (0, common_1.Param)('id', common_1.ParseUUIDPipe)),
|
|
171
|
-
tslib_1.__param(1, (0,
|
|
226
|
+
tslib_1.__param(1, (0, identity_server_1.CurrentUser)()),
|
|
227
|
+
tslib_1.__param(2, (0, workspaces_server_1.CurrentWorkspace)()),
|
|
172
228
|
tslib_1.__metadata("design:type", Function),
|
|
173
|
-
tslib_1.__metadata("design:paramtypes", [String, String]),
|
|
229
|
+
tslib_1.__metadata("design:paramtypes", [String, Object, String]),
|
|
174
230
|
tslib_1.__metadata("design:returntype", Promise)
|
|
175
231
|
], ManageSkillsController.prototype, "remove", null);
|
|
176
232
|
exports.ManageSkillsController = ManageSkillsController = tslib_1.__decorate([
|
|
@@ -178,5 +234,7 @@ exports.ManageSkillsController = ManageSkillsController = tslib_1.__decorate([
|
|
|
178
234
|
(0, identity_server_1.RequirePermissions)(identity_server_1.PERMISSIONS.COPILOT_SKILLS_MANAGE),
|
|
179
235
|
(0, common_1.Controller)('copilot'),
|
|
180
236
|
tslib_1.__metadata("design:paramtypes", [skill_repository_1.SkillRepository,
|
|
181
|
-
skill_catalog_service_1.SkillCatalogService
|
|
237
|
+
skill_catalog_service_1.SkillCatalogService,
|
|
238
|
+
database_1.UnitOfWork,
|
|
239
|
+
database_1.OutboxWriter])
|
|
182
240
|
], ManageSkillsController);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { UnitOfWork } from '@orthacms/database';
|
|
2
2
|
import type { Skill, SkillMode } from '@orthacms/copilot-domain';
|
|
3
3
|
/** A CMS skill as the manage page reads it — the row, plus its provenance. */
|
|
4
4
|
export interface SkillRecord extends Skill {
|
|
@@ -43,8 +43,15 @@ export interface UpdateSkillInput {
|
|
|
43
43
|
* and same reason, as `ConversationRepository`.
|
|
44
44
|
*/
|
|
45
45
|
export declare class SkillRepository {
|
|
46
|
-
private readonly
|
|
47
|
-
constructor(
|
|
46
|
+
private readonly uow;
|
|
47
|
+
constructor(uow: UnitOfWork);
|
|
48
|
+
/**
|
|
49
|
+
* The executor to run against: the ambient transaction when a caller opened
|
|
50
|
+
* one, the base connection otherwise — so a skill write and the
|
|
51
|
+
* `copilot.skill.*` event describing it commit together. Outside a unit of
|
|
52
|
+
* work this is exactly the old behaviour.
|
|
53
|
+
*/
|
|
54
|
+
private get db();
|
|
48
55
|
/**
|
|
49
56
|
* The workspace's skills, oldest first.
|
|
50
57
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skill.repository.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/infrastructure/persistence/skill.repository.ts"],"names":[],"mappings":"AAEA,OAAO,
|
|
1
|
+
{"version":3,"file":"skill.repository.d.ts","sourceRoot":"","sources":["../../../../../src/lib/skills/infrastructure/persistence/skill.repository.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAiB,MAAM,oBAAoB,CAAC;AAC/D,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAGjE,8EAA8E;AAC9E,MAAM,WAAW,WAAY,SAAQ,KAAK;IACtC,6CAA6C;IAC7C,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,OAAO,EAAE,OAAO,CAAC;IACjB,mDAAmD;IACnD,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,8BAA8B;IAC9B,SAAS,EAAE,IAAI,CAAC;IAChB,iBAAiB;IACjB,SAAS,EAAE,IAAI,CAAC;CACnB;AAED,mCAAmC;AACnC,MAAM,WAAW,gBAAgB;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,SAAS,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACrB;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,CAAC,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,qBACa,eAAe;IACZ,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAH,GAAG,EAAE,UAAU;IAE5C;;;;;OAKG;IACH,OAAO,KAAK,EAAE,GAEb;IAED;;;;;;;OAOG;IACG,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAgB1E;;;;OAIG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAcxE,iEAAiE;IAC3D,UAAU,CACZ,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAc9B,0BAA0B;IACpB,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D;;;;;;OAMG;IACG,MAAM,CACR,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,gBAAgB,GACxB,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAc9B,oEAAoE;IAC9D,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAYlE"}
|
|
@@ -16,9 +16,18 @@ const skills_1 = require("../schema/skills");
|
|
|
16
16
|
* and same reason, as `ConversationRepository`.
|
|
17
17
|
*/
|
|
18
18
|
let SkillRepository = class SkillRepository {
|
|
19
|
-
|
|
20
|
-
constructor(
|
|
21
|
-
this.
|
|
19
|
+
uow;
|
|
20
|
+
constructor(uow) {
|
|
21
|
+
this.uow = uow;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The executor to run against: the ambient transaction when a caller opened
|
|
25
|
+
* one, the base connection otherwise — so a skill write and the
|
|
26
|
+
* `copilot.skill.*` event describing it commit together. Outside a unit of
|
|
27
|
+
* work this is exactly the old behaviour.
|
|
28
|
+
*/
|
|
29
|
+
get db() {
|
|
30
|
+
return this.uow.current();
|
|
22
31
|
}
|
|
23
32
|
/**
|
|
24
33
|
* The workspace's skills, oldest first.
|
|
@@ -95,8 +104,7 @@ let SkillRepository = class SkillRepository {
|
|
|
95
104
|
exports.SkillRepository = SkillRepository;
|
|
96
105
|
exports.SkillRepository = SkillRepository = tslib_1.__decorate([
|
|
97
106
|
(0, common_1.Injectable)(),
|
|
98
|
-
tslib_1.
|
|
99
|
-
tslib_1.__metadata("design:paramtypes", [Object])
|
|
107
|
+
tslib_1.__metadata("design:paramtypes", [database_1.UnitOfWork])
|
|
100
108
|
], SkillRepository);
|
|
101
109
|
/** The row, with `source` stamped — a database row is always a CMS skill. */
|
|
102
110
|
function toRecord(row) {
|
|
@@ -20,6 +20,10 @@ export interface CopilotPluginOptions {
|
|
|
20
20
|
* misspelled, could point at a provider nobody registered, and had to be
|
|
21
21
|
* kept in step with this list on every change. A deployment that should
|
|
22
22
|
* not reach a backend does not register it.
|
|
23
|
+
*
|
|
24
|
+
* **May be empty only while `config.enabled` is false.** A copilot that is
|
|
25
|
+
* on and has nothing to call is a misconfiguration, and fails at
|
|
26
|
+
* construction rather than on the first message.
|
|
23
27
|
*/
|
|
24
28
|
providers: readonly ProviderRegistration[];
|
|
25
29
|
/** Optional custom handler picking a provider per run (plain code). */
|
|
@@ -50,13 +54,16 @@ export interface CopilotPluginOptions {
|
|
|
50
54
|
* engine, the capability profile, and the transcript tables this plugin now
|
|
51
55
|
* owns and migrates.
|
|
52
56
|
*
|
|
57
|
+
* Each entry is a backend this deployment actually configured. An enabled
|
|
58
|
+
* copilot needs at least one; a disabled one needs none, and a checkout with no
|
|
59
|
+
* keys registers none.
|
|
60
|
+
*
|
|
53
61
|
* @example
|
|
54
62
|
* ```typescript
|
|
55
63
|
* CopilotPlugin({
|
|
56
64
|
* providers: [
|
|
57
65
|
* { name: 'claude', provider: createAnthropicProvider(providers.claude) },
|
|
58
|
-
* { name: 'ollama', provider: createOpenAiProvider(providers.ollama) }
|
|
59
|
-
* { name: 'fake', provider: createFakeProvider() }
|
|
66
|
+
* { name: 'ollama', provider: createOpenAiProvider(providers.ollama) }
|
|
60
67
|
* ],
|
|
61
68
|
* config: config.plugins.copilot
|
|
62
69
|
* });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"copilot-plugin.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/copilot-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAEH,KAAK,aAAa,EAClB,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,0DAA0D;AAC1D,MAAM,MAAM,6BAA6B,GAAG,YAAY,GAAG;IACvD,aAAa,EAAE,mBAAmB,CAAC;CACtC,CAAC;AAEF,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACjC
|
|
1
|
+
{"version":3,"file":"copilot-plugin.d.ts","sourceRoot":"","sources":["../../../src/lib/utils/copilot-plugin.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAEH,KAAK,aAAa,EAClB,KAAK,eAAe,EACvB,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kCAAkC,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE,0DAA0D;AAC1D,MAAM,MAAM,6BAA6B,GAAG,YAAY,GAAG;IACvD,aAAa,EAAE,mBAAmB,CAAC;CACtC,CAAC;AAEF,wDAAwD;AACxD,MAAM,WAAW,oBAAoB;IACjC;;;;;;;;;;;;;;;;OAgBG;IACH,SAAS,EAAE,SAAS,oBAAoB,EAAE,CAAC;IAC3C,uEAAuE;IACvE,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB;;;;;;;;;OASG;IACH,MAAM,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IACpC,+DAA+D;IAC/D,MAAM,EAAE,mBAAmB,CAAC;CAC/B;AA+DD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,aAAa,CACzB,OAAO,EAAE,oBAAoB,GAC9B,6BAA6B,CAa/B"}
|
|
@@ -6,19 +6,28 @@ const copilot_domain_1 = require("@orthacms/copilot-domain");
|
|
|
6
6
|
const copilot_module_1 = require("../copilot.module");
|
|
7
7
|
/**
|
|
8
8
|
* Validate the wiring **eagerly** (like `ContentPlugin`'s registry and
|
|
9
|
-
* `I18nServerPlugin`'s locales):
|
|
10
|
-
*
|
|
11
|
-
* before boot — rather than on the first chat message, where it
|
|
12
|
-
* expensive to diagnose.
|
|
9
|
+
* `I18nServerPlugin`'s locales): a provider declaring no models, or an enabled
|
|
10
|
+
* copilot with no provider at all, is a misconfiguration that should fail at
|
|
11
|
+
* construction — before boot — rather than on the first chat message, where it
|
|
12
|
+
* is most expensive to diagnose.
|
|
13
|
+
*
|
|
14
|
+
* **The empty list is only a misconfiguration when the copilot is on.** A
|
|
15
|
+
* deployment that has not set `enabled` mounts no controllers and can serve no
|
|
16
|
+
* run, so it needs no backend — and that is the ordinary state of a fresh
|
|
17
|
+
* checkout, which configures no keys. There is no scripted offline adapter to
|
|
18
|
+
* fall back on: the fake provider is a private test fixture, so a host with no
|
|
19
|
+
* keys registers nothing rather than registering something that answers every
|
|
20
|
+
* question with a canned sentence.
|
|
13
21
|
*
|
|
14
22
|
* Name uniqueness is enforced by `buildModelRegistry`, which the module builds
|
|
15
23
|
* from the same list.
|
|
16
24
|
*/
|
|
17
25
|
function assertOptions(options) {
|
|
18
26
|
const names = options.providers.map((entry) => entry.name);
|
|
19
|
-
if (names.length === 0) {
|
|
20
|
-
throw new Error('CopilotPlugin
|
|
21
|
-
"e.g. `providers: [{ name: '
|
|
27
|
+
if (names.length === 0 && options.config.enabled) {
|
|
28
|
+
throw new Error('CopilotPlugin is enabled but has no model provider. Configure a backend and register it at ' +
|
|
29
|
+
"the composition root, e.g. `providers: [{ name: 'claude', provider: createAnthropicProvider(…) }]` " +
|
|
30
|
+
'— or leave the copilot off.');
|
|
22
31
|
}
|
|
23
32
|
for (const entry of options.providers) {
|
|
24
33
|
if (entry.provider.models().length === 0) {
|
|
@@ -59,13 +68,16 @@ function assertOptions(options) {
|
|
|
59
68
|
* engine, the capability profile, and the transcript tables this plugin now
|
|
60
69
|
* owns and migrates.
|
|
61
70
|
*
|
|
71
|
+
* Each entry is a backend this deployment actually configured. An enabled
|
|
72
|
+
* copilot needs at least one; a disabled one needs none, and a checkout with no
|
|
73
|
+
* keys registers none.
|
|
74
|
+
*
|
|
62
75
|
* @example
|
|
63
76
|
* ```typescript
|
|
64
77
|
* CopilotPlugin({
|
|
65
78
|
* providers: [
|
|
66
79
|
* { name: 'claude', provider: createAnthropicProvider(providers.claude) },
|
|
67
|
-
* { name: 'ollama', provider: createOpenAiProvider(providers.ollama) }
|
|
68
|
-
* { name: 'fake', provider: createFakeProvider() }
|
|
80
|
+
* { name: 'ollama', provider: createOpenAiProvider(providers.ollama) }
|
|
69
81
|
* ],
|
|
70
82
|
* config: config.plugins.copilot
|
|
71
83
|
* });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orthacms/copilot-server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "@orthacms/copilot-server — part of Ortha CMS.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/ortha-source/ortha-cms/tree/main/packages/copilot/server",
|
|
@@ -28,12 +28,12 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@nestjs/common": "^11.0.0",
|
|
30
30
|
"@nestjs/swagger": "^11.4.6",
|
|
31
|
-
"@orthacms/bootstrap-server": "^0.
|
|
32
|
-
"@orthacms/copilot-domain": "^0.
|
|
33
|
-
"@orthacms/database": "^0.
|
|
34
|
-
"@orthacms/identity-server": "^0.
|
|
35
|
-
"@orthacms/tools-server": "^0.
|
|
36
|
-
"@orthacms/workspaces-server": "^0.
|
|
31
|
+
"@orthacms/bootstrap-server": "^0.5.0",
|
|
32
|
+
"@orthacms/copilot-domain": "^0.5.0",
|
|
33
|
+
"@orthacms/database": "^0.5.0",
|
|
34
|
+
"@orthacms/identity-server": "^0.5.0",
|
|
35
|
+
"@orthacms/tools-server": "^0.5.0",
|
|
36
|
+
"@orthacms/workspaces-server": "^0.5.0",
|
|
37
37
|
"class-transformer": "^0.5.1",
|
|
38
38
|
"class-validator": "^0.15.1",
|
|
39
39
|
"drizzle-orm": "^0.45.0",
|