@orthacms/copilot-server 0.4.3 → 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.
Files changed (28) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -1
  4. package/dist/lib/chat/application/decide-proposal.service.d.ts.map +1 -1
  5. package/dist/lib/chat/application/decide-proposal.service.js +6 -1
  6. package/dist/lib/chat/application/dto/create-run.dto.d.ts +9 -2
  7. package/dist/lib/chat/application/dto/create-run.dto.d.ts.map +1 -1
  8. package/dist/lib/chat/application/dto/create-run.dto.js +15 -2
  9. package/dist/lib/chat/application/proposal-event-actor.d.ts +26 -0
  10. package/dist/lib/chat/application/proposal-event-actor.d.ts.map +1 -0
  11. package/dist/lib/chat/application/proposal-event-actor.js +36 -0
  12. package/dist/lib/chat/application/run-engine.service.d.ts.map +1 -1
  13. package/dist/lib/chat/application/run-engine.service.js +22 -3
  14. package/dist/lib/chat/application/system-prompt.d.ts.map +1 -1
  15. package/dist/lib/chat/application/system-prompt.js +7 -0
  16. package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts +5 -2
  17. package/dist/lib/chat/http/controllers/tool-permission.controller.d.ts.map +1 -1
  18. package/dist/lib/chat/http/controllers/tool-permission.controller.js +27 -4
  19. package/dist/lib/copilot.events.d.ts +56 -0
  20. package/dist/lib/copilot.events.d.ts.map +1 -0
  21. package/dist/lib/copilot.events.js +74 -0
  22. package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts +11 -3
  23. package/dist/lib/skills/http/controllers/manage-skills.controller.d.ts.map +1 -1
  24. package/dist/lib/skills/http/controllers/manage-skills.controller.js +84 -26
  25. package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts +10 -3
  26. package/dist/lib/skills/infrastructure/persistence/skill.repository.d.ts.map +1 -1
  27. package/dist/lib/skills/infrastructure/persistence/skill.repository.js +13 -5
  28. 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
@@ -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;CAmDlC"}
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
- /** The surfaces a run can be started from (design §2). */
12
- export declare const RUN_SURFACES: readonly ["chat", "palette", "entry", "records"];
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,0DAA0D;AAC1D,eAAO,MAAM,YAAY,kDAAmD,CAAC;AAE7E;;;;;;;;;;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"}
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
- /** The surfaces a run can be started from (design §2). */
20
- exports.RUN_SURFACES = ['chat', 'palette', 'entry', 'records'];
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
+ }
@@ -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;;;;;;;;;GASG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;;CAK9C;AAED;;;;;;;;;GASG;AACH,qBACa,SAAS;IAOU,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAC9C,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IAOvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAtBhC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8B;IAErD,yCAAyC;IACzC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAY;gBAGU,QAAQ,EAAE,aAAa,EACvB,QAAQ,EAAE,aAAa,EACxB,MAAM,EAAE,mBAAmB,EAClD,QAAQ,EAAE,wBAAwB,EAClC,KAAK,EAAE,YAAY,EACnB,aAAa,EAAE,sBAAsB,EACrC,SAAS,EAAE,kBAAkB,EAC7B,SAAS,EAAE,qBAAqB,EAChC,WAAW,EAAE,oBAAoB,EACjC,MAAM,EAAE,mBAAmB,EAO3B,WAAW,GAAE,kBAAkB,GAAG,IAAW,EAG9D,MAAM,GAAE,SAAS,GAAG,IAAW;IAQnC,qDAAqD;IAC9C,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,cAAc,CAAC,eAAe,CAAC;IA4KjE;;;OAGG;YACY,IAAI;IA4InB,2EAA2E;YAC5D,UAAU;IA6DzB;;;;;;;OAOG;IACH;;;;;;;;;;;;;OAaG;YACW,MAAM;YA2GN,WAAW;IAwJzB;;;;;;;;;;;;;;;;;OAiBG;YACW,cAAc;IAqI5B;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;YACW,aAAa;IA6E3B;;;;;;;;OAQG;YACW,WAAW;IAiCzB,gEAAgE;YAClD,KAAK;IA6BnB;;;;;;;;;;;;;;;OAeG;YACW,kBAAkB;IAmChC;;;;;;;;;;;;;;;;;;;OAmBG;YACW,WAAW;CAwC5B;AAED;;;;GAIG;AACH,qBAAa,eAAgB,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI9B;AA0BD,sEAAsE;AACtE,eAAO,MAAM,qBAAqB,KAAwB,CAAC"}
1
+ {"version":3,"file":"run-engine.service.d.ts","sourceRoot":"","sources":["../../../../src/lib/chat/application/run-engine.service.ts"],"names":[],"mappings":"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"}
@@ -166,6 +166,14 @@ let RunEngine = RunEngine_1 = class RunEngine {
166
166
  const history = await this.loadHistory(conversation.id);
167
167
  const usage = { inputTokens: 0, outputTokens: 0 };
168
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: '' };
169
177
  let stopReason = 'end';
170
178
  try {
171
179
  stopReason = yield* this.loop({
@@ -179,10 +187,15 @@ let RunEngine = RunEngine_1 = class RunEngine {
179
187
  history,
180
188
  usage,
181
189
  assistantBlocks,
190
+ partial,
182
191
  startedAt
183
192
  });
184
193
  }
185
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
+ }
186
199
  if ((0, copilot_domain_1.isAbortError)(error, input.signal)) {
187
200
  stopReason = 'aborted';
188
201
  }
@@ -252,6 +265,9 @@ let RunEngine = RunEngine_1 = class RunEngine {
252
265
  if (turn.text) {
253
266
  ctx.assistantBlocks.push({ type: 'text', text: turn.text });
254
267
  }
268
+ // Banked: anything that throws from here on must not append it a
269
+ // second time.
270
+ ctx.partial.text = '';
255
271
  ctx.assistantBlocks.push(...turn.toolUses);
256
272
  if (turn.stopReason === 'aborted')
257
273
  return 'aborted';
@@ -338,7 +354,10 @@ let RunEngine = RunEngine_1 = class RunEngine {
338
354
  /** One model call, reduced to text, tool uses, usage and a stop reason. */
339
355
  async *streamTurn(ctx, messages, tools) {
340
356
  const toolUses = [];
341
- let text = '';
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 = '';
342
361
  let usage = { inputTokens: 0, outputTokens: 0 };
343
362
  let stopReason = 'end';
344
363
  const stream = ctx.provider.provider.stream({
@@ -350,7 +369,7 @@ let RunEngine = RunEngine_1 = class RunEngine {
350
369
  }, ctx.input.signal);
351
370
  for await (const event of stream) {
352
371
  if (event.type === 'text-delta') {
353
- text += event.text;
372
+ ctx.partial.text += event.text;
354
373
  // Yielded, not collected. Buffering these into an array and
355
374
  // flushing after the provider's stream ends turns the whole
356
375
  // feature off: the answer arrives in one burst when the model
@@ -370,7 +389,7 @@ let RunEngine = RunEngine_1 = class RunEngine {
370
389
  stopReason = event.stopReason;
371
390
  }
372
391
  }
373
- return { text, toolUses, usage, stopReason };
392
+ return { text: ctx.partial.text, toolUses, usage, stopReason };
374
393
  }
375
394
  /**
376
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;AAsDxC;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAgHlE"}
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
- constructor(broker: ToolPermissionBroker);
38
- decide(runId: string, body: DecideToolPermissionDto, user: PublicUser, workspaceId: string): void;
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;IACrB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,oBAAoB;IAQzD,MAAM,CAC6B,KAAK,EAAE,MAAM,EACpC,IAAI,EAAE,uBAAuB,EACtB,IAAI,EAAE,UAAU,EACX,WAAW,EAAE,MAAM,GACxC,IAAI;IAiBP;;;;;;;;;;;;;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"}
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
- constructor(broker) {
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", void 0)
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,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
- constructor(skills: SkillRepository, catalog: SkillCatalogService);
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;gBADP,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,mBAAmB;IAGjD;;;;;;;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;IAmBjB,MAAM,CACoB,EAAE,EAAE,MAAM,EAC9B,IAAI,EAAE,cAAc,EACR,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,WAAW,CAAC;IAoBvB;;;;;;;OAOG;IAKG,MAAM,CACoB,EAAE,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GACxC,OAAO,CAAC,IAAI,CAAC;IAOhB;;;;;;;;;OASG;YACW,cAAc;CAiB/B"}
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
- constructor(skills, catalog) {
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.skills.create({
62
- workspaceId,
63
- name: body.name,
64
- title: body.title,
65
- description: body.description,
66
- instructions: body.instructions,
67
- mode: body.mode ?? 'manual',
68
- enabled: body.enabled ?? true,
69
- createdBy: user.id
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 updated = await this.skills.update(id, workspaceId, body);
83
- if (!updated) {
84
- throw new common_1.NotFoundException('Skill not found.');
85
- }
86
- return updated;
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 removed = await this.skills.remove(id, workspaceId);
98
- if (!removed) {
99
- throw new common_1.NotFoundException('Skill not found.');
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, workspaces_server_1.CurrentWorkspace)()),
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, workspaces_server_1.CurrentWorkspace)()),
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 { type Database } from '@orthacms/database';
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 db;
47
- constructor(db: Database);
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,EAAkB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AACnE,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;IACM,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,QAAQ;IAE3D;;;;;;;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"}
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
- db;
20
- constructor(db) {
21
- this.db = db;
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.__param(0, (0, database_1.InjectDatabase)()),
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orthacms/copilot-server",
3
- "version": "0.4.3",
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.4.3",
32
- "@orthacms/copilot-domain": "^0.4.3",
33
- "@orthacms/database": "^0.4.3",
34
- "@orthacms/identity-server": "^0.4.3",
35
- "@orthacms/tools-server": "^0.4.3",
36
- "@orthacms/workspaces-server": "^0.4.3",
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",